super-productivity/e2e/tests
Johannes Millan eff41c041d
feat(project): project completion experience (#8036)
* feat(project): add project completion with celebration + trophy view

Completing a project marks it done and archives it, with a celebration
dialog (confetti + live stats) and a prompt to resolve unfinished tasks
(move to Inbox / mark done). Completed projects show a trophy badge and
a Reopen action on the archived-projects page.

isDone stays distinct from isArchived; selectArchivedProjects is left
intact so completed projects' tasks stay filtered out of Today/Overdue.
Append/merge deferred to #8032. Plan: docs/plans/2026-06-05-project-completion.md

* refactor(project): drop Archive menu item; Complete is the retire path

Archive and Complete produced near-identical end states; collapse to one
user-facing action. Removes the 'Archive project' menu item and handler so
Complete is the single way to retire a project. The archiveProject action,
reducer and ProjectService.archive() stay (needed for op-log replay of
historical archive ops and the legacy unarchive/restore path). Wiki updated;
menu specs cover the Complete flow.

* fix(project): keep completion dialog open for the active project

MatDialog closeOnNavigation (default true) dismissed the celebration the
moment completing the currently-active project navigated to '/'. Navigate
first, then open the dialog.

* test(project): e2e for completion flow (complete, celebrate, reopen)

Covers the resolve-unfinished-tasks prompt, the celebration dialog with
stats, the trophy badge on the archived page, and reopening. Adds an
openProjectContextMenu page-object helper.

* feat(project): confirm project completion

* feat(project): show completion celebration fullscreen

* fix(project): harden completion celebration flow

* style(project): use spacing tokens in completion dialog

* fix(project): align completion screen with project context

* style(project): soften completion screen coloring

* style(project): reuse completion screen surfaces

* fix: refine project completion dialog actions

* fix: complete projects with atomic task resolution

* fix: restore archive path in project completion UI

* refactor(project): remove dead completion code

The project-level completeProject action (OpType.Update) was never
dispatched — completion goes exclusively through the atomic
TaskSharedActions.completeProject (OpType.Batch) meta-reducer. Drop the
dead action, its reducer case, the PROJECT_COMPLETE enum member and the
immutable 'PCO' op-log code (which would otherwise be permanently
reserved for an op that can never be produced); enum count 147->146.

Also remove the unused selectCompletedProjects / selectPlainArchivedProjects
selectors (no consumers) and the misspelled, unused 'angel' confetti
field, keeping the regression tests that guard selectArchivedProjects.

* fix(project): make project completion non-reversible

Completion resolves a project's open tasks (move-to-inbox / mark-done),
which reopen cannot truly restore, so the "Reopen"/"Undo" affordances on
the completion path were misleading. Drop the celebration dialog's Reopen
button and the post-complete undo snack; the fullscreen celebration is
the feedback and deliberate reactivation still lives on the
archived-projects page (Project.reopen kept for it).

Also restore the project title param on the archive confirm dialog (it
was rendering a raw {{title}} placeholder), remove the now-unused
moveTasksToInbox / markTasksDone resolution helpers (the meta-reducer
resolves tasks atomically), and drop the orphaned UNDO / S.COMPLETED
i18n keys. Updates the completion e2e to the close flow and asserts the
resolution props are forwarded to the atomic action.

* fix(tasks): cancel native reminders for project-completed tasks

Completing a project marks its unfinished tasks done inside the
meta-reducer (no per-task updateTask), so unscheduleDoneTask$'s
native-reminder cancellation is bypassed and an OS-scheduled Android
notification could still fire for a now-done task. Add a local-only
effect that cancels native reminders for the force-completed task ids.

Local-only by design: it dispatches no actions (the persistent
dismissReminderOnly/clearDeadlineReminder would each be an extra synced
op), and done tasks are already filtered from reminders$ on all
platforms — only the native Android notification needs explicit removal.

* test(project): drop TaskService spies orphaned by helper removal

getByIdWithSubTaskData$/moveToProject/setDone/setUnDone were only used
by the removed moveTasksToInbox/markTasksDone helpers and their deleted
tests.

* feat(sync): affectedEntities multi-entity conflict detection for atomic completion

WIP checkpoint of the atomic completeProject approach. The Batch op declares
every touched entity (PROJECT, INBOX, TASKs, TODAY_TAG) via a new
affectedEntities field threaded through op-log capture, conflict detection,
the sync server (+Prisma migration) and shared-schema. Per-effect
completeProject handlers (issue two-way-sync, time-block, repeat-cfg)
re-derive the task changes the atomic op bypasses.

* revert(sync): remove affectedEntities multi-entity conflict detection

Reverts 0893a86162. The affectedEntities feature existed solely to make the
atomic completeProject Batch op sync-correct (its only producer). Decoupling
project completion into normal per-task ops (next commit) makes the existing
per-entity conflict detection and effects fire naturally, so this entire
layer — sync-core, super-sync-server, shared-schema, op-log plumbing, the
Prisma migration, and the per-effect completeProject listeners — is no longer
needed. Preserved in history via the checkpoint commit.

* refactor(project): decouple completion from task resolution (Option C)

Completion was an atomic multi-entity Batch op (completeProject) that marked
tasks done / moved them to Inbox inside the project-shared meta-reducer.
Because it bypassed the normal per-task actions, every downstream consumer had
to be taught about it separately — conflict detection (the affectedEntities
feature, reverted in the previous commit), native-reminder cancellation, issue
two-way-sync, time-block and repeat-cfg effects.

Decouple instead: completion is now a plain single-entity PROJECT flag flip
(completeProject = OpType.Update, mirroring archiveProject). Unfinished-task
resolution runs first as the normal per-task actions (moveToOtherProject /
updateTask isDone) from the completion flow, so the existing effects and
per-entity conflict detection fire naturally — no special-casing anywhere.

- project.actions/reducer: restore plain completeProject action + on() handler
- project.service: complete() is a flag dispatch; restore moveTasksToInbox /
  markTasksDone (normal per-task dispatch + Rule #6 flush)
- work-context-menu: resolve unfinished work before the flag flip
- drop the completeProject meta-reducer block, the Batch action, the
  TASK_SHARED_COMPLETE_PROJECT op code, and the reminder-cancel effect
  (unscheduleDoneTask$ already cancels native reminders on the normal path);
  current-task clearing is covered by the existing task-internal effect

Net: ~190 LOC removed here on top of ~1565 (affectedEntities + a Prisma
migration) in the revert. Completion's task resolution is not undone either
way, so the atomic bundle never bought a clean reversal.

* docs(project): record decoupled-completion decision (ADR #5)

Document why project completion uses decoupled per-task resolution + a plain
single-entity flag flip instead of an atomic multi-entity op: the atomic op
forced a cross-stack affectedEntities conflict-detection feature and per-effect
listeners, for an undo guarantee it never delivered. Adds ARCHITECTURE-DECISIONS
#5 and a revision note + corrected undo/bulk-mechanic notes in the plan doc.

* test(project): pin completion ordering + resolution edge cases

Address multi-agent review of the decoupled-completion refactor:
- assert resolution (moveTasksToInbox / markTasksDone) runs BEFORE the
  completeProject flag flip (toHaveBeenCalledBefore) — the core invariant of
  the decoupled design that was previously not pinned
- cover the not-done branch of moveTasksToInbox (no setUnDone) and assert
  markTasksDone dispatches exactly the passed set
- add explicit PCO encode/decode round-trip assertions
- document the inbox-path current-task carry-forward nuance in ADR #5

Composition is covered end-to-end by e2e/tests/project/project-completion.spec.ts.

* refactor(project): tighten completion flow per review

- collapse 3x getCompletionInfo() to <=2: gate the resolve prompt once,
  recompute only after a resolution; each call now wrapped with an error
  snack so a failed archive load no longer aborts silently
- drop the dead post-confirm re-prompt branch (unreachable between two
  sequential single-user modals)
- reuse getDiffInDays + dateStrToUtcDate in completion-stats util instead
  of hand-rolled local-midnight/duration helpers
- use a Set for the top-level-task membership check (was O(n*m))
- drop redundant inline dialog sizing; the panelClass owns fullscreen
- remove dead --project-complete-accent test assertion

* refactor(project): finish review follow-ups for completion flow

- W3: reset the celebration confetti instance on dialog destroy so its
  rAF loop + window resize listener are torn down when the dialog closes
  before the animation ends (ConfettiService now returns the handle and
  fires without awaiting completion)
- S2: extract resolveBgImageToDataUrl() shared by app.component and the
  celebration dialog (was duplicated file://->data-url resolution)
- S3: split completeProject() into _getCompletionInfoOrNotify (dedupes the
  error handling), _promptResolveUnfinishedTasks and _confirmCompletion
- W2: keep prefers-reduced-motion gating app-wide (a11y) + document intent

* fix(project): close confetti teardown race on early dialog close

If the celebration dialog is dismissed while canvas-confetti is still
loading, the instance was assigned after ngOnDestroy ran, so reset() never
fired and the rAF loop + resize listener leaked. Guard with an _isDestroyed
flag and reset the instance immediately if it arrives post-destroy.

Also drop the now-dead CanvasConfetti type alias (superseded by
ConfettiInstance, zero references).

* docs(project): drop non-existent undo-snack from completion wiki

* refactor(project): extract completion task-tree and dialog helpers

* refactor(project): hide Archive menu item; Complete is the retire path

* refactor(project): drop dead archive(), reuse resolve-choice type

Multi-review follow-ups on the completion feature:
- Remove orphaned ProjectService.archive() (+ unused import, spec) — the
  menu collapsed Archive into Complete, leaving no caller. The
  archiveProject action/reducer stay for op-log decode of historical ops.
- Reuse the exported ResolveUnfinishedTasksChoice type instead of
  re-spelling the union three times in work-context-menu.
- Fix misleading moveTasksToInbox comment (setUnDone re-opens, not move).
- Note the as-shipped deviations (no extra selectors, no celebration
  effect) in the design plan so they aren't hunted for later.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-08 13:43:38 +02:00
..
add-task-bar refactor(e2e): extract ensureGlobalAddTaskBarOpen helper to reduce code duplication 2026-01-16 13:35:59 +01:00
app-features test(e2e): unbreak boards done-toggle and time-tracking selector 2026-06-06 19:00:50 +02:00
autocomplete Merge branch 'master' into feat/operation-logs 2026-01-03 18:51:51 +01:00
boards test(e2e): tighten boards #7498 regression specs 2026-05-06 21:37:06 +02:00
break test(e2e): use correct route /#/tag/TODAY/tasks instead of /#/tag/TODAY 2026-03-15 18:48:00 +01:00
calendar fix(calendar): hide archived event banners 2026-06-05 13:39:31 +02:00
daily-summary - fix(archive): also normalize malformed timeTracking field (#7487) 2026-05-05 15:41:25 +02:00
focus-mode docs: remove outdated and implemented plan docs 2026-06-08 12:38:51 +02:00
import-export test(e2e): dismiss add-task-bar backdrop before finish day (#8014) 2026-06-04 22:53:11 +02:00
issue-provider-panel test(e2e): strengthen assertions across reviewed specs (#7747) 2026-05-23 13:40:17 +02:00
keyboard test(e2e): add comprehensive E2E test coverage for core features 2026-01-12 15:11:27 +01:00
menu chore: address code review feedback 2026-01-15 10:57:43 +01:00
migration feat(tasks): allow dragging tasks into subtask lists (#7962) 2026-06-02 19:43:41 +02:00
navigation feat(history): merge Quick History and Worklog into a unified History view (#8033) 2026-06-05 18:10:56 +02:00
notes fix(note): linkify URLs in locked notes so markdown links stay clickable 2026-04-16 19:47:52 +02:00
performance test(e2e): disable failing for now 2025-09-21 00:59:11 +02:00
planner test(e2e): stabilize task visibility specs 2026-05-13 16:27:31 +02:00
plugins perf(plugins): skip no-op doc-mode saves + rename to doc-mode (#7825) 2026-05-27 17:30:38 +02:00
project feat(project): project completion experience (#8036) 2026-06-08 13:43:38 +02:00
recurring fix(task-repeat-cfg): keep custom weekdays interactive (#8034) 2026-06-05 20:12:34 +02:00
reminders fix(reminder): require action for reminder popup #8051 (#8057) 2026-06-06 17:34:06 +02:00
schedule test(e2e): strengthen assertions across reviewed specs (#7747) 2026-05-23 13:40:17 +02:00
search improve E2E test reliability and add snapshotOpType interface param 2026-01-27 20:55:30 +01:00
settings test(e2e): strengthen assertions across reviewed specs (#7747) 2026-05-23 13:40:17 +02:00
short-syntax refactor(e2e): replace waitForTimeout with condition-based waits 2026-01-03 15:29:38 +01:00
simple-counter chore: remove unused eslint disables 2026-06-03 21:31:06 +02:00
sync feat(history): merge Quick History and Worklog into a unified History view (#8033) 2026-06-05 18:10:56 +02:00
tags test(e2e): add comprehensive E2E test coverage for core features 2026-01-12 15:11:27 +01:00
task-basic test(e2e): fix done-toggle selector to use element instead of class 2026-03-24 16:25:42 +01:00
task-detail test(e2e): update Show/hide task panel button label (#7260) 2026-04-17 22:19:22 +02:00
task-dragdrop test(e2e): fix done-toggle selector to use element instead of class 2026-03-24 16:25:42 +01:00
task-list-basic test(e2e): stabilize finish day history flow 2026-06-06 19:00:50 +02:00
work-view test(e2e): stabilize detail-panel arrow-key nav against focus race 2026-06-05 12:29:08 +02:00
workflow test(e2e): add comprehensive E2E test coverage for core features 2026-01-12 15:11:27 +01:00
worklog test(e2e): fix worklog finish-day navigation race #8033 2026-06-06 20:09:59 +02:00
all-basic-routes-without-error.spec.ts feat(history): merge Quick History and Worklog into a unified History view (#8033) 2026-06-05 18:10:56 +02:00