Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,99 @@ _Last updated: 2026-08-22_
> **Mark tasks `[x]` immediately** after each one passes verification—do not batch completions. Update the roadmap status when all tasks are done.
> When referencing requirements, keep feature IDs (`F-`), non-goal IDs (`N-`), and scenario IDs (`S-058-`) inside the same parentheses immediately after the task title (omit categories that do not apply).
> When new high- or medium-impact questions arise during execution, add them to [docs/specs/4-architecture/open-questions.md](../../open-questions.md) instead of informal notes, and treat a task as fully resolved only once the governing spec sections reflect the clarified behaviour.
>
> **v8 only.** Every file this feature touches lives under `resources/js/v8/` (or the shared, non-versioned `resources/js/stores/`/`resources/js/services/`). Several tasks below name a `resources/js/v7/...` file with the same basename purely to say "leave it alone" — never edit it.

## Checklist

- [ ] T-058-01 – Confirm Feature 057 is implemented (or implement it first) before starting this feature (dependency gate).
_Intent:_ This feature's I2-I5 all require a real `GET /api/v3/Albums` to call; verify `docs/specs/4-architecture/features/057-album-listing-v3/tasks.md` is fully `[x]` before proceeding.
- [ ] T-058-01 – Confirm Feature 057 is implemented (dependency gate).
_Intent:_ This feature's I2-I8 all require a real `GET /api/v3/Albums` to call; confirm `docs/specs/4-architecture/features/057-album-listing-v3/tasks.md` is fully `[x]` before proceeding (already true on this branch — backend for 056/057 exists).
_Verification commands:_ N/A (documentation check).
_Notes:_ Plan Dependencies.
Comment thread
ildyria marked this conversation as resolved.

- [ ] T-058-02 – Feature test + implement `config/features.php` entry and `ModulesRightsResource::$is_album_listing_v3_enabled` (FR-058-01, FR-058-02).
_Intent:_ Test asserts init payload's `modules.is_album_listing_v3_enabled` matches `config('features.album-listing-v3')` for both `true`/`false`. Written to fail first, then implement.
- [ ] T-058-02 – Feature test + implement `config/features.php` entry and `ModulesRightsResource::$is_struct_of_array_enabled` (FR-058-01, FR-058-02).
_Intent:_ Test asserts init payload's `modules.is_struct_of_array_enabled` matches `config('features.struct-of-array')` for both `true`/`false`. Written to fail first, then implement.
_Verification commands:_
- `php artisan test --filter=ModulesRightsResource` (confirm exact existing test class name at implementation time)
- `make phpstan`
_Notes:_ Plan I1.

- [ ] T-058-03 – Add `album-list-v3-service.ts`, tree/breadcrumb/exclusion helper, and rewire `SearchTargetAlbum.vue` (FR-058-03, FR-058-04, NFR-058-02, NFR-058-03, NFR-058-06, S-058-02, S-058-03).
_Intent:_ DO-058-02/DO-058-03; flag-gated v3 path with client-side tree/breadcrumb/subtree-exclusion and `<img>`-based thumbnails (v3 Asset endpoint) with placeholder fallback on error. Confirm and document whether `Move`/`MoveAlbums` re-validates descendant-safety server-side (Risks note in plan.md).
- [ ] T-058-03 – Add `album-list-v3-service.ts` and the shared `AlbumListState` Pinia store (`tree`, `getExcludedTargetIds`, `isTopLevel`, `buildBreadcrumb`) (FR-058-03, FR-058-04).
_Intent:_ DO-058-02/DO-058-03; base-mode-only fetch (no `with_parent_id`/`for_bulk_edit`), `ensureLoaded()`/`invalidate()`, nested-set-stack `tree` getter, and three pure `lft`/`rgt`-derived getter methods: `getExcludedTargetIds(rootIds)` (multi-root exclusion), `isTopLevel(albumId)` (root-option trigger), `buildBreadcrumb(albumId)` (full ancestor-chain path text, no truncation — CSS handles overflow). No UI consumer wired yet — verify from the browser console against hand-built fixtures (single-root exclusion against `ListAlbums::do()`'s existing behavior; multi-root against a hand-built ancestor/descendant selection; `isTopLevel`/`buildBreadcrumb` against a hand-built 2-3 level tree).
_Verification commands:_
- `npm run check`
- `npm run format`
- Manual: `AlbumMove.vue`, `MoveDialog.vue`, `PhotoCopyDialog.vue`, `AlbumMergeDialog.vue` in both flag states.
- Manual: browser-console exercise of `tree`/`getExcludedTargetIds`/`isTopLevel`/`buildBreadcrumb` against a real session.
_Notes:_ Plan I2.

- [ ] T-058-04 – Manual verification pass: move-picker parity, flag on vs. off (NFR-058-02, NFR-058-03, S-058-01, S-058-02, S-058-03).
_Intent:_ Side-by-side comparison of breadcrumb text, thumbnail, and subtree-exclusion behavior between the v2 and v3 paths for the same fixture data.
- [ ] T-058-04 – Manual verification pass: shared store tree/exclusion/root/breadcrumb correctness (NFR-058-03).
_Intent:_ Side-by-side comparison of the store's `tree`, single-root `getExcludedTargetIds`, `isTopLevel`, and `buildBreadcrumb` output against `ListAlbums::do()`/`flatten()`'s existing server-side behavior for the same fixture data (full breadcrumb text only — not `shorten()`'s truncation algorithm, Q-058-06); separately verify the multi-root exclusion case against a hand-built selection containing an ancestor/descendant pair.
_Verification commands:_ Manual/browser-based (no automated frontend suite exists in this repo).
_Notes:_ Plan I2.

- [ ] T-058-05 – Add SoA→`AlbumTree[]` adapter and rewire `FixTree.vue` (FR-058-05, S-058-04).
_Intent:_ DO-058-04; `fetch()` calls the v3 endpoint with `with_parent_id=true` when flagged on, adapts the response, hands it to the existing `prepareAlbums()`/WASM pipeline unchanged. `updateFullTree()` untouched.
- [ ] T-058-05 – Add `Thumb.vue` + `thumb-asset-service.ts` (FR-058-05).
_Intent:_ DO-058-05/DO-058-06; blob fetch via axios with `AbortController` signal, module-level cache keyed by `(albumId, photoId, type)`, abort on unmount, placeholder fallback for `photoId === null` or a failed/aborted fetch. Verified standalone (e.g. dropped into an existing dev page), before any real consumer depends on it.
_Verification commands:_
- `npm run check`
- `npm run format`
- Manual: run validity-check/repair against seeded inconsistent-tree fixture data, both flag states.
- Manual: DevTools Network panel — confirm request de-dup for two same-key `<Thumb>` instances (S-058-13), confirm `(canceled)` status when unmounted mid-request (S-058-12).
_Notes:_ Plan I3.

- [ ] T-058-06 – Manual verification pass: Fix Tree parity, flag on vs. off (S-058-01, S-058-04).
_Intent:_ Confirm identical validity-check/repair results between v2 and v3 paths.
_Verification commands:_ Manual/browser-based.
_Notes:_ Plan I3.
- [ ] T-058-06 – Rewire `SearchTargetAlbum.vue` onto the shared store + `<Thumb>`, add "move to root" option and breadcrumb labels (FR-058-06, FR-058-10, NFR-058-02, NFR-058-03, NFR-058-07, S-058-02, S-058-03, S-058-10, S-058-11, S-058-12, S-058-13, S-058-16, S-058-17, S-058-18).
_Intent:_ Flag-gated v3 path: `ensureLoaded()` from the store, filter via `getExcludedTargetIds(props.albumIds ?? [])`; when `props.albumIds` is non-empty and `!isTopLevel(props.albumIds[0])`, prepend a synthetic `{id: null}` "move to root" row (mirrors `AlbumController::getTargetListAlbums`'s first-selected-album rule); render each option's label via `buildBreadcrumb` through an `#item-label` slot override (flag-off branch keeps `label-key="original"` unchanged); thumbnails via `<Thumb>` (root row → `photo-id: null` → placeholder).
_Verification commands:_
- `npm run check`
- `npm run format`
- Manual: `AlbumMove.vue`, `MoveDialog.vue`, `PhotoCopyDialog.vue`, `AlbumMergeDialog.vue` in both flag states; `AlbumMergeDialog.vue` with 3 albums selected including an ancestor/descendant pair; DevTools Network panel across sequential dialog opens for de-dup (S-058-10); moving album with vs. without a parent to confirm the root option's presence/absence (S-058-16/17); two same-titled albums under different parents to confirm breadcrumb disambiguation (S-058-18).
_Notes:_ Plan I4.

- [ ] T-058-07 – Rewire `BulkAlbumEdit.vue` for client-side pagination/search/select-all (FR-058-06, NFR-058-04, S-058-05, S-058-06, S-058-07, S-058-08).
_Intent:_ Single `for_bulk_edit=true` fetch when flagged on; `load(page)`, debounced search, and "select all matching" reimplemented as in-memory operations; both "numbered" and "infinite-scroll" UI modes keep existing markup. Write endpoints untouched.
- [ ] T-058-07 – Wire shared-store `invalidate()` into all identity-transition and regular-Album-mutation call sites (FR-058-11, FR-058-12, NFR-058-09, NFR-058-10, S-058-14, S-058-15, S-058-19, S-058-20, S-058-21, S-058-22, S-058-23, S-058-24).
_Intent:_ Ten v8 call sites, each an additive one-line call next to an existing `AlbumService.clearCache()`/`clearAlbums()` call (never touch the matching `resources/js/v7/...` file). Identity: `LoginForm.vue:125`, `WebauthnModal.vue:65`, `RegisterPage.vue:125`, `LeftMenu.vue:170`. Mutations: `AlbumMove.vue`/`MoveDialog.vue`/`AlbumMergeDialog.vue` (move/merge — likely already wired in T-058-06's dialog work; confirm here if not), `AlbumDelete.vue:67`, `DeleteDialog.vue:105` (album-delete path), `Unlock.vue:48`, `AlbumVisibility.vue:149`, `FixTree.vue:156` (post-`updateFullTree()`), `ImportFromServer.vue:170`. Do **not** wire: `AlbumPanel.vue`/`Albums.vue` (`togglePin`), any Tag/Person-Album dialog (`AlbumCreatePersonDialog.vue`, `AlbumCreateTagDialog.vue`, `TagRenameDialog.vue`, `TagDeleteDialog.vue`, `TagPanel.vue`, `TagMergeDialog.vue`), or `Search.vue`/`Timeline.vue`'s star/unstar callbacks — confirmed irrelevant to this store's tracked fields (Q-058-07).
_Verification commands:_
- `npm run check`
- `npm run format`
- Manual, one pass per included call site: perform the action, reopen a Move/Merge dialog (from T-058-06), confirm the store reflects the change. Login: password (S-058-14) and WebAuthn. Registration (S-058-24). Logout (S-058-15). Delete (S-058-19). Unlock (S-058-20). Visibility change (S-058-21). Fix Tree save (S-058-22) — may need to stub/precede T-058-09 with a minimal Fix Tree v3 read path, or verify this scenario after T-058-09 instead. Server-folder import (S-058-23). Spot-check one excluded site (e.g. pin-toggle) to confirm the store is *not* invalidated.
_Notes:_ Plan I5. S-058-22's full verification may be more natural after T-058-09 (Fix Tree's v3 read path) exists — order the manual pass accordingly, the wiring itself has no such dependency.

- [ ] T-058-08 – Manual verification pass: move-picker parity, flag on vs. off (NFR-058-02, NFR-058-03, S-058-01).
_Intent:_ Side-by-side comparison of breadcrumb text, thumbnail, root option, and subtree-exclusion behavior between the v2 (flag-off) and v3 (flag-on) paths for the same fixture data.
_Verification commands:_ Manual/browser-based (no automated frontend suite exists in this repo).
_Notes:_ Plan I4/I5.

- [ ] T-058-09 – Add SoA→`AlbumTree[]` adapter and rewire `FixTree.vue` (FR-058-07, S-058-04).
_Intent:_ DO-058-04; `fetch()` calls the v3 endpoint with `with_parent_id=true` when flagged on (its own separate admin-gated request, not the shared store), adapts the response, hands it to the existing `prepareAlbums()`/WASM pipeline unchanged. `updateFullTree()` untouched except for T-058-07's added `invalidate()` call on its completion.
_Verification commands:_
- `npm run check`
- `npm run format`
- Manual: run validity-check/repair against seeded inconsistent-tree fixture data, both flag states.
_Notes:_ Plan I6.

- [ ] T-058-10 – Rewire `BulkAlbumEdit.vue` for client-side pagination/search/select-all (FR-058-08, NFR-058-04, S-058-05, S-058-06, S-058-07, S-058-08).
_Intent:_ Single `for_bulk_edit=true` fetch when flagged on (its own separate admin-gated request, not the shared store); `load(page)`, debounced search, and "select all matching" reimplemented as in-memory operations; both "numbered" and "infinite-scroll" UI modes keep existing markup. Write endpoints untouched.
_Verification commands:_
- `npm run check`
- `npm run format`
- Manual: numbered pagination, infinite scroll, search, select-all-matching, both flag states.
_Notes:_ Plan I4.
_Notes:_ Plan I7.

- [ ] T-058-11 – Manual verification pass: Fix Tree and Bulk Album Edit parity, flag on vs. off (S-058-01, S-058-04..08).
_Intent:_ Confirm identical validity-check/repair results (Fix Tree) and identical pagination/search/select-all behavior (Bulk Album Edit) between v2 and v3 paths.
_Verification commands:_ Manual/browser-based.
_Notes:_ Plan I6/I7.

- [ ] T-058-08 – Full manual verification pass S-058-01..09 (both flag states) + quality gate + docs sync (NFR-058-05).
_Intent:_ Confirm S-058-09 (pure `.env` toggle, no rebuild) in addition to re-confirming S-058-01..08 end to end. Update `docs/specs/3-reference/api-design.md`, `docs/specs/4-architecture/knowledge-map.md`; move roadmap.md's Feature 058 row to Completed.
- [ ] T-058-12 – Full manual verification pass S-058-01..24 (both flag states) + quality gate + docs sync (NFR-058-05).
_Intent:_ Confirm S-058-09 (pure `.env` toggle, no rebuild) in addition to re-confirming every other scenario end to end. Update `docs/specs/3-reference/api-design.md`, `docs/specs/4-architecture/knowledge-map.md`; move roadmap.md's Feature 058 row to Completed. Confirm via `git diff` that no file under `resources/js/v7/` was touched (NFR-058-01).
_Verification commands:_
- `npm run format`
- `npm run check`
- `make phpstan`
- `vendor/bin/php-cs-fixer fix`
- `php artisan test --filter=ModulesRightsResource`
_Notes:_ Plan I5. Prepare commit summary per AGENTS.md commit protocol; do not commit directly.
- `git diff --stat -- resources/js/v7/` (expect empty)
_Notes:_ Plan I8. Prepare commit summary per AGENTS.md commit protocol; do not commit directly.

## Notes / TODOs

- Depends on Feature 057 being implemented first (T-058-01) — not yet started as of this writing.
- Feature 057/056 (backend) already implemented on this branch — T-058-01 is a formality, not a blocker.
- `STRUCT_OF_ARRAY_ENABLED` is named for future reuse by a Photos SoA v3 endpoint (Q-058-03); no photos work is included in this task list.
- v8 only — see the checklist header note. Every `resources/js/v7/...` counterpart file stays untouched.
9 changes: 7 additions & 2 deletions docs/specs/4-architecture/open-questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ Track unresolved high- and medium-impact questions here. Remove each row as soon
| Question ID | Feature | Priority | Summary | Status | Opened | Updated |
|-------------|---------|----------|---------|--------|--------|---------|
| ~~Q-057-05~~ | 057 – Album Listing v3 | High | Discovered while scoping Feature 058: the move-target picker consumer needs a per-album thumbnail, which the original minimal shape (id/title/_lft/_rgt) didn't carry | Resolved (add `cover_ids` to the base/default response — resolved server-side via the same 3-column priority `HasAlbumThumb::getCoverTypeForAlbum()` already uses, zero extra joins since all 3 columns live on `albums`; frontend resolves actual thumbnail bytes via the separate Feature 056 v3 Asset endpoint) | 2026-08-22 | 2026-08-22 |
| Q-058-01 | 058 – Album Listing v3 Adoption | High | Scope & approach for migrating the 3 v2 consumers, given real UX mismatches (move-picker loses thumbnail/breadcrumb/self-exclusion; bulk-edit loses server pagination+search) — migrate all 3 with client-side compensation vs. a narrower scope | Open | 2026-08-22 | 2026-08-22 |
| Q-058-02 | 058 – Album Listing v3 Adoption | Medium | Feature-flag granularity in `config/features.php` — one combined flag vs. one independent flag per consumer | Open | 2026-08-22 | 2026-08-22 |
| ~~Q-058-01~~ | 058 – Album Listing v3 Adoption | High | Scope & approach for migrating the 3 v2 consumers, given real UX mismatches (move-picker loses thumbnail/breadcrumb/self-exclusion; bulk-edit loses server pagination+search) — migrate all 3 with client-side compensation vs. a narrower scope | Resolved (all 3, client-side compensation, thumbnails preserved via a new `<Thumb>` component) | 2026-08-22 | 2026-08-22 |
| ~~Q-058-02~~ | 058 – Album Listing v3 Adoption | Medium | Feature-flag granularity in `config/features.php` — one combined flag vs. one independent flag per consumer | Resolved (A — one combined flag) | 2026-08-22 | 2026-08-22 |
| ~~Q-058-03~~ | 058 – Album Listing v3 Adoption | Medium | Flag naming/scope — `ALBUM_LISTING_V3_ENABLED` (feature-specific) vs. a general SoA toggle | Resolved (`STRUCT_OF_ARRAY_ENABLED`/`struct-of-array`/`is_struct_of_array_enabled` — reused later by a future Photos SoA endpoint) | 2026-08-22 | 2026-08-22 |
| ~~Q-058-04~~ | 058 – Album Listing v3 Adoption | High | Where the shared album list lives and how its tree is computed — bare composable vs. Pinia store; `_lft`/`_rgt` reconstruction vs. admin-gated `parent_ids` | Resolved (Pinia store `AlbumListState.ts`; tree from `_lft`/`_rgt` only, since `parent_ids` is admin-gated and unusable for non-admin move/merge dialogs) | 2026-08-22 | 2026-08-22 |
| ~~Q-058-05~~ | 058 – Album Listing v3 Adoption | High | Cyclic-dependency prevention for Album Move and Album Merge (multi-album) — single-root vs. multi-root exclusion | Resolved (one pure `getExcludedTargetIds(rootIds)` function, self ∪ descendants via `_lft`/`_rgt`, covering N roots uniformly) | 2026-08-22 | 2026-08-22 |
| ~~Q-058-06~~ | 058 – Album Listing v3 Adoption | High | Breadcrumb display and the "move to root" option — drop as dead scope (today's v8 picker renders neither) vs. build both as real UI additions | Resolved (build both — breadcrumb full-path text mirrors `ListAlbums::do()`, truncation uses CSS not `shorten()`'s algorithm; root option derived from `_lft`/`_rgt` containment, no `parent_ids` needed) | 2026-08-22 | 2026-08-22 |
| ~~Q-058-07~~ | 058 – Album Listing v3 Adoption | Medium | Breadth of the shared store's mutation-invalidation net — move/merge/login/logout only vs. widen to all regular-Album mutations | Resolved (widen to delete/unlock/visibility-change/Fix-Tree-save/server-import, plus WebAuthn login and registration's auto-login; explicitly excludes pin-toggle and Tag/Person-Album operations, confirmed irrelevant to `GET /api/v3/Albums`'s tracked fields) | 2026-08-22 | 2026-08-22 |
| ~~Q-057-01~~ | 057 – Album Listing v3 | High | Rights-curation filter for the default (root) listing mode — `applyVisibilityFilter()` vs. `applyReachabilityFilter()` (password-lock aware) | Resolved (A — visibility only) | 2026-08-22 | 2026-08-22 |
| ~~Q-057-02~~ | 057 – Album Listing v3 | High | Query-parameter shape for the fixTree/bulk-edit variants — two independent booleans vs. one `mode` enum | Resolved (A — two independent booleans, `with_parent_id` + `for_bulk_edit`) | 2026-08-22 | 2026-08-22 |
| ~~Q-057-03~~ | 057 – Album Listing v3 | Medium | Field set returned when the bulk-edit flag is set — minimal owner/visibility subset vs. full parity with today's `BulkAlbumResource` | Resolved (B — full `BulkAlbumResource` field parity) | 2026-08-22 | 2026-08-22 |
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading