# TRUTH — Gantt detail data + the current Dashboard (read, not guessed)

Read-only, 2026-10-18. Code at `RJmtm/3.0_Backend` **origin/main 6787e7873** (`/Users/rj/CascadeProjects/laravel3.0`, `git fetch` then `git show origin/main:<path>`; base branch `main` per `projects/laravel-admin/laravel-admin.md:5`). API ops from `newadmin-api-kit` 0577c24 (`bruno/collection/**`, `spec/laravel-admin-fixtures.json`) and the Atlas catalogue `laravel-admin/catalogue/modules/orders`. Prod shapes via rj-admin MCP **get/list only**. People are anonymised in every board; order ids, factory codes, supplier codes and fabric codes are real shapes.

Why this doc exists: Frank, 18 Oct: *"the Gantt pop-up is very small — I don't think you fully understand all the data that comes with the Gantt chart"* and *"I need stuff on my dashboard — I currently have them on my dashboard."*

---

## 1. What the old gantt shows on hover / click / expand

| Interaction | What it really does | Source |
|---|---|---|
| Hover a cell | **Nothing** (no tooltip). The cell is a coloured `td` with `data-colors` | `resources/views/admin/orders/gantt.blade.php:1093-1095` |
| `+` on an order id | fetches `GET /admin/orders/{id}/details?month&year` and draws one row per **piece**: `Product name: (code)` + day cells | `gantt.blade.php:1246-1292`; route `routes/web.php:1402` → `OrderController@getOrderDetails` `:2840` |
| Click any day cell (order or piece row) | opens a **modal** titled `0457 \| Customer` (zero-padded) with the HTML from `GET /admin/orders/{id}/modal-details[?product_id&group_number]` | `gantt.blade.php:1309-1357`; `routes/web.php:1403` → `OrderController@getOrderModalDetails` `:2948-2961` |
| The modal body | table **PROCESS · Action Date · Due Date · (ship icon)**, one block per piece headed `4510 - 1 \| Jacket`; under **INCOMING COMPONENTS ARRIVAL** an expanded sub-table **Component (code) · Bought (+ source sent/queued/manual) · Expected (date, or the reason in words) · Status (on time / late / waiting / no estimate) · Arrived**; under **SHIPMENT-IN-PROGRESS \| 7.2** a send icon that loads live courier tracking inline | `resources/views/admin/orders/gantt-order-details.blade.php:428-579`, reason/verdict/source maps `:18-42` |
| Component enrichment | `addSubComponentsForIncomingArrival()` → `ComponentTimelineBuilder::build()`; shell cloth prepended (name, fabric code, scan date) | `OrderController.php` (method after `fetchPhaseSummary`, `:2963+`) |
| Day colours past the factory due | the 0.2 cells **stop at `end_date`**: an order 5 days over due shows **blank** cells after its due day — the old grid hides lateness | `GanttDayGrid::build` / `StatusDateRangeHandler`; prod `gantt_detail` 4496 Oct 2026: `#D6E9FF` 9→14 Oct, then `[]` |

Conclusion: the real "popup" is a **multi-piece, multi-section record** (every step per piece with action + due dates, a 5-column component table, courier tracking), not a 4-line card. The r1 hover card carried ~10 of the fields below.

## 2. Every field available per order on the gantt surfaces

| # | Field | Grain | Op / source |
|---|---|---|---|
| 1 | order_id | order | `admin.orders.gantt-grid` (`GanttDetailController::ganttGrid` `:163-213`) |
| 2 | customer_name | order | gantt-grid · gantt-detail · phase-summary (`order_title`) |
| 3 | factory_name (codes per product, comma-joined) | order | `Order::getFactoriesGroupedByProduct()` via gantt-grid `:191` |
| 4 | days {date: [#hex…]} | order | gantt-grid `:192` (`GanttDayGrid::build`) |
| 5 | tracking_number (outbound shipment) | order | gantt-detail / phase-summary (`OrderShipment::getTrackingNumberForOrder`, `Gantt.php:198-207`) |
| 6 | product id · name (Jacket…) | piece | gantt-detail `products[]` (`Gantt.php:455-462`) |
| 7 | label (JA/TR/WC/SH) · type (ub/lb) | piece | gantt-detail |
| 8 | group_number | piece | gantt-detail · phase-summary · orders/due |
| 9 | code `4510-1` | piece | `ProductNumberService::generateProductNumberWithOrderAndProduct` |
| 10 | fabric_code | piece | gantt-detail (`Gantt.php:96-101`, archived-safe `fabricCodeWithTrashed`) |
| 11 | fabric_name (`H&S \| Sherry Stretch (HS2413)`) | piece | gantt-detail (`GanttDetailController.php:68-75`) |
| 12 | statuses {date: colours} per piece | piece | gantt-detail |
| 13 | product_statuses[]: title, created_at, updated_at, created_by, status_type (qr_code/normal), retailer_status | piece × step | gantt-detail (`Gantt.php:470-500`) |
| 14 | step start_date / end_date (due) | piece × step | phase-summary `statuses[]` (`StatusDateRangeHandler`) |
| 15 | step colour | piece × step | `ProcessList::getColorForStatus` |
| 16 | sf_tracking_number on FACTORY SHIPPED \| 0.25 | piece | phase-summary (`GanttDetailController::factoryCourierNumber` `:219-230`) |
| 17 | phases[]: formatted_title, created_at, fabric_code | piece | phase-summary `include=phases` (Wave23 builder) |
| 18 | component name · code | piece × component | modal sub-table / `get_order_production_timeline` components |
| 19 | component supplier | piece × component | timeline components (`supplier_name`) |
| 20 | bought_at + source (sent / queued / manual) | component | `ComponentBuyLookup` |
| 21 | expected_at or expected_at_reason (no delivery days on file · no cut-off on file · supplier working days not usable · buy not confirmed) | component | `ExpectedArrivalCalculator` |
| 22 | verdict (on time · late · waiting · no estimate) | component | same |
| 23 | arrived / scanned_in_at | component | `component_storage_insert` |
| 24 | factory per piece · factory start · **factory due (`end_date`)** · arrival_date | piece | `order_assigned_factories` (`OrderController@due :3250-3300`, `admin.orders.due`) |
| 25 | days over due · grouped label `4503-1-2` | garment group | countdown (`order-countdown-widget.blade.php:24-75`) |
| 26 | hand-finish house · start · due · arrival | piece | `order_assigned_hand_finish_factories` (`OrderHandFinishing.php:12-36`); steps 3.5.1–3.5.6 (`ProcessList.php:27-32`) |
| 27 | order status · previous_status | order | `get_order_production_timeline` / order resource |
| 28 | retailer (brandname) · account type | order | same; quick-search returns retailer too |
| 29 | is_express · is_returning_order | order | same (`DashboardController@quickSearch` `:258-291`) |
| 30 | payment_date · entered_manufacturing_at · created_at | order | timeline tool |
| 31 | combo per detail (`2-Piece`, `3-Piece`) | piece | timeline `details[].product_name` |
| 32 | fabric overdue alert: kind, supplier (routed), material, meterage cm, bought, expected, alerted_at, contacted_at | order × component | `FabricOverdueRowPresenter` (`overdueSourceRows :321`, `presentBySupplier :430`); MCP `incoming_components tab=alarm` |
| 33 | open tickets on the order: id, title, status, severity, category › sub-category | order | `search_tickets search=<id>` (prod 4496: `4 Days \| Your Order is Experiencing a Delay`, [Admin] Orders) |
| 34 | notes (index, text) | order | `get_order_detail view=notes_index` (`_notes`) |
| 35 | special handling: owner (fabric code), code `SP-0010`, audience, instruction | order | `view=special_handling` (prod 4496: tech/stretch fabric instruction to the factory) |
| 36 | QC photos (4 checkpoints, presigned) | piece | `orders/{order}/qc-photos` |

**Count: 36 real field groups (≈ 60 scalar fields) per order.** The r1 hover card showed fields 1, 2, 3, 6/7, 9, 10, 13 (title + since), 24 (due) — 8 of 36.

### Real actions that belong on a gantt peek (all exist)
Open order (`admin.orders.show`) · **Raise issue** (ticket modal, with the "Put order on hold" toggle — TRUTH-ORDERS §4.3) · **Cloud print** (`admin.orders.cloud-print`) · Print order / mark-up / QC tag / shirt tag (`admin.orders.print`) · **Send phase summary to factory WeChat** (`admin.orders.phase-summary.cloud-export`) · Phase summary link (`…/phase-summary/link`) · **Send hand-finishing slip** (`admin.orders.hand-finishing.cloud-export`, `…/link`) · Cloud order (`admin.orders.cloud-order`) · Track shipment (modal ship icon) · QC photos · Copy WeChat line (countdown formatter). **Not real:** a hold button, change status, assign factory, reschedule.

### Prod shapes read (MCP, 2026-10-18, people anonymised)
- **4510** — FRR, 2 pieces (4510-1 Jacket, 4510-2 Trousers), combo 2-Piece, fabric 9900; RFF 4 Oct 21:29 → Build 9 Oct 09:13 → INCOMING COMPONENTS ARRIVAL 12 Oct 04:00 → BEGIN 0.1 + 0.2 12 Oct 10:35; factory due **Wed 21 Oct**; 1 component (JA Lining YC62088, SYC, bought 9 Oct 17:14 sent, expected 10 Oct, arrived 10 Oct, on time); returning order; not express.
- **4496** — FRR, **express**, 3-Piece (JA · TR · WC), fabric 1324027 `H&S | Sherry Stretch (HS2413)`; 0.2 from 9 Oct; factory due **14 Oct → 5 days over**; 3 components (JA Lining YC62059 SYC · WC Inner Lining YC62059 SYC · WC Outer Lining PT042 SHONG), all on time; 1 ticket `4 Days | Your Order is Experiencing a Delay` (Closed, Medium); special handling SP-0010 (factory); no notes.
- **4503** — two garment groups with **different factory dues**: group 0 due 18 Oct (1 day over), group 1 due 21 Oct.
- Fabric overdue (alarm): **SVBCHK · #4521 · main fabric 228165 · 320 cm**, bought 14 Oct, expected 16 Oct, alerted 17 Oct, not contacted.

## 3. The current Dashboard (`/admin/dashboard`) — every block

`DashboardController@index` (`app/Http/Controllers/Admin/DashboardController.php:36-209`) → `resources/views/admin/dashboard.blade.php` (77 lines). REST twin: `GET /api/v1/admin/dashboard` = `admin.dashboard.index` (Wave19 `DashboardController::index`), "the four admin-homepage widgets in one read".

| # | Widget | Default | Data | Source |
|---|---|---|---|---|
| W1 | **Quick Order Search** (row 1, 8/12) | **collapsed** | input "Order # or Customer Name" → up to 10 rows: **Order #** (+ EXPRESS badge) · Customer · Retailer · Status (coloured pill) · Actions **View · Edit**; loading, "No orders found." | `partials/order-quick-search-widget.blade.php`; `admin.dashboard.quick-search` → `DashboardController@quickSearch :250-294` |
| W2 | **Queue Health** (row 1, 4/12, **super admin only**) | collapsed | **Pending Jobs** (count) · **Failed Jobs** (red pill when > 0, green 0) + **View** → failed jobs | `partials/queue-health-widget.blade.php`; `queueStats` `:170-173` |
| W3 | **Order Countdown & Staff Needed** (row 2, full width) | **open** | header badges **N 订单** · **N 逾期** (garment groups); 5 columns **DUE <Thu> · DUE <Wed> · DUE <Tue> · DUE TODAY · OVER DUE**, each **Copy All <day>**; inside each a section per **factory** (CN name) with **Copy**; inside each a pill per **garment group** (`4503-1-2`); OVER DUE pills carry **N Days**; empty: "No active orders in the production pipeline." | `partials/order-countdown-widget.blade.php:1-323`; bucketing `DashboardController.php:62-113` (Asia/Shanghai business day, `FactoryGarmentDueSource`) |
| W4 | **OVERDUE COMPONENTS** (6th column inside W3) | open when rows exist | count badge · **Copy All Overdue Components** · one card per **routed supplier** with **Copy** · rows **#order · material · meterage cm · ✓ contacted N ago** · **Contacted** button (stamps contacted_at, never removes) | `dashboard/_fabric-overdue.blade.php:26-146`; `FabricOverdueRowPresenter`; copy text `FabricChaseMessageFormatter` |
| W5 | **STAFF NEEDED** strip (inside W3) | open | 4 tiles **today → +3**: day name · staff count (`StaffSetting::calculateStaffCount(labour units)`) · **Copy** (stored StaffSetting text for `TODAY`/`Day_2…`) · **Copy All** | `order-countdown-widget.blade.php:287-317`; `DashboardController.php:115-167` |

**Nothing else is on the dashboard**: no charts, no KPIs, no factory load, no gantt. (Analytics is a separate future workstream; dashboard ≠ analytics.)

### Copy formats (byte-true)
- Day column: `周三要交货：` / `今天要发：` / `逾期：` prefix (`widget :107-111`), then factory, then one grouped label per line.
- OVER DUE + TODAY per factory: `FactoryChaseMessageFormatter` — `逾期：\nFRR\n5 Days --- 4496-1-2-3\n…` and `今天要发：\nFRR\n4514-1-2` (`app/Services/WeChat/FactoryChaseMessageFormatter.php`, `SEP = ' --- '`).
- Components: `FabricChaseMessageFormatter` — `Hello,\n\nThe following components … shipping confirmation?\n\n[SVBCHK]\n- 4521: 228165 (320.00cm)\n\nThank you.`
- Staff: `ocdCopy2` — `Monday\n0\n\nTuesday\n0…`. Every copy ends in a browser `alert("Text copied")`.

### Prod today (MCP `view=due` + `get_platform_health`, Mon 19 Oct, Shanghai day)
`DUE THU` FRR 4464-1 · 4464-2 · `DUE WED` FRR 4503-3-4 · 4515-1-2 · 4510-1-2 · 4517-1-2 · `DUE TUE` — · `DUE TODAY` FRR 4499-1 · 4499-2 · 4514-1-2 · `OVER DUE` FRR 4496 (5) · 4509 (2) · 4486 · 4487 · 4503-1-2 · 4505 · 4512 (1) · 4274 (118) · 3838 (309) · 3751 (336); C 2752 (740) · 3783 (307); WZ 2767 (733); W 2767-4 (733); FX 4274 (119). Badges **24 订单 · 15 逾期**. Staff needed **0 · 0 · 0 · 0**. Queue **0 pending · 22 failed**. Overdue components **1** (SVBCHK).

## 4. Design consequences
1. The gantt click target must be a **large peek** (side sheet `--sheet-w-xl` 960) holding fields 1–36; hover stays a 4-line preview.
2. Lateness must be drawn (the old grid hides it after `end_date`).
3. The dashboard keeps **W1–W5 exactly**, collapse defaults included, and gains two widgets that read existing data: **factory countdown summary** (days → factories → counts, links to Gantt › Countdown) and **factory load** (garments in 0.1–0.2 per day, links to Gantt › Factories).
4. Needs backend (small, read-only): `gantt-grid` rows lack factory due, current step, express (r1 §4); `orders/due` horizon caps at +3; no factory capacity field.
