Build and Push Lunchtime Images (Kaniko) / build-and-push (push) Successful in 1m44s
My Orders: year calendar and lunch profile first, chips and "How you compare" removed, open and paid payments merged into one table (open first, then newest). Activity is now a line chart with a cumulative switch and finer buckets (daily up to ~3 months, weekly up to ~13). Cards get subtle elevation and tighter spacing. Responsive: phone tab strip instead of the side menu, heatmap fits narrow screens, grouped column-chart labels are truncated, the manager weekday table scrolls instead of overflowing. Tooling: viewports.mjs (presets, overflow detector), shoot.mjs --sizes and --select, tests/responsive.ui.spec.ts, docs/ui-testing.md. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
61 lines
5.7 KiB
Markdown
61 lines
5.7 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Overview
|
|
|
|
Lunchtime is a self-hosted group lunch ordering app: FastAPI + SQLite backend (Python 3.12), React 18 + TypeScript + Vite + Ant Design frontend, nginx in front. All app code lives under `src/`. No linter is configured; end-to-end/API tests live in `src/e2e/` (Playwright, see below).
|
|
|
|
## Workflow
|
|
|
|
Development happens solely on `main`: no feature branches, worktrees or PRs. Commit directly to `main`.
|
|
|
|
## Commands
|
|
|
|
Full dev stack (backend with `--reload`, Vite HMR, nginx, Mailpit); also what the devcontainer starts:
|
|
|
|
```bash
|
|
cd src && docker compose up -d # app at http://localhost:8020, Mailpit at http://localhost:8025
|
|
```
|
|
|
|
Without Docker (native stack: uvicorn `--reload` on :5000, Vite HMR on **:8020** proxying `/api` to the backend, Mailpit on :8025 / SMTP :1025):
|
|
|
|
```bash
|
|
src/dev/devenv.sh setup # backend venv (src/backend/.venv), npm ci for frontend + e2e, Mailpit binary into src/.devenv/bin
|
|
src/dev/devenv.sh start # idempotent; waits until everything answers. Also: stop | restart | status | logs [service] | reset (wipes DB + uploads)
|
|
cd src/frontend && npx tsc --noEmit # type checking (vite build does not run tsc)
|
|
```
|
|
|
|
The native stack uses `src/dev/config.yaml` (via the `CONFIG_PATH` env var) instead of the root `config.yaml`, stores data in `src/backend/data/`, sets `ADMIN_EMAILS=admin@example.com`, and keeps pids/logs in `src/.devenv/`. `API_PROXY_TARGET` turns on Vite's `/api` proxy; it's unset under Compose, where nginx does the routing.
|
|
|
|
### Testing (requires a running stack; both Compose and native serve the app on :8020)
|
|
|
|
```bash
|
|
cd src/e2e
|
|
npx playwright test # everything; --project=api or --project=ui to narrow down
|
|
node shoot.mjs / /create "{order}" "{admin}" /stats [--as=user|admin|anon] [--dark] [--mobile] [--full] [--sizes=check|all|WxH,...] [--select=css]
|
|
```
|
|
|
|
- Logging in uses the real flow: `lib.mjs` `signIn(email?)` registers (or migrates, for an existing email), reads the confirmation mail from Mailpit's API and confirms it, giving back a user ID. In the browser, a user is "logged in" by setting localStorage `lunchtime_token` before load (`loginAs` in `fixtures.ts`). Fixtures: `user` (fresh per test), `admin` (the ADMIN_EMAILS account), `userPage`.
|
|
- `lib.mjs` also has `api()` (fetch with `X-User-Id`), `createOrder()` (with `SAMPLE_MENU`) and `submit()`. Use them to seed data instead of clicking through the UI.
|
|
- **After any layout change** run the responsive checks (phone, tablet, desktop; overflow detection; `tests/responsive.ui.spec.ts`): see `docs/ui-testing.md`. `shoot.mjs --sizes=...` exits 1 and prints `OVERFLOW` when a page is wider than its viewport.
|
|
- `shoot.mjs` prints PNG paths (default dir `src/e2e/screenshots/`, gitignored) plus console errors and failed requests. `{order}`/`{admin}` seed an order that has a menu and two submissions. Use it to view the UI after visual changes.
|
|
- Ant Design Select: its `role=option` nodes are hidden a11y helpers, so click `.ant-select-item-option` instead.
|
|
- Playwright is pinned to 1.65.0 to match the Chromium build in `PLAYWRIGHT_BROWSERS_PATH`. If they diverge, `npx playwright install chromium`.
|
|
|
|
CI (`.gitea/workflows/build-and-push-images.yaml`) builds and pushes backend/frontend images with Kaniko on changes to `src/**`.
|
|
|
|
## Architecture
|
|
|
|
**Backend** (`src/backend/app/`): almost every route is in the single `main.py` (~1300 lines), with Pydantic request models at the top. `services/` holds helpers (accounts/tokens, email, per-order file storage, submissions, config loading); `db.py` creates the schema inline in `init_db()` (`CREATE TABLE IF NOT EXISTS` plus ad-hoc `ALTER TABLE` migrations — there is no migration tool) and maps rows to dicts.
|
|
|
|
**Auth model:** there are no passwords or sessions. The client generates a user ID and sends it in the `X-User-Id` header (`get_existing_user_id` dependency). Per-order access is via tokens stored in `user_order_tokens` (`admin_token` = order owner, `submission_token` = participant); routes under `/api/orders/{id}/admin/...` check the caller's admin token. Email confirmation, user-ID change, email change and account migration flows use `account_confirmation_tokens` and send mail through `email_service`.
|
|
|
|
**Storage is split:** SQLite (`burger_orders.db` in `DB_PATH`) holds orders, submissions, user profiles and tokens. Per-order content is stored as files under `data/` next to the app, **keyed by the order's `admin_token`**: `configs/<token>.json` (menu/form definition), `descriptions/<token>.md`, `templates/`, `payments/`, and uploaded `images/` (served at `/api/images`). Deleting an order must also delete these files (see `delete_order` in `main.py`).
|
|
|
|
**App config:** `config.yaml` (mounted read-only at `/app/config.yaml`) controls `public_base_url`, SMTP settings and UI announcements; loaded by `services/config_service.py` and exposed via `GET /api/config`.
|
|
|
|
**Frontend** (`src/frontend/src/`): no router library — `lib/routing.ts` parses `window.location` into `home | create | order | admin` routes and `App.tsx` switches between the four `views/`. `lib/api.ts` wraps `fetch` (adds `X-User-Id`, clears the stored ID on "invalid user id" 401s); `lib/services/*` are typed per-domain API clients; `hooks/useApiRequest.ts` and `components/utils/AsyncContent.tsx` handle loading/error state. `lib/orderTemplating.ts` and `lib/orderFormatting.ts` render submissions into the text that is shared/exported from the form config. `TODO.md` tracks planned admin-view work.
|
|
|
|
Dev note: Compose mounts only `backend/app` and selected frontend source into the containers, so changes to `requirements.txt`/`package.json` need a rebuild. `src/frontend/dist/` is committed build output.
|