feat(representation): add welcome SMS notification for newly added doctors and clinics by representatives
This commit is contained in:
@@ -629,6 +629,8 @@ export default function AppointmentsPage() {
|
||||
|
||||
// ── Handle slot click → open booking modal
|
||||
function handleSlotClick(slot: SlotItem) {
|
||||
// نماینده اجازهی ثبت نوبت ندارد؛ صفحه برای او فقط مشاهده است.
|
||||
if (isRepresentation) return;
|
||||
const doctorName = doctors.find(d => d.uuid === selectedDoctorUuid)?.name ?? '';
|
||||
setBookingHint(false);
|
||||
setBookingSlot({
|
||||
@@ -677,19 +679,21 @@ export default function AppointmentsPage() {
|
||||
padding: '12px 16px', borderBottom: '1px solid var(--border)',
|
||||
display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
|
||||
}}>
|
||||
{/* New appointment button */}
|
||||
<button
|
||||
className="btn primary sm"
|
||||
onClick={() => {
|
||||
if (!selectedDoctorUuid) { toast.error('ابتدا یک پزشک انتخاب کنید'); return; }
|
||||
setViewMode('schedule');
|
||||
setBookingHint(true);
|
||||
}}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 5 }}
|
||||
>
|
||||
<PlusIcon style={{ width: 15, height: 15 }} />
|
||||
نوبت جدید
|
||||
</button>
|
||||
{/* New appointment button — نماینده اجازهی ثبت ندارد */}
|
||||
{!isRepresentation && (
|
||||
<button
|
||||
className="btn primary sm"
|
||||
onClick={() => {
|
||||
if (!selectedDoctorUuid) { toast.error('ابتدا یک پزشک انتخاب کنید'); return; }
|
||||
setViewMode('schedule');
|
||||
setBookingHint(true);
|
||||
}}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 5 }}
|
||||
>
|
||||
<PlusIcon style={{ width: 15, height: 15 }} />
|
||||
نوبت جدید
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* View toggle */}
|
||||
<div style={{
|
||||
@@ -752,7 +756,7 @@ export default function AppointmentsPage() {
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{bookingHint && (
|
||||
{bookingHint && !isRepresentation && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12,
|
||||
padding: '10px 16px', borderRadius: 'var(--r-sm)',
|
||||
|
||||
@@ -975,8 +975,8 @@ function AddressModal({ open, onClose, existing, doctorUuid, onSaved }: {
|
||||
|
||||
// ── Address Card ───────────────────────────────────────────────────────────
|
||||
|
||||
function AddressCard({ addr, onEdit, onDelete }: {
|
||||
addr: AddressData; onEdit: () => void; onDelete: () => void;
|
||||
function AddressCard({ addr, onEdit, onDelete, readOnly = false }: {
|
||||
addr: AddressData; onEdit: () => void; onDelete: () => void; readOnly?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-start gap-3 p-4 rounded-xl bg-slate-50 dark:bg-gray-800/60 border border-slate-100 dark:border-gray-700 group">
|
||||
@@ -1004,16 +1004,18 @@ function AddressCard({ addr, onEdit, onDelete }: {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="opacity-0 group-hover:opacity-100 flex items-center gap-1 transition-opacity shrink-0">
|
||||
<button onClick={onEdit}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
|
||||
<PencilIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button onClick={onDelete}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
||||
<TrashIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div className="opacity-0 group-hover:opacity-100 flex items-center gap-1 transition-opacity shrink-0">
|
||||
<button onClick={onEdit}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
|
||||
<PencilIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button onClick={onDelete}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
||||
<TrashIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1213,7 +1215,7 @@ function SessionEditor({ session, onChange, onRemove, addresses }: {
|
||||
|
||||
// ── Weekly Schedule Tab ────────────────────────────────────────────────────
|
||||
|
||||
function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addresses: AddressData[] }) {
|
||||
function WeeklyScheduleTab({ doctorUuid, addresses, readOnly = false }: { doctorUuid: string; addresses: AddressData[]; readOnly?: boolean }) {
|
||||
const qc = useQueryClient();
|
||||
const [scheduleMap, setScheduleMap] = useState<NewScheduleMap>(EMPTY_NEW_SCHEDULE);
|
||||
const [scheduleUuid, setScheduleUuid] = useState<string | null>(null);
|
||||
@@ -1315,6 +1317,48 @@ function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addr
|
||||
</div>
|
||||
);
|
||||
|
||||
// نمای فقطخواندنی برای نماینده: برنامهی هفتگی بهصورت متن، بدون فرم.
|
||||
if (readOnly) {
|
||||
if (scheduleQ.isLoading) {
|
||||
return <div className="space-y-2">{Array.from({ length: 4 }).map((_, i) => <div key={i} className="h-12 rounded-xl skeleton" />)}</div>;
|
||||
}
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{totalSlots > 0 && (
|
||||
<div className="flex items-center gap-2 mb-3 p-3 rounded-xl bg-emerald-50 dark:bg-emerald-500/10 border border-emerald-200 dark:border-emerald-500/30">
|
||||
<CalendarIcon className="w-4 h-4 text-emerald-600 dark:text-emerald-400 shrink-0" />
|
||||
<span className="text-sm text-emerald-700 dark:text-emerald-300">
|
||||
مجموع <strong>{totalSlots}</strong> نوبت در هفته
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{SCHEDULE_DAYS.map(day => {
|
||||
const activeSessions = (scheduleMap[day.key]?.sessions ?? []).filter(s => s.active);
|
||||
const daySlots = activeSessions.reduce((sum, s) => sum + calcSlotCount(s), 0);
|
||||
return (
|
||||
<div key={day.key} className="flex items-center gap-3 px-4 py-3 rounded-xl border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800/40">
|
||||
<b style={{ fontSize: 14, minWidth: 56, flexShrink: 0 }}>{day.label}</b>
|
||||
<div className="flex-1 flex gap-2 items-center flex-wrap min-w-0">
|
||||
{activeSessions.length > 0 ? activeSessions.map((s, i) => (
|
||||
<span key={i} className="text-xs px-2 py-1 rounded-lg bg-slate-100 dark:bg-gray-700 text-slate-700 dark:text-slate-200" style={{ fontFamily: 'monospace', direction: 'ltr', whiteSpace: 'nowrap' }}>
|
||||
{s.start_time} – {s.end_time}
|
||||
</span>
|
||||
)) : (
|
||||
<span className="muted text-xs">تعطیل</span>
|
||||
)}
|
||||
</div>
|
||||
{daySlots > 0 && (
|
||||
<span className="badge green" style={{ flexShrink: 0 }}>
|
||||
<span className="bdot" />{daySlots} نوبت
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{/* ─ خلاصه کل هفته */}
|
||||
@@ -1452,11 +1496,13 @@ function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addr
|
||||
{hasAnyOverlap ? 'تداخل زمانی در برنامه وجود دارد' : 'مکان مطب برای همه بازهها الزامی است'}
|
||||
</p>
|
||||
)}
|
||||
<button type="button" onClick={() => saveMut.mutate()}
|
||||
disabled={saveMut.isPending || hasAnyOverlap || missingLocation}
|
||||
className="btn primary sm" style={{ marginInlineStart: 'auto', opacity: (saveMut.isPending || hasAnyOverlap || missingLocation) ? 0.5 : 1 }}>
|
||||
{saveMut.isPending ? 'در حال ذخیره...' : 'ذخیره برنامه هفتگی'}
|
||||
</button>
|
||||
{!readOnly && (
|
||||
<button type="button" onClick={() => saveMut.mutate()}
|
||||
disabled={saveMut.isPending || hasAnyOverlap || missingLocation}
|
||||
className="btn primary sm" style={{ marginInlineStart: 'auto', opacity: (saveMut.isPending || hasAnyOverlap || missingLocation) ? 0.5 : 1 }}>
|
||||
{saveMut.isPending ? 'در حال ذخیره...' : 'ذخیره برنامه هفتگی'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1604,7 +1650,7 @@ function DateOverrideModal({ open, onClose, existing, doctorUuid, onSaved, addre
|
||||
|
||||
// ── Date Overrides Tab ─────────────────────────────────────────────────────
|
||||
|
||||
function DateOverridesTab({ doctorUuid, addresses }: { doctorUuid: string; addresses: AddressData[] }) {
|
||||
function DateOverridesTab({ doctorUuid, addresses, readOnly = false }: { doctorUuid: string; addresses: AddressData[]; readOnly?: boolean }) {
|
||||
const qc = useQueryClient();
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<DateOverrideData | null>(null);
|
||||
@@ -1643,12 +1689,14 @@ function DateOverridesTab({ doctorUuid, addresses }: { doctorUuid: string; addre
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-end">
|
||||
<button type="button" onClick={() => { setEditing(null); setModalOpen(true); }}
|
||||
className="flex items-center gap-1.5 text-sm cp-btn-primary">
|
||||
<PlusIcon className="w-4 h-4" />تاریخ خاص جدید
|
||||
</button>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div className="flex justify-end">
|
||||
<button type="button" onClick={() => { setEditing(null); setModalOpen(true); }}
|
||||
className="flex items-center gap-1.5 text-sm cp-btn-primary">
|
||||
<PlusIcon className="w-4 h-4" />تاریخ خاص جدید
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{overrides.length === 0 ? (
|
||||
<div className="text-center py-10 text-slate-400 dark:text-slate-500">
|
||||
<CalendarIcon className="w-10 h-10 mx-auto mb-2 opacity-30" />
|
||||
@@ -1673,16 +1721,18 @@ function DateOverridesTab({ doctorUuid, addresses }: { doctorUuid: string; addre
|
||||
</div>
|
||||
{ov.reason && <p className="text-xs text-slate-400 dark:text-slate-500 mt-0.5">{ov.reason}</p>}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button onClick={() => { setEditing(ov); setModalOpen(true); }}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
|
||||
<PencilIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button onClick={() => setDeletingUuid(ov.uuid)}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
||||
<TrashIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button onClick={() => { setEditing(ov); setModalOpen(true); }}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
|
||||
<PencilIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button onClick={() => setDeletingUuid(ov.uuid)}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
||||
<TrashIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -1772,7 +1822,7 @@ function HolidayModal({ open, onClose, existing, doctorUuid, onSaved }: {
|
||||
|
||||
// ── Holidays Tab ───────────────────────────────────────────────────────────
|
||||
|
||||
function HolidaysTab({ doctorUuid }: { doctorUuid: string }) {
|
||||
function HolidaysTab({ doctorUuid, readOnly = false }: { doctorUuid: string; readOnly?: boolean }) {
|
||||
const qc = useQueryClient();
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<HolidayData | null>(null);
|
||||
@@ -1805,12 +1855,14 @@ function HolidaysTab({ doctorUuid }: { doctorUuid: string }) {
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-end">
|
||||
<button type="button" onClick={() => { setEditing(null); setModalOpen(true); }}
|
||||
className="flex items-center gap-1.5 text-sm cp-btn-primary">
|
||||
<PlusIcon className="w-4 h-4" />تعطیلات جدید
|
||||
</button>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div className="flex justify-end">
|
||||
<button type="button" onClick={() => { setEditing(null); setModalOpen(true); }}
|
||||
className="flex items-center gap-1.5 text-sm cp-btn-primary">
|
||||
<PlusIcon className="w-4 h-4" />تعطیلات جدید
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{holidays.length === 0 ? (
|
||||
<div className="text-center py-10 text-slate-400 dark:text-slate-500">
|
||||
<CalendarIcon className="w-10 h-10 mx-auto mb-2 opacity-30" />
|
||||
@@ -1845,20 +1897,22 @@ function HolidaysTab({ doctorUuid }: { doctorUuid: string }) {
|
||||
</div>
|
||||
{h.reason && <p className="text-xs text-slate-400 dark:text-slate-500 mt-0.5">{h.reason}</p>}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button onClick={() => toggleMut.mutate(h)} disabled={toggleMut.isPending}
|
||||
className={`text-xs px-2.5 h-7 rounded-lg border transition-colors disabled:opacity-50 ${h.active ? 'border-slate-200 dark:border-gray-600 text-slate-500 dark:text-slate-400 hover:border-slate-300 hover:text-slate-700 dark:hover:text-slate-200' : 'border-emerald-200 dark:border-emerald-500/40 text-emerald-600 dark:text-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-500/10'}`}>
|
||||
{h.active ? 'غیرفعال' : 'فعال'}
|
||||
</button>
|
||||
<button onClick={() => { setEditing(h); setModalOpen(true); }}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
|
||||
<PencilIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button onClick={() => setDeletingUuid(h.uuid)}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
||||
<TrashIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button onClick={() => toggleMut.mutate(h)} disabled={toggleMut.isPending}
|
||||
className={`text-xs px-2.5 h-7 rounded-lg border transition-colors disabled:opacity-50 ${h.active ? 'border-slate-200 dark:border-gray-600 text-slate-500 dark:text-slate-400 hover:border-slate-300 hover:text-slate-700 dark:hover:text-slate-200' : 'border-emerald-200 dark:border-emerald-500/40 text-emerald-600 dark:text-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-500/10'}`}>
|
||||
{h.active ? 'غیرفعال' : 'فعال'}
|
||||
</button>
|
||||
<button onClick={() => { setEditing(h); setModalOpen(true); }}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
|
||||
<PencilIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button onClick={() => setDeletingUuid(h.uuid)}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
||||
<TrashIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -1882,7 +1936,7 @@ const SCHEDULE_TABS = [
|
||||
{ id: 'holidays' as const, label: 'تعطیلات' },
|
||||
];
|
||||
|
||||
function ScheduleSection({ doctorUuid }: { doctorUuid: string }) {
|
||||
function ScheduleSection({ doctorUuid, readOnly = false }: { doctorUuid: string; readOnly?: boolean }) {
|
||||
const [tab, setTab] = useState<'weekly' | 'overrides' | 'holidays'>('weekly');
|
||||
|
||||
const locationsQ = useQuery({
|
||||
@@ -1908,9 +1962,9 @@ function ScheduleSection({ doctorUuid }: { doctorUuid: string }) {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{tab === 'weekly' && <WeeklyScheduleTab doctorUuid={doctorUuid} addresses={availableLocations} />}
|
||||
{tab === 'overrides' && <DateOverridesTab doctorUuid={doctorUuid} addresses={availableLocations} />}
|
||||
{tab === 'holidays' && <HolidaysTab doctorUuid={doctorUuid} />}
|
||||
{tab === 'weekly' && <WeeklyScheduleTab doctorUuid={doctorUuid} addresses={availableLocations} readOnly={readOnly} />}
|
||||
{tab === 'overrides' && <DateOverridesTab doctorUuid={doctorUuid} addresses={availableLocations} readOnly={readOnly} />}
|
||||
{tab === 'holidays' && <HolidaysTab doctorUuid={doctorUuid} readOnly={readOnly} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2272,6 +2326,8 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
const dbUuid = useAuthStore(s => s.dbUuid);
|
||||
const doctorUuid = useAuthStore(s => s.doctorUuid);
|
||||
const uuid = isOwnProfile ? (doctorUuid ?? dbUuid ?? undefined) : paramUuid;
|
||||
// نماینده فقط مشاهده میکند؛ هیچ بخشی قابل ویرایش نیست.
|
||||
const isReadOnly = primaryRole === 'representation';
|
||||
|
||||
const [editOpen, setEditOpen] = useState(searchParams.get('edit') === '1');
|
||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||
@@ -2503,8 +2559,8 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<DoctorAvatar name={doctor.name} img={mainImage} idx={idNum}
|
||||
onUpload={primaryRole !== 'clinic' ? handleImageUpload : undefined} uploading={uploadingImg} />
|
||||
{primaryRole !== 'clinic' && (
|
||||
onUpload={primaryRole !== 'clinic' && !isReadOnly ? handleImageUpload : undefined} uploading={uploadingImg} />
|
||||
{primaryRole !== 'clinic' && !isReadOnly && (
|
||||
<span className="text-[10px] text-slate-400 dark:text-slate-500">کلیک برای تغییر عکس</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -2545,14 +2601,14 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 sm:mb-1 flex-wrap">
|
||||
{primaryRole !== 'clinic' && (
|
||||
{primaryRole !== 'clinic' && !isReadOnly && (
|
||||
<button onClick={() => setEditOpen(true)} className="cp-btn-primary text-sm">
|
||||
<PencilIcon className="w-4 h-4" />ویرایش
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Toggle active / Delete — فقط ادمین */}
|
||||
{!isOwnProfile && primaryRole !== 'clinic' && (
|
||||
{!isOwnProfile && primaryRole !== 'clinic' && !isReadOnly && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setToggleConfirm(true)}
|
||||
@@ -2620,15 +2676,17 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
<h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">
|
||||
آدرسهای مطب ({formatNumber(addresses.length)})
|
||||
</h2>
|
||||
<button onClick={() => { setEditingAddr(null); setAddrModalOpen(true); }}
|
||||
className="flex items-center gap-1.5 text-xs font-medium text-primary-600 dark:text-primary-400 hover:underline">
|
||||
<PlusIcon className="w-4 h-4" />افزودن آدرس
|
||||
</button>
|
||||
{!isReadOnly && (
|
||||
<button onClick={() => { setEditingAddr(null); setAddrModalOpen(true); }}
|
||||
className="flex items-center gap-1.5 text-xs font-medium text-primary-600 dark:text-primary-400 hover:underline">
|
||||
<PlusIcon className="w-4 h-4" />افزودن آدرس
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{addresses.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{addresses.map(addr => (
|
||||
<AddressCard key={addr.id} addr={addr}
|
||||
<AddressCard key={addr.id} addr={addr} readOnly={isReadOnly}
|
||||
onEdit={() => { setEditingAddr(addr); setAddrModalOpen(true); }}
|
||||
onDelete={() => setDeletingAddrId(addr.id)}
|
||||
/>
|
||||
@@ -2638,15 +2696,17 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
<div className="text-center py-8 text-slate-400 dark:text-slate-500">
|
||||
<MapPinIcon className="w-10 h-10 mx-auto mb-2 opacity-30" />
|
||||
<p className="text-sm">هنوز آدرسی ثبت نشده است</p>
|
||||
<button onClick={() => { setEditingAddr(null); setAddrModalOpen(true); }}
|
||||
className="mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline">
|
||||
اولین آدرس را اضافه کنید
|
||||
</button>
|
||||
{!isReadOnly && (
|
||||
<button onClick={() => { setEditingAddr(null); setAddrModalOpen(true); }}
|
||||
className="mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline">
|
||||
اولین آدرس را اضافه کنید
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{uuid && <ScheduleSection doctorUuid={uuid} />}
|
||||
{uuid && <ScheduleSection doctorUuid={uuid} readOnly={isReadOnly} />}
|
||||
|
||||
{isOwnProfile && <ClinicInvitationsSection />}
|
||||
|
||||
@@ -2686,9 +2746,11 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
</div>
|
||||
: <p className="text-sm text-slate-400">تخصصی ثبت نشده</p>
|
||||
}
|
||||
<button onClick={() => setEditOpen(true)} className="w-full mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline text-right">
|
||||
ویرایش تخصصها ←
|
||||
</button>
|
||||
{!isReadOnly && (
|
||||
<button onClick={() => setEditOpen(true)} className="w-full mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline text-right">
|
||||
ویرایش تخصصها ←
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="cp-card p-5">
|
||||
@@ -2703,9 +2765,11 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil
|
||||
</div>
|
||||
: <p className="text-sm text-slate-400">خدمتی ثبت نشده</p>
|
||||
}
|
||||
<button onClick={() => setEditOpen(true)} className="w-full mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline text-right">
|
||||
ویرایش خدمات ←
|
||||
</button>
|
||||
{!isReadOnly && (
|
||||
<button onClick={() => setEditOpen(true)} className="w-full mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline text-right">
|
||||
ویرایش خدمات ←
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="cp-card p-5">
|
||||
|
||||
Reference in New Issue
Block a user