docs: update PLAN.md — merge Today+Week view stages M7, add decisions
This commit is contained in:
76
PLAN.md
76
PLAN.md
@@ -35,7 +35,7 @@ internal/
|
||||
service/ # business rules (close day/week, merge, overtime)
|
||||
migrations/ # SQL migration files
|
||||
web/ # SvelteKit app (embedded in binary at build time)
|
||||
Makefile
|
||||
mise.toml # task/tool runner (replaces Makefile)
|
||||
PLAN.md
|
||||
```
|
||||
|
||||
@@ -104,6 +104,7 @@ CREATE TABLE sync_log (
|
||||
POST /api/entries/start { note? } -> Entry
|
||||
POST /api/entries/{id}/stop -> Entry
|
||||
GET /api/entries?from=YYYY-MM-DD&to=YYYY-MM-DD
|
||||
POST /api/entries { start_ms, end_ms, note? } -> Entry (completed interval)
|
||||
PUT /api/entries/{id} { start_time?, end_time?, note? }
|
||||
DELETE /api/entries/{id} (soft delete)
|
||||
|
||||
@@ -137,8 +138,9 @@ GET /healthz (unauthenticated)
|
||||
- **Midnight enforcement**: `end_time` must be on the same calendar day as `start_time`. Server auto-stops any running entry at 23:59:59 local time via a background goroutine. Client warns the user.
|
||||
- **Close day**: requires all entries for that day to have `end_time != NULL` (409 otherwise). Computes `worked_ms = sum(end_time - start_time)` across non-deleted entries. Entries are preserved but the day is now locked.
|
||||
- **Mark day**: writes a `closed_days` row with `kind != 'work'`; `worked_ms` = expected daily ms (hours_per_week * 3600000 / popcount(workdays_mask)) for workdays, 0 for non-workdays.
|
||||
- **Close week**: all workdays in the week must have a `closed_days` row. Sums `worked_ms`, snapshots `expected_ms` from settings effective at the week start, stores signed `delta_ms`.
|
||||
- **Close week**: all workdays in the week must have a `closed_days` row. Sums `worked_ms`, snapshots `expected_ms` from settings effective at close time, stores signed `delta_ms`.
|
||||
- **Settings change**: new row in `settings_history` with `effective_from`. Past closed weeks are unaffected because they store a frozen snapshot.
|
||||
- **Future intervals**: server rejects `CreateInterval` and `Update` if the resulting day key is in the future (400 Bad Request).
|
||||
|
||||
### Auth
|
||||
|
||||
@@ -155,16 +157,40 @@ GET /healthz (unauthenticated)
|
||||
- TypeScript
|
||||
- `vite-plugin-pwa` + Workbox for service worker and manifest
|
||||
- `dexie` for typed IndexedDB access
|
||||
- `vitest` for unit tests
|
||||
|
||||
### Screens
|
||||
|
||||
| Screen | Purpose |
|
||||
|--------|---------|
|
||||
| **Today** | Start/Stop button with live timer, list of today's entries, quick-mark actions |
|
||||
| **Week** | 7-day grid: worked vs expected per day, running totals, "Close week" CTA |
|
||||
| **Week** | Week picker, 7-day chip strip, day detail panel for selected day. Default: today selected (if in week) else Monday. Today tab in bottom nav is a shortcut that selects today. |
|
||||
| **History** | Month/week list of closed days and weeks with overtime indicators |
|
||||
| **Settings** | hours/week, workdays mask, timezone, effective-from, token setup |
|
||||
|
||||
The old dedicated **Today** route is merged into the Week view (see § Merged Week+Today View below).
|
||||
|
||||
### Merged Week+Today View (`/week`)
|
||||
|
||||
Three regions:
|
||||
|
||||
1. **Week header** — week picker, week totals, Close Week button, closed-week banner.
|
||||
2. **Day strip** — 7 chips (Mon–Sun), each showing: weekday label + date, mini progress bar (worked/expected), kind icon, closed badge, today highlight. Horizontal scroll-snap on mobile. Selected chip scrolls into view. `role="tablist"` with keyboard arrow navigation.
|
||||
3. **Day detail panel** — for the selected day, capabilities gated per table:
|
||||
|
||||
| Condition | canStartStop | canAddInterval | canEditEntries | canMarkKind | canCloseDay |
|
||||
|---|---|---|---|---|---|
|
||||
| Future | false | false | false | true | false |
|
||||
| Today, open | true | true | true | true | true* |
|
||||
| Today, closed | false | false | false | true | reopen |
|
||||
| Past, open | false | true | true | true | true |
|
||||
| Past, closed | false | false | false | true | reopen |
|
||||
|
||||
*Close Day button disabled with tooltip while a running entry exists.
|
||||
|
||||
**URL state**: `?week=2026-W18&day=2026-04-30`. Bare `/week` canonicalizes via `replaceState`. Chip clicks use `replaceState` (no history push). Week navigation (prev/next) uses `goto` (history push). Default day selection on week change: today if in week, else Monday.
|
||||
|
||||
**Bottom nav**: "Today" tab is a shortcut link to `/week?week=<currentWeek>&day=<todayKey>`. Active when route is `/week` and `?day === todayKey()`.
|
||||
|
||||
### Offline Strategy
|
||||
|
||||
1. App shell precached by Workbox.
|
||||
@@ -181,11 +207,11 @@ GET /healthz (unauthenticated)
|
||||
|
||||
## 5. Build & Deployment
|
||||
|
||||
```makefile
|
||||
# Single-binary production build
|
||||
make build
|
||||
# 1. pnpm build (inside web/)
|
||||
# 2. go build -tags production ./cmd/wotra
|
||||
```sh
|
||||
# Single-binary production build (via mise)
|
||||
mise run build
|
||||
# 1. npm run build (inside web/)
|
||||
# 2. go build -tags production -o wotra ./cmd/wotra
|
||||
# -> embeds web/build via go:embed
|
||||
```
|
||||
|
||||
@@ -201,23 +227,24 @@ Environment variables:
|
||||
|
||||
## 6. Milestones
|
||||
|
||||
### M1 — Backend MVP
|
||||
Repo scaffold, Go module, SQLite with migrations, entries CRUD, start/stop with same-day validation (midnight guard), auth middleware, service-layer tests.
|
||||
### M1–M6 — Done
|
||||
Full backend + frontend implemented, PWA offline sync, single-binary build, `mise.toml` task runner.
|
||||
|
||||
### M2 — Day Management
|
||||
Settings with effective-from history, close day (running-entry guard, entry merging), holiday/vacation/sick marking.
|
||||
### M7 — Merge Today+Week views ✅ in progress
|
||||
|
||||
### M3 — Week Close & Overtime
|
||||
Close week, expected/worked/delta computation with frozen settings snapshot, reopen day/week.
|
||||
Staged implementation:
|
||||
|
||||
### M4 — Svelte UI (online-only)
|
||||
Today view, Week view, History view, Settings screen, all consuming the API directly.
|
||||
| Stage | Goal | Status |
|
||||
|---|---|---|
|
||||
| 0 | Add Vitest setup | pending |
|
||||
| 1 | Day strip on week view (read-only) | pending |
|
||||
| 2 | Extract DayDetail component; render today in week view | pending |
|
||||
| 3 | Day selection + URL-driven state | pending |
|
||||
| 4 | Backend guard: reject future-day intervals | pending |
|
||||
| 5 | Bottom nav Today shortcut; delete /today route | pending |
|
||||
|
||||
### M5 — PWA + Offline
|
||||
Service worker, manifest, Dexie store, outbox pattern, `/api/sync/pull` and `/api/sync/push` endpoints, last-write-wins reconciliation.
|
||||
|
||||
### M6 — Polish
|
||||
CSV/JSON export, monthly summary view, single-binary build with embedded assets, Makefile, README.
|
||||
### M8 — Future
|
||||
CSV/JSON export, monthly summary view.
|
||||
|
||||
## 7. Decisions & Rationale
|
||||
|
||||
@@ -230,3 +257,8 @@ CSV/JSON export, monthly summary view, single-binary build with embedded assets,
|
||||
| Deployment | Single Go binary embeds SPA | Simplest self-hosting story |
|
||||
| SQLite driver | modernc.org/sqlite (pure Go) | No CGO; easy cross-compilation |
|
||||
| Breaks in closed day | Not included in worked_ms | Sum of entry durations only; gaps between entries are breaks |
|
||||
| Week close settings | Use close-time date, not week Monday | Settings changed mid-week apply to that week's close |
|
||||
| weekDayKeys formula | `(weekday+6)%7` for days-since-Monday | Avoids sign bug when Jan 4 is Sunday (affects year 2026) |
|
||||
| Merged Today+Week | Single `/week` route with day selection | Reduces duplication; week context always visible |
|
||||
| Future intervals | Rejected by server (400) | Nonsensical to track time that hasn't happened |
|
||||
| Test framework | Vitest (frontend) + Go testing (backend) | Automated coverage for capability logic and key utilities |
|
||||
|
||||
Reference in New Issue
Block a user