/opt/mawid/apps/dashboard/app/(app)/settings
NameSizeModeActions
page.tsx145300644editdlrm
Edit: /opt/mawid/apps/dashboard/app/(app)/settings/page.tsx (14530B)
'use client'; import { useEffect, useState } from 'react'; import { api, serviceName } from '@/lib/api'; import { useLang, type UiKey } from '@/lib/i18n'; import { Button, Card, Field, Input, Select, Spinner } from '@/components/ui'; type Interval = { start: string; end: string }; type WeekHours = Partial>; interface Clinic { id: string; name: string; phone: string; timezone: string; defaultLanguage: string; workingHours: WeekHours; settings: { reminderOffsetsHours?: number[]; waitlistHoldMinutes?: number; ownerWaPhone?: string; depositEnabled?: boolean; }; } interface ServiceRow { id: string; name: { ar?: string; tr?: string; en?: string }; durationMinutes: number; price: string | number | null; active: boolean; } interface StaffRow { id: string; name: string; role: string; active: boolean; services: { id: string }[]; } const DAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] as const; export default function SettingsPage() { const { lang } = useLang(); const [clinic, setClinic] = useState(null); const [services, setServices] = useState([]); const [staff, setStaff] = useState([]); const [flash, setFlash] = useState(null); async function loadAll() { const [c, sv, st] = await Promise.all([ api('/clinic'), api('/services'), api('/staff'), ]); setClinic(c); setServices(sv); setStaff(st); } useEffect(() => { loadAll(); }, []); function flashSaved(section: string) { setFlash(section); setTimeout(() => setFlash(null), 1500); } if (!clinic) return ; async function saveClinic(patch: object, section: string) { const updated = await api('/clinic', { method: 'PATCH', body: JSON.stringify(patch) }); setClinic(updated); flashSaved(section); } return (
); } function ClinicInfoCard({ clinic, onSave, saved, }: { clinic: Clinic; onSave: (patch: object, section: string) => Promise; saved: boolean; }) { const { t } = useLang(); const [name, setName] = useState(clinic.name); const [phone, setPhone] = useState(clinic.phone); const [timezone, setTimezone] = useState(clinic.timezone); const [defaultLanguage, setDefaultLanguage] = useState(clinic.defaultLanguage); const [ownerWaPhone, setOwnerWaPhone] = useState(clinic.settings.ownerWaPhone ?? ''); const [offsets, setOffsets] = useState( (clinic.settings.reminderOffsetsHours ?? [24, 2]).join(', '), ); function save() { const reminderOffsetsHours = offsets .split(',') .map((s) => Number(s.trim())) .filter((n) => !Number.isNaN(n) && n > 0); onSave( { name, phone, timezone, defaultLanguage, // settings are replaced wholesale — merge the existing object settings: { ...clinic.settings, ownerWaPhone: ownerWaPhone || undefined, reminderOffsetsHours, }, }, 'info', ); } return (

{t('clinicInfo')}

setName(e.target.value)} /> setPhone(e.target.value)} dir="ltr" /> setTimezone(e.target.value)} dir="ltr" /> setOwnerWaPhone(e.target.value)} dir="ltr" /> setOffsets(e.target.value)} dir="ltr" />
); } function WorkingHoursCard({ clinic, onSave, saved, }: { clinic: Clinic; onSave: (patch: object, section: string) => Promise; saved: boolean; }) { const { t } = useLang(); const [hours, setHours] = useState(clinic.workingHours ?? {}); function setDay(day: string, intervals: Interval[]) { setHours((prev) => ({ ...prev, [day]: intervals })); } return (

{t('workingHours')}

{DAYS.map((day) => { const intervals = hours[day] ?? []; return (
{t(`day_${day}` as UiKey)} {intervals.map((interval, i) => ( setDay( day, intervals.map((v, j) => (j === i ? { ...v, start: e.target.value } : v)), ) } /> – setDay( day, intervals.map((v, j) => (j === i ? { ...v, end: e.target.value } : v)), ) } /> ))}
); })}
); } function ServicesCard({ services, lang, onChanged, }: { services: ServiceRow[]; lang: string; onChanged: () => void; }) { const { t } = useLang(); const [editing, setEditing] = useState>>({}); const [adding, setAdding] = useState(false); const [newService, setNewService] = useState({ tr: '', ar: '', en: '', duration: 30, price: '' }); async function saveRow(row: ServiceRow) { const patch = editing[row.id]; if (!patch) return; await api(`/services/${row.id}`, { method: 'PATCH', body: JSON.stringify({ ...(patch.name ? { name: { ...row.name, ...patch.name } } : {}), ...(patch.durationMinutes !== undefined ? { durationMinutes: patch.durationMinutes } : {}), ...(patch.active !== undefined ? { active: patch.active } : {}), }), }); setEditing((prev) => ({ ...prev, [row.id]: {} })); onChanged(); } async function addService() { await api('/services', { method: 'POST', body: JSON.stringify({ name: { tr: newService.tr, ar: newService.ar, en: newService.en }, durationMinutes: newService.duration, ...(newService.price ? { price: Number(newService.price) } : {}), }), }); setAdding(false); setNewService({ tr: '', ar: '', en: '', duration: 30, price: '' }); onChanged(); } return (

{t('services')}

{adding && (
setNewService({ ...newService, tr: e.target.value })} /> setNewService({ ...newService, ar: e.target.value })} /> setNewService({ ...newService, en: e.target.value })} /> setNewService({ ...newService, duration: Number(e.target.value) })} />
setNewService({ ...newService, price: e.target.value })} />
)}
{services.map((row) => { const patch = editing[row.id] ?? {}; const dirty = Object.keys(patch).length > 0; return (
{serviceName(row.name, lang)} setEditing((prev) => ({ ...prev, [row.id]: { ...patch, durationMinutes: Number(e.target.value) }, })) } /> {dirty && ( )}
); })}
); } function StaffCard({ staff, services, lang, onChanged, }: { staff: StaffRow[]; services: ServiceRow[]; lang: string; onChanged: () => void; }) { const { t } = useLang(); const [adding, setAdding] = useState(false); const [newStaff, setNewStaff] = useState({ name: '', role: 'practitioner' }); async function toggleService(row: StaffRow, serviceId: string) { const current = row.services.map((s) => s.id); const next = current.includes(serviceId) ? current.filter((id) => id !== serviceId) : [...current, serviceId]; await api(`/staff/${row.id}`, { method: 'PATCH', body: JSON.stringify({ serviceIds: next }) }); onChanged(); } async function addStaff() { await api('/staff', { method: 'POST', body: JSON.stringify({ name: newStaff.name, role: newStaff.role }), }); setAdding(false); setNewStaff({ name: '', role: 'practitioner' }); onChanged(); } return (

{t('staffSection')}

{adding && (
setNewStaff({ ...newStaff, name: e.target.value })} /> setNewStaff({ ...newStaff, role: e.target.value })} />
)}
{staff.map((row) => (
{row.name} {row.role}
{services.map((service) => ( ))}
))}
); }