# TICKETS · flagship round 1 · Support tickets · brand-006 RJ Laravel Admin

**Sheet:** https://tickets.pina-brand-006-rj-laravel-admin.pages.dev/ · **artboards:** /canvas/ (3 pages: Inbox + ticket · Composer + wallet · New ticket · states · phone) · **one image:** /contact-sheet.png
**Status:** round 1 for Frank's review, 2026-10-18. Design only; no code, no ledger.
**Boards:** 18 + summary = 19. Each board = burned-in label chip + the screen at 1440×900 in the locked 4b·R shell + an annotation panel (decisions, sources, tags).
**Source:** `src/t_data.py` (fixtures, every vocabulary item cited) · `src/t_pages.py` (screens) · `src/t_build.py` (composites, shots, sheet, canvas, dist) · `src/tickets.css`. Shell, helpers and the order peek are imported read-only from the Orders flagship (`orders/src/o_pages.py` → `phase-2/archetypes/src/arch_pages.py`); same pipeline and quality bar as ORDERS-R1.
**Layout:** A11·a as approved — list · thread · context. Context is a flex column; nothing in it floats over the thread (the order peek takes the column's place, T04).

## Truth rules applied on every board

- **Statuses** are the six real rows with their DB colours: Open `#1291f3` · Pending Retailer Response `#fba037` · Pending Support Response `#d719f0` · Resolved `#168e06` · Closed `#de2121` · Re-Open `#050336` (hidden from the staff dropdown + filter). No "In Progress". (TRUTH §8)
- **Severities** Low / Medium / High; **types** Free / Premium (disabled); **categories → subs** = the live prod tree; **saved replies** = the 10 prod templates (MCP view=templates); "Delayed Order" body and `{owner-name}` / `{number-of-days}` blanks are real (template 11).
- **Owner = the retailer shop.** No assignee, no "Mine". The page shows "Shop · owner of the ticket" and "Opened by" (creator). (TRUTH §7)
- **Original message first** (`tickets.body`), never collapsed, with "opened by ‹user›" and **RJ staff · on behalf of ‹shop›** when staff opened it, plus Products (sub-orders) and its attachments. (TRUTH §2)
- **Get AI Suggestion** is an explicit button beside Cite article; idle shows no panel; loading / result (NN% confidence, Source Chunks, Use This Response / Dismiss) / no match / error use the real strings. The composer carries a cost line. (TRUTH §3)
- **With order ≈ 50% / no order ≈ 50%:** the Order block appears only with an order; without one the wallet opens in its place — no empty card. (TRUTH §4)
- **Hold** is not on the ticket page. It lives on the order (Raise Ticket modal → Put Order On Hold, T12). **No "Peek order"**: Open order swaps the order into the context column (T04).
- **Wallet** = the real box: who pays · Debit default · Reason template ("Write it myself" first, currency-matched prefill) · Amount · Reason (+ voice) · Review → Confirm this movement (Shop / Direction / Amount / Resulting balance, before → after with currency) → Back | Apply · no floor · two neutral one-liners. (TRUTH §6)
- **Order ids** bare 4-digit from the Orders flagship fixtures (4521 = S. Chen · Atelier North · Manufacturing). **People** are placeholders; shops fictional; dotted underline = example value.
- **Counts:** All **880**, Resolved **426**, Closed **454**, Open / Pending **0** (live, MCP search_tickets all-time, 18 Oct). Needs us / Waiting on shop on the working boards are a normal day (dotted); T13 shows the real 0.
- Tags: purple dashed = **proposal**, amber dashed = **needs backend**, blue = **API only**.

## Boards and sources per element

| Board | What it shows | Real element · source |
|---|---|---|
| T01 ★ | Inbox index: views Needs us · Waiting on shop · Resolved 426 · Closed 454 · All 880 · More; search; Shop / Category / Severity / Order chips; rows; Narrow-by rail with all-time counts | index row + filters `index.blade.php:263-330`, `TicketsController.php:55-85`; counts MCP; colours `tickets_statuses` · saved views **needs backend (H01)** · Order chip = API `has_order` |
| T02 ★ | #861 with order, staff-opened, shop replied last: original message, Admin / shop tags, inline auto-status events, composer, context (Ticket · Shop · Order · Wallet collapsed · Latest activity) | `show.blade.php:439-458, 466-487, 601-609, 641-729, 741-755`; `CommentsController.php:34-36`; `Retailer/Tickets/CommentsController.php:21-22` |
| T03 | #892 no order, retailer-opened, 0 replies: no Order block, wallet open in place | TRUTH §0.3, §1.5, §4; `show.blade.php:400-412` |
| T04 | Open order → Orders-flagship peek takes the context column; thread narrows (composer compacts) | `admin.orders.show`; peek = `GET /admin/orders/{order}` + `/phase-summary` (Orders O05) |
| T05 | AI: idle · loading · result (sources open) · no match · error | `show.blade.php:476-478, 500-544, 1108-1202`; `AiAssistantController::suggest`; `SupportResponseService.php:19-35` |
| T06 | Composer: Cite article picker, citation chip, attachments (one uploading), voice polished | `tickets.citations.search`; `AttachmentRequest.php:16-24` (≤ 15 MB image/video); `voiceTranscribe` |
| T07 | Saved reply "Delayed Order" → Enter Details with its two blanks | `show.blade.php:766-781, 992-1040`; template 11 |
| T08 | Internal note as a ghost (tab + amber note), banner saying why | `tickets_comments` columns; `Comment.php:45-51` · **needs backend** |
| T09 ★ | Wallet ladder 1-4 + states a-d (EUR template on USD shop, below-zero, no billable shop, no billing access) | `wallet-action-box.blade.php:103-272, 596-610`; `WalletActionTemplate::prefillAmountFor`; templates 1-2 |
| T10 | Status menu (5 staff statuses, Re-Open not offered) + saved toast with Undo as proposal | `TicketsController.php:95,272`; `CloseCommand.php:51-67` |
| T11 | /admin/tickets/new: Type · Title · Category → Sub · Brand search · Order ID · Severity · body (Record, Select Template) · attachments; Put Order On Hold here = proposal | TRUTH §5; `TicketsController.php:168`; `StoreTicketRequest.php:77` |
| T12 | Raise Ticket from the Orders list with Put Order On Hold | `modal-form.blade.php:45-48`; `orders/index.blade.php:1824`; `TicketsController.php:189-217`; `OrderHoldController.php:14-31` |
| T13 | Inbox zero (true today) with next steps + last closed | MCP counts; auto-close 72 h |
| T14 | Loading skeleton, ticket number painted | POWERUPS #24 |
| T15 | Read-only role (Dublin time): banner replaces composer, fields read-only, wallet neutral line | TRUTH §5 permissions; `TicketPolicy.php:31-60` |
| T16 | Long + CJK: 140-char staff title, 77-char shop, 19 replies, Chinese body/file/shopper, long URL wraps | TRUTH §1.5; POWERUPS #11 |
| T17 | Phone /m/tickets: list → reply (big mic, AI on tap) → context bottom sheet (wallet stays desktop) | STACK-AND-PACKAGES §2, §2.3; POWERUPS #18 |
| T18 | How you'd use it: 6 numbered pins on T02 | — |

## Open questions for Frank (my pick ★)

1. **Default view when you open Tickets?** 1a ★ Needs us (Open + Pending Support Response + Re-Open) · 1b all non-closed (adds Pending Retailer Response) · 1c today's index (everything, newest updated).
2. **Re-Open in Needs us?** 2a ★ yes — a shop re-opening is a ticket waiting on us · 2b keep it out, as the index filter does today.
3. **Thread order?** 3a ★ oldest-first, original message pinned first, composer at the bottom (drawn) · 3b newest-first with the composer on top, as Blade does today.
4. **Undo on the status toast?** 4a drop Undo; the toast only confirms (and says the shop was emailed) · 4b ★ keep Undo as a second update, accepting two history rows and possibly a second email · 4c hold the save for ~5 s before it fires (no email until then).
5. **Typed confirm for wallet money (T001 Q14)?** 5a ★ keep the real two-step (Review → Confirm → Apply) · 5b add "type the amount" above a threshold · 5c always typed.
6. **Where does the AI cost note live?** 6a ★ one quiet line under the composer (drawn) · 6b tooltip on the button only · 6c show the running cost per ticket (needs backend: count clicks).
7. **Put Order On Hold on /admin/tickets/new when an order is picked?** 7a keep it only in the Orders-list modal (today) · 7b ★ offer it once an order in RFF / Build / Manufacturing is picked (proposal, same store endpoint).
8. **"Open order" behaviour?** 8a ★ the order replaces the context column (T04) · 8b open the full order page in a new tab.
9. **The second create card "Technical Support"?** 9a ★ fold into New ticket ▾ as a preset (category Web App) — needs confirming what it pre-sets today · 9b drop it from the admin side.
10. **Shop's wallet on a no-order ticket?** 10a ★ open in place (T03) · 10b collapsed like the with-order ticket.
11. **Internal note?** 11a ★ build it (needs backend: `is_internal`) · 11b not needed; keep Send to ‹shop› only.
12. **Assignee / "Mine"?** 12a ★ not now — every admin is notified today and Owner stays the shop · 12b build it (needs backend: `assignee_id`, filter, routed notifications).

## Needs backend

1. Saved views per person (H01 prefs store) — T01.
2. Internal note: `tickets_comments.is_internal`, excluded from email + the shop's view — T08.
3. Persist the last AI suggestion per ticket so reopening does not re-bill — T05.
4. Link / change the order on an existing ticket (web update has no order field; check API `admin.tickets.update`) — T03.
5. Assignee + Mine (`assignee_id`, filter, notification routing) — question 12.
6. CN labels for statuses / severities / categories (DB data, EN only).
7. Attachment count on list payloads (for a paperclip on rows).
8. A tickets `order_id` filter (only `has_order` exists) — also needed by Orders D11.
9. AI click count per ticket, if 6c is picked.

No backend needed but not on the page today: showing the creator ("opened by", RJ staff badge) — the relation exists and the API returns it.

## Could not verify

- Which live role holds which ticket permissions (T15 shows a view-only member).
- What the "Technical Support" create card pre-sets.
- How the Blade wallet box confirms after Apply (T09 card 4 is a design for the after-state).
- Real knowledge-base article titles and AI answers (examples, dotted). The detail page itself was not captured on staging (snap account lacks `view tickets`, TRUTH §11); structure comes from `get_ticket 877` / `860` and the Blade source.
