# Monitoring Dashboard — Redesign Task List

**Project:** Monitoring Dashboard (Filament Admin)
**Route:** `/monitoring`
**Date:** 2026-03-05

## Current State
- 5-tab Filament page: Live Monitor, Flagged, Issues, Daily Report, Delayed Orders
- Dark mode blade view
- `order_monitoring` table + `OrderMonitoring` model
- Morning alert emails + assignment system

## Desired State
- 5 restructured tabs: My Queue, All Orders, Flagged, Issues Log, Director View
- Saved filter presets per user (save/load/delete/default)
- Supplier group + channel filter bar
- Role-aware: ops reps see their queue, director sees supplier scorecards
- Monitoring-first UX: clear status hierarchy, empty states, loading feedback, clickable tracking links

---

## Task 0 — Rename Route from `/usauto-monitoring` to `/monitoring`

**Goal:** Update the Filament page slug and all related references so the dashboard is accessible at `/monitoring`.

**Files to modify:**
- `app/Filament/Pages/USAutoMonitoring.php`
- `app/Console/Commands/SendMonitoringMorningAlerts.php`
- `resources/views/emails/monitoring-morning-alert.blade.php`

**Instructions:**

In `USAutoMonitoring.php`, change the `$slug` property:
```php
// Before
protected static string $slug = 'usauto-monitoring';

// After
protected static string $slug = 'monitoring';
```

In `SendMonitoringMorningAlerts.php`, update the dashboard URL used in the email digest:
```php
// Before
$dashboardUrl = url('/usauto-monitoring');

// After
$dashboardUrl = url('/monitoring');
```

In `monitoring-morning-alert.blade.php`, update any hardcoded URL references from `/usauto-monitoring` to `/monitoring`.

After making changes, clear the Filament component cache:
```bash
ea-php83 artisan filament:clear-cached-components
ea-php83 artisan view:clear
```

Verify the page loads at `https://central.go-parts.com/monitoring` and that the old URL `/usauto-monitoring` returns 404.

**Acceptance criteria:**
- `/monitoring` loads the dashboard correctly
- `/usauto-monitoring` returns 404
- Morning alert email links point to `/monitoring`
- Navigation sidebar link works correctly

**Dependencies:** None — can be done at any time, independently of all other tasks.

---

## Task 1 — Saved Filter Presets: Migration & Model

**Goal:** Create the database foundation for saving user filter presets.

**Files to create:**
- `database/migrations/2026_03_XX_create_user_filter_presets_table.php`
- `app/Models/UserFilterPreset.php`

**Instructions:**

Create a migration for table `user_filter_presets`:
```
id (bigint, PK)
user_id (int unsigned, FK → users.user_id)
page (varchar 50) — e.g. 'monitoring'
name (varchar 100) — user-given preset name
filters (json) — the saved filter state
is_default (tinyint 1, default 0)
created_at, updated_at
INDEX on (user_id, page)
```

Create a simple Eloquent model `UserFilterPreset`:
- `$fillable`: user_id, page, name, filters, is_default
- `$casts`: filters → array, is_default → boolean
- Relationship: `user()` → belongsTo User (FK: user_id → users.user_id)
- Scope: `scopeForPage($query, $page)` — filters by page
- Scope: `scopeForUser($query, $userId)` — filters by user_id

**Acceptance criteria:**
- Migration runs without error
- Model can be instantiated and saved
- No other files modified

**Dependencies:** None — start here.

---

## Task 2 — Saved Filter Presets: Backend Logic

**Goal:** Add save/load/delete preset methods to the Filament page controller.

**File to modify:** `app/Filament/Pages/USAutoMonitoring.php`

**Instructions:**

Add the following Livewire properties near the top of the class:
```php
public ?int $activePresetId = null;
public string $newPresetName = '';
public bool $showSavePresetModal = false;
public bool $newPresetIsDefault = false;
```

Add these Livewire actions to the class:

**`loadDefaultPreset()`** — called in `mount()`:
- Query `UserFilterPreset` where `user_id = auth()->id()`, `page = 'monitoring'`, `is_default = true`
- If found, apply its `filters` JSON to the current Livewire filter properties (`$this->filterSupplier`, `$this->filterStatus`, etc.)
- Set `$this->activePresetId`

**`savePreset()`**:
- Validate `$this->newPresetName` is not empty
- If `$this->newPresetIsDefault`, set all other presets for this user+page to `is_default = false`
- Create `UserFilterPreset` with current filter state as JSON
- Set `$this->activePresetId` to new preset id
- Close modal, show success notification

**`loadPreset(int $presetId)`**:
- Fetch `UserFilterPreset` by id, verify `user_id = auth()->id()`
- Apply its filters JSON to Livewire properties
- Set `$this->activePresetId = $presetId`

**`deletePreset(int $presetId)`**:
- Delete the preset (verify ownership first)
- If it was active (`$this->activePresetId == $presetId`), reset `$activePresetId` to null

**`clearPreset()`**:
- Reset `$this->activePresetId = null` (filters stay as-is, just deactivates preset indicator)

**`getUserPresetsProperty()`** — computed property:
- Returns all `UserFilterPreset` for current user and page `monitoring`

The "current filter state" JSON must capture all filter properties:
`filterSupplier`, `filterStatus`, `filterCarrierStatus`, `filterChannel`, `filterDateFrom`, `filterDateTo`, `filterDatePreset`, `filterAssignedTo`, `activeTab`

**Acceptance criteria:**
- Presets save and reload filters correctly
- Default preset auto-loads on page open
- Deleting a preset removes it from DB
- No preset can be loaded by a different user

**Dependencies:** Task 1 must be complete.

---

## Task 3 — Tab Restructure: My Queue Tab (Backend)

**Goal:** Add a "My Queue" computed property that shows orders assigned to the current user or their supplier group.

**File to modify:** `app/Filament/Pages/USAutoMonitoring.php`

**Instructions:**

Add Livewire property:
```php
public string $myQueueSupplierGroup = ''; // user's default supplier group
```

Add computed property `getMyQueueProperty()`:
- Start from the same base query as `getLiveOrdersProperty()` (orders → orders_items → shipments → suppliers → order_monitoring)
- Add a WHERE clause: `order_monitoring.assigned_to = auth()->id()` OR `order_monitoring.assigned_to IS NULL AND {supplier filter matches user's group}`
- Order by: `order_monitoring.assigned_to = {user_id} DESC` (assigned to me first), then `orders.date_placed ASC` (oldest first)
- Paginate 50
- Also add `getMyQueueCountProperty()` — count of items in queue assigned to the current user specifically

The supplier group filter should use the existing `SUPPLIER_CATEGORIES` constant from `OrderMonitoring` model. If `$myQueueSupplierGroup` is set, add a supplier code filter.

**Acceptance criteria:**
- My Queue shows orders assigned to the logged-in user at the top
- Orders in the user's supplier group (if set) appear below assigned orders
- Count badge on the tab shows only items assigned to the current user
- Empty state if no assigned orders

**Dependencies:** None (can run in parallel with Task 4).

---

## Task 4 — Tab Restructure: Director View (Backend)

**Goal:** Combine the existing Daily Report and Delayed Orders tabs into a single "Director View" with supplier scorecards.

**File to modify:** `app/Filament/Pages/USAutoMonitoring.php`

**Instructions:**

The Director View needs three data sets:

**1. `getSupplierScorecardProperty()`** — one row per supplier category:
```
supplier_category | total_orders | shipped | unshipped | delayed | issues | on_time_pct | avg_days_in_transit
```
- Group by supplier category (map supplier codes using `OrderMonitoring::SUPPLIER_CATEGORIES`)
- `delayed` = orders where `shipments.date_delivery > shipments.date_submitted + interval 5 day` or not delivered past expected date
- `issues` = orders where `current_sub_status` maps to LIT/RWP/DMG/BO (use `OrderMonitoring::ISSUE_TYPE_MAP`)
- `on_time_pct` = (shipped - delayed) / shipped * 100
- Respect the date range filters (`$this->filterDateFrom`, `$this->filterDateTo`)

**2. Keep `getReportStatsProperty()`** — existing monthly stats widgets (total, on_time, delayed, issues, unshipped) — no changes needed here.

**3. Keep `getDailyBreakdownProperty()`** — existing daily breakdown table — no changes needed.

Remove `getDelayedOrdersProperty()` — delayed orders will be a filter inside the All Orders tab instead of a separate tab.

**Acceptance criteria:**
- Supplier scorecard shows one row per supplier category with all metrics
- On-time % calculated correctly
- Respects date filters
- Delayed Orders tab is removed from the backend (queries deleted)

**Dependencies:** None (can run in parallel with Task 3).

---

## Task 5 — Tab Restructure: Full Blade View Rewrite

**Goal:** Restructure the Blade view from 5 current tabs to the new 5-tab structure with improved layout.

**File to modify:** `resources/views/filament/pages/monitoring.blade.php`

**Current tabs:** Live Monitor, Flagged, Issues, Daily Report, Delayed Orders

**New tabs:**
1. **My Queue** — personal action queue (uses `$this->myQueue`)
2. **All Orders** — full order list with full filter bar (uses `$this->liveOrders`)
3. **Flagged** — auto-flagged items needing attention (existing, no query change)
4. **Issues Log** — LIT, RWP, DMG, BO issues (existing, no query change)
5. **Director View** — supplier scorecard + daily breakdown (uses `$this->supplierScorecard` + existing stats/breakdown)

**Tab headers:**
- My Queue: show badge with count from `$this->myQueueCount` (only assigned-to-me count)
- Flagged: show badge with count from `$this->flaggedCount`
- All other tabs: no badge

**My Queue tab layout:**
- Top section: "Assigned to Me" orders (highlighted with left indigo border, same as existing assigned row style)
- Below: "In My Group" orders (if supplier group is set)
- Same columns as All Orders table
- Same action buttons (Assign, Flag, Follow-up, Resolve)

**All Orders tab layout:**
- Full filter bar (supplier group dropdown, channel dropdown, status dropdown, carrier status dropdown, date range, assigned-to toggle)
- Full table with all columns from existing Live Monitor
- "Preset: {name} ✕" indicator appears above filter bar when a preset is active

**Director View tab layout:**
- Top row: 5 stat cards (Total Orders, On Time %, Delayed, Open Issues, Unshipped) — existing `$this->reportStats`
- Supplier Scorecard table — new, one row per supplier category
- Daily Breakdown table — existing `$this->dailyBreakdown`
- No action buttons — read-only view

**Keep all existing dark-mode styling patterns:**
- `bg-gray-800` cards, `border-gray-700`, `rounded-xl`
- Semi-transparent badges: `bg-{color}-500/20 text-{color}-400 ring-1 ring-{color}-500/30`
- `hover:bg-gray-700/30 transition-colors` on rows
- `bg-gray-900/50` table headers

**Acceptance criteria:**
- All 5 tabs render without errors
- My Queue shows assigned orders at top
- Director View shows scorecard table
- Dark mode looks correct throughout
- No references to removed tabs (Daily Report, Delayed Orders)

**Dependencies:** Tasks 3 and 4 must be complete.

---

## Task 6 — Saved Filter Presets: UI

**Goal:** Add the preset save/load/delete UI to the filter bar.

**File to modify:** `resources/views/filament/pages/monitoring.blade.php`

**Instructions:**

In the **All Orders tab** filter bar, add a "Presets" section above or alongside the existing filters:

**Preset indicator** (shown when `$this->activePresetId` is not null):
```
[Preset: My Monitoring Filters  ✕]
```
- Styled as a small pill/badge in indigo
- Clicking ✕ calls `wire:click="clearPreset"`

**Presets dropdown button:**
- Label: "My Presets ▾" (or just a bookmark icon)
- Opens a small dropdown listing `$this->userPresets`
- Each item: preset name + "Set Default" star icon + trash icon
- Clicking preset name: `wire:click="loadPreset({{ $preset->id }})"`
- Clicking trash: `wire:click="deletePreset({{ $preset->id }})"`
- Bottom of dropdown: "+ Save Current Filters" link → sets `wire:click="$set('showSavePresetModal', true)"`

**Save Preset Modal** (triggered by `$this->showSavePresetModal`):
- Input: Preset name (`wire:model="newPresetName"`)
- Checkbox: "Set as my default" (`wire:model="newPresetIsDefault"`)
- Button: "Save" (`wire:click="savePreset"`) + "Cancel"
- Same modal styling as existing action modal in the blade view

Also add the preset bar to the **My Queue tab** filter area (same behavior, same presets — presets are page-wide, not tab-specific).

**Acceptance criteria:**
- Preset indicator shows/hides correctly
- Dropdown lists user's saved presets
- Save modal works end-to-end
- Delete removes from list immediately (Livewire reactivity)
- Default preset auto-applies on page load (from Task 2's `mount()` call)

**Dependencies:** Tasks 2 and 5 must be complete.

---

## Task 7 — Filter Bar Redesign

**Goal:** Add supplier group and channel filters; make the filter bar consistent and clean across tabs.

**Files to modify:**
- `app/Filament/Pages/USAutoMonitoring.php`
- `resources/views/filament/pages/monitoring.blade.php`

**Instructions:**

**Backend — add to `USAutoMonitoring.php`:**
```php
public string $filterSupplierGroup = '';  // IN_STOCK, EXPRS_MI, DEPO, TYC, PBI, CP
public string $filterChannel = '';         // b2b, b2c, all
```

Apply `$filterSupplierGroup` in `getLiveOrdersProperty()` and `getMyQueueProperty()`:
- Map supplier group to supplier codes using `OrderMonitoring::SUPPLIER_CATEGORIES`
- Add `->whereIn('sup.code', $codes)` when filter is set

Apply `$filterChannel` by filtering `orders.source`:
- b2b: `o.source = 500`
- b2c: `o.source != 500`
- all: no filter

**Frontend — filter bar layout (All Orders tab):**
```
[Date From] [Date To]  [This Month ▾]  |  [Supplier ▾]  [Channel ▾]  [Status ▾]  [Carrier Status ▾]  [Assigned To ▾]
```
- All dropdowns use same `$selectCls` styling variable
- Date presets dropdown: Today, Yesterday, This Week, This Month, Last Month, Custom
- Supplier dropdown options: All Suppliers, IN STOCK (USA*), EXPRS-MI, DEPO, TYC, PBI, CP
- Channel dropdown: All Channels, B2B (CarParts), B2C (Website/Marketplace)
- Assigned To dropdown: Everyone, Assigned to Me, Unassigned

**Acceptance criteria:**
- Supplier group filter correctly narrows results to that supplier category's codes
- Channel filter correctly filters by `orders.source`
- Date presets set `filterDateFrom`/`filterDateTo` correctly
- Filters apply in real-time (Livewire reactivity via `wire:model`)
- Filter bar is visually clean and fits without horizontal scroll at 1280px width

**Dependencies:** Task 5 must be complete.

---

## Task 8 — UI/UX Polish: Monitoring-First Design

**Goal:** Ensure the page looks and feels like a professional monitoring tool — clear status hierarchy, easy scanning, nothing confusing.

**Files to modify:** `resources/views/filament/pages/monitoring.blade.php`

**Checklist of improvements:**

**Status color consistency:**
- Define a single PHP array at the top of the template mapping all statuses to badge colors — use it everywhere (no hardcoded colors mid-template)
- Carrier status: label_created=amber, in_transit=blue, out_for_delivery=purple, delivered=green, exception=red, returned=orange
- Monitor status: monitoring=gray, flagged=amber, action_needed=red, escalated=red+ring, resolved=green, closed=gray

**Empty states:**
- Each tab must have a meaningful empty state (not just a blank table)
- My Queue empty: "You have no assigned orders. Check All Orders to find items to work on."
- Flagged empty: "No flagged orders. Everything looks good."
- Issues empty: "No issues found for this period."

**Loading feedback:**
- Add `wire:loading.class="opacity-50"` on table containers so users see feedback during filter changes
- Add a small spinner next to the active tab label while loading

**Row actions clarity:**
- Action buttons (Assign, Flag, FU, Resolve) should always be visible on hover — not hidden behind a "..." menu
- Use icon buttons with tooltips (`title` attribute) to save horizontal space
- Button order: Assign → Flag → Follow-up → Resolve (left to right, priority order)

**Tracking number links:**
- Tracking numbers in any tab should be hyperlinked to the carrier's tracking page
- FedEx: `https://www.fedex.com/fedextrack/?trknbr={tracking}`
- UPS: `https://www.ups.com/track?tracknum={tracking}`
- USPS: `https://tools.usps.com/go/TrackConfirmAction?tLabels={tracking}`
- Default (unknown carrier): `https://parcelsapp.com/en/tracking/{tracking}`
- Open in new tab (`target="_blank"`)

**Page header:**
- Add a subtle page subtitle: "Last updated: {now()->format('g:i A')}" that refreshes with Livewire
- Add a manual "Refresh" button that calls `$refresh` on the Livewire component

**Acceptance criteria:**
- All tabs have proper empty states
- Tracking numbers are clickable links opening in new tab
- Status badges use consistent colors across all tabs
- Loading state visible during filter changes
- Page header shows last updated time
- Action buttons visible on row hover with tooltips

**Dependencies:** Tasks 5, 6, and 7 must be complete.

---

## Summary

| # | Task | Depends On | Effort |
|---|---|---|---|
| 1 | Migration & Model (UserFilterPreset) | — | Small |
| 2 | Saved Filter Backend Logic | 1 | Medium |
| 3 | My Queue Tab — Backend | — | Medium |
| 4 | Director View — Backend | — | Medium |
| 5 | Full Blade View Restructure | 3, 4 | Large |
| 6 | Saved Filter Presets — UI | 2, 5 | Medium |
| 7 | Filter Bar Redesign | 5 | Medium |
| 8 | UI/UX Polish | 5, 6, 7 | Medium |

Tasks 1, 3, and 4 can all start in parallel. Tasks 2 and 5 are the critical path items.
