Scrum4Me/scripts
Janpeter Visser 852945efa3
feat(PBI-76): migrate localStorage prefs to user-settings store (Phase 1) (#188)
* feat(PBI-76): one-shot localStorage→user-settings migration helper

Reads all legacy keys (sprint_pb_*, pbi_*, story_sort, debug-mode,
and dynamic *_filter_kind/*_filter_status for jobs columns) and
returns a typed UserSettings patch plus the keys to clear.
Idempotent via scrum4me:settings_migrated=v1 marker. Skips invalid
values silently so existing corrupt entries do not block migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-76): bridge runs one-shot localStorage migration

After hydrate, scans legacy localStorage keys via buildMigrationPatch
and, if any data is found, pushes one bulk patch to the server,
applies it locally, then removes the legacy keys. Demo accounts skip
the migration entirely. Cancellable on unmount to avoid setState on
unmounted component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-76): migrate sprint-backlog to user-settings store

Replaces six useState+useEffect+localStorage flows with selectors
from useUserSettingsStore. Defaults are applied at the selector
level (filterStatus 'OPEN', sort 'code', etc) so the component
matches its previous behaviour. The collapsed Set is derived from
the persisted array, falling back to auto-collapse-DONE when no
preference exists yet. setPref calls are fire-and-forget — the
optimistic flow handles the local state update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-76): migrate pbi-list to user-settings store

Same pattern as sprint-backlog: replaces local useState +
localStorage hydration/persist with selectors from
useUserSettingsStore. filterPopoverOpen blijft lokaal — die
was nooit gepersisteerd in pbi-list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-76): migrate story-panel sort to user-settings store

Single pref (sortMode) — replaces sync localStorage useState
initializer with a selector. Default 'priority' applied at
the read site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-76): migrate jobs-column to user-settings store

Per-instance filter state (kinds + statuses) now lives under
views.jobsColumns[storageKeyPrefix] in user-settings. Removes
the local CSV-encoding helpers — store keeps arrays natively.
A single persist() call writes both fields together so the
two arrays cannot drift in optimistic mid-flight updates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-76): migrate debug-mode to user-settings store

DebugToggle reads debugMode from user-settings.devTools and
toggles via setPref. Removes the standalone stores/debug-store.ts
(no consumers left). Body classlist update only fires after the
store is hydrated to avoid a flash on initial paint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(PBI-76): remove unused readLocalStoragePref helper

No consumers left after migrating sprint-backlog, pbi-list,
story-panel, jobs-column, and debug-store to user-settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(PBI-76): mock user-settings action in backlog integration test

PbiList now imports the user-settings store, which transitively
loads actions/user-settings.ts → lib/prisma. The vitest jsdom
environment has no DATABASE_URL, so we add a mock alongside the
existing action mocks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): allow balanced parens in markdown link URLs

Previously the link-checker regex stopped at the first ')',
breaking on Next.js route-group paths like `app/(app)/...`. The
new regex matches one level of balanced parens inside the URL.

Caught by CI on PR #188 — pre-existing breakage from PBI-78 plan
doc that was already merged on main.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:13:39 +02:00
..
build-manual.mjs PBI-58: Developer manual + in-app /manual page (#148) 2026-05-07 18:00:10 +02:00
check-doc-links.mjs feat(PBI-76): migrate localStorage prefs to user-settings store (Phase 1) (#188) 2026-05-10 15:13:39 +02:00
create-admin.ts feat(ST-l9kkxh2m): CLI-script scripts/create-admin.ts voor admin-bootstrap 2026-05-05 14:34:10 +02:00
generate-docs-index.mjs docs: AI-optimized docs restructure (Phases 1–8) (#61) 2026-05-03 03:21:59 +02:00
insert-milestone.ts feat(codes): server actions + seed/scripts gebruiken code overal 2026-05-04 08:36:41 +02:00
README.md feat(PBI-66): wekelijkse sync van model_prices via Anthropic /v1/models (#167) 2026-05-08 09:38:33 +02:00
realtime-mutate.ts feat(M8): Realtime Solo Paneel via Postgres LISTEN/NOTIFY (ST-801..ST-806) (#8) 2026-04-27 13:59:32 +02:00
sync-model-prices.ts feat(PBI-66): wekelijkse sync van model_prices via Anthropic /v1/models (#167) 2026-05-08 09:38:33 +02:00
test-api.sh chore: update API test script base URL and IDs; add ST-313 to backlog 2026-04-25 22:53:50 +02:00

Scripts

sync-model-prices.ts

Wekelijks handmatig draaibaar script dat de tabel model_prices synchroniseert. Haalt de actuele Claude 4.x modellijst op via GET /v1/models (Anthropic API) en upsert de prijzen vanuit een hardcoded PRICE_TABLE in het script. Anthropic biedt geen prijs-API; bij elke prijswijziging update je de tabel in scripts/sync-model-prices.ts.

Prerequisites

What How
ANTHROPIC_API_KEY in .env.local Genereer op console.anthropic.com → API Keys. Free Evaluation tier is voldoende — /v1/models is een gratis metadata-call.
DATABASE_URL in .env.local Standaard Scrum4Me-setup.

Gebruik

# Eerst droog draaien — toont wat er zou gebeuren, schrijft niets
npm run db:sync-model-prices -- --dry-run

# Echt synchroniseren
npm run db:sync-model-prices

Output

Fetching /v1/models from Anthropic API...
  → 12 models received, 4 match Claude 4.x filter

Syncing prices:
  ✓ claude-opus-4-7                   (unchanged)
  ✓ claude-sonnet-4-6                 (unchanged)
  ✓ claude-haiku-4-5-20251001         (unchanged)
  ⚠ claude-sonnet-4-9                 (geen prijs in PRICE_TABLE — ...)

Result: 0 created, 0 updated, 3 unchanged, 1 skipped

Bij een nieuw model (⚠ skipped)

  1. Open de Anthropic pricing-pagina.
  2. Voeg het model toe aan PRICE_TABLE in scripts/sync-model-prices.ts:
    'claude-sonnet-4-9': { input: 3.0, output: 15.0 },
    
  3. Draai het script opnieuw.

Edge cases

  • API geeft 401: controleer ANTHROPIC_API_KEY.
  • API geeft 5xx: script doet 1× retry met 2s delay, daarna falen.
  • Model in DB maar niet meer in API: wordt niet verwijderd — alleen gelogd, zodat oude claude_jobs rijen kostberekening blijven hebben.

test-api.sh

Runs curl-based tests against all 7 Scrum4Me API endpoints. Covers happy paths, auth (401), demo-block (403), and input validation (400).

Prerequisites

What How
Database seeded npx prisma db seed
Dev server running npm run dev
curl installed curl --version

Step 1 — Get a token for lars

  1. Open http://localhost:3000 in your browser
  2. Log in as lars / scrum4me123
  3. Go to Settings → API Tokens
  4. Click New token, give it any label
  5. Copy the token — it is shown only once

Open scripts/test-api.sh and paste it into TOKEN="".

Step 2 — Find your IDs

You need four IDs. The easiest way is to use the API itself:

# 1. Get PRODUCT_ID — run this after setting TOKEN
curl -s -H "Authorization: Bearer <TOKEN>" http://localhost:3000/api/products | grep -o '"id":"[^"]*"' | head -1

# 2. SPRINT_ID — look in the database or the UI (Sprint → copy ID from the URL)
# 3. STORY_ID  — open a story in the Sprint Board, copy the ID from the URL or the activity log
# 4. TASK_ID   — open a task, copy its ID

Or query the database directly:

# With psql / Neon CLI:
SELECT id FROM "Sprint" WHERE status = 'ACTIVE' LIMIT 1;
SELECT id FROM "Story"  WHERE status = 'IN_SPRINT' LIMIT 1;
SELECT id FROM "Task"   WHERE story_id = '<story-id>' LIMIT 1;

Paste the four values into the variables at the top of test-api.sh.

Step 3 — (Optional) Get a demo token for 403 tests

The 403 demo-block tests are skipped unless you set DEMO_TOKEN.

  1. Log out, log in as demo / demo1234
  2. Go to Settings → API Tokens, create a token
  3. Paste it into DEMO_TOKEN="" in test-api.sh

Step 4 — Run

bash scripts/test-api.sh

Expected output when all IDs are set and a demo token is provided:

============================================================
  Scrum4Me API Test Suite
  Base URL : http://localhost:3000
  Token    : scrum4me... (lars)
  Demo     : demo1234... (403 tests active)
============================================================

── GET /api/products ──────────────────────────────────────────────
  PASS  TC-P-09  happy path (lars)
  PASS  TC-P-01  no token → 401
  PASS  TC-P-02  invalid token → 401

── GET /api/products/:id/next-story ───────────────────────────────
  PASS  TC-NS-08  happy path (lars, 200 or 404 both valid)
  PASS  TC-NS-01  no token → 401
  ...

============================================================
  Results: 30 passed, 0 failed
============================================================

Notes

  • product_id is required for POST /api/todos. The Zod schema enforces z.string().min(1). Sending a todo without a product_id returns 400.
  • TC-NS-08 accepts both 200 and 404. After a fresh seed lars has no active sprint, so 404 is the expected response unless you create a sprint and add stories to it.
  • Reorder test (TC-RO-10) uses a single task ID. A reorder with one element is valid (sort_order is updated to 1).
  • The todos happy-path test appends $(date +%s) to the title to avoid duplicate-title issues on repeated runs.