feat(representation): add representation management for doctors, including attach and update functionality
This commit is contained in:
@@ -2353,6 +2353,10 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
const [deletingAddrId, setDeletingAddrId] = useState<string | null>(null);
|
||||
const [editGender, setEditGender] = useState<'man' | 'woman' | ''>('');
|
||||
const [editActivityDate, setEditActivityDate] = useState('');
|
||||
const [repModalOpen, setRepModalOpen] = useState(false);
|
||||
const [selRepId, setSelRepId] = useState<number | null>(null);
|
||||
|
||||
const isAdmin = primaryRole === 'admin';
|
||||
|
||||
// ── Queries ──
|
||||
|
||||
@@ -2476,6 +2480,29 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
onError: (e: Error) => toast.error(e.message),
|
||||
});
|
||||
|
||||
const repsQuery = useQuery({
|
||||
queryKey: ['representations-select'],
|
||||
queryFn: () => api.get<ApiResponse<any>>('/api/v1/admin/representations?limit=200'),
|
||||
enabled: isAdmin && repModalOpen,
|
||||
staleTime: 60_000,
|
||||
});
|
||||
const repOptions: { value: number; label: string }[] = useMemo(() => {
|
||||
const rows = repsQuery.data?.data ?? [];
|
||||
return (rows as any[]).map((r) => ({ value: r.id as number, label: `${r.full_name}${r.city ? ` — ${r.city}` : ''}` }));
|
||||
}, [repsQuery.data]);
|
||||
|
||||
const setRepMut = useMutation({
|
||||
mutationFn: (representationId: number | null) =>
|
||||
api.put<ApiResponse<any>>(`/api/v1/admin/doctors/${uuid}/representation`, { representation_id: representationId }),
|
||||
onSuccess: () => {
|
||||
toast.success('نمایندهی پزشک بهروزرسانی شد');
|
||||
setRepModalOpen(false);
|
||||
qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
|
||||
qc.invalidateQueries({ queryKey: ['admin-doctors'] });
|
||||
},
|
||||
onError: (e: Error) => toast.error(e.message),
|
||||
});
|
||||
|
||||
const deleteAddrMut = useMutation({
|
||||
mutationFn: (id: string) => api.delete<ApiResponse<null>>(`/api/v1/clinic-pro/doctor-address/${id}`),
|
||||
onSuccess: () => {
|
||||
@@ -2703,9 +2730,19 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
<InfoCard
|
||||
icon={UserIcon}
|
||||
label="نماینده"
|
||||
value={doctor.representation
|
||||
? <button type="button" style={{ color: '#2563eb', textDecoration: 'underline', background: 'none', border: 0, padding: 0, cursor: 'pointer', font: 'inherit' }} onClick={() => navigate(`/admin/representations/${doctor.representation!.uuid}`)}>{doctor.representation.full_name ?? 'نماینده'}</button>
|
||||
: <span className="text-slate-400">بدون نماینده</span>}
|
||||
value={
|
||||
<span className="flex items-center gap-2">
|
||||
{doctor.representation
|
||||
? <button type="button" style={{ color: '#2563eb', textDecoration: 'underline', background: 'none', border: 0, padding: 0, cursor: 'pointer', font: 'inherit' }} onClick={() => navigate(`/admin/representations/${doctor.representation!.uuid}`)}>{doctor.representation.full_name ?? 'نماینده'}</button>
|
||||
: <span className="text-slate-400">بدون نماینده</span>}
|
||||
{isAdmin && (
|
||||
<button type="button" className="btn ghost sm"
|
||||
onClick={() => { setSelRepId(doctor.representation?.id ?? null); setRepModalOpen(true); }}>
|
||||
{doctor.representation ? 'تغییر' : 'افزودن نماینده'}
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3042,6 +3079,28 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
confirmLabel="بله، حذف کن" danger loading={deleteMut.isPending}
|
||||
onConfirm={() => deleteMut.mutate()} onCancel={() => setDeleteOpen(false)}
|
||||
/>
|
||||
|
||||
<Modal open={repModalOpen} title="نمایندهی پزشک" size="md" onClose={() => setRepModalOpen(false)}
|
||||
footer={
|
||||
<>
|
||||
<button className="btn ghost sm" onClick={() => setRepModalOpen(false)}>لغو</button>
|
||||
{doctor.representation && (
|
||||
<button className="btn danger sm" disabled={setRepMut.isPending}
|
||||
onClick={() => setRepMut.mutate(null)}>حذف نماینده</button>
|
||||
)}
|
||||
<button className="btn primary sm" disabled={setRepMut.isPending || !selRepId}
|
||||
onClick={() => setRepMut.mutate(selRepId)}>ذخیره</button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<label className="block text-sm text-slate-600 mb-2">انتخاب نماینده</label>
|
||||
<GlobalSearchableSelect
|
||||
options={repOptions}
|
||||
value={selRepId}
|
||||
onChange={(v) => setSelRepId(v === null || v === '' ? null : Number(v))}
|
||||
placeholder={repsQuery.isLoading ? 'در حال بارگذاری…' : 'جستجوی نماینده…'}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user