From e2a60e71a34468f2767b01585da4318bcad8f618 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Fri, 29 May 2026 08:12:35 +0100 Subject: [PATCH 01/26] . --- CLAUDE.md | 289 ++++++++++++++++++ README.md | 4 +- frontend/e2e/visuals.spec.ts | 50 ++- frontend/public/apple-touch-icon.png | Bin 0 -> 5239 bytes frontend/public/favicon.ico | Bin 15086 -> 2786 bytes frontend/public/favicon.svg | 13 + frontend/public/icons/icon-128x128.png | Bin 2875 -> 3188 bytes frontend/public/icons/icon-144x144.png | Bin 3077 -> 3731 bytes frontend/public/icons/icon-152x152.png | Bin 3293 -> 4002 bytes frontend/public/icons/icon-192x192.png | Bin 4306 -> 5682 bytes frontend/public/icons/icon-384x384.png | Bin 11028 -> 14215 bytes frontend/public/icons/icon-512x512.png | Bin 16332 -> 20715 bytes frontend/public/icons/icon-72x72.png | Bin 1995 -> 1605 bytes frontend/public/icons/icon-96x96.png | Bin 2404 -> 2199 bytes frontend/public/logo-mark.svg | 13 + frontend/public/logo-mono.svg | 13 + frontend/public/logo.svg | 13 + frontend/public/og-image.png | Bin 0 -> 215061 bytes .../app/components/block/block.component.ts | 6 +- .../components/modal/block-edit.component.ts | 29 +- .../app/components/modal/modal.component.ts | 8 + .../components/modal/settings.component.ts | 13 +- .../modal/tower-settings.component.ts | 49 +-- .../app/components/page/page.component.scss | 51 +++- .../app/components/pages/pages.component.scss | 18 +- .../double-slider/double-slider.component.ts | 11 + .../app/components/tasks/tasks.component.ts | 4 + .../app/components/tower/tower.component.ts | 14 + .../components/welcome/welcome.component.ts | 10 +- frontend/src/index.html | 10 + 30 files changed, 585 insertions(+), 33 deletions(-) create mode 100644 CLAUDE.md create mode 100644 frontend/public/apple-touch-icon.png create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/public/logo-mark.svg create mode 100644 frontend/public/logo-mono.svg create mode 100644 frontend/public/logo.svg create mode 100644 frontend/public/og-image.png diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c6d0986 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,289 @@ +# Life Towers — agent notes + +This file captures the load-bearing things to know about this codebase. Read it before making non-trivial changes. + +## What this is + +A personal-productivity TODO app where each user has multiple **pages**, each page holds several **towers** (vertical task columns), and each tower has **blocks** (atomic tasks). Pending blocks live in a **tasks accordion** at the top of the tower; completed ones fall into the tower as colored squares (the "falling animation" is the defining visual). Date-range slider filters which done blocks are visible. + +This is a port/modernisation of a legacy Angular 7 app. **Design parity with the legacy is a hard requirement** — the user cares deeply about the original aesthetic. The legacy source lives at `_legacy_reference/` (gitignored) and is the source of truth for any visual question. A detailed style guide is at `docs/DESIGN.md`. + +## Repo layout + +``` +backend/ # FastAPI + SQLite (WAL) + src/life_towers/ + main.py # ASGI app, lifespan, static mount + SPA fallback + api.py # Routes (/api/v1/{health,register,data}) + auth.py # Bearer UUIDv4 → user_id + db.py # sqlite3 factory + migration runner + limits.py # slowapi limiter + payload-size middleware + models.py # pydantic v2 schemas + logging.py # structlog JSON + migrations/ # 001_initial.sql, 002_keep_tasks_open.sql, … + tests/test_api.py # pytest + httpx AsyncClient + pyproject.toml # uv-managed +frontend/ # Angular 21+ standalone, signals, zoneless + src/app/ + components/ + pages/ # Top-level: page selector + Settings button + page/ # Tower row + slider + trash zone + confirm-delete + tower/ # White tower card: tasks accordion + add-block + falling stack + name input + block/ # Colored square (1/6 tower width) + tasks/ # Pending-blocks accordion with tickbox + welcome/ # Zero-state intro modal + "Try an example" + modal/ # Generic backdrop shell + sub-modals (block-edit carousel, settings, tower-settings) + shared/ # select-add, toggle, double-slider, color-picker, icon + services/ + api.service.ts # HttpClient wrapper, exact API contract + store.service.ts # signal-based store, debounced PUT, retry, loadExample + modal-state.service.ts # global open-modal counter (drag locking) + models/index.ts # Page / Tower / Block / HslColor TS interfaces + utils/{color,hash}.ts + library/ # Legacy SCSS dropped in verbatim — DO NOT REFACTOR + public/assets/ # SVG icons (arrow, pen, plus-sign, trash, x-sign) + src/assets/fonts/ # Self-hosted woff2 (Open Sans Condensed, Raleway, …) + e2e/ # Playwright (smoke + visuals) +docs/ + api-spec.md # Single source of truth for the HTTP API contract + DESIGN.md # Legacy visual spec (verbatim SCSS quotes) +_legacy_reference/ # Original Angular 7 source — read-only, gitignored +Dockerfile # Multi-stage: node:22-alpine build → python:3.13-slim runtime +docker-compose.yml # Production single-container +docker-compose.dev.yml # Ephemeral volume for Playwright runs +.forgejo/workflows/ # CI + deploy +``` + +## Tech stack quickrefs + +- **Frontend**: Angular 21+, **standalone components** (no NgModule), `signal()` / `computed()` / `effect()` / `linkedSignal()`, **zoneless change detection** (`provideZonelessChangeDetection`), `@if`/`@for` control flow, **OnPush** everywhere, esbuild builder (`@angular/build:application`), Reactive Forms, Angular Service Worker (PWA), Angular CDK (drag-drop, A11yModule for focus trap) +- **Backend**: FastAPI, pydantic v2, slowapi (rate limiting), structlog, **sqlite3 with WAL + foreign_keys ON** every connection, uv-managed deps +- **Runtime topology**: single Docker container — FastAPI process serves both `/api/v1/*` JSON endpoints AND the built Angular SPA as static files with SPA fallback. Behind nginx; uvicorn launched with `--proxy-headers --forwarded-allow-ips=*` +- **Storage**: SQLite at `/data/life-towers.db` on a named Docker volume. Tree-replace semantics (PUT replaces user's full tree atomically inside `BEGIN IMMEDIATE`) + +## Build / dev / test / deploy + +```bash +# Full local stack (ephemeral data) +docker compose -f docker-compose.dev.yml up --build -d # http://localhost:8000 +docker compose -f docker-compose.dev.yml down -v # teardown + +# Frontend dev (with backend proxied via proxy.conf.json) +cd frontend && npm start # ng serve on :4200, /api → :8000 +cd frontend && npm run build # production bundle to dist/frontend/browser/ +cd frontend && npm test # vitest (--run already in the script) +cd frontend && npm run test:e2e # Playwright + +# Backend dev +cd backend && uv sync +cd backend && uv run pytest -v +cd backend && uv run uvicorn life_towers.main:app --reload # :8000 + +# E2E in the sandbox: host-to-container port forwarding is broken here. +# Run Playwright INSIDE a container on the docker network instead: +docker run --rm \ + --network life-towers_default \ + -v "$(pwd)/frontend:/work" -w /work \ + -e PLAYWRIGHT_BASE_URL=http://life-towers:8000 \ + mcr.microsoft.com/playwright:v1.60.0-noble \ + npx playwright test +``` + +## Design system — the legacy is the source of truth + +`docs/DESIGN.md` is the comprehensive spec. Key tokens: + +```scss +// _legacy_reference/frontend/src/library/common-variables.scss +$accent-color: #a2666f; // rose +$text-color: #5d576b; // muted purple-grey (also iOS theme-color) +$light-color: #ffffff; +$background-gradient: linear-gradient(90deg, #fff9e07f 0, #ffd6d67f 100%); // 50% alpha — modal backdrop +$background-gradient-opaque: linear-gradient(90deg, #fffcf0 0, #ffebeb 100%); // body background +$shadow: 0 0 1.5px 1.5px rgba(0,0,0,0.1), 0 0 3px 2px rgba(0,0,0,0.05); +$shadow-border: 0 0 0 0.75px rgba(0,0,0,0.1); // hairline +$normal-font: 'Open Sans Condensed', sans-serif; +$title-font: 'Raleway', serif; +$mobile-width: 520px; +``` + +Spacing tokens (`library/main.scss`): +```scss +:root { + --large-padding: 30px; // 20px on mobile + --medium-padding: 15px; + --small-padding: 10px; // 7.5px on mobile + --border-radius: 5px; // 3px on mobile +} +``` + +Font sizes (`library/text.scss`): `--larger/large/medium/small-font-size` = `22/18/16/11` desktop, `20/16/14/10` mobile. + +**Animation timings**: +- `$long-animation-time: 200ms` — opacity, hover transforms, modal entry +- `$short-animation-time: 100ms` — tighter transitions (red trash-highlight overlay) +- **Falling animation**: `transform 1.5s cubic-bezier(0.5, 0, 1, 0)` (gravity ease-in) +- **Modal opacity entry/exit**: `300ms` + +## Architectural conventions to follow + +### Components + +- Every component is **standalone**, **OnPush**, with `signal()`-based local state. No NgModule. +- Templates use **`@if` / `@for` / `@switch`** — never `*ngIf`/`*ngFor`. +- Inline templates + inline styles in `@Component({ template: \`...\`, styles: \`...\` })` is the norm. Larger components use templateUrl + styleUrl (only `pages.component`, `page.component`). +- SCSS inside `styles:` template literal — **`//` comments are fine inside SCSS but watch for backticks**; `// `display: contents`` inside a template literal closes it early. Use `/* */` if you need to mention CSS strings in comments. +- Library files (`library/*.scss`) are dropped from the legacy verbatim. Don't refactor them. Components `@import '../../../library/main'` (or similar relative depth). + +### State + +- `StoreService` is the single source of truth (signal-based). +- Mutations update the signal immediately (**optimistic**) and call `scheduleSave()` which debounces 750ms and PUTs the full tree. +- Failure mode: exponential backoff up to 5 attempts (1s, 2s, 4s, 8s, 16s). +- LocalStorage cache key: `life-towers.cache.v4`. Token: `life-towers.token.v4`. +- `init()` flow: stored token? Use it. No token? Mint via `uuidV4()` → register → GET data. On 401: re-register the SAME token (idempotent server-side), never silently mint a fresh one (would orphan data). + +### Reactivity caveats with zoneless + +- Plain field mutations in event handlers (`(click)="x = true"`) still trigger CD because Angular's event manager marks the view dirty — even with zoneless. But any async mutation outside an Angular event (setTimeout, raw addEventListener, MutationObserver) **will not** trigger CD; use signals there. +- `effect()` running on `signal()` reads triggers re-runs; wrap writes in `untracked()` to avoid loops. +- For input-driven derived state that the user can also override (e.g. `tasks.expanded` seeded from `initiallyOpen` but also clickable), use either `linkedSignal` or `effect()` + a flag (the codebase uses the latter for `tasks.component`). + +### Sync model + +- **Tree-replace**: `PUT /api/v1/data` sends the entire user hierarchy atomically. Backend wraps in `BEGIN IMMEDIATE`, deletes existing pages (cascading to towers + blocks via FK), inserts new rows. `position` columns track ordering. +- **No granular endpoints** for individual entity CRUD. Keep it tree-replace. +- Spec is in `docs/api-spec.md`. Backend pydantic models match it. Frontend `models/index.ts` matches it. Field names are **snake_case** on both sides. + +### Backend + +- All endpoints are inside `APIRouter(prefix="/api/v1")`. Spec drives behavior — if you change a limit, update both spec and code. +- Migrations: package data under `src/life_towers/migrations/`, loaded via `importlib.resources.files("life_towers").joinpath("migrations")`. The runner tracks applied state in a `schema_migrations(filename TEXT PRIMARY KEY, applied_at INTEGER)` table. +- All sqlite connections must do `PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA busy_timeout=5000`. The `get_connection()` factory does this. +- Errors → JSON `{"error": code, "detail": str}` via a single `HTTPException` handler in `main.py`. Stack traces never leak (logged server-side). +- Rate limits via slowapi: `/register` 30/hour/IP, `GET /data` 60/min/token, `PUT /data` 30/min/token. + +## Visual + interaction details that bit me + +### Falling animation (tower.component) + +The `.block-container` has `transform: scaleY(-1)` so blocks visually fall from the TOP into the bottom of the tower. Each block default-positions at `translateY(500%)` via a `*` rule; the inline `[style.transform]="b._transform"` binding overrides per-block. + +When exactly **one** new done block is added, the `reconcile()` method: +1. Sets the new block's `_transform: 'translateY(500%)'` and `_opacity: '0'` (off-screen) +2. Calls `requestAnimationFrame` → `requestAnimationFrame` to let the browser paint the initial state +3. Sets `_anim: 'descend'`, `_transform: 'translateY(0)'`, `_opacity: '1'` — the CSS transition fires + +**Critical**: `grewByOne` detection is position-independent (set-difference). When a tickbox flips a pending block to done, the new entry inserts at its original `tower.blocks` index, not appended. Use the new ID, not `styled[length-1]`. + +The **date range** slider asymmetry: blocks below `range.from` are removed from `visibleBlocks` entirely (instant shuffle, no gap), blocks above `range.to` get `_anim: 'ascend'` and stay in the list flying up. `prevDoneIds` tracks the full `allDone` array — not the filtered styled list — so range expansions don't mis-fire as "new block". + +### Block-edit carousel (modal/block-edit.component) + +- Lives at `position: fixed; z-index: 10001` to escape the modal dialog wrapper and cover the viewport +- Two placeholder cards flank the real cards so the active card can fully center via `scroll-snap-align: center` +- `.mask` overlay on non-active cards has three tiers: `active opacity 0`, `near-active 0.55`, default `1`. Card opacity also tiered (1 / 0.85 / 0.6) mimicking the legacy `1.33*(1-t/2)` curve +- Backdrop click (anywhere not a non-placeholder card) closes the modal +- Delete on an existing card **does not** close the modal — it stays open and the card re-renders out of the list +- Auto-save on tag/toggle change; description deferred to blur + +### Select-add (shared/select-add) + +- Has a `.top` chip and a `.bottom` slide-down panel. `:has(.bottom.open) .top, .background { border-radius: var(--border-radius) var(--border-radius) 0 0 }` squares the bottom corners when open so the chip and panel read as one card +- Shadow seam between chip + panel solved with `clip-path: inset(...)`: `.background.active` clips bottom (`inset(-6px -6px 0 -6px)`), `.bottom.open` clips top (`inset(0 -6px -6px -6px)`). 6px > the total $shadow spread (5px) so neither edge bleeds across the seam +- Closing animation requires a two-transition setup: default state has `transition: ... visibility 0s $long-animation-time` (visibility delays on close), `.open` overrides with `visibility 0s 0s` (instant on open) + +### Modal shell (modal/modal.component) + +- `:host { display: contents }` — critical. Without this, the `lt-modal` host element takes a flex slot in `pages.component`'s `inner-spacing` layout, pushing the Settings button up when the modal mounts +- `section.modal` is `position: fixed; z-index: 10000` with `transition: opacity 300ms`. The component flips `active = true` in `ngAfterViewInit` via `setTimeout(0)` so the opacity 0 → 1 transition runs +- `ModalStateService.open()` / `.close()` are called in `AfterViewInit`/`OnDestroy`. `page.component` reads `modalState.anyOpen` and binds it to every tower's `[cdkDragDisabled]` so users can't drag towers behind an open modal + +### Carousel card date format + +```ts +formatDate(ts: number): string { + return new Date(ts * 1000).toLocaleString(undefined, { + year: 'numeric', month: 'short', day: 'numeric', + hour: '2-digit', minute: '2-digit', + }); // "May 28, 2026, 14:32" +} +``` + +### Double-slider relative-time labels + +`page.component.ts` formats with `Intl.RelativeTimeFormat(undefined, { numeric: 'auto', style: 'short' })`. Buckets: `<45s` second, `<45min` minute, `<22h` hour, `<26d` day, `<320d` month, else year. Labels are **deduped** by string (multiple distinct timestamps that round to the same "5 hr ago" appear once). + +When `values.length` grows (new block added), the slider snaps the **higher** of `oneValue`/`otherValue` to `MAX - 1` so the newest entry is always visible; the lower thumb (the user's left edge) stays put. + +### Color picker (shared/color-picker) + +- Row of 12 preset color swatches + a rainbow hue slider + a big preview swatch +- Saturation and lightness are FIXED at 0.7 / 0.55. Only hue varies +- Preset hues `[0, 15, 30, 45, 195, 215, 235, 255, 280, 310, 335, 355]` — skips the green/yellow zone (60°–180°) which muddies with the rose accent palette + +### Tickbox (tasks/tasks.component) + +- Always-visible `✓` glyph at `opacity: 0.45` (rest), `0.85` (hover), `1` (active) +- `transform: translateY(2px)` is critical — Unicode `✓` has uneven font-metrics, geometric centering looks half a square too high. The 2px nudge moves it to optical center +- `:active` state must re-state the translateY or the glyph jumps when pressed + +### Mobile responsive + +- `$mobile-width: 520px` is the single breakpoint +- Tower row: on mobile, `width: calc(66vw - var(--medium-padding)) !important` per tower with `overflow-x: auto` + `scroll-snap-type: x mandatory`. About 1.5 columns visible by default +- Carousel cards: `width: 85vw`, placeholders `7.5vw`, carousel `padding: 0 7.5vw` → snap-center lines up perfectly +- Modal cards (settings/tower-settings/welcome/confirm-delete): `width: 88vw; padding: var(--medium-padding)` on mobile. Confirm-buttons stack vertically with full width +- Block hover effect (`gravitate`) gated behind `@media (hover: hover) and (pointer: fine)` so touch devices don't get stuck scale-up +- Viewport meta blocks pinch-zoom: `` +- `scrollbar-gutter: stable` + `overflow-y: scroll` on `html` so modal opens don't shift content sideways + +### Tower drag-drop + +- CDK drag-drop on `.towers cdkDropList[orientation=horizontal]`, each tower is `cdkDrag` +- Trash zone: `` is OUTSIDE `.towers` (anchored to `page.component :host` at `bottom: 8px; left: 50%`). The legacy structure — moving it inside `.towers` breaks because it becomes a flex item +- Trash-highlight: `pointerenter` on trash → direct DOM `document.querySelector('.cdk-drag-preview').classList.add('trash-highlight')`. Matches legacy approach +- Drop over trash → opens a confirm modal (no immediate delete) + +### Welcome modal + example data + +- Shows when `!store.loading() && store.pages().length === 0`. Auto-dismisses when `pages().length > 0` +- "Try an example" calls `store.loadExample()` which creates a "Hobbies" page with three towers (Reading, Side projects, Exercise) at varied `created_at` ages so the slider has interesting labels + +## Frontend sharp edges + +- **`crypto.randomUUID()` requires a secure context** (HTTPS or localhost). On a plain-HTTP origin behind nginx, it throws and `init()` rejects, leaving `loading = true` forever. Always fall back to `crypto.getRandomValues` (`uuidV4()` helper in `store.service.ts`) +- **Angular 17+ deprecated `fileReplacements`** for env-per-build. Don't use `environment.ts` — use relative API paths everywhere + `proxy.conf.json` for ng serve +- **Angular 19+ `application` builder copies `public/`, NOT `src/assets/`**. SVG icons must live at `frontend/public/assets/` to be served at `/assets/foo.svg`. Fonts referenced via `url()` in styles.scss go through the CSS asset pipeline and emit to `/media/` +- **Backticks inside `styles:` template literal** close the string early — break TS parsing +- **Async iframe-like spawning of agents in this sandbox** (host port forwarding) doesn't work — run e2e via Playwright Docker image on the same `life-towers_default` network + +## Backend sharp edges + +- The 256 KiB payload cap is enforced by middleware reading `Content-Length`. Chunked encoding bypasses the check. Defense-in-depth would also stream `request.stream()` +- 10 MiB per-user quota is checked against the request body, NOT the existing user's stored total. With the 256 KiB request cap, the 10 MiB check is effectively unreachable. Documented spec gap +- The migrations directory was moved to `src/life_towers/migrations/` (inside the package) to ship as `importlib.resources` data — don't recreate `backend/migrations/` + +## Visual e2e + +- `frontend/e2e/visuals.spec.ts` is the source-of-truth for "what should this look like." It captures ~15 screenshots into `frontend/visuals/` (gitignored) +- Add a screenshot every time we land a visual change. Don't merge if it broke the visuals run +- Mobile screenshots use a separate test that spawns its own `browser.newContext({ viewport: { width: 390, height: 844 } })` — that's the iPhone 14 Pro viewport + +## Conventions to enforce on changes + +- Never use the legacy `frontend-legacy/` or `backend-legacy/` paths — those folders are GONE. `_legacy_reference/` is the reference, gitignored +- Never refactor `library/*.scss` — those files are dropped verbatim from the legacy and downstream components depend on their exports +- Never change the API contract in `docs/api-spec.md` without updating both pydantic models, api.py, frontend `models/index.ts`, and the backend tests in `tests/test_api.py` +- Never put modal-related elements as direct flex children of a layout container without `:host { display: contents }` on the modal — they'll add invisible flex slots +- Always run `npm run build` after a frontend change to catch TS/template errors that don't surface in the IDE +- Always run the visuals test after a visual change. Pull the screenshots, look at them, compare to the legacy + +## Where to look next + +- For the API contract: `docs/api-spec.md` +- For visual design questions: `docs/DESIGN.md` + `_legacy_reference/frontend/src/library/` +- For the sync flow: `frontend/src/app/services/store.service.ts` (the `init()`, `flush()`, `scheduleSave()` chain) +- For the falling animation: `frontend/src/app/components/tower/tower.component.ts:reconcile()` +- For drag-drop + trash: `frontend/src/app/components/page/page.component.{ts,html,scss}` +- For deploy: `Dockerfile`, `docker-compose.yml`, `.forgejo/workflows/` diff --git a/README.md b/README.md index fbc43f6..f8f5a93 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Life Towers +

+ Life Towers +

A personal productivity tool for organising tasks into visual "towers" of blocks, grouped on pages. Each user is identified by a client-generated token — no accounts, no passwords. diff --git a/frontend/e2e/visuals.spec.ts b/frontend/e2e/visuals.spec.ts index 95f94ba..756a4d7 100644 --- a/frontend/e2e/visuals.spec.ts +++ b/frontend/e2e/visuals.spec.ts @@ -7,8 +7,15 @@ import { test } from '@playwright/test'; test.describe('Life Towers visuals', () => { test('capture key UI states', async ({ page }) => { await page.goto('/'); - await page.waitForSelector('text=Add a new page to get started!', { timeout: 15000 }); - await page.screenshot({ path: 'visuals/01-empty-state.png', fullPage: true }); + await page.waitForSelector('text=Welcome to Life Towers', { timeout: 15000 }); + await page.waitForTimeout(350); // let the welcome modal finish fade-in + await page.screenshot({ path: 'visuals/01-welcome-modal.png', fullPage: true }); + + // Dismiss the welcome modal with Start fresh, then continue. + await page.getByRole('button', { name: 'Start fresh' }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.screenshot({ path: 'visuals/01b-empty-state-after-dismiss.png', fullPage: true }); // Open the page dropdown (without creating a page yet). await page.locator('lt-select-add .top').first().click(); @@ -26,7 +33,7 @@ test.describe('Life Towers visuals', () => { await page.locator('img[alt="Add tower"]').click(); await page.waitForSelector('section.modal.active'); await page.waitForTimeout(350); - await page.locator('input[placeholder="Tower name…"]').fill('Reading'); + await page.locator('input[placeholder="New tower"]').fill('Reading'); await page.screenshot({ path: 'visuals/03-new-tower-modal.png', fullPage: true }); await page.locator('lt-tower-settings button[type="submit"]').click(); await page.waitForSelector('section.modal', { state: 'detached' }); @@ -100,7 +107,7 @@ test.describe('Life Towers visuals', () => { await page.locator('img[alt="Add tower"]').click(); await page.waitForSelector('section.modal.active'); await page.waitForTimeout(350); - await page.locator('input[placeholder="Tower name…"]').fill('Side projects'); + await page.locator('input[placeholder="New tower"]').fill('Side projects'); await page.locator('lt-tower-settings button[type="submit"]').click(); await page.waitForSelector('section.modal', { state: 'detached' }); await page.waitForTimeout(300); @@ -157,4 +164,39 @@ test.describe('Life Towers visuals', () => { await page.waitForTimeout(350); await page.screenshot({ path: 'visuals/11-settings-modal.png', fullPage: true }); }); + + test('"Try an example" populates a sample page', async ({ page }) => { + await page.goto('/'); + await page.waitForSelector('text=Welcome to Life Towers', { timeout: 15000 }); + await page.waitForTimeout(350); + await page.getByRole('button', { name: 'Try an example' }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + await page.waitForTimeout(1800); + await page.screenshot({ path: 'visuals/12-example-data.png', fullPage: true }); + }); + + test('Mobile viewport — welcome + example + carousel', async ({ browser }) => { + const ctx = await browser.newContext({ viewport: { width: 390, height: 844 } }); + const page = await ctx.newPage(); + + await page.goto((process.env['PLAYWRIGHT_BASE_URL'] ?? 'http://localhost:8000') + '/'); + await page.waitForSelector('text=Welcome to Life Towers', { timeout: 15000 }); + await page.waitForTimeout(350); + await page.screenshot({ path: 'visuals/13-mobile-welcome.png', fullPage: true }); + + await page.getByRole('button', { name: 'Try an example' }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + await page.waitForTimeout(1800); + await page.screenshot({ path: 'visuals/14-mobile-populated.png', fullPage: true }); + + // Open the block-edit carousel for the first tower's first task. + await page.locator('lt-tasks .container').first().click(); + await page.waitForTimeout(400); + await page.locator('lt-tasks .task-container').first().click(); + await page.waitForSelector('section.modal.active'); + await page.waitForTimeout(400); + await page.screenshot({ path: 'visuals/15-mobile-carousel.png', fullPage: true }); + + await ctx.close(); + }); }); diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7ed3e652afa89c08c0c85b7d51984e11135daf56 GIT binary patch literal 5239 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 Nr__JTFzvMr^FKp4S3v*( diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..8635f77 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/public/icons/icon-128x128.png b/frontend/public/icons/icon-128x128.png index 5a9a2ccdb34a97a06510d04238d8bedd8e063d3a..8a5f0fb9189352a51a34c1cfde1e441d64bf9505 100644 GIT binary patch literal 3188 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 ZUjT_fSx^~10a*Y5002ovPDHLkV1ljCVV(d0 diff --git a/frontend/public/icons/icon-144x144.png b/frontend/public/icons/icon-144x144.png index 11702cd7bd67cee3c26172d0b69968b568c210e0..d612bbb414badbd30c3890d63db78e28b76fa6fd 100644 GIT binary patch literal 3731 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 literal 3077 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 literal 3293 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 diff --git a/frontend/public/icons/icon-384x384.png b/frontend/public/icons/icon-384x384.png index 613ac793e063b1da8d0dc36dcbd1cc0ae620291d..de679b4e64c48c3f73093a5d7a27f8f523c66f54 100644 GIT binary patch literal 14215 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 literal 11028 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 diff --git a/frontend/public/icons/icon-72x72.png b/frontend/public/icons/icon-72x72.png index 033724e15f5485c4d86af398a68ca293e6116f72..209d6844c9f923d5bcf01eecb4e9baae943beb46 100644 GIT binary patch delta 1599 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 literal 2404 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 W@LC!C(#4Aa0000 + + + + + + + + + + + + diff --git a/frontend/public/logo-mono.svg b/frontend/public/logo-mono.svg new file mode 100644 index 0000000..d6d553d --- /dev/null +++ b/frontend/public/logo-mono.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg new file mode 100644 index 0000000..769b8e0 --- /dev/null +++ b/frontend/public/logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/public/og-image.png b/frontend/public/og-image.png new file mode 100644 index 0000000000000000000000000000000000000000..c13be7034a54957f47af726490dfebea9bf24ebb GIT binary patch literal 215061 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�gm5j24#6j(lU8l`(KSc{;rP>$2Q%7eQ# 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 literal 0 HcmV?d00001 diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 81da198..10fe47b 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -27,7 +27,11 @@ import { getColorOfTag } from '../../utils/color'; position: absolute; width: 100%; height: 100%; - @include gravitate(); + cursor: pointer; + + @media (hover: hover) and (pointer: fine) { + @include gravitate(); + } } } `, diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index a7d5d3b..7f1b0d0 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -177,6 +177,11 @@ interface EditedValue { overflow-x: auto; overflow-y: hidden; scroll-behavior: smooth; + scroll-snap-type: x mandatory; + + @media (max-width: $mobile-width) { + padding: 0 7.5vw; + } &::-webkit-scrollbar { width: 0; @@ -192,8 +197,12 @@ interface EditedValue { flex: 0 0 auto; width: 66vw; max-width: 400px; + scroll-snap-align: center; @media (max-width: $mobile-width) { - width: 300px; + width: 85vw; + max-width: 85vw; + padding: var(--medium-padding); + margin: calc(var(--medium-padding) / 2); opacity: 1 !important; } box-sizing: border-box; @@ -246,6 +255,10 @@ interface EditedValue { opacity: 0 !important; width: 60vw; max-width: 60vw; + @media (max-width: $mobile-width) { + width: 7.5vw; + max-width: 7.5vw; + } box-shadow: none; background: transparent; } @@ -256,7 +269,7 @@ interface EditedValue { .exit { position: absolute; - left: 0; + right: 0; @include exit(); } @@ -291,6 +304,13 @@ interface EditedValue { transform: translateY(-50%) translateX(-50%); } } + + @media (max-width: $mobile-width) { + lt-select-add, lt-toggle { + max-width: 100%; + width: 100%; + } + } } `, }) @@ -389,10 +409,13 @@ export class BlockEditComponent implements AfterViewInit { formatDate(ts: number): string { const d = new Date(ts * 1000); - return d.toLocaleDateString(undefined, { + // e.g. "May 28, 2026, 14:32" + return d.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', + hour: '2-digit', + minute: '2-digit', }); } diff --git a/frontend/src/app/components/modal/modal.component.ts b/frontend/src/app/components/modal/modal.component.ts index 4c854ce..3edda60 100644 --- a/frontend/src/app/components/modal/modal.component.ts +++ b/frontend/src/app/components/modal/modal.component.ts @@ -31,6 +31,14 @@ import { ModalStateService } from '../../services/modal-state.service'; styles: ` @import '../../../library/main'; + /* lt-modal host must not occupy a flex slot in its parent — section.modal + is position:fixed, but the host element itself would otherwise take a + slot and push siblings around when it mounts/unmounts. + display: contents removes the host box without affecting descendants. */ + :host { + display: contents; + } + section.modal { position: fixed; top: 0; diff --git a/frontend/src/app/components/modal/settings.component.ts b/frontend/src/app/components/modal/settings.component.ts index c428d64..7b0a076 100644 --- a/frontend/src/app/components/modal/settings.component.ts +++ b/frontend/src/app/components/modal/settings.component.ts @@ -111,7 +111,11 @@ const UUIDV4_RE = @include card(); width: 66vw; max-width: 480px; - @media (max-width: $mobile-width) { width: 300px; } + @media (max-width: $mobile-width) { + width: 88vw; + max-width: 88vw; + padding: var(--medium-padding); + } box-sizing: border-box; padding: var(--large-padding); position: relative; @@ -171,6 +175,13 @@ const UUIDV4_RE = button { margin: 0; flex: 0 0 auto; + max-width: 100%; + } + + @media (max-width: $mobile-width) { + flex-wrap: wrap; + input { width: 100%; } + button { margin-left: auto; } } } diff --git a/frontend/src/app/components/modal/tower-settings.component.ts b/frontend/src/app/components/modal/tower-settings.component.ts index 4d99802..04ad0e4 100644 --- a/frontend/src/app/components/modal/tower-settings.component.ts +++ b/frontend/src/app/components/modal/tower-settings.component.ts @@ -21,10 +21,7 @@ export interface TowerSettingsResult { imports: [ReactiveFormsModule, ColorPickerComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: ` -
-
-

{{ tower() ? 'Tower settings' : 'New tower' }}

-
+
- +
+ +
@@ -133,14 +136,14 @@ interface EditedValue { (input)="updateNewDescription($any($event.target).value)" > -
- + -
+ Already done +
+

Settings

@if (page()) { @@ -43,19 +43,23 @@ const UUIDV4_RE = aria-label="Page name" /> - +
+ - + +
- @if (tower()) { + + } @else { + } `, @@ -86,14 +88,14 @@ export interface TowerSettingsResult { border: 0; } - // Generous gap between the name input and the color picker — the picker - // is a substantial control and crowding it against the title looks busy. - .picker-row { - padding-top: var(--medium-padding); - } - button { display: block; + // Stay full-width on mobile, but switch to flex so forms.scss's + // bottom-alignment keeps the underline hugging the label in the 42px + // tap target (plain block centres the text and strands the underline). + @media (max-width: $mobile-width) { + display: flex; + } } } `, @@ -105,6 +107,7 @@ export class TowerSettingsComponent implements OnInit { readonly close = output(); private readonly fb = inject(FormBuilder); + private readonly destroyRef = inject(DestroyRef); form = this.fb.group({ name: ['', [Validators.required, Validators.maxLength(200)]], @@ -117,17 +120,36 @@ export class TowerSettingsComponent implements OnInit { if (t) { this.form.patchValue({ name: t.name }); this.currentColor = { ...t.base_color }; + + // Edit mode: persist name changes as they happen. Wire this up *after* + // the initial patchValue so seeding the form doesn't fire a save. + this.form.valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.autoSave()); } } onColorChange(color: HslColor): void { this.currentColor = color; + // In edit mode the picker is a live control — commit each change. + if (this.tower()) this.autoSave(); } onSubmit(): void { + // Only the create flow reaches here via its Submit button; edit mode + // auto-saves (and Enter on the single field is a harmless redundant save). if (this.form.invalid) return; - const v = this.form.value; - this.save.emit({ name: v.name ?? '', base_color: this.currentColor }); + this.emitSave(); + } + + /** Emit a save only when the form is valid (skips e.g. an empty name). */ + private autoSave(): void { + if (this.form.invalid) return; + this.emitSave(); + } + + private emitSave(): void { + this.save.emit({ name: this.form.value.name ?? '', base_color: this.currentColor }); } } diff --git a/frontend/src/app/components/page/page.component.html b/frontend/src/app/components/page/page.component.html index 34d0292..32c2c86 100644 --- a/frontend/src/app/components/page/page.component.html +++ b/frontend/src/app/components/page/page.component.html @@ -7,7 +7,7 @@ @for (tower of page().towers; track tower.id) { - + } diff --git a/frontend/src/app/components/page/page.component.scss b/frontend/src/app/components/page/page.component.scss index 9c9cc9f..4ae3ad3 100644 --- a/frontend/src/app/components/page/page.component.scss +++ b/frontend/src/app/components/page/page.component.scss @@ -5,6 +5,7 @@ flex-direction: column; height: 100%; + min-height: 0; position: relative; // anchor for absolute-positioned .trash @include inner-spacing(var(--large-padding)); @@ -18,10 +19,12 @@ justify-content: center; width: 100%; + box-sizing: border-box; margin-left: auto; margin-right: auto; - flex: 1 0 auto; + flex: 1 1 auto; + min-height: 0; transition: box-shadow $short-animation-time; @@ -55,6 +58,7 @@ max-width: 200px; box-sizing: content-box; flex: 0 0 auto; + min-height: 0; &:not(:nth-last-child(1)) { margin-right: var(--medium-padding); @@ -84,15 +88,22 @@ // without needing !important on every property — only the width triples need it // to beat the per-child-count selectors generated above. @media (max-width: $mobile-width) { + --mobile-tower-width: calc(66vw - var(--small-padding)); + --mobile-tower-side-padding: max( + var(--medium-padding), + calc((100% - var(--mobile-tower-width)) / 2) + ); + overflow-x: auto; overflow-y: visible; -webkit-overflow-scrolling: touch; scroll-snap-type: x mandatory; + scroll-padding-inline: var(--mobile-tower-side-padding); flex-wrap: nowrap; justify-content: flex-start; - // Side padding lets the first and last tower scroll fully into view. - padding: 0 var(--medium-padding); + padding: 0 var(--mobile-tower-side-padding); max-width: 100%; + gap: var(--medium-padding); &::-webkit-scrollbar { display: none; @@ -100,12 +111,16 @@ // Override the @for width-calc rules above. & > * { - width: calc(66vw - var(--small-padding)) !important; - max-width: calc(66vw - var(--small-padding)) !important; - min-width: calc(66vw - var(--small-padding)) !important; - scroll-snap-align: start; + width: var(--mobile-tower-width) !important; + max-width: var(--mobile-tower-width) !important; + min-width: var(--mobile-tower-width) !important; + scroll-snap-align: center; flex-shrink: 0; } + + & > *:not(:nth-last-child(1)) { + margin-right: 0; + } } } diff --git a/frontend/src/app/components/page/page.component.ts b/frontend/src/app/components/page/page.component.ts index 8e152a1..33c5b77 100644 --- a/frontend/src/app/components/page/page.component.ts +++ b/frontend/src/app/components/page/page.component.ts @@ -5,6 +5,7 @@ import { output, signal, inject, + HostListener, } from '@angular/core'; import { Page } from '../../models'; import { StoreService } from '../../services/store.service'; @@ -66,6 +67,7 @@ export class PageComponent { private readonly modalState = inject(ModalStateService); /** True while any lt-modal is mounted — used to lock tower drag. */ readonly modalOpen = this.modalState.anyOpen; + readonly mobileDragDisabled = signal(this.isMobileViewport()); readonly showAddTower = signal(false); readonly isDragging = signal(false); @@ -116,6 +118,18 @@ export class PageComponent { this.dateRange.set({ from: range.from as number, to: range.to as number }); } + @HostListener('window:resize') + onResize(): void { + this.mobileDragDisabled.set(this.isMobileViewport()); + } + + private isMobileViewport(): boolean { + return ( + typeof window !== 'undefined' && + window.matchMedia('(max-width: 520px), (pointer: coarse)').matches + ); + } + // ── Tower mutations ──────────────────────────────────────────────────────── onAddTower(result: TowerSettingsResult): void { diff --git a/frontend/src/app/components/pages/pages.component.scss b/frontend/src/app/components/pages/pages.component.scss index 786d65d..8a218ab 100644 --- a/frontend/src/app/components/pages/pages.component.scss +++ b/frontend/src/app/components/pages/pages.component.scss @@ -2,6 +2,7 @@ :host { height: 100%; + min-height: 0; display: flex; flex-direction: column; @@ -21,7 +22,8 @@ } .page-container { - flex: 1 0 auto; + flex: 1 1 auto; + min-height: 0; // Generous breathing room between the page selector dropdown and the // towers — the dropdown can open downward without crowding the towers. padding-top: var(--large-padding); diff --git a/frontend/src/app/components/shared/color-picker/color-picker.component.ts b/frontend/src/app/components/shared/color-picker/color-picker.component.ts index fe5104c..29292c3 100644 --- a/frontend/src/app/components/shared/color-picker/color-picker.component.ts +++ b/frontend/src/app/components/shared/color-picker/color-picker.component.ts @@ -61,7 +61,10 @@ const FIXED_L = 0.55; display: block; padding: var(--medium-padding); @include card(); - box-shadow: $shadow-border; + border: 1px solid rgba($text-color, 0.14); + box-shadow: inset 0 0 0 1px rgba($light-color, 0.7); + background-color: rgba($text-color, 0.025); + box-sizing: border-box; } .picker { @@ -76,35 +79,40 @@ const FIXED_L = 0.55; grid-template-columns: repeat(12, 1fr); gap: 6px; + @media (max-width: $mobile-width) { + grid-template-columns: repeat(6, 1fr); + gap: var(--small-padding); + } + .swatch { all: unset; cursor: pointer; aspect-ratio: 1; border-radius: 4px; - box-shadow: $shadow-border; + box-shadow: 0 0 0 1px rgba($text-color, 0.18); transition: transform $short-animation-time, box-shadow $long-animation-time; &:hover, &:focus-visible { - box-shadow: $shadow; + box-shadow: 0 0 0 2px $light-color, 0 0 0 4px rgba($text-color, 0.5); transform: scale(1.1); } &.active { - box-shadow: $shadow; + box-shadow: 0 0 0 2px $light-color, 0 0 0 4px rgba($text-color, 0.5); transform: scale(1.15); - outline: 2px solid $light-color; - outline-offset: 1px; } } } .hue-slider { + padding: 8px 0; + input[type='range'] { -webkit-appearance: none; appearance: none; width: 100%; - height: 12px; + height: 16px; border-radius: 1000px; background: linear-gradient( to right, @@ -119,11 +127,15 @@ const FIXED_L = 0.55; outline: none; cursor: pointer; + &:focus-visible { + box-shadow: 0 0 0 3px rgba($text-color, 0.35); + } + &::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; - height: 24px; - width: 24px; + height: 32px; + width: 32px; border-radius: 1000px; background-color: var(--thumb-color, #{$light-color}); box-shadow: 0 0 0 2px #{$light-color}, #{$shadow}; @@ -135,8 +147,8 @@ const FIXED_L = 0.55; } &::-moz-range-thumb { - height: 24px; - width: 24px; + height: 32px; + width: 32px; border-radius: 1000px; background-color: var(--thumb-color, white); border: 2px solid white; @@ -153,7 +165,7 @@ const FIXED_L = 0.55; .preview { height: 40px; border-radius: var(--border-radius); - box-shadow: $shadow-border; + box-shadow: 0 0 0 1px rgba($text-color, 0.18); } `, }) diff --git a/frontend/src/app/components/shared/select-add/select-add.component.ts b/frontend/src/app/components/shared/select-add/select-add.component.ts index 7e01d73..399763d 100644 --- a/frontend/src/app/components/shared/select-add/select-add.component.ts +++ b/frontend/src/app/components/shared/select-add/select-add.component.ts @@ -6,6 +6,9 @@ import { signal, OnChanges, SimpleChanges, + ElementRef, + HostListener, + inject, } from '@angular/core'; @Component({ @@ -19,7 +22,7 @@ import { [class.always-shadow]="alwaysDropShadow()" >
-
+

{{ resolvedSelected() ?? placeholder() }}

@@ -33,7 +36,9 @@ import { (blur)="onRename(item, $any($event.target).value)" /> } @else { -

{{ item }}

+ } }
@@ -43,9 +48,20 @@ import { placeholder="Add a value…" (keydown.enter)="onAdd(addInput.value); addInput.value = ''" /> - + @if (editable()) { - } @@ -58,6 +74,7 @@ import { @import '../../../../library/main'; $inner-padding: var(--medium-padding); + $dropdown-shadow: 0 4px 14px rgba($text-color, 0.16), $shadow-border; :host { display: block; @@ -80,13 +97,22 @@ import { align-items: center; position: relative; cursor: pointer; + min-height: 46px; + box-sizing: border-box; + gap: var(--small-padding); p { - display: inline-block; + display: block; @include sub-title-text(); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; } img.arrow { + flex: 0 0 auto; @include square(16px); transition: transform $long-animation-time; @@ -98,81 +124,129 @@ import { .bottom-container { width: 100%; - height: 300px; position: absolute; - overflow-y: hidden; + top: 100%; + left: 0; + right: 0; + overflow: visible; pointer-events: none; + z-index: 5; .bottom { - position: absolute; + position: relative; width: 100%; - pointer-events: all; + pointer-events: none; box-sizing: border-box; display: flex; flex-direction: column; align-items: flex-start; border-radius: 0 0 var(--border-radius) var(--border-radius); padding: $inner-padding; - padding-top: 0; - @include inner-spacing($inner-padding); + padding-top: var(--small-padding); + gap: var(--small-padding); // Default (closed) state — also the target of the close transition. background-color: transparent; box-shadow: none; - transform: translateY(-100%); + transform: translateY(-8px); + opacity: 0; visibility: hidden; + // Clip the top edge so the panel's shadow can't bleed back up into + // the chip area; sides + bottom get a 10px slack for the shadow. + clip-path: inset(0 -10px -10px -10px); // Delay the visibility change until after the slide animation finishes // so the panel stays visible while it animates closed. transition: transform $long-animation-time, + opacity $long-animation-time, background-color $long-animation-time, box-shadow $long-animation-time, visibility 0s $long-animation-time; &.open { visibility: visible; + pointer-events: all; transform: none; + opacity: 1; background-color: $light-color; - box-shadow: $shadow; - // Show shadow on left/right/bottom only; clip the top edge so the - // shadow doesn't bleed over the seam where .bottom meets .top. - clip-path: inset(0 -6px -6px -6px); + box-shadow: $dropdown-shadow; // On open, visibility flips immediately (no delay); transform + // colors + shadow animate over $long-animation-time. transition: transform $long-animation-time, + opacity $long-animation-time, background-color $long-animation-time, box-shadow $long-animation-time, visibility 0s 0s; } - p { + .option { @include sub-title-text(); - display: inline-block; + display: flex; + align-items: center; + width: 100%; + min-height: 36px; + margin: 0; + padding: 0; + border: 0; + background: transparent; text-align: left; cursor: pointer; + + &:after { + display: none; + } + + @media (max-width: $mobile-width) { + min-height: 42px; + } } input[type='text'] { @include sub-title-text(); width: 100%; + min-height: 36px; + box-sizing: border-box; + text-align: left; + + &::placeholder { + color: rgba($text-color, 0.72); + opacity: 1; + } + + @media (max-width: $mobile-width) { + min-height: 42px; + } } .add-row { - height: 32px; - @media (max-width: $mobile-width) { height: 24px; } + min-height: 40px; position: relative; width: 100%; display: flex; - align-items: center; + align-items: flex-end; gap: var(--small-padding); input[type='text'] { flex: 1; + min-height: 0; + padding: 0; + border-bottom: solid 2px transparent; + + &:focus, + &:focus-visible { + box-shadow: none; + border-bottom-color: $text-color; + } } button { margin: 0; - position: static; + position: relative; + flex: 0 0 auto; + + &.add-button { + align-self: flex-end; + } &.pen { opacity: 0.25; @@ -184,6 +258,10 @@ import { background: transparent; position: relative; + // Kill the global button's hover-grow underline pseudo-element + // for the icon-only edit control. + &:after { content: none; display: none; } + img { @include square(16px); } @@ -224,16 +302,18 @@ import { width: 100%; @include card(); z-index: 3; + box-sizing: border-box; transition: box-shadow $long-animation-time, height $long-animation-time, border-radius $long-animation-time; &.active { - box-shadow: $shadow; - // Show shadow on top/left/right only; clip the bottom edge so the - // shadow doesn't bleed over the seam where .top meets .bottom. - clip-path: inset(-6px -6px 0 -6px); + // Same shadow recipe as the panel below so the two halves read as + // one continuous card. Clip the bottom edge so this shadow doesn't + // bleed across the seam where .top meets .bottom. + box-shadow: $dropdown-shadow; + clip-path: inset(-10px -10px 0 -10px); } } @@ -251,25 +331,31 @@ import { } } + // Hover lifts the chip — but only when the dropdown is closed. When + // it's open the chip is already showing $dropdown-shadow and a hover + // override would make the top heavier than the panel below. &:hover { @media (min-width: $mobile-width) { - .background { box-shadow: $shadow; } + .background:not(.active) { box-shadow: $shadow; } } } &.shadow-border { .background.active { box-shadow: $shadow-border; - clip-path: inset(-6px -6px 0 -6px); + clip-path: inset(-10px -10px 0 -10px); + } + .bottom.open { + box-shadow: $shadow-border; } } &.shadow-border:hover { - .background { box-shadow: $shadow-border; } + .background:not(.active) { box-shadow: $shadow-border; } } &.always-shadow { - .background { box-shadow: $shadow; } + .background:not(.active) { box-shadow: $shadow; } // When open, clip the bottom so the always-on shadow doesn't bleed // over the seam; restore full shadow when closed. &:has(.bottom.open) .background { @@ -309,6 +395,7 @@ export class SelectAddComponent implements OnChanges { // ── Internal state ───────────────────────────────────────────────────────── readonly open = signal(false); readonly editing = signal(false); + private readonly host = inject(ElementRef); // Resolved values that merge old + new API protected resolvedItems(): string[] { @@ -332,6 +419,20 @@ export class SelectAddComponent implements OnChanges { // Nothing to do — signals handle reactivity } + @HostListener('document:click', ['$event']) + onDocumentClick(event: MouseEvent): void { + if (!this.open()) return; + if (!this.host.nativeElement.contains(event.target as Node)) { + this.open.set(false); + this.editing.set(false); + } + } + + toggleOpen(event: MouseEvent): void { + event.stopPropagation(); + this.open.update((v) => !v); + } + onSelectItem(item: string): void { this.select.emit(item); // Legacy compat: also emit the index diff --git a/frontend/src/app/components/shared/toggle/toggle.component.ts b/frontend/src/app/components/shared/toggle/toggle.component.ts index 5ec865e..3adc84e 100644 --- a/frontend/src/app/components/shared/toggle/toggle.component.ts +++ b/frontend/src/app/components/shared/toggle/toggle.component.ts @@ -30,7 +30,12 @@ import { $size: 30px; @include center-child(); - @include inner-spacing(var(--medium-padding), $horizontal: true); + gap: var(--medium-padding); + + @media (max-width: $mobile-width) { + width: 100%; + gap: var(--small-padding); + } .toggle { display: contents; @@ -41,7 +46,7 @@ import { // Fixed width (not max-width) so multiple toggles align column-wise // — the thumb position is identical across rows regardless of label. flex: 0 0 auto; - width: 4 * $size; + width: var(--toggle-label-width, #{4 * $size}); box-sizing: border-box; padding: 0 var(--small-padding); line-height: 1.3; @@ -50,10 +55,19 @@ import { &.active { font-weight: bold; } &:first-of-type { text-align: right; } &:last-of-type { text-align: left; } + + @media (max-width: $mobile-width) { + flex: 1 1 0; + width: auto; + min-width: 0; + padding: 0; + overflow-wrap: anywhere; + } } label { display: block; + flex: 0 0 auto; input[type='checkbox'] { -webkit-appearance: none; diff --git a/frontend/src/app/components/tasks/tasks.component.ts b/frontend/src/app/components/tasks/tasks.component.ts index 5a035d3..9ad521a 100644 --- a/frontend/src/app/components/tasks/tasks.component.ts +++ b/frontend/src/app/components/tasks/tasks.component.ts @@ -17,16 +17,26 @@ import { getColorOfTag } from '../../utils/color';
-

+

{{ pending().length === 0 ? '' : pending().length }} {{ pending().length === 0 ? '​' : pending().length === 1 ? 'task' : 'tasks' }}

@for (b of pending(); track b.id) {
@@ -34,11 +44,15 @@ import { getColorOfTag } from '../../utils/color'; type="button" class="tickbox" [style.background-color]="colorOf(b.tag)" + (pointerup)="$event.stopPropagation()" + (touchend)="$event.stopPropagation()" (click)="$event.stopPropagation(); markDone.emit(b)" [attr.aria-label]="'Mark ' + (b.description || b.tag) + ' done'" >

{{ b.description || b.tag }}

@@ -82,10 +96,24 @@ import { getColorOfTag } from '../../utils/color'; :first-child { margin-top: var(--small-padding); } - height: 0; box-sizing: border-box; - transition: height $long-animation-time; - overflow-y: hidden; + transition: max-height $long-animation-time; + + /* + * Clip during the open/close animation. We animate to the element's + * exact content height (read off #all in the template) so tall lists + * simply scroll inside the outer .container (max-height: 30vh) — + * .all-task itself is NOT a nested scroller. A nested overflow-y:auto + * here pops a scrollbar the instant a tickbox grows on hover, because + * its scale(1.05) + shadow widen the scrollable-overflow box. + */ + overflow: hidden; + + // Sideways breathing room so the clip doesn't shear the tickbox's + // hover shadow; negative side margins keep rows flush with the header, + // and the bottom padding clears the last row's shadow. + margin: 0 calc(var(--small-padding) / -2); + padding: 0 calc(var(--small-padding) / 2) calc(var(--small-padding) / 2); .task-container { display: flex; @@ -106,14 +134,16 @@ import { getColorOfTag } from '../../utils/color'; // done without opening the edit carousel. Hover & focus reveal a // subtle inner check mark. .tickbox { - flex: 0 0 auto; all: unset; // strip native button styles + flex: 0 0 24px; cursor: pointer; position: relative; box-sizing: border-box; @include square(24px); + min-width: 24px; + min-height: 24px; @media (max-width: $mobile-width) { - @include square(20px); + @include square(24px); } border-radius: 4px; box-shadow: $shadow-border; @@ -123,14 +153,25 @@ import { getColorOfTag } from '../../utils/color'; content: '✓'; position: absolute; inset: 0; + /* + * Neutralise the global animated-underline bar from + * forms.scss (button:after { height: 2px; width: 0->100% on + * hover; background-color: $text-color }). The all:unset on the + * button does NOT reach the pseudo-element, so without these + * resets the bar paints a dark stripe across the top AND + * squashes this box to 2px — which centres the glyph near the + * top instead of the middle. + */ + width: 100%; + height: 100%; + background: none; @include center-child(); color: $light-color; - font-size: 18px; - font-weight: bold; - line-height: 1; - opacity: 0.5; + font: bold 18px/1 $normal-font; // re-assert font (all:unset dropped it to serif) + opacity: 0; // hidden at rest — only revealed on hover/focus/active text-shadow: 0 0 1px rgba(0, 0, 0, 0.4); - transform: translateY(2px); + // The ✓ glyph sits a touch high in its em-box; nudge to optical centre. + transform: translateY(1px); transition: opacity $short-animation-time, transform $short-animation-time; } @@ -142,7 +183,7 @@ import { getColorOfTag } from '../../utils/color'; } &:active { transform: scale(0.95); - &::after { opacity: 1; transform: translateY(2px) scale(1.05); } + &::after { opacity: 1; transform: translateY(1px) scale(1.05); } } } @@ -152,10 +193,13 @@ import { getColorOfTag } from '../../utils/color'; overflow-x: hidden; text-align: left; flex: 1 1 auto; + min-width: 0; cursor: pointer; @media (max-width: $mobile-width) { - font-size: calc(var(--small-font-size) / 2 + var(--medium-font-size) / 2); + font-size: var(--medium-font-size); + font-weight: 500; + filter: saturate(0.85) brightness(0.82); } position: relative; @@ -178,6 +222,7 @@ export class TasksComponent { readonly edit = output(); readonly expanded = signal(false); + private lastToggleAt = 0; constructor() { // Re-sync `expanded` whenever the `initiallyOpen` input changes so flipping @@ -193,4 +238,15 @@ export class TasksComponent { colorOf(tag: string): string { return getColorOfTag(tag, this.baseColor()); } + + toggleExpanded(event: Event): void { + event.stopPropagation(); + if (event.type === 'touchend') { + event.preventDefault(); + } + const now = Date.now(); + if (now - this.lastToggleAt < 250) return; + this.lastToggleAt = now; + this.expanded.update((v) => !v); + } } diff --git a/frontend/src/app/components/tower/tower.component.ts b/frontend/src/app/components/tower/tower.component.ts index 4e56d36..2141ce0 100644 --- a/frontend/src/app/components/tower/tower.component.ts +++ b/frontend/src/app/components/tower/tower.component.ts @@ -7,6 +7,10 @@ import { computed, effect, untracked, + AfterViewInit, + OnDestroy, + ElementRef, + viewChild, } from '@angular/core'; import { Tower, Block } from '../../models'; import { BlockComponent } from '../block/block.component'; @@ -26,6 +30,8 @@ interface StyledBlock extends Block { _opacity: string; } +const BLOCKS_PER_ROW = 6; + @Component({ selector: 'lt-tower', standalone: true, @@ -33,51 +39,79 @@ interface StyledBlock extends Block { changeDetection: ChangeDetectionStrategy.OnPush, template: `
-
+
+ + + +
+ +
- Add block +
+ Add block -
-
- @for (b of visibleBlocks(); track b.id) { - - } +
+
+ @for (b of visibleBlocks(); track b.id) { + + } +
- + @if (hiddenBlockCount() > 0) { +

+ {{ hiddenBlockCount() }} more

+ }
@if (editEntry(); as entry) { - + } `, @@ -100,7 +139,9 @@ interface StyledBlock extends Block { @import '../../../library/main'; :host { + display: block; cursor: pointer; + min-height: 0; &.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); @@ -140,7 +181,7 @@ interface StyledBlock extends Block { } } - input { + .tower-header { display: none; } } @@ -149,8 +190,10 @@ interface StyledBlock extends Block { display: flex; flex-direction: column; align-items: center; + position: relative; max-width: 100%; height: 100%; + min-height: 0; @include inner-spacing(var(--small-padding)); @@ -158,7 +201,14 @@ interface StyledBlock extends Block { display: flex; flex-direction: column; flex: 1 1 auto; + margin-bottom: 0; + min-height: 0; position: relative; + box-sizing: border-box; + container-type: inline-size; + --block-stack-height: 0px; + --add-block-size: 48px; + --add-block-clearance: var(--medium-padding); @include card(); overflow: hidden; @@ -168,7 +218,9 @@ interface StyledBlock extends Block { @media (max-width: $mobile-width) { @include inner-spacing(var(--small-padding)); - padding: var(--small-padding); + padding: 0; + --add-block-size: 32px; + --add-block-clearance: var(--small-padding); } width: 100%; @@ -189,16 +241,16 @@ interface StyledBlock extends Block { } lt-tasks { - flex: 0 0 auto; + flex: 0 1 auto; min-height: 56px; - max-height: 30vh; - overflow: hidden; + max-height: min(30vh, 45%); + overflow: auto; display: block; width: 100%; @media (max-width: $mobile-width) { min-height: 44px; - max-height: 25vh; + max-height: min(25vh, 45%); } .container { @@ -207,80 +259,169 @@ interface StyledBlock extends Block { } } - img.add-block { - flex: 0 0 auto; - align-self: center; - margin: var(--medium-padding) 0; - } - - img { + .stack-zone { position: relative; - z-index: 2; - height: 48px; + flex: 1 1 auto; + min-height: 0; + width: 100%; - @media (max-width: $mobile-width) { - height: 32px; + img { + position: relative; + z-index: 2; + height: 48px; + + @media (max-width: $mobile-width) { + height: 32px; + } + + opacity: 0.33; + transition: opacity $long-animation-time; + cursor: pointer; + + &:hover { + opacity: 1; + } } - opacity: 0.33; - transition: opacity $long-animation-time; - cursor: pointer; - - &:hover { - opacity: 1; - } - } - - .block-container-container { - position: relative; - flex: 1; - - .block-container { - display: flex; - flex-flow: row wrap; - justify-content: flex-start; - align-content: flex-start; - align-items: flex-end; + img.add-block { position: absolute; - bottom: 0; - width: 100%; - transform: scaleY(-1); + z-index: 3; + left: 50%; + top: max( + 0px, + min( + calc(50% - var(--add-block-size) / 2), + calc(100% - var(--block-stack-height) - var(--add-block-clearance) - var(--add-block-size)) + ) + ); + transform: translateX(-50%); + } - /* Default resting position for all blocks before JS sets them */ - * { - transform: translateY(500%); - } + .block-container-container { + position: absolute; + inset: 0; - .descend { - transition: transform 1.5s cubic-bezier(0.5, 0, 1, 0), - opacity 500ms cubic-bezier(0.5, 0, 1, 0); - } + .block-container { + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + align-content: flex-start; + align-items: flex-end; + position: absolute; + bottom: 0; + width: 100%; + transform: scaleY(-1); - .ascend { - transition: transform 1.5s cubic-bezier(0.5, 0, 1, 0), - opacity 500ms cubic-bezier(0.5, 0, 1, 0) 1s; + /* Default resting position for all blocks before JS sets them */ + * { + transform: translateY(500%); + } + + .descend { + transition: transform 1.5s cubic-bezier(0.5, 0, 1, 0), + opacity 500ms cubic-bezier(0.5, 0, 1, 0); + } + + .ascend { + transition: transform 1.5s cubic-bezier(0.5, 0, 1, 0), + opacity 500ms cubic-bezier(0.5, 0, 1, 0) 1s; + } } } } } - input[type='text'] { - font-size: var(--small-font-size); + .more-blocks { + @include small-text(); + position: absolute; + top: calc(100% + var(--small-padding)); + left: 0; + right: 0; + margin: 0; + line-height: 1; + color: rgba($text-color, 0.72); text-align: center; + pointer-events: none; + user-select: none; + } - @media (min-width: $mobile-width) { - width: 50%; + .tower-header { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + + input[type='text'] { + box-sizing: border-box; + min-width: 0; + font-size: var(--small-font-size); + text-align: center; + + /* Truncate long titles with an ellipsis instead of wrapping. */ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + /* Reserve a symmetric gutter on each side. The right one keeps the + title and its focus underline clear of the absolutely-positioned + pen (so the underline stops before it); the equal left one keeps + the centered title optically centered. (pen 22px + 4px gap.) */ + width: calc(100% - 52px); + + @media (max-width: $mobile-width) { + width: calc(100% - 60px); + } } - @media (max-width: $mobile-width) { - width: 100%; + .edit-tower { + all: unset; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + box-sizing: border-box; + width: 22px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--border-radius); + cursor: pointer; + opacity: 0.35; + transition: opacity $short-animation-time, box-shadow $long-animation-time, background-color $short-animation-time; + + /* Suppress the global button's animated hover underline + (button::after), which the 'all: unset' reset doesn't reach. */ + &:after { + content: none; + } + + img { + width: 13px; + height: 13px; + opacity: 1; + } + + &:hover, + &:focus-visible { + opacity: 1; + background-color: rgba($light-color, 0.86); + box-shadow: $shadow-border; + } + + @media (max-width: $mobile-width) { + width: 26px; + height: 26px; + opacity: 0.55; + } } } } } `, }) -export class TowerComponent { +export class TowerComponent implements AfterViewInit, OnDestroy { // ── Inputs ───────────────────────────────────────────────────────────────── readonly tower = input.required(); /** Set by page component when this tower is being dragged over trash. */ @@ -308,6 +449,11 @@ export class TowerComponent { * which list of blocks to show and which one to focus initially. */ readonly editEntry = signal(null); readonly showSettings = signal(false); + readonly hiddenBlockCount = signal(0); + + private readonly stackZone = viewChild>('stackZone'); + private readonly maxVisibleBlocks = signal(null); + private resizeObserver: ResizeObserver | null = null; // ── Derived ──────────────────────────────────────────────────────────────── /** Pending (not-done) blocks — fed to the tasks accordion. */ @@ -324,6 +470,13 @@ export class TowerComponent { return this.tower().blocks.filter(b => b.is_done === isDone); }); + readonly editViewTitle = computed(() => { + const entry = this.editEntry(); + if (!entry) return ''; + const prefix = entry.filter === 'done' ? 'Completed tasks' : 'Tasks'; + return `${prefix} of ${this.tower().name}`; + }); + /** Unique tags from existing blocks of this tower. */ readonly towerTags = computed(() => { const set = new Set(); @@ -339,6 +492,11 @@ export class TowerComponent { private readonly _visibleBlocks = signal([]); readonly visibleBlocks = this._visibleBlocks.asReadonly(); + readonly blockStackHeight = computed(() => { + const rows = Math.ceil(this.visibleBlocks().length / BLOCKS_PER_ROW); + const cqw = rows * (100 / BLOCKS_PER_ROW); + return rows === 0 ? '0px' : `${Number(cqw.toFixed(4))}cqw`; + }); private prevDoneIds: string[] = []; private isFirstRun = true; @@ -346,15 +504,69 @@ export class TowerComponent { constructor() { effect(() => { const range = this.dateRange(); - // Always keep ALL done blocks in the visible list. The date filter - // only flips per-block `_anim` so the 1.5s ascend/descend transition - // can animate them in and out of the falling stack. + const maxVisibleBlocks = this.maxVisibleBlocks(); + // Reconcile all done blocks, then cap the rendered stack to the rows + // that fit below the tasks and add button. const allDone = this.tower().blocks.filter((b) => b.is_done); - untracked(() => this.reconcile(allDone, range)); + untracked(() => this.reconcile(allDone, range, maxVisibleBlocks)); }); } - private reconcile(allDone: Block[], range: { from: number; to: number } | null): void { + ngAfterViewInit(): void { + const stackZone = this.stackZone()?.nativeElement; + if (!stackZone) return; + + this.measureBlockCapacity(); + + if (typeof ResizeObserver !== 'undefined') { + this.resizeObserver = new ResizeObserver(() => this.measureBlockCapacity()); + this.resizeObserver.observe(stackZone); + } + + if (typeof requestAnimationFrame === 'function') { + requestAnimationFrame(() => this.measureBlockCapacity()); + } + } + + ngOnDestroy(): void { + this.resizeObserver?.disconnect(); + } + + private measureBlockCapacity(): void { + const stackZone = this.stackZone()?.nativeElement; + if (!stackZone) return; + + const width = stackZone.clientWidth; + const height = stackZone.clientHeight; + if (width <= 0 || height <= 0) { + this.maxVisibleBlocks.set(0); + return; + } + + const styles = getComputedStyle(stackZone); + const addBlockSize = this.parseCssPixels(styles.getPropertyValue('--add-block-size'), 48); + const fallbackClearance = addBlockSize <= 32 ? 7.5 : 15; + const clearance = this.parseCssPixels( + styles.getPropertyValue('--add-block-clearance'), + fallbackClearance, + ); + + const rowHeight = width / BLOCKS_PER_ROW; + const availableHeight = Math.max(0, height - addBlockSize - 2 * clearance); + const rows = Math.floor((availableHeight + 0.5) / rowHeight); + this.maxVisibleBlocks.set(Math.max(0, rows) * BLOCKS_PER_ROW); + } + + private parseCssPixels(value: string, fallback: number): number { + const parsed = Number.parseFloat(value); + return Number.isFinite(parsed) ? parsed : fallback; + } + + private reconcile( + allDone: Block[], + range: { from: number; to: number } | null, + maxVisibleBlocks: number | null, + ): void { const ids = allDone.map((b) => b.id); const prev = this.prevDoneIds; const prevSet = new Set(prev); @@ -393,16 +605,39 @@ export class TowerComponent { }); } + const visibleLimit = + maxVisibleBlocks === null ? Number.POSITIVE_INFINITY : Math.max(0, maxVisibleBlocks); + const restingBlocks = styled.filter((b) => b._opacity === '1'); + const hiddenCount = Number.isFinite(visibleLimit) + ? Math.max(0, restingBlocks.length - visibleLimit) + : 0; + let visibleStyled = styled; + if (Number.isFinite(visibleLimit)) { + const shownRestingBlocks = + hiddenCount > 0 ? restingBlocks.slice(hiddenCount) : restingBlocks; + const remainingSlots = Math.max(0, visibleLimit - shownRestingBlocks.length); + const transitioningBlocks = + remainingSlots > 0 + ? styled.filter((b) => b._opacity !== '1').slice(-remainingSlots) + : []; + const shownIds = new Set([ + ...shownRestingBlocks.map((b) => b.id), + ...transitioningBlocks.map((b) => b.id), + ]); + visibleStyled = styled.filter((b) => shownIds.has(b.id)); + } + this.hiddenBlockCount.set(hiddenCount); + if (grewByOne) { const newId = newIds[0]; - const newBlock = styled.find(b => b.id === newId); + const newBlock = visibleStyled.find(b => b.id === newId); if (newBlock && inRange(newBlock)) { // Snap newly-added in-range block to start position, then on the next // paint flip it back to rest — that's what makes it visibly fall. newBlock._anim = ''; newBlock._transform = 'translateY(500%)'; newBlock._opacity = '0'; - this._visibleBlocks.set(styled); + this._visibleBlocks.set(visibleStyled); requestAnimationFrame(() => { requestAnimationFrame(() => { newBlock._anim = 'descend'; @@ -417,7 +652,7 @@ export class TowerComponent { } } // existing fall-through path (no growth, first run, or new block out of range): - this._visibleBlocks.set(styled); + this._visibleBlocks.set(visibleStyled); this.prevDoneIds = ids; this.isFirstRun = false; @@ -472,7 +707,8 @@ export class TowerComponent { } onTowerSave(result: TowerSettingsResult): void { - this.showSettings.set(false); + // Tower edits auto-save, so this fires on every change and must NOT close + // the modal — the user closes it via the exit button / backdrop. this.updateTower.emit(result); } diff --git a/frontend/src/app/components/welcome/welcome.component.ts b/frontend/src/app/components/welcome/welcome.component.ts index 5842d25..e1168b2 100644 --- a/frontend/src/app/components/welcome/welcome.component.ts +++ b/frontend/src/app/components/welcome/welcome.component.ts @@ -7,7 +7,7 @@ import { Component, ChangeDetectionStrategy, output } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush, template: `
- +

Welcome to Life Towers

@@ -60,6 +60,11 @@ import { Component, ChangeDetectionStrategy, output } from '@angular/core'; h2 { text-align: center; margin-bottom: var(--medium-padding); + padding: 0 36px; + + @media (max-width: $mobile-width) { + padding: 0 28px; + } } p.lead { color: $text-color; } @@ -85,6 +90,10 @@ import { Component, ChangeDetectionStrategy, output } from '@angular/core'; border-bottom-color: rgba($accent-color, 0.33); &:after { background-color: $accent-color; } } + + @media (max-width: $mobile-width) { + gap: var(--small-padding); + } } } `, diff --git a/frontend/src/app/services/analytics.service.ts b/frontend/src/app/services/analytics.service.ts new file mode 100644 index 0000000..84d3e0c --- /dev/null +++ b/frontend/src/app/services/analytics.service.ts @@ -0,0 +1,65 @@ +import { Injectable, isDevMode } from '@angular/core'; +import { + init as plausibleInit, + track as plausibleTrack, + type PlausibleEventOptions, +} from '@plausible-analytics/tracker'; + +const ANALYTICS_AUTO_CAPTURE_PAGEVIEWS = true; +const ANALYTICS_DOMAIN = 'schmelczer.dev/towers'; +const ANALYTICS_ENDPOINT = 'https://stats.schmelczer.dev/status'; + +@Injectable({ providedIn: 'root' }) +export class AnalyticsService { + private isInitialized = false; + private hasTrackedStart = false; + + init(): void { + if (this.isInitialized) return; + try { + plausibleInit({ + domain: ANALYTICS_DOMAIN, + endpoint: ANALYTICS_ENDPOINT, + autoCapturePageviews: ANALYTICS_AUTO_CAPTURE_PAGEVIEWS, + logging: isDevMode(), + }); + this.isInitialized = true; + } catch (error) { + console.warn('Could not initialize analytics.', error); + } + } + + private track(eventName: string, options: PlausibleEventOptions = {}): void { + try { + plausibleTrack(eventName, options); + } catch (error) { + console.warn(`Could not track analytics event "${eventName}".`, error); + } + } + + trackStart(): void { + if (this.hasTrackedStart) return; + this.hasTrackedStart = true; + this.track('Start'); + } + + trackExampleLoaded(): void { + this.track('Example Loaded'); + } + + trackPageCreated(): void { + this.track('Page Created'); + } + + trackTowerCreated(): void { + this.track('Tower Created'); + } + + trackBlockCreated({ isDone }: { isDone: boolean }): void { + this.track('Block Created', { props: { isDone: String(isDone) } }); + } + + trackBlockCompleted(): void { + this.track('Block Completed'); + } +} diff --git a/frontend/src/app/services/store.service.ts b/frontend/src/app/services/store.service.ts index a1e3e5b..f158445 100644 --- a/frontend/src/app/services/store.service.ts +++ b/frontend/src/app/services/store.service.ts @@ -1,5 +1,6 @@ import { Injectable, inject, signal, computed, OnDestroy } from '@angular/core'; import { ApiService } from './api.service'; +import { AnalyticsService } from './analytics.service'; import { Page, Tower, Block, TreeDto, SaveStatus, HslColor } from '../models'; const TOKEN_KEY = 'life-towers.token.v4'; @@ -55,9 +56,24 @@ interface PendingPut { tree: TreeDto; } +interface ExampleBlockSeed { + tag: string; + desc: string; + done: boolean; + ageHrs: number; +} + +interface ExampleDonePattern { + tag: string; + desc: (sequence: number) => string; +} + +const EXAMPLE_DONE_BLOCKS_PER_TOWER = 120; + @Injectable({ providedIn: 'root' }) export class StoreService implements OnDestroy { private readonly api = inject(ApiService); + private readonly analytics = inject(AnalyticsService); // ── State ────────────────────────────────────────────────────────────────── private readonly _pages = signal([]); @@ -226,6 +242,8 @@ export class StoreService implements OnDestroy { towers: [], }; this._pages.update((pages) => [...pages, page]); + this.analytics.trackStart(); + this.analytics.trackPageCreated(); this.scheduleSave(); } @@ -256,6 +274,8 @@ export class StoreService implements OnDestroy { this._pages.update((pages) => pages.map((p) => (p.id === pageId ? { ...p, towers: [...p.towers, tower] } : p)), ); + this.analytics.trackStart(); + this.analytics.trackTowerCreated(); this.scheduleSave(); } @@ -318,6 +338,8 @@ export class StoreService implements OnDestroy { : p, ), ); + this.analytics.trackStart(); + this.analytics.trackBlockCreated({ isDone: is_done }); this.scheduleSave(); } @@ -336,7 +358,7 @@ export class StoreService implements OnDestroy { t.id === towerId ? { ...t, - blocks: t.blocks.map((b) => (b.id === blockId ? { ...b, ...patch } : b)), + blocks: this.patchBlockList(t.blocks, blockId, patch).blocks, } : t, ), @@ -366,6 +388,7 @@ export class StoreService implements OnDestroy { } toggleBlock(pageId: string, towerId: string, blockId: string): void { + let becameDone = false; this._pages.update((pages) => pages.map((p) => p.id === pageId @@ -373,21 +396,58 @@ export class StoreService implements OnDestroy { ...p, towers: p.towers.map((t) => t.id === towerId - ? { - ...t, - blocks: t.blocks.map((b) => - b.id === blockId ? { ...b, is_done: !b.is_done } : b, - ), - } + ? (() => { + const block = t.blocks.find((b) => b.id === blockId); + if (!block) return t; + const result = this.patchBlockList(t.blocks, blockId, { + is_done: !block.is_done, + }); + becameDone = result.becameDone; + return { ...t, blocks: result.blocks }; + })() : t, ), } : p, ), ); + this.analytics.trackStart(); + if (becameDone) this.analytics.trackBlockCompleted(); this.scheduleSave(); } + private patchBlockList( + blocks: Block[], + blockId: string, + patch: Partial>, + ): { blocks: Block[]; becameDone: boolean } { + const nextBlocks: Block[] = []; + let completedBlock: Block | null = null; + let becameDone = false; + + for (const block of blocks) { + if (block.id !== blockId) { + nextBlocks.push(block); + continue; + } + + const updated: Block = { ...block, ...patch }; + becameDone = !block.is_done && updated.is_done; + if (becameDone) { + // Display newly completed todos as the newest square, without changing + // created_at because the date slider still filters on creation time. + completedBlock = updated; + } else { + nextBlocks.push(updated); + } + } + + return { + blocks: completedBlock ? [...nextBlocks, completedBlock] : nextBlocks, + becameDone, + }; + } + /** * Switch to a different user's token. Any pending writes for the OLD * account must be cancelled first — otherwise a queued PUT could fire @@ -529,30 +589,56 @@ export class StoreService implements OnDestroy { default_date_from: null, default_date_to: null, towers: [ + // Done blocks are listed oldest-first so they fill the falling stack + // oldest → newest (left → right), matching the slider's old → new + // labels; pending tasks stay on top for the accordion. this.makeExampleTower('Reading', { h: 0.05, s: 0.7, l: 0.55 }, now, [ - { tag: 'novel', desc: 'Finish The Brothers Karamazov', done: false, ageHrs: 0 }, - { tag: 'novel', desc: "Read Dostoyevsky's notes", done: true, ageHrs: 2 }, - { tag: 'article', desc: 'How does WebAssembly GC work?', done: true, ageHrs: 6 }, - { tag: 'paper', desc: 'Re-read "Out of the Tar Pit"', done: true, ageHrs: 30 }, - { tag: 'novel', desc: 'Submit a short story', done: true, ageHrs: 72 }, + { tag: 'novel', desc: 'Finish The Brothers Karamazov', done: false, ageHrs: 0 }, + ...this.makeExampleDoneBlocks( + [ + { tag: 'novel', desc: (n) => `Read chapter ${n}` }, + { tag: 'paper', desc: (n) => `Annotated paper ${n}` }, + { tag: 'article', desc: (n) => `Saved article notes ${n}` }, + { tag: 'essay', desc: (n) => `Drafted reading response ${n}` }, + ], + 2, + 8, + ), ]), this.makeExampleTower('Side projects', { h: 0.58, s: 0.65, l: 0.5 }, now, [ - { tag: 'angular', desc: 'Modernise the towers app', done: false, ageHrs: 0 }, - { tag: 'rust', desc: 'Port the sync layer to Tauri', done: false, ageHrs: 1 }, - { tag: 'angular', desc: 'Wire CDK drag-drop', done: true, ageHrs: 24 }, - { tag: 'rust', desc: 'Spike SQLite vs LMDB', done: true, ageHrs: 96 }, + { tag: 'angular', desc: 'Modernise the towers app', done: false, ageHrs: 0 }, + { tag: 'rust', desc: 'Port the sync layer to Tauri', done: false, ageHrs: 1 }, + ...this.makeExampleDoneBlocks( + [ + { tag: 'angular', desc: (n) => `Refined UI pass ${n}` }, + { tag: 'rust', desc: (n) => `Completed systems spike ${n}` }, + { tag: 'infra', desc: (n) => `Tuned deploy workflow ${n}` }, + { tag: 'docs', desc: (n) => `Captured project note ${n}` }, + ], + 4, + 9, + ), ]), this.makeExampleTower('Exercise', { h: 0.36, s: 0.6, l: 0.5 }, now, [ - { tag: 'run', desc: '10k Sunday', done: false, ageHrs: 0 }, - { tag: 'climb', desc: 'Lead 6a outdoors', done: false, ageHrs: 4 }, - { tag: 'climb', desc: 'Bouldering session', done: true, ageHrs: 12 }, - { tag: 'run', desc: 'Easy 5k loop', done: true, ageHrs: 48 }, - { tag: 'climb', desc: 'Top-roped 5c', done: true, ageHrs: 96 }, + { tag: 'run', desc: '10k Sunday', done: false, ageHrs: 0 }, + { tag: 'climb', desc: 'Lead 6a outdoors', done: false, ageHrs: 4 }, + ...this.makeExampleDoneBlocks( + [ + { tag: 'run', desc: (n) => `Easy run ${n}` }, + { tag: 'climb', desc: (n) => `Bouldering session ${n}` }, + { tag: 'mobility', desc: (n) => `Mobility circuit ${n}` }, + { tag: 'strength', desc: (n) => `Strength session ${n}` }, + ], + 6, + 11, + ), ]), ], }; this._pages.update((pages) => [...pages, page]); + this.analytics.trackStart(); + this.analytics.trackExampleLoaded(); this.scheduleSave(); } @@ -560,7 +646,7 @@ export class StoreService implements OnDestroy { name: string, base_color: HslColor, nowSec: number, - blocks: Array<{ tag: string; desc: string; done: boolean; ageHrs: number }>, + blocks: ExampleBlockSeed[], ): Tower { return { id: uuidV4(), @@ -576,6 +662,23 @@ export class StoreService implements OnDestroy { }; } + private makeExampleDoneBlocks( + patterns: ExampleDonePattern[], + newestAgeHrs: number, + spacingHrs: number, + ): ExampleBlockSeed[] { + return Array.from({ length: EXAMPLE_DONE_BLOCKS_PER_TOWER }, (_, i) => { + const pattern = patterns[i % patterns.length]; + const sequence = Math.floor(i / patterns.length) + 1; + return { + tag: pattern.tag, + desc: pattern.desc(sequence), + done: true, + ageHrs: newestAgeHrs + (EXAMPLE_DONE_BLOCKS_PER_TOWER - 1 - i) * spacingHrs, + }; + }); + } + ngOnDestroy(): void { this.cancelPendingWrites(); if (typeof window !== 'undefined') { diff --git a/frontend/src/app/services/store.service.vitest.ts b/frontend/src/app/services/store.service.vitest.ts index 0836e9b..75d3f30 100644 --- a/frontend/src/app/services/store.service.vitest.ts +++ b/frontend/src/app/services/store.service.vitest.ts @@ -218,6 +218,87 @@ describe('StoreService', () => { expect(api.getData).toHaveBeenCalledTimes(1); }); + it('moves a pending block to the end when it becomes done', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + api.getData.mockResolvedValue({ + pages: [ + { + id: 'page-1', + name: 'Page', + hide_create_tower_button: false, + keep_tasks_open: false, + default_date_from: null, + default_date_to: null, + towers: [ + { + id: 'tower-1', + name: 'Tower', + base_color: { h: 0.5, s: 0.5, l: 0.5 }, + blocks: [ + { + id: 'old-pending', + tag: 'a', + description: 'Created first, completed last', + is_done: false, + created_at: 100, + }, + { + id: 'newer-pending', + tag: 'b', + description: 'Still pending', + is_done: false, + created_at: 300, + }, + { + id: 'existing-done', + tag: 'c', + description: 'Already done', + is_done: true, + created_at: 200, + }, + ], + }, + ], + }, + ], + } satisfies TreeDto); + const store = configure(api); + await store.init(); + + store.updateBlock('page-1', 'tower-1', 'old-pending', { is_done: true }); + + const blocks = store.pages()[0].towers[0].blocks; + expect(blocks.map((b) => b.id)).toEqual([ + 'newer-pending', + 'existing-done', + 'old-pending', + ]); + expect(blocks[2].created_at).toBe(100); + }); + + it('loads welcome example data with hundreds of completed squares', () => { + const api = makeMockApi(); + const store = configure(api); + + store.loadExample(); + + const [page] = store.pages(); + expect(page.name).toBe('Hobbies'); + expect(page.towers).toHaveLength(3); + + const doneBlocks = page.towers.flatMap((tower) => tower.blocks.filter((b) => b.is_done)); + expect(doneBlocks.length).toBeGreaterThanOrEqual(300); + + for (const tower of page.towers) { + const doneDates = tower.blocks.filter((b) => b.is_done).map((b) => b.created_at); + expect(doneDates.length).toBeGreaterThanOrEqual(100); + expect(doneDates).toEqual([...doneDates].sort((a, b) => a - b)); + } + + store.ngOnDestroy(); + }); + // ── Debounced save ───────────────────────────────────────────────────────── it('debounces saves: multiple mutations within 750ms → one PUT', async () => { diff --git a/frontend/src/index.html b/frontend/src/index.html index cfcc28d..032f734 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -13,11 +13,24 @@ + - + - + + + + + + + + + + + + + diff --git a/frontend/src/library/forms.scss b/frontend/src/library/forms.scss index 43df3cf..086af81 100644 --- a/frontend/src/library/forms.scss +++ b/frontend/src/library/forms.scss @@ -38,6 +38,10 @@ input[type='text'] { &:focus { box-shadow: 0 1px $text-color; } + + &:focus-visible { + box-shadow: 0 2px $text-color; + } } button { @@ -56,6 +60,19 @@ button { border-bottom: solid $height #5d576b55; position: relative; + // Mobile: grow buttons to a ~42px tap target, but pin the label to the bottom + // so the bottom-border underline keeps hugging it (as on desktop). A bare + // min-height vertically centres the text and strands the underline well below + // it. Using flex (not a hit-area pseudo-element) means buttons that `all: unset` + // their styling — .tickbox, .swatch, .edit-tower — reset these props and opt out + // automatically, instead of inheriting a stray absolute overlay. + @media (max-width: $mobile-width) { + min-height: 42px; + display: inline-flex; + align-items: flex-end; + justify-content: center; + } + &:disabled { color: #5d576b55; border-bottom: solid $height #5d576b33; diff --git a/frontend/src/library/main.scss b/frontend/src/library/main.scss index 0156b7f..6c4fee6 100644 --- a/frontend/src/library/main.scss +++ b/frontend/src/library/main.scss @@ -34,12 +34,31 @@ @mixin exit { @include square(16px); - background: url('/assets/x-sign.svg') no-repeat center center; - background-size: 50% 50%; + -webkit-appearance: none; + appearance: none; + background: transparent url('/assets/x-sign.svg') no-repeat center center / 50% 50%; + border: 0 !important; + border-bottom: 0 !important; + box-shadow: none; box-sizing: content-box; + color: transparent; + display: block; + flex: 0 0 auto; + font-size: 0; + line-height: 0; + margin: 0; + min-height: 0; + overflow: hidden; padding: 8px; + text-decoration: none !important; @include jump(); + + &:before, + &:after { + content: none !important; + display: none; + } } img { diff --git a/frontend/src/library/utils.scss b/frontend/src/library/utils.scss index 6f367f3..5190f07 100644 --- a/frontend/src/library/utils.scss +++ b/frontend/src/library/utils.scss @@ -6,4 +6,22 @@ margin-bottom: $spacing; } } + + & > lt-modal { + @if $horizontal { + margin-right: 0 !important; + } @else { + margin-bottom: 0 !important; + } + } + + // Fixed modal hosts should not make the previous content child count as + // "not last" for spacing. + & > *:not(:last-child):not(lt-modal):not(:has(~ *:not(lt-modal))) { + @if $horizontal { + margin-right: 0; + } @else { + margin-bottom: 0; + } + } } diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 79a3348..af0b622 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -1,6 +1,14 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ + resolve: { + alias: { + // The package ships only a `module` field, which Vite's resolver + // can't always find — point it at the file directly. + '@plausible-analytics/tracker': + '@plausible-analytics/tracker/plausible.js', + }, + }, test: { globals: true, environment: 'jsdom', From f87480e79d71809c9cbbbb00202d209ac03bbbe5 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 30 May 2026 14:47:25 +0100 Subject: [PATCH 03/26] better --- frontend/angular.json | 3 +- .../components/modal/block-edit.component.ts | 2 + .../app/components/page/page.component.scss | 39 +++--------- .../app/components/pages/pages.component.html | 21 ++++++- .../app/components/pages/pages.component.scss | 61 +++++++++++++++++++ .../app/components/pages/pages.component.ts | 22 ++++++- .../shared/select-add/select-add.component.ts | 12 ++++ 7 files changed, 124 insertions(+), 36 deletions(-) diff --git a/frontend/angular.json b/frontend/angular.json index 4a1b666..e4ba9a9 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -5,7 +5,8 @@ "packageManager": "npm", "schematicCollections": [ "angular-eslint" - ] + ], + "analytics": false }, "newProjectRoot": "projects", "projects": { diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index 604e6f5..63296ae 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -74,6 +74,7 @@ interface EditedValue { [alwaysDropShadow]="true" [onlyShadowBorder]="true" placeholder="Tag this item…" + emptyHint="No tags yet. Type one below to create it." (select)="updateTag(b.id, $event)" (add)="updateTag(b.id, $event)" /> @@ -125,6 +126,7 @@ interface EditedValue { [alwaysDropShadow]="true" [onlyShadowBorder]="true" placeholder="Set a category…" + emptyHint="No tags yet. Type one below to create it." (select)="updateNewTag($event)" (add)="updateNewTag($event)" /> diff --git a/frontend/src/app/components/page/page.component.scss b/frontend/src/app/components/page/page.component.scss index 4ae3ad3..cf5689d 100644 --- a/frontend/src/app/components/page/page.component.scss +++ b/frontend/src/app/components/page/page.component.scss @@ -28,7 +28,8 @@ transition: box-shadow $short-animation-time; - max-width: 800px; + max-width: 100%; + gap: var(--medium-padding); &.cdk-drop-list-dragging { *:not(.cdk-drag-placeholder) { @@ -55,38 +56,17 @@ } & > * { + width: 100%; max-width: 200px; - box-sizing: content-box; - flex: 0 0 auto; + box-sizing: border-box; + flex: 1 1 0; + min-width: 0; min-height: 0; - - &:not(:nth-last-child(1)) { - margin-right: var(--medium-padding); - @media (max-width: $mobile-width) { - // Was --small-padding (7.5px) — too tight to read as a real gap - // between white tower cards on the cream-pink background. - margin-right: var(--medium-padding); - } - } } position: relative; - @for $i from 1 to 12 { - & > *:first-child:nth-last-child(#{$i}), - & > *:first-child:nth-last-child(#{$i}) ~ * { - width: calc((100% - (#{$i} - 1) * var(--medium-padding)) / #{$i}); - - @media (max-width: $mobile-width) { - width: calc((100% - (#{$i} - 1) * var(--small-padding)) / #{$i}); - } - } - } - // Mobile: fixed-width towers with horizontal scroll (1.5-column rhythm). - // This block is declared AFTER the @for loop so it wins the specificity race - // without needing !important on every property — only the width triples need it - // to beat the per-child-count selectors generated above. @media (max-width: $mobile-width) { --mobile-tower-width: calc(66vw - var(--small-padding)); --mobile-tower-side-padding: max( @@ -109,17 +89,12 @@ display: none; } - // Override the @for width-calc rules above. & > * { width: var(--mobile-tower-width) !important; max-width: var(--mobile-tower-width) !important; min-width: var(--mobile-tower-width) !important; scroll-snap-align: center; - flex-shrink: 0; - } - - & > *:not(:nth-last-child(1)) { - margin-right: 0; + flex: 0 0 var(--mobile-tower-width); } } } diff --git a/frontend/src/app/components/pages/pages.component.html b/frontend/src/app/components/pages/pages.component.html index 44dbccf..28fd921 100644 --- a/frontend/src/app/components/pages/pages.component.html +++ b/frontend/src/app/components/pages/pages.component.html @@ -27,12 +27,31 @@ [page]="selectedPage()" (close)="showSettings.set(false)" (updatePage)="onUpdatePage($event)" - (deletePage)="onRemovePage()" + (deletePage)="onRequestRemovePage()" (switchAccount)="onSwitchAccount($event)" /> } +@if (confirmDeletePageId()) { + +
+
+ +

Delete page

+
+

+ Delete {{ confirmDeletePageName() || 'this page' }} and all of its towers and blocks? + This can't be undone. +

+
+ + +
+
+
+} + @if (showWelcome()) { (null); constructor() { effect(() => { @@ -63,6 +64,12 @@ export class PagesComponent { readonly selectedPageName = computed(() => this.selectedPage()?.name ?? null); + readonly confirmDeletePageName = computed(() => { + const id = this.confirmDeletePageId(); + if (!id) return ''; + return this.store.pages().find((p) => p.id === id)?.name ?? ''; + }); + readonly selectedPageIndex = computed(() => { const page = this.selectedPage(); if (!page) return -1; @@ -94,12 +101,23 @@ export class PagesComponent { } } - onRemovePage(): void { + onRequestRemovePage(): void { const page = this.selectedPage(); if (!page) return; - this.store.deletePage(page.id); + this.confirmDeletePageId.set(page.id); + } + + confirmRemovePage(): void { + const pageId = this.confirmDeletePageId(); + if (!pageId) return; + this.store.deletePage(pageId); this.selectedPageId.set(null); this.showSettings.set(false); + this.confirmDeletePageId.set(null); + } + + cancelRemovePage(): void { + this.confirmDeletePageId.set(null); } onSwitchAccount(token: string): void { diff --git a/frontend/src/app/components/shared/select-add/select-add.component.ts b/frontend/src/app/components/shared/select-add/select-add.component.ts index 399763d..f1cb3fb 100644 --- a/frontend/src/app/components/shared/select-add/select-add.component.ts +++ b/frontend/src/app/components/shared/select-add/select-add.component.ts @@ -28,6 +28,9 @@ import {
+ @if (resolvedItems().length === 0 && emptyHint()) { +

{{ emptyHint() }}

+ } @for (item of resolvedItems(); track item) { @if (editing()) { (null); readonly editable = input(false); readonly placeholder = input('Select…'); + readonly emptyHint = input(''); readonly alwaysDropShadow = input(false); readonly onlyShadowBorder = input(false); From 5ac6633d402cca8c47187d8312bff9480e8b4a2c Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 30 May 2026 15:40:53 +0100 Subject: [PATCH 04/26] getting there --- CLAUDE.md | 2 +- backend/src/life_towers/api.py | 9 +- .../life_towers/migrations/001_initial.sql | 2 + .../migrations/002_keep_tasks_open.sql | 2 - backend/src/life_towers/models.py | 2 + backend/tests/test_api.py | 37 ++ frontend/e2e/visuals.spec.ts | 10 +- .../components/modal/block-edit.component.ts | 136 ++++++- .../app/components/modal/modal.component.ts | 15 +- .../app/components/page/page.component.html | 1 + .../src/app/components/page/page.component.ts | 3 + .../app/components/pages/pages.component.html | 12 +- .../app/components/pages/pages.component.ts | 24 +- .../shared/select-add/select-add.component.ts | 14 +- .../app/components/tower/tower.component.ts | 253 ++++++++++--- .../tower/tower.component.vitest.ts | 95 +++++ .../components/welcome/welcome.component.ts | 346 ++++++++++++++++-- frontend/src/app/models/index.ts | 2 + frontend/src/app/services/store.service.ts | 36 +- .../src/app/services/store.service.vitest.ts | 9 +- 20 files changed, 898 insertions(+), 112 deletions(-) delete mode 100644 backend/src/life_towers/migrations/002_keep_tasks_open.sql create mode 100644 frontend/src/app/components/tower/tower.component.vitest.ts diff --git a/CLAUDE.md b/CLAUDE.md index 91799c9..091d70c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,7 +20,7 @@ backend/ # FastAPI + SQLite (WAL) limits.py # slowapi limiter + payload-size middleware models.py # pydantic v2 schemas logging.py # structlog JSON - migrations/ # 001_initial.sql, 002_keep_tasks_open.sql, … + migrations/ # 001_initial.sql consolidated schema tests/test_api.py # pytest + httpx AsyncClient pyproject.toml # uv-managed frontend/ # Angular 21+ standalone, signals, zoneless diff --git a/backend/src/life_towers/api.py b/backend/src/life_towers/api.py index 3a8fd4a..9fcee2a 100644 --- a/backend/src/life_towers/api.py +++ b/backend/src/life_towers/api.py @@ -94,7 +94,7 @@ async def get_data( block_rows = conn.execute( """ - SELECT id, tag, description, is_done, created_at + SELECT id, tag, description, is_done, difficulty, created_at FROM blocks WHERE tower_id = ? ORDER BY position @@ -108,6 +108,7 @@ async def get_data( tag=b["tag"], description=b["description"], is_done=bool(b["is_done"]), + difficulty=b["difficulty"], created_at=b["created_at"], ) for b in block_rows @@ -213,8 +214,9 @@ async def put_data( """ INSERT INTO blocks (id, tower_id, user_id, position, tag, - description, is_done, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + description, is_done, difficulty, + created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( block.id, @@ -224,6 +226,7 @@ async def put_data( block.tag, block.description, 1 if block.is_done else 0, + block.difficulty, created_at, now, ), diff --git a/backend/src/life_towers/migrations/001_initial.sql b/backend/src/life_towers/migrations/001_initial.sql index 14ab170..b8d3761 100644 --- a/backend/src/life_towers/migrations/001_initial.sql +++ b/backend/src/life_towers/migrations/001_initial.sql @@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS pages ( position INTEGER NOT NULL, name TEXT NOT NULL, hide_create_tower_button INTEGER NOT NULL DEFAULT 0 CHECK (hide_create_tower_button IN (0, 1)), + keep_tasks_open INTEGER NOT NULL DEFAULT 0 CHECK (keep_tasks_open IN (0, 1)), default_date_from INTEGER, default_date_to INTEGER, created_at INTEGER NOT NULL, @@ -47,6 +48,7 @@ CREATE TABLE IF NOT EXISTS blocks ( tag TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', is_done INTEGER NOT NULL DEFAULT 0 CHECK (is_done IN (0, 1)), + difficulty INTEGER NOT NULL DEFAULT 1 CHECK (difficulty >= 1), created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ) STRICT; diff --git a/backend/src/life_towers/migrations/002_keep_tasks_open.sql b/backend/src/life_towers/migrations/002_keep_tasks_open.sql deleted file mode 100644 index f9417ad..0000000 --- a/backend/src/life_towers/migrations/002_keep_tasks_open.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE pages ADD COLUMN keep_tasks_open INTEGER NOT NULL DEFAULT 0 - CHECK (keep_tasks_open IN (0, 1)); diff --git a/backend/src/life_towers/models.py b/backend/src/life_towers/models.py index d848ad4..4fe8694 100644 --- a/backend/src/life_towers/models.py +++ b/backend/src/life_towers/models.py @@ -27,6 +27,7 @@ class BlockIn(BaseModel): tag: str = Field(max_length=200) description: str = Field(max_length=10_000) is_done: bool + difficulty: int = Field(default=1, ge=1, le=100) created_at: Optional[int] = None @field_validator("id") @@ -42,6 +43,7 @@ class BlockOut(BaseModel): tag: str description: str is_done: bool + difficulty: int created_at: int diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index a23603b..0037c88 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -215,6 +215,7 @@ def _make_tree() -> dict: "tag": f"tag-{pi}-{ti}-{bi}", "description": f"desc-{pi}-{ti}-{bi}", "is_done": False, + "difficulty": bi + 1, "created_at": 1700000000 + bi, } ) @@ -266,6 +267,42 @@ async def test_round_trip(client: AsyncClient) -> None: for bi, block in enumerate(tower["blocks"]): assert block["id"] == tree["pages"][pi]["towers"][ti]["blocks"][bi]["id"] assert block["tag"] == tree["pages"][pi]["towers"][ti]["blocks"][bi]["tag"] + assert ( + block["difficulty"] + == tree["pages"][pi]["towers"][ti]["blocks"][bi]["difficulty"] + ) + + +@pytest.mark.asyncio +async def test_difficulty_defaults_to_one_when_omitted(client: AsyncClient) -> None: + """A block sent without `difficulty` is stored and returned as 1.""" + token = make_uuidv4() + await client.post("/api/v1/register", json={"token": token}) + headers = {"Authorization": f"Bearer {token}"} + + tree = _make_tree() + # Drop difficulty from the very first block. + del tree["pages"][0]["towers"][0]["blocks"][0]["difficulty"] + + put_resp = await client.put("/api/v1/data", json=tree, headers=headers) + assert put_resp.status_code == 204 + + data = (await client.get("/api/v1/data", headers=headers)).json() + assert data["pages"][0]["towers"][0]["blocks"][0]["difficulty"] == 1 + + +@pytest.mark.asyncio +async def test_difficulty_must_be_positive(client: AsyncClient) -> None: + """difficulty < 1 is rejected by validation.""" + token = make_uuidv4() + await client.post("/api/v1/register", json={"token": token}) + headers = {"Authorization": f"Bearer {token}"} + + tree = _make_tree() + tree["pages"][0]["towers"][0]["blocks"][0]["difficulty"] = 0 + + resp = await client.put("/api/v1/data", json=tree, headers=headers) + assert resp.status_code == 400 # --------------------------------------------------------------------------- diff --git a/frontend/e2e/visuals.spec.ts b/frontend/e2e/visuals.spec.ts index 8b5429c..a1e3384 100644 --- a/frontend/e2e/visuals.spec.ts +++ b/frontend/e2e/visuals.spec.ts @@ -11,8 +11,8 @@ test.describe('Life Towers visuals', () => { await page.waitForTimeout(350); // let the welcome modal finish fade-in await page.screenshot({ path: 'visuals/01-welcome-modal.png', fullPage: true }); - // Dismiss the welcome modal with Start fresh, then continue. - await page.getByRole('button', { name: 'Start fresh' }).click(); + // Dismiss the welcome modal with Start empty, then continue. + await page.getByRole('button', { name: 'Start empty' }).click(); await page.waitForSelector('section.modal', { state: 'detached' }); await page.screenshot({ path: 'visuals/01b-empty-state-after-dismiss.png', fullPage: true }); @@ -171,11 +171,11 @@ test.describe('Life Towers visuals', () => { await page.screenshot({ path: 'visuals/11-settings-modal.png', fullPage: true }); }); - test('"Try an example" populates a sample page', async ({ page }) => { + test('"Load sample towers" populates a sample page', async ({ page }) => { await page.goto('/'); await page.waitForSelector('text=Welcome to Life Towers', { timeout: 15000 }); await page.waitForTimeout(350); - await page.getByRole('button', { name: 'Try an example' }).click(); + await page.getByRole('button', { name: 'Load sample towers' }).click(); await page.waitForSelector('section.modal', { state: 'detached' }); await page.waitForTimeout(1800); await page.screenshot({ path: 'visuals/12-example-data.png', fullPage: true }); @@ -190,7 +190,7 @@ test.describe('Life Towers visuals', () => { await page.waitForTimeout(350); await page.screenshot({ path: 'visuals/13-mobile-welcome.png', fullPage: true }); - await page.getByRole('button', { name: 'Try an example' }).click(); + await page.getByRole('button', { name: 'Load sample towers' }).click(); await page.waitForSelector('section.modal', { state: 'detached' }); await page.waitForTimeout(1800); await page.screenshot({ path: 'visuals/14-mobile-populated.png', fullPage: true }); diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index 63296ae..cd50b5b 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -23,12 +23,14 @@ export interface BlockEditSave { tag: string; description: string; is_done: boolean; + difficulty: number; } interface EditedValue { tag: string; description: string; is_done: boolean; + difficulty: number; } @Component({ @@ -73,8 +75,7 @@ interface EditedValue { [selected]="editedFor(b.id).tag" [alwaysDropShadow]="true" [onlyShadowBorder]="true" - placeholder="Tag this item…" - emptyHint="No tags yet. Type one below to create it." + [placeholder]="tagPlaceholder('Tag this item…')" (select)="updateTag(b.id, $event)" (add)="updateTag(b.id, $event)" /> @@ -96,6 +97,26 @@ interface EditedValue { Already done +
+ Difficulty +
+ + {{ editedFor(b.id).difficulty }} + +
+
+
@@ -125,8 +146,7 @@ interface EditedValue { [selected]="newValue().tag" [alwaysDropShadow]="true" [onlyShadowBorder]="true" - placeholder="Set a category…" - emptyHint="No tags yet. Type one below to create it." + [placeholder]="tagPlaceholder('Set a category…')" (select)="updateNewTag($event)" (add)="updateNewTag($event)" /> @@ -147,6 +167,26 @@ interface EditedValue { Already done +
+ Difficulty +
+ + {{ newValue().difficulty }} + +
+
+
+

Welcome to Life Towers

-

Welcome to Life Towers

+ -

- A visual TODO with bite. Each page is a context (work, hobbies, a project). - Each tower is a stack of related tasks. As you finish a task, it falls into the - tower as a colored square — the more you do, the taller it grows. +

+ Life Towers turns completed tasks into visible stacks. Create pages for work, hobbies, + home, or any project. Add towers for task groups, then check off tasks to build them + block by block.

-

- Everything you write is saved to a small remote database, keyed to a private UUID token shown - under Settings → Account. Copy that token to recover your data on another device, or - paste a friend's token to look at theirs. +

+ Preview showing three towers with pending task bars at the top and completed task + blocks stacked below. +

+ + +

How Life Towers works

+
+
+
Pages
+
Keep each area separate.
+
+
+
Towers
+
Stack related tasks together.
+
+
+
Blocks
+
Finished tasks become colored blocks.
+
+
+ + + +

+ Start empty if you know what to track, or load sample towers you can edit or delete.

- - + +
`, @@ -34,12 +109,27 @@ import { Component, ChangeDetectionStrategy, output } from '@angular/core'; :host { display: block; } + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; + } + .welcome-card { @include card(); - width: 66vw; - max-width: 480px; + width: min(560px, calc(100vw - (2 * var(--large-padding)))); + max-width: 560px; + max-height: calc(100svh - (2 * var(--medium-padding))); + overflow-y: auto; @media (max-width: $mobile-width) { - width: 88vw; + width: min(88vw, calc(100vw - (2 * var(--medium-padding)))); max-width: 88vw; padding: var(--medium-padding); } @@ -48,8 +138,20 @@ import { Component, ChangeDetectionStrategy, output } from '@angular/core'; position: relative; box-shadow: $shadow; text-align: left; + font-family: $normal-font; + font-weight: 300; + font-size: var(--medium-font-size); + line-height: 1.45; @include inner-spacing(var(--medium-padding)); + h2, + h3, + p, + dl, + dd { + margin: 0; + } + .exit { position: absolute; top: var(--medium-padding); @@ -58,31 +160,231 @@ import { Component, ChangeDetectionStrategy, output } from '@angular/core'; } h2 { + font-family: $title-font; + font-weight: 400; + font-size: var(--larger-font-size); text-align: center; - margin-bottom: var(--medium-padding); padding: 0 36px; + line-height: 1.25; @media (max-width: $mobile-width) { padding: 0 28px; } } - p.lead { color: $text-color; } + .lead { + color: $text-color; + font-family: inherit; + font-weight: inherit; + font-size: inherit; + line-height: inherit; + max-width: 46ch; + margin-inline: auto; + text-align: center; + } - p.muted { - color: rgba($text-color, 0.7); - font-size: var(--medium-font-size); - em { font-style: italic; } + .tower-preview { + box-sizing: border-box; + padding: var(--medium-padding) 0; + border-top: 1px solid rgba($text-color, 0.08); + border-bottom: 1px solid rgba($text-color, 0.08); + + @media (max-width: $mobile-width) { + padding-block: var(--small-padding); + } + } + + .preview-shell { + width: min(100%, 370px); + margin: 0 auto; + } + + .preview-page-tab { + width: 96px; + height: 14px; + margin: 0 auto var(--small-padding); + border-radius: var(--border-radius); + background: rgba($text-color, 0.08); + box-shadow: $shadow-border; + } + + .preview-towers { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + align-items: end; + gap: var(--small-padding); + } + + .preview-tower { + display: flex; + flex-direction: column; + min-height: 128px; + padding: 6px; + box-sizing: border-box; + border-radius: var(--border-radius); + background: rgba($text-color, 0.025); + box-shadow: $shadow-border; + } + + .preview-task { + display: block; + width: 74%; + height: 5px; + margin-bottom: 4px; + border-radius: var(--border-radius); + background: var(--task-color); + } + + .preview-task--short { + width: 48%; + } + + .preview-stack { + display: grid; + grid-template-columns: repeat(3, 1fr); + align-content: end; + gap: 2px; + min-height: 86px; + margin-top: auto; + + span { + display: block; + aspect-ratio: 1; + border-radius: 2px; + background: var(--block-color); + box-shadow: $shadow-border; + } + + span:nth-child(2n) { + filter: saturate(0.9) brightness(1.06); + } + + span:nth-child(3n) { + filter: saturate(1.08) brightness(0.96); + } + } + + @media (prefers-reduced-motion: no-preference) { + @keyframes preview-task-pulse { + 0%, 100% { + opacity: 0.42; + transform: scaleX(0.86); + } + 45%, 70% { + opacity: 1; + transform: scaleX(1); + } + } + + @keyframes preview-block-fall { + 0% { + opacity: 0; + transform: translateY(-220%); + } + 60%, 100% { + opacity: 1; + transform: translateY(0); + } + } + + .preview-task { + transform-origin: left center; + animation: preview-task-pulse 2600ms ease-in-out infinite; + } + + .preview-stack span { + opacity: 0; + animation: preview-block-fall 1200ms cubic-bezier(0.5, 0, 1, 0) forwards; + animation-delay: calc(160ms + var(--fall-index, 0) * 95ms); + } + + .preview-stack span:nth-child(1) { --fall-index: 0; } + .preview-stack span:nth-child(2) { --fall-index: 1; } + .preview-stack span:nth-child(3) { --fall-index: 2; } + .preview-stack span:nth-child(4) { --fall-index: 3; } + .preview-stack span:nth-child(5) { --fall-index: 4; } + .preview-stack span:nth-child(6) { --fall-index: 5; } + .preview-stack span:nth-child(7) { --fall-index: 6; } + .preview-stack span:nth-child(8) { --fall-index: 7; } + .preview-stack span:nth-child(9) { --fall-index: 8; } + } + + .preview-tower--reading { + --block-color: hsl(18, 70%, 58%); + --task-color: hsla(18, 70%, 58%, 0.26); + } + + .preview-tower--projects { + --block-color: hsl(209, 65%, 52%); + --task-color: hsla(209, 65%, 52%, 0.24); + } + + .preview-tower--exercise { + --block-color: hsl(130, 45%, 44%); + --task-color: hsla(130, 45%, 44%, 0.22); + } + + .basics { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--small-padding); + + @media (max-width: $mobile-width) { + grid-template-columns: 1fr; + } + } + + .basic { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; + } + + .basic__label { + font-family: inherit; + font-weight: inherit; + color: $text-color; + font-size: inherit; + line-height: inherit; + } + + .basic__text, + .account-note, + .choice-note { + font-family: inherit; + font-weight: inherit; + color: rgba($text-color, 0.82); + font-size: inherit; + line-height: inherit; + } + + .account-note, + .choice-note { + max-width: 54ch; + margin-inline: auto; + } + + .choice-note { + text-align: center; } .actions { display: flex; justify-content: space-around; + align-items: flex-end; + flex-wrap: wrap; gap: var(--large-padding); margin-top: var(--large-padding); button { + font-family: inherit; + font-weight: inherit; + font-size: inherit; + margin: 0; max-width: 100%; + line-height: inherit; + text-align: center; } button.primary { diff --git a/frontend/src/app/models/index.ts b/frontend/src/app/models/index.ts index 1f0af8b..e9216d0 100644 --- a/frontend/src/app/models/index.ts +++ b/frontend/src/app/models/index.ts @@ -9,6 +9,8 @@ export interface Block { tag: string; description: string; is_done: boolean; + /** How many squares this block draws in the tower (>= 1). */ + difficulty: number; created_at: number; } diff --git a/frontend/src/app/services/store.service.ts b/frontend/src/app/services/store.service.ts index f158445..ad5b41d 100644 --- a/frontend/src/app/services/store.service.ts +++ b/frontend/src/app/services/store.service.ts @@ -60,6 +60,7 @@ interface ExampleBlockSeed { tag: string; desc: string; done: boolean; + difficulty?: number; ageHrs: number; } @@ -318,12 +319,14 @@ export class StoreService implements OnDestroy { tag: string, description: string, is_done = false, + difficulty = 1, ): void { const block: Block = { id: uuidV4(), tag, description, is_done, + difficulty, created_at: Math.floor(Date.now() / 1000), }; this._pages.update((pages) => @@ -578,7 +581,7 @@ export class StoreService implements OnDestroy { // ── Example data ────────────────────────────────────────────────────────── - loadExample(): void { + loadExample(): string { const now = Math.floor(Date.now() / 1000); const page: Page = { @@ -593,7 +596,13 @@ export class StoreService implements OnDestroy { // oldest → newest (left → right), matching the slider's old → new // labels; pending tasks stay on top for the accordion. this.makeExampleTower('Reading', { h: 0.05, s: 0.7, l: 0.55 }, now, [ - { tag: 'novel', desc: 'Finish The Brothers Karamazov', done: false, ageHrs: 0 }, + { + tag: 'novel', + desc: 'Finish The Brothers Karamazov', + done: false, + difficulty: 4, + ageHrs: 0, + }, ...this.makeExampleDoneBlocks( [ { tag: 'novel', desc: (n) => `Read chapter ${n}` }, @@ -606,8 +615,20 @@ export class StoreService implements OnDestroy { ), ]), this.makeExampleTower('Side projects', { h: 0.58, s: 0.65, l: 0.5 }, now, [ - { tag: 'angular', desc: 'Modernise the towers app', done: false, ageHrs: 0 }, - { tag: 'rust', desc: 'Port the sync layer to Tauri', done: false, ageHrs: 1 }, + { + tag: 'angular', + desc: 'Modernise the towers app', + done: false, + difficulty: 3, + ageHrs: 0, + }, + { + tag: 'rust', + desc: 'Port the sync layer to Tauri', + done: false, + difficulty: 5, + ageHrs: 1, + }, ...this.makeExampleDoneBlocks( [ { tag: 'angular', desc: (n) => `Refined UI pass ${n}` }, @@ -620,8 +641,8 @@ export class StoreService implements OnDestroy { ), ]), this.makeExampleTower('Exercise', { h: 0.36, s: 0.6, l: 0.5 }, now, [ - { tag: 'run', desc: '10k Sunday', done: false, ageHrs: 0 }, - { tag: 'climb', desc: 'Lead 6a outdoors', done: false, ageHrs: 4 }, + { tag: 'run', desc: '10k Sunday', done: false, difficulty: 2, ageHrs: 0 }, + { tag: 'climb', desc: 'Lead 6a outdoors', done: false, difficulty: 4, ageHrs: 4 }, ...this.makeExampleDoneBlocks( [ { tag: 'run', desc: (n) => `Easy run ${n}` }, @@ -640,6 +661,7 @@ export class StoreService implements OnDestroy { this.analytics.trackStart(); this.analytics.trackExampleLoaded(); this.scheduleSave(); + return page.id; } private makeExampleTower( @@ -657,6 +679,7 @@ export class StoreService implements OnDestroy { tag: b.tag, description: b.desc, is_done: b.done, + difficulty: b.difficulty ?? 1, created_at: nowSec - Math.floor(b.ageHrs * 3600), })), }; @@ -674,6 +697,7 @@ export class StoreService implements OnDestroy { tag: pattern.tag, desc: pattern.desc(sequence), done: true, + difficulty: 1 + ((i + (i % patterns.length) * 2) % 5), ageHrs: newestAgeHrs + (EXAMPLE_DONE_BLOCKS_PER_TOWER - 1 - i) * spacingHrs, }; }); diff --git a/frontend/src/app/services/store.service.vitest.ts b/frontend/src/app/services/store.service.vitest.ts index 75d3f30..3081883 100644 --- a/frontend/src/app/services/store.service.vitest.ts +++ b/frontend/src/app/services/store.service.vitest.ts @@ -241,6 +241,7 @@ describe('StoreService', () => { tag: 'a', description: 'Created first, completed last', is_done: false, + difficulty: 1, created_at: 100, }, { @@ -248,6 +249,7 @@ describe('StoreService', () => { tag: 'b', description: 'Still pending', is_done: false, + difficulty: 1, created_at: 300, }, { @@ -255,6 +257,7 @@ describe('StoreService', () => { tag: 'c', description: 'Already done', is_done: true, + difficulty: 1, created_at: 200, }, ], @@ -281,19 +284,23 @@ describe('StoreService', () => { const api = makeMockApi(); const store = configure(api); - store.loadExample(); + const pageId = store.loadExample(); const [page] = store.pages(); + expect(page.id).toBe(pageId); expect(page.name).toBe('Hobbies'); expect(page.towers).toHaveLength(3); const doneBlocks = page.towers.flatMap((tower) => tower.blocks.filter((b) => b.is_done)); expect(doneBlocks.length).toBeGreaterThanOrEqual(300); + expect(new Set(page.towers.flatMap((tower) => tower.blocks.map((b) => b.difficulty))).size) + .toBeGreaterThan(1); for (const tower of page.towers) { const doneDates = tower.blocks.filter((b) => b.is_done).map((b) => b.created_at); expect(doneDates.length).toBeGreaterThanOrEqual(100); expect(doneDates).toEqual([...doneDates].sort((a, b) => a - b)); + expect(new Set(tower.blocks.map((b) => b.difficulty)).size).toBeGreaterThan(1); } store.ngOnDestroy(); From 617e3ef16d452b3ec0cda7ef882fb02b2f7e9e91 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 30 May 2026 15:55:29 +0100 Subject: [PATCH 05/26] good --- .../app/components/block/block.component.ts | 19 ++++- .../app/components/tower/tower.component.ts | 77 ++++++++++++------- .../tower/tower.component.vitest.ts | 34 ++++++++ .../components/welcome/welcome.component.ts | 30 ++------ frontend/src/app/services/store.service.ts | 76 +++++++++--------- .../src/app/services/store.service.vitest.ts | 6 +- 6 files changed, 147 insertions(+), 95 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 10fe47b..07f12f1 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -13,7 +13,16 @@ import { getColorOfTag } from '../../utils/color'; imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: ` -
+
`, styles: ` @import '../../../library/main'; @@ -30,7 +39,12 @@ import { getColorOfTag } from '../../utils/color'; cursor: pointer; @media (hover: hover) and (pointer: fine) { - @include gravitate(); + transition: transform $long-animation-time; + + &:hover, + &.hovered { + transform: translateY(4px); + } } } } @@ -39,6 +53,7 @@ import { getColorOfTag } from '../../utils/color'; export class BlockComponent { readonly block = input.required(); readonly baseColor = input.required(); + readonly hovered = input(false); /** Emits when the square is clicked — parent opens the block-edit modal. */ readonly clicked = output(); diff --git a/frontend/src/app/components/tower/tower.component.ts b/frontend/src/app/components/tower/tower.component.ts index 9581e4f..a9a3fdb 100644 --- a/frontend/src/app/components/tower/tower.component.ts +++ b/frontend/src/app/components/tower/tower.component.ts @@ -70,6 +70,7 @@ export function selectVisibleStyledBlocks( styled: StyledBlock[], visibleLimit: number, enteringInRangeId: string | null, + prevVisibleIds: ReadonlySet = new Set(), ): { visibleStyled: StyledBlock[]; hiddenCount: number } { // `visibleLimit` is a number of SQUARE slots. A block draws `difficulty` // squares, so we cap by cumulative square cost, not by raw block count. @@ -88,18 +89,19 @@ export function selectVisibleStyledBlocks( } const hiddenCount = Math.max(0, totalSquares(restingBlocks) - totalSquares(shownRestingBlocks)); - const usedSquares = totalSquares(shownRestingBlocks); - const remainingBudget = Math.max(0, normalizedLimit - usedSquares); - const transitioningBlocks = - remainingBudget > 0 - ? fitNewestBySquares( - styled.filter((b) => b._opacity !== '1'), - remainingBudget, - ) - : []; + + // Blocks leaving past the upper date bound (opacity 0, `_anim: 'ascend'`) must + // stay in the render list so their fly-up transition actually plays — even + // when the resting stack already fills the whole square budget. Without this, + // a capped stack (e.g. the example page) destroys the element the instant the + // slider hides it and it just vanishes. Restrict to blocks that were visible a + // moment ago: ones already off-screen have nothing to animate from, so leaving + // them out keeps the rendered set (and the phantom flex slots) bounded. + const exitingBlocks = styled.filter((b) => b._opacity !== '1' && prevVisibleIds.has(b.id)); + const shownIds = new Set([ ...shownRestingBlocks.map((b) => b.id), - ...transitioningBlocks.map((b) => b.id), + ...exitingBlocks.map((b) => b.id), ]); return { @@ -152,7 +154,6 @@ export function selectVisibleStyledBlocks(
} @@ -290,7 +296,7 @@ export function selectVisibleStyledBlocks( --block-stack-height: 0px; --add-block-size: 48px; --add-block-clearance: var(--medium-padding); - --empty-add-block-center-offset: 0px; + --add-block-center-offset: 0px; @include card(); overflow: hidden; @@ -372,23 +378,13 @@ export function selectVisibleStyledBlocks( top: max( 0px, min( - calc(50% - var(--add-block-size) / 2), + calc(50% - var(--add-block-size) / 2 - var(--add-block-center-offset)), calc(100% - var(--block-stack-height) - var(--add-block-clearance) - var(--add-block-size)) ) ); transform: translateX(-50%); } - &.empty img.add-block { - top: max( - 0px, - min( - calc(50% - var(--add-block-size) / 2 - var(--empty-add-block-center-offset)), - calc(100% - var(--add-block-size)) - ) - ); - } - .block-container-container { position: absolute; inset: 0; @@ -544,6 +540,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { readonly editEntry = signal(null); readonly showSettings = signal(false); readonly hiddenBlockCount = signal(0); + readonly hoveredBlockId = signal(null); private readonly stackZone = viewChild>('stackZone'); private readonly towerRoot = viewChild>('towerRoot'); @@ -656,7 +653,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { const styles = getComputedStyle(stackZone); const addBlockSize = this.parseCssPixels(styles.getPropertyValue('--add-block-size'), 48); - this.measureEmptyAddBlockOffset(stackZone); + this.measureAddBlockCenterOffset(stackZone); const fallbackClearance = addBlockSize <= 32 ? 7.5 : 15; const clearance = this.parseCssPixels( styles.getPropertyValue('--add-block-clearance'), @@ -669,7 +666,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { this.maxVisibleBlocks.set(Math.max(0, rows) * BLOCKS_PER_ROW); } - private measureEmptyAddBlockOffset(stackZone: HTMLElement): void { + private measureAddBlockCenterOffset(stackZone: HTMLElement): void { const towerRoot = this.towerRoot()?.nativeElement; if (!towerRoot) return; @@ -678,7 +675,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { const stackCenter = stackRect.top + stackRect.height / 2; const towerCenter = towerRect.top + towerRect.height / 2; const offset = Math.max(0, stackCenter - towerCenter); - stackZone.style.setProperty('--empty-add-block-center-offset', `${offset}px`); + stackZone.style.setProperty('--add-block-center-offset', `${offset}px`); } private parseCssPixels(value: string, fallback: number): number { @@ -737,6 +734,9 @@ export class TowerComponent implements AfterViewInit, OnDestroy { } const newInRangeId = grewByOne ? newIds[0] : null; + // Captured before any `_visibleBlocks.set` below — lets the cap keep + // currently-shown blocks that are now flying out so their exit animates. + const prevVisibleIds = new Set(this._visibleBlocks().map((b) => b.id)); const visibleLimit = maxVisibleBlocks === null ? Number.POSITIVE_INFINITY : Math.max(0, maxVisibleBlocks); let visibleStyled = styled; @@ -746,6 +746,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { styled, visibleLimit, newInRangeId, + prevVisibleIds, )); } this.hiddenBlockCount.set(hiddenCount); @@ -809,9 +810,31 @@ export class TowerComponent implements AfterViewInit, OnDestroy { } onEditBlock(block: Block): void { + this.hoveredBlockId.set(null); this.editEntry.set({ filter: block.is_done ? 'done' : 'pending', activeId: block.id }); } + onBlockPointerEnter(blockId: string): void { + this.hoveredBlockId.set(blockId); + } + + onBlockPointerLeave(blockId: string, event: PointerEvent): void { + if (this.relatedTargetBelongsToBlock(event.relatedTarget, blockId)) return; + if (this.hoveredBlockId() === blockId) this.hoveredBlockId.set(null); + } + + private relatedTargetBelongsToBlock(target: EventTarget | null, blockId: string): boolean { + const towerRoot = this.towerRoot()?.nativeElement; + let element = target instanceof Element ? target : null; + + while (element && element !== towerRoot) { + if (element instanceof HTMLElement && element.dataset['blockId'] === blockId) return true; + element = element.parentElement; + } + + return false; + } + /** Tickbox in the tasks accordion — flip is_done to true without opening the carousel. */ onMarkTaskDone(block: Block): void { this.saveBlock.emit({ diff --git a/frontend/src/app/components/tower/tower.component.vitest.ts b/frontend/src/app/components/tower/tower.component.vitest.ts index 056c7e1..c54cc83 100644 --- a/frontend/src/app/components/tower/tower.component.vitest.ts +++ b/frontend/src/app/components/tower/tower.component.vitest.ts @@ -78,6 +78,40 @@ describe('selectVisibleStyledBlocks', () => { expect(result.hiddenCount).toBe(4); }); + it('keeps a previously-visible block that is now flying out, even on a full stack', () => { + // Budget is full with three resting blocks; a fourth block has just left the + // range (opacity 0 → ascending). It was visible a moment ago, so it must stay + // rendered so its fly-up transition can play instead of vanishing instantly. + const styled = [ + block('old-1'), + block('old-2'), + block('old-3'), + block('leaving', '0'), + ]; + const prevVisible = new Set(['old-1', 'old-2', 'old-3', 'leaving']); + + const result = selectVisibleStyledBlocks(styled, 3, null, prevVisible); + + expect(result.visibleStyled.map((b) => b.id)).toContain('leaving'); + expect(result.hiddenCount).toBe(0); + }); + + it('does not resurrect an off-screen block that leaves the range', () => { + // 'hidden-leaving' was never rendered (not in prevVisible) — nothing to + // animate from, so keep it out and avoid an unbounded phantom render set. + const styled = [ + block('old-1'), + block('old-2'), + block('old-3'), + block('hidden-leaving', '0'), + ]; + const prevVisible = new Set(['old-1', 'old-2', 'old-3']); + + const result = selectVisibleStyledBlocks(styled, 3, null, prevVisible); + + expect(result.visibleStyled.map((b) => b.id)).not.toContain('hidden-leaving'); + }); + it('counts hidden squares when reserving the entering block', () => { const styled = [ block('newly-done', '1', 3), diff --git a/frontend/src/app/components/welcome/welcome.component.ts b/frontend/src/app/components/welcome/welcome.component.ts index be26d3c..eefa8de 100644 --- a/frontend/src/app/components/welcome/welcome.component.ts +++ b/frontend/src/app/components/welcome/welcome.component.ts @@ -89,15 +89,6 @@ import { A11yModule } from '@angular/cdk/a11y';
- - -

- Start empty if you know what to track, or load sample towers you can edit or delete. -

-
@@ -240,15 +231,16 @@ import { A11yModule } from '@angular/cdk/a11y'; } .preview-stack { - display: grid; - grid-template-columns: repeat(3, 1fr); - align-content: end; + display: flex; + flex-flow: row wrap-reverse; + align-content: flex-start; gap: 2px; min-height: 86px; margin-top: auto; span { display: block; + flex: 0 0 calc((100% - 4px) / 3); aspect-ratio: 1; border-radius: 2px; background: var(--block-color); @@ -349,9 +341,7 @@ import { A11yModule } from '@angular/cdk/a11y'; line-height: inherit; } - .basic__text, - .account-note, - .choice-note { + .basic__text { font-family: inherit; font-weight: inherit; color: rgba($text-color, 0.82); @@ -359,16 +349,6 @@ import { A11yModule } from '@angular/cdk/a11y'; line-height: inherit; } - .account-note, - .choice-note { - max-width: 54ch; - margin-inline: auto; - } - - .choice-note { - text-align: center; - } - .actions { display: flex; justify-content: space-around; diff --git a/frontend/src/app/services/store.service.ts b/frontend/src/app/services/store.service.ts index ad5b41d..fd35146 100644 --- a/frontend/src/app/services/store.service.ts +++ b/frontend/src/app/services/store.service.ts @@ -69,7 +69,7 @@ interface ExampleDonePattern { desc: (sequence: number) => string; } -const EXAMPLE_DONE_BLOCKS_PER_TOWER = 120; +const EXAMPLE_DONE_BLOCKS_PER_TOWER = 12; @Injectable({ providedIn: 'root' }) export class StoreService implements OnDestroy { @@ -333,11 +333,11 @@ export class StoreService implements OnDestroy { pages.map((p) => p.id === pageId ? { - ...p, - towers: p.towers.map((t) => - t.id === towerId ? { ...t, blocks: [...t.blocks, block] } : t, - ), - } + ...p, + towers: p.towers.map((t) => + t.id === towerId ? { ...t, blocks: [...t.blocks, block] } : t, + ), + } : p, ), ); @@ -356,16 +356,16 @@ export class StoreService implements OnDestroy { pages.map((p) => p.id === pageId ? { - ...p, - towers: p.towers.map((t) => - t.id === towerId - ? { - ...t, - blocks: this.patchBlockList(t.blocks, blockId, patch).blocks, - } - : t, - ), - } + ...p, + towers: p.towers.map((t) => + t.id === towerId + ? { + ...t, + blocks: this.patchBlockList(t.blocks, blockId, patch).blocks, + } + : t, + ), + } : p, ), ); @@ -377,13 +377,13 @@ export class StoreService implements OnDestroy { pages.map((p) => p.id === pageId ? { - ...p, - towers: p.towers.map((t) => - t.id === towerId - ? { ...t, blocks: t.blocks.filter((b) => b.id !== blockId) } - : t, - ), - } + ...p, + towers: p.towers.map((t) => + t.id === towerId + ? { ...t, blocks: t.blocks.filter((b) => b.id !== blockId) } + : t, + ), + } : p, ), ); @@ -396,21 +396,21 @@ export class StoreService implements OnDestroy { pages.map((p) => p.id === pageId ? { - ...p, - towers: p.towers.map((t) => - t.id === towerId - ? (() => { - const block = t.blocks.find((b) => b.id === blockId); - if (!block) return t; - const result = this.patchBlockList(t.blocks, blockId, { - is_done: !block.is_done, - }); - becameDone = result.becameDone; - return { ...t, blocks: result.blocks }; - })() - : t, - ), - } + ...p, + towers: p.towers.map((t) => + t.id === towerId + ? (() => { + const block = t.blocks.find((b) => b.id === blockId); + if (!block) return t; + const result = this.patchBlockList(t.blocks, blockId, { + is_done: !block.is_done, + }); + becameDone = result.becameDone; + return { ...t, blocks: result.blocks }; + })() + : t, + ), + } : p, ), ); diff --git a/frontend/src/app/services/store.service.vitest.ts b/frontend/src/app/services/store.service.vitest.ts index 3081883..273841a 100644 --- a/frontend/src/app/services/store.service.vitest.ts +++ b/frontend/src/app/services/store.service.vitest.ts @@ -280,7 +280,7 @@ describe('StoreService', () => { expect(blocks[2].created_at).toBe(100); }); - it('loads welcome example data with hundreds of completed squares', () => { + it('loads welcome example data with a stack of completed squares per tower', () => { const api = makeMockApi(); const store = configure(api); @@ -292,13 +292,13 @@ describe('StoreService', () => { expect(page.towers).toHaveLength(3); const doneBlocks = page.towers.flatMap((tower) => tower.blocks.filter((b) => b.is_done)); - expect(doneBlocks.length).toBeGreaterThanOrEqual(300); + expect(doneBlocks.length).toBeGreaterThanOrEqual(90); expect(new Set(page.towers.flatMap((tower) => tower.blocks.map((b) => b.difficulty))).size) .toBeGreaterThan(1); for (const tower of page.towers) { const doneDates = tower.blocks.filter((b) => b.is_done).map((b) => b.created_at); - expect(doneDates.length).toBeGreaterThanOrEqual(100); + expect(doneDates.length).toBeGreaterThanOrEqual(30); expect(doneDates).toEqual([...doneDates].sort((a, b) => a - b)); expect(new Set(tower.blocks.map((b) => b.difficulty)).size).toBeGreaterThan(1); } From 3930982bd838f516d670adf0f1433427c3fd5c57 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 30 May 2026 15:59:03 +0100 Subject: [PATCH 06/26] i'm happy --- frontend/src/app/components/tower/tower.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/tower/tower.component.ts b/frontend/src/app/components/tower/tower.component.ts index a9a3fdb..be0db59 100644 --- a/frontend/src/app/components/tower/tower.component.ts +++ b/frontend/src/app/components/tower/tower.component.ts @@ -170,7 +170,7 @@ export function selectVisibleStyledBlocks(
- @for (sq of squares(); track sq.key) { + @for (sq of squares(); track sq.key; let i = $index) { Date: Sat, 30 May 2026 16:57:56 +0100 Subject: [PATCH 07/26] Clean up --- Dockerfile | 3 +- README.md | 9 +- backend/pyproject.toml | 7 - backend/src/life_towers/api.py | 15 +- backend/src/life_towers/auth.py | 1 + backend/src/life_towers/logging.py | 9 +- backend/src/life_towers/main.py | 11 +- backend/src/life_towers/models.py | 24 +- backend/tests/test_api.py | 43 +++ backend/uv.lock | 11 - frontend/README.md | 62 +--- frontend/angular.json | 5 +- frontend/e2e/smoke.spec.ts | 29 +- frontend/e2e/visuals.spec.ts | 15 +- frontend/package-lock.json | 19 - frontend/package.json | 1 - frontend/src/app/app.config.ts | 2 - frontend/src/app/app.html | 343 ------------------ frontend/src/app/app.scss | 0 frontend/src/app/app.ts | 2 +- .../components/modal/block-edit.component.ts | 41 ++- .../app/components/modal/modal.component.ts | 3 +- .../modal/page-settings.component.ts | 151 -------- .../components/modal/settings.component.ts | 12 +- .../modal/tower-settings.component.ts | 2 +- .../app/components/page/page.component.html | 13 +- .../app/components/page/page.component.scss | 2 +- .../src/app/components/page/page.component.ts | 14 +- .../app/components/pages/pages.component.scss | 2 +- .../app/components/pages/pages.component.ts | 2 - .../double-slider/double-slider.component.ts | 37 +- .../shared/select-add/select-add.component.ts | 22 +- .../shared/toggle/toggle.component.ts | 18 +- .../app/components/tasks/tasks.component.ts | 53 +-- .../app/components/tower/tower.component.ts | 53 ++- frontend/src/app/models/index.ts | 6 +- frontend/src/app/services/api.service.ts | 6 +- .../src/app/services/api.service.vitest.ts | 69 +++- .../src/app/services/modal-state.service.ts | 2 +- frontend/src/app/services/store.service.ts | 165 +++++---- .../src/app/services/store.service.vitest.ts | 114 +++++- frontend/src/app/styles/_form-shared.scss | 106 ------ frontend/src/assets/arrow.svg | 4 - .../src/assets/fonts/material-icons.woff2 | Bin 128352 -> 0 bytes frontend/src/assets/fonts/roboto-300.woff2 | Bin 43136 -> 0 bytes frontend/src/assets/fonts/roboto-400.woff2 | Bin 43136 -> 0 bytes frontend/src/assets/fonts/roboto-500.woff2 | Bin 43136 -> 0 bytes frontend/src/assets/pen.svg | 4 - frontend/src/assets/plus-sign.svg | 12 - frontend/src/assets/trash.svg | 13 - frontend/src/assets/x-sign.svg | 4 - frontend/src/index.html | 2 +- frontend/src/styles.scss | 34 +- 53 files changed, 564 insertions(+), 1013 deletions(-) delete mode 100644 frontend/src/app/app.html delete mode 100644 frontend/src/app/app.scss delete mode 100644 frontend/src/app/components/modal/page-settings.component.ts delete mode 100644 frontend/src/app/styles/_form-shared.scss delete mode 100644 frontend/src/assets/arrow.svg delete mode 100644 frontend/src/assets/fonts/material-icons.woff2 delete mode 100644 frontend/src/assets/fonts/roboto-300.woff2 delete mode 100644 frontend/src/assets/fonts/roboto-400.woff2 delete mode 100644 frontend/src/assets/fonts/roboto-500.woff2 delete mode 100644 frontend/src/assets/pen.svg delete mode 100644 frontend/src/assets/plus-sign.svg delete mode 100644 frontend/src/assets/trash.svg delete mode 100644 frontend/src/assets/x-sign.svg diff --git a/Dockerfile b/Dockerfile index aad08e4..82fba50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,7 @@ USER appuser ENV LIFE_TOWERS_DB_PATH=/data/life-towers.db \ LIFE_TOWERS_STATIC_DIR=/app/static \ + LIFE_TOWERS_FORWARDED_ALLOW_IPS=* \ PYTHONUNBUFFERED=1 \ PYTHONPATH=/app/src \ PATH=/app/.venv/bin:$PATH @@ -53,4 +54,4 @@ EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD curl -fsS http://localhost:8000/api/v1/health || exit 1 -CMD ["uvicorn", "life_towers.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips=*"] +CMD ["sh", "-c", "uvicorn life_towers.main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips=${LIFE_TOWERS_FORWARDED_ALLOW_IPS}"] diff --git a/README.md b/README.md index 05bd0f8..b555e61 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,9 @@ Then visit http://localhost:8000. For a production-style run, set `LIFE_TOWERS_IMAGE` to point at your registry tag and use the default `docker-compose.yml`: ```bash -LIFE_TOWERS_IMAGE=registry.example.com/life-towers:latest \ - docker compose pull && docker compose up -d +export LIFE_TOWERS_IMAGE=registry.example.com/life-towers:latest +docker compose pull +docker compose up -d ``` ## Environment variables @@ -92,6 +93,6 @@ docker compose -f docker-compose.dev.yml down -v ## Deployment -Forgejo CI (`.forgejo/workflows/ci.yml`) builds and tests the backend, frontend, and Docker image on every push to `master`. On successful push to `master` it also tags and pushes the image to a registry — configure `REGISTRY_URL`, `REGISTRY_USER`, and `REGISTRY_PASSWORD` as repository secrets. +Forgejo CI (`.forgejo/workflows/ci.yml`) tests the backend, frontend, production build, and Playwright e2e flow on every push to `master`. -The deploy workflow (`.forgejo/workflows/deploy.yml`) triggers on `workflow_dispatch` or a `v*` tag push. It SSHs into the target server and runs `docker compose pull && docker compose up -d`, then polls the healthcheck. The server must have a `.env` file alongside `docker-compose.yml` that pins `LIFE_TOWERS_IMAGE` to the registry tag pushed by CI — otherwise `docker compose pull` is a no-op against the placeholder `life-towers:local`. Configure `DEPLOY_HOST`, `DEPLOY_USER`, `DEPLOY_SSH_KEY`, and `DEPLOY_PATH` as repository secrets before use. +The Docker workflow (`.forgejo/workflows/docker.yml`) builds and pushes images tagged `latest` and `sha-` on pushes to `master` or manual dispatch. It publishes to `vars.REGISTRY` (default `ghcr.io`) under the repository name, using `secrets.GITHUB_TOKEN` for registry auth. diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 3e93509..c643010 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -11,13 +11,6 @@ dependencies = [ "pydantic>=2.0.0", ] -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "httpx>=0.27.0", -] - [build-system] requires = ["hatchling"] build-backend = "hatchling.build" diff --git a/backend/src/life_towers/api.py b/backend/src/life_towers/api.py index 9fcee2a..8fbcc7f 100644 --- a/backend/src/life_towers/api.py +++ b/backend/src/life_towers/api.py @@ -1,8 +1,6 @@ -"""APIRouter with all Life Towers endpoints.""" - from __future__ import annotations -import json +import sqlite3 import time from typing import Annotated @@ -12,6 +10,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request from .auth import get_current_user from .db import db_connection from .limits import limiter +from .logging import token_log_id from .models import ( BlockOut, DataIn, @@ -53,7 +52,7 @@ async def register(request: Request, body: RegisterRequest) -> RegisterResponse: (now, token), ) conn.commit() - logger.info("user_registered", user_id=token, new=existing is None) + logger.info("user_registered", user_id=token_log_id(token), new=existing is None) return RegisterResponse(user_id=token) @@ -239,8 +238,14 @@ async def put_data( ) conn.commit() + except sqlite3.IntegrityError as exc: + conn.rollback() + raise HTTPException( + status_code=409, + detail="Submitted IDs conflict with existing data", + ) from exc except Exception: conn.rollback() raise - logger.info("data_replaced", user_id=user_id, pages=len(body.pages)) + logger.info("data_replaced", user_id=token_log_id(user_id), pages=len(body.pages)) diff --git a/backend/src/life_towers/auth.py b/backend/src/life_towers/auth.py index 4142948..8f7e01f 100644 --- a/backend/src/life_towers/auth.py +++ b/backend/src/life_towers/auth.py @@ -39,6 +39,7 @@ def get_current_user(request: Request) -> str: u = uuid.UUID(token) if u.version != 4: raise ValueError("Not v4") + token = str(u) except (ValueError, AttributeError): raise _unauthorized() diff --git a/backend/src/life_towers/logging.py b/backend/src/life_towers/logging.py index fea46c0..1cb9288 100644 --- a/backend/src/life_towers/logging.py +++ b/backend/src/life_towers/logging.py @@ -4,6 +4,7 @@ from __future__ import annotations import time import uuid as _uuid_mod +from hashlib import sha256 import structlog from fastapi import Request, Response @@ -26,8 +27,12 @@ def configure_logging() -> None: ) +def token_log_id(token: str) -> str: + return sha256(token.encode("utf-8")).hexdigest()[:12] + + async def request_logging_middleware(request: Request, call_next) -> Response: - """Log each request with method, path, status, duration_ms, user_id, request_id.""" + """Log each request without writing bearer credentials to the log stream.""" request_id = str(_uuid_mod.uuid4()) structlog.contextvars.clear_contextvars() structlog.contextvars.bind_contextvars(request_id=request_id) @@ -38,7 +43,7 @@ async def request_logging_middleware(request: Request, call_next) -> Response: if auth: parts = auth.split() if len(parts) == 2 and parts[0].lower() == "bearer": - user_id = parts[1] + user_id = token_log_id(parts[1]) start = time.monotonic() response = await call_next(request) diff --git a/backend/src/life_towers/main.py b/backend/src/life_towers/main.py index b9dd444..0e2a29a 100644 --- a/backend/src/life_towers/main.py +++ b/backend/src/life_towers/main.py @@ -1,8 +1,5 @@ -"""ASGI app, lifespan, static files mount, route registration.""" - from __future__ import annotations -import json import os from contextlib import asynccontextmanager from html import escape @@ -37,7 +34,6 @@ STATUS_CODE_MAP: dict[int, str] = { 413: "payload_too_large", 422: "bad_request", 429: "rate_limited", - 507: "quota_exceeded", 500: "server_error", } @@ -94,7 +90,12 @@ def create_app() -> FastAPI: request: Request, exc: RequestValidationError ) -> JSONResponse: fields = sorted( - {".".join(str(loc) for loc in e.get("loc", ()) if loc != "body") for e in exc.errors()} + field + for field in { + ".".join(str(loc) for loc in e.get("loc", ()) if loc != "body") + for e in exc.errors() + } + if field ) if fields: detail_str = "Validation failed for: " + ", ".join(f for f in fields if f) diff --git a/backend/src/life_towers/models.py b/backend/src/life_towers/models.py index 4fe8694..b40d5e3 100644 --- a/backend/src/life_towers/models.py +++ b/backend/src/life_towers/models.py @@ -8,12 +8,14 @@ from pydantic import BaseModel, Field, field_validator, model_validator import uuid as _uuid_mod -def _is_uuidv4(value: str) -> bool: +def _canonical_uuidv4(value: str) -> str: try: u = _uuid_mod.UUID(value) - return u.version == 4 + if u.version == 4: + return str(u) except (ValueError, AttributeError): - return False + pass + raise ValueError("must be a UUIDv4") class HslColor(BaseModel): @@ -33,9 +35,7 @@ class BlockIn(BaseModel): @field_validator("id") @classmethod def validate_id(cls, v: str) -> str: - if not _is_uuidv4(v): - raise ValueError(f"id must be a UUIDv4, got: {v!r}") - return v + return _canonical_uuidv4(v) class BlockOut(BaseModel): @@ -56,9 +56,7 @@ class TowerIn(BaseModel): @field_validator("id") @classmethod def validate_id(cls, v: str) -> str: - if not _is_uuidv4(v): - raise ValueError(f"id must be a UUIDv4, got: {v!r}") - return v + return _canonical_uuidv4(v) class TowerOut(BaseModel): @@ -80,9 +78,7 @@ class PageIn(BaseModel): @field_validator("id") @classmethod def validate_id(cls, v: str) -> str: - if not _is_uuidv4(v): - raise ValueError(f"id must be a UUIDv4, got: {v!r}") - return v + return _canonical_uuidv4(v) class PageOut(BaseModel): @@ -139,9 +135,7 @@ class RegisterRequest(BaseModel): @field_validator("token") @classmethod def validate_token(cls, v: str) -> str: - if not _is_uuidv4(v): - raise ValueError("token must be a UUIDv4") - return v + return _canonical_uuidv4(v) class RegisterResponse(BaseModel): diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 0037c88..404fa09 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -166,6 +166,21 @@ async def test_register_non_uuidv4_token(client: AsyncClient) -> None: assert resp.status_code == 400 +@pytest.mark.asyncio +async def test_uuid_inputs_are_canonicalized(client: AsyncClient) -> None: + token = make_uuidv4() + upper_token = token.upper() + register_resp = await client.post("/api/v1/register", json={"token": upper_token}) + assert register_resp.status_code == 200 + assert register_resp.json() == {"user_id": token} + + data_resp = await client.get( + "/api/v1/data", + headers={"Authorization": f"Bearer {upper_token}"}, + ) + assert data_resp.status_code == 200 + + # --------------------------------------------------------------------------- # Auth / GET /data # --------------------------------------------------------------------------- @@ -322,6 +337,34 @@ async def test_put_duplicate_page_id(client: AsyncClient) -> None: resp = await client.put("/api/v1/data", json=tree, headers=headers) assert resp.status_code == 400 # pydantic validation error → 400 bad_request per spec + assert resp.json() == {"error": "bad_request", "detail": "Validation failed"} + + +@pytest.mark.asyncio +async def test_put_cross_user_id_conflict_returns_409(client: AsyncClient) -> None: + first_token = make_uuidv4() + second_token = make_uuidv4() + await client.post("/api/v1/register", json={"token": first_token}) + await client.post("/api/v1/register", json={"token": second_token}) + + tree = _make_tree() + first_resp = await client.put( + "/api/v1/data", + json=tree, + headers={"Authorization": f"Bearer {first_token}"}, + ) + assert first_resp.status_code == 204 + + second_resp = await client.put( + "/api/v1/data", + json=tree, + headers={"Authorization": f"Bearer {second_token}"}, + ) + assert second_resp.status_code == 409 + assert second_resp.json() == { + "error": "conflict", + "detail": "Submitted IDs conflict with existing data", + } @pytest.mark.asyncio diff --git a/backend/uv.lock b/backend/uv.lock index 7f77e7c..90f555b 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -201,13 +201,6 @@ dependencies = [ { name = "uvicorn", extra = ["standard"] }, ] -[package.optional-dependencies] -dev = [ - { name = "httpx" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, -] - [package.dev-dependencies] dev = [ { name = "httpx" }, @@ -218,15 +211,11 @@ dev = [ [package.metadata] requires-dist = [ { name = "fastapi", specifier = ">=0.111.0" }, - { name = "httpx", marker = "extra == 'dev'", specifier = ">=0.27.0" }, { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, { name = "slowapi", specifier = ">=0.1.9" }, { name = "structlog", specifier = ">=24.1.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.29.0" }, ] -provides-extras = ["dev"] [package.metadata.requires-dev] dev = [ diff --git a/frontend/README.md b/frontend/README.md index 78d1bf3..3fe60d1 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,59 +1,11 @@ -# Frontend +# Life Towers Frontend -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.13. - -## Development server - -To start a local development server, run: +Angular frontend for the Life Towers app. See the root `README.md` for the full +stack setup. ```bash -ng serve +npm start # dev server on :4200, with /api proxied to :8000 +npm test # Vitest unit tests +npm run test:e2e # Playwright against PLAYWRIGHT_BASE_URL or localhost:8000 +npm run build # production bundle ``` - -Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. - -## Code scaffolding - -Angular CLI includes powerful code scaffolding tools. To generate a new component, run: - -```bash -ng generate component component-name -``` - -For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: - -```bash -ng generate --help -``` - -## Building - -To build the project run: - -```bash -ng build -``` - -This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. - -## Running unit tests - -To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command: - -```bash -ng test -``` - -## Running end-to-end tests - -For end-to-end (e2e) testing, run: - -```bash -ng e2e -``` - -Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. - -## Additional Resources - -For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/frontend/angular.json b/frontend/angular.json index e4ba9a9..2639328 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -77,9 +77,6 @@ "proxyConfig": "proxy.conf.json" } }, - "test": { - "builder": "@angular/build:unit-test" - }, "lint": { "builder": "@angular-eslint/builder:lint", "options": { @@ -92,4 +89,4 @@ } } } -} \ No newline at end of file +} diff --git a/frontend/e2e/smoke.spec.ts b/frontend/e2e/smoke.spec.ts index 4a20b30..f9bb312 100644 --- a/frontend/e2e/smoke.spec.ts +++ b/frontend/e2e/smoke.spec.ts @@ -11,8 +11,11 @@ test.describe('Life Towers smoke test', () => { test('create page → tower → block, mark done, reload, persists', async ({ page }) => { await page.goto('/'); - // Wait for the empty-state hint that means init() completed. - await expect(page.getByText('Add a new page to get started!')).toBeVisible({ timeout: 15000 }); + // Wait for init, then dismiss the welcome modal so the page controls are reachable. + await expect(page.getByText('Welcome to Life Towers')).toBeVisible({ timeout: 15000 }); + await page.getByRole('button', { name: 'Start empty' }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + await expect(page.getByText('Add a new page to get started!')).toBeVisible(); // Create a page via the select-add dropdown. await page.locator('lt-select-add .top').first().click(); @@ -24,7 +27,7 @@ test.describe('Life Towers smoke test', () => { // Create a tower. await page.locator('img[alt="Add tower"]').click(); - await page.locator('input[placeholder="Tower name…"]').fill('Side projects'); + await page.locator('input[placeholder="New tower"]').fill('Side projects'); await page.locator('lt-tower-settings button[type="submit"]').click(); // Tower's name input is rendered with the tower name as its value. @@ -43,27 +46,25 @@ test.describe('Life Towers smoke test', () => { await page.locator('textarea[placeholder="Write a description here…"]').fill( 'Modernise the towers app', ); - await page.getByRole('button', { name: 'Create and exit' }).click(); + await page.getByLabel('Already done').uncheck(); + await page.getByRole('button', { name: 'Create and exit', exact: true }).click(); // New block is pending → appears in the tasks accordion. // (Tasks header shows N tasks.) await expect(page.locator('lt-tasks')).toContainText('1'); // Open the tasks accordion + click the task to edit it, then flip done. - await page.locator('lt-tasks .container').click(); - await page.locator('lt-tasks .task-container').click(); + await page.locator('lt-tasks .header').click(); + await page.locator('lt-tasks .task-description').click(); - // Toggle done in the block-edit modal — the right label is "Done". + // Toggle done in the block-edit modal. const putLanded = page.waitForResponse( (r) => r.url().endsWith('/api/v1/data') && r.request().method() === 'PUT' && r.ok(), ); - // Toggle uses verbose labels — "Goal accomplished" flips it to done. - await page - .locator('lt-block-edit lt-toggle span') - .filter({ hasText: 'Goal accomplished' }) - .click(); - await page.getByRole('button', { name: 'Create and exit' }).click(); + await page.locator('lt-block-edit .card.active').getByLabel('Already done').check(); await putLanded; + await page.locator('lt-block-edit .card.active .exit').click(); + await page.waitForSelector('section.modal', { state: 'detached' }); // Done block now appears as a colored square in the tower's falling stack. await expect(page.locator('lt-tower lt-block').first()).toBeVisible(); @@ -73,7 +74,7 @@ test.describe('Life Towers smoke test', () => { await expect(page.locator('lt-select-add .top').first()).toContainText('Hobbies', { timeout: 15000, }); - await expect(page.getByDisplayValue('Side projects')).toBeVisible(); + await expect(page.locator('lt-tower input').first()).toHaveValue('Side projects'); await expect(page.locator('lt-tower lt-block').first()).toBeVisible(); }); }); diff --git a/frontend/e2e/visuals.spec.ts b/frontend/e2e/visuals.spec.ts index a1e3384..dc538ab 100644 --- a/frontend/e2e/visuals.spec.ts +++ b/frontend/e2e/visuals.spec.ts @@ -1,5 +1,10 @@ import { test } from '@playwright/test'; +test.skip( + process.env['CAPTURE_VISUALS'] !== '1', + 'Set CAPTURE_VISUALS=1 to run the visual screenshot capture suite.', +); + /** * Visual capture: drives the UI into key states and writes screenshots * for human review of the legacy-styled design. @@ -55,12 +60,12 @@ test.describe('Life Towers visuals', () => { .fill('Finish The Brothers Karamazov'); // Uncheck "Already done" so this becomes a pending task. await createCard.getByLabel('Already done').uncheck(); - await page.getByRole('button', { name: 'Create and exit' }).click(); + await page.getByRole('button', { name: 'Create and exit', exact: true }).click(); await page.waitForSelector('section.modal', { state: 'detached' }); // Open the tasks accordion to show the new tickbox. await page.waitForTimeout(200); - await page.locator('lt-tasks .container').click(); + await page.locator('lt-tasks .header').click(); await page.waitForTimeout(300); await page.screenshot({ path: 'visuals/04b-tasks-accordion-with-tickbox.png', fullPage: true }); @@ -83,7 +88,7 @@ test.describe('Life Towers visuals', () => { await cc.locator('lt-select-add .top').click(); await page.waitForTimeout(100); await cc.locator('textarea[placeholder="Write a description here…"]').fill(desc); - await page.getByRole('button', { name: 'Create and exit' }).click(); + await page.getByRole('button', { name: 'Create and exit', exact: true }).click(); await page.waitForSelector('section.modal', { state: 'detached' }); } @@ -196,9 +201,9 @@ test.describe('Life Towers visuals', () => { await page.screenshot({ path: 'visuals/14-mobile-populated.png', fullPage: true }); // Open the block-edit carousel for the first tower's first task. - await page.locator('lt-tasks .container').first().click(); + await page.locator('lt-tasks .header').first().click(); await page.waitForTimeout(400); - await page.locator('lt-tasks .task-container').first().click(); + await page.locator('lt-tasks .task-description').first().click(); await page.waitForSelector('section.modal.active'); await page.waitForTimeout(400); await page.screenshot({ path: 'visuals/15-mobile-carousel.png', fullPage: true }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 84bcb8a..8ea82d5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,7 +14,6 @@ "@angular/core": "^21.2.0", "@angular/forms": "^21.2.0", "@angular/platform-browser": "^21.2.0", - "@angular/router": "^21.2.0", "@angular/service-worker": "^21.2.0", "@plausible-analytics/tracker": "^0.4.5", "rxjs": "~7.8.0", @@ -719,24 +718,6 @@ } } }, - "node_modules/@angular/router": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.2.14.tgz", - "integrity": "sha512-Yo3LdgcqkfMu2/Ycl8o/4QjCBqZhtA+a7B8JVdW5cWdrpFTxKCOrzm+YRUMuIFmH5nzSv9oGnUuz64uk1+7r5Q==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/common": "21.2.14", - "@angular/core": "21.2.14", - "@angular/platform-browser": "21.2.14", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, "node_modules/@angular/service-worker": { "version": "21.2.14", "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-21.2.14.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8c0c02c..a9aaf77 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,6 @@ "@angular/core": "^21.2.0", "@angular/forms": "^21.2.0", "@angular/platform-browser": "^21.2.0", - "@angular/router": "^21.2.0", "@angular/service-worker": "^21.2.0", "rxjs": "~7.8.0", "tslib": "^2.3.0" diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index b8fcf5c..8a08685 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -4,7 +4,6 @@ import { isDevMode, provideZonelessChangeDetection, } from '@angular/core'; -import { provideRouter } from '@angular/router'; import { provideHttpClient, withFetch } from '@angular/common/http'; import { provideServiceWorker } from '@angular/service-worker'; @@ -12,7 +11,6 @@ export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection(), - provideRouter([]), provideHttpClient(withFetch()), provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode(), diff --git a/frontend/src/app/app.html b/frontend/src/app/app.html deleted file mode 100644 index 0edf11a..0000000 --- a/frontend/src/app/app.html +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - -
- -
- - - - - - - - - - diff --git a/frontend/src/app/app.scss b/frontend/src/app/app.scss deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/app/app.ts b/frontend/src/app/app.ts index 05f109e..ec56fbb 100644 --- a/frontend/src/app/app.ts +++ b/frontend/src/app/app.ts @@ -16,6 +16,6 @@ export class App implements OnInit { ngOnInit(): void { this.analytics.init(); - this.store.init(); + void this.store.init(); } } diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index cd50b5b..64e73e1 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -3,7 +3,6 @@ import { ChangeDetectionStrategy, input, output, - inject, signal, computed, effect, @@ -33,6 +32,10 @@ interface EditedValue { difficulty: number; } +function clampDifficulty(value: number): number { + return Math.max(1, Math.min(100, value)); +} + @Component({ selector: 'lt-block-edit', standalone: true, @@ -48,6 +51,8 @@ interface EditedValue { class="carousel" (scroll)="onScroll()" (click)="onBackdropClick($event)" + (keydown.enter)="onBackdropClick($any($event))" + tabindex="-1" >
@@ -56,12 +61,22 @@ interface EditedValue { class="card" [class.active]="activeIdx() === i + 1" [class.near-active]="activeIdx() === i || activeIdx() === i + 2" + role="button" + tabindex="0" + [attr.aria-label]="'Focus ' + (editedFor(b.id).tag || 'block')" (click)="onCardClick(i + 1)" + (keydown.enter)="onCardClick(i + 1)" + (keydown.space)="$event.preventDefault(); onCardClick(i + 1)" >
-
+
+
@@ -127,12 +144,22 @@ interface EditedValue { class="card create-card" [class.active]="activeIdx() === blocks().length + 1" [class.near-active]="activeIdx() === blocks().length" + role="button" + tabindex="0" + aria-label="Focus create card" (click)="onCardClick(blocks().length + 1)" + (keydown.enter)="onCardClick(blocks().length + 1)" + (keydown.space)="$event.preventDefault(); onCardClick(blocks().length + 1)" >
-
+
Create now
-
+
@@ -182,6 +210,7 @@ interface EditedValue { type="button" class="step" aria-label="Increase difficulty" + [disabled]="newValue().difficulty >= 100" (click)="updateNewDifficulty(1); $event.stopPropagation()" >+
@@ -671,7 +700,7 @@ export class BlockEditComponent implements AfterViewInit { } updateDifficulty(id: string, delta: number): void { - const next = Math.max(1, this.editedFor(id).difficulty + delta); + const next = clampDifficulty(this.editedFor(id).difficulty + delta); this.patchEdited(id, { difficulty: next }); this.flushExisting(id); } @@ -717,7 +746,7 @@ export class BlockEditComponent implements AfterViewInit { } updateNewDifficulty(delta: number): void { - this.newValue.update((v) => ({ ...v, difficulty: Math.max(1, v.difficulty + delta) })); + this.newValue.update((v) => ({ ...v, difficulty: clampDifficulty(v.difficulty + delta) })); } submitNew(): void { diff --git a/frontend/src/app/components/modal/modal.component.ts b/frontend/src/app/components/modal/modal.component.ts index 3a0fda2..47c9eb8 100644 --- a/frontend/src/app/components/modal/modal.component.ts +++ b/frontend/src/app/components/modal/modal.component.ts @@ -23,6 +23,8 @@ import { ModalStateService } from '../../services/modal-state.service'; class="modal" [class.active]="active()" (click)="onBackdropClick($event)" + (keydown.enter)="onBackdropClick($any($event))" + tabindex="-1" > +
- Add tower + Add tower
} @@ -57,7 +66,7 @@
-
+

Delete tower

Delete {{ confirmDeleteTowerName() || 'this tower' }} and all of its blocks? This can't be undone.

diff --git a/frontend/src/app/components/page/page.component.scss b/frontend/src/app/components/page/page.component.scss index cf5689d..ed8aa0c 100644 --- a/frontend/src/app/components/page/page.component.scss +++ b/frontend/src/app/components/page/page.component.scss @@ -1,4 +1,4 @@ -@import '../../../styles'; +@import '../../../library/main'; :host { display: flex; diff --git a/frontend/src/app/components/page/page.component.ts b/frontend/src/app/components/page/page.component.ts index 7311479..5b0d990 100644 --- a/frontend/src/app/components/page/page.component.ts +++ b/frontend/src/app/components/page/page.component.ts @@ -4,8 +4,11 @@ import { input, output, signal, + computed, inject, HostListener, + effect, + untracked, } from '@angular/core'; import { Page } from '../../models'; import { StoreService } from '../../services/store.service'; @@ -16,7 +19,6 @@ import { DoubleSliderComponent, DoubleSliderRange, } from '../shared/double-slider/double-slider.component'; -import { computed } from '@angular/core'; import { CdkDropList, CdkDrag, CdkDragDrop } from '@angular/cdk/drag-drop'; import { ModalStateService } from '../../services/modal-state.service'; @@ -116,6 +118,14 @@ export class PageComponent { /** Selected date range — `null` = show everything. */ readonly dateRange = signal<{ from: number; to: number } | null>(null); + constructor() { + effect(() => { + if (!this.showSlider()) { + untracked(() => this.dateRange.set(null)); + } + }); + } + onSliderRangeChange(range: DoubleSliderRange): void { this.dateRange.set({ from: range.from as number, to: range.to as number }); } @@ -147,7 +157,7 @@ export class PageComponent { } onDeleteTower(towerId: string): void { - this.store.deleteTower(this.page().id, towerId); + this.confirmDeleteTowerId.set(towerId); } // ── Block mutations ──────────────────────────────────────────────────────── diff --git a/frontend/src/app/components/pages/pages.component.scss b/frontend/src/app/components/pages/pages.component.scss index 631de3e..13f10bb 100644 --- a/frontend/src/app/components/pages/pages.component.scss +++ b/frontend/src/app/components/pages/pages.component.scss @@ -1,4 +1,4 @@ -@import '../../../styles'; +@import '../../../library/main'; :host { height: 100%; diff --git a/frontend/src/app/components/pages/pages.component.ts b/frontend/src/app/components/pages/pages.component.ts index 900ba40..cbce254 100644 --- a/frontend/src/app/components/pages/pages.component.ts +++ b/frontend/src/app/components/pages/pages.component.ts @@ -82,8 +82,6 @@ export class PagesComponent implements OnDestroy { return pages[0] ?? null; }); - readonly selectedPageName = computed(() => this.selectedPage()?.name ?? null); - readonly confirmDeletePageName = computed(() => { const id = this.confirmDeletePageId(); if (!id) return ''; diff --git a/frontend/src/app/components/shared/double-slider/double-slider.component.ts b/frontend/src/app/components/shared/double-slider/double-slider.component.ts index f118e44..892e935 100644 --- a/frontend/src/app/components/shared/double-slider/double-slider.component.ts +++ b/frontend/src/app/components/shared/double-slider/double-slider.component.ts @@ -32,7 +32,7 @@ export interface DoubleSliderRange { id="ds-1" type="range" min="0" - [max]="MAX - 1" + [max]="maxIndex()" [value]="oneValue()" (input)="oneValue.set(+$any($event.target).value)" /> @@ -40,7 +40,7 @@ export interface DoubleSliderRange { id="ds-2" type="range" min="0" - [max]="MAX - 1" + [max]="maxIndex()" [value]="otherValue()" (input)="otherValue.set(+$any($event.target).value)" /> @@ -168,10 +168,9 @@ export class DoubleSliderComponent { readonly rangeChange = output>(); - readonly MAX = 100; - readonly oneValue = signal(0); - readonly otherValue = signal(this.MAX - 1); + readonly otherValue = signal(0); + readonly maxIndex = computed(() => Math.max(0, this.values().length - 1)); private prevValuesLength = 0; @@ -198,32 +197,33 @@ export class DoubleSliderComponent { const hi = Math.max(a, b); untracked(() => { this.rangeChange.emit({ - from: vs[this.indexFromValue(lo)], - to: vs[this.indexFromValue(hi)], + from: vs[this.clampIndex(lo)], + to: vs[this.clampIndex(hi)], }); }); }); - // Snap the higher thumb to MAX - 1 when a new entry is appended. + // Snap the higher thumb to the newest value when a new entry is appended. effect(() => { const len = this.values().length; untracked(() => { + const max = Math.max(0, len - 1); if (len > this.prevValuesLength) { const a = this.oneValue(); const b = this.otherValue(); - if (a > b) this.oneValue.set(this.MAX - 1); - else this.otherValue.set(this.MAX - 1); + if (a > b) this.oneValue.set(max); + else this.otherValue.set(max); + } else { + if (this.oneValue() > max) this.oneValue.set(max); + if (this.otherValue() > max) this.otherValue.set(max); } this.prevValuesLength = len; }); }); } - private indexFromValue(value: number): number { - return Math.min( - this.values().length - 1, - Math.floor((value / this.MAX) * this.values().length), - ); + private clampIndex(value: number): number { + return Math.max(0, Math.min(this.values().length - 1, Math.round(value))); } /** @@ -231,9 +231,10 @@ export class DoubleSliderComponent { * upward and rotates -45° as a thumb approaches. */ getOffset(index: number): string { - const labelIndex = index / Math.max(1, this.drawnLabels().length); - const a = this.oneValue() / this.MAX - 0.1; - const b = this.otherValue() / this.MAX - 0.1; + const labelIndex = index / Math.max(1, this.drawnLabels().length - 1); + const max = Math.max(1, this.maxIndex()); + const a = this.oneValue() / max - 0.1; + const b = this.otherValue() / max - 0.1; const dist = Math.min(Math.abs(labelIndex - a), Math.abs(labelIndex - b)); const ACTIVE_ZONE = 0.2; const base = 'translateX(-50%) rotate(-30deg) translateY(100%)'; diff --git a/frontend/src/app/components/shared/select-add/select-add.component.ts b/frontend/src/app/components/shared/select-add/select-add.component.ts index 5ee768e..baac459 100644 --- a/frontend/src/app/components/shared/select-add/select-add.component.ts +++ b/frontend/src/app/components/shared/select-add/select-add.component.ts @@ -4,8 +4,6 @@ import { input, output, signal, - OnChanges, - SimpleChanges, ElementRef, HostListener, inject, @@ -22,7 +20,15 @@ import { [class.always-shadow]="alwaysDropShadow()" >
-
+

{{ resolvedSelected() ?? placeholder() }}

@@ -33,6 +39,7 @@ import { } @else { @@ -46,6 +53,7 @@ import { type="text" #addInput placeholder="Add a value…" + maxlength="200" (keydown.enter)="onAdd(addInput.value); addInput.value = ''" />
-

{{ b.description || b.tag }}

+ >{{ b.description || b.tag }}
}
@@ -87,9 +88,19 @@ import { getColorOfTag } from '../../utils/color'; max-height: 30vh; overflow-y: auto; - .header { cursor: pointer; } + .header { + all: unset; + @include medium-text(); + display: block; + width: 100%; + box-sizing: border-box; + cursor: pointer; + text-align: center; - p { font-size: var(--medium-font-size); } + &::after { + content: none; + } + } .all-task { @include inner-spacing(var(--small-padding)); @@ -124,7 +135,7 @@ import { getColorOfTag } from '../../utils/color'; gap: calc(var(--small-padding) / 2); } - &:hover p { + &:hover .task-description { @media (min-width: $mobile-width) { color: inherit !important; } @@ -187,7 +198,9 @@ import { getColorOfTag } from '../../utils/color'; } } - p { + .task-description { + all: unset; + @include medium-text(); white-space: nowrap; text-overflow: ellipsis; overflow-x: hidden; @@ -203,6 +216,7 @@ import { getColorOfTag } from '../../utils/color'; } position: relative; + &::after { content: none; } } } } @@ -222,7 +236,6 @@ export class TasksComponent { readonly edit = output(); readonly expanded = signal(false); - private lastToggleAt = 0; constructor() { // Re-sync `expanded` whenever the `initiallyOpen` input changes so flipping @@ -241,12 +254,6 @@ export class TasksComponent { toggleExpanded(event: Event): void { event.stopPropagation(); - if (event.type === 'touchend') { - event.preventDefault(); - } - const now = Date.now(); - if (now - this.lastToggleAt < 250) return; - this.lastToggleAt = now; this.expanded.update((v) => !v); } } diff --git a/frontend/src/app/components/tower/tower.component.ts b/frontend/src/app/components/tower/tower.component.ts index be0db59..a6422c9 100644 --- a/frontend/src/app/components/tower/tower.component.ts +++ b/frontend/src/app/components/tower/tower.component.ts @@ -140,7 +140,6 @@ export function selectVisibleStyledBlocks(
(); - /** Set by page component when this tower is being dragged over trash. */ - readonly trashHighlight = input(false); /** Optional date range filter — when set, blocks with `created_at` * outside [from, to] are hidden from the falling stack. */ readonly dateRange = input<{ from: number; to: number } | null>(null); @@ -547,6 +544,8 @@ export class TowerComponent implements AfterViewInit, OnDestroy { private readonly towerRoot = viewChild>('towerRoot'); private readonly maxVisibleBlocks = signal(null); private resizeObserver: ResizeObserver | null = null; + private destroyed = false; + private readonly animationFrames = new Set(); // ── Derived ──────────────────────────────────────────────────────────────── /** Pending (not-done) blocks — fed to the tasks accordion. */ @@ -638,6 +637,9 @@ export class TowerComponent implements AfterViewInit, OnDestroy { } ngOnDestroy(): void { + this.destroyed = true; + for (const id of this.animationFrames) cancelAnimationFrame(id); + this.animationFrames.clear(); this.resizeObserver?.disconnect(); } @@ -788,25 +790,48 @@ export class TowerComponent implements AfterViewInit, OnDestroy { block._opacity = '0'; } this._visibleBlocks.set(visibleStyled); - requestAnimationFrame(() => { - requestAnimationFrame(() => { - for (const block of blocks) { - block._anim = 'descend'; - block._transform = 'translateY(0)'; - block._opacity = '1'; - } - this._visibleBlocks.set([...this._visibleBlocks()]); + this.requestFrame(() => { + this.requestFrame(() => { + if (this.destroyed) return; + this.finishDescendAnimation(blocks); }); }); } + private requestFrame(callback: () => void): void { + if (typeof requestAnimationFrame !== 'function') { + callback(); + return; + } + const id = requestAnimationFrame(() => { + this.animationFrames.delete(id); + if (!this.destroyed) callback(); + }); + this.animationFrames.add(id); + } + + private finishDescendAnimation(blocks: StyledBlock[]): void { + for (const block of blocks) { + block._anim = 'descend'; + block._transform = 'translateY(0)'; + block._opacity = '1'; + } + this._visibleBlocks.set([...this._visibleBlocks()]); + } + // ── Event handlers ───────────────────────────────────────────────────────── onRename(event: Event): void { const input = event.target as HTMLInputElement; const newName = input.value.trim(); - if (newName && newName !== this.tower().name) { + if (!newName) { + input.value = this.tower().name; + return; + } + if (newName !== this.tower().name) { this.updateTower.emit({ name: newName, base_color: this.tower().base_color }); + } else { + input.value = this.tower().name; } } diff --git a/frontend/src/app/models/index.ts b/frontend/src/app/models/index.ts index e9216d0..083f09d 100644 --- a/frontend/src/app/models/index.ts +++ b/frontend/src/app/models/index.ts @@ -40,7 +40,7 @@ export type SaveStatus = | 'saving' | 'saved' | 'retrying' - | 'error' // generic / network — will keep trying - | 'too-large' // 413 — payload exceeds the server cap, won't retry + | 'error' // generic / network — retries exhausted until the next mutation + | 'too-large' // 413 — payload exceeds the server cap, will not retry | 'rate-limited' // 429 — will retry after Retry-After - | 'invalid'; // 400 — server rejected the body, won't retry + | 'invalid'; // 400 — server rejected the body, will not retry diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 2e9b8e8..58bf117 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -23,9 +23,9 @@ export class ApiService { ); } - putData(token: string, tree: TreeDto): Promise { - return firstValueFrom( - this.http.put('/api/v1/data', tree, { headers: this.authHeaders(token) }), + async putData(token: string, tree: TreeDto): Promise { + await firstValueFrom( + this.http.put('/api/v1/data', tree, { headers: this.authHeaders(token) }), ); } diff --git a/frontend/src/app/services/api.service.vitest.ts b/frontend/src/app/services/api.service.vitest.ts index d8a681f..e9cf798 100644 --- a/frontend/src/app/services/api.service.vitest.ts +++ b/frontend/src/app/services/api.service.vitest.ts @@ -1,28 +1,61 @@ -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { TestBed } from '@angular/core/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; +import { ApiService } from './api.service'; +import type { TreeDto } from '../models'; -// Mock environment -vi.mock('../../environments/environment', () => ({ - environment: { apiBase: 'http://test-api', production: false }, -})); +describe('ApiService', () => { + let service: ApiService; + let http: HttpTestingController; -describe('ApiService URL patterns', () => { - const baseUrl = 'http://test-api'; - - it('constructs correct health URL', () => { - expect(`${baseUrl}/api/v1/health`).toBe('http://test-api/api/v1/health'); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideHttpClient(), provideHttpClientTesting(), ApiService], + }); + service = TestBed.inject(ApiService); + http = TestBed.inject(HttpTestingController); }); - it('constructs correct register URL', () => { - expect(`${baseUrl}/api/v1/register`).toBe('http://test-api/api/v1/register'); + afterEach(() => { + http.verify(); }); - it('constructs correct data URL', () => { - expect(`${baseUrl}/api/v1/data`).toBe('http://test-api/api/v1/data'); + it('gets health', async () => { + const promise = service.health(); + const req = http.expectOne('/api/v1/health'); + expect(req.request.method).toBe('GET'); + req.flush({ status: 'ok' }); + await expect(promise).resolves.toEqual({ status: 'ok' }); }); - it('formats Authorization header correctly', () => { - const token = 'abc-def-123'; - const header = `Bearer ${token}`; - expect(header).toBe('Bearer abc-def-123'); + it('registers a token', async () => { + const promise = service.register('token-1'); + const req = http.expectOne('/api/v1/register'); + expect(req.request.method).toBe('POST'); + expect(req.request.body).toEqual({ token: 'token-1' }); + req.flush({ user_id: 'token-1' }); + await expect(promise).resolves.toEqual({ user_id: 'token-1' }); + }); + + it('gets data with a bearer token', async () => { + const tree: TreeDto = { pages: [] }; + const promise = service.getData('token-1'); + const req = http.expectOne('/api/v1/data'); + expect(req.request.method).toBe('GET'); + expect(req.request.headers.get('Authorization')).toBe('Bearer token-1'); + req.flush(tree); + await expect(promise).resolves.toEqual(tree); + }); + + it('puts data with a bearer token', async () => { + const tree: TreeDto = { pages: [] }; + const promise = service.putData('token-1', tree); + const req = http.expectOne('/api/v1/data'); + expect(req.request.method).toBe('PUT'); + expect(req.request.headers.get('Authorization')).toBe('Bearer token-1'); + expect(req.request.body).toBe(tree); + req.flush(null); + await expect(promise).resolves.toBeUndefined(); }); }); diff --git a/frontend/src/app/services/modal-state.service.ts b/frontend/src/app/services/modal-state.service.ts index c1565ed..eb78a78 100644 --- a/frontend/src/app/services/modal-state.service.ts +++ b/frontend/src/app/services/modal-state.service.ts @@ -5,7 +5,7 @@ import { Injectable, computed, signal } from '@angular/core'; * mount and decrements on destroy. Consumers read `anyOpen` to react. * * Used by `page.component` to disable tower drag-and-drop while any modal - * (block-edit carousel, page-settings, tower-settings, confirm-delete, + * (block-edit carousel, settings, tower-settings, confirm-delete, * settings) is on screen — otherwise the user can drag towers from behind * the open card. */ diff --git a/frontend/src/app/services/store.service.ts b/frontend/src/app/services/store.service.ts index fd35146..280b049 100644 --- a/frontend/src/app/services/store.service.ts +++ b/frontend/src/app/services/store.service.ts @@ -1,10 +1,10 @@ -import { Injectable, inject, signal, computed, OnDestroy } from '@angular/core'; +import { Injectable, inject, signal, OnDestroy } from '@angular/core'; import { ApiService } from './api.service'; import { AnalyticsService } from './analytics.service'; import { Page, Tower, Block, TreeDto, SaveStatus, HslColor } from '../models'; const TOKEN_KEY = 'life-towers.token.v4'; -const CACHE_KEY = 'life-towers.cache.v4'; +const CACHE_KEY_PREFIX = 'life-towers.cache.v4'; const DEBOUNCE_MS = 750; const MAX_RETRIES = 5; @@ -29,7 +29,7 @@ function uuidV4(): string { function isUuidV4(value: string): boolean { return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test( - value, + value.toLowerCase(), ); } @@ -51,6 +51,10 @@ function safeSet(key: string, value: string): void { } } +function cacheKeyForToken(token: string): string { + return `${CACHE_KEY_PREFIX}.${token}`; +} + interface PendingPut { token: string; tree: TreeDto; @@ -87,11 +91,10 @@ export class StoreService implements OnDestroy { readonly loading = this._loading.asReadonly(); readonly token = this._token.asReadonly(); - readonly pageCount = computed(() => this._pages().length); - // ── Debounce / retry ─────────────────────────────────────────────────────── private debounceTimer: ReturnType | null = null; private retryTimer: ReturnType | null = null; + private retryResolver: (() => void) | null = null; private flushInFlight = false; // True while in-flight if a new mutation arrived; we'll re-flush after. private dirtyDuringFlush = false; @@ -99,14 +102,8 @@ export class StoreService implements OnDestroy { // ── Cross-tab sync ───────────────────────────────────────────────────────── private readonly storageListener = (e: StorageEvent) => { if (e.key === TOKEN_KEY && e.newValue && e.newValue !== this._token()) { - // Another tab switched accounts. Re-init with the new token instead of - // continuing to sync the old account's state to the new one. - this._token.set(e.newValue); - this._pages.set([]); - this._loading.set(true); - this.cancelPendingWrites(); - void this.init(); - } else if (e.key === CACHE_KEY && e.newValue && !this.flushInFlight) { + this.switchToken(e.newValue); + } else if (e.key === cacheKeyForToken(this._token()) && e.newValue && !this.flushInFlight) { // Another tab just wrote a fresh cache; adopt it if we're not mid-save // (to avoid clobbering our own state with the other tab's older view). try { @@ -120,17 +117,21 @@ export class StoreService implements OnDestroy { // ── Single-flight init ───────────────────────────────────────────────────── private initPromise: Promise | null = null; + private initGeneration = 0; // ── Init ─────────────────────────────────────────────────────────────────── async init(): Promise { if (this.initPromise) return this.initPromise; - this.initPromise = this.doInit().finally(() => { - this.initPromise = null; + const generation = ++this.initGeneration; + this.initPromise = this.doInit(generation).finally(() => { + if (this.initGeneration === generation) { + this.initPromise = null; + } }); return this.initPromise; } - private async doInit(): Promise { + private async doInit(generation: number): Promise { if (typeof window !== 'undefined') { // (idempotent — adding the same listener twice is a no-op) window.addEventListener('storage', this.storageListener); @@ -140,6 +141,9 @@ export class StoreService implements OnDestroy { if (stored && !isUuidV4(stored)) { // Garbage in localStorage from a buggy past version — refuse it. stored = null; + } else if (stored) { + stored = stored.toLowerCase(); + safeSet(TOKEN_KEY, stored); } const token = stored ?? uuidV4(); if (!stored) { @@ -157,7 +161,7 @@ export class StoreService implements OnDestroy { try { const tree = await this.api.getData(token); - this.adoptServerTree(tree); + if (this.isCurrentInit(generation, token)) this.adoptServerTree(tree, token); } catch (err: unknown) { const status = (err as { status?: number })?.status; if (status === 401) { @@ -165,26 +169,32 @@ export class StoreService implements OnDestroy { try { await this.api.register(token); const tree = await this.api.getData(token); - this.adoptServerTree(tree); + if (this.isCurrentInit(generation, token)) this.adoptServerTree(tree, token); } catch { - this.loadFromCache(); + if (this.isCurrentInit(generation, token)) this.loadFromCache(token); } } else { - this.loadFromCache(); + if (this.isCurrentInit(generation, token)) this.loadFromCache(token); } } finally { - this._loading.set(false); + if (this.initGeneration === generation) { + this._loading.set(false); + } } } + private isCurrentInit(generation: number, token: string): boolean { + return this.initGeneration === generation && this._token() === token; + } + /** * Apply a freshly-fetched server tree. If the server is empty but our local * cache holds data, the cache wins and we schedule a push — otherwise the * "server forgot me" recovery would silently wipe offline edits. */ - private adoptServerTree(tree: TreeDto): void { + private adoptServerTree(tree: TreeDto, token: string): void { if (tree.pages.length === 0) { - const cached = safeGet(CACHE_KEY); + const cached = safeGet(cacheKeyForToken(token)); if (cached) { try { const cachedTree: TreeDto = JSON.parse(cached); @@ -199,11 +209,11 @@ export class StoreService implements OnDestroy { } } this._pages.set(tree.pages); - this.updateCache(tree); + this.updateCache(token, tree); } - private loadFromCache(): void { - const raw = safeGet(CACHE_KEY); + private loadFromCache(token: string): void { + const raw = safeGet(cacheKeyForToken(token)); if (raw) { try { const tree: TreeDto = JSON.parse(raw); @@ -214,8 +224,8 @@ export class StoreService implements OnDestroy { } } - private updateCache(tree: TreeDto): void { - safeSet(CACHE_KEY, JSON.stringify(tree)); + private updateCache(token: string, tree: TreeDto): void { + safeSet(cacheKeyForToken(token), JSON.stringify(tree)); } private cancelPendingWrites(): void { @@ -227,6 +237,11 @@ export class StoreService implements OnDestroy { clearTimeout(this.retryTimer); this.retryTimer = null; } + if (this.retryResolver !== null) { + const resolve = this.retryResolver; + this.retryResolver = null; + resolve(); + } this.dirtyDuringFlush = false; } @@ -261,13 +276,13 @@ export class StoreService implements OnDestroy { } reorderPages(fromIndex: number, toIndex: number): void { + let changed = false; this._pages.update((pages) => { - const arr = [...pages]; - const [item] = arr.splice(fromIndex, 1); - arr.splice(toIndex, 0, item); - return arr; + const reordered = reorder(pages, fromIndex, toIndex); + changed = reordered !== null; + return reordered ?? pages; }); - this.scheduleSave(); + if (changed) this.scheduleSave(); } addTower(pageId: string, name: string, base_color: HslColor): void { @@ -301,16 +316,17 @@ export class StoreService implements OnDestroy { } reorderTowers(pageId: string, fromIndex: number, toIndex: number): void { + let changed = false; this._pages.update((pages) => pages.map((p) => { if (p.id !== pageId) return p; - const towers = [...p.towers]; - const [item] = towers.splice(fromIndex, 1); - towers.splice(toIndex, 0, item); + const towers = reorder(p.towers, fromIndex, toIndex); + if (towers === null) return p; + changed = true; return { ...p, towers }; }), ); - this.scheduleSave(); + if (changed) this.scheduleSave(); } addBlock( @@ -352,6 +368,7 @@ export class StoreService implements OnDestroy { blockId: string, patch: Partial>, ): void { + let becameDone = false; this._pages.update((pages) => pages.map((p) => p.id === pageId @@ -359,16 +376,21 @@ export class StoreService implements OnDestroy { ...p, towers: p.towers.map((t) => t.id === towerId - ? { - ...t, - blocks: this.patchBlockList(t.blocks, blockId, patch).blocks, - } + ? (() => { + const result = this.patchBlockList(t.blocks, blockId, patch); + becameDone = result.becameDone; + return { ...t, blocks: result.blocks }; + })() : t, ), } : p, ), ); + if (becameDone) { + this.analytics.trackStart(); + this.analytics.trackBlockCompleted(); + } this.scheduleSave(); } @@ -390,35 +412,6 @@ export class StoreService implements OnDestroy { this.scheduleSave(); } - toggleBlock(pageId: string, towerId: string, blockId: string): void { - let becameDone = false; - this._pages.update((pages) => - pages.map((p) => - p.id === pageId - ? { - ...p, - towers: p.towers.map((t) => - t.id === towerId - ? (() => { - const block = t.blocks.find((b) => b.id === blockId); - if (!block) return t; - const result = this.patchBlockList(t.blocks, blockId, { - is_done: !block.is_done, - }); - becameDone = result.becameDone; - return { ...t, blocks: result.blocks }; - })() - : t, - ), - } - : p, - ), - ); - this.analytics.trackStart(); - if (becameDone) this.analytics.trackBlockCompleted(); - this.scheduleSave(); - } - private patchBlockList( blocks: Block[], blockId: string, @@ -458,10 +451,13 @@ export class StoreService implements OnDestroy { * if the timing flipped). */ switchToken(newToken: string): void { - if (!isUuidV4(newToken)) return; + const token = newToken.toLowerCase(); + if (!isUuidV4(token)) return; this.cancelPendingWrites(); - safeSet(TOKEN_KEY, newToken); - this._token.set(newToken); + this.initGeneration++; + this.initPromise = null; + safeSet(TOKEN_KEY, token); + this._token.set(token); this._pages.set([]); this._loading.set(true); this._saveStatus.set('idle'); @@ -522,7 +518,7 @@ export class StoreService implements OnDestroy { try { await this.api.putData(put.token, put.tree); this._saveStatus.set('saved'); - this.updateCache(put.tree); + this.updateCache(put.token, put.tree); return; } catch (err: unknown) { const status = (err as { status?: number })?.status; @@ -566,8 +562,10 @@ export class StoreService implements OnDestroy { } await new Promise((resolve) => { + this.retryResolver = resolve; this.retryTimer = setTimeout(() => { this.retryTimer = null; + this.retryResolver = null; resolve(); }, delayMs); }); @@ -710,3 +708,22 @@ export class StoreService implements OnDestroy { } } } + +function reorder(items: readonly T[], fromIndex: number, toIndex: number): T[] | null { + if ( + fromIndex === toIndex || + !Number.isInteger(fromIndex) || + !Number.isInteger(toIndex) || + fromIndex < 0 || + toIndex < 0 || + fromIndex >= items.length || + toIndex >= items.length + ) { + return null; + } + + const next = [...items]; + const [item] = next.splice(fromIndex, 1); + next.splice(toIndex, 0, item); + return next; +} diff --git a/frontend/src/app/services/store.service.vitest.ts b/frontend/src/app/services/store.service.vitest.ts index 273841a..bd04358 100644 --- a/frontend/src/app/services/store.service.vitest.ts +++ b/frontend/src/app/services/store.service.vitest.ts @@ -3,6 +3,7 @@ import { TestBed } from '@angular/core/testing'; import { provideZonelessChangeDetection } from '@angular/core'; import { StoreService } from './store.service'; import { ApiService } from './api.service'; +import { AnalyticsService } from './analytics.service'; import type { TreeDto } from '../models'; // ── localStorage stub ──────────────────────────────────────────────────────── @@ -63,7 +64,9 @@ function makeMockApi(): MockApi { const FIXED_UUID = '11111111-2222-4333-8444-555555555555'; const TOKEN_KEY = 'life-towers.token.v4'; -const CACHE_KEY = 'life-towers.cache.v4'; +const CACHE_KEY = `life-towers.cache.v4.${FIXED_UUID}`; +const OTHER_TOKEN = 'aaaabbbb-cccc-4ddd-8eee-ffffffffffff'; +const OTHER_CACHE_KEY = `life-towers.cache.v4.${OTHER_TOKEN}`; // ── Helpers ────────────────────────────────────────────────────────────────── function configure(api: MockApi): StoreService { @@ -72,6 +75,18 @@ function configure(api: MockApi): StoreService { providers: [ provideZonelessChangeDetection(), { provide: ApiService, useValue: api }, + { + provide: AnalyticsService, + useValue: { + init: vi.fn(), + trackStart: vi.fn(), + trackExampleLoaded: vi.fn(), + trackPageCreated: vi.fn(), + trackTowerCreated: vi.fn(), + trackBlockCreated: vi.fn(), + trackBlockCompleted: vi.fn(), + }, + }, StoreService, ], }); @@ -137,6 +152,18 @@ describe('StoreService', () => { expect(store.token()).toBe(FIXED_UUID); }); + it('canonicalizes a stored uppercase token', async () => { + storage[TOKEN_KEY] = FIXED_UUID.toUpperCase(); + const api = makeMockApi(); + const store = configure(api); + + await store.init(); + + expect(storage[TOKEN_KEY]).toBe(FIXED_UUID); + expect(api.getData).toHaveBeenCalledWith(FIXED_UUID); + expect(store.token()).toBe(FIXED_UUID); + }); + it('rejects a non-UUIDv4 stored token and mints a fresh one', async () => { storage[TOKEN_KEY] = 'not-a-uuid'; const api = makeMockApi(); @@ -292,13 +319,17 @@ describe('StoreService', () => { expect(page.towers).toHaveLength(3); const doneBlocks = page.towers.flatMap((tower) => tower.blocks.filter((b) => b.is_done)); - expect(doneBlocks.length).toBeGreaterThanOrEqual(90); + const doneSquares = doneBlocks.reduce((sum, block) => sum + block.difficulty, 0); + expect(doneSquares).toBeGreaterThanOrEqual(90); expect(new Set(page.towers.flatMap((tower) => tower.blocks.map((b) => b.difficulty))).size) .toBeGreaterThan(1); for (const tower of page.towers) { const doneDates = tower.blocks.filter((b) => b.is_done).map((b) => b.created_at); - expect(doneDates.length).toBeGreaterThanOrEqual(30); + const doneSquareCount = tower.blocks + .filter((b) => b.is_done) + .reduce((sum, block) => sum + block.difficulty, 0); + expect(doneSquareCount).toBeGreaterThanOrEqual(30); expect(doneDates).toEqual([...doneDates].sort((a, b) => a - b)); expect(new Set(tower.blocks.map((b) => b.difficulty)).size).toBeGreaterThan(1); } @@ -438,15 +469,73 @@ describe('StoreService', () => { // Mutate, then switch BEFORE the debounce fires. store.addPage('old-account'); - const newToken = 'aaaabbbb-cccc-4ddd-8eee-ffffffffffff'; api.getData.mockResolvedValue({ pages: [] }); - store.switchToken(newToken); + store.switchToken(OTHER_TOKEN); // Run all timers — the OLD debounce must have been cancelled, // so no PUT should have happened. await vi.advanceTimersByTimeAsync(2000); expect(api.putData).not.toHaveBeenCalled(); - expect(store.token()).toBe(newToken); + expect(store.token()).toBe(OTHER_TOKEN); + }); + + it('switchToken invalidates an init already in flight', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + let resolveFirstGet: ((v: TreeDto) => void) | null = null; + api.getData + .mockReturnValueOnce(new Promise((res) => (resolveFirstGet = res))) + .mockResolvedValueOnce({ pages: [mkPage('new-account')] }); + const store = configure(api); + + const firstInit = store.init(); + store.switchToken(OTHER_TOKEN); + resolveFirstGet!({ pages: [mkPage('old-account')] }); + await firstInit; + + expect(api.getData).toHaveBeenCalledWith(OTHER_TOKEN); + expect(store.token()).toBe(OTHER_TOKEN); + expect(store.pages()[0].name).toBe('new-account'); + }); + + it('switchToken cancels a pending retry without wedging future saves', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + api.putData + .mockRejectedValueOnce(httpError(429, { 'Retry-After': '30' })) + .mockResolvedValue(undefined); + const store = configure(api); + await store.init(); + + store.addPage('old-account'); + await vi.advanceTimersByTimeAsync(750); + expect(api.putData).toHaveBeenCalledTimes(1); + + api.getData.mockResolvedValue({ pages: [] }); + store.switchToken(OTHER_TOKEN); + await vi.advanceTimersByTimeAsync(0); + + store.addPage('new-account'); + await vi.advanceTimersByTimeAsync(750); + + expect(api.putData).toHaveBeenCalledTimes(2); + expect(api.putData.mock.calls[1][0]).toBe(OTHER_TOKEN); + expect((api.putData.mock.calls[1][1] as TreeDto).pages[0].name).toBe('new-account'); + }); + + it('does not load another account cache after switching tokens', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + storage[CACHE_KEY] = JSON.stringify({ pages: [mkPage('old-cache')] } satisfies TreeDto); + const api = makeMockApi(); + const store = configure(api); + await store.init(); + + api.getData.mockRejectedValue(httpError(0)); + store.switchToken(OTHER_TOKEN); + await vi.advanceTimersByTimeAsync(0); + + expect(storage[OTHER_CACHE_KEY]).toBeUndefined(); + expect(store.pages()).toHaveLength(0); }); it('switchToken rejects a non-UUIDv4 input', () => { @@ -458,6 +547,19 @@ describe('StoreService', () => { expect(store.token()).toBe(''); // never initialized }); + it('switchToken canonicalizes uppercase UUID input', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + const store = configure(api); + await store.init(); + + store.switchToken(OTHER_TOKEN.toUpperCase()); + await vi.advanceTimersByTimeAsync(0); + + expect(store.token()).toBe(OTHER_TOKEN); + expect(storage[TOKEN_KEY]).toBe(OTHER_TOKEN); + }); + // ── Cross-tab sync ──────────────────────────────────────────────────────── it('adopts a fresh cache written by another tab via the storage event', async () => { diff --git a/frontend/src/app/styles/_form-shared.scss b/frontend/src/app/styles/_form-shared.scss deleted file mode 100644 index 54c4044..0000000 --- a/frontend/src/app/styles/_form-shared.scss +++ /dev/null @@ -1,106 +0,0 @@ -.form { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.field { - display: flex; - flex-direction: column; - gap: 0.35rem; - - label { - font-size: 0.85rem; - font-weight: 500; - color: var(--text-muted); - font-family: var(--font-ui); - } - - input[type='text'], - input[type='email'], - textarea { - width: 100%; - padding: 0.5rem 0.75rem; - border-radius: 0.5rem; - border: 1px solid var(--border); - background: var(--bg); - color: var(--text); - font-family: var(--font-ui); - font-size: 0.95rem; - resize: vertical; - - &:focus { - outline: 2px solid var(--accent); - outline-offset: 1px; - } - } - - &--checkbox { - flex-direction: row; - align-items: center; - - label { - display: flex; - align-items: center; - gap: 0.5rem; - cursor: pointer; - color: var(--text); - } - } -} - -.actions { - display: flex; - gap: 0.75rem; - justify-content: flex-end; - padding-top: 0.5rem; -} - -.btn { - padding: 0.5rem 1.25rem; - border-radius: 0.5rem; - border: none; - cursor: pointer; - font-size: 0.95rem; - font-family: var(--font-ui); - transition: background 0.15s; - - &:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } - - &:disabled { - opacity: 0.45; - cursor: not-allowed; - } - - &--primary { - background: var(--accent); - color: #fff; - - &:hover:not(:disabled) { - background: var(--accent-dark); - } - } - - &--secondary { - background: var(--surface-hover); - color: var(--text); - - &:hover:not(:disabled) { - background: var(--border); - } - } - - &--danger { - background: transparent; - color: #e53e3e; - border: 1px solid #e53e3e; - margin-right: auto; - - &:hover:not(:disabled) { - background: rgba(229, 62, 62, 0.1); - } - } -} diff --git a/frontend/src/assets/arrow.svg b/frontend/src/assets/arrow.svg deleted file mode 100644 index bf07063..0000000 --- a/frontend/src/assets/arrow.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/src/assets/fonts/material-icons.woff2 b/frontend/src/assets/fonts/material-icons.woff2 deleted file mode 100644 index 5492a6e75937db19f9ba860cd2575744887fa26b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128352 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

l*K8_>P7#5$l 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

7eh|( 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

7eh|( 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

7eh|( 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 - - - diff --git a/frontend/src/assets/plus-sign.svg b/frontend/src/assets/plus-sign.svg deleted file mode 100644 index 928fbee..0000000 --- a/frontend/src/assets/plus-sign.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/frontend/src/assets/trash.svg b/frontend/src/assets/trash.svg deleted file mode 100644 index 73e0a95..0000000 --- a/frontend/src/assets/trash.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/frontend/src/assets/x-sign.svg b/frontend/src/assets/x-sign.svg deleted file mode 100644 index 6f353d8..0000000 --- a/frontend/src/assets/x-sign.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/src/index.html b/frontend/src/index.html index 032f734..147335a 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -4,7 +4,7 @@ Life Towers - + diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index e93bdc4..7b4c607 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -15,38 +15,6 @@ src: url('assets/fonts/raleway-400.woff2') format('woff2'); } -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url('assets/fonts/roboto-300.woff2') format('woff2'); -} - -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('assets/fonts/roboto-400.woff2') format('woff2'); -} - -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('assets/fonts/roboto-500.woff2') format('woff2'); -} - -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - font-display: block; - src: url('assets/fonts/material-icons.woff2') format('woff2'); -} - @import 'library/main'; $line-height: 2px; @@ -56,7 +24,7 @@ $line-height: 2px; padding: 0; &:active, - &:focus { + &:focus:not(:focus-visible) { outline: 0; } From 5bf8e752e75d15451628d609e1d8e7864ce83dee Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 31 May 2026 09:39:34 +0100 Subject: [PATCH 08/26] Reveiw --- backend/src/life_towers/auth.py | 29 ++++++++++--------- backend/src/life_towers/limits.py | 9 ++---- backend/src/life_towers/logging.py | 10 +++---- backend/src/life_towers/main.py | 4 +-- .../life_towers/migrations/001_initial.sql | 6 ++-- backend/tests/test_api.py | 11 ------- frontend/angular.json | 1 - .../components/modal/block-edit.component.ts | 25 +++++++++------- .../components/modal/settings.component.ts | 10 ++++--- .../modal/tower-settings.component.ts | 8 +++-- .../src/app/components/page/page.component.ts | 11 ++++--- .../app/components/pages/pages.component.scss | 6 ---- .../app/components/pages/pages.component.ts | 8 +++-- .../color-picker/color-picker.component.ts | 5 ++-- .../double-slider/double-slider.component.ts | 4 +-- .../app/components/tower/tower.component.ts | 9 +++--- .../components/welcome/welcome.component.ts | 12 ++++---- .../src/app/services/api.service.vitest.ts | 17 ----------- frontend/src/app/utils/hash.ts | 2 +- frontend/tsconfig.app.json | 2 +- frontend/tsconfig.spec.json | 2 +- frontend/vitest.config.ts | 2 +- 22 files changed, 81 insertions(+), 112 deletions(-) diff --git a/backend/src/life_towers/auth.py b/backend/src/life_towers/auth.py index 8f7e01f..45f396a 100644 --- a/backend/src/life_towers/auth.py +++ b/backend/src/life_towers/auth.py @@ -2,11 +2,10 @@ from __future__ import annotations -import uuid - from fastapi import HTTPException, Request from .db import db_connection +from .models import _canonical_uuidv4 # Single generic detail used for ALL 401 responses. Per spec, the response # must not distinguish between missing / malformed / unknown tokens — that @@ -21,26 +20,28 @@ def _unauthorized() -> HTTPException: ) -def get_current_user(request: Request) -> str: - """Dependency that extracts and validates a Bearer token, returns user_id.""" +def extract_bearer_token(request: Request) -> str | None: + """Return the raw Bearer token from the Authorization header, or None.""" auth_header = request.headers.get("Authorization") or request.headers.get( "authorization" ) if not auth_header: - raise _unauthorized() - + return None parts = auth_header.split() - if len(parts) != 2 or parts[0].lower() != "bearer": - raise _unauthorized() + if len(parts) == 2 and parts[0].lower() == "bearer": + return parts[1] + return None - token = parts[1] + +def get_current_user(request: Request) -> str: + """Dependency that extracts and validates a Bearer token, returns user_id.""" + token = extract_bearer_token(request) + if token is None: + raise _unauthorized() try: - u = uuid.UUID(token) - if u.version != 4: - raise ValueError("Not v4") - token = str(u) - except (ValueError, AttributeError): + token = _canonical_uuidv4(token) + except ValueError: raise _unauthorized() with db_connection() as conn: diff --git a/backend/src/life_towers/limits.py b/backend/src/life_towers/limits.py index b566d84..bc6f90d 100644 --- a/backend/src/life_towers/limits.py +++ b/backend/src/life_towers/limits.py @@ -8,6 +8,8 @@ from fastapi import Request, Response from slowapi import Limiter from slowapi.util import get_remote_address +from .auth import extract_bearer_token + PAYLOAD_LIMIT_BYTES = 2 * 1024 * 1024 # 2 MiB _TOO_LARGE_BODY = json.dumps( @@ -20,12 +22,7 @@ _TOO_LARGE_BODY = json.dumps( def _get_token_or_ip(request: Request) -> str: """Key function for rate limiting: use Bearer token if present, else IP.""" - auth = request.headers.get("Authorization") or request.headers.get("authorization") - if auth: - parts = auth.split() - if len(parts) == 2 and parts[0].lower() == "bearer": - return parts[1] - return get_remote_address(request) + return extract_bearer_token(request) or get_remote_address(request) limiter = Limiter(key_func=_get_token_or_ip, default_limits=[]) diff --git a/backend/src/life_towers/logging.py b/backend/src/life_towers/logging.py index 1cb9288..539b01b 100644 --- a/backend/src/life_towers/logging.py +++ b/backend/src/life_towers/logging.py @@ -9,6 +9,8 @@ from hashlib import sha256 import structlog from fastapi import Request, Response +from .auth import extract_bearer_token + def configure_logging() -> None: """Configure structlog for JSON output.""" @@ -38,12 +40,8 @@ async def request_logging_middleware(request: Request, call_next) -> Response: structlog.contextvars.bind_contextvars(request_id=request_id) # Extract user_id from Authorization header for logging (no DB call here) - auth = request.headers.get("Authorization") or request.headers.get("authorization") - user_id: str | None = None - if auth: - parts = auth.split() - if len(parts) == 2 and parts[0].lower() == "bearer": - user_id = token_log_id(parts[1]) + token = extract_bearer_token(request) + user_id = token_log_id(token) if token else None start = time.monotonic() response = await call_next(request) diff --git a/backend/src/life_towers/main.py b/backend/src/life_towers/main.py index 0e2a29a..b9be419 100644 --- a/backend/src/life_towers/main.py +++ b/backend/src/life_towers/main.py @@ -98,7 +98,7 @@ def create_app() -> FastAPI: if field ) if fields: - detail_str = "Validation failed for: " + ", ".join(f for f in fields if f) + detail_str = "Validation failed for: " + ", ".join(fields) else: detail_str = "Validation failed" return JSONResponse( @@ -116,7 +116,7 @@ def create_app() -> FastAPI: code = STATUS_CODE_MAP.get(exc.status_code, "server_error") detail = {"error": code, "detail": str(exc.detail)} - headers = getattr(exc, "headers", None) or {} + headers = exc.headers or {} return JSONResponse(status_code=exc.status_code, content=detail, headers=headers) # Generic 500 handler diff --git a/backend/src/life_towers/migrations/001_initial.sql b/backend/src/life_towers/migrations/001_initial.sql index b8d3761..372c02f 100644 --- a/backend/src/life_towers/migrations/001_initial.sql +++ b/backend/src/life_towers/migrations/001_initial.sql @@ -1,10 +1,8 @@ -- Life Towers v4 initial schema. --- SQLite with WAL mode and foreign keys enabled at connection time. +-- WAL mode, foreign keys, and busy_timeout are applied per-connection in +-- db._apply_pragmas(), so they are not (and need not be) set here. -- All timestamps are unix epoch seconds (INTEGER). -PRAGMA journal_mode = WAL; -PRAGMA foreign_keys = ON; - CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 404fa09..a4f8538 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -51,17 +51,6 @@ async def client(tmp_path: Path) -> AsyncGenerator[AsyncClient, None]: db_module._DB_PATH = None -# --------------------------------------------------------------------------- -# Health -# --------------------------------------------------------------------------- - -@pytest.mark.asyncio -async def test_health(client: AsyncClient) -> None: - resp = await client.get("/api/v1/health") - assert resp.status_code == 200 - assert resp.json() == {"status": "ok"} - - @pytest.mark.asyncio async def test_spa_index_injects_absolute_open_graph_urls( tmp_path: Path, monkeypatch: pytest.MonkeyPatch diff --git a/frontend/angular.json b/frontend/angular.json index 2639328..e8f741a 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -8,7 +8,6 @@ ], "analytics": false }, - "newProjectRoot": "projects", "projects": { "frontend": { "projectType": "application", diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index 64e73e1..51cb5eb 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -4,7 +4,6 @@ import { input, output, signal, - computed, effect, viewChild, ElementRef, @@ -614,10 +613,12 @@ export class BlockEditComponent implements AfterViewInit { const t = this.tags(); untracked(() => { const cur = this.newValue(); - if (!cur.tag && t.length > 0) { - this.newValue.set({ ...cur, tag: t[0], is_done: this.defaultDone() }); - } else if (!cur.tag) { - this.newValue.set({ ...cur, is_done: this.defaultDone() }); + if (!cur.tag) { + this.newValue.set({ + ...cur, + tag: t.length > 0 ? t[0] : '', + is_done: this.defaultDone(), + }); } }); }); @@ -648,19 +649,21 @@ export class BlockEditComponent implements AfterViewInit { ); } + private colorOfTag(tag: string): string { + return tag ? getColorOfTag(tag, this.baseColor()) : 'transparent'; + } + colorOfTagForBlock(id: string): string { - const v = this.editedFor(id); - return v.tag ? getColorOfTag(v.tag, this.baseColor()) : 'transparent'; + return this.colorOfTag(this.editedFor(id).tag); } tagPlaceholder(fallback: string): string { return this.tags().length === 0 ? 'No tags yet. Open to create one.' : fallback; } - colorOfNewTag = computed(() => { - const t = this.newValue().tag; - return t ? getColorOfTag(t, this.baseColor()) : 'transparent'; - }); + colorOfNewTag(): string { + return this.colorOfTag(this.newValue().tag); + } formatDate(ts: number, compact = false): string { const d = new Date(ts * 1000); diff --git a/frontend/src/app/components/modal/settings.component.ts b/frontend/src/app/components/modal/settings.component.ts index 289e5e9..9d044ce 100644 --- a/frontend/src/app/components/modal/settings.component.ts +++ b/frontend/src/app/components/modal/settings.component.ts @@ -16,9 +16,6 @@ export interface UpdatePagePayload { keep_tasks_open: boolean; } -const UUIDV4_RE = - /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; - @Component({ selector: 'lt-settings', standalone: true, @@ -255,10 +252,15 @@ export class SettingsComponent { readonly hideCreateTowerButton = signal(false); readonly keepTasksOpen = signal(false); + private static readonly UUIDV4_RE = + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + // Token-switch state readonly tokenInput = signal(''); readonly tokenInputTouched = signal(false); - readonly isValidToken = computed(() => UUIDV4_RE.test(this.tokenInput())); + readonly isValidToken = computed(() => + SettingsComponent.UUIDV4_RE.test(this.tokenInput()), + ); constructor() { effect(() => { diff --git a/frontend/src/app/components/modal/tower-settings.component.ts b/frontend/src/app/components/modal/tower-settings.component.ts index 71389f6..7443e40 100644 --- a/frontend/src/app/components/modal/tower-settings.component.ts +++ b/frontend/src/app/components/modal/tower-settings.component.ts @@ -138,12 +138,14 @@ export class TowerSettingsComponent implements OnInit { onSubmit(): void { // Only the create flow reaches here via its Submit button; edit mode // auto-saves (and Enter on the single field is a harmless redundant save). - if (this.form.invalid) return; - this.emitSave(); + this.tryEmitSave(); } - /** Emit a save only when the form is valid (skips e.g. an empty name). */ private autoSave(): void { + this.tryEmitSave(); + } + + private tryEmitSave(): void { if (this.form.invalid) return; this.emitSave(); } diff --git a/frontend/src/app/components/page/page.component.ts b/frontend/src/app/components/page/page.component.ts index 5b0d990..544ed13 100644 --- a/frontend/src/app/components/page/page.component.ts +++ b/frontend/src/app/components/page/page.component.ts @@ -214,13 +214,16 @@ export class PageComponent { onTrashEnter(): void { this.nearTrashcan = true; - const preview = document.querySelector('.cdk-drag-preview'); - if (preview) preview.classList.add('trash-highlight'); + this.dragPreview()?.classList.add('trash-highlight'); } onTrashLeave(): void { this.nearTrashcan = false; - const preview = document.querySelector('.cdk-drag-preview'); - if (preview) preview.classList.remove('trash-highlight'); + this.dragPreview()?.classList.remove('trash-highlight'); + } + + /** The CDK drag preview currently in flight, if any. Matches legacy DOM-driven trash highlight. */ + private dragPreview(): Element | null { + return document.querySelector('.cdk-drag-preview'); } } diff --git a/frontend/src/app/components/pages/pages.component.scss b/frontend/src/app/components/pages/pages.component.scss index 13f10bb..cdb7480 100644 --- a/frontend/src/app/components/pages/pages.component.scss +++ b/frontend/src/app/components/pages/pages.component.scss @@ -71,12 +71,6 @@ } } - p { - strong { - font-weight: bold; - } - } - .confirm-buttons { display: flex; justify-content: center; diff --git a/frontend/src/app/components/pages/pages.component.ts b/frontend/src/app/components/pages/pages.component.ts index cbce254..46e01b7 100644 --- a/frontend/src/app/components/pages/pages.component.ts +++ b/frontend/src/app/components/pages/pages.component.ts @@ -38,9 +38,10 @@ export class PagesComponent implements OnDestroy { constructor() { effect(() => { - if (!this.store.loading() && this.store.pages().length === 0) { + const pages = this.store.pages(); + if (!this.store.loading() && pages.length === 0) { this.showWelcome.set(true); - } else if (this.store.pages().length > 0) { + } else if (pages.length > 0) { this.showWelcome.set(false); } }); @@ -89,9 +90,10 @@ export class PagesComponent implements OnDestroy { }); readonly selectedPageIndex = computed(() => { + const pages = this.store.pages(); const page = this.selectedPage(); if (!page) return -1; - return this.store.pages().findIndex((p) => p.id === page.id); + return pages.findIndex((p) => p.id === page.id); }); onSelectPage(index: number): void { diff --git a/frontend/src/app/components/shared/color-picker/color-picker.component.ts b/frontend/src/app/components/shared/color-picker/color-picker.component.ts index 29292c3..e12ee89 100644 --- a/frontend/src/app/components/shared/color-picker/color-picker.component.ts +++ b/frontend/src/app/components/shared/color-picker/color-picker.component.ts @@ -185,9 +185,8 @@ export class ColorPickerComponent { return `hsl(${h}, 70%, 55%)`; } - toCss(c: HslColor): string { - return toCss(c); - } + /** Re-exported so the template can call the utility directly. */ + readonly toCss = toCss; pickHue(h: number): void { this.colorChange.emit({ h: h / 360, s: FIXED_S, l: FIXED_L }); diff --git a/frontend/src/app/components/shared/double-slider/double-slider.component.ts b/frontend/src/app/components/shared/double-slider/double-slider.component.ts index 892e935..f4db1e4 100644 --- a/frontend/src/app/components/shared/double-slider/double-slider.component.ts +++ b/frontend/src/app/components/shared/double-slider/double-slider.component.ts @@ -18,7 +18,7 @@ export interface DoubleSliderRange { * Two-thumb range slider — legacy "double-slider". * Hands an indexed range over an arbitrary values array; emits the * underlying values on each change. Labels magnetically lift as a thumb - * approaches them (rotated -45°), per the legacy. + * approaches them (rotated -30°), per the legacy. */ @Component({ selector: 'lt-double-slider', @@ -228,7 +228,7 @@ export class DoubleSliderComponent { /** * Magnetic label position: returns a CSS `transform` that lifts the label - * upward and rotates -45° as a thumb approaches. + * upward and rotates -30° as a thumb approaches. */ getOffset(index: number): string { const labelIndex = index / Math.max(1, this.drawnLabels().length - 1); diff --git a/frontend/src/app/components/tower/tower.component.ts b/frontend/src/app/components/tower/tower.component.ts index a6422c9..72d59d1 100644 --- a/frontend/src/app/components/tower/tower.component.ts +++ b/frontend/src/app/components/tower/tower.component.ts @@ -695,6 +695,8 @@ export class TowerComponent implements AfterViewInit, OnDestroy { if (this.isFirstRun && animateInitialStack && maxVisibleBlocks === null) { this._visibleBlocks.set([]); this.hiddenBlockCount.set(0); + this.prevDoneIds = allDone.map((b) => b.id); + this.isFirstRun = false; return; } @@ -708,9 +710,6 @@ export class TowerComponent implements AfterViewInit, OnDestroy { newIds.length === 1 && prev.every((id) => ids.includes(id)); // no IDs disappeared - const inRange = (b: Block) => - !range || (b.created_at >= range.from && b.created_at <= range.to); - const styled: StyledBlock[] = []; for (const b of allDone) { if (range && b.created_at < range.from) { @@ -755,7 +754,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { this.hiddenBlockCount.set(hiddenCount); if (this.isFirstRun && animateInitialStack) { - const initialBlocks = visibleStyled.filter((b) => b._opacity === '1' && inRange(b)); + const initialBlocks = visibleStyled.filter((b) => b._opacity === '1'); if (initialBlocks.length > 0) { this.startDescendAnimation(visibleStyled, initialBlocks); this.prevDoneIds = ids; @@ -767,7 +766,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { if (grewByOne) { const newId = newIds[0]; const newBlock = visibleStyled.find((b) => b.id === newId); - if (newBlock && inRange(newBlock)) { + if (newBlock) { // Snap newly-added in-range block to start position, then on the next // paint flip it back to rest — that's what makes it visibly fall. this.startDescendAnimation(visibleStyled, [newBlock]); diff --git a/frontend/src/app/components/welcome/welcome.component.ts b/frontend/src/app/components/welcome/welcome.component.ts index eefa8de..b571af2 100644 --- a/frontend/src/app/components/welcome/welcome.component.ts +++ b/frontend/src/app/components/welcome/welcome.component.ts @@ -333,20 +333,20 @@ import { A11yModule } from '@angular/cdk/a11y'; min-width: 0; } - .basic__label { + .basic__label, + .basic__text { font-family: inherit; font-weight: inherit; - color: $text-color; font-size: inherit; line-height: inherit; } + .basic__label { + color: $text-color; + } + .basic__text { - font-family: inherit; - font-weight: inherit; color: rgba($text-color, 0.82); - font-size: inherit; - line-height: inherit; } .actions { diff --git a/frontend/src/app/services/api.service.vitest.ts b/frontend/src/app/services/api.service.vitest.ts index e9cf798..4b9b4e3 100644 --- a/frontend/src/app/services/api.service.vitest.ts +++ b/frontend/src/app/services/api.service.vitest.ts @@ -21,23 +21,6 @@ describe('ApiService', () => { http.verify(); }); - it('gets health', async () => { - const promise = service.health(); - const req = http.expectOne('/api/v1/health'); - expect(req.request.method).toBe('GET'); - req.flush({ status: 'ok' }); - await expect(promise).resolves.toEqual({ status: 'ok' }); - }); - - it('registers a token', async () => { - const promise = service.register('token-1'); - const req = http.expectOne('/api/v1/register'); - expect(req.request.method).toBe('POST'); - expect(req.request.body).toEqual({ token: 'token-1' }); - req.flush({ user_id: 'token-1' }); - await expect(promise).resolves.toEqual({ user_id: 'token-1' }); - }); - it('gets data with a bearer token', async () => { const tree: TreeDto = { pages: [] }; const promise = service.getData('token-1'); diff --git a/frontend/src/app/utils/hash.ts b/frontend/src/app/utils/hash.ts index fffe090..d95c6da 100644 --- a/frontend/src/app/utils/hash.ts +++ b/frontend/src/app/utils/hash.ts @@ -16,5 +16,5 @@ export function hash(s: string): number { h = ((h << 5) - h + s.charCodeAt(i)) | 0; } // Map the signed int32 to [0, 1) — same formula as legacy - return h / (Math.pow(2, 32) - 2) + 0.5; + return h / (2 ** 32 - 2) + 0.5; } diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index 264f459..bec29af 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -10,6 +10,6 @@ "src/**/*.ts" ], "exclude": [ - "src/**/*.spec.ts" + "src/**/*.vitest.ts" ] } diff --git a/frontend/tsconfig.spec.json b/frontend/tsconfig.spec.json index d383706..6883a29 100644 --- a/frontend/tsconfig.spec.json +++ b/frontend/tsconfig.spec.json @@ -10,6 +10,6 @@ }, "include": [ "src/**/*.d.ts", - "src/**/*.spec.ts" + "src/**/*.vitest.ts" ] } diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index af0b622..8dad81b 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ test: { globals: true, environment: 'jsdom', - include: ['src/**/*.vitest.ts', 'src/**/*.spec.vitest.ts'], + include: ['src/**/*.vitest.ts'], setupFiles: ['./vitest.setup.ts'], coverage: { provider: 'v8', From 0ba28b0a14c1aae4bbef32aba6fac08cb50d5a69 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 31 May 2026 10:38:56 +0100 Subject: [PATCH 09/26] Add task toggle --- frontend/e2e/smoke.spec.ts | 105 +++++++++++++++++- .../components/modal/block-edit.component.ts | 17 ++- .../modal/block-edit.component.vitest.ts | 14 +++ .../app/components/tasks/tasks.component.ts | 80 ++++++++----- .../tasks/tasks.component.vitest.ts | 26 +++++ .../app/components/tower/tower.component.ts | 11 +- .../tower/tower.component.vitest.ts | 12 +- frontend/src/app/services/store.service.ts | 87 +++++++++++---- .../src/app/services/store.service.vitest.ts | 93 ++++++++++++++++ 9 files changed, 387 insertions(+), 58 deletions(-) create mode 100644 frontend/src/app/components/modal/block-edit.component.vitest.ts create mode 100644 frontend/src/app/components/tasks/tasks.component.vitest.ts diff --git a/frontend/e2e/smoke.spec.ts b/frontend/e2e/smoke.spec.ts index f9bb312..2493014 100644 --- a/frontend/e2e/smoke.spec.ts +++ b/frontend/e2e/smoke.spec.ts @@ -1,4 +1,21 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, type Page } from '@playwright/test'; + +async function expectTaskListOpen(page: Page, description: string): Promise { + const tasks = page.locator('lt-tasks', { hasText: description }).first(); + const taskBody = tasks.locator('.all-task'); + const taskRow = tasks.locator('.task-container', { hasText: description }).first(); + + await expect(tasks.locator('.header')).toHaveCount(0); + await expect + .poll(async () => + taskBody.evaluate((el) => { + const height = el.getBoundingClientRect().height; + return height / Math.max(1, el.scrollHeight); + }), + ) + .toBeGreaterThan(0.9); + await taskRow.locator('.tickbox').click({ trial: true }); +} /** * Smoke test: drives the legacy-styled UI end-to-end. @@ -77,4 +94,90 @@ test.describe('Life Towers smoke test', () => { await expect(page.locator('lt-tower input').first()).toHaveValue('Side projects'); await expect(page.locator('lt-tower lt-block').first()).toBeVisible(); }); + + test('keep tasks open shows new pending tasks and survives immediate reload', async ({ page }) => { + await page.goto('/'); + + await expect(page.getByText('Welcome to Life Towers')).toBeVisible({ timeout: 15000 }); + await page.getByRole('button', { name: 'Start empty' }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.locator('lt-select-add .top').first().click(); + await page.locator('lt-select-add input[placeholder="Add a value…"]').fill('Work'); + await page.locator('lt-select-add input[placeholder="Add a value…"]').press('Enter'); + + await page.locator('img[alt="Add tower"]').click(); + await page.locator('input[placeholder="New tower"]').fill('Today'); + await page.locator('lt-tower-settings button[type="submit"]').click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.getByRole('button', { name: 'Settings' }).click(); + await page.getByText('Keep tasks open').click(); + await page.locator('lt-settings .exit').click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.locator('img[alt="Add block"]').first().click(); + const createCard = page.locator('lt-block-edit .create-card'); + await expect(createCard.getByLabel('Already done')).not.toBeChecked(); + await createCard.locator('lt-select-add .top').click(); + await createCard.locator('lt-select-add input[placeholder="Add a value…"]').fill('ops'); + await createCard.locator('lt-select-add input[placeholder="Add a value…"]').press('Enter'); + await createCard + .locator('textarea[placeholder="Write a description here…"]') + .fill('Review deploy notes'); + await page.getByRole('button', { name: 'Create and exit', exact: true }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await expectTaskListOpen(page, 'Review deploy notes'); + + await page.reload(); + + await expect(page.locator('lt-select-add .top').first()).toContainText('Work', { + timeout: 15000, + }); + await expectTaskListOpen(page, 'Review deploy notes'); + + await page.locator('img[alt="Add block"]').first().click(); + await expect(page.locator('lt-block-edit .create-card').getByLabel('Already done')).not.toBeChecked(); + }); + + test('keep tasks open expands existing pending tasks after reload', async ({ page }) => { + await page.goto('/'); + + await expect(page.getByText('Welcome to Life Towers')).toBeVisible({ timeout: 15000 }); + await page.getByRole('button', { name: 'Start empty' }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.locator('lt-select-add .top').first().click(); + await page.locator('lt-select-add input[placeholder="Add a value…"]').fill('Ops'); + await page.locator('lt-select-add input[placeholder="Add a value…"]').press('Enter'); + + await page.locator('img[alt="Add tower"]').click(); + await page.locator('input[placeholder="New tower"]').fill('Queue'); + await page.locator('lt-tower-settings button[type="submit"]').click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.locator('img[alt="Add block"]').first().click(); + await page.locator('lt-block-edit lt-select-add .top').click(); + await page.locator('lt-block-edit lt-select-add input[placeholder="Add a value…"]').fill('triage'); + await page.locator('lt-block-edit lt-select-add input[placeholder="Add a value…"]').press('Enter'); + await page + .locator('textarea[placeholder="Write a description here…"]') + .fill('Clean up alerts'); + await page.getByLabel('Already done').uncheck(); + await page.getByRole('button', { name: 'Create and exit', exact: true }).click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.getByRole('button', { name: 'Settings' }).click(); + await page.getByText('Keep tasks open').click(); + await page.locator('lt-settings .exit').click(); + await page.waitForSelector('section.modal', { state: 'detached' }); + + await page.reload(); + + await expect(page.locator('lt-select-add .top').first()).toContainText('Ops', { + timeout: 15000, + }); + await expectTaskListOpen(page, 'Clean up alerts'); + }); }); diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index 51cb5eb..58cad90 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -35,6 +35,10 @@ function clampDifficulty(value: number): number { return Math.max(1, Math.min(100, value)); } +export function createDoneValue(defaultDone: boolean, currentDone: boolean, edited: boolean): boolean { + return edited ? currentDone : defaultDone; +} + @Component({ selector: 'lt-block-edit', standalone: true, @@ -586,6 +590,7 @@ export class BlockEditComponent implements AfterViewInit { is_done: true, difficulty: 1, }); + private newDoneEdited = false; // 1-based index of the centered card. 0/N+2 are placeholders. readonly activeIdx = signal(1); @@ -617,11 +622,20 @@ export class BlockEditComponent implements AfterViewInit { this.newValue.set({ ...cur, tag: t.length > 0 ? t[0] : '', - is_done: this.defaultDone(), }); } }); }); + + effect(() => { + const isDone = this.defaultDone(); + untracked(() => { + this.newValue.update((v) => ({ + ...v, + is_done: createDoneValue(isDone, v.is_done, this.newDoneEdited), + })); + }); + }); } ngAfterViewInit(): void { @@ -745,6 +759,7 @@ export class BlockEditComponent implements AfterViewInit { } updateNewDone(is_done: boolean): void { + this.newDoneEdited = true; this.newValue.update((v) => ({ ...v, is_done })); } diff --git a/frontend/src/app/components/modal/block-edit.component.vitest.ts b/frontend/src/app/components/modal/block-edit.component.vitest.ts new file mode 100644 index 0000000..b69ca31 --- /dev/null +++ b/frontend/src/app/components/modal/block-edit.component.vitest.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from 'vitest'; +import { createDoneValue } from './block-edit.component'; + +describe('createDoneValue', () => { + it('uses the create-card default before the user edits the checkbox', () => { + expect(createDoneValue(false, true, false)).toBe(false); + expect(createDoneValue(true, false, false)).toBe(true); + }); + + it('keeps the user-edited checkbox value', () => { + expect(createDoneValue(false, true, true)).toBe(true); + expect(createDoneValue(true, false, true)).toBe(false); + }); +}); diff --git a/frontend/src/app/components/tasks/tasks.component.ts b/frontend/src/app/components/tasks/tasks.component.ts index f348a17..183da91 100644 --- a/frontend/src/app/components/tasks/tasks.component.ts +++ b/frontend/src/app/components/tasks/tasks.component.ts @@ -1,10 +1,28 @@ -import { Component, ChangeDetectionStrategy, input, output, signal, effect, untracked } from '@angular/core'; +import { + Component, + ChangeDetectionStrategy, + computed, + effect, + input, + output, + signal, + untracked, +} from '@angular/core'; import { Block, HslColor } from '../../models'; import { getColorOfTag } from '../../utils/color'; +export function shouldExpandTasks(keepTasksOpen: boolean, manuallyExpanded: boolean): boolean { + return keepTasksOpen || manuallyExpanded; +} + +export function taskListMaxHeight(expanded: boolean): string { + return expanded ? 'none' : '0px'; +} + /** * Tasks accordion — shows pending (not-done) blocks inside a tower. - * Sits ABOVE the falling-blocks area. Clicking the header expands/collapses. + * Sits ABOVE the falling-blocks area. Clicking the header expands/collapses + * unless the page setting is keeping tasks open. * Clicking the colored tickbox marks the task done. * Clicking the description opens the block-edit modal via the `edit` output. */ @@ -21,23 +39,25 @@ import { getColorOfTag } from '../../utils/color'; (mousedown)="$event.stopPropagation()" (touchstart)="$event.stopPropagation()" > - + @if (!initiallyOpen()) { + + }

@for (b of pending(); track b.id) {
@@ -111,12 +131,9 @@ import { getColorOfTag } from '../../utils/color'; transition: max-height $long-animation-time; /* - * Clip during the open/close animation. We animate to the element's - * exact content height (read off #all in the template) so tall lists - * simply scroll inside the outer .container (max-height: 30vh) — - * .all-task itself is NOT a nested scroller. A nested overflow-y:auto - * here pops a scrollbar the instant a tickbox grows on hover, because - * its scale(1.05) + shadow widen the scrollable-overflow box. + * Clip while collapsed only. When open, let the outer .container own + * scrolling via max-height: 30vh; a nested scroller here pops a + * scrollbar the instant a tickbox grows on hover. */ overflow: hidden; @@ -235,16 +252,18 @@ export class TasksComponent { /** Emitted when the description is clicked — parent opens the block-edit modal. */ readonly edit = output(); - readonly expanded = signal(false); + private readonly manuallyExpanded = signal(false); + readonly expanded = computed(() => + shouldExpandTasks(this.initiallyOpen(), this.manuallyExpanded()), + ); + readonly taskListMaxHeight = taskListMaxHeight; constructor() { - // Re-sync `expanded` whenever the `initiallyOpen` input changes so flipping - // the "Keep tasks open" page setting expands/collapses the accordion live. - // User clicks (which mutate `expanded` directly) are respected until the - // setting changes again. + // When the page setting switches back to collapsed, discard any older manual + // open state so the setting is reflected immediately. effect(() => { - const open = this.initiallyOpen(); - untracked(() => this.expanded.set(open)); + const keepOpen = this.initiallyOpen(); + if (!keepOpen) untracked(() => this.manuallyExpanded.set(false)); }); } @@ -254,6 +273,7 @@ export class TasksComponent { toggleExpanded(event: Event): void { event.stopPropagation(); - this.expanded.update((v) => !v); + if (this.initiallyOpen()) return; + this.manuallyExpanded.update((v) => !v); } } diff --git a/frontend/src/app/components/tasks/tasks.component.vitest.ts b/frontend/src/app/components/tasks/tasks.component.vitest.ts new file mode 100644 index 0000000..fe12ec6 --- /dev/null +++ b/frontend/src/app/components/tasks/tasks.component.vitest.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest'; +import { shouldExpandTasks, taskListMaxHeight } from './tasks.component'; + +describe('shouldExpandTasks', () => { + it('expands when tasks should be kept open by page setting', () => { + expect(shouldExpandTasks(true, false)).toBe(true); + }); + + it('expands when the user manually opens the accordion', () => { + expect(shouldExpandTasks(false, true)).toBe(true); + }); + + it('collapses when keep-open is disabled', () => { + expect(shouldExpandTasks(false, false)).toBe(false); + }); +}); + +describe('taskListMaxHeight', () => { + it('does not cap open task lists by a measured height', () => { + expect(taskListMaxHeight(true)).toBe('none'); + }); + + it('clips collapsed task lists', () => { + expect(taskListMaxHeight(false)).toBe('0px'); + }); +}); diff --git a/frontend/src/app/components/tower/tower.component.ts b/frontend/src/app/components/tower/tower.component.ts index 72d59d1..4dc87d0 100644 --- a/frontend/src/app/components/tower/tower.component.ts +++ b/frontend/src/app/components/tower/tower.component.ts @@ -21,11 +21,18 @@ import { TowerSettingsComponent, TowerSettingsResult } from '../modal/tower-sett import { toCss } from '../../utils/color'; /** Tracks which entry path the block-edit modal was opened from. */ -interface EditEntry { +export interface EditEntry { filter: 'done' | 'pending'; activeId: string | null; } +export function editEntryForNewBlock(keepTasksOpen: boolean): EditEntry { + return { + filter: keepTasksOpen ? 'pending' : 'done', + activeId: null, + }; +} + /** A done block augmented with per-render animation state. */ export interface StyledBlock extends Block { _anim: '' | 'descend' | 'ascend'; @@ -875,7 +882,7 @@ export class TowerComponent implements AfterViewInit, OnDestroy { /** Called by the template "Add block" plus-icon. */ openAddBlock(): void { - this.editEntry.set({ filter: 'done', activeId: null }); + this.editEntry.set(editEntryForNewBlock(this.keepTasksOpen())); } closeEdit(): void { diff --git a/frontend/src/app/components/tower/tower.component.vitest.ts b/frontend/src/app/components/tower/tower.component.vitest.ts index c54cc83..495f141 100644 --- a/frontend/src/app/components/tower/tower.component.vitest.ts +++ b/frontend/src/app/components/tower/tower.component.vitest.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import type { StyledBlock } from './tower.component'; -import { selectVisibleStyledBlocks } from './tower.component'; +import { editEntryForNewBlock, selectVisibleStyledBlocks } from './tower.component'; function block(id: string, opacity = '1', difficulty = 1): StyledBlock { return { @@ -127,3 +127,13 @@ describe('selectVisibleStyledBlocks', () => { expect(result.visibleStyled.map((b) => b.id)).toEqual(['newly-done', 'done-4']); }); }); + +describe('editEntryForNewBlock', () => { + it('opens the create card in the pending task view when tasks are kept open', () => { + expect(editEntryForNewBlock(true)).toEqual({ filter: 'pending', activeId: null }); + }); + + it('keeps the existing completed-task default when tasks are collapsed', () => { + expect(editEntryForNewBlock(false)).toEqual({ filter: 'done', activeId: null }); + }); +}); diff --git a/frontend/src/app/services/store.service.ts b/frontend/src/app/services/store.service.ts index 280b049..71589bc 100644 --- a/frontend/src/app/services/store.service.ts +++ b/frontend/src/app/services/store.service.ts @@ -5,6 +5,7 @@ import { Page, Tower, Block, TreeDto, SaveStatus, HslColor } from '../models'; const TOKEN_KEY = 'life-towers.token.v4'; const CACHE_KEY_PREFIX = 'life-towers.cache.v4'; +const PENDING_CACHE_KEY_PREFIX = 'life-towers.cache-pending.v4'; const DEBOUNCE_MS = 750; const MAX_RETRIES = 5; @@ -50,14 +51,26 @@ function safeSet(key: string, value: string): void { /* ignore */ } } +function safeRemove(key: string): void { + try { + localStorage.removeItem(key); + } catch { + /* ignore */ + } +} function cacheKeyForToken(token: string): string { return `${CACHE_KEY_PREFIX}.${token}`; } +function pendingCacheKeyForToken(token: string): string { + return `${PENDING_CACHE_KEY_PREFIX}.${token}`; +} + interface PendingPut { token: string; tree: TreeDto; + revision: number; } interface ExampleBlockSeed { @@ -98,6 +111,9 @@ export class StoreService implements OnDestroy { private flushInFlight = false; // True while in-flight if a new mutation arrived; we'll re-flush after. private dirtyDuringFlush = false; + // Monotonic local mutation version. Prevents an older in-flight PUT from + // clearing a newer pending cache entry when it completes. + private localMutationRevision = 0; // ── Cross-tab sync ───────────────────────────────────────────────────────── private readonly storageListener = (e: StorageEvent) => { @@ -193,19 +209,21 @@ export class StoreService implements OnDestroy { * "server forgot me" recovery would silently wipe offline edits. */ private adoptServerTree(tree: TreeDto, token: string): void { + if (safeGet(pendingCacheKeyForToken(token))) { + const cachedTree = this.readCachedTree(token); + if (cachedTree?.pages && cachedTree.pages.length > 0) { + this._pages.set(cachedTree.pages); + this.scheduleSave(); + return; + } + } + if (tree.pages.length === 0) { - const cached = safeGet(cacheKeyForToken(token)); - if (cached) { - try { - const cachedTree: TreeDto = JSON.parse(cached); - if (cachedTree.pages && cachedTree.pages.length > 0) { - this._pages.set(cachedTree.pages); - this.scheduleSave(); - return; - } - } catch { - /* fall through to server-empty */ - } + const cachedTree = this.readCachedTree(token); + if (cachedTree?.pages && cachedTree.pages.length > 0) { + this._pages.set(cachedTree.pages); + this.scheduleSave(); + return; } } this._pages.set(tree.pages); @@ -213,19 +231,27 @@ export class StoreService implements OnDestroy { } private loadFromCache(token: string): void { + const tree = this.readCachedTree(token); + if (tree) this._pages.set(tree.pages); + } + + private readCachedTree(token: string): TreeDto | null { const raw = safeGet(cacheKeyForToken(token)); - if (raw) { - try { - const tree: TreeDto = JSON.parse(raw); - this._pages.set(tree.pages); - } catch { - /* ignore */ - } + if (!raw) return null; + try { + return JSON.parse(raw) as TreeDto; + } catch { + return null; } } - private updateCache(token: string, tree: TreeDto): void { + private updateCache(token: string, tree: TreeDto, pending = false): void { safeSet(cacheKeyForToken(token), JSON.stringify(tree)); + if (pending) { + safeSet(pendingCacheKeyForToken(token), '1'); + } else { + safeRemove(pendingCacheKeyForToken(token)); + } } private cancelPendingWrites(): void { @@ -461,12 +487,17 @@ export class StoreService implements OnDestroy { this._pages.set([]); this._loading.set(true); this._saveStatus.set('idle'); + this.localMutationRevision = 0; void this.init(); } // ── Save / sync ──────────────────────────────────────────────────────────── private scheduleSave(): void { + this.localMutationRevision += 1; + const token = this._token(); + if (token) this.updateCache(token, { pages: this._pages() }, true); + if (this.flushInFlight) { // A save is already happening. Mark dirty so we re-flush when it finishes. this.dirtyDuringFlush = true; @@ -494,6 +525,7 @@ export class StoreService implements OnDestroy { this.flushInFlight = true; this.dirtyDuringFlush = false; + const revision = this.localMutationRevision; // Cancel any pending retry — runFlush() supersedes it. if (this.retryTimer !== null) { @@ -502,7 +534,7 @@ export class StoreService implements OnDestroy { } try { - await this.attempt({ token, tree: { pages: this._pages() } }, 0); + await this.attempt({ token, tree: { pages: this._pages() }, revision }, 0); } finally { this.flushInFlight = false; // Coalesce mutations that arrived during the flush into a fresh save. @@ -518,7 +550,13 @@ export class StoreService implements OnDestroy { try { await this.api.putData(put.token, put.tree); this._saveStatus.set('saved'); - this.updateCache(put.token, put.tree); + if ( + this._token() === put.token && + put.revision === this.localMutationRevision && + !this.dirtyDuringFlush + ) { + this.updateCache(put.token, put.tree); + } return; } catch (err: unknown) { const status = (err as { status?: number })?.status; @@ -573,7 +611,10 @@ export class StoreService implements OnDestroy { // The token may have changed during the wait — re-check before retrying. if (this._token() !== put.token) return; // Re-snapshot the latest pages so the retry pushes current state. - await this.attempt({ token: put.token, tree: { pages: this._pages() } }, attempt + 1); + await this.attempt( + { token: put.token, tree: { pages: this._pages() }, revision: put.revision }, + attempt + 1, + ); } } diff --git a/frontend/src/app/services/store.service.vitest.ts b/frontend/src/app/services/store.service.vitest.ts index bd04358..2ecbe76 100644 --- a/frontend/src/app/services/store.service.vitest.ts +++ b/frontend/src/app/services/store.service.vitest.ts @@ -65,6 +65,7 @@ function makeMockApi(): MockApi { const FIXED_UUID = '11111111-2222-4333-8444-555555555555'; const TOKEN_KEY = 'life-towers.token.v4'; const CACHE_KEY = `life-towers.cache.v4.${FIXED_UUID}`; +const PENDING_CACHE_KEY = `life-towers.cache-pending.v4.${FIXED_UUID}`; const OTHER_TOKEN = 'aaaabbbb-cccc-4ddd-8eee-ffffffffffff'; const OTHER_CACHE_KEY = `life-towers.cache.v4.${OTHER_TOKEN}`; @@ -352,6 +353,7 @@ describe('StoreService', () => { expect(api.putData).not.toHaveBeenCalled(); await vi.advanceTimersByTimeAsync(750); expect(api.putData).toHaveBeenCalledTimes(1); + expect(storage[PENDING_CACHE_KEY]).toBeUndefined(); const [, tree] = api.putData.mock.calls[0]; expect((tree as TreeDto).pages).toHaveLength(3); }); @@ -382,6 +384,97 @@ describe('StoreService', () => { expect(lastTree.pages).toHaveLength(2); }); + it('does not let an older in-flight save clear a newer pending cache entry', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + let resolveFirstPut: (() => void) | null = null; + api.putData + .mockReturnValueOnce(new Promise((res) => (resolveFirstPut = () => res()))) + .mockResolvedValueOnce(undefined); + const store = configure(api); + await store.init(); + + store.addPage('first'); + await vi.advanceTimersByTimeAsync(750); + expect(api.putData).toHaveBeenCalledTimes(1); + + store.addPage('second'); + expect(JSON.parse(storage[CACHE_KEY]).pages.map((p: TreeDto['pages'][number]) => p.name)) + .toEqual(['first', 'second']); + expect(storage[PENDING_CACHE_KEY]).toBe('1'); + + resolveFirstPut!(); + await vi.advanceTimersByTimeAsync(0); + + expect(JSON.parse(storage[CACHE_KEY]).pages.map((p: TreeDto['pages'][number]) => p.name)) + .toEqual(['first', 'second']); + expect(storage[PENDING_CACHE_KEY]).toBe('1'); + + await vi.advanceTimersByTimeAsync(750); + expect(api.putData).toHaveBeenCalledTimes(2); + expect(storage[PENDING_CACHE_KEY]).toBeUndefined(); + }); + + it('keeps a pending local mutation across reload before the debounce saves', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + const serverPage = mkPage('server'); + api.getData.mockResolvedValue({ pages: [serverPage] }); + const store = configure(api); + await store.init(); + + store.updatePage(FIXED_UUID, { keep_tasks_open: true }); + + expect(JSON.parse(storage[CACHE_KEY]).pages[0].keep_tasks_open).toBe(true); + expect(storage[PENDING_CACHE_KEY]).toBe('1'); + store.ngOnDestroy(); + + const reloadedApi = makeMockApi(); + reloadedApi.getData.mockResolvedValue({ pages: [serverPage] }); + const reloadedStore = configure(reloadedApi); + await reloadedStore.init(); + + expect(reloadedStore.pages()[0].keep_tasks_open).toBe(true); + + await vi.advanceTimersByTimeAsync(750); + expect(reloadedApi.putData).toHaveBeenCalledTimes(1); + const [, tree] = reloadedApi.putData.mock.calls[0]; + expect((tree as TreeDto).pages[0].keep_tasks_open).toBe(true); + }); + + it('does not let a stale in-flight save clear a newer pending settings cache', async () => { + storage[TOKEN_KEY] = FIXED_UUID; + const api = makeMockApi(); + const serverPage = mkPage('server'); + let resolveFirstPut: (() => void) | null = null; + api.getData.mockResolvedValue({ pages: [serverPage] }); + api.putData + .mockReturnValueOnce(new Promise((res) => (resolveFirstPut = () => res()))) + .mockResolvedValueOnce(undefined); + const store = configure(api); + await store.init(); + + store.updatePage(FIXED_UUID, { name: 'renamed' }); + await vi.advanceTimersByTimeAsync(750); + expect(api.putData).toHaveBeenCalledTimes(1); + expect((api.putData.mock.calls[0][1] as TreeDto).pages[0].keep_tasks_open).toBe(false); + + store.updatePage(FIXED_UUID, { keep_tasks_open: true }); + expect(JSON.parse(storage[CACHE_KEY]).pages[0].keep_tasks_open).toBe(true); + expect(storage[PENDING_CACHE_KEY]).toBe('1'); + + resolveFirstPut!(); + await vi.advanceTimersByTimeAsync(0); + expect(JSON.parse(storage[CACHE_KEY]).pages[0].keep_tasks_open).toBe(true); + expect(storage[PENDING_CACHE_KEY]).toBe('1'); + + await vi.advanceTimersByTimeAsync(750); + expect(api.putData).toHaveBeenCalledTimes(2); + expect((api.putData.mock.calls[1][1] as TreeDto).pages[0].keep_tasks_open).toBe(true); + expect(JSON.parse(storage[CACHE_KEY]).pages[0].keep_tasks_open).toBe(true); + expect(storage[PENDING_CACHE_KEY]).toBeUndefined(); + }); + // ── Error handling ──────────────────────────────────────────────────────── it('marks status "too-large" on 413 and does NOT retry', async () => { From afce46ccf875d1a7b7f24d7d30b6780006a16fb4 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 31 May 2026 10:49:26 +0100 Subject: [PATCH 10/26] docs: add CLAUDE.md agent guide, drop legacy design/API specs, refresh READMEs --- CLAUDE.md | 291 +++++++++++++++++ README.md | 14 +- docs/DESIGN.md | 795 --------------------------------------------- docs/api-spec.md | 127 -------- frontend/README.md | 62 +--- 5 files changed, 307 insertions(+), 982 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 docs/DESIGN.md delete mode 100644 docs/api-spec.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..091d70c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,291 @@ +# Life Towers — agent notes + +This file captures the load-bearing things to know about this codebase. Read it before making non-trivial changes. + +## What this is + +A personal-productivity TODO app where each user has multiple **pages**, each page holds several **towers** (vertical task columns), and each tower has **blocks** (atomic tasks). Pending blocks live in a **tasks accordion** at the top of the tower; completed ones fall into the tower as colored squares (the "falling animation" is the defining visual). Date-range slider filters which done blocks are visible. + +This is a port/modernisation of a legacy Angular 7 app. **Design parity with the legacy is a hard requirement** — the user cares deeply about the original aesthetic. The legacy source lives at `_legacy_reference/` (gitignored) and is the source of truth for any visual question. A detailed style guide is at `docs/DESIGN.md`. + +## Repo layout + +``` +backend/ # FastAPI + SQLite (WAL) + src/life_towers/ + main.py # ASGI app, lifespan, static mount + SPA fallback + api.py # Routes (/api/v1/{health,register,data}) + auth.py # Bearer UUIDv4 → user_id + db.py # sqlite3 factory + migration runner + limits.py # slowapi limiter + payload-size middleware + models.py # pydantic v2 schemas + logging.py # structlog JSON + migrations/ # 001_initial.sql consolidated schema + tests/test_api.py # pytest + httpx AsyncClient + pyproject.toml # uv-managed +frontend/ # Angular 21+ standalone, signals, zoneless + src/app/ + components/ + pages/ # Top-level: page selector + Settings button + page/ # Tower row + slider + trash zone + confirm-delete + tower/ # White tower card: tasks accordion + add-block + falling stack + name input + block/ # Colored square (1/6 tower width) + tasks/ # Pending-blocks accordion with tickbox + welcome/ # Zero-state intro modal + "Try an example" + modal/ # Generic backdrop shell + sub-modals (block-edit carousel, settings, tower-settings) + shared/ # select-add, toggle, double-slider, color-picker, icon + services/ + api.service.ts # HttpClient wrapper, exact API contract + store.service.ts # signal-based store, debounced PUT, retry, loadExample + modal-state.service.ts # global open-modal counter (drag locking) + models/index.ts # Page / Tower / Block / HslColor TS interfaces + utils/{color,hash}.ts + library/ # Legacy SCSS dropped in verbatim — DO NOT REFACTOR + public/assets/ # SVG icons (arrow, pen, plus-sign, trash, x-sign) + src/assets/fonts/ # Self-hosted woff2 (Open Sans Condensed, Raleway, …) + e2e/ # Playwright (smoke + visuals) +docs/ + api-spec.md # Single source of truth for the HTTP API contract + DESIGN.md # Legacy visual spec (verbatim SCSS quotes) +_legacy_reference/ # Original Angular 7 source — read-only, gitignored +Dockerfile # Multi-stage: node:22-alpine build → python:3.13-slim runtime +docker-compose.yml # Production single-container +docker-compose.dev.yml # Ephemeral volume for Playwright runs +.forgejo/workflows/ # CI + deploy +``` + +## Tech stack quickrefs + +- **Frontend**: Angular 21+, **standalone components** (no NgModule), `signal()` / `computed()` / `effect()` / `linkedSignal()`, **zoneless change detection** (`provideZonelessChangeDetection`), `@if`/`@for` control flow, **OnPush** everywhere, esbuild builder (`@angular/build:application`), Reactive Forms, Angular Service Worker (PWA), Angular CDK (drag-drop, A11yModule for focus trap) +- **Backend**: FastAPI, pydantic v2, slowapi (rate limiting), structlog, **sqlite3 with WAL + foreign_keys ON** every connection, uv-managed deps +- **Runtime topology**: single Docker container — FastAPI process serves both `/api/v1/*` JSON endpoints AND the built Angular SPA as static files with SPA fallback. Behind nginx; uvicorn launched with `--proxy-headers --forwarded-allow-ips=*` +- **Storage**: SQLite at `/data/life-towers.db` on a named Docker volume. Tree-replace semantics (PUT replaces user's full tree atomically inside `BEGIN IMMEDIATE`) + +## Build / dev / test / deploy + +```bash +# Full local stack (ephemeral data) +docker compose -f docker-compose.dev.yml up --build -d # http://localhost:8000 +docker compose -f docker-compose.dev.yml down -v # teardown + +# Frontend dev (with backend proxied via proxy.conf.json) +cd frontend && npm start # ng serve on :4200, /api → :8000 +cd frontend && npm run build # production bundle to dist/frontend/browser/ +cd frontend && npm test # vitest (--run already in the script) +cd frontend && npm run test:e2e # Playwright + +# Backend dev +cd backend && uv sync +cd backend && uv run pytest -v +cd backend && uv run uvicorn life_towers.main:app --reload # :8000 + +# E2E in the sandbox: host-to-container port forwarding is broken here. +# Run Playwright INSIDE a container on the docker network instead: +docker run --rm \ + --network life-towers_default \ + -v "$(pwd)/frontend:/work" -w /work \ + -e PLAYWRIGHT_BASE_URL=http://life-towers:8000 \ + mcr.microsoft.com/playwright:v1.60.0-noble \ + npx playwright test +``` + +## Design system — the legacy is the source of truth + +`docs/DESIGN.md` is the comprehensive spec. Key tokens: + +```scss +// _legacy_reference/frontend/src/library/common-variables.scss +$accent-color: #a2666f; // rose +$text-color: #5d576b; // muted purple-grey (also iOS theme-color) +$light-color: #ffffff; +$background-gradient: linear-gradient(90deg, #fff9e07f 0, #ffd6d67f 100%); // 50% alpha — modal backdrop +$background-gradient-opaque: linear-gradient(90deg, #fffcf0 0, #ffebeb 100%); // body background +$shadow: 0 0 1.5px 1.5px rgba(0,0,0,0.1), 0 0 3px 2px rgba(0,0,0,0.05); +$shadow-border: 0 0 0 0.75px rgba(0,0,0,0.1); // hairline +$normal-font: 'Open Sans Condensed', sans-serif; +$title-font: 'Raleway', serif; +$mobile-width: 520px; +``` + +Spacing tokens (`library/main.scss`): +```scss +:root { + --large-padding: 30px; // 20px on mobile + --medium-padding: 15px; + --small-padding: 10px; // 7.5px on mobile + --border-radius: 5px; // 3px on mobile +} +``` + +Font sizes (`library/text.scss`): `--larger/large/medium/small-font-size` = `22/18/16/11` desktop, `20/16/14/10` mobile. + +**Animation timings**: +- `$long-animation-time: 200ms` — opacity, hover transforms, modal entry +- `$short-animation-time: 100ms` — tighter transitions (red trash-highlight overlay) +- **Falling animation**: `transform 1.5s cubic-bezier(0.5, 0, 1, 0)` (gravity ease-in) +- **Modal opacity entry/exit**: `300ms` + +## Architectural conventions to follow + +### Components + +- Every component is **standalone**, **OnPush**, with `signal()`-based local state. No NgModule. +- Templates use **`@if` / `@for` / `@switch`** — never `*ngIf`/`*ngFor`. +- Inline templates + inline styles in `@Component({ template: \`...\`, styles: \`...\` })` is the norm. Larger components use templateUrl + styleUrl (only `pages.component`, `page.component`). +- SCSS inside `styles:` template literal — **`//` comments are fine inside SCSS but watch for backticks**; `// `display: contents`` inside a template literal closes it early. Use `/* */` if you need to mention CSS strings in comments. +- Library files (`library/*.scss`) are dropped from the legacy verbatim. Don't refactor them. Components `@import '../../../library/main'` (or similar relative depth). + +### State + +- `StoreService` is the single source of truth (signal-based). +- Mutations update the signal immediately (**optimistic**) and call `scheduleSave()` which debounces 750ms and PUTs the full tree. +- Failure mode: exponential backoff up to 5 attempts (1s, 2s, 4s, 8s, 16s). +- LocalStorage cache key: `life-towers.cache.v4`. Token: `life-towers.token.v4`. +- `init()` flow: stored token? Use it. No token? Mint via `uuidV4()` → register → GET data. On 401: re-register the SAME token (idempotent server-side), never silently mint a fresh one (would orphan data). + +### Reactivity caveats with zoneless + +- Plain field mutations in event handlers (`(click)="x = true"`) still trigger CD because Angular's event manager marks the view dirty — even with zoneless. But any async mutation outside an Angular event (setTimeout, raw addEventListener, MutationObserver) **will not** trigger CD; use signals there. +- `effect()` running on `signal()` reads triggers re-runs; wrap writes in `untracked()` to avoid loops. +- For input-driven derived state that the user can also override (e.g. `tasks.expanded` seeded from `initiallyOpen` but also clickable), use either `linkedSignal` or `effect()` + a flag (the codebase uses the latter for `tasks.component`). + +### Sync model + +- **Tree-replace**: `PUT /api/v1/data` sends the entire user hierarchy atomically. Backend wraps in `BEGIN IMMEDIATE`, deletes existing pages (cascading to towers + blocks via FK), inserts new rows. `position` columns track ordering. +- **No granular endpoints** for individual entity CRUD. Keep it tree-replace. +- Spec is in `docs/api-spec.md`. Backend pydantic models match it. Frontend `models/index.ts` matches it. Field names are **snake_case** on both sides. + +### Backend + +- All endpoints are inside `APIRouter(prefix="/api/v1")`. Spec drives behavior — if you change a limit, update both spec and code. +- Migrations: package data under `src/life_towers/migrations/`, loaded via `importlib.resources.files("life_towers").joinpath("migrations")`. The runner tracks applied state in a `schema_migrations(filename TEXT PRIMARY KEY, applied_at INTEGER)` table. +- All sqlite connections must do `PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA busy_timeout=5000`. The `get_connection()` factory does this. +- Errors → JSON `{"error": code, "detail": str}` via a single `HTTPException` handler in `main.py`. Stack traces never leak (logged server-side). +- Rate limits via slowapi: `/register` 30/hour/IP, `GET /data` 60/min/token, `PUT /data` 30/min/token. + +## Visual + interaction details that bit me + +### Falling animation (tower.component) + +The `.block-container` has `transform: scaleY(-1)` so blocks visually fall from the TOP into the bottom of the tower. Each block default-positions at `translateY(500%)` via a `*` rule; the inline `[style.transform]="b._transform"` binding overrides per-block. + +When exactly **one** new done block is added, the `reconcile()` method: +1. Sets the new block's `_transform: 'translateY(500%)'` and `_opacity: '0'` (off-screen) +2. Calls `requestAnimationFrame` → `requestAnimationFrame` to let the browser paint the initial state +3. Sets `_anim: 'descend'`, `_transform: 'translateY(0)'`, `_opacity: '1'` — the CSS transition fires + +**Critical**: `grewByOne` detection is position-independent (set-difference). When a tickbox flips a pending block to done, the new entry inserts at its original `tower.blocks` index, not appended. Use the new ID, not `styled[length-1]`. + +The **date range** slider asymmetry: blocks below `range.from` are removed from `visibleBlocks` entirely (instant shuffle, no gap), blocks above `range.to` get `_anim: 'ascend'` and stay in the list flying up. `prevDoneIds` tracks the full `allDone` array — not the filtered styled list — so range expansions don't mis-fire as "new block". + +### Block-edit carousel (modal/block-edit.component) + +- Lives at `position: fixed; z-index: 10001` to escape the modal dialog wrapper and cover the viewport +- Two placeholder cards flank the real cards so the active card can fully center via `scroll-snap-align: center` +- `.mask` overlay on non-active cards has three tiers: `active opacity 0`, `near-active 0.55`, default `1`. Card opacity also tiered (1 / 0.85 / 0.6) mimicking the legacy `1.33*(1-t/2)` curve +- Backdrop click (anywhere not a non-placeholder card) closes the modal +- Delete on an existing card **does not** close the modal — it stays open and the card re-renders out of the list +- Auto-save on tag/toggle change; description deferred to blur + +### Select-add (shared/select-add) + +- Has a `.top` chip and a `.bottom` slide-down panel. `:has(.bottom.open) .top, .background { border-radius: var(--border-radius) var(--border-radius) 0 0 }` squares the bottom corners when open so the chip and panel read as one card +- Shadow seam between chip + panel solved with `clip-path: inset(...)`: `.background.active` clips bottom (`inset(-6px -6px 0 -6px)`), `.bottom.open` clips top (`inset(0 -6px -6px -6px)`). 6px > the total $shadow spread (5px) so neither edge bleeds across the seam +- Closing animation requires a two-transition setup: default state has `transition: ... visibility 0s $long-animation-time` (visibility delays on close), `.open` overrides with `visibility 0s 0s` (instant on open) + +### Modal shell (modal/modal.component) + +- `:host { display: contents }` — critical. Without this, the `lt-modal` host element takes a flex slot in `pages.component`'s `inner-spacing` layout, pushing the Settings button up when the modal mounts +- `section.modal` is `position: fixed; z-index: 10000` with `transition: opacity 300ms`. The component flips `active = true` in `ngAfterViewInit` via `setTimeout(0)` so the opacity 0 → 1 transition runs +- `ModalStateService.open()` / `.close()` are called in `AfterViewInit`/`OnDestroy`. `page.component` reads `modalState.anyOpen` and binds it to every tower's `[cdkDragDisabled]` so users can't drag towers behind an open modal + +### Carousel card date format + +```ts +formatDate(ts: number): string { + return new Date(ts * 1000).toLocaleString(undefined, { + year: 'numeric', month: 'short', day: 'numeric', + hour: '2-digit', minute: '2-digit', + }); // "May 28, 2026, 14:32" +} +``` + +### Double-slider relative-time labels + +`page.component.ts` formats with `Intl.RelativeTimeFormat(undefined, { numeric: 'auto', style: 'short' })`. Buckets: `<45s` second, `<45min` minute, `<22h` hour, `<26d` day, `<320d` month, else year. Labels are **deduped** by string (multiple distinct timestamps that round to the same "5 hr ago" appear once). + +When `values.length` grows (new block added), the slider snaps the **higher** of `oneValue`/`otherValue` to `MAX - 1` so the newest entry is always visible; the lower thumb (the user's left edge) stays put. + +### Color picker (shared/color-picker) + +- Row of 12 preset color swatches + a rainbow hue slider + a big preview swatch +- Saturation and lightness are FIXED at 0.7 / 0.55. Only hue varies +- Preset hues `[0, 15, 30, 45, 195, 215, 235, 255, 280, 310, 335, 355]` — skips the green/yellow zone (60°–180°) which muddies with the rose accent palette + +### Tickbox (tasks/tasks.component) + +- `✓` glyph is hidden at rest (`opacity: 0`) and only revealed on interaction: `0.85` (hover/focus-visible), `1` (active). It fades via the `opacity` transition +- The tickbox is a ` } }
@@ -41,11 +53,23 @@ import { type="text" #addInput placeholder="Add a value…" + maxlength="200" (keydown.enter)="onAdd(addInput.value); addInput.value = ''" /> - + @if (editable()) { - } @@ -58,6 +82,7 @@ import { @import '../../../../library/main'; $inner-padding: var(--medium-padding); + $dropdown-shadow: 0 4px 14px rgba($text-color, 0.16), $shadow-border; :host { display: block; @@ -80,13 +105,22 @@ import { align-items: center; position: relative; cursor: pointer; + min-height: 46px; + box-sizing: border-box; + gap: var(--small-padding); p { - display: inline-block; + display: block; @include sub-title-text(); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; } img.arrow { + flex: 0 0 auto; @include square(16px); transition: transform $long-animation-time; @@ -98,81 +132,129 @@ import { .bottom-container { width: 100%; - height: 300px; position: absolute; - overflow-y: hidden; + top: 100%; + left: 0; + right: 0; + overflow: visible; pointer-events: none; + z-index: 5; .bottom { - position: absolute; + position: relative; width: 100%; - pointer-events: all; + pointer-events: none; box-sizing: border-box; display: flex; flex-direction: column; align-items: flex-start; border-radius: 0 0 var(--border-radius) var(--border-radius); padding: $inner-padding; - padding-top: 0; - @include inner-spacing($inner-padding); + padding-top: var(--small-padding); + gap: var(--small-padding); // Default (closed) state — also the target of the close transition. background-color: transparent; box-shadow: none; - transform: translateY(-100%); + transform: translateY(-8px); + opacity: 0; visibility: hidden; + // Clip the top edge so the panel's shadow can't bleed back up into + // the chip area; sides + bottom get a 10px slack for the shadow. + clip-path: inset(0 -10px -10px -10px); // Delay the visibility change until after the slide animation finishes // so the panel stays visible while it animates closed. transition: transform $long-animation-time, + opacity $long-animation-time, background-color $long-animation-time, box-shadow $long-animation-time, visibility 0s $long-animation-time; &.open { visibility: visible; + pointer-events: all; transform: none; + opacity: 1; background-color: $light-color; - box-shadow: $shadow; - // Show shadow on left/right/bottom only; clip the top edge so the - // shadow doesn't bleed over the seam where .bottom meets .top. - clip-path: inset(0 -6px -6px -6px); + box-shadow: $dropdown-shadow; // On open, visibility flips immediately (no delay); transform + // colors + shadow animate over $long-animation-time. transition: transform $long-animation-time, + opacity $long-animation-time, background-color $long-animation-time, box-shadow $long-animation-time, visibility 0s 0s; } - p { + .option { @include sub-title-text(); - display: inline-block; + display: flex; + align-items: center; + width: 100%; + min-height: 36px; + margin: 0; + padding: 0; + border: 0; + background: transparent; text-align: left; cursor: pointer; + + &:after { + display: none; + } + + @media (max-width: $mobile-width) { + min-height: 42px; + } } input[type='text'] { @include sub-title-text(); width: 100%; + min-height: 36px; + box-sizing: border-box; + text-align: left; + + &::placeholder { + color: rgba($text-color, 0.72); + opacity: 1; + } + + @media (max-width: $mobile-width) { + min-height: 42px; + } } .add-row { - height: 32px; - @media (max-width: $mobile-width) { height: 24px; } + min-height: 40px; position: relative; width: 100%; display: flex; - align-items: center; + align-items: flex-end; gap: var(--small-padding); input[type='text'] { flex: 1; + min-height: 0; + padding: 0; + border-bottom: solid 2px transparent; + + &:focus, + &:focus-visible { + box-shadow: none; + border-bottom-color: $text-color; + } } button { margin: 0; - position: static; + position: relative; + flex: 0 0 auto; + + &.add-button { + align-self: flex-end; + } &.pen { opacity: 0.25; @@ -184,6 +266,10 @@ import { background: transparent; position: relative; + // Kill the global button's hover-grow underline pseudo-element + // for the icon-only edit control. + &:after { content: none; display: none; } + img { @include square(16px); } @@ -224,16 +310,18 @@ import { width: 100%; @include card(); z-index: 3; + box-sizing: border-box; transition: box-shadow $long-animation-time, height $long-animation-time, border-radius $long-animation-time; &.active { - box-shadow: $shadow; - // Show shadow on top/left/right only; clip the bottom edge so the - // shadow doesn't bleed over the seam where .top meets .bottom. - clip-path: inset(-6px -6px 0 -6px); + // Same shadow recipe as the panel below so the two halves read as + // one continuous card. Clip the bottom edge so this shadow doesn't + // bleed across the seam where .top meets .bottom. + box-shadow: $dropdown-shadow; + clip-path: inset(-10px -10px 0 -10px); } } @@ -251,25 +339,31 @@ import { } } + // Hover lifts the chip — but only when the dropdown is closed. When + // it's open the chip is already showing $dropdown-shadow and a hover + // override would make the top heavier than the panel below. &:hover { @media (min-width: $mobile-width) { - .background { box-shadow: $shadow; } + .background:not(.active) { box-shadow: $shadow; } } } &.shadow-border { .background.active { box-shadow: $shadow-border; - clip-path: inset(-6px -6px 0 -6px); + clip-path: inset(-10px -10px 0 -10px); + } + .bottom.open { + box-shadow: $shadow-border; } } &.shadow-border:hover { - .background { box-shadow: $shadow-border; } + .background:not(.active) { box-shadow: $shadow-border; } } &.always-shadow { - .background { box-shadow: $shadow; } + .background:not(.active) { box-shadow: $shadow; } // When open, clip the bottom so the always-on shadow doesn't bleed // over the seam; restore full shadow when closed. &:has(.bottom.open) .background { @@ -279,7 +373,7 @@ import { } `, }) -export class SelectAddComponent implements OnChanges { +export class SelectAddComponent { // ── New API (spec) ───────────────────────────────────────────────────────── /** List of string options */ readonly items = input([]); @@ -309,6 +403,7 @@ export class SelectAddComponent implements OnChanges { // ── Internal state ───────────────────────────────────────────────────────── readonly open = signal(false); readonly editing = signal(false); + private readonly host = inject(ElementRef); // Resolved values that merge old + new API protected resolvedItems(): string[] { @@ -320,7 +415,7 @@ export class SelectAddComponent implements OnChanges { protected resolvedSelected(): string | null { // New API: string const s = this.selected(); - if (s != null) return s; + if (s != null && s.trim()) return s; // Legacy API: index into options const idx = this.selectedIndex(); const opts = this.resolvedItems(); @@ -328,8 +423,18 @@ export class SelectAddComponent implements OnChanges { return null; } - ngOnChanges(_changes: SimpleChanges): void { - // Nothing to do — signals handle reactivity + @HostListener('document:click', ['$event']) + onDocumentClick(event: MouseEvent): void { + if (!this.open()) return; + if (!this.host.nativeElement.contains(event.target as Node)) { + this.open.set(false); + this.editing.set(false); + } + } + + toggleOpen(event: Event): void { + event.stopPropagation(); + this.open.update((v) => !v); } onSelectItem(item: string): void { From e3dcf75eb5bea9bf9910b575785447a29c5d131f Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 31 May 2026 10:49:26 +0100 Subject: [PATCH 20/26] frontend(modals): consolidate settings modals and drop page-settings --- .../app/components/modal/modal.component.ts | 46 +++++- .../modal/page-settings.component.ts | 141 ------------------ .../components/modal/settings.component.ts | 104 +++++++++---- .../modal/tower-settings.component.ts | 85 ++++++++--- 4 files changed, 178 insertions(+), 198 deletions(-) delete mode 100644 frontend/src/app/components/modal/page-settings.component.ts diff --git a/frontend/src/app/components/modal/modal.component.ts b/frontend/src/app/components/modal/modal.component.ts index 4c854ce..47c9eb8 100644 --- a/frontend/src/app/components/modal/modal.component.ts +++ b/frontend/src/app/components/modal/modal.component.ts @@ -2,6 +2,7 @@ import { Component, ChangeDetectionStrategy, output, + input, signal, AfterViewInit, OnDestroy, @@ -22,8 +23,20 @@ import { ModalStateService } from '../../services/modal-state.service'; class="modal" [class.active]="active()" (click)="onBackdropClick($event)" + (keydown.enter)="onBackdropClick($any($event))" + tabindex="-1" > -