feat: implement client targets v2 (weekly/monthly periods, working days, pro-ration)

- 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
This commit is contained in:
2026-02-24 19:02:32 +01:00
parent 3850e2db06
commit 7101f38bc8
8 changed files with 564 additions and 220 deletions

View File

@@ -0,0 +1,10 @@
-- CreateEnum
CREATE TYPE "PeriodType" AS ENUM ('WEEKLY', 'MONTHLY');
-- AlterTable: rename weekly_hours -> target_hours, add period_type, add working_days
ALTER TABLE "client_targets"
RENAME COLUMN "weekly_hours" TO "target_hours";
ALTER TABLE "client_targets"
ADD COLUMN "period_type" "PeriodType" NOT NULL DEFAULT 'WEEKLY',
ADD COLUMN "working_days" TEXT[] NOT NULL DEFAULT ARRAY['MON','TUE','WED','THU','FRI']::TEXT[];

View File

@@ -99,13 +99,20 @@ model OngoingTimer {
@@map("ongoing_timers")
}
enum PeriodType {
WEEKLY
MONTHLY
}
model ClientTarget {
id String @id @default(uuid())
weeklyHours Float @map("weekly_hours")
startDate DateTime @map("start_date") @db.Date // Always a Monday
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
id String @id @default(uuid())
targetHours Float @map("target_hours")
periodType PeriodType @default(WEEKLY) @map("period_type")
workingDays String[] @map("working_days") // e.g. ["MON","WED","FRI"]
startDate DateTime @map("start_date") @db.Date
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
userId String @map("user_id") @db.VarChar(255)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)