прототип мобильного приложения Клиники ухо, горло, нос им. проф. Е.Н.Оленевой. подготволен совместно с Claude.ai design и Claude CLI
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.
 
 
 
 

121 lines
6.0 KiB

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>
);
}