Refactor doctor name handling across the application

- Removed the "دکتر" prefix from doctor names in various components and API responses to ensure consistency and clarity.
- Updated the AppointmentDetailPage, CommentsPage, DashboardPage, RatingsPage, SecretariesPage, and other relevant files to reflect the changes in doctor name formatting.
- Adjusted API documentation to align with the new naming conventions.
- Implemented validation to prevent the creation of clinics without a name and restricted users to a single clinic.
- Added tests to verify that doctor names are stored without titles and that clinic creation adheres to the new validation rules.
This commit is contained in:
hamed
2026-07-19 16:09:55 +03:30
parent 7761c37a3e
commit b05aeaf58b
30 changed files with 260 additions and 71 deletions
+1 -1
View File
@@ -142,7 +142,7 @@ export default function AppointmentDetailPage() {
<h3 className="font-semibold text-gray-800 mb-4">اطلاعات بیمار</h3>
<InfoRow label="نام بیمار" value={appt.patient_name} />
<InfoRow label="موبایل" value={<span dir="ltr">{appt.patient_mobile}</span>} />
<InfoRow label="پزشک" value={appt.doctor?.name ? `دکتر ${appt.doctor.name}` : null} />
<InfoRow label="پزشک" value={appt.doctor?.name ?? null} />
<InfoRow label="تاریخ نوبت" value={formatDate(appt.slot_start)} />
<InfoRow label="ساعت شروع" value={timeOf(appt.slot_start)} />
<InfoRow label="ساعت پایان" value={timeOf(appt.slot_end)} />
+1 -1
View File
@@ -62,7 +62,7 @@ export default function CommentsPage() {
header: 'کاربر',
render: (c) => <b>{c.patient_name}</b>,
},
{ key: 'doctor_name', header: 'پزشک', render: (c) => `دکتر ${c.doctor_name}` },
{ key: 'doctor_name', header: 'پزشک', render: (c) => c.doctor_name },
{ key: 'title', header: 'عنوان' },
{
key: 'body',
+2 -2
View File
@@ -400,7 +400,7 @@ function AdminDashboard() {
if (!recent) return [];
const evs: { title: string; sub: string; time: string; color: string }[] = [];
(recent.appointments ?? []).slice(0, 4).forEach(a => {
evs.push({ title: `نوبت ${APPT_LABEL[a.status] ?? a.status}`, sub: `${a.user_name || a.user_mobile} · دکتر ${a.doctor_name}`, time: a.created_at, color: APPT_COLOR[a.status] ?? '#94a3b8' });
evs.push({ title: `نوبت ${APPT_LABEL[a.status] ?? a.status}`, sub: `${a.user_name || a.user_mobile} · ${a.doctor_name}`, time: a.created_at, color: APPT_COLOR[a.status] ?? '#94a3b8' });
});
(recent.payments ?? []).slice(0, 3).forEach(p => {
evs.push({ title: `پرداخت ${PAY_LABEL[p.status] ?? p.status}`, sub: `${p.user_name || p.user_mobile} · ${formatRial(p.amount)}`, time: p.created_at, color: PAY_COLOR[p.status] ?? '#94a3b8' });
@@ -413,7 +413,7 @@ function AdminDashboard() {
const apptRows = useMemo<MiniRow[]>(() =>
(recent?.appointments ?? []).slice(0, 5).map(a => ({
title: a.user_name || a.user_mobile, sub: `دکتر ${a.doctor_name}`, meta: formatDateTime(a.slot_start),
title: a.user_name || a.user_mobile, sub: a.doctor_name, meta: formatDateTime(a.slot_start),
badgeLabel: APPT_LABEL[a.status] ?? a.status, badgeCls: APPT_CLS[a.status] ?? 'gray',
initials: (a.user_name || a.user_mobile).slice(0, 2), hue: 222,
})), [recent]);
+2 -2
View File
@@ -52,7 +52,7 @@ export default function RatingsPage() {
const columns: Column<Rating>[] = [
{ key: 'patient_name', header: 'بیمار', render: (r) => <b>{r.patient_name}</b> },
{ key: 'doctor_name', header: 'پزشک', render: (r) => `دکتر ${r.doctor_name}` },
{ key: 'doctor_name', header: 'پزشک', render: (r) => r.doctor_name },
{ key: 'overall', header: 'کلی', render: (r) => <Stars value={r.overall} /> },
{
key: 'ratings_detail',
@@ -111,7 +111,7 @@ export default function RatingsPage() {
<ConfirmDialog
open={!!deleteTarget}
title="حذف امتیاز"
message={`آیا از حذف امتیاز ${deleteTarget?.patient_name} برای دکتر ${deleteTarget?.doctor_name} اطمینان دارید؟`}
message={`آیا از حذف امتیاز ${deleteTarget?.patient_name} برای ${deleteTarget?.doctor_name} اطمینان دارید؟`}
confirmLabel="حذف"
danger
loading={deleteMutation.isPending}
+1 -1
View File
@@ -186,7 +186,7 @@ export default function SecretariesPage() {
const columns: Column<Secretary>[] = [
{ key: 'user_name', header: 'نام', render: (s) => <b>{s.user_name}</b> },
{ key: 'mobile_number', header: 'موبایل', render: (s) => <span dir="ltr">{maskMobile(s.mobile_number)}</span> },
{ key: 'doctor_name', header: 'پزشک', render: (s) => `دکتر ${s.doctor_name}` },
{ key: 'doctor_name', header: 'پزشک', render: (s) => s.doctor_name },
{ key: 'is_active', header: 'وضعیت', render: (s) => <ActiveBadge active={s.is_active} /> },
{ key: 'created_at', header: 'تاریخ ثبت', render: (s) => formatDate(s.created_at) },
];