- Fix mustChangePassword session flag for users with pw=NULL - Add PrF (Private Führung) as new ArtFuehrung type - Split datetime-local into separate date + TimePicker5 (5-min steps, auto-repeat) - Responsive Beginn/Ende layout: stacked on mobile, inline on desktop - Sort BEOs alphabetically by Kürzel in selector - Title shows active kuppel; hide user display in header - Selected BEOs show Kürzel only (name stays in dropdown) - Session timeout reduced to 1 hour - Add CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm run dev # Development server
npm run build # Production build (run after every change to verify)
npm run lint # ESLint
No test suite exists. Deploy via ./deploy.sh [tag] — builds multiplatform Docker image (amd64 + arm64) and pushes to docker.citysensor.de.
Architecture
Next.js 16 App Router application. All pages are server components; interactive parts are Client Components in app/MainClient.tsx and components/.
Auth flow: Users come from the existing MySQL beos table (not a separate users table). Login via app/login/actions.ts → lib/auth.ts (bcryptjs). Sessions are JWT cookies via jose (lib/session.ts, 1-hour expiry). If pw IS NULL, the default password is logbuch123 and mustChangePassword is forced to true. The middleware file exports proxy (not middleware) — Next.js 16 requirement.
Database: MySQL, database name sternwarte, via lib/db.ts connection pool. The pre-existing beos table has non-standard columns: `kürzel` (umlaut → always needs backticks), pw, id (all lowercase). The DB charset is latin1 — avoid non-ASCII characters in SQL WHERE clauses; use LIKE 'Ascii%' prefix patterns instead.
SQL in JS: MySQL backticks inside JS template literals cause parse errors. Write complex queries using string concatenation (+), not template literals. LIMIT cannot be a parameterized placeholder in complex grouped queries — embed it directly after validating: LIST_SQL + \ LIMIT ${limit}``.
API routes (app/api/): all check getSession() and return 401 if unauthenticated. The logbuch list query uses GROUP_CONCAT to aggregate BEOs and Objekte into comma-separated strings per entry.
Key components
CustomSelect: replaces native<select>everywhere — iOS/Android native popups ignore CSS sizing. SupportskeepOpenprop for multi-select use cases (BEOs, Objekte).TimePicker5: custom time picker, no native<input type="time">. Shows HH:MM with ▲/▼ buttons, 5-minute steps, auto-repeat on hold (400 ms delay → 1-hour steps at 350 ms). Keyboard: ↑/↓.LogbuchForm: Beginn/Ende stored as"YYYY-MM-DDTHH:MM"strings. Date and time are split into separate<input type="date">+<TimePicker5>. Beginn date change syncs Ende date automatically.LogbuchList: acceptscompactandlimitprops. Compact mode used for the 5-entry preview below the form on desktop (hidden lg:block).
Data model
ArtFuehrung is stored as abbreviations in the DB (RF, SF, PrF, BEOS, SonF, TD, Beob, ToT, Sonst). Display names are in ARTEN_MAP in types/logbuch.ts. BEOS and TD hide the Besucher and Objekte fields. SonF pre-selects "Sonne" as the only object.
Deployment
output: 'standalone' is set in next.config.ts for Docker. The MySQL container name in production is db — set DB_HOST=db in the server's environment.