You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
872 lines
48 KiB
872 lines
48 KiB
import React, { useState, useEffect } from 'react'; |
|
import { I } from '../icons.jsx'; |
|
import { CLINIC_DATA } from '../data.js'; |
|
import { Avatar, AppointmentCard, ScreenHeader } from '../components.jsx'; |
|
|
|
export function ApptsTabScreen({ nav }) { |
|
const { appointments, doctors, clinic } = CLINIC_DATA; |
|
const [tab, setTab] = useState('upcoming'); |
|
const items = appointments.filter(a => a.status === tab); |
|
return ( |
|
<div style={{ paddingBottom: 100 }}> |
|
<div style={{ padding: '12px 20px 12px' }}> |
|
<h1 className="h-screen" style={{ marginBottom: 14 }}>Мои приёмы</h1> |
|
<div className="seg" style={{ width: '100%', display: 'flex' }}> |
|
<button onClick={() => setTab('upcoming')} className={tab==='upcoming'?'on':''} style={{ flex: 1 }}>Предстоящие · {appointments.filter(a=>a.status==='upcoming').length}</button> |
|
<button onClick={() => setTab('past')} className={tab==='past'?'on':''} style={{ flex: 1 }}>Прошедшие</button> |
|
</div> |
|
</div> |
|
<div style={{ padding: '0 16px', display: 'flex', flexDirection: 'column', gap: 10 }}> |
|
{items.map(a => { |
|
const d = doctors.find(x => x.id === a.doctor); |
|
const ad = clinic.addresses.find(x => x.id === a.address); |
|
return <AppointmentCard key={a.id} appt={a} doctor={d} addr={ad} onClick={() => nav.push('appt:' + a.id)} />; |
|
})} |
|
{items.length === 0 && ( |
|
<div style={{ textAlign: 'center', padding: '40px 20px' }}> |
|
<div style={{ fontSize: 48, opacity: .3, marginBottom: 8 }}>📋</div> |
|
<div className="sub">Нет приёмов в этой категории</div> |
|
</div> |
|
)} |
|
</div> |
|
<div style={{ padding: 16, marginTop: 12 }}> |
|
<button onClick={() => nav.push('booking-specs')} className="btn-p block"> |
|
<I.plus size={18} /> Записаться на приём |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function ApptDetailScreen({ nav, apptId }) { |
|
const a = CLINIC_DATA.appointments.find(x => x.id === apptId); |
|
const d = CLINIC_DATA.doctors.find(x => x.id === a.doctor); |
|
const ad = CLINIC_DATA.clinic.addresses.find(x => x.id === a.address); |
|
const isUp = a.status === 'upcoming'; |
|
return ( |
|
<div style={{ paddingBottom: 120 }}> |
|
<ScreenHeader title="Приём" onBack={() => nav.pop()} /> |
|
<div style={{ padding: '0 20px' }}> |
|
<div className="card" style={{ padding: 20, textAlign: 'center', marginBottom: 14, background: isUp ? 'linear-gradient(135deg,#E3F4F2,#F2FAF9)' : '#fff' }}> |
|
<div style={{ fontSize: 14, color: 'var(--c-primary-darker)', fontWeight: 700, marginBottom: 6 }}>{a.weekday}, {a.date}</div> |
|
<div style={{ fontSize: 42, fontFamily: 'var(--font-narrow)', fontWeight: 700, color: 'var(--c-fg-1)', lineHeight: 1, marginBottom: 8 }}>{a.time}</div> |
|
<div className="sub">{a.type}</div> |
|
</div> |
|
<div className="card" style={{ padding: 0, marginBottom: 14 }}> |
|
<button onClick={() => nav.push('doctor:' + d.id)} style={{ width: '100%', textAlign: 'left', padding: 14, display: 'flex', gap: 12, alignItems: 'center' }}> |
|
<Avatar init={d.init} size={48} /> |
|
<div style={{ flex: 1 }}> |
|
<div style={{ fontWeight: 700, fontSize: 15 }}>{d.name.split(' ').slice(0,2).join(' ')}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{d.spec}</div> |
|
</div> |
|
<I.chev size={16} style={{ color: 'var(--c-fg-4)' }} /> |
|
</button> |
|
</div> |
|
<div className="card" style={{ padding: 0 }}> |
|
<div style={{ padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 12 }}> |
|
<I.pin size={20} style={{ color: 'var(--c-primary-darker)' }} /> |
|
<div style={{ flex: 1 }}> |
|
<div style={{ fontSize: 14, fontWeight: 700 }}>{ad.full}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{a.room} · 2 этаж</div> |
|
</div> |
|
<button className="btn-s" style={{ padding: '8px 12px', fontSize: 12 }}>Карта</button> |
|
</div> |
|
<div className="divider" /> |
|
<div style={{ padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 12 }}> |
|
<I.phone size={20} style={{ color: 'var(--c-primary-darker)' }} /> |
|
<div style={{ flex: 1, fontSize: 14 }}>(342) 207-03-03</div> |
|
<I.chev size={16} style={{ color: 'var(--c-fg-4)' }} /> |
|
</div> |
|
</div> |
|
|
|
{isUp && ( |
|
<div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 8 }}> |
|
<button className="btn-g" style={{ width: '100%', padding: 14 }}> |
|
<I.calendar size={18} /> Добавить в календарь |
|
</button> |
|
<button className="btn-g" style={{ width: '100%', padding: 14 }}> |
|
<I.bell size={18} /> Напомнить позже |
|
</button> |
|
</div> |
|
)} |
|
|
|
{a.hasReport && ( |
|
<div style={{ marginTop: 16 }}> |
|
<div className="h-sec" style={{ marginBottom: 10 }}>Заключение врача</div> |
|
<div className="card"> |
|
<div style={{ fontSize: 14, lineHeight: 1.55, color: 'var(--c-fg-2)' }}> |
|
Диагноз: хронический риносинусит, обострение. Назначено: Аква Марис, Назонекс 14 дней. Контрольный осмотр через 2 недели. |
|
</div> |
|
<button className="btn-s" style={{ marginTop: 12, padding: '8px 14px', fontSize: 13 }}> |
|
<I.doc size={15} /> Открыть PDF |
|
</button> |
|
</div> |
|
</div> |
|
)} |
|
</div> |
|
|
|
{isUp && ( |
|
<div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, padding: '14px 20px 34px', background: '#fff', borderTop: '1px solid var(--c-border)', display: 'flex', gap: 10 }}> |
|
<button className="btn-g" style={{ flex: 1, padding: 14, color: 'var(--c-danger)', borderColor: 'var(--c-accent-50)' }}>Отменить</button> |
|
<button className="btn-p" style={{ flex: 2 }}>Перенести</button> |
|
</div> |
|
)} |
|
</div> |
|
); |
|
} |
|
|
|
function EndoscopyTile({ label, time, seed = 0, big = false }) { |
|
const variants = [ |
|
'radial-gradient(circle at 45% 40%, #FCE1DD 0%, #E9A29B 35%, #B36962 65%, #5A2624 100%)', |
|
'radial-gradient(circle at 55% 50%, #FBD5D0 0%, #DC8981 40%, #9B4640 75%, #3F1B19 100%)', |
|
'radial-gradient(circle at 40% 35%, #FFE4DE 0%, #E7988F 40%, #A55651 70%, #4A1E1B 100%)', |
|
'radial-gradient(circle at 50% 55%, #FCDAD4 0%, #E5968C 35%, #A8544E 75%, #442220 100%)', |
|
]; |
|
const insetPad = big ? 14 : 8; |
|
return ( |
|
<div style={{ position: 'relative', background: '#0B0606', borderRadius: big ? 22 : 14, overflow: 'hidden', aspectRatio: '1', width: '100%' }}> |
|
<div style={{ |
|
position: 'absolute', inset: insetPad, borderRadius: '50%', |
|
background: variants[seed % 4], |
|
}} /> |
|
<div style={{ |
|
position: 'absolute', inset: insetPad, borderRadius: '50%', |
|
boxShadow: 'inset 0 0 30px rgba(0,0,0,0.65), inset 0 0 6px rgba(255,255,255,0.15)', |
|
pointerEvents: 'none', |
|
}} /> |
|
{/* subtle highlight spot (specular) */} |
|
<div style={{ |
|
position: 'absolute', top: `${22 + (seed % 3) * 6}%`, left: `${30 + (seed % 4) * 5}%`, |
|
width: big ? 26 : 14, height: big ? 26 : 14, borderRadius: '50%', |
|
background: 'radial-gradient(circle, rgba(255,255,255,0.55) 0%, transparent 70%)', |
|
pointerEvents: 'none', |
|
}} /> |
|
<div style={{ |
|
position: 'absolute', left: insetPad + 4, right: insetPad + 4, bottom: insetPad + 4, |
|
padding: big ? '8px 12px' : '5px 8px', |
|
background: 'rgba(0,0,0,0.55)', borderRadius: 8, |
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center', |
|
color: '#fff', fontSize: big ? 13 : 11, |
|
}}> |
|
<span style={{ fontWeight: 700 }}>{label}</span> |
|
<span style={{ opacity: .7, fontFamily: 'var(--font-narrow)' }}>{time}</span> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function ResultEndoscopyScreen({ nav, resultId = 'r2' }) { |
|
const r = CLINIC_DATA.results.find(x => x.id === resultId) || CLINIC_DATA.results.find(x => x.kind === 'image'); |
|
const doctor = CLINIC_DATA.doctors.find(d => d.id === r.doctor); |
|
const [selected, setSelected] = useState(null); |
|
|
|
const images = [ |
|
{ label: 'Носоглотка', time: '10:32' }, |
|
{ label: 'Аденоиды', time: '10:33' }, |
|
{ label: 'Хоана лев.', time: '10:34' }, |
|
{ label: 'Хоана прав.', time: '10:35' }, |
|
]; |
|
const diagnoses = ['Гипертрофия аденоидов III ст.']; |
|
const conclusion = 'Слизистая носоглотки умеренно гиперемирована, отёчна. Глоточная миндалина (аденоиды) III степени, заполняет просвет хоан на 2/3 с обеих сторон. Отделяемое слизистое. Устья слуховых труб обозримы, без особенностей.'; |
|
const recommendations = [ |
|
'Консультация ЛОР-хирурга в течение 2 недель', |
|
'Курс промываний «Кукушка» (5 процедур)', |
|
'Контрольная эндоскопия через 1 месяц', |
|
'Обсудить показания к аденотомии', |
|
]; |
|
|
|
return ( |
|
<div style={{ paddingBottom: 40 }}> |
|
<ScreenHeader title={r.name} subtitle={`${r.date} · ${images.length} снимков`} onBack={() => nav.pop()} rightIcon={I.doc} /> |
|
|
|
<div style={{ padding: '0 20px' }}> |
|
<div className="card" style={{ padding: 14, display: 'flex', gap: 12, alignItems: 'center', marginBottom: 14 }}> |
|
<Avatar init={doctor.init} size={44} /> |
|
<div style={{ flex: 1 }}> |
|
<div className="sub" style={{ fontSize: 11, marginBottom: 2 }}>Исследование провёл</div> |
|
<div style={{ fontSize: 14, fontWeight: 700 }}>{doctor.name.split(' ').slice(0,2).join(' ')}</div> |
|
</div> |
|
<span className="chip chip-success">Готово</span> |
|
</div> |
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 16 }}> |
|
{images.map((img, i) => ( |
|
<button key={i} onClick={() => setSelected(i)} className="press" style={{ padding: 0, background: 'transparent', borderRadius: 14 }}> |
|
<EndoscopyTile label={img.label} time={img.time} seed={i} /> |
|
</button> |
|
))} |
|
</div> |
|
|
|
<div className="card" style={{ marginBottom: 12 }}> |
|
<div className="h-row" style={{ marginBottom: 10 }}>Диагноз</div> |
|
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}> |
|
{diagnoses.map(d => <span key={d} className="chip chip-warm" style={{ fontSize: 13, padding: '7px 12px' }}>{d}</span>)} |
|
</div> |
|
</div> |
|
|
|
<div className="card" style={{ marginBottom: 12 }}> |
|
<div className="h-row" style={{ marginBottom: 8 }}>Заключение</div> |
|
<p style={{ margin: 0, fontSize: 14, color: 'var(--c-fg-2)', lineHeight: 1.55 }}>{conclusion}</p> |
|
</div> |
|
|
|
<div className="card" style={{ marginBottom: 16 }}> |
|
<div className="h-row" style={{ marginBottom: 6 }}>Рекомендации</div> |
|
{recommendations.map((rec, i, a) => ( |
|
<div key={i}> |
|
<div style={{ display: 'flex', gap: 12, padding: '10px 0', alignItems: 'flex-start' }}> |
|
<div style={{ |
|
width: 22, height: 22, borderRadius: 999, background: 'var(--c-primary-100)', |
|
color: 'var(--c-primary-darker)', fontSize: 12, fontWeight: 700, |
|
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, marginTop: 1, |
|
}}>{i + 1}</div> |
|
<div style={{ fontSize: 14, color: 'var(--c-fg-2)', lineHeight: 1.5 }}>{rec}</div> |
|
</div> |
|
{i < a.length - 1 && <div className="divider" />} |
|
</div> |
|
))} |
|
</div> |
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}> |
|
<button className="btn-p block"> |
|
<I.doc size={18} /> Скачать PDF |
|
</button> |
|
<div style={{ display: 'flex', gap: 10 }}> |
|
<button onClick={() => nav.push('chat')} className="btn-g" style={{ flex: 1, padding: 14 }}> |
|
<I.chat size={16} /> Обсудить |
|
</button> |
|
<button className="btn-g" style={{ flex: 1, padding: 14 }}> |
|
<I.arrow size={16} /> Поделиться |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{selected !== null && ( |
|
<div |
|
onClick={() => setSelected(null)} |
|
style={{ |
|
position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.95)', zIndex: 200, |
|
display: 'flex', flexDirection: 'column', |
|
}} |
|
> |
|
<div style={{ padding: '50px 20px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', color: '#fff' }}> |
|
<button onClick={(e) => { e.stopPropagation(); setSelected(null); }} className="press" style={{ |
|
width: 38, height: 38, borderRadius: 999, background: 'rgba(255,255,255,0.15)', |
|
display: 'flex', alignItems: 'center', justifyContent: 'center', |
|
}}> |
|
<I.close size={18} style={{ color: '#fff' }} /> |
|
</button> |
|
<div style={{ fontSize: 15, fontWeight: 700 }}>{images[selected].label}</div> |
|
<button onClick={(e) => e.stopPropagation()} className="press" style={{ |
|
width: 38, height: 38, borderRadius: 999, background: 'rgba(255,255,255,0.15)', |
|
display: 'flex', alignItems: 'center', justifyContent: 'center', |
|
}}> |
|
<I.doc size={18} style={{ color: '#fff' }} /> |
|
</button> |
|
</div> |
|
<div |
|
onClick={(e) => e.stopPropagation()} |
|
style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '0 20px' }} |
|
> |
|
<div style={{ width: '100%', maxWidth: 340 }}> |
|
<EndoscopyTile label={images[selected].label} time={images[selected].time} seed={selected} big /> |
|
</div> |
|
</div> |
|
<div style={{ padding: '16px 20px 36px', display: 'flex', justifyContent: 'center', gap: 8 }}> |
|
{images.map((_, i) => ( |
|
<button |
|
key={i} |
|
onClick={(e) => { e.stopPropagation(); setSelected(i); }} |
|
style={{ |
|
width: i === selected ? 24 : 8, height: 8, borderRadius: 999, |
|
background: i === selected ? '#fff' : 'rgba(255,255,255,0.35)', |
|
border: 0, padding: 0, cursor: 'pointer', transition: 'width .2s', |
|
}} |
|
/> |
|
))} |
|
</div> |
|
</div> |
|
)} |
|
</div> |
|
); |
|
} |
|
|
|
export function ResultsScreen({ nav }) { |
|
const { results, doctors } = CLINIC_DATA; |
|
return ( |
|
<div style={{ paddingBottom: 100 }}> |
|
<ScreenHeader title="Анализы и обследования" onBack={() => nav.pop()} /> |
|
<div className="pills" style={{ marginBottom: 12 }}> |
|
{['Все','Готовы','В работе','Аудио','Эндоскопия'].map((p,i)=>( |
|
<button key={i} className={'pill' + (i===0?' on':'')}>{p}</button> |
|
))} |
|
</div> |
|
<div style={{ padding: '0 16px', display: 'flex', flexDirection: 'column', gap: 10 }}> |
|
{results.map(r => { |
|
const d = doctors.find(x => x.id === r.doctor); |
|
const isReady = r.status === 'ready'; |
|
const kindIcons = { audio: I.hearing, image: I.video, lab: I.doc }; |
|
const Ic = kindIcons[r.kind]; |
|
return ( |
|
<button key={r.id} onClick={() => isReady && nav.push(r.kind === 'audio' ? 'result-audio' : 'result:' + r.id)} className="press card" style={{ |
|
display: 'flex', gap: 12, alignItems: 'center', textAlign: 'left', opacity: isReady ? 1 : .7, |
|
}}> |
|
<div style={{ width: 44, height: 44, borderRadius: 12, background: isReady ? 'var(--c-primary-100)' : 'var(--c-warning-50)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<Ic size={22} style={{ color: isReady ? 'var(--c-primary-darker)' : 'var(--c-warning)' }} /> |
|
</div> |
|
<div style={{ flex: 1 }}> |
|
<div style={{ fontWeight: 700, fontSize: 15, marginBottom: 2 }}>{r.name}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{r.date} · {d.name.split(' ')[0]}</div> |
|
</div> |
|
{isReady ? <I.chev size={16} style={{ color: 'var(--c-fg-4)' }} /> : <span className="chip" style={{ background: 'var(--c-warning-50)', color: 'var(--c-warning)' }}>В работе</span>} |
|
</button> |
|
); |
|
})} |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function ResultAudioScreen({ nav }) { |
|
const freqs = [250, 500, 1000, 2000, 4000, 8000]; |
|
const leftDB = [10, 15, 20, 25, 35, 50]; |
|
const rightDB = [5, 10, 15, 20, 30, 40]; |
|
const w = 320, h = 220, pl = 30, pt = 10, pr = 10, pb = 30; |
|
const iw = w - pl - pr, ih = h - pt - pb; |
|
const xFor = i => pl + (i / (freqs.length - 1)) * iw; |
|
const yFor = db => pt + (db / 120) * ih; |
|
return ( |
|
<div style={{ paddingBottom: 40 }}> |
|
<ScreenHeader title="Аудиограмма" subtitle="8 апр 2026" onBack={() => nav.pop()} rightIcon={I.doc} /> |
|
<div style={{ padding: '0 20px' }}> |
|
<div className="card" style={{ marginBottom: 14 }}> |
|
<div style={{ display: 'flex', gap: 16, marginBottom: 12 }}> |
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}> |
|
<div style={{ width: 10, height: 10, borderRadius: 999, background: 'var(--c-accent)' }} /> |
|
<span style={{ fontSize: 12, fontWeight: 700 }}>Правое ухо</span> |
|
</div> |
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}> |
|
<div style={{ width: 10, height: 10, background: 'var(--c-primary-darker)' }} /> |
|
<span style={{ fontSize: 12, fontWeight: 700 }}>Левое ухо</span> |
|
</div> |
|
</div> |
|
<svg width="100%" viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}> |
|
{[0, 20, 40, 60, 80, 100, 120].map(db => ( |
|
<g key={db}> |
|
<line x1={pl} y1={yFor(db)} x2={w - pr} y2={yFor(db)} stroke="#E4EAF2" strokeWidth="1" /> |
|
<text x={pl - 6} y={yFor(db) + 4} fontSize="10" fill="#9AA7B4" textAnchor="end">{db}</text> |
|
</g> |
|
))} |
|
{freqs.map((f, i) => ( |
|
<g key={f}> |
|
<line x1={xFor(i)} y1={pt} x2={xFor(i)} y2={h - pb} stroke="#E4EAF2" strokeWidth="1" /> |
|
<text x={xFor(i)} y={h - pb + 14} fontSize="10" fill="#9AA7B4" textAnchor="middle">{f < 1000 ? f : (f/1000) + 'k'}</text> |
|
</g> |
|
))} |
|
<rect x={pl} y={pt} width={iw} height={yFor(25)-pt} fill="#E8F5EE" opacity=".5" /> |
|
<polyline points={rightDB.map((db,i)=>`${xFor(i)},${yFor(db)}`).join(' ')} fill="none" stroke="#E04E44" strokeWidth="2" /> |
|
{rightDB.map((db,i)=>(<circle key={i} cx={xFor(i)} cy={yFor(db)} r="4" fill="#fff" stroke="#E04E44" strokeWidth="2" />))} |
|
<polyline points={leftDB.map((db,i)=>`${xFor(i)},${yFor(db)}`).join(' ')} fill="none" stroke="#166B63" strokeWidth="2" strokeDasharray="4 3" /> |
|
{leftDB.map((db,i)=>( |
|
<g key={i} stroke="#166B63" strokeWidth="2"> |
|
<line x1={xFor(i)-4} y1={yFor(db)-4} x2={xFor(i)+4} y2={yFor(db)+4} /> |
|
<line x1={xFor(i)+4} y1={yFor(db)-4} x2={xFor(i)-4} y2={yFor(db)+4} /> |
|
</g> |
|
))} |
|
</svg> |
|
<div className="sub" style={{ fontSize: 11, textAlign: 'center', marginTop: 8 }}>dB HL / частота (Гц) · зелёная зона — норма</div> |
|
</div> |
|
|
|
<div className="card" style={{ marginBottom: 14 }}> |
|
<div className="h-row" style={{ marginBottom: 8 }}>Заключение</div> |
|
<div style={{ fontSize: 14, color: 'var(--c-fg-2)', lineHeight: 1.55, marginBottom: 12 }}> |
|
Правое ухо — норма. Левое ухо — лёгкая нейросенсорная тугоухость в области высоких частот (4–8 кГц). |
|
</div> |
|
<div style={{ display: 'flex', gap: 8, alignItems: 'center', padding: 12, background: 'var(--c-primary-50)', borderRadius: 10 }}> |
|
<I.stetho size={18} style={{ color: 'var(--c-primary-darker)' }} /> |
|
<div style={{ fontSize: 13, color: 'var(--c-fg-2)' }}>Рекомендован контроль через 6 месяцев</div> |
|
</div> |
|
</div> |
|
|
|
<button className="btn-g" style={{ width: '100%', padding: 14 }}> |
|
<I.doc size={18} /> Скачать заключение |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function RecoveryScreen({ nav }) { |
|
const { recovery } = CLINIC_DATA; |
|
const surgeon = CLINIC_DATA.doctors.find(d => d.id === recovery.surgeon); |
|
return ( |
|
<div style={{ paddingBottom: 40 }}> |
|
<ScreenHeader title="Восстановление" onBack={() => nav.pop()} /> |
|
<div style={{ padding: '0 20px 16px' }}> |
|
<div className="card" style={{ padding: 18, marginBottom: 14, background: 'linear-gradient(135deg, var(--c-primary-darker), #0F4A44)', color: '#fff', border: 0 }}> |
|
<div style={{ fontSize: 12, textTransform: 'uppercase', letterSpacing: .8, opacity: .7, marginBottom: 4 }}>Операция</div> |
|
<div style={{ fontSize: 20, fontWeight: 700, marginBottom: 14 }}>{recovery.op}</div> |
|
<div style={{ height: 8, background: 'rgba(255,255,255,0.2)', borderRadius: 999, overflow: 'hidden', marginBottom: 12 }}> |
|
<div style={{ width: `${recovery.dayNow/recovery.totalDays*100}%`, height: '100%', background: '#fff', borderRadius: 999 }} /> |
|
</div> |
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}> |
|
<span style={{ opacity: .8 }}>День {recovery.dayNow}</span> |
|
<span style={{ opacity: .8 }}>{recovery.totalDays - recovery.dayNow} дней до выписки</span> |
|
</div> |
|
</div> |
|
|
|
<div className="h-sec" style={{ marginBottom: 10 }}>Лекарства сегодня</div> |
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 18 }}> |
|
{recovery.meds.map((m,i)=>( |
|
<div key={i} className="card" style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 14 }}> |
|
<div style={{ width: 40, height: 40, borderRadius: 10, background: 'var(--c-warning-50)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.pill size={20} style={{ color: 'var(--c-warning)' }} /> |
|
</div> |
|
<div style={{ flex: 1 }}> |
|
<div style={{ fontSize: 14, fontWeight: 700 }}>{m.name}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{m.freq} · след. {m.nextTake}</div> |
|
</div> |
|
{m.total > 0 && ( |
|
<div style={{ fontSize: 12, color: 'var(--c-fg-3)', fontWeight: 700 }}>{m.taken}/{m.total}</div> |
|
)} |
|
</div> |
|
))} |
|
</div> |
|
|
|
<div className="h-sec" style={{ marginBottom: 10 }}>План восстановления</div> |
|
<div className="card" style={{ padding: 0 }}> |
|
{recovery.steps.map((s, i) => ( |
|
<div key={i} style={{ display: 'flex', gap: 14, padding: '14px 16px', alignItems: 'flex-start', position: 'relative' }}> |
|
{i < recovery.steps.length - 1 && ( |
|
<div style={{ position: 'absolute', left: 27, top: 36, bottom: -10, width: 2, background: s.done ? 'var(--c-primary-darker)' : 'var(--c-divider)' }} /> |
|
)} |
|
<div style={{ |
|
width: 28, height: 28, borderRadius: 999, flexShrink: 0, |
|
background: s.done ? 'var(--c-primary-darker)' : '#fff', |
|
border: s.active ? '3px solid var(--c-primary-darker)' : s.done ? 0 : '2px solid var(--c-border-strong)', |
|
display: 'flex', alignItems: 'center', justifyContent: 'center', |
|
position: 'relative', zIndex: 1, |
|
}}> |
|
{s.done && <I.check size={16} style={{ color: '#fff' }} sw={3} />} |
|
{s.active && <div style={{ width: 10, height: 10, borderRadius: 999, background: 'var(--c-primary-darker)' }} />} |
|
</div> |
|
<div style={{ flex: 1, paddingBottom: 4 }}> |
|
<div style={{ fontSize: 14, fontWeight: 700, color: s.done ? 'var(--c-fg-3)' : 'var(--c-fg-1)', marginBottom: 2 }}>{s.title}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{s.note}</div> |
|
</div> |
|
</div> |
|
))} |
|
</div> |
|
|
|
<div className="card" style={{ marginTop: 14, display: 'flex', gap: 12, alignItems: 'center', padding: 14 }}> |
|
<Avatar init={surgeon.init} size={44} /> |
|
<div style={{ flex: 1 }}> |
|
<div className="sub" style={{ fontSize: 11 }}>Ваш хирург</div> |
|
<div style={{ fontSize: 14, fontWeight: 700 }}>{surgeon.name.split(' ').slice(0,2).join(' ')}</div> |
|
</div> |
|
<button onClick={() => nav.push('chat:doctor-syndaev')} className="btn-s" style={{ padding: '8px 12px' }}> |
|
<I.chat size={15} /> Чат |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function AudioTestScreen({ nav }) { |
|
const [stage, setStage] = useState('intro'); |
|
const [progress, setProgress] = useState(0); |
|
const [currFreq, setCurrFreq] = useState(500); |
|
|
|
useEffect(() => { |
|
if (stage !== 'test') return; |
|
const id = setInterval(() => { |
|
setProgress(p => { |
|
if (p >= 100) { clearInterval(id); setStage('done'); return 100; } |
|
return p + 2; |
|
}); |
|
setCurrFreq([250,500,1000,2000,4000,8000][Math.floor(Math.random()*6)]); |
|
}, 200); |
|
return () => clearInterval(id); |
|
}, [stage]); |
|
|
|
return ( |
|
<div style={{ paddingBottom: 40, height: '100%', display: 'flex', flexDirection: 'column' }}> |
|
<ScreenHeader title="Тест слуха" onBack={() => nav.pop()} /> |
|
<div style={{ flex: 1, padding: '0 20px', display: 'flex', flexDirection: 'column' }}> |
|
{stage === 'intro' && ( |
|
<> |
|
<div style={{ textAlign: 'center', padding: '24px 0' }}> |
|
<div style={{ width: 120, height: 120, margin: '0 auto 20px', borderRadius: 999, background: 'var(--c-primary-100)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.hearing size={60} style={{ color: 'var(--c-primary-darker)' }} /> |
|
</div> |
|
<h1 className="h-screen" style={{ marginBottom: 10 }}>Проверим Ваш слух</h1> |
|
<p className="sub" style={{ fontSize: 14, maxWidth: 300, margin: '0 auto' }}>Тест займёт 3 минуты. Результат не заменяет консультацию сурдолога, но покажет, стоит ли записаться на приём.</p> |
|
</div> |
|
<div className="card" style={{ marginBottom: 14 }}> |
|
<div className="h-row" style={{ marginBottom: 10 }}>Для точного теста</div> |
|
{[ |
|
{ i: I.volume, t: 'Наденьте наушники', s: 'Без Bluetooth, проводные лучше' }, |
|
{ i: I.shield, t: 'Выберите тихое место', s: 'Без фоновых звуков и разговоров' }, |
|
{ i: I.clock, t: 'Не торопитесь', s: 'Отвечайте, когда уверены' }, |
|
].map((x,i,a)=>{ |
|
const XI = x.i; |
|
return ( |
|
<div key={i}> |
|
<div style={{ display: 'flex', gap: 12, padding: '10px 0', alignItems: 'flex-start' }}> |
|
<XI size={20} style={{ color: 'var(--c-primary-darker)', flexShrink: 0, marginTop: 1 }} /> |
|
<div> |
|
<div style={{ fontSize: 14, fontWeight: 700, marginBottom: 2 }}>{x.t}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{x.s}</div> |
|
</div> |
|
</div> |
|
{i < a.length - 1 && <div className="divider" />} |
|
</div> |
|
); |
|
})} |
|
</div> |
|
<div style={{ flex: 1 }} /> |
|
<button onClick={() => setStage('test')} className="btn-p block" style={{ marginBottom: 10 }}>Начать тест</button> |
|
<div className="sub" style={{ fontSize: 12, textAlign: 'center' }}>Регулярные тесты помогают заметить изменения слуха вовремя</div> |
|
</> |
|
)} |
|
|
|
{stage === 'test' && ( |
|
<> |
|
<div style={{ padding: '16px 0 8px' }}> |
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}> |
|
<span className="sub" style={{ fontSize: 12 }}>Правое ухо · {currFreq} Гц</span> |
|
<span className="sub" style={{ fontSize: 12 }}>{Math.round(progress)}%</span> |
|
</div> |
|
<div style={{ height: 6, background: 'var(--c-divider)', borderRadius: 999, overflow: 'hidden' }}> |
|
<div style={{ width: `${progress}%`, height: '100%', background: 'var(--c-primary-darker)', borderRadius: 999, transition: 'width .2s' }} /> |
|
</div> |
|
</div> |
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}> |
|
<div style={{ position: 'relative', width: 220, height: 220, marginBottom: 24 }}> |
|
{[0,1,2].map(i => ( |
|
<div key={i} style={{ |
|
position: 'absolute', inset: -20 * i, borderRadius: 999, |
|
border: '2px solid var(--c-primary-200)', |
|
opacity: 0.5 - i * 0.15, |
|
animation: `pulse 1.4s ${i * 0.3}s ease-out infinite`, |
|
}} /> |
|
))} |
|
<div style={{ position: 'absolute', inset: 0, borderRadius: 999, background: 'var(--c-primary-darker)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.volume size={70} style={{ color: '#fff' }} /> |
|
</div> |
|
</div> |
|
<div style={{ fontSize: 18, fontWeight: 700, textAlign: 'center', marginBottom: 8 }}>Слышите звук?</div> |
|
<div className="sub" style={{ fontSize: 14, textAlign: 'center', maxWidth: 260 }}>Нажмите кнопку сразу, как услышите тон — даже если очень тихо</div> |
|
</div> |
|
<button className="btn-p block" style={{ padding: 22, fontSize: 17, marginBottom: 8 }}>Слышу!</button> |
|
<button className="btn-g" style={{ width: '100%', padding: 14 }}>Не слышу</button> |
|
</> |
|
)} |
|
|
|
{stage === 'done' && ( |
|
<> |
|
<div style={{ textAlign: 'center', padding: '24px 0' }}> |
|
<div style={{ width: 96, height: 96, margin: '0 auto 20px', borderRadius: 999, background: 'var(--c-success-50)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.check size={48} style={{ color: 'var(--c-success)' }} sw={3} /> |
|
</div> |
|
<h1 className="h-screen" style={{ marginBottom: 8 }}>Тест завершён</h1> |
|
<p className="sub" style={{ fontSize: 14 }}>Ваш слух в пределах нормы</p> |
|
</div> |
|
<div className="card" style={{ marginBottom: 14 }}> |
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}> |
|
<div> |
|
<div className="sub" style={{ fontSize: 11, marginBottom: 4 }}>Правое ухо</div> |
|
<div style={{ fontSize: 18, fontWeight: 700, color: 'var(--c-success)' }}>Норма</div> |
|
<div className="sub" style={{ fontSize: 12, marginTop: 2 }}>10 дБ, все частоты</div> |
|
</div> |
|
<div> |
|
<div className="sub" style={{ fontSize: 11, marginBottom: 4 }}>Левое ухо</div> |
|
<div style={{ fontSize: 18, fontWeight: 700, color: 'var(--c-warning)' }}>Почти норма</div> |
|
<div className="sub" style={{ fontSize: 12, marginTop: 2 }}>25 дБ на 4 кГц</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div className="card" style={{ marginBottom: 14, background: 'var(--c-primary-50)' }}> |
|
<div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}> |
|
<I.stetho size={20} style={{ color: 'var(--c-primary-darker)', flexShrink: 0, marginTop: 2 }} /> |
|
<div> |
|
<div style={{ fontSize: 14, fontWeight: 700, marginBottom: 4 }}>Рекомендация</div> |
|
<div style={{ fontSize: 13, color: 'var(--c-fg-2)', lineHeight: 1.5 }}>Небольшое снижение на левом ухе. Рекомендуем пройти аудиометрию у сурдолога.</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div style={{ flex: 1 }} /> |
|
<button onClick={() => nav.push('booking-specs')} className="btn-p block" style={{ marginBottom: 10 }}>Записаться к сурдологу</button> |
|
<button onClick={() => nav.pop()} className="btn-g" style={{ width: '100%', padding: 14 }}>Сохранить и закрыть</button> |
|
</> |
|
)} |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function ProfileTabScreen({ nav }) { |
|
const sections = [ |
|
{ |
|
title: 'Здоровье', |
|
items: [ |
|
{ i: I.file, t: 'Медицинская карта', s: 'История, диагнозы', go: 'medcard' }, |
|
{ i: I.doc, t: 'Анализы', s: '5 результатов', go: 'results' }, |
|
{ i: I.pill, t: 'Лекарства', s: '3 активных курса', go: 'recovery' }, |
|
{ i: I.hearing, t: 'История тестов слуха', s: '2 аудиограммы', go: 'results' }, |
|
] |
|
}, |
|
{ |
|
title: 'Оплата и бонусы', |
|
items: [ |
|
{ i: I.card, t: 'Способы оплаты', s: 'Mir •••• 4821' }, |
|
{ i: I.gift, t: 'Бонусы', s: '2 480 баллов · 5%', badge: 'Серебро' }, |
|
{ i: I.file, t: 'История платежей', s: '12 операций' }, |
|
] |
|
}, |
|
{ |
|
title: 'Клиника', |
|
items: [ |
|
{ i: I.pin, t: 'Адреса и часы работы', s: '3 клиники' }, |
|
{ i: I.phone, t: '(342) 207-03-03', s: 'Ежедневно 9:00–21:00' }, |
|
] |
|
}, |
|
{ |
|
title: 'Настройки', |
|
items: [ |
|
{ i: I.bell, t: 'Уведомления' }, |
|
{ i: I.shield, t: 'Конфиденциальность' }, |
|
{ i: I.user, t: 'Члены семьи', s: '+ 2 профиля' }, |
|
] |
|
}, |
|
]; |
|
return ( |
|
<div style={{ paddingBottom: 100 }}> |
|
<div style={{ padding: '12px 20px 16px' }}> |
|
<h1 className="h-screen" style={{ marginBottom: 18 }}>Профиль</h1> |
|
<div className="card" style={{ padding: 18, display: 'flex', gap: 14, alignItems: 'center', background: 'linear-gradient(135deg, var(--c-primary-100), var(--c-warm-100))', border: 0 }}> |
|
<Avatar init="АС" size={64} style={{ fontSize: 24, boxShadow: 'var(--sh-sm)' }} /> |
|
<div style={{ flex: 1 }}> |
|
<div style={{ fontSize: 18, fontWeight: 700 }}>Анна Сергеевна</div> |
|
<div className="sub" style={{ fontSize: 13, marginBottom: 6 }}>+7 (912) 485-••-•• · 42 года</div> |
|
<button onClick={() => nav.push('qr')} className="chip" style={{ fontSize: 12, fontWeight: 700 }}> |
|
<I.qr size={12} /> QR пациента |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
<div style={{ padding: '0 16px' }}> |
|
{sections.map((sec, si) => ( |
|
<div key={si} style={{ marginBottom: 18 }}> |
|
<div style={{ fontSize: 11, color: 'var(--c-fg-3)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: .8, padding: '0 4px 8px' }}>{sec.title}</div> |
|
<div className="card" style={{ padding: 0 }}> |
|
{sec.items.map((it, i) => { |
|
const II = it.i; |
|
return ( |
|
<React.Fragment key={i}> |
|
<button onClick={() => it.go && nav.push(it.go)} className="press" style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 14, padding: '13px 16px', textAlign: 'left' }}> |
|
<div style={{ width: 34, height: 34, borderRadius: 9, background: 'var(--c-primary-100)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}> |
|
<II size={18} style={{ color: 'var(--c-primary-darker)' }} /> |
|
</div> |
|
<div style={{ flex: 1 }}> |
|
<div style={{ fontSize: 14, fontWeight: 600 }}>{it.t}</div> |
|
{it.s && <div className="sub" style={{ fontSize: 12 }}>{it.s}</div>} |
|
</div> |
|
{it.badge && <span className="chip chip-warm" style={{ fontSize: 11 }}>{it.badge}</span>} |
|
<I.chev size={15} style={{ color: 'var(--c-fg-4)' }} /> |
|
</button> |
|
{i < sec.items.length - 1 && <div style={{ height: 1, background: 'var(--c-divider)', marginLeft: 64 }} />} |
|
</React.Fragment> |
|
); |
|
})} |
|
</div> |
|
</div> |
|
))} |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function QRScreen({ nav }) { |
|
const cells = []; |
|
for (let i = 0; i < 441; i++) cells.push(Math.random() > 0.52 ? 1 : 0); |
|
const marker = (cx, cy) => { |
|
for (let y = 0; y < 7; y++) { |
|
for (let x = 0; x < 7; x++) { |
|
const isEdge = x === 0 || x === 6 || y === 0 || y === 6; |
|
const isInner = x >= 2 && x <= 4 && y >= 2 && y <= 4; |
|
cells[(cy + y) * 21 + (cx + x)] = (isEdge || isInner) ? 1 : 0; |
|
} |
|
} |
|
}; |
|
marker(0, 0); marker(14, 0); marker(0, 14); |
|
|
|
return ( |
|
<div style={{ paddingBottom: 40, height: '100%', display: 'flex', flexDirection: 'column', background: 'linear-gradient(180deg, var(--c-primary-darker) 0%, var(--c-primary-dark) 100%)', color: '#fff' }}> |
|
<div style={{ display: 'flex', padding: '12px 16px 8px' }}> |
|
<button onClick={() => nav.pop()} className="press" style={{ width: 38, height: 38, borderRadius: 999, background: 'rgba(255,255,255,0.15)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.chevL size={20} style={{ color: '#fff' }} /> |
|
</button> |
|
</div> |
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '0 32px' }}> |
|
<div style={{ marginBottom: 24, textAlign: 'center' }}> |
|
<div style={{ fontSize: 22, fontWeight: 700, marginBottom: 6 }}>Ваш QR-код</div> |
|
<div style={{ fontSize: 13, opacity: .75 }}>Покажите на ресепшен, чтобы быстро отметиться</div> |
|
</div> |
|
<div style={{ background: '#fff', borderRadius: 24, padding: 20, marginBottom: 20 }}> |
|
<div style={{ width: 210, height: 210, display: 'grid', gridTemplateColumns: 'repeat(21,1fr)', gap: 0 }}> |
|
{cells.map((c, i) => ( |
|
<div key={i} style={{ background: c ? '#0F4A44' : 'transparent' }} /> |
|
))} |
|
</div> |
|
</div> |
|
<div style={{ textAlign: 'center' }}> |
|
<div style={{ fontSize: 13, opacity: .75, marginBottom: 3 }}>Пациент №</div> |
|
<div style={{ fontSize: 20, fontWeight: 700, letterSpacing: 1, fontFamily: 'var(--font-narrow)' }}>УГН-2014-00482</div> |
|
</div> |
|
</div> |
|
<div style={{ padding: '0 20px 20px' }}> |
|
<div style={{ background: 'rgba(255,255,255,0.12)', borderRadius: 16, padding: 14, display: 'flex', gap: 12, alignItems: 'center' }}> |
|
<I.shield size={22} style={{ color: '#fff', opacity: .8 }} /> |
|
<div style={{ fontSize: 13, lineHeight: 1.45, opacity: .9 }}>Код обновляется каждые 60 секунд. Не передавайте третьим лицам.</div> |
|
</div> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function TelemedScreen({ nav }) { |
|
return ( |
|
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: '#0F1A20', color: '#fff', position: 'relative' }}> |
|
<div style={{ flex: 1, background: 'linear-gradient(135deg, #1F8F85, #166B63)', position: 'relative' }}> |
|
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<div style={{ width: 140, height: 140, borderRadius: 999, background: 'rgba(255,255,255,0.15)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-narrow)', fontSize: 56, fontWeight: 700 }}>СА</div> |
|
</div> |
|
<div style={{ position: 'absolute', top: 60, left: 0, right: 0, padding: '0 20px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> |
|
<button onClick={() => nav.pop()} className="press" style={{ width: 36, height: 36, borderRadius: 999, background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.chevD size={22} style={{ color: '#fff' }} /> |
|
</button> |
|
<div style={{ background: 'rgba(0,0,0,0.4)', borderRadius: 999, padding: '8px 14px', display: 'flex', alignItems: 'center', gap: 8 }}> |
|
<span style={{ width: 8, height: 8, borderRadius: 999, background: '#E04E44', animation: 'blink 1.2s infinite' }} /> |
|
<span style={{ fontSize: 13, fontWeight: 700 }}>05:42</span> |
|
</div> |
|
<button className="press" style={{ width: 36, height: 36, borderRadius: 999, background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<I.menu size={20} style={{ color: '#fff' }} /> |
|
</button> |
|
</div> |
|
<div style={{ position: 'absolute', bottom: 160, left: 0, right: 0, textAlign: 'center' }}> |
|
<div style={{ fontSize: 18, fontWeight: 700, marginBottom: 3 }}>Синдяев А.В.</div> |
|
<div style={{ fontSize: 13, opacity: .75 }}>ЛОР-хирург</div> |
|
</div> |
|
<div style={{ position: 'absolute', top: 110, right: 16, width: 96, height: 130, borderRadius: 14, background: '#2a3540', border: '2px solid rgba(255,255,255,0.2)', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<div style={{ width: 44, height: 44, borderRadius: 999, background: 'var(--c-primary-100)', color: 'var(--c-primary-darker)', fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>АС</div> |
|
</div> |
|
</div> |
|
<div style={{ padding: '24px 0 50px', background: 'rgba(15,26,32,0.9)', backdropFilter: 'blur(10px)', display: 'flex', justifyContent: 'center', gap: 14 }}> |
|
{[ |
|
{ i: I.mic, bg: 'rgba(255,255,255,0.15)' }, |
|
{ i: I.video, bg: 'rgba(255,255,255,0.15)' }, |
|
{ i: I.chat, bg: 'rgba(255,255,255,0.15)' }, |
|
{ i: I.phone, bg: '#E04E44' }, |
|
].map((c,i)=>{ |
|
const CI = c.i; |
|
return ( |
|
<button key={i} className="press" style={{ width: 58, height: 58, borderRadius: 999, background: c.bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
|
<CI size={24} style={{ color: '#fff' }} /> |
|
</button> |
|
); |
|
})} |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function MedcardScreen({ nav }) { |
|
return ( |
|
<div style={{ paddingBottom: 40 }}> |
|
<ScreenHeader title="Медицинская карта" onBack={() => nav.pop()} /> |
|
<div style={{ padding: '0 20px' }}> |
|
<div className="card" style={{ marginBottom: 14 }}> |
|
<div className="h-row" style={{ marginBottom: 10 }}>Основное</div> |
|
{[['Пол','Женский'],['Возраст','42 года'],['Рост / Вес','168 см · 62 кг'],['Группа крови','II (A), Rh+']].map(([k,v])=>( |
|
<div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '8px 0' }}> |
|
<span className="sub">{k}</span> |
|
<span style={{ fontSize: 14, fontWeight: 700 }}>{v}</span> |
|
</div> |
|
))} |
|
</div> |
|
|
|
<div className="card" style={{ marginBottom: 14 }}> |
|
<div className="h-row" style={{ marginBottom: 10 }}>Аллергии</div> |
|
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}> |
|
<span className="chip chip-danger">Пенициллин</span> |
|
<span className="chip chip-danger">Пыльца берёзы</span> |
|
<span className="chip chip-soft">+ добавить</span> |
|
</div> |
|
</div> |
|
|
|
<div className="h-sec" style={{ padding: '4px 4px 10px' }}>История диагнозов</div> |
|
<div className="card" style={{ padding: 0 }}> |
|
{[ |
|
{ d: '12 апр 2026', t: 'Искривление носовой перегородки', doc: 'Синдяев А.В.', tag: 'Операция' }, |
|
{ d: '8 апр 2026', t: 'Хронический риносинусит', doc: 'Макарова Л.Г.', tag: 'Приём' }, |
|
{ d: '15 ноя 2025', t: 'ОРВИ', doc: 'Суворова С.В.', tag: 'Приём' }, |
|
].map((r,i,a)=>( |
|
<div key={i}> |
|
<div style={{ padding: '14px 16px' }}> |
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}> |
|
<span className="sub" style={{ fontSize: 12 }}>{r.d}</span> |
|
<span className="chip chip-soft" style={{ fontSize: 10 }}>{r.tag}</span> |
|
</div> |
|
<div style={{ fontSize: 14, fontWeight: 700, marginBottom: 3 }}>{r.t}</div> |
|
<div className="sub" style={{ fontSize: 12 }}>{r.doc}</div> |
|
</div> |
|
{i < a.length - 1 && <div className="divider" />} |
|
</div> |
|
))} |
|
</div> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
export function NotificationsScreen({ nav }) { |
|
const n = [ |
|
{ tm: '2 ч назад', t: 'Напоминание', s: 'Приём Амоксиклава в 20:00', i: I.pill, tint: 'warning' }, |
|
{ tm: 'Сегодня', t: 'Готово заключение', s: 'Макарова Л.Г. — эндоскопия носоглотки', i: I.doc, tint: 'primary' }, |
|
{ tm: 'Вчера', t: 'Новое сообщение', s: 'Синдяев А.В.: «Как самочувствие?»', i: I.chat, tint: 'primary' }, |
|
{ tm: '15 апр', t: 'Акция', s: 'Бесплатная консультация хирурга в апреле', i: I.gift, tint: 'warm' }, |
|
{ tm: '12 апр', t: 'Приём завершён', s: 'Не забудьте оставить отзыв о враче', i: I.star, tint: 'warning' }, |
|
]; |
|
const tints = { |
|
primary: { bg: 'var(--c-primary-100)', c: 'var(--c-primary-darker)' }, |
|
warning: { bg: 'var(--c-warning-50)', c: 'var(--c-warning)' }, |
|
warm: { bg: 'var(--c-warm-100)', c: 'var(--c-warm-text)' }, |
|
}; |
|
return ( |
|
<div style={{ paddingBottom: 40 }}> |
|
<ScreenHeader title="Уведомления" onBack={() => nav.pop()} /> |
|
<div style={{ padding: '0 16px', display: 'flex', flexDirection: 'column', gap: 8 }}> |
|
{n.map((x,i)=>{ |
|
const t = tints[x.tint]; |
|
const XI = x.i; |
|
return ( |
|
<div key={i} className="card" style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: 14 }}> |
|
<div style={{ width: 38, height: 38, borderRadius: 10, background: t.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}> |
|
<XI size={18} style={{ color: t.c }} /> |
|
</div> |
|
<div style={{ flex: 1, minWidth: 0 }}> |
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 2 }}> |
|
<span style={{ fontSize: 14, fontWeight: 700 }}>{x.t}</span> |
|
<span className="sub" style={{ fontSize: 11 }}>{x.tm}</span> |
|
</div> |
|
<div className="sub" style={{ fontSize: 13 }}>{x.s}</div> |
|
</div> |
|
</div> |
|
); |
|
})} |
|
</div> |
|
</div> |
|
); |
|
}
|
|
|