wutzcalc/client/vite.config.ts
iris 1d99881e88 client: standalone /stats page with charts, off the admin dashboard
Closes #40.

New /stats route (client/stats.html + src/stats/), served by the same
catch-all pattern as /admin. Reuses the admin login (STATS_PUBLIC env var
on the server side decides whether it needs one at all).

Three uPlot charts (daily revenue, daily transaction count, top-5-drink
sold-qty trend) plus the same three tables Admin.tsx used to render inline
— those move here wholesale, Admin.tsx now just links to /stats instead of
fetching /admin/api/stats itself. The 'Statistik zurücksetzen' reset button
moves here too, gated on an actual admin session (checked separately from
whether /api/stats itself succeeded, since STATS_PUBLIC can make that true
for an anonymous viewer).

Chart lib is uPlot (~45kb) per mara's steer not to hand-roll this. Both
client and server build/typecheck clean; manually smoke-tested the auth
gate (401 unauthed, 200 after login) and the /stats route against a fresh
DB.
2026-07-30 11:40:34 +02:00

54 lines
2 KiB
TypeScript

import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
import legacy from '@vitejs/plugin-legacy';
import { resolve } from 'node:path';
// The dev-server proxy target — same default (3000) as the server's own
// `process.env.PORT ?? 3000` in server/src/index.ts. Override with
// `WUTZ_SERVER_PORT` when running `dev:server` on a non-default port, e.g.:
// PORT=4000 pnpm dev:server
// WUTZ_SERVER_PORT=4000 pnpm dev:client
const serverTarget = `http://localhost:${process.env.WUTZ_SERVER_PORT ?? 3000}`;
export default defineConfig({
plugins: [
preact(),
legacy({
targets: ['ios >= 12', 'safari >= 12'],
modernPolyfills: true,
renderLegacyChunks: true,
}),
],
build: {
target: 'es2017',
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin.html'),
stats: resolve(__dirname, 'stats.html'),
},
},
},
server: {
port: 5173,
proxy: {
'/api': serverTarget,
'/healthz': serverTarget,
// NOT a bare '/admin' prefix: that would also swallow the /admin and
// /admin.html *page* requests themselves (and, since Vite's proxy
// matches by string prefix, they do — '/admin' matches '/admin.html'
// too), forwarding them to the backend instead of letting Vite serve
// its own dev-mode admin.html. In dev the backend only has a stale
// production build to fall back on (or nothing, if `client` was never
// built), so the page ends up loaded with hashed prod asset paths that
// don't exist in Vite's dev module graph — Vite's dev server then
// answers those with its SPA-fallback HTML instead of JS, which is
// exactly the "wrong MIME type" / NS_ERROR_CORRUPTED_CONTENT failure
// reported for the admin page. Only the backend-owned endpoints need
// to cross the proxy.
'/admin/api': serverTarget,
'/admin/login': serverTarget,
'/admin/logout': serverTarget,
},
},
});