38686-vm/docs/plans/parked-work.md
Konrad du Plessis bde6f24bb1 docs: refresh CLAUDE.md + parked-work for 15 May session wins
CLAUDE.md changes:
- 'What's mid-flight' breadcrumb updated: SiteReport/Absences
  migrations are LIVE on prod; only the 4 latest UX commits await
  a pull-and-restart (no migration / collectstatic needed).
- URL Routes table entries for /workers/ and /history/ now document
  the new ?team= filter (and team=none for unassigned / no-team cases).
- Worker Management UI inline description mentions the team filter
  + Absences tab on the worker detail page.
- Two new Coding Style gotchas captured: (1) Bootstrap dropdowns
  inside .card elements get clipped by sibling cards — fix is to
  lift the wrapping card with position:relative + z-index;
  (2) JS reading from data-worker-id was unreliable on production —
  read input[name="workers"][value] directly (the attendance-form
  pattern that's been working for years).

parked-work.md changes:
- Pending-deploy section rewritten: the BIG deploy (migrations +
  collectstatic) is DONE; only 4 small commits await a pull +
  service restart.
- 'Recently shipped' grew two new entries: the team filters on
  /workers/ + /history/, and the two absences UX polish fixes.
- Updated timestamp to 15 May 2026.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 00:50:47 +02:00

192 lines
8.9 KiB
Markdown

# Parked / deferred work
> Updated 15 May 2026. A small index of features that are designed,
> half-built, blocked on input, or pending an operator step. When a
> fresh session opens, glance here first to see what's already on
> the workbench.
---
## ⚠ Pending pull-and-restart on production
**Status:** The big absences/SiteReport deploy (migrations
`0013`/`0014`/`0015` + collectstatic + service restart) was
completed on 14 May 2026 — `/history/` no longer crashes,
`/absences/*` is reachable, badge CSS is collected. ✓
Since that deploy, four small commits have shipped to `origin/ai-dev`
that are NOT yet on production:
| Commit | What it does |
|---|---|
| `4368e53` | Fixes the absence-form team filter (workers were all disappearing when a team was selected — read input.value instead of data-attr) |
| `02c6d4d` | Fixes the Reasons multi-checkbox dropdown stacking-context bug on `/absences/` (lifted the filter card above the table card) |
| `4b57cff` | Adds a Team filter to `/workers/` |
| `398a5b2` | Adds a Team filter to `/history/` (and CSV export) |
**To deploy these:**
- No migrations needed (pure template + view changes; no schema changes).
- No `collectstatic` needed (no new CSS or JS files).
- Just: ask Gemini in Flatlogic to `git pull origin ai-dev` and then `sudo systemctl restart django-dev.service`.
That's the whole sequence. Production should pick up all four
improvements instantly. Rollback is `git reset --hard <previous SHA>`
on the VM if anything looks wrong (no data migrations were involved,
so rollback is safe).
---
## Blocked on Konrad's input
### Phase A.2 — Manual JournalEntry web UI
**Status:** Designed in the Site Work Logging brainstorm. Model
class drafted in the design doc (not yet in `core/models.py`).
Not yet built.
**Blocked on:** Q7 from the original brainstorm — "Who is Vi?" The
answer drives whether `JournalEntry` needs a `recipient` or
`audience` field (i.e. is this a public log, or does it have
specific addressees like a client). **Default if unanswered:** no
recipient field — entries are all-admin-readable.
**Scope when unblocked:** ~1 hour of work. Manual entry form at
`/journal/new/`, list at `/journal/`, edit at `/journal/<id>/edit/`.
Admin-only views. Lives on `ai-dev` (no webhook involvement).
### Phase B — Letterly inbound webhook (`integrations` branch)
**Status:** Designed in the Site Work Logging brainstorm. Belongs
on the `integrations` branch, **not** `ai-dev`.
**Blocked on:** Q5 from the original brainstorm — Konrad needs to
share a sample Letterly webhook payload. Need to know:
- Does it include the recording user (so we can map to a Django User)?
- Does it include project / location metadata, or just the transcript?
- Does it include an audio URL for link-back?
- Does Letterly support custom fields (so a deep-link from
`/site-report/<id>/edit/` could embed a `work_log_id` that the
webhook reads back)?
**Scope when unblocked:** ~3-4 hours including tests + one-off
Letterly account setup. Adds a `@csrf_exempt` view at
`/webhooks/letterly/`, `JournalEntry` row creation, HMAC body
verification, shared-secret URL token.
---
## Small polish follow-ups from the Absences feature
These were flagged during code review of the absences feature but
deemed non-blocking. They can roll into a future janitorial pass.
- **`AbsenceQuickForm` is defined but never wired up** —
`core/forms.py:797-827`. Originally planned for a per-worker ✗
modal on the attendance form, but Round C replaced that paradigm
with the "Submit + Log Absences" button. Either wire the form up
somewhere or delete the class. ~30 LOC.
- **N+1 in `team_workers_map` build** — affects both `attendance_log`
and `absence_log`. Pre-existing in `attendance_log`; Round A
inherited the pattern. Fix is to use
`Prefetch('workers', queryset=Worker.objects.filter(active=True),
to_attr='active_workers_cached')` like the payroll dashboard does.
Recommend extracting `_build_team_workers_map(user)` as a shared
helper.
- **`absence_list` permission check duplicates `_user_can_log_absences`** —
`core/views.py:5535` inlines `is_admin(user) or
user.supervised_teams.exists()` instead of calling the helper.
Trivial DRY cleanup.
- **`--badge-neutral-bg` referenced but never defined** —
`static/css/custom.css` near line 2197. The CSS uses
`var(--badge-neutral-bg, #6c757d)` for `.badge-absence-unpaid` and
`.badge-absence-other`; the fallback always wins because the
variable isn't declared in `:root`. Either define it in both
themes or drop the `var()` wrapper.
- **`conflicting_worklogs()` runs N queries per (worker, date) pair** —
`core/forms.py:785-786`. Fine at FoxFitt's scale. Worth profiling
if/when batch sizes grow.
- **CSV export filter block is duplicated** — `core/views.py`
`absence_export_csv` copies the filter logic from `absence_list`.
TODO comment in code says "factor into `_apply_absence_filters`
when a third filter joins."
- **Paid-checkmark icon in `site_report_detail.html`** uses
`color: var(--badge-bonus-bg)` (a BACKGROUND color) as foreground
— fails WCAG contrast on white. Same fix as we applied to absence
templates (use `text-success`). Trivial one-line change.
---
## Defaulted (not blocking — flag if you disagree)
From Q9, Q4 of the Site Work Logging brainstorm:
- **Q9 — photos on site reports.** Deferred to v2 of SiteReport.
Construction supervisors often want to attach a phone photo of
"plinths cast today" but it adds ~50 LOC + storage handling +
thumbnail rendering. v1 ships without.
- **Q4 — per-project metric templates.** Same metric set for all
projects in v1. If/when a non-solar-farm project lands and the
metrics diverge wildly, we add a `MetricTemplate` model. YAGNI
for now — `core/site_report_schema.py` is one Python file edit.
---
## Recently shipped (for context, so a fresh session knows what just landed)
- **Team filters on `/workers/` and `/history/`** (commits `4b57cff`,
`398a5b2`, 15 May 2026): Both pages now have a Team dropdown in
their filter row. `/workers/?team=<id>` filters by Team.workers
M2M membership (who's CURRENTLY on the team); `/history/?team=<id>`
filters by WorkLog.team FK (logs TAGGED with the team when
created — different semantics, intentional). Both accept
`team=none` for the "no team assigned" / "ad-hoc work log" case.
CSV export at `/history/export/` honours the same param.
+8 regression tests.
- **Absences UX polish** (commits `4368e53`, `02c6d4d`, 15 May 2026):
Two production-found bugs fixed. (1) Absence-form team filter was
hiding ALL workers when a team was selected — switched the JS to
read `<input name="workers">[value]` directly instead of going
through `data-worker-id` (proven attendance-form pattern). (2)
Reasons multi-checkbox dropdown on `/absences/` was rendering
behind the table — lifted the filter card with
`position: relative; z-index: 10` so the whole card sits above
its sibling table card in the stacking order. See the Coding
Style section in CLAUDE.md for both gotchas.
- **Worker Absences feature** (commits `bf6f0a5``27fe05e`,
14 May 2026): Complete absence-tracking system. 8 reason choices,
optional project FK (auto-attributes paid-absence Bonus
adjustments to the project), date-range logging with weekend
toggles, supervisor scoping (admin sees all; supervisors see
their teams). Standalone `/absences/log/` form + "Submit + Log
Absences" shortcut on attendance form. List/edit/delete + CSV
export. Worker-detail "Absences" tab with YTD totals. Dashboard
alert card "X absent in last 7 days". Bidirectional cascade with
PayrollAdjustment via `_sync_absence_payroll_adjustment` helper
(single-chokepoint design, transaction.atomic-wrapped). 12
commits pushed to `origin/ai-dev`. Test count: 85 → 149 (+64
tests). Migrations `0013_add_site_report`, `0014_add_absence`,
`0015_absence_project` are LIVE on production as of late 14 May
2026.
- **Phase A.1 — SiteReport** (commit `864ae72`, 14 May 2026):
Model, migration `0013_add_site_report.py`, form, two-step flow
from attendance log, 16 new tests. `CLAUDE.md` updated with model
summary + `site_report_schema.py` pattern section.
- **Pastel soft-fill payroll action buttons** (commit `6c6ade9`,
24 Apr 2026): unified Worker Lookup / Batch Pay / Add Adjustment
/ Price Overtime treatment.
- **Path A display-only rename** (UX Polish Pass, Apr 2026):
"New Loan" / "Advance Payment" / "Advance Repayment" display as
"Loan" / "Advance" / "Advance Repaid" — but **DB values are
unchanged**. See "UI-vs-DB naming drift" section in `CLAUDE.md`
before writing any filter/comparison on `PayrollAdjustment.type`.
---
## Original Site Work Logging brainstorm
The full Q1-Q13 question list and design rationale lives in
`~/.claude/plans/prancy-painting-brook.md` (local — deliberately
not committed to the repo, since the parts that matter have been
absorbed into `CLAUDE.md` and this file). If Konrad answers Q5 or
Q7 later, refer back to that file for the original framing.