Files
Calendar_Frontend/CLAUDE.md
PaddyandClaude Opus 5.5 0cda40f85e Rewrite the calendar frontend in Next.js
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>
2026-09-28 22:15:59 +02:00

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.

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.tsx is the session gate, page.tsx the event list, login/page.tsx a forwarder to the admin app.
  • components/admin/ - list, toolbar, chips, EventSheet (create/edit/duplicate/delete) and EventForm, PublishDialog, SubscriptionsDialog, hooks useEvents / 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.ts and must match calendarNames in the API.
  • After npx shadcn add, point cn imports back at @/lib/utils and drop the cn / next-themes packages it adds (see docs/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.ts sets 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/me and believes the answer; the API's requireAppAccess('calendar') is the actual gate. /admin/me answers 200 for an account without the calendar permission - check apps.
  • 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").