136 lines
5.2 KiB
TypeScript
136 lines
5.2 KiB
TypeScript
|
|
import { useMemo, useState } from 'react';
|
||
|
|
import { Boxes, MoonStar, Sparkles, SunMedium } from 'lucide-react';
|
||
|
|
import {
|
||
|
|
useAppStore,
|
||
|
|
} from './store/appStore';
|
||
|
|
import { useBotsSync } from './hooks/useBotsSync';
|
||
|
|
import { ImageFactoryModule } from './modules/images/ImageFactoryModule';
|
||
|
|
import { BotWizardModule } from './modules/onboarding/BotWizardModule';
|
||
|
|
import { BotDashboardModule } from './modules/dashboard/BotDashboardModule';
|
||
|
|
import { pickLocale } from './i18n';
|
||
|
|
import { appZhCn } from './i18n/app.zh-cn';
|
||
|
|
import { appEn } from './i18n/app.en';
|
||
|
|
import './App.css';
|
||
|
|
|
||
|
|
function App() {
|
||
|
|
const { theme, setTheme, locale, setLocale } = useAppStore();
|
||
|
|
const [showImageFactory, setShowImageFactory] = useState(false);
|
||
|
|
const [showCreateWizard, setShowCreateWizard] = useState(false);
|
||
|
|
useBotsSync();
|
||
|
|
const t = pickLocale(locale, { 'zh-cn': appZhCn, en: appEn });
|
||
|
|
const urlView = useMemo(() => {
|
||
|
|
const params = new URLSearchParams(window.location.search);
|
||
|
|
const forcedBotId =
|
||
|
|
(params.get('botId') || params.get('bot_id') || params.get('id') || '').trim();
|
||
|
|
const compactRaw = (params.get('compact') || params.get('h5') || params.get('mobile') || '').trim().toLowerCase();
|
||
|
|
const compactByFlag = ['1', 'true', 'yes', 'on'].includes(compactRaw);
|
||
|
|
const compactMode = compactByFlag || forcedBotId.length > 0;
|
||
|
|
return { forcedBotId, compactMode };
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={`app-shell ${urlView.compactMode ? 'app-shell-compact' : ''}`} data-theme={theme}>
|
||
|
|
<div className="app-frame">
|
||
|
|
<header className="app-header">
|
||
|
|
<div className="row-between app-header-top">
|
||
|
|
<div className="app-title">
|
||
|
|
<img src="/app-bot-icon.svg" alt="Nanobot" className="app-title-icon" />
|
||
|
|
<div>
|
||
|
|
<h1>{t.title}</h1>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="global-switches">
|
||
|
|
<div className="switch-compact">
|
||
|
|
<button
|
||
|
|
className={`switch-btn ${theme === 'dark' ? 'active' : ''}`}
|
||
|
|
onClick={() => setTheme('dark')}
|
||
|
|
title={t.dark}
|
||
|
|
aria-label={t.dark}
|
||
|
|
>
|
||
|
|
<MoonStar size={14} />
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
className={`switch-btn ${theme === 'light' ? 'active' : ''}`}
|
||
|
|
onClick={() => setTheme('light')}
|
||
|
|
title={t.light}
|
||
|
|
aria-label={t.light}
|
||
|
|
>
|
||
|
|
<SunMedium size={14} />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="switch-compact">
|
||
|
|
<button
|
||
|
|
className={`switch-btn switch-btn-lang ${locale === 'zh' ? 'active' : ''}`}
|
||
|
|
onClick={() => setLocale('zh')}
|
||
|
|
title={t.zh}
|
||
|
|
aria-label={t.zh}
|
||
|
|
>
|
||
|
|
<span>ZH</span>
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
className={`switch-btn switch-btn-lang ${locale === 'en' ? 'active' : ''}`}
|
||
|
|
onClick={() => setLocale('en')}
|
||
|
|
title={t.en}
|
||
|
|
aria-label={t.en}
|
||
|
|
>
|
||
|
|
<span>EN</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<main className="main-stage">
|
||
|
|
<BotDashboardModule
|
||
|
|
onOpenCreateWizard={() => setShowCreateWizard(true)}
|
||
|
|
onOpenImageFactory={() => setShowImageFactory(true)}
|
||
|
|
forcedBotId={urlView.forcedBotId || undefined}
|
||
|
|
compactMode={urlView.compactMode}
|
||
|
|
/>
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{!urlView.compactMode && showImageFactory && (
|
||
|
|
<div className="modal-mask app-modal-mask" onClick={() => setShowImageFactory(false)}>
|
||
|
|
<div className="modal-card app-modal-card" onClick={(e) => e.stopPropagation()}>
|
||
|
|
<div className="row-between">
|
||
|
|
<h3>{t.nav.images.title}</h3>
|
||
|
|
<button className="btn btn-secondary btn-sm icon-btn" onClick={() => setShowImageFactory(false)} title={t.close} aria-label={t.close}>
|
||
|
|
<Boxes size={14} />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div className="app-modal-body">
|
||
|
|
<ImageFactoryModule />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{!urlView.compactMode && showCreateWizard && (
|
||
|
|
<div className="modal-mask app-modal-mask" onClick={() => setShowCreateWizard(false)}>
|
||
|
|
<div className="modal-card app-modal-card" onClick={(e) => e.stopPropagation()}>
|
||
|
|
<div className="row-between">
|
||
|
|
<h3>{t.nav.onboarding.title}</h3>
|
||
|
|
<button className="btn btn-secondary btn-sm icon-btn" onClick={() => setShowCreateWizard(false)} title={t.close} aria-label={t.close}>
|
||
|
|
<Sparkles size={14} />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div className="app-modal-body">
|
||
|
|
<BotWizardModule
|
||
|
|
onCreated={() => {
|
||
|
|
setShowCreateWizard(false);
|
||
|
|
}}
|
||
|
|
onGoDashboard={() => setShowCreateWizard(false)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export default App;
|