e9a8171252
- 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>
122 lines
6.0 KiB
React
122 lines
6.0 KiB
React
import React, { useEffect, useMemo, useState } from 'react';
|
|
import { TabBar } from './components.jsx';
|
|
import { HomeCardsScreen, HomeListScreen, HomeFeedScreen, HomeTimelineXScreen, HomeSplashScreen } from './screens/screens-home.jsx';
|
|
import {
|
|
BookingSpecsScreen, BookingDoctorScreen, BookingTimeScreen,
|
|
BookingConfirmScreen, BookingSuccessScreen,
|
|
DoctorsTabScreen, DoctorDetailScreen,
|
|
} from './screens/screens-booking.jsx';
|
|
import {
|
|
ApptsTabScreen, ApptDetailScreen,
|
|
ResultsScreen, ResultAudioScreen, ResultEndoscopyScreen,
|
|
RecoveryScreen, AudioTestScreen,
|
|
ProfileTabScreen, QRScreen,
|
|
TelemedScreen, MedcardScreen, NotificationsScreen,
|
|
} from './screens/screens-misc.jsx';
|
|
import { ChatsListScreen, ChatConversationScreen } from './screens/screens-chats.jsx';
|
|
import { ArticlesScreen, ArticleDetailScreen } from './screens/screens-articles.jsx';
|
|
import { HomeV2Screen, SearchScreen, ContactsScreen, PricesScreen } from './screens/screens-v2.jsx';
|
|
import { DevColorsScreen, DevExamplesScreen } from './screens/screens-dev.jsx';
|
|
import { DocsScreen } from './screens/screens-docs.jsx';
|
|
import { ProfilePlateScreen, ApptsPlateScreen, ApptDetailPlateScreen, MedcardPlateScreen } from './screens/screens-plate.jsx';
|
|
|
|
function renderScreen(screenId, nav, ctx) {
|
|
const parts = screenId.split(':');
|
|
const id = parts[0];
|
|
const plate = ctx.design === 'plate';
|
|
// В plate-режиме главная всегда «Светлая плитка», независимо от homeVariant
|
|
const homeVariant = plate ? 'splash' : ctx.homeVariant;
|
|
const HOME = { cards: HomeCardsScreen, list: HomeListScreen, feed: HomeFeedScreen, timelineX: HomeTimelineXScreen, splash: HomeSplashScreen }[homeVariant] || HomeCardsScreen;
|
|
|
|
// Plate-подмены (fallback на Клод если plate-версии нет)
|
|
if (plate) {
|
|
switch (id) {
|
|
case 'profile': return <ProfilePlateScreen nav={nav} ctx={ctx} />;
|
|
case 'appts': return <ApptsPlateScreen nav={nav} />;
|
|
case 'appt': return <ApptDetailPlateScreen nav={nav} apptId={parts[1]} />;
|
|
case 'medcard': return <MedcardPlateScreen nav={nav} />;
|
|
}
|
|
}
|
|
|
|
switch (id) {
|
|
case 'home': return <HOME nav={nav} ctx={ctx} />;
|
|
case 'home-v2': return <HomeV2Screen nav={nav} ctx={ctx} />;
|
|
case 'doctors': return <DoctorsTabScreen nav={nav} ctx={ctx} />;
|
|
case 'doctor': return <DoctorDetailScreen nav={nav} doctorId={parts[1]} />;
|
|
case 'booking-specs': return <BookingSpecsScreen nav={nav} />;
|
|
case 'booking-doctor': return <BookingDoctorScreen nav={nav} specId={parts[1]} />;
|
|
case 'booking-time': return <BookingTimeScreen nav={nav} doctorId={parts[1]} />;
|
|
case 'booking-confirm': return <BookingConfirmScreen nav={nav} doctorId={parts[1]} dateIdx={+parts[2]} time={parts[3]} />;
|
|
case 'booking-success': return <BookingSuccessScreen nav={nav} />;
|
|
case 'appts': return <ApptsTabScreen nav={nav} />;
|
|
case 'appt': return <ApptDetailScreen nav={nav} apptId={parts[1]} />;
|
|
case 'results': return <ResultsScreen nav={nav} />;
|
|
case 'result-audio': return <ResultAudioScreen nav={nav} />;
|
|
case 'result': return <ResultEndoscopyScreen nav={nav} resultId={parts[1]} />;
|
|
case 'recovery': return <RecoveryScreen nav={nav} />;
|
|
case 'audiotest': return <AudioTestScreen nav={nav} />;
|
|
case 'chat': return parts[1]
|
|
? <ChatConversationScreen nav={nav} chatId={parts[1]} />
|
|
: <ChatsListScreen nav={nav} />;
|
|
case 'profile': return <ProfileTabScreen nav={nav} ctx={ctx} />;
|
|
case 'qr': return <QRScreen nav={nav} />;
|
|
case 'telemed': return <TelemedScreen nav={nav} />;
|
|
case 'medcard': return <MedcardScreen nav={nav} />;
|
|
case 'notifications': return <NotificationsScreen nav={nav} />;
|
|
case 'articles': return <ArticlesScreen nav={nav} />;
|
|
case 'article': return <ArticleDetailScreen nav={nav} articleId={parts[1]} />;
|
|
case 'search': return <SearchScreen nav={nav} />;
|
|
case 'contacts': return <ContactsScreen nav={nav} />;
|
|
case 'prices': return <PricesScreen nav={nav} />;
|
|
case 'dev-colors': return <DevColorsScreen nav={nav} ctx={ctx} />;
|
|
case 'dev-examples': return <DevExamplesScreen nav={nav} ctx={ctx} />;
|
|
case 'docs': return <DocsScreen nav={nav} />;
|
|
default: return <div style={{padding: 40, textAlign: 'center'}}>Экран не найден: {screenId}</div>;
|
|
}
|
|
}
|
|
|
|
const TAB_IDS = ['home', 'appts', 'doctors', 'chat', 'profile'];
|
|
|
|
export function PhoneApp({ initialScreen, ctx, onCurrentChange }) {
|
|
const [stack, setStack] = useState([initialScreen]);
|
|
|
|
useEffect(() => { setStack([initialScreen]); }, [initialScreen]);
|
|
|
|
const current = stack[stack.length - 1];
|
|
|
|
useEffect(() => {
|
|
if (onCurrentChange) onCurrentChange(current);
|
|
}, [current, onCurrentChange]);
|
|
|
|
const nav = useMemo(() => ({
|
|
push: (id) => setStack(s => [...s, id]),
|
|
pop: () => setStack(s => s.length > 1 ? s.slice(0, -1) : s),
|
|
set: (id) => setStack([id]),
|
|
reset:() => setStack(['home']),
|
|
}), []);
|
|
|
|
const rootId = current.split(':')[0];
|
|
const hasSubId = current.includes(':');
|
|
const tabId = hasSubId ? null : (rootId === 'home-v2' ? 'home' : (TAB_IDS.includes(rootId) ? rootId : null));
|
|
const showTabBar = tabId !== null;
|
|
|
|
const modalScreens = ['qr', 'telemed', 'booking-success', 'audiotest'];
|
|
const isModal = modalScreens.includes(current.split(':')[0]);
|
|
|
|
return (
|
|
<div style={{
|
|
position: 'absolute', inset: 0,
|
|
background: 'var(--c-bg)',
|
|
overflow: 'hidden',
|
|
fontFamily: 'var(--font-base)',
|
|
}}>
|
|
<div style={{ position: 'absolute', inset: 0, overflowY: 'auto', overflowX: 'hidden', paddingTop: 58, paddingBottom: showTabBar ? 80 : 0 }}>
|
|
{renderScreen(current, nav, ctx)}
|
|
</div>
|
|
{showTabBar && !isModal && (
|
|
<TabBar active={tabId} onChange={(t) => nav.set(t)} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|