// Doctor-facing pages: Dashboard, My Patients, X-ray detail — wired to API. function DoctorDashboard({ onNav, me }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let mounted = true; window.API.doctorDashboard().then(d => { if (mounted) { setData(d); setLoading(false); } }).catch(() => mounted && setLoading(false)); return () => { mounted = false; }; }, []); const weekCount = data?.kpis?.week_patients ?? 0; const recent = data?.recent || []; const telegramLinked = data?.kpis?.telegram_linked; // Group by day const groupedByDay = {}; for (const v of recent) { const created = new Date(v.created_at); const d = uzDate(created); const key = d === uzDate(new Date()) ? 'Bugun' : (d === uzDate(new Date(Date.now() - 86400000)) ? 'Kecha' : d); (groupedByDay[key] ||= []).push(v); } const spark1 = makeSpark([3,5,4,7,5,8,4]); const spark2 = makeSpark([12,14,11,18,15,22,19]); const spark3 = makeSpark([1,2,1,3,2,1,3]); return (
{uzDate(new Date())} · Shifokor

Xush kelibsiz, {me?.full_name || 'doktor'}

{loading ? "Ma'lumot yuklanmoqda..." : `Bu hafta sizga ${weekCount} ta vizit yo'naltirildi.`}

{!telegramLinked ? ( ) : (
Telegram bot
Ulangan
Aktiv
)}
So'nggi bemorlar
Sizga yo'naltirilgan
{recent.length === 0 ? ( ) : (
{Object.entries(groupedByDay).map(([day, vs]) => (
{day}
{vs.map(v => (
{new Date(v.created_at).toTimeString().slice(0, 5)}
{v.patient.full_name}
{uzMoneyShort(v.total_amount)} so'm ·
))}
))}
)}
Telegram bot
YourSmile Bot
Tez yuklash uchun
Rentgenlarni Telegram orqali tezda oling. Fayl (~300 MB) instant forward qilinadi.
); } /* ========================================================= DOCTOR — MY PATIENTS ========================================================= */ function DoctorPatientsPage({ onNav, toast }) { const [q, setQ] = useState(''); const [page, setPage] = useState(1); const [showSentModal, setShowSentModal] = useState(null); const [visits, setVisits] = useState([]); useEffect(() => { window.API.listXrayRecords({ limit: 200 }).then(setVisits).catch(() => {}); }, []); // Group visits by patient (latest per patient) const byPatient = new Map(); for (const v of visits) { const existing = byPatient.get(v.patient.id); if (!existing || new Date(v.created_at) > new Date(existing.created_at)) { byPatient.set(v.patient.id, v); } } let myPatients = Array.from(byPatient.values()); if (q) myPatients = myPatients.filter(v => v.patient.full_name.toLowerCase().includes(q.toLowerCase())); const pageSize = 9; const paged = myPatients.slice((page - 1) * pageSize, page * pageSize); const handleAction = async (v, kind) => { if (kind === 'telegram') { setShowSentModal({ v, kind: 'telegram' }); } else if (kind === 'link') { try { await window.API.requestLink(v.id); toast({ kind: 'success', title: 'Link so\'raldi', msg: 'Reception bildirishnoma oldi' }); } catch (err) { toast({ kind: 'danger', title: 'Xato', msg: err.message }); } } }; return (
{ setQ(e.target.value); setPage(1); }} style={{ paddingLeft: 36 }} />
{paged.length === 0 ? ( ) : (
{paged.map(v => (
{highlight(v.patient.full_name, q)}
{v.patient.phone || '—'}
So'nggi vizit · {uzDate(v.created_at)}
{uzMoneyShort(v.total_amount)} so'm
))}
)} {paged.length > 0 && }
{showSentModal && ( setShowSentModal(null)} title="Telegram orqali yuborildi" footer={}>
{showSentModal.v.patient.full_name} uchun fayl botga yuborildi
Telegram'da botingizdan tekshiring (~300 MB instant forward)
)}
); } /* ========================================================= DOCTOR — X-RAY DETAIL ========================================================= */ function DoctorXrayPage({ onNav, visitId, toast }) { const [visit, setVisit] = useState(null); useEffect(() => { if (!visitId) return; window.API.getVisit(visitId).then(setVisit).catch(() => {}); }, [visitId]); if (!visit) return
Yuklanmoqda...
; const requestLink = async () => { try { await window.API.requestLink(visit.id); toast({ kind: 'success', title: 'Link so\'raldi', msg: 'Reception bildirishnoma oldi' }); } catch (err) { toast({ kind: 'danger', title: 'Xato', msg: err.message }); } }; return (
onNav('doctor-patients')} key="b">Bemorlarim, visit.patient.full_name]} />
{visit.xray_record?.external_link ? ( 3D viewer ochish ) : ( )}
Bemor
{visit.patient.full_name}
{visit.patient.birth_date &&
{age(new Date(visit.patient.birth_date))} yosh
}
{visit.patient.phone && } {visit.patient.birth_date && }
Xizmatlar
{visit.items?.map(item => (
{item.service.name} {uzMoneyShort(item.price_at_moment)}
))}
Jami {uzMoneyShort(visit.total_amount)} so'm
Holat
Fayl
To'lov
3D viewer link{visit.xray_record?.external_link ? Bor : Yo'q}
); } Object.assign(window, { DoctorDashboard, DoctorPatientsPage, DoctorXrayPage });