diff --git a/src/features/users/components/BulkActionsDropdown.tsx b/src/features/users/components/BulkActionsDropdown.tsx index 8111114..c0a9b08 100644 --- a/src/features/users/components/BulkActionsDropdown.tsx +++ b/src/features/users/components/BulkActionsDropdown.tsx @@ -1,5 +1,5 @@ // BulkActionsDropdown - Bulk operations menu for selected users -import { useState } from 'react'; +import { useState, useTransition } from 'react'; import { DropdownMenu, DropdownMenuContent, @@ -37,6 +37,13 @@ import { mockTags } from '../data/mockUserCrmData'; import type { User } from '@/lib/types/user'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; +import { + bulkSuspendUsers, + bulkBanUsers, + bulkDeleteUsers, + bulkVerifyUsers, + bulkTagUsers, +} from '@/lib/actions/user-management'; interface BulkActionsDropdownProps { selectedUsers: User[]; @@ -55,54 +62,73 @@ export function BulkActionsDropdown({ }: BulkActionsDropdownProps) { const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); const [actionType, setActionType] = useState<'suspend' | 'ban' | 'delete' | 'export' | 'verify'>('suspend'); - const [isProcessing, setIsProcessing] = useState(false); + const [isPending, startTransition] = useTransition(); const count = selectedUsers.length; + const userIds = selectedUsers.map(u => u.id); const handleAction = (action: 'suspend' | 'ban' | 'delete' | 'export' | 'verify') => { setActionType(action); setConfirmDialogOpen(true); }; - const handleConfirm = async () => { - setIsProcessing(true); - try { - // Simulate API call - await new Promise((resolve) => setTimeout(resolve, 1500)); - - switch (actionType) { - case 'suspend': - toast.success(`Suspended ${count} user${count > 1 ? 's' : ''}`); - break; - case 'ban': - toast.success(`Banned ${count} user${count > 1 ? 's' : ''}`); - break; - case 'delete': - toast.success(`Deleted ${count} user${count > 1 ? 's' : ''}`); - break; - case 'export': - toast.success(`Exported ${count} user${count > 1 ? 's' : ''} to CSV`); - break; - case 'verify': - toast.success(`Verified ${count} user${count > 1 ? 's' : ''}`); - break; + const executeServerAction = async (action: () => Promise<{ success: boolean; message: string }>) => { + startTransition(async () => { + try { + const result = await action(); + if (result.success) { + toast.success(result.message); + setConfirmDialogOpen(false); + onClearSelection(); + } else { + toast.error(result.message); + } + } catch (error) { + toast.error("An unexpected error occurred."); } + }); + }; - setConfirmDialogOpen(false); - onClearSelection(); - } catch (error) { - toast.error('Operation failed'); - } finally { - setIsProcessing(false); + const handleConfirm = async () => { + switch (actionType) { + case 'suspend': + executeServerAction(() => bulkSuspendUsers(userIds)); + break; + case 'ban': + executeServerAction(() => bulkBanUsers(userIds)); + break; + case 'delete': + executeServerAction(() => bulkDeleteUsers(userIds)); + break; + case 'verify': + executeServerAction(() => bulkVerifyUsers(userIds)); + break; + case 'export': + // Export is usually client-side or separate download endpoint, + // but we can simulate a server request or just handle it here. + toast.success(`Exporting ${count} users...`); + setTimeout(() => { + toast.success("Export Complete (Mock CSV Download)"); + setConfirmDialogOpen(false); + onClearSelection(); + }, 1000); + break; } }; - const handleTagUsers = async (tagId: string) => { + const handleTagUsers = (tagId: string) => { const tag = mockTags.find((t) => t.id === tagId); if (!tag) return; - toast.success(`Added tag "${tag.name}" to ${count} user${count > 1 ? 's' : ''}`); - onClearSelection(); + startTransition(async () => { + const result = await bulkTagUsers(userIds, tagId); + if (result.success) { + toast.success(result.message, { description: `Applied tag: ${tag.name}` }); + onClearSelection(); + } else { + toast.error(result.message); + } + }); }; const actionConfig = { @@ -246,16 +272,16 @@ export function BulkActionsDropdown({ -