feat: add social media fields to Clinic entity and update API documentation

This commit is contained in:
hamed
2026-06-21 13:26:47 +03:30
parent 3f1b2de971
commit 57965f184d
7 changed files with 205 additions and 2 deletions
+40 -2
View File
@@ -73,6 +73,8 @@ interface OptUuid { id: number; uuid: string; name: string; }
// ── Edit form schema ───────────────────────────────────────────────────────
const urlOrEmpty = z.string().refine(v => v === '' || /^https?:\/\/.+/.test(v), { message: 'آدرس URL معتبر نیست' });
const editSchema = z.object({
name: z.string().min(1, 'نام الزامی است'),
telephone: z.string().optional(),
@@ -81,6 +83,11 @@ const editSchema = z.object({
specialties: z.array(z.number()),
insurance: z.array(z.number()),
doctor_services: z.array(z.number()),
sm_instagram: urlOrEmpty,
sm_telegram: urlOrEmpty,
sm_aparat: urlOrEmpty,
sm_youtube: urlOrEmpty,
sm_linkedin: urlOrEmpty,
});
type EditForm = z.infer<typeof editSchema>;
@@ -282,6 +289,11 @@ function EditModal({ clinic, onClose, onSaved }: {
specialties: (clinic.specialties ?? []).map(s => Number(s.id)),
insurance: (clinic.list_bime ?? []).map(s => Number(s.id)),
doctor_services: (clinic.services ?? []).map(s => Number(s.id)),
sm_instagram: clinic.social_media?.instagram ?? '',
sm_telegram: clinic.social_media?.telegram ?? '',
sm_aparat: clinic.social_media?.aparat ?? '',
sm_youtube: clinic.social_media?.youtube ?? '',
sm_linkedin: clinic.social_media?.linkedin ?? '',
},
});
@@ -324,12 +336,19 @@ function EditModal({ clinic, onClose, onSaved }: {
specialties: values.specialties,
insurance: values.insurance,
doctor_services: values.doctor_services,
social_media: {
instagram: values.sm_instagram || null,
telegram: values.sm_telegram || null,
aparat: values.sm_aparat || null,
youtube: values.sm_youtube || null,
linkedin: values.sm_linkedin || null,
},
}),
onSuccess: () => { toast.success('اطلاعات کلینیک ذخیره شد'); onSaved(); onClose(); },
onError: (e: Error) => toast.error(e.message),
});
const [activeTab, setActiveTab] = useState<'basic' | 'tags'>('basic');
const [activeTab, setActiveTab] = useState<'basic' | 'tags' | 'social'>('basic');
return (
<div className="overlay" onClick={onClose}>
@@ -343,7 +362,7 @@ function EditModal({ clinic, onClose, onSaved }: {
{/* Tab switcher */}
<div style={{ borderBottom: '1px solid var(--border)', padding: '0 16px', display: 'flex', gap: 4 }}>
{([['basic', 'اطلاعات پایه'], ['tags', 'تخصص و بیمه']] as const).map(([id, label]) => (
{([['basic', 'اطلاعات پایه'], ['tags', 'تخصص و بیمه'], ['social', 'شبکه‌های اجتماعی']] as const).map(([id, label]) => (
<button key={id} type="button" onClick={() => setActiveTab(id)}
style={{
padding: '10px 14px', fontSize: 13, fontWeight: 600, border: 'none', background: 'none',
@@ -381,6 +400,25 @@ function EditModal({ clinic, onClose, onSaved }: {
</>
)}
{/* ── Social tab ── */}
{activeTab === 'social' && (
<>
{([
['sm_instagram', 'اینستاگرام', 'https://instagram.com/...'],
['sm_telegram', 'تلگرام', 'https://t.me/...'],
['sm_aparat', 'آپارات', 'https://aparat.com/...'],
['sm_youtube', 'یوتیوب', 'https://youtube.com/...'],
['sm_linkedin', 'لینکدین', 'https://linkedin.com/...'],
] as const).map(([field, label, placeholder]) => (
<div key={field}>
<label style={{ fontSize: 13, fontWeight: 600, display: 'block', marginBottom: 6 }}>{label}</label>
<input className="input" dir="ltr" placeholder={placeholder} {...register(field)} />
{errors[field] && <div className="err-text">{errors[field]?.message}</div>}
</div>
))}
</>
)}
{/* ── Tags tab ── */}
{activeTab === 'tags' && (
<>
+7
View File
@@ -60,6 +60,13 @@ export interface ClinicDetail {
clinic_logo: string | null;
caption: string | null;
images_clinic: { url: string; fid?: number }[];
social_media: {
instagram: string | null;
telegram: string | null;
aparat: string | null;
youtube: string | null;
linkedin: string | null;
} | null;
specialties: { id: string; uuid: string; name: string }[];
services: { id: string; uuid: string; name: string }[];
list_bime: { id: string; uuid: string; name: string }[];