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:
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user