The balance now accounts for any active timer whose project belongs to
the tracked client. computeBalance() fetches the user's OngoingTimer,
computes its elapsed seconds, and adds them to the matching period's
tracked seconds before running the balance formula — so both
currentPeriodTrackedSeconds and totalBalanceSeconds reflect the live
timer without requiring a schema change.
On the frontend, useClientTargets polls every 30 s while a timer is
running, and a pulsing green dot is shown next to the balance figure on
the Dashboard and Clients pages to signal the live contribution.
- Remove stale corrHoursInt/corrMins state (leftover from previous refactor)
- Use corrDuration (HH:MM string) parsed once and reuse totalHours in submit handler
- Single type="time" input + +/− toggle button matches TimerWidget style
- flex-1 on Date and Duration columns for equal width and consistent height alignment
- Correction list now shows '13h 32m' instead of '13.65h', using
formatDurationHoursMinutes (same formatter used everywhere else)
- Sign shown as '−' (minus) for negative corrections instead of bare '-'
- Correction input replaced with separate hours + minutes integer fields
and a +/− toggle button, removing the awkward decimal entry
- Add PeriodType enum and working_days column to ClientTarget schema
- Rename weekly_hours -> target_hours; remove Monday-only constraint
- Add migration 20260224000000_client_targets_v2
- Rewrite computeBalance() to support weekly/monthly periods, per-spec
pro-ration for first period, ongoing vs completed period logic, and
elapsed working-day counting (§4–§6 of requirements doc)
- Update Zod schemas and TypeScript input types for new fields
- Frontend: replace WeekBalance with PeriodBalance; update
ClientTargetWithBalance to currentPeriod* fields
- ClientTargetPanel: period type radio, working-day toggles, free date
picker, dynamic hours label
- DashboardPage: rename widget to Targets, dynamic This week/This month
label
Deleting a target or correction sets deletedAt instead of hard-deleting.
Creating a target for a user+client that has a soft-deleted record
reactivates it (clears deletedAt, applies new weeklyHours/startDate)
rather than failing the unique constraint. All reads filter deletedAt = null
on the target, its corrections, and the parent client.
Replace hard deletes with deletedAt timestamp flags on all three entities.
Deleting a client or project only sets its own deletedAt; child records are
excluded implicitly by filtering on parent deletedAt in every read query.
Raw SQL statistics queries also filter out soft-deleted parents.
FK ON DELETE CASCADE removed from Project→Client and TimeEntry→Project.
- Add breakMinutes field to TimeEntry model and database migration
- Users can now add break duration (minutes) to time entries
- Break time is subtracted from total tracked duration
- Validation ensures break time cannot exceed total entry duration
- Statistics and client target balance calculations account for breaks
- Frontend UI includes break time input in TimeEntryFormModal
- Duration displays show break time deduction (e.g., '7h (−1h break)')
- Both project/client statistics and weekly balance calculations updated
Allows users to discard a running timer without creating a time entry.
A trash icon in the timer widget reveals a confirmation step ('Discard / Keep')
to prevent accidental data loss. Backend exposes a new DELETE /api/timer
endpoint that simply deletes the ongoingTimer row.
Allows users to retroactively correct the start time of an ongoing timer
without stopping it. A pencil icon in the timer widget opens an inline
time input pre-filled with the current start time; confirming sends the
new time to the backend which validates it is in the past before persisting.