# Backend helpers for the power-ups (rounds 1 + 2) · brand-006 RJ Laravel Admin

**Purpose:** the API-request list. Per HANDOFF §5, each row below becomes one `api-request` issue in `RJmtm/newadmin-api-kit` when someone files them. **No issues have been created.**
**Sources:** round 1 `../POWERUPS.md` (24 adopted, 3 needs-backend) · round 2 `ADVANCED.md` (29 patterns) · catalogue check against newadmin-api-kit `spec/laravel-admin-openapi.yaml` (branch t012/kit, 789 admin paths, Atlas lock), 2026-10-18.
**Rules each helper keeps:** polling every few seconds, not websockets (T001 Q2; push is listed only as an upgrade) · typed confirm on money and irreversible actions (Q14) · version check on measurement writes (Q22) · per-staff timezone, API returns UTC (Q25) · server PDFs via short-lived links (Q26) · bulk ≤100 per call, frontend batches (T003 #13) · pickers are names-only and gated on the form's own permission (T003 Theme A) · the shell shows only what the server allows (T003).
**Effort:** S ≤ 1 day · M 2–4 days · L ≥ 1 week, for one backend developer including tests and Atlas docs. All paths are under `/api/v1`.

**Totals: 28 helpers · 11 S · 15 M · 2 L.** Round 1 needs 3 of them (H01, H02, H03); round 2 needs the other 25, and also reuses H01 and H02.

## Foundations (build first; others depend on them)

| ID | Helper | Endpoint(s) | Effort | Round · patterns it unlocks | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H01 | Per-staff prefs store (key → JSON) | `GET /admin/me/prefs/{key}` · `PUT /admin/me/prefs/{key}` | S | R1 saved views & column presets, density per person, recent / pinned across devices · R2 My home, import mapping memory | — | `PUT /admin/me/preferences` takes only `{locale, timezone}`; extend beside it |
| H02 | Record history store + read | `GET /admin/records/{type}/{id}/history?at=` | L | R1 activity side panel · R2 time-travel, while-you-were-away digest, bulk undo before-values, part of why-is-this-order-here | — | Only `admin.orders.payload-audits.*` and API-key audit; `GET /admin/audit-logs` → 404. Precedent: `admin.email-templates.versions.*` |
| H03 | Version check on measurements (verify) | `PUT /admin/orders/{order}/measurements` with `If-Match` / `expected_version` → 409 `version_conflict` | S | R1 “someone else changed this” banner · R2 presence (hard guard), restore, offline replays | — | **In the spec** (`admin.orders.measurement-check` returns `version` + ETag; decision-B correction); verify the 409 on staging |

## Find & act

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H04 | One search across types | `GET /admin/search?q=&types=orders,accounts,fabric_codes,tickets,designs&limit=` | M | R2 one search · R1 ⌘K beyond orders · R2 sentence ⌘K | — | Partial: `admin.dashboard.quick-search`, `admin.reports.stock.search`, `admin.tickets.citations.search` |
| H05 | Sentence → filter or action | `POST /admin/search/parse` | M | R2 say it in ⌘K | H04, H09 (phase-age filter) | New; reuses the LLM wiring of `admin.tickets.ai.suggest`; never writes |
| H06 | Counts for the filtered set | `GET /admin/orders/counts?<list filters>` | S | R2 totals strip, My home tiles | — | `admin.orders.status-catalogue` has tabs, not counts |

## Collaboration

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H07 | Presence heartbeat | `POST /admin/presence` · `DELETE /admin/presence/{token}` | S | R2 who else is here + soft edit lock | H03 (hard guard) | New; Redis TTL 30 s, no table. Poll 10 s; websockets = upgrade |
| H08 | Names-only admin picker | `GET /admin/pickers/admins?q=` | S | R2 @mentions; later ticket owner (ghost G3) | — | Pattern exists: `admin.pickers.accounts.*`, `main-products/form-options` |
| H10 | Comments on any record | `GET` / `POST /admin/records/{type}/{id}/comments` | M | R2 comments + @mentions | H08 | Partial: `admin.orders.notes.*`, `admin.tickets.comments.store`; mention writes reuse `/admin/notifications` |
| H11 | Per-record watches + fan-out | `POST /admin/watches` · `DELETE /admin/watches/{id}` · `GET /admin/watches` | M | R2 watch → inbox | — | Partial: `/admin/notifications` (+ `unread-count`), per-event-type `/admin/events/self/subscriptions` |
| H12 | Hand-over digest | `GET /admin/me/digest?since=` | M | R2 while you were away | H11, H02 | New |

## Time & history

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H09 | Phase ages + phase targets | `GET /admin/orders/phase-ages?ids=` · `GET` / `PUT /admin/settings/phase-targets` | M | R2 stuck-in-phase timers; feeds H05, H19 | — | Partial: `admin.orders.phase-summary.all` (statuses with created_at, due_date), dashboard `over_due` |
| H13 | Why-is-this-order-here | `GET /admin/orders/{order}/explain` | M | R2 explain timeline | H09 (optional H02) | Partial: phase-summary, gantt-detail, payload-audits, hold / remove-hold |
| H14 | Restore one value | `POST /admin/records/{type}/{id}/history/{change}/restore` | M | R2 time-travel | H02, H03 | Precedent: `admin.email-templates.versions.revert`, `admin.orders.fitq.undo` |
| H15 | Scheduled actions | `POST /admin/scheduled-actions` · `GET ?mine=1` · `DELETE /{id}` | M | R2 do it later | — | New; Laravel scheduler (watched by `admin.schedule-monitor.index`); allow-listed ops, never money; permission re-checked at run time |

## Safety & control

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H16 | Dry-run on bulk writes | `dry_run=1` on `POST /admin/accounts/bulk-features`, `/admin/accounts/bulk-permissions`, `/admin/accessory-codes/bulk`, `/admin/api-keys/bulk-revoke` | S | R2 impact preview; improves R1 bulk bar | — | Pattern exists: `admin.fx-rates.preview`, `design-blockers.preset-impact`, `api-key-revocation-preview`, bulk-hub preview |
| H17 | Bulk run record + revert | `GET /admin/bulk-runs/{id}` · `POST /admin/bulk-runs/{id}/revert` | M | R2 undo a whole bulk run (10 min) | H02 (or own before-values) | New; revert runs as a job batch (`/admin/dashboard/job-batches`) |
| H18 | Maker–checker approvals | `POST /admin/approvals` · `GET ?state=pending` · `POST /{id}/approve` · `POST /{id}/reject` | L | R2 second approver for money | per-currency thresholds setting | New; executes existing `admin.billing.wallets.transactions.store`, order void, sessions revoke-all; approve uses the typed confirm (Q14) |
| H20 | See as another role | `as_admin={id}` on `GET /admin/me` and `GET /admin/me/menu` | S | R2 see the admin as someone else's role | — | `admin.me.show`, `admin.me.menu` exist; super admin only, writes refused |

## Data at scale

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H21 | JSON rows on import preview | `POST /admin/{import}/import/preview` accepts `{rows, columns}` | S | R2 paste from Excel | — | File flows exist: fabric-codes, lining-codes, pt-colors, fitq-config, fabric-attributes, colourway-families preview/confirm |
| H22 | Metric series for sparklines | `GET /admin/metrics/series?metric=&ids=&weeks=12` | M | R2 trends inside the table | — | New; read-only, cached nightly |

## Phone & floor

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H23 | Resolve a scanned QR | `GET /admin/qr/resolve?code=` | S | R2 scan the QR to open it | — | Partial: `admin.qr-links.index/store` (short_code, entity_type), `admin.accessory-qr.show` |
| H24 | Idempotency-Key middleware | header on vision-check resolve, QC checks, order checkpoints | M | R2 keeps working when Wi-Fi drops; R2 swipe verdicts offline | H03 | New; 24 h key store; replay returns first result |
| H25 | Web Push subscriptions | `POST /admin/me/push-subscriptions` · `DELETE /{id}` | M | R2 ping me when it's done; approver pings for H18 | — | Upgrade only. Fallback works today: in-tab notification off `admin.notifications.unread-count` (`running_batches`) |

## Intelligence

| ID | Helper | Endpoint(s) | Effort | Patterns | Depends on | Catalogue today |
|---|---|---|---|---|---|---|
| H26 | Ticket summary mode | `POST /admin/tickets/ai/suggest` `{mode:"summary"}` | S | R2 ticket summary + suggested next step | — | Exists: `admin.tickets.ai.suggest` (answer, confidence, chunks); add a mode |
| H19 | Order anomaly flags | `GET /admin/orders/flags?ids=` | M | R2 anomaly flags | H09 (express stillness) | New rules (read-only); inputs exist: measurements, `admin.reports.stock`, dashboard fabric_overdue |
| H27 | Health aggregate | `GET /admin/health/summary` | S | R2 health strip (top administrator only, T003 #45) | — | Parts exist: dashboard `queue_stats`, `admin.failed-jobs.index`, `admin.schedule-monitor.index`, `admin.api-request-logs.stats` |
| H28 | Similar in-stock cloths | `GET /admin/fabric-intelligence/similar?code=&in_stock=1&limit=3` | M | R2 out of stock? closest cloths | — | Partial: `admin.fabric-intelligence.cloths.detail`, colourway families, `admin.reports.stock` |

No helper needed (UI-only or already in the catalogue): R2 do that again (⌘.), compare two records, swipe photo verdicts (`admin.vision-checks.results.resolve`); and the 21 round-1 patterns tagged exists / UI-only.

## Suggested build order

Waves are parallel inside, sequential between. A wave-2 item can start before wave 1 ends unless it lists a wave-1 dependency.

1. **Foundations:** **H01 prefs store** (S, unlocks 5 patterns across both rounds) · **H02 record history** (L, the long pole: start day one; unlocks 5) · H03 verify the 409 on staging (S).
2. **Highest value, no dependencies:** **H04 one search** (M, ★, also feeds round-1 ⌘K) · H23 QR resolve · H16 dry-run · H27 health · H26 ticket summary · H07 presence · H21 JSON import preview · H06 counts · H20 see-as-role · H08 admin picker (all S).
3. **Order intelligence + collaboration:** H09 phase ages → H13 explain, H19 flags · H11 watches · H10 comments · H24 idempotency.
4. **Built on the above:** H05 sentence parse (H04 + H09) · H12 digest (H11 + H02) · H14 restore (H02) · H17 bulk revert (H02) · H15 scheduled actions · H22 metric series · H28 similar cloths · H25 web push.
5. **Last, needs a policy decision on thresholds:** H18 maker–checker approvals.

Waves 1–2 cover all 10 round-2 ★ picks except stuck-in-phase timers and why-is-this-order-here, which land in wave 3.

## Not in this list (tracked elsewhere)

- Decision-B ghosts G1–G4 (cancel queued print job, bulk vision resolve, ticket owner, internal ticket note): `../../../PROPOSALS.md`.
- Round-1 notes that are not power-up blockers: orders bulk status (`POST /admin/orders/bulk-status` → 404; the bulk bar ships on accounts / accessory codes / API keys first) and menu `badge` counts (absent live, EVIDENCE §1).
