// 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 ? (
) : (
)}
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 (
{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 });