# Dashboard — Implementation Tasks

> Produced by the Reversa **Writer** (phase: generation) · doc_level: `complete`
> Generated on 2026-09-19

**Confidence scale:** 🟢 CONFIRMED · 🟡 INFERRED · 🔴 GAP

## Prerequisites

- [ ] Dependencies listed in `design.md` are available: `orders`, `order_product`, `products`, `customers` tables and their models; the `admin` auth guard/middleware (see `auth` unit); a date library and the `pages.dashboard` render surface.
- [ ] Schema/migrations compatible: `orders.status` + `orders.created_at`, `order_product.product_id`/`qty`, `products.deleted_at` (soft-deletes). See `_reversa_sdd/data-dictionary.md` and `_reversa_sdd/erd-complete.md`.
- [ ] Timezone/locale documented: KPI and buckets use the **server local date/time** (`CURDATE()`, `Carbon::now()`); confirm the effective app timezone.

## Tasks

> Each task references the legacy file the behavior was extracted from.

- [ ] T-01 — Expose only `GET /dashboard` behind the `['web','admin']` group; do not implement the other resource verbs.
  - Legacy origin: `routes/web.php:42`, `app/Http/Controllers/DashboardController.php:28`
  - Done when: `GET /dashboard` renders for an authenticated admin and redirects anonymous users to `auth/login`; no create/store/show/edit/update/destroy actions exist.
  - Confidence: 🟢

- [ ] T-02 — Compute the three KPIs: total products, total customers, and today's completed orders (`status='done'` AND `DATE(created_at)=CURDATE()`).
  - Legacy origin: `DashboardController.php:34-38`
  - Done when: `total.products`/`total.customers` equal the table counts; `total.orders` counts only today's done orders.
  - Confidence: 🟢

- [x] T-03 — Resolve the `range` parameter with a `month` default and whitelist to `day`/`week`/`month`.
  - Legacy origin: `DashboardController.php:47-50`
  - Done when: missing/blank `range` → month; an unexpected value falls back safely rather than erroring.
  - Confidence: 🟢 — ✅ Fixed 2026-09-19 (was a 🔴 no-`default` gap; team added the whitelist).

- [ ] T-04 — Build the `day` chart: three buckets `Sáng` (06–12), `Trưa` (12–18), `Chiều` (18–24), excluding 00:00–06:00.
  - Legacy origin: `DashboardController.php:52-64`, `_reversa_sdd/flowcharts/dashboard.md`
  - Done when: three ordered buckets exist with the correct half-open windows; a 03:00 order lands in none. Reimplement with explicit per-window boundaries (do NOT depend on Carbon mutation).
  - Confidence: 🟢

- [ ] T-05 — Build the `week` chart: one bucket per day `startOfWeek→endOfWeek`, labelled by ISO date, counting `DATE(created_at)=day`.
  - Legacy origin: `DashboardController.php:65-77`
  - Done when: the week's days each get a bucket with the correct per-day count.
  - Confidence: 🟢

- [x] T-06 — Build the `month` chart: contiguous 6-day windows across the current month labelled `dd/mm-dd/mm`, plus `yearRange` = distinct covered year(s) joined by ` - `.
  - Legacy origin: `DashboardController.php:78-99`
  - Done when: 6-day windows are produced with correct `[start, end]` counts and `yearRange`, covering every day of the month with no gap. ✅ Fixed 2026-09-22 (`questions.md#question-1`) — the `WHEN` upper bound is now inclusive (`<=`), closing the 1-day gap that previously dropped every 6th day. A separate month-boundary spillover (non-6-multiple month lengths) remains open, tracked in `requirements.md` BR-05.
  - Confidence: 🟢

- [ ] T-07 — Aggregate order counts per bucket and pivot into aligned `labelChart`/`valueChart`, defaulting empty buckets to `0` and preserving label order.
  - Legacy origin: `DashboardController.php:102-112`
  - Done when: `valueChart[i]` is the `COUNT(*)` for `labelChart[i]`; empty buckets show `0` in original order.
  - Confidence: 🟢

- [ ] T-08 — Compute the top-10 products by `SUM(order_product.qty)` desc, joining `products` and excluding soft-deleted (`deleted_at IS NULL`); expose `id`, `name`, `quantity`.
  - Legacy origin: `DashboardController.php:114-119`
  - Done when: ≤10 rows ordered by total qty desc; a soft-deleted product never appears.
  - Confidence: 🟢

- [ ] T-09 — Pass the view contract (`total`, `chartRange`, `range`, `yearRange`, `labelChart`, `valueChart`, `products`) to the dashboard view.
  - Legacy origin: `DashboardController.php:40-44,121`
  - Done when: the view receives all seven variables with the shapes in `design.md`.
  - Confidence: 🟢

- [ ] T-10 — Do NOT reintroduce the dashboard as the landing page; keep root→`/pos` and leave the Encore dashboard-builder path dead.
  - Legacy origin: `routes/web.php:22`, `app/Http/Controllers/HomeController.php:12-40`
  - Done when: `/` still lands on `/pos`; `/dashboard` is reachable directly only.
  - Confidence: 🟢

- [ ] T-11 — Drop the dead/erroneous imports (`App\Models\OrderProduct`, `Cassandra\Custom`) — do not carry them into the reimplementation.
  - Legacy origin: `DashboardController.php:8,12`
  - Done when: the reimplemented controller imports only what it uses.
  - Confidence: 🟢

## Test Tasks

- [ ] TT-01 — Happy path: authenticated `GET /dashboard` returns the view with correct KPIs and a month chart (see `requirements.md` Acceptance Criteria).
- [ ] TT-02 — KPI `orders` counts only today's `done` orders (draft today and done-yesterday are excluded).
- [ ] TT-03 — `range=day` buckets a 07:00/14:00/20:00 order into `Sáng`/`Trưa`/`Chiều` and excludes a 03:00 order.
- [ ] TT-04 — Empty bucket renders as `0` with its label retained, in order.
- [ ] TT-05 — Top-10 excludes a soft-deleted product with historical sales and is ordered by total qty desc, capped at 10.
- [ ] TT-06 — Anonymous `GET /dashboard` redirects to `auth/login`.
- [ ] TT-07 — `range=week` produces one bucket per weekday with correct per-day counts; `range=month` produces 6-day windows and a correct `yearRange` (regression guard for the T-06 fix).
- [ ] TT-08 — Unexpected `range` value falls back safely to `month` (no error / no undefined query) — verifies the T-03 whitelist.

## Data Migration Tasks (if applicable)

- [ ] TM-01 — None. The dashboard is read-only over existing operational tables; no dashboard-owned data to migrate. Ensure `orders.status`, `orders.created_at`, `order_product.qty`, `products.deleted_at` are migrated by their owning units (`orders`, `products`). Reference: `_reversa_sdd/data-dictionary.md`.

## Suggested Order

1. T-01 → T-02 (route + KPIs) — the minimal reachable screen.
2. T-03 (range resolution + whitelist) — gate for all chart tasks.
3. T-04 → T-06 (day/week/month builders) — can be built in parallel once T-03 lands.
4. T-07 (pivot to labelChart/valueChart) — depends on the builders.
5. T-08 → T-09 (top products + view contract) — completes the view payload.
6. T-10 → T-11 (landing-page guard + import cleanup) — verification/cleanup.

## Pending Gaps (🔴)

- ✅ **`range` fallback (T-03) — fixed 2026-09-19.** Legacy previously had no `default` case; now coerces an out-of-whitelist `range` to `month`.
- ✅ **Month-chart 6th-day gap (T-06) — fixed 2026-09-22.** Confirmed a bug (`questions.md#question-1`); the `WHEN` clause's upper bound is now inclusive, closing the gap.
- 🟡 **Month-boundary spillover (T-06, not yet fixed).** For a non-6-multiple month length, the last window can spill a few days into the next calendar month (e.g. October's last bucket reaching into early November) — a separate, pre-existing quirk in the loop's termination condition, deferred.
- 🔴 **Chart metric confirmation.** Confirm whether the sales chart should remain an order **count** or become revenue / units sold before porting (see `design.md` Risks & Gaps).
- 🔴 **Dashboard latency at scale.** No cache and a non-SARGable today-KPI predicate; confirm acceptable load time / whether caching or precomputation is needed at production volumes.
