Skip to content

feat(milestones-ui): selector + management modal + card badge (closes #40)#47

Merged
Musiker15 merged 1 commit into
mainfrom
feat/milestones-ui
May 25, 2026
Merged

feat(milestones-ui): selector + management modal + card badge (closes #40)#47
Musiker15 merged 1 commit into
mainfrom
feat/milestones-ui

Conversation

@Musiker15
Copy link
Copy Markdown
Member

Summary

Completes the milestone feature started in #39. Backend was merged; this PR ships the UI.

What's in

Surface What
Board header "Milestones (N)" button next to Export. Opens the management modal.
Card drawer New "Milestone" selector right after "Due date". Lists active milestones + the currently-assigned one even if archived.
Card preview Small ◆ <name> badge alongside Due / checklist counters when the card has a milestone.
New modal Create form (name, optional description, optional start/end). Per-row edit-in-place, archive/unarchive, delete-with-confirm. "Show archived" toggle.

Files touched

  • New: milestone-manager.tsx, milestone-types.ts
  • Patched: board-client.tsx, card-drawer.tsx, kanban-view.tsx, board-cache.ts (IDB snapshot), cards.ts (CardDto + repo include)

Encryption

enc_name is opaque ({ name, description? } JSON, XChaCha20-Poly1305, bindTo "milestone:new"). Dates and archived are server-visible — matches the rest of the system.

Out of scope (deliberate follow-ups)

  • board-export.ts doesn't yet emit milestones in JSON/Markdown exports. Trivial drop-in follow-up.
  • onChange(snapshot) in the card drawer doesn't carry milestoneId. The dedicated PUT /api/cards/[id]/milestone is the source of truth for that field; the bulk save doesn't need to round-trip it.

Test plan

  • pnpm typecheck clean
  • pnpm lint clean
  • pnpm test — 107/107
  • CI green
  • Manual smoke (after merge): board → Milestones button → create / edit / archive / delete. Open card → select milestone → reload → still assigned. Card badge visible.

Closes #40.

🤖 Generated with Claude Code

…d badge

Closes the UI half of #39's milestone feature. Backend (schema, repo,
API) is on main since c2a8df0364080b; this PR makes them visible.

### New files
- `milestone-types.ts` — shared `ApiMilestone`, `MilestoneMeta`,
  `MilestoneView` for board-client / drawer / manager / cache to all
  reference the same shape without circular import.
- `milestone-manager.tsx` — self-contained modal: create form,
  list of milestones with archive-toggle, edit-in-place, delete with
  confirm. Bind-to convention is "milestone:new" (same simple
  convention as templates / labels).

### board-client.tsx
- Loads milestones in the initial Promise.all with `?archived=1`
  (the manager filters client-side via the "Show archived" toggle).
- `decryptMilestone` helper next to `decryptTemplate`.
- Four new handlers: `createMilestone`, `updateMilestone`,
  `deleteMilestone`, `assignCardMilestone` — all wire through the
  routes shipped in #39.
- New "Milestones (N)" button in the board header next to the export
  buttons. Opens the manager modal.
- Mounts `<MilestoneManager>` conditionally.

### card-drawer.tsx
- `CardSnapshot` gains `milestoneId: string | null`.
- `DrawerProps` gains `milestones: MilestoneView[]` and
  `onMilestoneChange(milestoneId: string | null): Promise<void>`.
- New `<label>` block right after Due Date with a `<select>` listing
  active milestones (plus the currently-assigned one even if it is
  archived). Changing the value PUTs `/api/cards/[id]/milestone`.

### kanban-view.tsx
- `KanbanCard` gains `milestoneId: string | null`.
- `KanbanView` accepts `milestones: MilestoneView[]`.
- `SortableCard` receives an optional `milestone` prop and renders a
  small "◆ <name>" badge next to the Due-date / checklist counters
  on the card preview.

### Other plumbing
- `cards.ts` repository now returns `milestoneId` in the CardDto so
  the field flows through the public API. `Card_milestoneId_idx` from
  #39 keeps the per-milestone listing cheap.
- `board-cache.ts` (IndexedDB snapshot) adds `milestones` to the
  cached `BoardSnapshot` so offline-first reloads keep them visible.
  Snapshot version stays at 1 — defensive `?? []` on load handles
  pre-existing caches without bump.

### Out of scope (follow-ups)
- Export (`board-export.ts`) does not yet include milestones in JSON
  / Markdown exports. Trivial follow-up.
- The card-snapshot `onChange` propagation doesn't carry
  `milestoneId` because the source of truth for that field is the
  dedicated `PUT /api/cards/[id]/milestone` path, not the bulk save.

### Test plan
- [x] `pnpm typecheck` clean
- [x] `pnpm lint` clean
- [x] `pnpm test` — 107/107 (no new tests; behaviour exercised by E2E)
- [ ] CI green
- [ ] Manual: open board → "Milestones" button → create, edit,
      archive, delete. Open card → select milestone in drawer →
      reload → still assigned. Card preview shows the badge.

Closes #40.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Musiker15 <info@musiker15.de>
@Musiker15 Musiker15 merged commit 8af87e0 into main May 25, 2026
8 checks passed
@Musiker15 Musiker15 deleted the feat/milestones-ui branch May 25, 2026 13:54
Musiker15 added a commit that referenced this pull request May 30, 2026
User.isAdmin was stored and read but enforced nothing. Add a real admin
area, strictly metadata-only so zero-knowledge holds for admins too (an
admin can manage accounts but never decrypt anyone's content).

- guard: requireAdmin() (requireSession + isAdmin -> 403 otherwise)
- repo src/lib/repositories/admin.ts: listUsers (metadata only),
  setUserLock (indefinite lock + revoke sessions / unlock + reset counter),
  resetFailedAttempts, setUserAdmin (grant/revoke). Safety rails: no
  self-lock; the last admin cannot be demoted (pure wouldRemoveLastAdmin,
  unit-tested). Every action audited (ADMIN_USER_LOCK/UNLOCK/
  RESET_ATTEMPTS, ADMIN_ROLE_GRANT/REVOKE).
- routes: GET /api/admin/users, PATCH /api/admin/users/[id]
  (lock|unlock|resetAttempts|setAdmin via a zod discriminated union)
- UI: (app)/admin/ page (isAdmin-gated, bounces non-admins) + user table
  with per-row actions; admin nav link shown only to admins
- docs: CLAUDE.md (gap list, endpoint table, header), CHANGELOG, OpenAPI
  (Admin tag + routes + AdminUser schema), audit-log comment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PR 1b: Milestone UI — selector in card-drawer + management modal

1 participant