Commit graph

19995 commits

Author SHA1 Message Date
Johannes Millan
447ae68ff3 test(e2e): open attachment dialog via detail panel after #7314
The attach-dialog entry point moved out of the task context menu in
PR #7314 and now lives in the detail panel. Update the WebDAV sync
attachment test to use openTaskDetailPanel() and click the attachment
input-item instead of right-clicking and looking for an "Attach" menu
item that no longer exists.
2026-04-25 22:36:14 +02:00
Het Savani
f49683c93b
fix:google calendar text exceeding background (#7361)
* fix:google calendar text exceeding background

* Chore: removed comments

* fix(issue-panel): wrap long provider names instead of clipping

Replace the clip-and-tooltip approach with white-space: normal on the
button label so long provider names (e.g. "Google Calendar (iCal)")
wrap to a second line rather than overflowing or being silently cut
off. Also reverts unrelated package-lock, source-map, and lint-comment
changes that snuck into the original PR.

Refs #7331

---------

Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-04-25 16:14:59 +02:00
Artur Martins
d0cb24e770
Change AutoPlan plugin URLs to Codeberg (#7347)
Updated URLs for AutoPlan plugin to point to Codeberg since the github repo https://github.com/00sapo/sp-autoplan was archived on the 22nd Feb 2026.
2026-04-25 15:50:31 +02:00
Spyros Seimenis
b86b74130c
fix(plugins): pass issueNumber into ctx for plugin field mappings (#7360)
The plugin-api contract for fieldMapping.toTaskValue/toIssueValue declares
ctx as { issueId: string; issueNumber?: number }, but the host adapter
was constructing ctx with issueId only at both call sites
(_extractTaskFieldsFromIssueWithSyncValues and _applyFieldMappingPull).

This caused mappings that interpolated ctx.issueNumber, like the GitHub
plugin's title mapping (`#${ctx.issueNumber} ${title}`), to render
"#undefined ..." whenever the title was pulled via the update path.
The initial add-task path was unaffected because titles came pre-formatted
from mapSearchResult.

Reads issue.number from the PluginIssue record at both sites and
includes it in ctx, falling back to undefined when absent so non-GitHub
plugins are unaffected.
2026-04-25 15:45:40 +02:00
Spyros Seimenis
32c138b5f6
feat(github): add "Include pull requests" option to issue provider (#7358)
Adds an advanced checkbox on the GitHub issue provider that, when enabled,
drops the hardcoded `is:issue` filter in both backlog auto-import and
in-provider search. Lets users sync PRs (assigned, authored, or
review-requested) into their backlog with a query like
`state:open is:pull-request (assignee:@me OR review-requested:@me)`.

Default is off, existing behavior is unchanged.
2026-04-25 14:29:08 +02:00
Johannes Millan
de731704ef refactor(task-repeat-cfg): honor getFirstRepeatOccurrence contract (#7355)
The JSDoc promised "first occurrence on or after startDate," but the
implementation anchored on `max(startDate, today)` — so a past startDate
was silently advanced forward. This was the root cause of #7344 and
forced the introduction of `getFirstOccurrenceAnchor` as a call-site
workaround.

- Refactor `getFirstRepeatOccurrence` to always anchor on startDate.
  Drop the unused `today` parameter. Return null when startDate is
  missing instead of silently falling back to 1970-01-01; existing
  callers already handle null.
- Delete `getFirstOccurrenceAnchor` + spec — the workaround is obsolete
  now that the util honors its contract.
- Switch `rescheduleTaskOnRepeatCfgUpdate$` to `getNextRepeatOccurrence`.
  Editing an existing recurring cfg needs the next occurrence from
  today, not retro-anchoring on startDate.
- Rebase deterministic spec tests whose `startDate` was past-dated but
  expected today-anchored results under the old contract. Intent is
  preserved; only the setup now matches the contract explicitly.
2026-04-24 20:05:45 +02:00
Johannes Millan
72e3241b79 fix(task-repeat-cfg): avoid past remindAt when converting timed past-dated task to recurring (#7354)
Follow-up to #7344. When a timed task with a past dueWithTime was converted
to recurring, the preserved first-occurrence date produced a past remindAt
and the reminder module flagged it as overdue, firing a "missed reminder"
popup immediately on save.

Clamp the anchor to today when cfg.startTime is set and the planned day is
past. Non-timed past-day preservation (the #7344 fix) is untouched.
2026-04-24 19:17:32 +02:00
Johannes Millan
ba992d06e5 Merge branch 'feat/issue-7343-fa101e'
* feat/issue-7343-fa101e:
  fix(reminder): show task start time in countdown banner
2026-04-24 18:33:29 +02:00
Johannes Millan
d47d461617 fix(sync): keep Dropbox auth-code input above iOS keyboard
The paste field sat below the "Get Auth Code" button, so on iPhone the
on-screen keyboard covered it and users could not paste the code. After
the user taps the link the field now moves to the top of the dialog and
receives focus synchronously with the click, so iOS opens the keyboard
on a visible target. Adds autocomplete/autocorrect hardening to avoid
mangling pasted tokens.

Fixes discussion #7340.
2026-04-24 18:08:30 +02:00
Johannes Millan
2d20cd29cf refactor(task-repeat-cfg): rename anchor util and trim duplicated comments
- Rename util getAnchorDateForRepeatCfgConversion → getFirstOccurrenceAnchor
  to match neighboring "what it returns" naming convention
  (getEffectiveRepeatStartDate, getNewestPossibleDueDate, etc.).
- Collapse the duplicated multi-line "why anchor" comments at both call
  sites into single-line pointers; full rationale lives in the util JSDoc.
- JSDoc on the util now explicitly documents why rescheduleTaskOnRepeatCfgUpdate$
  does NOT use it (different semantics for cfg edits vs. initial conversion).

No behavior change. Follow-up polish on the #7344 fix.
2026-04-24 17:59:50 +02:00
Johannes Millan
0e6df712e8 fix(reminder): show task start time in countdown banner
The "X starts at {{start}}" countdown banner was rendering remindAt (the
reminder fire time, e.g. 2:30 for a 30-min-before reminder) instead of
the task's actual scheduled start (dueWithTime, e.g. 3:00), confusing
users about when the task actually begins. Fall back to remindAt for
reminder-only tasks that have no dueWithTime.

Also extend the "leave banner as is" early-return guard to include
dueWithTime, otherwise rescheduling a task while keeping the same
reminder offset would leave a stale time in the banner.

Closes #7343
2026-04-24 17:58:31 +02:00
Johannes Millan
b94b6c4e78 fix(task-repeat-cfg): preserve planned date when converting past-dated task to recurring (#7344)
When an existing task with a past dueDay was converted to a recurring
task, both conversion effects called getFirstRepeatOccurrence with
`new Date()` as the anchor, which silently advanced the first occurrence
to the next future date matching the recurrence pattern — shifting the
task out of the user's planned date without consent.

Anchor on the task's existing dueDay only when it equals cfg.startDate
(the dialog default). If the user overrode startDate, fall back to
today — preserving existing behavior for all other flows.
2026-04-24 17:57:38 +02:00
Johannes Millan
672821e0a5 feat(focus-mode): enlarge timer and reorganize Pomodoro controls
Closes #7348.

Sizing
- Clock face scales 175/225/280/110px across breakpoints; digits scale
  44/58/72/28px via --clock-time-size. Tokens hoisted to .clock-display.
- Breathing dot (flow mode) tracks the clock face at 80% of diameter.
- Duration slider fills the clock face in countdown prep and its
  editable text scales with the circle (~16% of diameter).
- tabular-nums + line-height: 1 on .clock-time so countdown digits
  don't shift width/metrics while ticking.

Layout
- Pomodoro settings cog moved inside the clock face, centered at
  bottom; fades in on hover like the other in-circle controls.
- --control-offset derived from clock-face/clock-time/icon-button so
  pause/done icons sit ~4px from the digits at every breakpoint,
  clamping to --s on the smallest circle.
- Duration slider hides the underlying clock digits via an
  is-duration-slider-active class bound from isShowDurationSlider().

Component API
- input-duration-slider now accepts --input-duration-slider-size and
  --input-duration-slider-value-size (defaults 130px / 16px preserved
  via var() fallbacks — the three existing dialog consumers are
  unchanged).
- breathing-dot accepts --breathing-dot-size (default 144px preserved).
2026-04-24 17:41:00 +02:00
Johannes Millan
ce98b0d0fe fix(api): support subtask creation via local REST API
POST /tasks now accepts `parentId` and creates a subtask via
TaskService.addSubTaskTo (inheriting the parent's projectId). Invalid
parent references return 404 PARENT_NOT_FOUND; nested parents return
400 INVALID_PARENT. `subTaskIds` on POST and `parentId`/`subTaskIds`
on PATCH are now rejected with 400 UNSUPPORTED_FIELD instead of being
silently dropped.

Fixes #7346
2026-04-24 17:40:28 +02:00
Johannes Millan
2f84f76b17 fix(i18n): reframe sync repair dialog as routine cleanup (#7330)
The existing copy ("Data Damage Detected" / "Your data appears to be
damaged" / "This should not normally happen") implied a defect when the
dialog actually surfaces routine Checkpoint-D cleanup of orphan
references after Last-Write-Wins resolution of concurrent cross-device
edits — working as designed, just communicated as a bug.

Reword D_DATA_REPAIR_CONFIRM, D_DATA_REPAIRED, and the DATA_REPAIRED
toast to describe the behavior accurately while preserving the "report
if frequent" invitation. No behavioral change.
2026-04-24 17:40:28 +02:00
Johannes Millan
c590447d00 test(validation): add cross-model invariant repro harness for #7330
Catalogs the six post-sync corruption shapes that can trigger the
"data was automatically corrected" alert. Calls isRelatedModelDataValid
directly to bypass Typia and isolate cross-model checks.
2026-04-24 17:40:28 +02:00
Johannes Millan
6b364bb6a1 test(e2e): silence ERR_FAILED noise from intentional JS aborts
Attach the console listener in the legacy-migration helper after
page.unroute('**/*.js') so the benign `Failed to load resource:
net::ERR_FAILED` messages from the intentional seeding-phase aborts
stop surfacing as console errors. pageerror stays attached early
since no JS runs while bundle loads are aborted.
2026-04-24 17:40:28 +02:00
Johannes Millan
40a7f9e849 fix(snap): set TMPDIR to XDG_RUNTIME_DIR for tray icon on Ubuntu 24.04+
Chromium's StatusIconLinuxDbus writes the tray PNG to TMPDIR and hands
gnome-shell the path via DBus IconThemePath. Inside a strict snap, /tmp
is bind-mounted to /tmp/snap-private-tmp/snap.<name>/tmp with mode 0700
on the parent, so gnome-shell (outside the mount namespace) cannot
stat the file and falls back to the three-dot placeholder icon.
Redirecting TMPDIR to \$XDG_RUNTIME_DIR (/run/user/\$UID) puts the
write where both sides can read it. Matches Signal Desktop's snap and
the official Electron snapcraft guide. electron-builder's SnapOptions
docstring still claims this is a default but app-builder-lib 26.8.1's
snap target no longer injects it.

Refs #7298
2026-04-24 17:40:28 +02:00
Johannes Millan
ede02dd12a fix(simple-counter): widen calcCountdownProgress to accept null remaining
Template binds `countdownTime$ | async` which is `number | null`. The
function already tolerated `null` for `total`; widen `remaining` the
same way so strict template type checking passes. Also pick up the
prettier indentation fix on the progress-circle element.
2026-04-24 17:40:28 +02:00
Johannes Millan
8b5714ab2f test(reminder): use real Observable for cfg$ in non-native mock
The non-native describe block stubbed cfg$ as { subscribe: () => {} }
which worked while the effect only subscribed to cfg$ lazily. After
6614dfa4 introduced a _reminderCfg$ class-field initializer that pipes
on cfg$ at construction time, the stub fails with
"cfg$.pipe is not a function" for all 4 non-native tests.

Swap to NEVER — a real Observable that never emits. Matches the
stub's intent and has .pipe().
2026-04-24 17:40:28 +02:00
Johannes Millan
f5424443da refactor(reminder): narrow mobile effects to reminder cfg slice
Follow-up to 6466ddc0 addressing multi-agent review feedback.

- Extract _reminderCfg$ = cfg$.pipe(map(c => c?.reminder),
  distinctUntilChanged()). Both scheduling effects now subscribe to
  this narrow slice, so unrelated global-config edits (theme, sync,
  pomodoro, etc.) no longer trigger reschedule loops and serial
  native bridge calls.
- Tighten the flip-disabled test to assert the notification IDs
  passed to cancelReminder, not just the call count.
- Replace `any` in the new spec blocks with ReminderConfig,
  TaskWithReminder, and a small TestCfg shape.
- Update SYNC-SAFE JSDoc to note the cfg reactivity.

Refs #6980
2026-04-24 17:40:28 +02:00
Johannes Millan
62b87f2431 fix(reminder): respect disableReminders on mobile native scheduling
The "Disable all reminders" setting was only enforced at the in-app
dialog layer. On Android, native notifications scheduled via
AlarmManager kept firing regardless of the toggle, because the mobile
NgRx effects did not observe the config.

- scheduleNotifications$ now subscribes to cfg$ and, when
  disableReminders is true, cancels every tracked reminder and
  short-circuits further scheduling.
- scheduleDueDateNotifications$ extends its existing disabled branch
  to respect the master disableReminders flag in addition to
  notifyOnDueDate.

Known remaining gap: SyncReminderWorker (WorkManager) can still
schedule reminders from the server every 15 min for users with
background sync configured. Tracked as a follow-up.

Refs #6980
2026-04-24 17:40:28 +02:00
Johannes Millan
632debb30d fix(i18n): remove stale 'Dropbox' references from conflict dialog
The sync conflict dialog title and body still hardcoded "Dropbox" in 9
locales even when users sync via WebDAV, making the prompt misleading.
English was corrected in 2020 but the translations never got updated.

Fixes #7339
2026-04-24 17:40:28 +02:00
Johannes Millan
9488a7984b fix(main-header): adjust sync icon size
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 17:40:28 +02:00
Johannes Millan
c81c0b941d test: restore karma coverage reporter setup 2026-04-24 17:40:28 +02:00
Johannes Millan
404aed6384 fix(simple-counter): preserve paused countdown state
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 17:40:28 +02:00
Johannes Millan
495d0eaf3d fix(simple-counter): keep paused countdown time visible
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 17:40:28 +02:00
David Vornholt
0988c2d8c6
Add ext-idle-notify backend for Wayland idle detection (#7337)
* fix(electron): support Wayland idle detection via ext-idle-notify

* fix(electron): address wayland idle helper review

---------

Co-authored-by: Johannes Millan <johannesjo@users.noreply.github.com>
2026-04-24 16:50:24 +02:00
aakhter
8d8d63d231
feat(plugins): add reInitData API (#7305)
* feat: add plugin data reinit api

* test(plugins): stub data init in bridge spec

* test(plugins): stub data init in counter bridge spec

---------

Co-authored-by: Aamer Akhter <aamer_akhter@users.noreply.bitbucket.org>
2026-04-23 22:14:16 +02:00
Jaydeep pipaliya
42c6674456
fix(tasks): use attachment title as task name when pasting a bare URL (#7059)
* fix(tasks): use attachment title as task name when pasting a bare URL

When a URL is pasted as the only input and urlBehavior is 'extract',
the URL is removed from the title and moved to an attachment, leaving
the task title blank. Now, if the title is empty after URL extraction,
the first attachment's title (derived from the URL basename) is used
as the task name.

Fixes #6444

* test(tasks): add unit test for bare URL paste using attachment title

* fix(tasks): handle duplicate URL edge case and add root URL test

Address review feedback:
- Use allUrls[0] instead of newAttachments[0] so the fallback
  fires even when the pasted URL already exists as an attachment
  (newAttachments is filtered by filterDuplicateUrlAttachments)
- Remove redundant .trim() — removeUrlsFromTitle already trims
- Add test for root URL (https://example.com) → domain basename 'example'
2026-04-23 21:54:14 +02:00
Johannes Millan
e72aa77336
Add Electron packaging smoke test to catch missing dependencies (#7336)
* ci(electron): verify packaged app.asar and smoke-test launch

Adds coverage for the gap that let #7320 ship: CI built electron TS
and ran electron-builder on release, but never exercised the packaged
binary. A stray relative import reaching out of electron/** was
compiled by tsc into src/app/util/*.js, fell outside the files glob
in electron-builder.yaml, and was missing from app.asar — the main
process crashed on launch for every 18.2.7 user.

Two new checks, both running on ubuntu-latest:

1. tools/verify-electron-requires.js extracts app.asar and re-resolves
   every relative require() under electron/** against the packaged
   tree. Runs on every PR that touches electron/**, electron-builder
   config, package.json, or the workflow itself. ~10s after the build.

2. A launch-under-xvfb smoke test starts the packaged binary, waits
   30s for a crash, and greps stderr for 'Cannot find module' /
   'Uncaught Exception'. Runs on push to master, release tags, and
   manual dispatch (not every PR — needs xvfb setup + idle wait).

Exposed as npm run electron:verify-asar for local use after dist.

* ci(electron): harden smoke workflow and verify script from review

Review feedback on the previous commit surfaced a handful of real
defects. Addressed here:

verify-electron-requires.js
- Guard resolved paths against escaping the extracted asar tree.
  Without this, a relative require with enough `..` climbs above the
  temp dir and Node's resolver hits the host filesystem, masking a
  genuinely missing module behind a stray file on the CI runner.
- Walk .cjs and .mjs files too. electron/simple-store.test.cjs ships
  via the `electron/**/*` glob and was being skipped.
- Move cleanup out of the path where `process.exit(1)` runs inside a
  `try/finally` (synchronous exit does not guarantee `finally`), so
  the temp dir is reliably removed.
- Code comment noting that esbuild-bundled preload.js is opaque to
  this walker — that coverage comes from the launch smoke test.

electron-smoke.yml
- Broaden pull_request paths filter. The previous filter only matched
  electron/**, which is exactly the wrong answer: #7320's root cause
  was a file under src/app/util/ that electron/ imported. Now uses a
  negative list — docs, translations, android/, ios/, other workflows.
- Explicit Xvfb screen geometry (`-screen 0 1280x720x24`) and
  `--disable-dev-shm-usage` on the Electron side. Both known CI
  flake preventers.
- Expanded crash-marker regex: SIGSEGV, Segmentation fault,
  TypeError:, ReferenceError:, FATAL ERROR, Check failed. Also scan
  the log during the liveness loop, not just at the end — catches
  main-process exceptions that get logged without killing the
  process.
- Move the launch log under .tmp/smoke/ so upload-artifact@v7 reliably
  finds it on failure; /tmp paths are fragile across runner images.
- Sweep stray electron worker processes on teardown via pkill.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 21:53:28 +02:00
SettingDust
e5aec5ceb2
feat: display cycle mode when interval is one (#7329)
fix #7328
2026-04-23 14:14:04 +02:00
dependabot[bot]
94842d6123
chore(deps): bump the npm_and_yarn group across 1 directory with 3 updates (#7332)
Bumps the npm_and_yarn group with 3 updates in the /packages/plugin-dev directory: [lodash](https://github.com/lodash/lodash), [picomatch](https://github.com/micromatch/picomatch) and [rollup](https://github.com/rollup/rollup).


Updates `lodash` from 4.17.21 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.18.1)

Updates `picomatch` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/4.0.3...4.0.4)

Updates `rollup` from 4.44.0 to 4.60.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.44.0...v4.60.2)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 4.0.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: rollup
  dependency-version: 4.60.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-23 13:54:43 +02:00
Benjamin
9e308db7a9
feat: add Super Productivity MCP to community plugins (#7335) 2026-04-23 13:04:53 +02:00
Johannes Millan
cc81e513d8
refactor(tasks): reduce detail panel and context menu overlap (#7314)
* refactor(tasks): reduce detail panel and context menu overlap

Move noun-style edits (estimate dialog, deadline dialog, attachment dialog)
out of the task context menu and expose them only through the detail panel,
keeping the menu focused on state-change verbs. Add quick-schedule chips
(Today/Tomorrow/Next Week), an unschedule button, and a remove-deadline
affordance directly on the detail panel so panel users keep the same
fast paths the menu offered.

https://claude.ai/code/session_01JZT5LytmhbkXLA1PKkXnz2

* fix(tasks): revert quick-schedule chips on detail panel due-date row

The inline Today/Tomorrow/Next Week chips intercepted clicks on the
schedule row, breaking the existing "click row to open schedule dialog"
interaction (covered by the reminders-default-task-remind-option e2e test),
and the quick-schedule handlers were missing the edge-case logic from the
context menu's _schedule() (time preservation when rescheduling a
dueWithTime task, already-planned guard). Keep the × unschedule and ×
remove-deadline affordances which only render when the relevant state is
set and don't interfere with the unscheduled-task click path.

https://claude.ai/code/session_01JZT5LytmhbkXLA1PKkXnz2

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-22 20:20:31 +02:00
Serif
5935319646
feat(i18n): add sorting options and error messages in Turkish translations (#7321) 2026-04-22 17:37:00 +02:00
Johannes Millan
3d40f56aae 18.2.8 2026-04-22 16:19:14 +02:00
Johannes Millan
8b147daaf4 Merge branch 'feat/issue-7324-0317f4'
* feat/issue-7324-0317f4:
  fix(jira): warn web users about browser limitations
2026-04-22 16:18:06 +02:00
Johannes Millan
f6b2742ac0 fix(jira): warn web users about browser limitations 2026-04-22 16:16:03 +02:00
Johannes Millan
99b7dee74a fix(backup): move shared timestamp util into electron/ for snap packaging
electron/backup.ts imported getBackupTimestamp from src/app/util/, which
tsc compiled alongside the source. electron-builder only packages
electron/** and the Angular renderer bundle, so the compiled util was
missing from app.asar and the main process crashed on launch with
"Cannot find module '../src/app/util/get-backup-timestamp'".

Moved the util to electron/shared-with-frontend/ (existing convention
for code shared between main and renderer), updated all importers.

Fixes #7270
Refs #7315, #7323, #7265, #7320
2026-04-22 16:14:56 +02:00
Johannes Millan
6e6fef101c 18.2.7 2026-04-21 22:37:49 +02:00
Johannes Millan
99fda61fb9 fix(schedule): correct today button translation key namespace 2026-04-21 22:33:22 +02:00
Johannes Millan
54a0931f10 18.2.6 2026-04-21 22:27:37 +02:00
Johannes Millan
226ee90cec test(reminders): stub dialog template to avoid rendering child deps
Zoneless change detection runs on TestBed.createComponent, so the
template's pipes (DateTimeFormatService → DateAdapter) and child
components (tag-list → WorkContextService → LOCAL_ACTIONS → Actions)
all get instantiated — none of which the ngOnDestroy tests need.
The previous WorkContextService stub only covered one branch.

Override the component template with an empty string before
compileComponents (same pattern as dialog-edit-task-repeat-cfg.spec)
so only the component class is exercised.
2026-04-21 22:20:36 +02:00
Johannes Millan
0dc6a26d6a test(reminders): stub WorkContextService to fix ScannedActionsSubject error
The new "destroy clears unhandled deadline reminders" describe block
creates DialogViewTaskRemindersComponent via TestBed.createComponent.
The dialog's standalone imports include TagListComponent, which injects
WorkContextService, which injects LOCAL_ACTIONS, whose factory calls
inject(Actions) — and Actions needs ScannedActionsSubject, which
provideMockStore does not provide. Result: NG0201 thrown in afterAll
once per test in the describe, aborting the suite at ~5228/8662.

Stub WorkContextService with just the activeWorkContextTypeAndId\$
stream TagListComponent reads, so the real constructor (and its
LOCAL_ACTIONS → Actions chain) never runs.
2026-04-21 22:09:55 +02:00
Johannes Millan
9c7cb7c3eb test(reminders): move TestBed.inject after overrideProvider
Calling TestBed.inject(MockStore) in beforeEach instantiated the test
module, so the subsequent TestBed.overrideProvider(MAT_DIALOG_DATA) in
createComponent threw "Cannot override provider when the test module
has already been instantiated" across all 7 destroy-cleanup specs.

Move the store inject and dispatch spy into createComponent, after the
override runs, so TestBed stays un-instantiated until the per-test
MAT_DIALOG_DATA value is registered.
2026-04-21 22:02:03 +02:00
Johannes Millan
30550efa34 chore(deps): sync plugin lockfiles after vite-plugin bump to ^6.4.2
packages/vite-plugin/package.json was bumped to vite ^6.4.2 in
4b9bd23e56 but dependent plugin lockfiles still referenced ^6.0.0.
npm install during the e2e build brought them in sync and hoisted
vitest's nested esbuild binaries, dropping ~500 redundant entries.
2026-04-21 21:50:53 +02:00
Johannes Millan
ff523fd4dd refactor(reminders): address code review feedback for deadline fixes
- Document intent of editReminder cancel path: when user cancels the
  inner schedule dialog, the outer dialog closes and the destroy handler
  clears deadlineRemindAt (same as ESC). Prevents the worker from
  re-firing the past-due reminder.
- Replace page.waitForTimeout with expect().not.toBeVisible({timeout}) in
  the deadline ESC e2e test so it fails fast if the dialog reappears and
  complies with the "no waitForTimeout" rule in e2e/CLAUDE.md.
- Capture realTodayStr per test in is-deadline-approaching spec to close
  the (narrow) midnight-rollover flake window.
2026-04-21 21:50:52 +02:00
Johannes Millan
c9694cc58f fix(tasks): make amber deadline badge actually render
.deadline-btn.mat-accent mat-icon needed the same \!important override
as .mat-warn mat-icon to beat Material's icon color variable. Without
it, setting color="accent" on the button left the icon in its default
neutral color.
2026-04-21 21:50:52 +02:00
Johannes Millan
944d003775 feat(tasks): color deadline badge amber within 2 days of the deadline
The deadline feature spec (issue #4328) promised green/amber/red urgency
tiers, but only the overdue-red state was implemented. Preview testers
flagged the missing intermediate tier.

Adds isDeadlineApproaching(task, todayStr) returning true when an active,
non-overdue deadline is within two calendar days. Task row badge now
shows 'warn' (red) when overdue, 'accent' (amber) when approaching, and
default color otherwise.
2026-04-21 21:50:52 +02:00