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)
|
service/ # business rules (close day/week, merge, overtime)
|
||||||
migrations/ # SQL migration files
|
migrations/ # SQL migration files
|
||||||
web/ # SvelteKit app (embedded in binary at build time)
|
web/ # SvelteKit app (embedded in binary at build time)
|
||||||
Makefile
|
mise.toml # task/tool runner (replaces Makefile)
|
||||||
PLAN.md
|
PLAN.md
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -104,6 +104,7 @@ CREATE TABLE sync_log (
|
|||||||
POST /api/entries/start { note? } -> Entry
|
POST /api/entries/start { note? } -> Entry
|
||||||
POST /api/entries/{id}/stop -> Entry
|
POST /api/entries/{id}/stop -> Entry
|
||||||
GET /api/entries?from=YYYY-MM-DD&to=YYYY-MM-DD
|
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? }
|
PUT /api/entries/{id} { start_time?, end_time?, note? }
|
||||||
DELETE /api/entries/{id} (soft delete)
|
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.
|
- **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.
|
- **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.
|
- **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.
|
- **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
|
### Auth
|
||||||
|
|
||||||
@@ -155,16 +157,40 @@ GET /healthz (unauthenticated)
|
|||||||
- TypeScript
|
- TypeScript
|
||||||
- `vite-plugin-pwa` + Workbox for service worker and manifest
|
- `vite-plugin-pwa` + Workbox for service worker and manifest
|
||||||
- `dexie` for typed IndexedDB access
|
- `dexie` for typed IndexedDB access
|
||||||
|
- `vitest` for unit tests
|
||||||
|
|
||||||
### Screens
|
### Screens
|
||||||
|
|
||||||
| Screen | Purpose |
|
| Screen | Purpose |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| **Today** | Start/Stop button with live timer, list of today's entries, quick-mark actions |
|
| **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. |
|
||||||
| **Week** | 7-day grid: worked vs expected per day, running totals, "Close week" CTA |
|
|
||||||
| **History** | Month/week list of closed days and weeks with overtime indicators |
|
| **History** | Month/week list of closed days and weeks with overtime indicators |
|
||||||
| **Settings** | hours/week, workdays mask, timezone, effective-from, token setup |
|
| **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
|
### Offline Strategy
|
||||||
|
|
||||||
1. App shell precached by Workbox.
|
1. App shell precached by Workbox.
|
||||||
@@ -181,11 +207,11 @@ GET /healthz (unauthenticated)
|
|||||||
|
|
||||||
## 5. Build & Deployment
|
## 5. Build & Deployment
|
||||||
|
|
||||||
```makefile
|
```sh
|
||||||
# Single-binary production build
|
# Single-binary production build (via mise)
|
||||||
make build
|
mise run build
|
||||||
# 1. pnpm build (inside web/)
|
# 1. npm run build (inside web/)
|
||||||
# 2. go build -tags production ./cmd/wotra
|
# 2. go build -tags production -o wotra ./cmd/wotra
|
||||||
# -> embeds web/build via go:embed
|
# -> embeds web/build via go:embed
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -201,23 +227,24 @@ Environment variables:
|
|||||||
|
|
||||||
## 6. Milestones
|
## 6. Milestones
|
||||||
|
|
||||||
### M1 — Backend MVP
|
### M1–M6 — Done
|
||||||
Repo scaffold, Go module, SQLite with migrations, entries CRUD, start/stop with same-day validation (midnight guard), auth middleware, service-layer tests.
|
Full backend + frontend implemented, PWA offline sync, single-binary build, `mise.toml` task runner.
|
||||||
|
|
||||||
### M2 — Day Management
|
### M7 — Merge Today+Week views ✅ in progress
|
||||||
Settings with effective-from history, close day (running-entry guard, entry merging), holiday/vacation/sick marking.
|
|
||||||
|
|
||||||
### M3 — Week Close & Overtime
|
Staged implementation:
|
||||||
Close week, expected/worked/delta computation with frozen settings snapshot, reopen day/week.
|
|
||||||
|
|
||||||
### M4 — Svelte UI (online-only)
|
| Stage | Goal | Status |
|
||||||
Today view, Week view, History view, Settings screen, all consuming the API directly.
|
|---|---|---|
|
||||||
|
| 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
|
### M8 — Future
|
||||||
Service worker, manifest, Dexie store, outbox pattern, `/api/sync/pull` and `/api/sync/push` endpoints, last-write-wins reconciliation.
|
CSV/JSON export, monthly summary view.
|
||||||
|
|
||||||
### M6 — Polish
|
|
||||||
CSV/JSON export, monthly summary view, single-binary build with embedded assets, Makefile, README.
|
|
||||||
|
|
||||||
## 7. Decisions & Rationale
|
## 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 |
|
| Deployment | Single Go binary embeds SPA | Simplest self-hosting story |
|
||||||
| SQLite driver | modernc.org/sqlite (pure Go) | No CGO; easy cross-compilation |
|
| 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 |
|
| 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