Sprints 7–9: electronic medical card, splash home, plate design system

- Sprint 7: Electronic patient card (MedcardScreen rewritten with hero
  passport, 5 tabs, bidirectional links with past appointments)
- Sprint 8: 5th home variant "Светлая плитка" (HomeSplashScreen)
- Sprint 9: Tweaks "Дизайн" section (Клод / Прозрачная карточка) with
  plate versions of Profile, Appts, Appt details and Medcard in
  screens-plate.jsx; fallback to Клод for other screens

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
AR 15 M4
2026-04-21 19:19:55 +05:00
parent bf1c78ff14
commit e9a8171252
8 changed files with 1328 additions and 80 deletions
+150
View File
@@ -438,6 +438,156 @@ export function HomeTimelineXScreen({ nav }) {
);
}
export function HomeSplashScreen({ nav }) {
const { patient, doctors, appointments, articles } = CLINIC_DATA;
const firstName = patient.shortName.split(' ')[0];
const upcoming = appointments.find(a => a.status === 'upcoming');
const upDoc = upcoming && doctors.find(d => d.id === upcoming.doctor);
const article = articles[0];
return (
<div style={{ paddingBottom: 100 }}>
<div style={{ padding: '8px 20px 8px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<button onClick={() => nav.push('profile')} className="press" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{ width: 34, height: 34, borderRadius: 999, background: 'var(--c-primary-200)' }} />
<span style={{ fontSize: 16, fontWeight: 600, color: 'var(--c-fg-1)' }}>Главная</span>
</button>
<button onClick={() => nav.push('notifications')} className="press" style={{ width: 40, height: 40, borderRadius: 999, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<I.bell size={22} style={{ color: 'var(--c-primary-darker)' }} />
</button>
</div>
<div style={{ padding: '4px 20px 14px' }}>
<h1 style={{ fontSize: 28, fontWeight: 800, lineHeight: 1.15, color: 'var(--c-fg-1)', margin: 0 }}>Добрый день, {firstName}!</h1>
</div>
<div style={{ padding: '0 20px 20px' }}>
<button onClick={() => nav.push('search')} className="press" style={{
width: '100%', display: 'flex', alignItems: 'center', gap: 12,
padding: '14px 16px', background: '#fff',
border: '1px solid var(--c-border)', borderRadius: 14,
textAlign: 'left',
}}>
<I.search size={18} style={{ color: 'var(--c-fg-3)' }} />
<span style={{ color: 'var(--c-fg-3)', fontSize: 15 }}>Поиск врача...</span>
</button>
</div>
<div style={{ padding: '0 20px 8px' }}>
<div style={{ fontSize: 14, fontWeight: 700, color: 'var(--c-fg-1)', marginBottom: 10 }}>Записи на прием</div>
{upcoming && upDoc && (
<button onClick={() => nav.push('appt:' + upcoming.id)} className="press" style={{
width: '100%', textAlign: 'left',
background: 'var(--c-primary-50)',
border: '1px solid var(--c-primary-100)',
borderRadius: 16, padding: 16, marginBottom: 10,
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
<span style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, fontWeight: 700, letterSpacing: .7, color: 'var(--c-primary-darker)' }}>
<span style={{ width: 6, height: 6, borderRadius: 999, background: 'var(--c-primary-darker)' }} />
БЛИЖАЙШАЯ ЗАПИСЬ
</span>
<span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, color: 'var(--c-primary-darker)', fontWeight: 700 }}>
<I.star size={12} /> Активно
</span>
</div>
<div style={{ display: 'flex', gap: 12, alignItems: 'center', marginBottom: 10 }}>
<Avatar init={upDoc.init} size={48} />
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 15, fontWeight: 700, color: 'var(--c-fg-1)' }}>{upDoc.name}</div>
<div style={{ fontSize: 13, color: 'var(--c-primary-darker)', marginTop: 2 }}>{upDoc.spec.split(' · ')[0]}</div>
</div>
</div>
<div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
<span style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px', background: '#fff', border: '1px solid var(--c-primary-100)', borderRadius: 10, fontSize: 13, fontWeight: 600 }}>
<I.calendar size={14} style={{ color: 'var(--c-primary-darker)' }} /> {upcoming.date}
</span>
<span style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px', background: '#fff', border: '1px solid var(--c-primary-100)', borderRadius: 10, fontSize: 13, fontWeight: 600 }}>
<I.clock size={14} style={{ color: 'var(--c-primary-darker)' }} /> {upcoming.time}
</span>
</div>
<I.pin size={16} style={{ color: 'var(--c-primary-darker)', opacity: .6 }} />
</button>
)}
<button onClick={() => nav.set('appts')} className="press" style={{
width: '100%', textAlign: 'left',
background: 'var(--c-primary-50)',
border: '1px solid var(--c-primary-100)',
borderRadius: 16, padding: 16, marginBottom: 10,
display: 'flex', gap: 14, alignItems: 'center',
}}>
<div style={{ width: 54, height: 54, borderRadius: 999, background: 'var(--c-primary-darker)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<I.calendar size={24} style={{ color: '#fff' }} />
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: .7, color: 'var(--c-primary-darker)', marginBottom: 4 }}>МОИ ПРИЕМЫ</div>
<div style={{ fontSize: 15, fontWeight: 700, color: 'var(--c-fg-1)', marginBottom: 2 }}>Ближайшие приемы</div>
<div style={{ fontSize: 12, color: 'var(--c-fg-3)', lineHeight: 1.35 }}>Просмотрите ваши предстоящие прием</div>
</div>
</button>
<button onClick={() => nav.push('booking-specs')} className="press" style={{
width: '100%', textAlign: 'left', position: 'relative', overflow: 'hidden',
background: 'var(--c-warm-100)',
borderRadius: 16, padding: 16, marginBottom: 18,
}}>
<div style={{ position: 'absolute', top: -20, right: -20, width: 90, height: 90, borderRadius: 999, background: 'rgba(255,255,255,0.35)' }} />
<div style={{ position: 'absolute', top: 30, right: 30, width: 40, height: 40, borderRadius: 999, background: 'rgba(255,255,255,0.25)' }} />
<div style={{ position: 'relative', display: 'flex', gap: 14, alignItems: 'center', marginBottom: 14 }}>
<div style={{ width: 48, height: 48, borderRadius: 999, background: 'var(--c-warm-text)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<I.calendar size={22} style={{ color: '#fff' }} />
</div>
<div style={{ fontSize: 16, fontWeight: 700, color: 'var(--c-fg-1)' }}>Записаться на прием</div>
</div>
<div style={{ position: 'relative', padding: 12, background: '#fff', borderRadius: 12, textAlign: 'center', fontSize: 14, fontWeight: 600, color: 'var(--c-fg-1)' }}>
Выбрать удобное время
</div>
</button>
</div>
<div style={{ padding: '0 20px 8px' }}>
<div style={{ fontSize: 14, fontWeight: 700, color: 'var(--c-fg-1)', marginBottom: 10 }}>Услуги и консультации</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2,1fr)', gap: 10, marginBottom: 18 }}>
<SplashTile icon={I.calendar} sub="Записаться на прием" main="К врачу" onClick={() => nav.push('booking-specs')} />
<SplashTile icon={I.chat} sub="Связаться с врачом в" main="Чате" onClick={() => nav.set('chat')} />
</div>
</div>
<div style={{ padding: '0 20px 20px' }}>
<div style={{ fontSize: 14, fontWeight: 700, color: 'var(--c-fg-1)', marginBottom: 10 }}>Полезная информация</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2,1fr)', gap: 10 }}>
<SplashTile icon={I.doc} sub="Статья" main={article.title.split(' ').slice(0,2).join(' ')} onClick={() => nav.push('article:' + article.id)} />
<SplashTile icon={I.file} sub="Все" main="Статьи" onClick={() => nav.push('articles')} />
<SplashTile icon={I.shield} sub="Информация" main="Цены" onClick={() => nav.push('prices')} />
<SplashTile icon={I.phone} sub="Информация" main="Контакты" onClick={() => nav.push('contacts')} />
</div>
</div>
</div>
);
}
function SplashTile({ icon: Ic, sub, main, onClick }) {
return (
<button onClick={onClick} className="press" style={{
background: 'var(--c-primary-50)',
border: '1px solid var(--c-primary-100)',
borderRadius: 16, padding: 16,
textAlign: 'left', minHeight: 120,
display: 'flex', flexDirection: 'column', justifyContent: 'space-between', gap: 12,
}}>
<div style={{ width: 40, height: 40, borderRadius: 999, background: 'var(--c-primary-darker)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Ic size={20} style={{ color: '#fff' }} />
</div>
<div>
<div style={{ fontSize: 12, color: 'var(--c-primary-darker)', marginBottom: 2 }}>{sub}</div>
<div style={{ fontSize: 15, fontWeight: 700, color: 'var(--c-fg-1)' }}>{main}</div>
</div>
</button>
);
}
export function HomeFeedScreen({ nav }) {
const { doctors, appointments, clinic, articles, recovery } = CLINIC_DATA;
const upcoming = appointments.find(a => a.status === 'upcoming');