diff --git a/src/features/users/components/ActionButtons.tsx b/src/features/users/components/ActionButtons.tsx index cacb875..8174dba 100644 --- a/src/features/users/components/ActionButtons.tsx +++ b/src/features/users/components/ActionButtons.tsx @@ -35,8 +35,9 @@ import { interface ActionButtonsProps { userId: string; userName: string; - onSuspend?: () => void; // Provided by parent to open modal - onSendNotification?: () => void; // Provided by parent + onSuspend?: () => void; + onSendNotification?: () => void; + onCreateTicket?: () => void; } export function ActionButtons({ @@ -44,6 +45,7 @@ export function ActionButtons({ userName, onSuspend, onSendNotification, + onCreateTicket, }: ActionButtonsProps) { const [isPending, startTransition] = useTransition(); @@ -97,20 +99,9 @@ export function ActionButtons({ }; const handleCreateTicket = () => { - // Quick action: create a generic ticket for now - // In reality, this might open a dialog - startTransition(async () => { - const result = await createSupportTicket(userId, "User requires manual review"); - if (result.success) { - toast.success('Support Ticket Created', { - description: result.message, - }); - } else { - toast.error('Failed to create ticket', { - description: result.message, - }); - } - }); + if (onCreateTicket) { + onCreateTicket(); + } }; return ( diff --git a/src/features/users/components/UserInspectorSheet.tsx b/src/features/users/components/UserInspectorSheet.tsx index bb25995..7dda8ad 100644 --- a/src/features/users/components/UserInspectorSheet.tsx +++ b/src/features/users/components/UserInspectorSheet.tsx @@ -64,6 +64,9 @@ import { BookingsTab } from './tabs/BookingsTab'; import { SecurityTab } from './tabs/SecurityTab'; import { SupportTab } from './tabs/SupportTab'; import { AuditTab } from './tabs/AuditTab'; +import { NotificationComposer } from './dialogs/NotificationComposer'; +import { ModerationDialog } from './dialogs/ModerationDialog'; +import { EscalationForm } from './dialogs/EscalationForm'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; @@ -84,6 +87,10 @@ export function UserInspectorSheet({ }: UserInspectorSheetProps) { const [activeTab, setActiveTab] = useState('overview'); + const [notificationOpen, setNotificationOpen] = useState(false); + const [moderationOpen, setModerationOpen] = useState(false); + const [escalationOpen, setEscalationOpen] = useState(false); + if (!user) return null; // Fetch related data @@ -208,8 +215,9 @@ export function UserInspectorSheet({ handleAction('Suspend')} - onSendNotification={() => onSendNotification(user)} + onSuspend={() => setModerationOpen(true)} + onSendNotification={() => setNotificationOpen(true)} + onCreateTicket={() => setEscalationOpen(true)} /> @@ -254,6 +262,27 @@ export function UserInspectorSheet({ + + {/* Dialogs */} + + + ); diff --git a/src/features/users/components/dialogs/EscalationForm.tsx b/src/features/users/components/dialogs/EscalationForm.tsx new file mode 100644 index 0000000..b0811cb --- /dev/null +++ b/src/features/users/components/dialogs/EscalationForm.tsx @@ -0,0 +1,221 @@ +'use client'; + +import { useState, useTransition } from 'react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Loader2, Ticket, Phone, Clock, AlertTriangle } from 'lucide-react'; +import { createEscalationTicket } from '@/lib/actions/userActions'; +import { toast } from 'sonner'; +import { cn } from '@/lib/utils'; + +interface EscalationFormProps { + open: boolean; + onOpenChange: (open: boolean) => void; + userId: string; + userName?: string; // Optional for display +} + +const TICKET_TYPES = ['Refund', 'Payment', 'Account access', 'Fraud', 'Event issue', 'Other']; +const PRIORITIES = ['Low', 'Normal', 'High', 'Urgent']; + +export function EscalationForm({ + open, + onOpenChange, + userId, + userName +}: EscalationFormProps) { + const [isPending, startTransition] = useTransition(); + const [type, setType] = useState(''); + const [priority, setPriority] = useState('Normal'); + const [subject, setSubject] = useState(''); + const [description, setDescription] = useState(''); + const [callbackRequired, setCallbackRequired] = useState(false); + const [callbackPhone, setCallbackPhone] = useState(''); + const [assigneeId, setAssigneeId] = useState(''); + + const handleSubmit = () => { + if (!type || !subject || !description) { + toast.error("Please fill in all required fields"); + return; + } + + startTransition(async () => { + const result = await createEscalationTicket({ + userId, + type: type as any, + priority: priority as any, + subject, + description, + callbackRequired, + callbackPhone: callbackRequired ? callbackPhone : undefined, + assigneeId: assigneeId || undefined, + }); + + if (result.success) { + toast.success(result.message); + onOpenChange(false); + setSubject(''); + setDescription(''); + } else { + toast.error(result.message); + } + }); + }; + + return ( + + + + + + Create Escalation Ticket + + + Raise a new internal support ticket or escalation. + + + +
+
+
+ + +
+
+ + +
+
+ +
+ + setSubject(e.target.value)} + /> +
+ +
+ +