Skip to content

Develop - #82

Merged
bladehell-ai merged 22 commits into
mainfrom
develop
Aug 21, 2026
Merged

Develop#82
bladehell-ai merged 22 commits into
mainfrom
develop

Conversation

@bladehell-ai

Copy link
Copy Markdown
Collaborator

No description provided.

abdulhaseeb-qa and others added 22 commits August 19, 2026 11:54
Add minDate/maxDate props to shared DatePicker component and apply
constraints to all 12 date range instances: "to" date cannot precede
"from" date, and "to" auto-resets when "from" moves past it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… filters

- Filters collapsed by default on time entries page (open on click)
- Manual time entry form requires project, date, start/end time with
  inline error messages (manual validation replaces zodResolver)
- Clicking a table row opens view modal with disabled fields
- View modal has Edit button to switch to editable mode
- Cancel in edit mode returns to view without closing dialog
- Added useUpdateManualEntry hook for editing existing entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… footer

- Checkboxes on every row for users with delete or approve permissions
- Header checkbox selects/deselects all visible entries
- Delete button (destructive) appears when entries are selected
- Approve button only shows count of selected pending entries
- Edit button moved from dialog header to footer next to Close

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Date pickers default to empty (show all entries)
- Date filtering only applied when user explicitly picks dates
- Clear filters button also resets date range
- Date range counts toward active filter badge

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Removed Late column (header + cell) from attendance table
- Removed Late Days stats card from summary strip
- Cleaned up unused Clock icon and formatMinutes imports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Give Status column 40% width to absorb the extra space left by the
removed Late column, keeping the remaining columns compact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single w-[40%] on Status with explicit percentage widths on all
columns so gaps are uniform after removing the Late column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b5092e3 removed the Late column and the Late Days tile outright, so the
owner and HR lost them too. Put both back behind a `canSeeLate` role check
(`owner` / `hr_manager`) instead, so the people who need the signal keep it
and employees do not see their own lateness.

Also closes a gap the removal left open: the Late badge on the Status cell
still rendered "Late 15m" to everyone, one cell over from the column that
had just been taken away. It now rides the same gate, as does the Late Days
stat tile. All three late surfaces are gated together — hiding one alone
leaks the same number from the others.

Details:
- The stat strip's column count follows the tile count (lg:grid-cols-5 ->
  lg:grid-cols-4) so the grid does not leave a hole; the skeleton count
  matches so the strip does not reflow on load.
- 1c0ae90's `w-[40%]` on Status exists only to absorb the space the Late
  column would occupy, so it now applies only when that column is absent.
- Restores the `Clock` and `formatMinutes` imports, and puts the `Tooltip`
  imports back to use — they were left unused by the removal and were
  raising three no-unused-vars warnings.

Deliberately a role check rather than the `attendance.view_all` permission:
that permission is also held by org_manager and finance_manager, who must
not see it. Trade-off — a custom org role will not match; a dedicated
`attendance.view_late` permission is the configurable version if wanted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drops hr_manager from `canSeeLate`, so the Late column, the Late badge on
the Status cell and the Late Days stat tile are now visible to the owner
and to no one else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-only

feat(hr): show attendance lateness to the owner only
…provements

- Add week navigation (prev/next arrows) to Weekly Progress card and chart
- Smooth chart transitions with keepPreviousData instead of flash on refetch
- Fix task names not displaying in time entries listing (task.title → task.name)
- Replace task dropdown with free-text input for employee-defined tasks
- Add text truncation with ellipsis for long task names
- Fix task not updating when editing manual time entry (task_name support in update)
- Only reset approval status on time changes, not task/notes edits
- Support overnight (cross-midnight) time entries in manual entry form

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Restrict employee to only see their own department (not full org tree)
- Allow employees to delete own pending/rejected manual time entries
- Allow leave applications up to 7 days in the past
- Simplify leave balance display to show only remaining days
- Show "Half day" instead of 0.5 in leave listings
- Fix attendance month filter showing number instead of month name
- Add calendar icon to attendance month filter
- Fix Docker dev server performance (health check interval, .env corrections)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Conflict: the attendance table header. This branch gave every column an
explicit width; develop restored the Late column behind the owner-only
`canSeeLate` gate. Kept both — Late is back and still gated, and it carries
a width like every other column. Dropped develop's 40%-on-Status hack, which
only existed to absorb the missing Late column's space and is redundant now
that all columns are sized.
`tasks` has only ever had a `name` column, and TimeEntryController serializes
the model directly, so `task.title` was always undefined. Three files declared
it anyway: time/page.tsx typed `title` while reading `name`, which failed the
build, and dashboard/page.tsx read `title` alone, so the timesheet rendered
"No task" for every entry that had one.
fix: employee role restrictions, leave/attendance UI improvements
… local one

Screenshots stopped reaching the dashboard while time sync stayed healthy. On
2026-08-20 seventeen users tracked 5-8 hours each and three had any screenshot
at all. Live uploads (arrival within 60s of capture) went 1627/1639 on Aug 12
to 0/835 on Aug 18 -- the offline-first rollout landed on Aug 13.

`startTimer()` makes no network call any more, so `currentEntry.id` is the local
SQLite id `local-<ts>-<rand>`, and it was passed straight to
`screenshotService.start()`. `POST /screenshots/presign` validates
`time_entry_id` as a uuid and looks it up in `time_entries`, so every live shot
422'd, exhausted its retries and fell into the offline queue. `rebindEntryId()`
was written for exactly this case and survived the refactor, but its only caller
-- `reconcileTimerState()` -- was deleted with it.

The queued fallback then lost them for good. Resolution reads
`timer_sessions.server_entry_id`, but the 05:00 purge and `clearForLogout()`
delete that row with no knowledge of the queue's separate database, and with a
15-42 hour backlog lag shots routinely outlived their own session. The
`idempotency_key` fallback could not help: the queued key is the shot's dedupe
key, never the session's. Unresolvable items are held rather than dropped, so
they piled up behind `LIMIT 500` and were finally deleted -- silently, with
their image files -- by the 7-day TTL sweep. Heartbeats were unaffected because
they are priority 1 and carry the session uuid, which is what isolated the fault.

- `SessionSyncWorker` fires `onSessionConfirmed(localId, serverEntryId)` once per
  session, on the transition to a known server id; index.js rebinds capture to it
- `liveCaptureEntryId()` seeds `start()` from an already-known server id, covering
  restore-after-restart and project switch
- queued screenshots record `session_uuid`; `add()` persists it and
  `_resolveEntryId()` resolves through it first
- `purgeConfirmed()` takes a keep-list fed by `offlineQueue.referencedSessionKeys()`
- a flush that holds screenshots now logs it, escalating past 50

12 regression tests in desktop/test/screenshot-entry-id-binding.test.js
(46 suites / 796 tests pass). Full write-up in
bugs/desktop-screenshots-bound-to-local-entry-id.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Employees came back from a break, saw nothing, and carried on working after the
idle watchdog had auto-stopped the timer -- so the work after their return was
never recorded either.

It takes one completely ordinary setup: a machine that never sleeps and never
locks (on charger, external display, screensaver off). There, every existing
path misses the user.

- the idle alert beeps into an empty room, and the stop then dismisses it
- the "Timer auto-stopped" toast fires at the moment of the stop, long before
  anyone is back at the desk
- `notifyTrackingState()` -- whose entire job is telling the user their current
  state -- only runs on wake/unlock/startup, and such a machine emits none of
  those events
- the idle watchdog self-gates on `isTimerRunning`, so the instant it stops the
  timer it also stops looking

Nothing was watching for the user coming BACK.

Adds a return-from-break watcher: the mirror image of the watchdog, running
while the timer is STOPPED. It polls `getSystemIdleTime()` every 15s and fires
when the user has been away past the org idle threshold and is active again.
Absence is peak-tracked, because the poll that catches the return reads 0 --
the OS counter resets on the first keystroke -- so a last-reading model would
make every absence invisible. An unreadable counter fails silent rather than
inventing an absence.

Three cues, because each one alone is routinely swallowed:

- a `silent:false` notification with a unique id, so Action Center cannot dedup
  it against the auto-stop toast from hours earlier
- an in-renderer WebAudio beep -- macOS Focus and Windows Action Center drop
  notification sound with no fallback, the same reason the idle alert carries
  its own. Three descending tones, distinct from the idle alert's two rising
  ones, and no external resource so the strict CSP is unchanged
- the window itself, with a red banner that clears when tracking resumes

Decision logic is pure in desktop/src/main/return-to-work.js. 24 regression
tests in desktop/test/return-to-work.test.js (46 suites / 808 tests pass).
Full write-up in bugs/desktop-no-notification-on-return-from-break.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-to-local-entry-id

fix(desktop): bind screenshot capture to the server entry id, not the local one
…from-break

fix(desktop): notify with sound when a user returns to a stopped timer
@bladehell-ai
bladehell-ai merged commit ec25917 into main Aug 21, 2026
12 checks passed
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.

3 participants