feat(web): category→subpage settings IA for global and repo settings #200

Merged
dominik.polakovics merged 2 commits from afk/198 into main 2026-07-22 03:14:16 +02:00

Closes #198

Dissolves the Settings.tsx (759-line) and RepoSettings.tsx (1215-line) monoliths into the category-list → detail-subpage IA from the issue spec, at /settings/:section? and /repos/:id/settings/:section?.

Shared foundation (components/settings/, lib/)

  • SettingsLayout — mobile category index (lucide icon + title + one-line description + chevron rows, danger pinned last in --danger) / mobile back-header (chevron to the index, not browser-back) / desktop (≥1024px, the shell breakpoint) master-detail with a slim plain-text category nav, visually lighter than the SideNav rail. The bare index redirects to the first category on desktop — reactively, so growing the viewport on the index redirects live.
  • useSettingsForm — one shared primitive owning dirty tracking, dirty-fields-only PATCH submit (unchanged endpoints/payloads), and the unsaved-changes guard: useBeforeLeave + beforeunload, both via native confirm('Discard unsaved changes?'). Dirty is derived from buildPatch — no parallel bookkeeping.
  • createSeededDrafts — the repo monolith's SSE seed/resync mechanism extracted generically; untouched drafts follow repo.changed refetches, dirty drafts survive, the seed advances.
  • createMediaQuery — reactive matchMedia primitive (jsdom-safe).
  • Category metadata (slug, title, description, icon, danger flag) is declared once per area and drives routes, index rows, and the desktop nav; the wiring tests walk the arrays directly.

Areas

  • Global (routes/settings/): general (Git author) · agents (Spawn defaults, AFK defaults, Capacity) · notifications (Install app + Web Push, device-local, guard-free). Form sections remount keyed on the refetched settings after a save, so the guard reads clean. Breadcrumb-free per spec.
  • Repo (routes/repo-settings/): general · integrations · branches · agents · autoland · secrets · danger. Breadcrumbs gain the section segment with "Settings" linking to the index. Secrets and Danger moved verbatim (guard-free). Two deliberate resolution changes in Autoland, both commented in-code: autolandBlocked and the lander's repo-provider fallback now read the SAVED tracker_binding/provider — those drafts live in other sections and cross-section drafts no longer exist.

Notes for review

  • lucide-solid added (this issue introduces the dependency; app-wide icon migration is #199). Per-icon deep imports; icons inherit currentColor in both themes.
  • Old monolith route + test files removed; largest new file is 486 lines (all under the 1000-line cap). Bundle: 269.9 → 282.1 kB (78.3 → 81.6 kB gzip).
  • All 59 monolith test cases are re-expressed in the per-section suites (verified by title diff; one autoland case renamed for the saved-binding semantics), plus new coverage for metadata↔route wiring, per-section dirty PATCH payloads, guard behavior, and the Danger 409→force flow. Full suite: 63 files / 924 tests green; lint and build (tsc) clean.
  • e2e smoke: /settings/settings/notifications deep link (a desktop viewport now redirects the bare path to General).

🤖 Generated with Claude Code

Closes #198 Dissolves the `Settings.tsx` (759-line) and `RepoSettings.tsx` (1215-line) monoliths into the category-list → detail-subpage IA from the issue spec, at `/settings/:section?` and `/repos/:id/settings/:section?`. ## Shared foundation (`components/settings/`, `lib/`) - **`SettingsLayout`** — mobile category index (lucide icon + title + one-line description + chevron rows, danger pinned last in `--danger`) / mobile back-header (chevron to the index, not browser-back) / desktop (≥1024px, the shell breakpoint) master-detail with a slim plain-text category nav, visually lighter than the SideNav rail. The bare index redirects to the first category on desktop — reactively, so growing the viewport on the index redirects live. - **`useSettingsForm`** — one shared primitive owning dirty tracking, dirty-fields-only PATCH submit (unchanged endpoints/payloads), and the unsaved-changes guard: `useBeforeLeave` + `beforeunload`, both via native `confirm('Discard unsaved changes?')`. Dirty is derived from `buildPatch` — no parallel bookkeeping. - **`createSeededDrafts`** — the repo monolith's SSE seed/resync mechanism extracted generically; untouched drafts follow `repo.changed` refetches, dirty drafts survive, the seed advances. - **`createMediaQuery`** — reactive matchMedia primitive (jsdom-safe). - Category metadata (slug, title, description, icon, danger flag) is declared **once per area** and drives routes, index rows, and the desktop nav; the wiring tests walk the arrays directly. ## Areas - **Global** (`routes/settings/`): general (Git author) · agents (Spawn defaults, AFK defaults, Capacity) · notifications (Install app + Web Push, device-local, guard-free). Form sections remount keyed on the refetched settings after a save, so the guard reads clean. Breadcrumb-free per spec. - **Repo** (`routes/repo-settings/`): general · integrations · branches · agents · autoland · secrets · danger. Breadcrumbs gain the section segment with "Settings" linking to the index. Secrets and Danger moved verbatim (guard-free). Two deliberate resolution changes in Autoland, both commented in-code: `autolandBlocked` and the lander's repo-provider fallback now read the SAVED `tracker_binding`/`provider` — those drafts live in other sections and cross-section drafts no longer exist. ## Notes for review - `lucide-solid` added (this issue introduces the dependency; app-wide icon migration is #199). Per-icon deep imports; icons inherit `currentColor` in both themes. - Old monolith route + test files removed; largest new file is 486 lines (all under the 1000-line cap). Bundle: 269.9 → 282.1 kB (78.3 → 81.6 kB gzip). - All 59 monolith test cases are re-expressed in the per-section suites (verified by title diff; one autoland case renamed for the saved-binding semantics), plus new coverage for metadata↔route wiring, per-section dirty PATCH payloads, guard behavior, and the Danger 409→force flow. Full suite: 63 files / 924 tests green; lint and build (tsc) clean. - e2e smoke: `/settings` → `/settings/notifications` deep link (a desktop viewport now redirects the bare path to General). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(web): category→subpage settings IA for global and repo settings (#198)
Some checks failed
ci / native (pull_request) Failing after 1m6s
7da7d61cd2
Dissolve the Settings.tsx (759-line) and RepoSettings.tsx (1215-line)
monoliths into a category-list → detail-subpage structure, mobile-first,
at /settings/:section? and /repos/:id/settings/:section?:

- SettingsLayout: mobile category index (icon + title + description +
  chevron rows) / mobile back-header / desktop (>=1024px) master-detail
  with a slim plain-text category nav; the bare index redirects to the
  first category on desktop, reactively on viewport growth.
- Category metadata (slug, title, description, lucide icon, danger flag)
  declared once per area drives routes, index rows and the desktop nav;
  danger pinned last in --danger red.
- useSettingsForm: shared per-section dirty-fields-only PATCH submit +
  unsaved-changes guard (useBeforeLeave + beforeunload, native confirm).
  Device-local sections (Notifications, Install app, Secrets, Danger)
  stay guard-free. Global form sections remount keyed on the refetched
  settings so a save reads clean.
- createSeededDrafts: the repo monolith's SSE seed/resync mechanism
  extracted; each repo section resyncs only its own fields.
- Repo breadcrumbs gain the section segment with Settings linking to the
  index; global settings stays breadcrumb-free.
- Adds lucide-solid (per-icon deep imports, currentColor inheritance).
- Old monolith tests split into per-section suites plus new coverage for
  metadata↔route wiring, per-section dirty PATCH payloads and the guard.

Closes #198

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
style(web): prettier formatting on settings section files
All checks were successful
ci / native (pull_request) Successful in 7m30s
59322a23bf
Fixes the failing format:check CI gate; whitespace/line-wrapping only,
no behavior change.

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

[autoland] verdict: pass

PASS — ci/native run 223 green (lint, format:check, test 924, build, go build/test, golangci-lint). Conventional title, Closes #198, diff scoped to settings IA per issue spec. Lander fixed the sole failure (prettier formatting on 5 files) inline.

[autoland] verdict: pass PASS — ci/native run 223 green (lint, format:check, test 924, build, go build/test, golangci-lint). Conventional title, Closes #198, diff scoped to settings IA per issue spec. Lander fixed the sole failure (prettier formatting on 5 files) inline.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab!200
No description provided.