152 lines
7.1 KiB
TypeScript
152 lines
7.1 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useTransition, useState } from 'react';
|
||
|
|
import type { User } from '@/lib/types/user';
|
||
|
|
import { Button } from '@/components/ui/button';
|
||
|
|
import { Input } from '@/components/ui/input';
|
||
|
|
import { Label } from '@/components/ui/label';
|
||
|
|
import { Textarea } from '@/components/ui/textarea';
|
||
|
|
import {
|
||
|
|
Select,
|
||
|
|
SelectContent,
|
||
|
|
SelectItem,
|
||
|
|
SelectTrigger,
|
||
|
|
SelectValue,
|
||
|
|
} from '@/components/ui/select';
|
||
|
|
import {
|
||
|
|
Dialog,
|
||
|
|
DialogContent,
|
||
|
|
DialogFooter,
|
||
|
|
DialogHeader,
|
||
|
|
DialogTitle,
|
||
|
|
DialogTrigger,
|
||
|
|
} from '@/components/ui/dialog';
|
||
|
|
import {
|
||
|
|
MessageSquare,
|
||
|
|
Mail,
|
||
|
|
Plus,
|
||
|
|
UserCircle2,
|
||
|
|
Headphones,
|
||
|
|
Loader2
|
||
|
|
} from 'lucide-react';
|
||
|
|
import { createSupportTicket } from '@/lib/actions/user-tabs';
|
||
|
|
import { toast } from 'sonner';
|
||
|
|
|
||
|
|
interface SupportTabProps {
|
||
|
|
user: User;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function SupportTab({ user }: SupportTabProps) {
|
||
|
|
const [isPending, startTransition] = useTransition();
|
||
|
|
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
||
|
|
|
||
|
|
// Form State
|
||
|
|
const [subject, setSubject] = useState('');
|
||
|
|
const [priority, setPriority] = useState('Normal');
|
||
|
|
const [type, setType] = useState('Inquiry');
|
||
|
|
|
||
|
|
const handleCreateTicket = () => {
|
||
|
|
startTransition(async () => {
|
||
|
|
const result = await createSupportTicket(user.id, { subject, priority, type });
|
||
|
|
if (result.success) {
|
||
|
|
toast.success(result.message);
|
||
|
|
setCreateDialogOpen(false);
|
||
|
|
setSubject('');
|
||
|
|
} else {
|
||
|
|
toast.error(result.message);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<h3 className="text-sm font-semibold">Communication Timeline</h3>
|
||
|
|
|
||
|
|
<Dialog open={createDialogOpen} onOpenChange={setCreateDialogOpen}>
|
||
|
|
<DialogTrigger asChild>
|
||
|
|
<Button size="sm" className="h-8 gap-2">
|
||
|
|
<Plus className="h-3.5 w-3.5" /> Create Ticket
|
||
|
|
</Button>
|
||
|
|
</DialogTrigger>
|
||
|
|
<DialogContent>
|
||
|
|
<DialogHeader>
|
||
|
|
<DialogTitle>New Support Ticket</DialogTitle>
|
||
|
|
</DialogHeader>
|
||
|
|
<div className="grid gap-4 py-4">
|
||
|
|
<div className="grid gap-2">
|
||
|
|
<Label>Subject</Label>
|
||
|
|
<Input
|
||
|
|
value={subject}
|
||
|
|
onChange={e => setSubject(e.target.value)}
|
||
|
|
placeholder="Brief summary of the issue"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div className="grid grid-cols-2 gap-4">
|
||
|
|
<div className="grid gap-2">
|
||
|
|
<Label>Type</Label>
|
||
|
|
<Select value={type} onValueChange={setType}>
|
||
|
|
<SelectTrigger><SelectValue /></SelectTrigger>
|
||
|
|
<SelectContent>
|
||
|
|
<SelectItem value="Inquiry">Inquiry</SelectItem>
|
||
|
|
<SelectItem value="Refund">Refund Request</SelectItem>
|
||
|
|
<SelectItem value="Technical">Technical Issue</SelectItem>
|
||
|
|
<SelectItem value="Complaint">Complaint</SelectItem>
|
||
|
|
</SelectContent>
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
<div className="grid gap-2">
|
||
|
|
<Label>Priority</Label>
|
||
|
|
<Select value={priority} onValueChange={setPriority}>
|
||
|
|
<SelectTrigger><SelectValue /></SelectTrigger>
|
||
|
|
<SelectContent>
|
||
|
|
<SelectItem value="Low">Low</SelectItem>
|
||
|
|
<SelectItem value="Normal">Normal</SelectItem>
|
||
|
|
<SelectItem value="High">High</SelectItem>
|
||
|
|
<SelectItem value="Urgent">Urgent</SelectItem>
|
||
|
|
</SelectContent>
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<DialogFooter>
|
||
|
|
<Button variant="outline" onClick={() => setCreateDialogOpen(false)}>Cancel</Button>
|
||
|
|
<Button onClick={handleCreateTicket} disabled={!subject || isPending}>
|
||
|
|
{isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||
|
|
Create Ticket
|
||
|
|
</Button>
|
||
|
|
</DialogFooter>
|
||
|
|
</DialogContent>
|
||
|
|
</Dialog>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Timeline Stream */}
|
||
|
|
<div className="relative pl-4 border-l-2 border-border/50 space-y-6">
|
||
|
|
{[
|
||
|
|
{ id: 1, type: 'ticket', title: 'Refund requested for "Tech Summit"', status: 'Open', date: '2 hours ago', icon: Headphones, color: 'text-blue-600 bg-blue-50' },
|
||
|
|
{ id: 2, type: 'email', title: 'Sent: "Your tickets are ready!"', status: 'Delivered', date: 'Yesterday', icon: Mail, color: 'text-slate-600 bg-slate-50' },
|
||
|
|
{ id: 3, type: 'ticket', title: 'Login issue reported', status: 'Resolved', date: '3 days ago', icon: MessageSquare, color: 'text-emerald-600 bg-emerald-50' },
|
||
|
|
].map((item) => (
|
||
|
|
<div key={item.id} className="relative">
|
||
|
|
<div className={`absolute -left-[25px] top-0 h-8 w-8 rounded-full border-2 border-background flex items-center justify-center ${item.color}`}>
|
||
|
|
<item.icon className="h-4 w-4" />
|
||
|
|
</div>
|
||
|
|
<div className="bg-card border rounded-lg p-3 shadow-sm hover:shadow-md transition-shadow">
|
||
|
|
<div className="flex justify-between items-start">
|
||
|
|
<h4 className="text-sm font-medium">{item.title}</h4>
|
||
|
|
<span className="text-xs text-muted-foreground whitespace-nowrap">{item.date}</span>
|
||
|
|
</div>
|
||
|
|
<div className="mt-1 flex items-center gap-2">
|
||
|
|
<span className="text-xs font-medium px-2 py-0.5 rounded-full bg-secondary/50 text-secondary-foreground">
|
||
|
|
{item.type === 'ticket' ? 'Ticket #1234' : 'Email System'}
|
||
|
|
</span>
|
||
|
|
<span className="text-xs text-muted-foreground">• {item.status}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|