Skip to content

feat(completion): explicit completion-date control menu#12

Open
renatomen wants to merge 10 commits into
feat/completion-occurrence-context-skip-fixfrom
feat/completion-date-control-menu
Open

feat(completion): explicit completion-date control menu#12
renatomen wants to merge 10 commits into
feat/completion-occurrence-context-skip-fixfrom
feat/completion-date-control-menu

Conversation

@renatomen

Copy link
Copy Markdown
Owner

Summary

PR2 of the completion-date-control work — stacked on #11 (base is the PR1 branch; it will retarget to main once #11 merges).

Lets users choose, per action, which date a task's completion/skip is recorded against — without opening the task.

Core (U3–U6)

  • U3resolveCompletionDate(task, mode, ctx) resolves today / asScheduled / onDue / onPicked to a concrete date, reporting unavailability instead of substituting (so options can be disabled). asScheduled prefers the clicked occurrence and never falls through to due; completion-anchored recurrences resolve asScheduled to scheduled (explicit re-anchor).
  • U4 — Optional completionDate threaded through the status pipeline so non-recurring completions record a chosen completedDate (default today; recurring unaffected by the existing status && !recurring guard).
  • U5 — Four explicit completion actions with a per-type un-complete affordance (recurring: per-date toggle; non-recurring: single "Mark incomplete" reverting to the default status). Unavailable modes render disabled with a reason tooltip.
  • U6 — "Completed on (pick date)" opens the shared date picker; completion is recorded date-only.

Refinements

  • Actions nested under a "Mark complete or skip" submenu ("Mark complete" for non-recurring), with an appearance setting "Group complete and skip actions in a submenu" (default on) to switch to a flat, divider-delimited menu.
  • Uniform sentence-case labels: Completed today / on schedule / on due date / on (pick date), Skip instance, Mark incomplete.
  • Rescheduling a recurring task reactivates occurrences: clears every completed/skipped instance dated on or after the new scheduled date (handles accidental off-schedule completions), preserving older history.
  • Confirmation dialog before a reschedule clears recorded instances (lists the dates; cancel aborts the reschedule). Covers the context-menu and scheduled-date-modal paths.

Testing

  • New/updated unit suites: completionDateResolver, taskContextMenu.complete, taskContextMenu.pickDate, TaskService (completedDate threading, reschedule-clear, confirm callback), issue-1724. All green; typecheck, lint (ts/architecture), build green.

Known follow-ups (not blocking)

  • i18n: new contextMenus.task.completion.* and the appearance-setting keys are English-only; the 8 other locales fall back to English (tracked in i18n.state.json).
  • Calendar drag/resize reschedule still clears instances silently (no confirmation) — deferred (needs drag-revert-on-cancel handling).
  • HTTP/CLI/MCP exposure of the non-recurring completion-date parameter is deferred.

renatomen added 10 commits July 4, 2026 15:38
U3: resolveCompletionDate(task, mode, ctx) maps today/asScheduled/onDue/onPicked
to a concrete date, reporting unavailability (via i18n reason key) instead of
substituting a fallback so the menu can disable an option. asScheduled prefers
the clicked occurrence and never falls through to due; completion-anchored
recurrences resolve asScheduled to scheduled (explicit re-anchor).
…line

U4: updateProperty/updateTaskProperty accept an optional completionDate that
overrides the getCompletionDateForTask default (task.occurrence_date || today)
for non-recurring status completions. Parameter is optional so all existing
status writes and the status-icon path are unchanged; recurring tasks remain
unaffected by the existing property===status && !recurring guard. External
HTTP/CLI/MCP exposure deferred (Scope Boundaries).
U5: replace the single recurring 'Mark complete for this date' item with four
explicit actions (Complete today / as scheduled / on due date / on…), and add
them net-new for non-recurring tasks. Each resolves via the four-mode resolver
and dispatches the native mechanism: recurring -> toggleRecurringTaskComplete;
non-recurring -> a status change carrying the completion date. Unavailable
modes render disabled with a reason tooltip (R4). Un-complete is per task type
(R11): recurring toggles the resolved date out per action; a completed
non-recurring task collapses to one 'Mark incomplete' that reverts to the
default status. Recurring labels carry an (Instance) prefix.

U6: 'Complete on…' opens the shared date picker and records the chosen date
(date-only, matching completedDate/complete_instances storage); cancel is a
no-op.

Adds contextMenus.task.completion i18n keys (en); other locales fall back to
English pending translation (tracked in i18n.state.json).
Non-recurring completion/un-completion now surface a neutral failure notice
(the old key wrongly said 'recurring task'). Adds tests for the no-completed-
status guard, recurring 'Complete on due date' dispatch, and non-recurring
'Complete today' dispatch.
Local runs in non-UTC timezones (e.g. Pacific/Auckland, UTC+13) fail date/
day-boundary tests that pass on CI (GitHub Actions runs in UTC). Set
process.env.TZ='UTC' before the config is exported so Jest's worker processes
inherit it and read the zone at startup, before any Date is constructed.
Overridable via 'TZ=... jest' for timezone-specific testing.
…le reactivates occurrence

Post-review UX refinements:
- Nest the four completion actions (and, for recurring, skip) under a single
  'Complete or skip' submenu ('Complete' for non-recurring) to keep the
  top-level menu compact; the occurrence note stays top-level.
- Uniform sentence-case labels for both task types (no per-type qualifier):
  Completed today / Completed on schedule / Completed on due date /
  Completed on (pick date); Skip instance; Mark incomplete.
- Rescheduling a recurring task onto a date removes that date from
  complete_instances/skipped_instances, so moving the scheduled date back
  reactivates the occurrence without manually editing those lists.
…dule clears instances on or after the new date

- Rename the submenu 'Complete or skip' -> 'Mark complete or skip' ('Complete'
  -> 'Mark complete' for the non-recurring variant).
- Rescheduling a recurring task now drops every complete_instances/
  skipped_instances entry on or after the new scheduled date (was: exact-date
  match only), so an accidental off-schedule completion recorded on a later date
  is also cleared when moving the schedule back. Entries strictly before the new
  date are preserved as genuine history.
Rescheduling a recurring task that would clear completed/skipped instances (on
or after the new date) now prompts for confirmation, listing the affected dates;
declining aborts the reschedule entirely (nothing is written). Threaded via an
optional confirmClearInstances callback on updateProperty: the context-menu and
scheduled-date-modal paths supply a ConfirmationModal; the headless API (and any
silent update) omit it and keep the prior behavior. Calendar drag still clears
silently (revert-on-cancel deferred).
… menu

Adds an appearance setting 'Group complete and skip actions in a submenu'
(default on = current submenu behavior). When off, the completion actions (and
skip for recurring tasks) render directly in the task context menu instead of
nested under 'Mark complete or skip'. Read via settings.completionMenuAsSubmenu,
defaulting to the submenu when unset (backward compatible).
In flat menu mode (submenu setting off), wrap the completion/skip actions with a
separator before and after so the block is visually delimited from the rest of
the task context menu.
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.

1 participant