import { ArrowLeft, ArrowRight, Clock, ExternalLink, Images, RotateCcw } from 'lucide-react'; import { lazy, Suspense, useId } from 'react'; import { useTranslation } from '../../i18n'; import { useIsPhone } from '../../mobile/useIsPhone'; import { DialogButton, DialogFooter, DialogHeader, DialogSection, DialogShell, DialogTile, FooterSpacer, fs, NEUTRAL_TINT, PILL, } from '../shared/DialogShell'; import ErrorBoundary from '../shared/ErrorBoundary'; import TrekIcon from '../shared/TrekIcon'; import { DEMO_FEATURES, DEMO_FULL_VERSION, DEMO_LIMITS, TREK_REPO_URL, type DemoLine } from './demoWelcomeModel'; import MDemoWelcome from './MDemoWelcome'; import { useDemoWelcome } from './useDemoWelcome'; // The credits and their photo list load when a visitor opens them, not with the dashboard. const DemoCredits = lazy(() => import('./DemoCredits')); type Translate = ReturnType['t']; const FOOTER_LINK = 'inline-flex items-center gap-1.5 font-medium text-content-muted no-underline hover:text-content'; /** The tiles: what a visitor can open right now, one card each. */ function FeatureGrid({ t }: { t: Translate }) { return (
{DEMO_FEATURES.map(({ id, icon: Icon, titleKey, bodyKey }) => (
{t(titleKey)}

{t(bodyKey)}

))}
); } /** A short list with an icon per line, for what the demo leaves out and what the full version adds. */ function LineList({ lines, tone, t }: { lines: readonly DemoLine[]; tone: 'muted' | 'accent'; t: Translate }) { const iconClass = tone === 'accent' ? 'text-success' : 'text-content-faint'; return ( ); } /** The welcome itself: the hourly reset, the tiles, and the two lists under them. */ function WelcomeBody({ t }: { t: Translate }) { return ( <>
{t('demo.welcome.resetTitle')}

{t('demo.welcome.resetBody')}

); } /** * The footer's links: the source code, and the credits for the demo's photos. * One above the other on a phone, where the footer has no room for both * beside the button. */ function FooterLinks({ t, onCredits, focusCredits }: { t: Translate; onCredits: () => void; focusCredits: boolean }) { return (
{t('demo.welcome.repo')}
); } /** * The welcome a demo instance opens with: what TREK is, that the demo resets * on the hour (with the live countdown), what to try right away, what the * demo leaves out and what the full version adds. The photo credits open in * its place and back. Closed by the button, Escape or a click on the * backdrop, and shown again on the next visit. */ export default function DemoWelcome() { const { t } = useTranslation(); const labelId = useId(); const { open, close, countdown, creditsOpen, backFromCredits, openCredits, closeCredits } = useDemoWelcome(); const phone = useIsPhone(); // On a phone the welcome is a sheet of the phone shell, not the desktop dialog made narrow. if (phone) return ( ); return ( } tint={NEUTRAL_TINT} labelId={labelId} onClose={close} eyebrow={t('demo.welcome.eyebrow')} title={t(creditsOpen ? 'demo.credits.title' : 'demo.welcome.title')} sub={creditsOpen ? undefined : t('demo.welcome.tagline')} subWraps pills={ {t('demo.welcome.resetIn')} {countdown} } /> } footer={ {/* The start button keeps its place in both views, so switching does not move the focus onto it again. */} {creditsOpen ? ( } > {t('demo.credits.back')} ) : ( )} }> {t('demo.welcome.start')} } > {creditsOpen ? ( ) : ( )} ); }