89beb5f160
installTracking() beacons one view per page load and one click per control that carries a data-key, so every existing button is already counted. The new STATS pane reads it back: a 7/30/90-day range, the three totals, an SVG timeline and eight proportional bar lists (pages, clicked features, country, region, city, browser, system, device).
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
import { createRoot } from 'react-dom/client';
|
|
import './styles/fonts.css';
|
|
import './styles/tokens.css';
|
|
import './styles/app.css';
|
|
import './styles/landing.css';
|
|
import { ThemeProvider } from './theme/ThemeProvider';
|
|
import { I18nProvider } from './i18n/I18nProvider';
|
|
import { Landing } from './Landing';
|
|
import { Workspace } from './App';
|
|
import { Admin } from './Admin';
|
|
import { ProfilePage } from './ProfilePage';
|
|
import { PhotosPage } from './PhotosPage';
|
|
import { installTracking } from './track';
|
|
|
|
// Five routes, no router: the landing page, the workspace, the strip moderation
|
|
// screen, the member's own profile and the member's photo folder. nginx serves
|
|
// index.html for all of them (SPA fallback), so this is a pathname check.
|
|
const path = window.location.pathname;
|
|
const page = path.startsWith('/app') ? (
|
|
<Workspace />
|
|
) : path.startsWith('/admin') ? (
|
|
<Admin />
|
|
) : path.startsWith('/photos') ? (
|
|
<PhotosPage />
|
|
) : path.startsWith('/profile') ? (
|
|
<ProfilePage />
|
|
) : (
|
|
<Landing />
|
|
);
|
|
const root = document.getElementById('root');
|
|
if (!root) throw new Error('#root missing');
|
|
|
|
// The traffic counter records this page and then any click on a `data-key`
|
|
// control, for the life of the page.
|
|
installTracking();
|
|
|
|
// No StrictMode: it double-invokes effects, which would load CanvasKit twice and
|
|
// run the render pipeline twice on every mount.
|
|
createRoot(root).render(
|
|
<ThemeProvider>
|
|
<I18nProvider>{page}</I18nProvider>
|
|
</ThemeProvider>
|
|
);
|