Replaces the Angular 18 app with Next.js 16 / React 19 / Tailwind 4 / shadcn, the same stack and design system as nachklang-feedback, planned in docs/rewrite-plan.md. - Admin: all calendars in one mobile-friendly list (month groups, calendar chips, Kommend/Vergangen/Alle, search, sort, filters in the URL), an edit sheet for create/edit/duplicate/delete, publish confirmation, and an "Abonnieren" dialog with subscription links for every calendar. - Public landing page: subscribe links from env instead of hardcoded, plus the next public events, server-rendered and revalidated every 5 minutes. - All times handled explicitly as Europe/Berlin; whole-day events stored as 00:00-23:59. Fixes carried over from the Angular app: the move dialog leaking a cancelled calendar change, saves that looked saved when they failed, in-place Date mutation, and duplicated calendar-id mappings. - Same shared-session auth as before (proxy.ts gate, /admin/me, 401 vs 403). - 112 Vitest tests over lib/; docs in docs/ (architecture, features, gotchas, API wish list, deployment). Needs the API branch feature/calendar-ical-escaping-subscriptions deployed first (GET /calendar/events/subscriptions). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
@AGENTS.md
Commands
npm run dev # Dev server at http://localhost:4200/ - the port is required, see Authentication
npm run build # Production build (output: standalone; postbuild copies .next/static in)
npm run lint # ESLint (eslint-config-next)
npm test # Vitest, pure logic in lib/ (runs with TZ=America/New_York on purpose)
NEXT_PUBLIC_API_BASE_URL and NEXT_PUBLIC_ADMIN_APP_URL must be set (.env.local, from
.env.local.example); lib/env.ts fails the build without them.
Docs
docs/ is kept current as part of every change - features in features.md, surprises in
gotchas.md, API requests in api-wishlist.md.
- docs/architecture.md - routes, auth, data flow, time handling
- docs/features.md - what the app does
- docs/gotchas.md - read before assuming how the API, Next or Base UI behaves
- docs/api-wishlist.md - API changes this app would like
- docs/deployment.md - Plesk build/run, switchover, rollback
- docs/rewrite-plan.md - the Angular → Next rewrite: decisions and progress log
Architecture
Next.js 16 (App Router) + React 19 + TypeScript + Tailwind 4 + shadcn (Base UI primitives), the same
stack and design system as nachklang-feedback. Rewritten from Angular in September 2026. German UI.
app/page.tsx- public landing page (server component,revalidate = 300).app/admin/- the Verwaltung:layout.tsxis the session gate,page.tsxthe event list,login/page.tsxa forwarder to the admin app.components/admin/- list, toolbar, chips,EventSheet(create/edit/duplicate/delete) andEventForm,PublishDialog,SubscriptionsDialog, hooksuseEvents/useListState.lib/- all logic that isn't rendering, and all of it is unit-tested:api.ts,dates.ts(Berlin time),event-form.ts(form rules, validation, payloads),filter.ts,url-state.ts,calendars.ts(the one calendar list),events.ts(parsing the API's raw rows).proxy.ts- cookie-presence gate on/admin/*.
Rules worth knowing before editing (details in docs/):
- Every wall-clock time is Europe/Berlin, whatever the browser's zone. Forms hold strings, the API gets UTC instants. Whole-day events are 00:00-23:59 Berlin, end inclusive.
- Always send the whole event. The API's PUT defaults missing fields (
status→PUBLIC,repeatFrequency→''). - Calendars live in
lib/calendars.tsand must matchcalendarNamesin the API. - After
npx shadcn add, pointcnimports back at@/lib/utilsand drop thecn/next-themespackages it adds (seedocs/gotchas.md).
Authentication
This app has no login form and no accounts of its own. Since the auth cutover
(docs/calendar-auth-migration.md in the API repo) it shares one identity with the tickets,
feedback and admin apps: accounts live in the admin app, and the session is an httpOnly
cookie on .nachklang.art that the API's host sets. Consequences that cannot be designed
around:
credentials: "include"is mandatory on every call (lib/api.tssets it once). Without it the browser sends no cookie and the API answers 401.- This app can never read or verify the session. It calls
GET /admin/meand believes the answer; the API'srequireAppAccess('calendar')is the actual gate./admin/meanswers 200 for an account without the calendar permission - checkapps. - 401 and 403 mean different things and must not be collapsed. 401 means "nobody is signed
in" and is the only one worth redirecting to the login page - redirecting on 403 produces a
loop where signing in succeeds and lands straight back on the refusal. See
app/admin/layout.tsx. - Sign-out ends the session for all four apps; there is only one.
The dev server must be reachable at a port the API trusts. npm run dev pins 4200 (next dev -p 4200), which
is in better-auth's dev localhostOrigins and in the admin app's
NEXT_PUBLIC_ALLOWED_REDIRECT_ORIGINS; another port fails sign-out and the return redirect,
not the sign-in.
The public calendar stays anonymous. GET /calendar/events/public/json needs no session
at all, because nachklang.art reads it to show the next upcoming event - and so does this app's
landing page, server-side.
Local testing
Use a throwaway database, never shared infrastructure: the API repo's docker/init scripts seed a
MariaDB container (podman works) including the dev@nachklang.art account; start the API from a
scratch directory with its own .env. The exact commands are in the progress log of
docs/rewrite-plan.md ("Local test environment").