feat: rebrand AgendaPro -> AgendaMax + calendar fixes + current-week demo seed
- Rebrand all user-facing text to AgendaMax - Fix sidebar scrolling: card container no longer overflows html (flex h-full min-h-0) - Fix calendar toolbar hover: active buttons keep readable contrast - Sticky calendar toolbar (month/week/day always visible on scroll) - Seed guarantees 2-4 appointments per day for current week (Mon-Sat)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# Task 3 Report — scheduling.ts DB functions
|
||||
|
||||
## Files changed
|
||||
- `server/lib/scheduling.ts` — appended DB-backed functions from Task 3 plan (`CandidateInfo`, `safeArr`, `getCandidates`, `getExistingBusy`, `isAvailable`, `rankOne`, `pickBestSlotEmployee`, `AutoAssignResult`, `AutoAssignCtx`, `autoAssign`, `runInTransaction`) + `import type { DatabaseSync } from "node:sqlite"`.
|
||||
|
||||
## Typecheck
|
||||
`npm run typecheck` → **0 errors** (after the deviation below).
|
||||
|
||||
## test:unit
|
||||
`npm run test:unit` → **15/15 pass** (pure tests unaffected).
|
||||
|
||||
## tx smoke-test output
|
||||
Temp file `_tx-smoke.mjs` (deleted after run). It imports `runInTransaction` and `db`, exercises happy path + throw path, and verifies a real `UPDATE` was rolled back.
|
||||
|
||||
```
|
||||
PASS runInTransaction returns fn value (42)
|
||||
PASS no active tx after commit
|
||||
PASS runInTransaction rethrows on error
|
||||
PASS no active tx after rollback
|
||||
PASS rollback undid the UPDATE
|
||||
5/5 passed
|
||||
EXIT=0
|
||||
```
|
||||
|
||||
`PRAGMA active_transaction` was used (newer node:sqlite exposes it; falls back to a no-row / 0 object) to confirm no dangling transaction after both commit and rollback. The real-state rollback check (UPDATE `_tx_probe SET v=999` inside a throwing fn) proves the ROLLBACK is functional, not just emitted.
|
||||
|
||||
## Deviations
|
||||
1. **Plan typo fix in `autoAssign` (line 256).** The plan literally wrote:
|
||||
```ts
|
||||
if (!best || ranked.score > best.score || (ranked.score === best.score && c.info.id < best.info.id)) {
|
||||
```
|
||||
but inside `autoAssign` the loop variable `c` is a `CandidateInfo` (no `.info` field), so `c.info.id` is a type error (TS2339). Fixed to `c.id < best.info.id`. `best` is still a `RankedCandidate` so `best.info.id` is correct. The semantic intent (tie-break by lowest employee id) is preserved exactly. Note: the sibling function `pickBestSlotEmployee` uses `c.id` correctly in the plan — confirming the autoAssign line was a typo, not an intentional shape.
|
||||
|
||||
No other deviations. The `import type { DatabaseSync }` is placed mid-file (after `scoreCandidate`), which is valid because ES module `import` declarations are hoisted; `tsc -b --noEmit` and `tsx` both accept it.
|
||||
|
||||
## Self-review
|
||||
- **Does `autoAssign` return `null` when no candidate is free?** YES. Two null paths:
|
||||
1. `candidates.length === 0` → immediate `return null` (line 245).
|
||||
2. After the loop, `if (!best) return null` (line 260) covers the case where every candidate was filtered out (closed that day, slot outside working window, or has a conflict).
|
||||
- **Does it respect per-employee working hours?** YES. For each candidate it calls `getWorkingHoursForDate(c.empWh, ctx.bizWh, new Date(ctx.startMs))` — employee override wins, business is the fallback, and a closed day returns `null` → candidate is `continue`d. Then the slot must satisfy `ctx.startMs >= wh.startMs && ctx.endMs <= wh.endMs`, otherwise the candidate is skipped.
|
||||
- **Does `runInTransaction` roll back on throw?** YES. Verified by smoke test: a thrown error inside `fn` causes `db.exec("ROLLBACK")` to run, the error is re-thrown, and a real `UPDATE` made inside the throwing fn is undone (the probe row retained its pre-tx value).
|
||||
Reference in New Issue
Block a user