* feat(history): merge Quick History and Worklog into one view
- single /history route; legacy worklog & quick-history redirect to it
- one History menu entry; navigate-to-task targets history
- show per-day work start-end as its own column in the day table
- show enabled habits/simple-counters (icon + tooltip) in the expanded day
- extract HistoryTaskRowComponent for archived task rows
- remove WorklogComponent and QuickHistoryComponent
* fix(history): restore quick history behavior
* refactor(history): collapse to a single unified view
- remove the Worklog/Quick History toggle; show one full all-time
history tree (year -> month -> week -> day)
- drop the quick-history mode, significance filter and ?view= param
- extract shared HistoryDayMetaComponent; reuse HistoryTaskRowComponent
in the Daily Summary "This Week" widget
- convert project colors + dateStr auto-expand to signals
- update e2e specs to the single-view selectors
* refactor(history): remove dead quick-history code and i18n keys
After merging Quick History into the unified History view, the
quick-history data pipeline had no consumers. Remove it:
- _quickHistoryData$, quickHistoryWeeks$, _loadQuickHistoryForWorkContext
- map-archive-to-worklog-weeks util (+ tz spec) and WorklogYearsWithWeeks
- orphaned F.QUICK_HISTORY, MH.QUICK_HISTORY, MH.WORKLOG translation keys
Keep WorklogWeekSimple (base of the still-used WorklogWeek).
* docs(history): reflect Quick History merge into unified History
The quick-history and worklog routes are now legacy aliases for the
single unified History view; drop the stale 'current-year mode/toggle'
framing. Concepts-note prose flagged for human review.
* feat(history): improve a11y and i18n of the unified history view
Accessibility:
- week table uses <thead> + <th scope=col>; icon-only work-times
header gets a translated aria-label
- expandable day row: real <button> disclosure control carrying
aria-expanded/aria-controls (keyboard + AT path) instead of a
role=button <tr>; row keeps a pointer click
- archived-task title is now a <button> (was a non-focusable <span>)
i18n: translate previously hardcoded aria-labels (export/restore) and
the week-range tooltip; reuse VIEW_TASK_DETAILS/RESTORE_TASK_FROM_ARCHIVE,
add EXPORT_DATA + WEEK_RANGE_TOOLTIP keys.
Polish (carried in): convert worklogData$ to a toSignal signal (drop
AsyncPipe + dead animations), relocate the shared row to
features/worklog/worklog-task-row (WorklogTaskRowComponent) to break a
worklog<->history cycle, make template-unused services private, tighten
projectColor input type. SCSS focus rings use focus-ring tokens.
e2e: locate task titles via td.title button after the span->button change.
Replace waitForLoadState('networkidle') with waitForAppReady() after
post-import page reloads and before task count verification. networkidle
only waits for network silence but not Angular bootstrap, loading
screens, or blocking dialogs - causing intermittent failures under load.
- Replace page.reload() with page.goto() pattern throughout sync tests
(service workers cause reload timeouts)
- Add handling for "Encryption Settings Will Change" dialog in import tests
- Add page reload after sync to ensure archive data is loaded in worklog test
- Add skipSpinnerCheck option for cases where sync may have already completed
- Improve waits and selectors in error-handling tests for better stability
- Make snackbar visibility check non-blocking in wrong-password test
Reduces flaky tests from 6 to 1 in supersync test suite.
- Fix import-sync merge test: use goto instead of reload for reliability
with service workers
- Fix tag management test: add proper menu close handling with multiple
Escape presses and fallback click to dismiss overlays
- Fix encryption enable/disable tests: use fresh client contexts instead
of reconfiguring existing clients to avoid clean slate conflicts
- Fix encryption password change tests: correct wait states and method
names
- Add encryption password field to SuperSync form with e2e class selector
Problem: When a new client (C) joined via file-based sync (Dropbox/WebDAV/
LocalFile), it created a SYNC_IMPORT operation with "clean slate" semantics.
Tasks created by other clients (B) after C joined but before syncing had
vector clocks CONCURRENT with C's SYNC_IMPORT, causing them to be filtered
by SyncImportFilterService - resulting in data loss.
Solution:
- Add `createSyncImportOp` parameter to `hydrateFromRemoteSync()` (default true)
- File-based sync bootstrap passes `false`, skipping SYNC_IMPORT creation
while still updating vector clock, saving state cache, and dispatching loadAllData
- Add confirmation dialog when clients with unsynced local ops receive a snapshot
- Add comprehensive E2E tests for concurrent task creation after late join
The fix preserves SYNC_IMPORT for explicit "use local/remote" conflict resolution
while preventing the bug in file-based sync bootstrap flows.
Test coverage:
- 30 unit tests for sync-hydration.service (createSyncImportOp scenarios)
- 5 E2E tests in webdav-provider-switch.spec.ts:
- "should sync tasks when Client B connects to existing WebDAV server"
- "should sync concurrent tasks created after late join (CRITICAL BUG FIX)"
- "should sync three clients with late joiner (original bug report scenario)"
- "should handle bidirectional sync after provider switch"
- "should replace multiple local ops when user accepts confirmation"
- Tags test: Use right-click context menu approach instead of 'g' shortcut
to avoid typing into editable task title
- Tags test: Add robust dismissAllOverlays helper with multiple Escape
presses and backdrop click fallback
- Late-join test: Improve conflict dialog handling with retry loop and
wait for dialog to close
- Late-join test: Add extra sync cycle after conflict resolution for
more reliable data propagation
- All files: Fix incorrect port in warning messages (1900 -> 1901)
All 48 supersync e2e tests pass consistently.