task-d complate
This commit is contained in:
150
src/features/crm/enquiries/components/enquiry-followups-tab.tsx
Normal file
150
src/features/crm/enquiries/components/enquiry-followups-tab.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import { AlertModal } from '@/components/modal/alert-modal';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { deleteEnquiryFollowupMutation } from '../api/mutations';
|
||||
import { enquiryFollowupsOptions } from '../api/queries';
|
||||
import type { EnquiryReferenceData } from '../api/types';
|
||||
import { FollowupFormSheet } from './followup-form-sheet';
|
||||
|
||||
export function EnquiryFollowupsTab({
|
||||
enquiryId,
|
||||
customerId,
|
||||
referenceData,
|
||||
canCreate,
|
||||
canUpdate,
|
||||
canDelete
|
||||
}: {
|
||||
enquiryId: string;
|
||||
customerId: string;
|
||||
referenceData: EnquiryReferenceData;
|
||||
canCreate: boolean;
|
||||
canUpdate: boolean;
|
||||
canDelete: boolean;
|
||||
}) {
|
||||
const { data } = useSuspenseQuery(enquiryFollowupsOptions(enquiryId));
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||
const selected = data.items.find((item) => item.id === selectedId);
|
||||
const followupTypeMap = new Map(referenceData.followupTypes.map((item) => [item.id, item.label]));
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
...deleteEnquiryFollowupMutation,
|
||||
onSuccess: () => {
|
||||
toast.success('Follow-up deleted successfully');
|
||||
setDeleteOpen(false);
|
||||
setSelectedId(null);
|
||||
},
|
||||
onError: (error) =>
|
||||
toast.error(error instanceof Error ? error.message : 'Failed to delete follow-up')
|
||||
});
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className='flex flex-row items-start justify-between gap-4'>
|
||||
<div>
|
||||
<CardTitle>Follow-ups</CardTitle>
|
||||
<CardDescription>
|
||||
Track the latest contact moments, outcomes, and next steps for this enquiry.
|
||||
</CardDescription>
|
||||
</div>
|
||||
{canCreate ? (
|
||||
<Button
|
||||
size='sm'
|
||||
onClick={() => {
|
||||
setSelectedId(null);
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<Icons.add className='mr-2 h-4 w-4' /> Add Follow-up
|
||||
</Button>
|
||||
) : null}
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
{!data.items.length ? (
|
||||
<div className='text-muted-foreground rounded-lg border border-dashed p-8 text-center text-sm'>
|
||||
No follow-ups recorded yet.
|
||||
</div>
|
||||
) : (
|
||||
data.items.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className='flex flex-col gap-3 rounded-lg border p-4 md:flex-row md:items-start md:justify-between'
|
||||
>
|
||||
<div className='space-y-2'>
|
||||
<div className='flex flex-wrap items-center gap-2'>
|
||||
<Badge variant='outline'>
|
||||
{followupTypeMap.get(item.followupType) ?? 'Unknown type'}
|
||||
</Badge>
|
||||
<span className='text-sm font-medium'>
|
||||
{new Date(item.followupDate).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
{item.outcome ? <p className='text-sm'>{item.outcome}</p> : null}
|
||||
{item.nextAction ? (
|
||||
<p className='text-muted-foreground text-sm'>Next action: {item.nextAction}</p>
|
||||
) : null}
|
||||
{item.nextFollowupDate ? (
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
Next follow-up: {new Date(item.nextFollowupDate).toLocaleDateString()}
|
||||
</p>
|
||||
) : null}
|
||||
{item.notes ? <p className='text-muted-foreground text-sm'>{item.notes}</p> : null}
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
disabled={!canUpdate}
|
||||
onClick={() => {
|
||||
setSelectedId(item.id);
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<Icons.edit className='mr-2 h-4 w-4' /> Edit
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
disabled={!canDelete}
|
||||
onClick={() => {
|
||||
setSelectedId(item.id);
|
||||
setDeleteOpen(true);
|
||||
}}
|
||||
>
|
||||
<Icons.trash className='mr-2 h-4 w-4' /> Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</CardContent>
|
||||
|
||||
<FollowupFormSheet
|
||||
enquiryId={enquiryId}
|
||||
followup={selected}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
referenceData={referenceData}
|
||||
customerId={customerId}
|
||||
/>
|
||||
<AlertModal
|
||||
isOpen={deleteOpen}
|
||||
onClose={() => setDeleteOpen(false)}
|
||||
onConfirm={() => {
|
||||
if (selectedId) {
|
||||
deleteMutation.mutate({ enquiryId, followupId: selectedId });
|
||||
}
|
||||
}}
|
||||
loading={deleteMutation.isPending}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user