diff --git a/assets/admin/pages/PatientDetailPage.test.tsx b/assets/admin/pages/PatientDetailPage.test.tsx index 5467e88a..ffca079d 100644 --- a/assets/admin/pages/PatientDetailPage.test.tsx +++ b/assets/admin/pages/PatientDetailPage.test.tsx @@ -59,11 +59,14 @@ describe('PatientDetailPage (پرونده تب‌دار)', () => { expect(screen.getByText('اینستاگرام')).toBeInTheDocument(); }); - it('shows a placeholder for not-yet-built tabs', async () => { + it('renders the call-center tab with a register form and history', async () => { renderDetail(); await screen.findByText('ساغر صابری'); fireEvent.click(screen.getByText('کال سنتر')); - expect(screen.getByText(/به‌زودی تکمیل می‌شود/)).toBeInTheDocument(); + expect(await screen.findByText('ثبت تماس جدید')).toBeInTheDocument(); + expect(screen.getByText('تاریخچه تماس‌ها')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('موضوع تماس')).toBeInTheDocument(); + expect(await screen.findByText('تماسی ثبت نشده است.')).toBeInTheDocument(); }); it('renders the attachments tab with an upload button', async () => { diff --git a/assets/admin/pages/PatientDetailPage.tsx b/assets/admin/pages/PatientDetailPage.tsx index 760ed093..e68c39e2 100644 --- a/assets/admin/pages/PatientDetailPage.tsx +++ b/assets/admin/pages/PatientDetailPage.tsx @@ -146,6 +146,8 @@ export default function PatientDetailPage() { row={(p) => ({ title: formatRial(p.amount_rials), meta: [p.created_at ? formatDate(p.created_at) : '', p.gateway].filter(Boolean).join(' · '), badge: PAYMENT_STATUS[p.status] || p.status })} /> ) : tab === 'wallet' ? ( + ) : tab === 'callcenter' ? ( + ) : tab === 'attach' ? ( ) : tab === 'records' ? ( @@ -403,6 +405,116 @@ function MessagesTab({ uuid }: { uuid: string }) { ); } +interface Call { uuid: string; subject: string; summary?: string | null; outcome: string; called_at: number; personnel?: string | null } + +/** کال سنتر — patient call log: register a call + filterable history (all / success / missed). */ +function CallCenterTab({ uuid }: { uuid: string }) { + const qc = useQueryClient(); + const userName = useAuthStore((s) => s.userName); + const [filter, setFilter] = useState<'all' | 'success' | 'missed'>('all'); + const [date, setDate] = useState(''); + const [time, setTime] = useState(''); + const [subject, setSubject] = useState(''); + const [summary, setSummary] = useState(''); + const [outcome, setOutcome] = useState<'success' | 'missed'>('success'); + + const { data, isLoading } = useQuery>({ + queryKey: ['patient-calls', uuid], + queryFn: () => api.get(`/api/v1/patient/${uuid}/calls`), + }); + const calls = data?.data ?? []; + const shown = filter === 'all' ? calls : calls.filter((c) => c.outcome === filter); + const successCount = calls.filter((c) => c.outcome === 'success').length; + const missedCount = calls.filter((c) => c.outcome === 'missed').length; + const invalidate = () => qc.invalidateQueries({ queryKey: ['patient-calls', uuid] }); + + const create = useMutation({ + mutationFn: () => { + const iso = date ? `${date}T${time || '00:00'}` : null; + const calledAt = iso ? Math.floor(new Date(iso).getTime() / 1000) : Math.floor(Date.now() / 1000); + return api.post(`/api/v1/patient/${uuid}/call`, { subject, summary, outcome, called_at: calledAt, personnel: userName }); + }, + onSuccess: () => { invalidate(); setDate(''); setTime(''); setSubject(''); setSummary(''); setOutcome('success'); toast.success('تماس ثبت شد'); }, + onError: (e: any) => toast.error(e.message), + }); + const del = useMutation({ + mutationFn: (u: string) => api.delete(`/api/v1/patient/call/${u}`), + onSuccess: () => { invalidate(); toast.success('تماس حذف شد'); }, + onError: (e: any) => toast.error(e.message), + }); + + const chip = (key: 'all' | 'success' | 'missed', label: string) => ( + + ); + + return ( +
+ {/* register form */} +
+
ثبت تماس جدید
+ +
+ +
setTime(e.target.value)} dir="ltr" />
+ +
setSubject(e.target.value)} placeholder="موضوع تماس" />
+ +