Files
clinicpro/assets/admin/components/ui/InviteDoctorModal.tsx
T
hamed 74577c2ff6 feat: unify doctor title handling and enhance specialty selection
- Implemented a helper function `displayDoctorName` to prepend "دکتر" to doctor names for consistent display across the application.
- Updated various components (InviteDoctorModal, DashboardPage, DoctorDetailPage, DoctorsPage, etc.) to utilize the new helper for rendering doctor names.
- Modified the DoctorFormPage to automatically add the "دکتر" title in the UI without requiring user input.
- Fixed the EditSpecialtyPicker component to allow multiple specialty selections, resolving a UI bug where only one specialty could be selected at a time.
- Ensured that the backend strips the "دکتر" title from the name during pre-registration and doctor creation processes.
- Added tests for the new functionality, including checks for title handling and specialty selection logic.
- Updated API documentation to reflect changes in name handling and display logic.
2026-07-19 19:57:03 +03:30

79 lines
3.3 KiB
TypeScript

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { useMutation } from '@tanstack/react-query';
import { XMarkIcon } from '@heroicons/react/24/outline';
import { toast } from 'sonner';
import { api } from '../../lib/api';
import type { ApiResponse } from '../../lib/api';
import MobileInput from './MobileInput';
import Portal from './Portal';
import { iranMobileSchema } from '../../lib/utils';
const inviteSchema = z.object({
mobile: iranMobileSchema,
name: z.string().optional(),
specialty: z.string().optional(),
});
type InviteForm = z.infer<typeof inviteSchema>;
interface Props {
clinicUuid: string;
onClose: () => void;
onInvited?: () => void;
}
export default function InviteDoctorModal({ clinicUuid, onClose, onInvited }: Props) {
const { register, handleSubmit, formState: { errors } } = useForm<InviteForm>({
resolver: zodResolver(inviteSchema),
});
const inviteMut = useMutation({
mutationFn: (d: InviteForm) =>
api.post<ApiResponse<unknown>>(`/api/v1/admin/clinic/${clinicUuid}/invite-doctor`, d),
onSuccess: () => {
toast.success('دعوتنامه ارسال شد');
onInvited?.();
onClose();
},
onError: (e: Error) => toast.error(e.message),
});
return (
<Portal>
<div className="overlay" onClick={onClose}>
<div className="modal" style={{ maxWidth: 420 }} onClick={e => e.stopPropagation()}>
<div className="modal-head">
<b>دعوت پزشک به کلینیک</b>
<button className="mini-btn" onClick={onClose}><XMarkIcon style={{ width: 16, height: 16 }} /></button>
</div>
<form onSubmit={handleSubmit(d => inviteMut.mutate(d))}>
<div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div>
<label style={{ fontSize: 13, fontWeight: 600, display: 'block', marginBottom: 6 }}>شماره موبایل پزشک *</label>
<MobileInput className="input" hasError={!!errors.mobile} {...register('mobile')} />
{errors.mobile && <div className="err-text">{errors.mobile.message}</div>}
</div>
<div>
<label style={{ fontSize: 13, fontWeight: 600, display: 'block', marginBottom: 6 }}>نام پزشک (اختیاری)</label>
<input className="input" placeholder="نام و نام خانوادگی" {...register('name')} />
</div>
<div>
<label style={{ fontSize: 13, fontWeight: 600, display: 'block', marginBottom: 6 }}>تخصص (اختیاری)</label>
<input className="input" placeholder="مثال: قلب و عروق" {...register('specialty')} />
</div>
<p className="muted" style={{ fontSize: 12 }}>پیامک دعوتنامه با لینک ۷۲ ساعته ارسال می‌شود</p>
</div>
<div className="modal-foot">
<button type="button" className="btn ghost sm" onClick={onClose}>انصراف</button>
<button type="submit" className="btn primary sm" disabled={inviteMut.isPending}>
{inviteMut.isPending ? 'در حال ارسال...' : 'ارسال دعوتنامه'}
</button>
</div>
</form>
</div>
</div>
</Portal>
);
}