feat(statistics-deck): Week view (Room, Desk, Laptop) (#135) #148
No reviewers
Labels
No labels
blocked
bug
enhancement
high-priority
low-priority
needs-info
needs-triage
ready-for-agent
ready-for-human
referenced
research
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gabogg/hikcentral!148
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/deck-week-view"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #135
Problem
The deck shell (#133, PR #144) has no Week view: the WEEK tab shows placeholders. The RFC (§3, §4, §4.1, §5) asks for a Week view in three layout profiles. It must show ranked KPIs and graphs, honest comparisons ("—" with a reason, never 0%), the data-quality marks on every per-day graph, and no scrolling from the Laptop minimum (760 px) up.
Approach
app/static/js/src/ui/statistics_deck/views/week_view.jscallsregisterView('week', …). index.html gets one import line and one stylesheet link.weekKpis,comparisonView,alignByWeekday/visitorsByDay/dwellByDay,dayMarks/dayTicks,heatmapModel,entranceShareRows,reliableRecordDate/peakDay, and the Chart.js configs.WEEK_LAYOUTS). Each profile is its own layout, and each smaller one keeps the top of both rankings:ctx.loadDays(). The previous week comes fromctx.loadPeriodDays(ctx.previousPeriod())and the same ISO week last year fromctx.loadPeriodDays(ctx.periodFor(last_year.reference_start))(shell support from #150). The last-year load happens only when the summary carrieslast_year, which the route omits when that week has no data.bucket=hour(a directfetchJson) feeds the heatmap. The entrances route feeds entrance share. The summary, hourly and entrances answers are cached per page for complete weeks only. A Partial week is fetched afresh on each render.ctx.locale,ctx.createChart).deckQualityMarks(dashed amber for Estimate, grey hatching for Unreliable).hatchPattern/elementBox..sd-swatch-unreliablehatch, and Estimate day labels carry.sd-swatch-estimate. An open day without data is a single hatched "no data" band.minmax(0, …), and the heatmap and entrance lists shrink their rows. Below 760 px the graph rows stack like the shell's.Verification
TZ=America/Caracas pytest -q: 416 passed. Node frontend suite: 159 tests, 33 of them intests/frontend/test_statistics_deck_week.test.js.ruff check .,ruff format --check .andpython3 scripts/check_docs.pyare clean.esanden: no page, root, view, row or cell overflow in any of the 9 cases, and the top bar is 38 px.—with "no reliable day" for records and—+ PARTIAL_PERIOD vs the previous week. There is no last-year line. Every day is hatched, and days without data are "no data" bands on the heatmap.The tests cover every Acceptance item:
—+ PARTIAL_PERIOD below the coverage floor.—with a reason for NO_DATA_PREVIOUS_PERIOD, CLOSED_PERIOD, ZERO_REFERENCE_VISITORS and PARTIAL_PERIOD, and no last-year line when last year has no data.ComparisonReasoncode, which the test reads fromapp/schemas/statistics.py.Decisions
last_year, the Visitors tile shows no last-year line and the graph draws no last-year series or legend entry (RFC §5.1 "last year only when it has data"), consistent with the Day view. A last year that exists but whose change is withheld still shows—with its reason.—and its reason instead.peak_timestamp_epochagainst the daily rows, so no facility time is formatted from the browser clock. Without a timestamp the peak is shown undated.formatCoveragetext (5 / 7 DAYS). A complete week shows its open-day count. The Visitors tile adds the coverage only for a partial week.Known duplication left for consolidation (maintainer)
These are local to
week_view.json purpose, per the coordination rules:formatNumber/formatChange/formatPercentcomparisonView/reasonText(the "—" plus reason text)entranceShareRows,renderEntrances)<data>text parts (trParts,numberParts)statisticsDeck.comparison.reason.*is shared-intent i18n that the Day and Month PRs may also add.Outside the view files
app/static/index.html: one<link>and oneimport.app/static/js/i18n.js:statisticsDeck.views.week.*andstatisticsDeck.comparison.reason.*at the end of bothstatisticsDeckblocks.fix/deck-shell-view-support) and uses its view APIs.🤖 Generated with Claude Code
Code review: PR #148 (
feat/deck-week-view@93d196cvsmaster@08b6d1c)Standards sources: AGENTS.md,
docs/standards/code-standards.md,docs/standards/ui-design-guidelines.md, ADR 0002, the deck shell's exported helpers, and the Fowler smell baseline. Duplication across the Day, Week and Month views is known and will be consolidated after merge, so it isn't flagged here. Spec: #135 and RFC §3–§5.Standards
Verified:
node --checkpasses and the 24 tests pass; there is noinnerHTML; no facility time is formatted with the browser clock; zero radius, tabular numbers and the 1 px grid gap are in place.formatCoveragewithoutwithData. The numbers in the KPI sub-lines are also plain text, not<data>(§3.3/§7).viewColors()(L418–430) copiescharts.js's private token lookup to add cyan and primary. ExtendCOLOR_TOKENSinstead (being added by #150).period.business_days === 0re-implementsisClosedPeriod().<data value>holds a visitor count. Dash tiles and null shares emit<data value="">for "—". Use<span>when there's no real number.options.localeisn't set, so ticks use the browser locale, not the deck language.cachekeeps summary, previous-week, hour and entrance data for the whole page, including the open Partial week, which then goes stale. "Never change once closed" isn't enforced.loadDaysresolves[]on failure, sorequests.days.catch(() => [])is dead code. A failed days fetch draws empty charts instead of the cell's "could not be loaded" message.tierClass()switches on tier a third time.dailyUrlplus a second cache duplicate the shell'sloadDays. The previous week is fetched twice when the user steps back to it.dwellChartConfigandvisitorsChartConfigbuild the samepluginsblock twice.peakShown = peak && !(peakAt.found && !peakAt.date)hides a three-way state. Name it (e.g.peakAt.hiddenAsUnreliable).heatmapModel: an open day with no row gets level-0 cells with no hatching, so it reads as zero traffic.rgba(234,234,234,.7)andrgba(124,135,151,.55), with two hatch gradients of different periods (5 px vs 6 px).Spec
Verified:
minmax(0, …)row splits; weekend share, Closed Days and holidays handled correctly..sd-swatch-*.peak.timestamp_epoch,peakDayfalls back to matchingpeak_people_inside, so a tie can date the peak to the wrong day. Leave it undated instead.last_year, which will be true for the whole first year. The Day view (#149) hides the line instead. The two views must agree.Summary. Standards: 13 findings, worst S-6/S-7 (a stale Partial-week cache; failed day loads drawn as empty charts). Spec: 6 findings, worst C-6 (last-year line policy, pending the maintainer's decision). First pass, so all findings get fixed on this branch. S-2, S-5 and S-9 build on the shell-support PR #150.
🤖 Generated with Claude Code
93d196c1055eb0543296First-pass review fixes: all findings addressed in
5eb0543The branch is rebased onto
origin/fix/deck-shell-view-support(#150) and force-pushed with--force-with-lease. The fixes use #150's view APIs. The PR description is updated to match the code.5eb0543)trParts,numberParts), so every number renders as<data value>. The Partial coverage is the shell'sformatCoveragetext, with its counts as<data>. A complete week shows its open-day count.text lines keep their numbers as <data> parts,KPI sub-lines render their numbers as <data>…, partial-week testviewColors(). UsescurrentDeckColors(), whosecyan/primary/dimcome from #150.<data>. The busiest day, and any "—" tile or null share, is a<span>, so no<data value="">remains.KPI sub-lines render…,entrance rows show … a missing share is a dash <span>ctx.createChart(deck locale on ticks).formatNumber/formatChangeuseIntl.NumberFormat(ctx.locale).complete weeks are cached per page; a Partial week is fetched afreshloadDaysnow rejects. The lead and dwell graphs show "could not be loaded", and the KPIs still render from the summary.failed day rows show "could not be loaded"…TIER_GLYPHmap and oneTIER_SWATCHmap replace the ternaries andtierClass().dailyUrland the second days cache. The reference weeks usectx.loadPeriodDays(ctx.previousPeriod())andctx.periodFor(last_year.reference_start). Only thebucket=hourcall remains a directfetchJson(hourlyUrl).dayMarkPlugins(marks)/barConfigbuilds the plugins block for both per-day graphs.peakDayreturns{date, hiddenAsUnreliable}, and the tile reads that named flag.missinginheatmapModeland renders as one hatched "NO DATA" band, not zero-traffic cells.an open day without data or without a row is a missing band…, swatch render testrgba(...)values and both hatch gradients from the Week CSS. Legend lines use tokens. The hatch and estimate styles come from the shell's.sd-swatch-*.var()fallbacksreferenceStateandvisitorsLegend: a previous week without counted days draws no line. The legend reads "PREVIOUS WEEK — " (CLOSED_PERIOD when every day was closed), or "— COULD NOT BE LOADED" on failure.a reference week without data is not drawn…,a previous week without data draws no line…≈symbol are removed, along with thechangeVsPreviouscopy. The ◇/◆ on x labels stay (§4.1)..sd-swatch-unreliable, Estimate day labels use.sd-swatch-estimate, and the gap legend key is an.sd-swatch.the heatmap uses the shell's shared swatches…ENTRANCE_ROWS.no data last year: the visitors tile hides the last-year line,no last-year data: no line, no legend entry and no last-year fetchVerification
TZ=America/Caracas pytest -q: 416 passed.ruff check .,ruff format --check .andcheck_docs.pyare clean.🤖 Generated with Claude Code
Code review, second pass: PR #148 (Week) @
5eb0543All first-pass fixes (S-1–S-13, C-1–C-6) are verified; 76 node tests pass.
Standards
ctx.locale || deckLocale(tr.lang)is dead, andWEEK_LAYOUTS[profile] || roomis inconsistent withweekRequests.numberPartsrepeats the digit-split regex of the shell'swithData. Candidate for the planned consolidation.Spec
Verdict: mergeable. No P1 or P2; the P3s are filed as a follow-up issue.
🤖 Generated with Claude Code
5eb05432961e482a5a6fRebased onto master after the Month view (#147) merged. I resolved the
index.htmlandi18n.jsconflicts: both views' CSS and imports are kept, andviews.weeksits next toviews.month. ForstatisticsDeck.comparison.reason.*, master's Month wording is kept as the canonical set, per the maintainer's decision. The Week tests now assert that set (1e482a5). The full suite passes on top of master (416), and ruff and check_docs are clean.