2026-09-14 21:31:32 -04:00
|
|
|
|
# Admin spec — parent phone card
|
|
|
|
|
|
|
|
|
|
|
|
Screens `1d`–`1i`. One card, `nestquest-admin-card`, four tabs. Reference viewport
|
|
|
|
|
|
390×844. Plain CubeCraft styling and plain domain words — no parchment, no Cinzel, no
|
|
|
|
|
|
quest vocabulary in this surface.
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Shell
|
|
|
|
|
|
|
|
|
|
|
|
- Page `--nq-a-page`; cards `--nq-a-surface` with 1px `--nq-a-border`.
|
|
|
|
|
|
- **Header** white, 1px bottom border: screen title 24px/800, tracking −.02em, plus a
|
|
|
|
|
|
12px/600 `--nq-a-ink-secondary` sub-line. Optional trailing action (icon square 36px,
|
|
|
|
|
|
or a gradient `New` button).
|
|
|
|
|
|
- **Tab bar** absolute bottom, `--nq-a-tabbar-height` 70px + 12px bottom padding, white,
|
|
|
|
|
|
1px top border, 4 items: `house` Today · `list-checks` Tasks · `calendar-days` Schedule ·
|
|
|
|
|
|
`history` History. Active icon and label `--nq-brand-blue` at 700; inactive icon
|
|
|
|
|
|
`--nq-a-ink-tertiary`, label `--nq-a-ink-secondary` 600.
|
|
|
|
|
|
- **Every scroll column ends with 92px bottom padding** so content clears the tab bar.
|
|
|
|
|
|
This was a real defect in review — do not omit it.
|
|
|
|
|
|
- Two densities exist. Ship **compact**; `--nq-a-density-*` carries the roomy values.
|
|
|
|
|
|
- Everything this card mutates goes through a NestQuest service, which enforces the admin
|
|
|
|
|
|
allowlist server-side. The card must assume it can be rendered by a non-admin and still
|
|
|
|
|
|
be safe — hiding controls is not the boundary (D-003).
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Today (1d compact, 1h roomy)
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 Header
|
|
|
|
|
|
Title `Today`; sub `<weekday>, <Month D> · cycle day N of 14`; trailing `settings` square.
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 Stat row
|
|
|
|
|
|
Three equal cards, 8px gap, padding 12px: numeral 22px/800 (done in `--nq-brand-blue`,
|
|
|
|
|
|
remaining in `--nq-a-ink`, overdue in `--nq-a-danger`), label 10px/600 uppercase tracking
|
|
|
|
|
|
.08em `--nq-a-ink-secondary` — `Done`, `Remaining`, `Overdue`.
|
|
|
|
|
|
|
|
|
|
|
|
### 2.3 Child rows
|
|
|
|
|
|
One card, rows divided by `--nq-a-divider`, row padding 11px 13px:
|
|
|
|
|
|
30px gradient avatar square (radius 7px, initial 13px/800 white) · name 14px/700 ·
|
|
|
|
|
|
meta 11px/600 (`3 of 7 · 1 overdue`; all-done in `--nq-a-success`; away reads
|
|
|
|
|
|
`Away · returns <Mon D>`) · 82×6px progress track with gradient fill. An away child's
|
|
|
|
|
|
avatar is flat `#9898A8` and the row carries a lowercase `override` pill when the absence
|
2026-09-24 20:41:06 -04:00
|
|
|
|
comes from a presence override rather than a presence pattern.
|
2026-09-14 21:31:32 -04:00
|
|
|
|
|
|
|
|
|
|
### 2.4 Roomy variant (1h)
|
|
|
|
|
|
Header becomes a hero: kicker 11px/700 uppercase, `8 of 12 done` 32px/800, 10px gradient
|
|
|
|
|
|
progress bar, 13px/600 sub-line. Each child gets its own 12px-radius card with
|
|
|
|
|
|
`0 4px 12px rgba(0,0,0,.06)`, 48px avatar, 19px/800 name, right-aligned `3/7` +
|
|
|
|
|
|
status line, and its overdue item inlined as a `--nq-a-danger-bg` row with a
|
|
|
|
|
|
`Mark done` action.
|
|
|
|
|
|
|
|
|
|
|
|
### 2.5 Needs attention
|
|
|
|
|
|
Section label 11px/700 uppercase, then one card of event rows: Lucide glyph 18px ·
|
|
|
|
|
|
title `<Quest> · <Child>` 13px/700 · meta 11px/600 (`Overdue since 9:00 AM` in
|
|
|
|
|
|
`--nq-a-danger`; `Completed 7:04 AM from panel` in `--nq-a-ink-secondary`) · trailing
|
|
|
|
|
|
text action `Mark done` (`--nq-brand-blue`) or `Undo` (`--nq-brand-purple`).
|
|
|
|
|
|
|
|
|
|
|
|
### 2.6 Permission note
|
|
|
|
|
|
An info strip (`--nq-a-info-bg`, `info` glyph, 11.5px/600 `--nq-a-info-ink`):
|
|
|
|
|
|
"Undo is admin-only. A panel tap can never reverse a completion." Keep this copy — it is
|
|
|
|
|
|
the one place the security model is explained to the user.
|
|
|
|
|
|
|
|
|
|
|
|
## 3. Definitions (1e)
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 List
|
|
|
|
|
|
Header `Tasks` / `7 definitions · 3 children` + gradient `New` button. Filter chips row
|
|
|
|
|
|
(All / one per child): active `#18181D` fill white text, inactive white with border.
|
|
|
|
|
|
Rows: glyph 17px · title 13.5px/700 · meta 11px/600 `<assignees> · <recurrence> · <windows>`
|
|
|
|
|
|
· `chevron-right`. An inactive definition renders the whole row at
|
2026-09-26 12:47:34 -04:00
|
|
|
|
`--nq-a-ink-tertiary` with `· inactive` in the meta. A definition with completion
|
|
|
|
|
|
history is never hard-deleted (glossary, D-017): Delete retires it to this inactive state.
|
2026-09-14 21:31:32 -04:00
|
|
|
|
The row being edited takes `--nq-a-selected` with brand-blue glyph, `Editing` meta and
|
|
|
|
|
|
`chevron-down`.
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 Edit sheet
|
|
|
|
|
|
Bottom sheet, `--nq-a-radius-sheet`, `--nq-a-sheet-shadow`, 38×4px grabber, padding
|
|
|
|
|
|
14px 18px 24px. Title 19px/800. Fields, 11px gap, each with a 10px/700 uppercase label:
|
|
|
|
|
|
|
|
|
|
|
|
| Field | Control |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| Assigned children | multi-select row → sheet; shows `Declan` or `All three` |
|
|
|
|
|
|
| Repeats | select (Daily / Weekly / Monthly / Yearly / Custom + interval) |
|
|
|
|
|
|
| Due time | time field per window |
|
|
|
|
|
|
| Window | 3-up segmented, **multi-select** — a definition may own several |
|
|
|
|
|
|
| Skip on away days | toggle, gradient when on, sub "No instance when the child is absent" |
|
|
|
|
|
|
|
|
|
|
|
|
Footer: `Cancel` (flex 1, bordered) + `Save changes` (flex 2, gradient), both 13px vertical
|
|
|
|
|
|
padding. Sheet enters over the dimmed list in `--nq-dur-modal`.
|
|
|
|
|
|
|
2026-09-26 12:47:34 -04:00
|
|
|
|
Delete (editing an existing definition only, never on `New`): a full-width bordered
|
|
|
|
|
|
`Delete task` button below the footer, danger-ink text. Tapping it swaps the footer for a
|
|
|
|
|
|
danger-tinted confirm — "Delete this task? If it has no completion history it is deleted;
|
|
|
|
|
|
otherwise it is retired (deactivated) and its history is kept." — with `Go back` and a
|
|
|
|
|
|
danger-filled `Delete task`. Confirming calls `DELETE /api/v1/admin/quest-definitions/{id}`
|
|
|
|
|
|
(admin plane only; the kids' panel has no equivalent control). Hybrid outcome (D-017): no
|
|
|
|
|
|
completion event on any instance → the definition, its instances, assignees, windows and
|
|
|
|
|
|
unreferenced rule are hard-deleted and the row leaves the list; otherwise the definition
|
|
|
|
|
|
is retired (deactivated) and the row stays, shown inactive. Retirement preserves the
|
|
|
|
|
|
definition row, schedule rule, assignees, windows and all durable completion history. Like
|
|
|
|
|
|
any deactivation it regenerates, so future uncompleted instances are cleared (they are
|
|
|
|
|
|
rolling, regenerable rows) and none are recreated while the definition is inactive — the
|
|
|
|
|
|
retired task stops generating and no longer shows open work. Durable instance history lives
|
|
|
|
|
|
only in the append-only `completion_events`. An error keeps the sheet open with the API's
|
|
|
|
|
|
detail.
|
|
|
|
|
|
|
2026-09-14 21:31:32 -04:00
|
|
|
|
### 3.3 Icon picker
|
2026-09-26 09:02:07 -04:00
|
|
|
|
Each definition carries one optional icon, shown on both surfaces. The picker offers three
|
|
|
|
|
|
sources:
|
|
|
|
|
|
|
|
|
|
|
|
- **Lucide** — a grid of the curated Lucide glyphs, drawn stroked.
|
2026-09-30 19:16:39 -04:00
|
|
|
|
- **Font Awesome** — a search over the **full** Font Awesome Free *solid* set (every
|
|
|
|
|
|
glyph in `@fortawesome/free-solid-svg-icons`), plus a curated quick-pick grid, all drawn
|
|
|
|
|
|
filled. With the search field empty the quick-pick grid shows; typing filters the full
|
|
|
|
|
|
set by name or label (an exact name ranks first, then name prefixes, then the rest),
|
|
|
|
|
|
shown 40 results per page with *Previous* / *Show more*.
|
|
|
|
|
|
**Free-text rule:** an admin may type a Free Solid icon name directly (trimmed,
|
|
|
|
|
|
case-insensitive — `Dog` stores `fa:dog`). Text that is not the exact name of a Free
|
|
|
|
|
|
Solid icon — a partial search, a misspelling, or a Pro or Brands name — is rejected in
|
|
|
|
|
|
the picker with an inline error and cannot be saved until a result is selected or the
|
|
|
|
|
|
full name is typed.
|
2026-09-26 09:02:07 -04:00
|
|
|
|
- **Emoji** — a text input; the typed emoji is stored as entered (not trimmed) and must be
|
|
|
|
|
|
1–8 code points with no whitespace, control characters, `<`, `>` or `&`.
|
|
|
|
|
|
|
2026-09-30 19:16:39 -04:00
|
|
|
|
The curated Lucide set and the Font Awesome quick-pick list come from
|
|
|
|
|
|
`tools/icons/curated-icons.json`; `tools/icons/generate.mjs` generates their glyph path
|
|
|
|
|
|
data, together with the full Font Awesome Free Solid set, into the admin and panel bundles
|
|
|
|
|
|
(no CDN, no runtime fetch). Embedding the full set makes both bundles heavier — the panel
|
|
|
|
|
|
bundle (`nestquest-cards.js`) is about 1 MB raw / 276 kB gzip. See
|
|
|
|
|
|
`THIRD-PARTY-NOTICES.md` for licences.
|
2026-09-26 09:02:07 -04:00
|
|
|
|
|
|
|
|
|
|
The stored value is a **namespaced key**:
|
|
|
|
|
|
|
|
|
|
|
|
| Source | Stored key | Example |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| Lucide | `lucide:<name>` | `lucide:dog` |
|
|
|
|
|
|
| Font Awesome | `fa:<name>` | `fa:paw` |
|
|
|
|
|
|
| Emoji | `emoji:<grapheme>` | `emoji:🐶` |
|
|
|
|
|
|
|
|
|
|
|
|
`<name>` is a lowercase kebab name (`[a-z0-9-]`, no leading or trailing `-`). A **legacy
|
|
|
|
|
|
bare name** written before namespacing (`dog`) reads as `lucide:<name>` — the API returns it
|
|
|
|
|
|
namespaced, and any write of a bare name is stored as `lucide:<name>`; no data migration. An
|
|
|
|
|
|
empty value clears the icon. An empty, unknown, or unresolvable key renders the **fallback
|
|
|
|
|
|
glyph** — in the admin, the generic task glyph (clipboard with a check); on the panel, the
|
|
|
|
|
|
star (`PANEL-SPEC.md` §3.2).
|
2026-09-14 21:31:32 -04:00
|
|
|
|
|
|
|
|
|
|
## 4. Schedule (1f)
|
|
|
|
|
|
|
2026-09-24 20:41:06 -04:00
|
|
|
|
A child has **zero or more** presence patterns (schema 9). Each pattern has a name, a
|
|
|
|
|
|
kind (`Home` or `Away`), its own cycle length (1–4 weeks), its own anchor date, and a
|
|
|
|
|
|
weekday set per cycle week. The screen shows one child at a time.
|
|
|
|
|
|
|
|
|
|
|
|
- Header `Schedule` / `<Child> · N patterns` (`<Child> · no patterns, home every day`
|
|
|
|
|
|
when the child has none).
|
|
|
|
|
|
- **Child** chip row: one chip per child; the selected chip drives everything below.
|
2026-09-14 21:31:32 -04:00
|
|
|
|
- **Month grid** card: month label 11px/700 uppercase + chevron pair; weekday initials
|
2026-09-24 20:41:06 -04:00
|
|
|
|
10px/700 `--nq-a-ink-tertiary`, **Sunday first** (`S M T W T F S`); day cells
|
|
|
|
|
|
`aspect-ratio: 1`, radius 6px — home days `--nq-a-selected` with `#201F7A` numerals,
|
|
|
|
|
|
away days `--nq-a-page` with `--nq-a-ink-secondary`, override days
|
|
|
|
|
|
`--nq-a-warning-bg` with `--nq-a-warning-ink` and a 1px warning border, today filled
|
|
|
|
|
|
with the brand gradient in white 800. Legend row beneath, 10.5px/600, one swatch per
|
|
|
|
|
|
state (`Home`, `Away`, `Override · away`, `Override · home`, `Today`), then the note
|
|
|
|
|
|
"Combined from every pattern: away beats home; overrides beat both."
|
|
|
|
|
|
- The grid shows the child's **combined** presence, first match wins: an override
|
|
|
|
|
|
covering the date; else away if any Away pattern covers it; else home if any Home
|
|
|
|
|
|
pattern covers it; else away when the child has at least one Home pattern (a Home
|
|
|
|
|
|
pattern lists the days the child is here); else home (only Away patterns, or none).
|
|
|
|
|
|
- **Presence patterns** card: `Add` text action; one row per pattern with
|
|
|
|
|
|
`calendar-check` (Home) / `calendar-x` (Away), title = pattern name 13px/700, meta
|
|
|
|
|
|
`Home · Every 2 weeks` 12px/600, and the weekday set (`Thu, Fri` for a 1-week cycle,
|
|
|
|
|
|
otherwise `Week 1: Sat, Sun · Week 2: No days`). Trailing `pencil` opens the pattern
|
|
|
|
|
|
editor (§4.1); trailing `trash-2` asks `Delete pattern?` inline before deleting. With
|
|
|
|
|
|
no patterns the card reads "No patterns for <Child> — home every day."
|
2026-09-14 21:31:32 -04:00
|
|
|
|
- **Overrides** card: `Add` text action; rows with `calendar-x` / `calendar-check`,
|
|
|
|
|
|
title `<Child> away · <Mon D – D>`, meta reason, trailing `trash-2`.
|
2026-09-24 20:41:06 -04:00
|
|
|
|
- Cycle position is always computed from each pattern's own anchor date. **Never** from
|
|
|
|
|
|
ISO week numbers or week parity (D-004).
|
|
|
|
|
|
- Weekday numbering on the wire stays **Monday=0 .. Sunday=6**; Sunday-first is display
|
|
|
|
|
|
order only.
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 Pattern editor
|
|
|
|
|
|
|
|
|
|
|
|
Pushed screen with a back chevron; title `New pattern` / `Edit pattern`, sub "Repeating
|
|
|
|
|
|
presence for <Child>". Form card fields:
|
|
|
|
|
|
|
|
|
|
|
|
- **Name** text field (required, not blank).
|
|
|
|
|
|
- **Covered days are** 2-up segmented (`Away` / `Home`), selected = `#18181D`.
|
|
|
|
|
|
- **Cycle length** select (`1 week` … `4 weeks`) and **Anchor date** side by side.
|
|
|
|
|
|
Changing the cycle length keeps existing weeks and adds new weeks covering no day.
|
|
|
|
|
|
- One **weekday picker** per cycle week, labelled `Week N · from <Mon D>` (the date is
|
|
|
|
|
|
anchor + (N−1) weeks): seven toggle cells in **Sunday-first** order
|
|
|
|
|
|
(`Su Mo Tu We Th Fr Sa`). A week may cover no day.
|
|
|
|
|
|
|
|
|
|
|
|
Footer: `Cancel` (flex 1, white, bordered) + `Save pattern` (flex 2, gradient), as the
|
|
|
|
|
|
override editor (§6). Save stays disabled until the name is set, the anchor is a valid
|
|
|
|
|
|
date, and something changed.
|
2026-09-14 21:31:32 -04:00
|
|
|
|
|
|
|
|
|
|
## 5. History (1g)
|
|
|
|
|
|
|
|
|
|
|
|
- Header `History` / `Append-only event log · last 14 days` + bordered `CSV` button
|
|
|
|
|
|
(`download` glyph) that exports the filtered range.
|
|
|
|
|
|
- Stat row: `91%` On time · `128` Completed · `7` Missed, numerals 20px/800.
|
|
|
|
|
|
- Filter chips: `All events` / `Reversals` / `Missed`.
|
|
|
|
|
|
- Day-grouped sections (label 11px/700 uppercase) of event rows: 7px status dot
|
|
|
|
|
|
(green completed, `--nq-a-reversal` un-completed, `--nq-a-danger` missed) ·
|
|
|
|
|
|
title `<Quest> · <Child>` 12.5px/700 · meta 10.5px/600 giving event type, actor and
|
|
|
|
|
|
on-time flag (`completed · panel (Declan) · on time`, `uncompleted · joshua (admin)`,
|
|
|
|
|
|
`missed · nightly sweep`, `completed · panel (Declan) · late 22 min`) · time 11px/600.
|
|
|
|
|
|
- Footer strip (`--nq-a-page`, `lock` glyph): "Events are never edited or deleted. A
|
|
|
|
|
|
reversal is its own row." Rows are never editable in the UI (D-005).
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Presence override editor (1i)
|
|
|
|
|
|
|
|
|
|
|
|
Pushed screen with a back chevron; title `Presence override` / sub "Beats the custody
|
|
|
|
|
|
pattern for these dates".
|
|
|
|
|
|
|
|
|
|
|
|
- Form card (roomy density, 18px padding, 16px gaps):
|
|
|
|
|
|
**Child** 3-up segmented, selected = gradient;
|
|
|
|
|
|
**Status for these dates** 2-up segmented (`Away` / `Home`), selected = `#18181D`;
|
|
|
|
|
|
**From** / **Through** date fields side by side, 13px padding, 14px/700 values;
|
|
|
|
|
|
**Reason (optional)** text field.
|
|
|
|
|
|
- **Consequence warning** — `--nq-a-warning-bg` card with `alert-circle`:
|
|
|
|
|
|
headline `This removes N upcoming tasks` 13.5px/700 `--nq-a-warning-ink`, body 12px/600
|
|
|
|
|
|
`--nq-a-warning-ink-2` naming the child, the date range, and stating "Completed days are
|
|
|
|
|
|
untouched." The count must be computed live from the materialiser before saving.
|
|
|
|
|
|
- Footer: `Cancel` (flex 1, white, bordered) + `Save override` (flex 2, gradient), 15px
|
|
|
|
|
|
vertical padding.
|
|
|
|
|
|
- Saving must never delete an instance that already has a completion event.
|