mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-19 01:17:31 +00:00
* 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>
85 lines
5.5 KiB
Markdown
85 lines
5.5 KiB
Markdown
# Project View
|
|
|
|
A **project** is the primary way to organize tasks in Super Productivity. Every task belongs to exactly one project, making projects the main organizational structure for your work.
|
|
|
|
## What Projects Are
|
|
|
|
Projects are self-contained workspaces. Each project has:
|
|
|
|
- **Task lists** — Active tasks and an optional backlog
|
|
- **Notes** — Project-specific notes
|
|
- **Theme settings** — Custom colors and appearance
|
|
- **Integration settings** — Connections to external issue trackers (like Jira or GitHub)
|
|
|
|
When you switch to a project view, you see only the tasks that belong to that project, along with the project's notes and settings.
|
|
|
|
## Projects Vs Tags
|
|
|
|
Projects and tags serve different organizational purposes:
|
|
|
|
| Aspect | Projects | Tags |
|
|
| ------------------------- | ---------------------------------------------------------------- | --------------------------------------------------- |
|
|
| **Relationship to tasks** | Each task belongs to exactly **one** project (required) | Each task can have **zero or more** tags (optional) |
|
|
| **Purpose** | Primary organizational structure — every task has a home project | Secondary labels for cross-cutting categorization |
|
|
| **Features** | Backlog, notes, theme, integrations | Simple labels for filtering and grouping |
|
|
|
|
A task must always belong to a project. Tags are optional labels you can add to tasks across different projects to create cross-cutting categories.
|
|
|
|
## Project Features
|
|
|
|
**Backlog**: Projects can have a separate backlog list for tasks you're not actively working on. You can move tasks between the active list and the backlog.
|
|
|
|
**Notes**: Each project can have its own notes, separate from task notes.
|
|
|
|
**Theme**: Projects can have custom colors and appearance settings to help you visually distinguish them.
|
|
|
|
**Integrations**: Projects can connect to external issue trackers (like Jira, GitHub, GitLab) to sync tasks and track work.
|
|
|
|
## Organizing Projects
|
|
|
|
Projects themselves are **flat** — there are no parent projects or subprojects. A project cannot contain other projects.
|
|
|
|
However, you can **organize projects into folders** in the navigation menu. Folders can be nested (folders within folders) to create a hierarchy for navigation, but this is purely for organization — it doesn't change how projects work or how tasks are stored.
|
|
|
|
You can move projects between folders without affecting the tasks or any other project data.
|
|
|
|
## Hiding Projects
|
|
|
|
When a project is active but you do not want it in the sidebar project list, enable **Hide project from sidebar** in the project settings or use the Projects visibility menu in the sidebar.
|
|
|
|
Hiding a project changes navigation only. The project stays active, and its tasks can still appear in cross-project task views such as Today, Planner, Schedule, Boards, and Search. Project navigation helpers, including project short syntax, do not match hidden projects.
|
|
|
|
## How Tasks Belong to Projects
|
|
|
|
Every task has a project assignment. When you create a task, it's assigned to the current project (or the Inbox if you're in a tag context without a default project). You can change a task's project by:
|
|
|
|
- Dragging and dropping the task to another project in the navigation
|
|
- Editing the task and selecting a different project
|
|
|
|
When you view a project, you see all tasks that belong to it. When you view a tag, you see tasks from multiple projects that have that tag, and each task shows its project as a badge.
|
|
|
|
## Completing Projects
|
|
|
|
When you finish with a project, you **complete** it to retire it from your active workflow and celebrate the result. Right-click the project in the sidebar (or click the `⋮` button) and choose **Complete project**.
|
|
|
|
If the project still has **unfinished tasks**, you're asked what to do with them first: **Move to Inbox** (the default — they stay actionable), **Mark as done**, or **Cancel**. Before the project is completed, a final confirmation asks you to confirm the action.
|
|
|
|
On completion you get a full-screen **celebration** with a short summary — tasks done, time tracked, days worked, and how many days the project ran from its first worked day to completion (time-based stats are hidden when you don't track time).
|
|
|
|
A completed project becomes fully **dormant** — all its data is preserved, but it stops generating noise across the app. Specifically, it:
|
|
|
|
- Is removed from the sidebar project list and the main visibility checklist
|
|
- Has its tasks hidden from Today, Tags, Boards, Overdue, Deadline, and Scheduled views
|
|
- Has its repeating tasks suspended — no new instances are generated
|
|
- Has its reminders suppressed — no notifications fire for its tasks
|
|
|
|
**Finding and reopening** completed projects: click the eye icon in the Projects section header to open the visibility menu, then navigate to **Archived projects**, where completed projects appear with a trophy badge and their completion date. Use the **Reopen** action there to clear the completed state and bring the project back to the sidebar with all tasks, repeating configs, and reminders active again.
|
|
|
|
## Related
|
|
|
|
- [[4.02-Inbox-View]] — The default project for uncategorized tasks
|
|
- [[4.07-Tag-View]] — How tags provide cross-cutting categorization
|
|
- [[4.05-Board-View]] — Visual column-based organization
|
|
- [[4.01-The-Today-View]] — Today's task list
|
|
- [[4.03-Planner-View]] — Day-level planning across projects
|
|
- [[4.04-Schedule-View]] — Time-based scheduling
|