# Admin Dashboard — Wallets & Collaboration Ledger (Frontend Spec)

**Audience:** Frontend engineers building the admin oversight pages for marketplace money flow.
**Backend status:** Almost everything in this doc is `BACKEND TODO`. The data is all there (`mp_brand_wallets`, `mp_wallet_transactions`, `mp_collaboration_transfers`, `mp_creator_withdrawals`), but no admin endpoints expose it yet. This doc locks the API contract so BE and FE can build in parallel.

> ⚠ **Admin role gating reminder:** today the marketplace admin routes are protected only by `auth:sanctum` — there is no `admin.only` middleware in place yet. The FE must hide the admin nav entirely for non-admin users until BE adds the role check. (See the existing follow-up about admin role enforcement.)

---

## 1. The "Money Center" overview page

### `GET /api/admin/marketplace/money/overview` ⚠️ BACKEND TODO

A single landing page that answers "is everything healthy?" at a glance.

**Proposed query params:**
| Param | Type | Default | Notes |
|---|---|---|---|
| `from` | date | first day of current month | YYYY-MM-DD |
| `to`   | date | today | YYYY-MM-DD |

**Proposed response (200):**
```json
{
  "success": true,
  "data": {
    "period": { "from": "2026-04-01", "to": "2026-04-30" },
    "currency": "SAR",
    "totals": {
      "brand_wallets_balance":     45230.00,
      "brand_wallets_pending":      3450.00,
      "platform_revenue_period":    8420.00,
      "vat_collected_period":       3870.00,
      "creator_earnings_period":   42100.00,
      "creator_withdrawals_paid":  18000.00,
      "creator_outstanding":       24100.00
    },
    "counts": {
      "brands_with_wallet":   142,
      "active_collabs":        37,
      "completed_collabs":    218,
      "pending_withdrawals":    9
    }
  }
}
```

**Suggested layout — KPI strip across the top:**

```
┌───────────────────┬───────────────────┬───────────────────┬───────────────────┐
│ Brand wallets     │ Platform revenue  │ VAT collected     │ Creator earnings  │
│ 45,230 SAR        │ 8,420 SAR         │ 3,870 SAR         │ 42,100 SAR        │
│ +3,450 pending    │ this month        │ this month        │ this month        │
└───────────────────┴───────────────────┴───────────────────┴───────────────────┘
```

Each card should be clickable → drills into the detailed page (sections 2-5 below).

Below the KPI strip, two side-by-side mini-tables:
- **Top brand spenders this period** (sourced from `mp_wallet_transactions` where source=collaboration_payment)
- **Top earning creators this period** (sourced from `mp_collaboration_transfers` where account_type=creator)

---

## 2. Brand wallets list

### `GET /api/admin/marketplace/brand-wallets` ⚠️ BACKEND TODO

**Proposed query params:**
| Param | Type | Default | Notes |
|---|---|---|---|
| `q` | string | — | Search by brand name / email |
| `min_balance` | number | — | Filter |
| `max_balance` | number | — | Filter |
| `sort` | enum | `balance:desc` | `balance:asc\|desc`, `pending:asc\|desc`, `last_activity:desc` |
| `per_page` | int | 25 | Max 100 |

**Proposed response (200):**
```json
{
  "success": true,
  "data": [
    {
      "brand_id":         42,
      "brand_name":       "Acme Coffee",
      "brand_email":      "ops@acme.sa",
      "balance":          1240.00,
      "pending":            50.00,
      "currency":         "SAR",
      "lifetime_topped_up":  9500.00,
      "lifetime_spent":      8260.00,
      "last_activity_at":  "2026-04-29T10:32:11Z"
    }
  ],
  "meta": { "current_page": 1, "last_page": 6, "per_page": 25, "total": 142 }
}
```

**Suggested table:**

```
Brand Wallets                                          [ Search 🔍 ] [ Export CSV ]

┌─────────────────────┬──────────┬──────────┬─────────────┬──────────────┬─────────────┐
│ Brand               │ Balance  │ Pending  │ Lifetime in │ Lifetime out │ Last action │
├─────────────────────┼──────────┼──────────┼─────────────┼──────────────┼─────────────┤
│ Acme Coffee         │ 1,240.00 │    50.00 │    9,500.00 │     8,260.00 │ 2 hours ago │ →
│ Bloom Studio        │ 3,800.00 │   600.00 │   12,000.00 │     8,200.00 │ yesterday   │ →
│ Cedar Mart          │     0.00 │     0.00 │      500.00 │       500.00 │ 3 weeks ago │ →
└─────────────────────┴──────────┴──────────┴─────────────┴──────────────┴─────────────┘
```

Click on a row → drill into Section 3.

---

## 3. Single-brand wallet detail

### `GET /api/admin/marketplace/brand-wallets/{brandId}` ⚠️ BACKEND TODO

Same payload shape as a list row, plus full transaction history embedded (or a separate paginated endpoint, see below).

### `GET /api/admin/marketplace/brand-wallets/{brandId}/transactions` ⚠️ BACKEND TODO

Paginated `mp_wallet_transactions` for one brand.

**Same payload shape as the brand-side `/api/brand/wallet/transactions`** (see brand wallet doc, section 4) — admin just gets it for any brand instead of self-only.

**Suggested layout:**

```
← Back to Brand Wallets

Acme Coffee  (brand #42, ops@acme.sa)

┌──────────────────────────┬────────────────────────────────────────┐
│ Balance                  │ Pending in active CRs                  │
│ 1,240.00 SAR             │ 50.00 SAR                              │
└──────────────────────────┴────────────────────────────────────────┘

Filters: [ Type ▾ ] [ Source ▾ ] [ From ▢ ] [ To ▢ ]   [ Adjust balance ⓘ ]

(transactions table — same shape as brand-side wallet history doc)
```

**Bonus button — `Adjust balance`:**

### `POST /api/admin/marketplace/brand-wallets/{brandId}/adjust` ⚠️ BACKEND TODO

For manual corrections (chargebacks, goodwill credits, refunds outside Moyasar).

**Body:**
```json
{
  "type":   "credit",
  "amount": 100.00,
  "note":   "Goodwill credit for failed CR #98"
}
```

Should call `WalletService::credit()` / `debit()` with `source = admin_adjustment` and `actor_id = current admin user id`. Existing service handles the lock + balance_after snapshot.

**FE behavior:** confirm modal with a summary of what's about to happen ("Add 100 SAR to Acme Coffee's wallet — irreversible"). Require a `note` (free text) so the audit trail is meaningful.

---

## 4. Collaboration ledger explorer

This is the central page for "follow the money" investigations.

### `GET /api/admin/marketplace/transfers` ⚠️ BACKEND TODO

Paginated read of `mp_collaboration_transfers` with rich filters.

**Proposed query params:**
| Param | Type | Default | Notes |
|---|---|---|---|
| `account_type` | enum | — | `creator` \| `platform` \| `vat` |
| `creator_id`   | int  | — | Filter to one creator |
| `brand_id`     | int  | — | Filter to one brand (joins via CR) |
| `cr_id`        | int  | — | Filter to one CR |
| `from` / `to`  | date | — | `posted_at` window |
| `per_page` | int | 25 | Max 100 |

**Proposed response (200):**
```json
{
  "success": true,
  "data": [
    {
      "id":           901,
      "account_type": "creator",
      "account_id":   21,
      "account_label": "Sara A.",
      "type":         "credit",
      "amount":       600.00,
      "currency":     "SAR",
      "posted_at":    "2026-04-30T09:11:42Z",
      "note":         "CR completed: creator earnings",
      "collaboration_request": {
        "id":          123,
        "title":       "Spring campaign post",
        "brand_id":    42,
        "brand_name":  "Acme Coffee"
      }
    },
    {
      "id":           902,
      "account_type": "platform",
      "account_id":   0,
      "account_label": "Platform",
      "type":         "credit",
      "amount":        60.00,
      "currency":     "SAR",
      "posted_at":    "2026-04-30T09:11:42Z",
      "note":         "CR completed: platform commission",
      "collaboration_request": { "id": 123, "title": "Spring campaign post", "brand_id": 42, "brand_name": "Acme Coffee" }
    }
  ],
  "meta": { "current_page": 1, "last_page": 12, "per_page": 25, "total": 287 }
}
```

**Suggested layout:**

```
Collaboration Ledger                                   [ Export CSV ]

Filters:
[ Account: All ▾ ] [ Brand ▾ ] [ Creator ▾ ] [ CR id ▢ ] [ From ▢ ] [ To ▢ ] [ Apply ]

KPI strip (auto-recalculates on filter change):
  Total credits in view: 12,450 SAR  •  287 entries

┌────────────┬─────────┬──────────────┬─────────┬────────────────────────┬──────────┐
│ Posted     │ Account │ Account name │ Type    │ For CR                 │ Amount   │
├────────────┼─────────┼──────────────┼─────────┼────────────────────────┼──────────┤
│ 30 Apr 09:11│ creator │ Sara A.      │ credit  │ #123 — Spring post     │ +600.00  │
│ 30 Apr 09:11│ platform│ Platform     │ credit  │ #123 — Spring post     │  +60.00  │
│ 30 Apr 09:11│ vat     │ VAT payable  │ credit  │ #123 — Spring post     │  +30.00  │
│ 29 Apr 14:00│ creator │ Omar K.      │ credit  │ #119 — Eid promo       │ +300.00  │
└────────────┴─────────┴──────────────┴─────────┴────────────────────────┴──────────┘
                                                           [ Page 1 of 12  ‹  › ]
```

**Color cues:**
- `account_type=creator` — blue badge
- `account_type=platform` — green badge
- `account_type=vat` — amber badge

**Click on the CR title** → existing admin CR detail page (out of scope here).

---

## 5. Per-CR money-flow drill-down

### `GET /api/admin/marketplace/collaboration-requests/{id}/transfers` ⚠️ BACKEND TODO

Same payload as the brand-side per-CR endpoint (see brand wallet doc, section 5), but no ownership check — admin can view any CR.

**FE: re-use the same "Money Flow" component** described in the brand doc; just plug in admin-scoped data. This guarantees brands and admins see the same numbers and there's only one component to maintain.

---

## 6. Reports

Two pre-canned reports finance asks for monthly:

### `GET /api/admin/marketplace/reports/platform-revenue` ⚠️ BACKEND TODO

**Query params:** `from`, `to` (required), optional `group_by=day|week|month` (default `day`).

**Proposed response (200):**
```json
{
  "success": true,
  "data": {
    "period":   { "from": "2026-04-01", "to": "2026-04-30" },
    "currency": "SAR",
    "total":     8420.00,
    "series":   [
      { "date": "2026-04-01", "amount":  120.00 },
      { "date": "2026-04-02", "amount":  340.00 },
      "…"
    ]
  }
}
```

### `GET /api/admin/marketplace/reports/vat-collected` ⚠️ BACKEND TODO

Same shape as platform-revenue, sourced from `account_type=vat` rows.

**Suggested page:**

```
Reports  ›  Platform Revenue

Period: [ This month ▾ ]   Granularity: [ Daily ▾ ]   [ Export CSV ]

Total: 8,420.00 SAR

(line chart of `series`)

(table mirroring the series, sortable)
```

---

## 7. Page map

| Route | Purpose | Endpoints (all TODO unless noted) |
|---|---|---|
| `/admin/marketplace/money` | KPI overview + top spenders/earners | `GET /api/admin/marketplace/money/overview` |
| `/admin/marketplace/brand-wallets` | List of all brand wallets, sortable/filterable | `GET /api/admin/marketplace/brand-wallets` |
| `/admin/marketplace/brand-wallets/:id` | Single brand wallet + tx history + adjust button | `GET /…/brand-wallets/{id}` + `…/transactions` + `POST …/adjust` |
| `/admin/marketplace/transfers` | Collaboration ledger explorer with filters | `GET /api/admin/marketplace/transfers` |
| `/admin/marketplace/collaboration-requests/:id` | Existing admin CR detail + Money Flow panel | existing CR endpoints + `GET /…/{id}/transfers` |
| `/admin/marketplace/reports/platform-revenue` | Charted commission report | `GET /…/reports/platform-revenue` |
| `/admin/marketplace/reports/vat-collected` | Charted VAT report | `GET /…/reports/vat-collected` |
| `/admin/marketplace/withdrawals` | **Already documented in `ADMIN_DASHBOARD_FRONTEND.md`** ✅ exists | `GET /api/admin/marketplace/withdrawals` etc. |

---

## 8. Backend follow-ups required

A single follow-up "Build admin money-monitoring API surface" should cover these endpoints, in priority order:

1. `GET /api/admin/marketplace/money/overview` — KPI page (high impact, blocks dashboard)
2. `GET /api/admin/marketplace/brand-wallets` (+ `/{id}`, + `/{id}/transactions`) — brand visibility
3. `GET /api/admin/marketplace/transfers` (+ `/collaboration-requests/{id}/transfers`) — ledger explorer
4. `POST /api/admin/marketplace/brand-wallets/{id}/adjust` — manual corrections (gated behind admin role)
5. `GET /api/admin/marketplace/reports/{platform-revenue,vat-collected}` — finance reports
6. **(Cross-cutting prereq)** add proper admin role gating before any of these go live — none of them should be reachable by a regular brand or creator token.

All endpoints are read-only except the adjust endpoint. The data exists today; this is purely a "expose what's already in the database" effort.

---

## 9. Cross-checks the FE should run when QAing

These are the integrity invariants the new dashboard surfaces. They're worth building into a QA checklist:

| Check | How |
|---|---|
| Brand wallet balance = SUM(credits) − SUM(debits) | Open a brand wallet, sum the transaction history, compare to displayed balance. |
| Platform-revenue period total = SUM(account_type=platform credits) | Open ledger explorer, filter by period+platform, compare to report KPI. |
| Creator earnings period = SUM(account_type=creator credits) | Same idea. |
| Per-CR brand-paid amount ≈ creator + platform + vat | Open any completed CR money-flow panel; the three legs should sum to (or be very close to) the brand wallet debit for that CR. |
| Withdrawals paid + locked + available = total earnings | Open a creator's earnings page (creator dashboard), the four numbers should reconcile. |

If any of these fail in production, that's a real data bug — not a UI bug.

---

## 10. Error handling

| HTTP | When | FE behavior |
|---|---|---|
| 401 | Token missing/expired | Redirect to login |
| 403 | Token isn't an admin | Hide admin nav entirely; show "Access denied" if URL is hit directly |
| 404 | Brand/CR not found | "Not found" page with back link |
| 422 | Adjust-balance validation failed | Inline error in the modal |
| 500 | Server error | Toast + retry; log to Sentry |
