task-c.1
This commit is contained in:
173
src/features/crm/customers/components/contact-share-sheet.tsx
Normal file
173
src/features/crm/customers/components/contact-share-sheet.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import {
|
||||
shareCustomerContactMutation,
|
||||
unshareCustomerContactMutation
|
||||
} from '../api/mutations';
|
||||
import type { CustomerContactRecord, CustomerReferenceData } from '../api/types';
|
||||
import { formatDateTime } from '@/lib/format';
|
||||
|
||||
export function ContactShareSheet({
|
||||
customerId,
|
||||
contact,
|
||||
referenceData,
|
||||
open,
|
||||
onOpenChange,
|
||||
canManage
|
||||
}: {
|
||||
customerId: string;
|
||||
contact: CustomerContactRecord | null;
|
||||
referenceData: CustomerReferenceData;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
canManage: boolean;
|
||||
}) {
|
||||
const [sharedToUserId, setSharedToUserId] = useState('');
|
||||
const [remark, setRemark] = useState('');
|
||||
const existingSharedIds = useMemo(
|
||||
() => new Set(contact?.shares.map((item) => item.sharedToUserId) ?? []),
|
||||
[contact]
|
||||
);
|
||||
const candidates = useMemo(
|
||||
() => referenceData.crmUsers.filter((user) => !existingSharedIds.has(user.id)),
|
||||
[existingSharedIds, referenceData.crmUsers]
|
||||
);
|
||||
|
||||
const shareMutation = useMutation({
|
||||
...shareCustomerContactMutation,
|
||||
onSuccess: () => {
|
||||
toast.success('แชร์ contact เรียบร้อย');
|
||||
setSharedToUserId('');
|
||||
setRemark('');
|
||||
},
|
||||
onError: (error) =>
|
||||
toast.error(error instanceof Error ? error.message : 'ไม่สามารถแชร์ contact ได้')
|
||||
});
|
||||
const unshareMutation = useMutation({
|
||||
...unshareCustomerContactMutation,
|
||||
onSuccess: () => {
|
||||
toast.success('ยกเลิกการแชร์เรียบร้อย');
|
||||
},
|
||||
onError: (error) =>
|
||||
toast.error(error instanceof Error ? error.message : 'ไม่สามารถยกเลิกการแชร์ได้')
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Shared With</DialogTitle>
|
||||
<DialogDescription>จัดการผู้ใช้ที่เข้าถึง contact นี้ได้</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className='space-y-4'>
|
||||
{!contact?.shares.length ? (
|
||||
<div className='text-muted-foreground text-sm'>ยังไม่มีการแชร์ contact นี้</div>
|
||||
) : (
|
||||
<div className='space-y-2'>
|
||||
{contact.shares.map((share) => (
|
||||
<div
|
||||
key={share.id}
|
||||
className='flex items-start justify-between gap-3 rounded-lg border p-3'
|
||||
>
|
||||
<div className='space-y-1 text-sm'>
|
||||
<div className='font-medium'>{share.sharedToName ?? share.sharedToUserId}</div>
|
||||
<div className='text-muted-foreground text-xs'>
|
||||
{formatDateTime(share.sharedAt)} โดย {share.sharedByName ?? share.sharedByUserId}
|
||||
</div>
|
||||
{share.remark ? <div className='text-xs'>{share.remark}</div> : null}
|
||||
</div>
|
||||
{canManage ? (
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() =>
|
||||
unshareMutation.mutate({
|
||||
customerId,
|
||||
contactId: contact.id,
|
||||
shareId: share.id
|
||||
})
|
||||
}
|
||||
>
|
||||
<Icons.trash className='mr-2 h-4 w-4' />
|
||||
Remove
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{canManage ? (
|
||||
<div className='space-y-3 border-t pt-4'>
|
||||
<Select value={sharedToUserId} onValueChange={setSharedToUserId}>
|
||||
<SelectTrigger aria-label='Shared user'>
|
||||
<SelectValue placeholder='Select user' />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{candidates.map((user) => (
|
||||
<SelectItem key={user.id} value={user.id}>
|
||||
{user.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Textarea
|
||||
value={remark}
|
||||
onChange={(event) => setRemark(event.target.value)}
|
||||
placeholder='Remark'
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant='outline' onClick={() => onOpenChange(false)}>
|
||||
ปิด
|
||||
</Button>
|
||||
{canManage ? (
|
||||
<Button
|
||||
isLoading={shareMutation.isPending}
|
||||
disabled={!contact || !sharedToUserId}
|
||||
onClick={() => {
|
||||
if (!contact) {
|
||||
return;
|
||||
}
|
||||
|
||||
shareMutation.mutate({
|
||||
customerId,
|
||||
contactId: contact.id,
|
||||
values: {
|
||||
sharedToUserId,
|
||||
remark
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icons.check className='mr-2 h-4 w-4' />
|
||||
Share
|
||||
</Button>
|
||||
) : null}
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user