# Page Review — Dashboard (P15) + Branches (P16)

- **Routes:** `/` (SiteController dashboard) · `/branch` (index, view, create, update, delete, addShopGallery, status, toggleApprovalStatus, districts)
- **Reviewed:** 2026-07-01 · **Status:** 🔍 (branch code agent running)

## 2. Design findings
| # | What | Actual | Sev | Evidence |
|---|------|--------|-----|----------|
| D1 | Dashboard | ✅ rich RTL: greeting, contract-activation banner (إجراء مطلوب → مراجعة العقد), 4 status KPI cards, costs-to-date + available-balance(+withdraw), costs donut, upcoming-bookings table | — | ss_1648lhv9x |
| D2 | Dashboard greeting date | "30 يونيو 2026" vs top-bar "الأربعاء 1 يوليو" — 1-day mismatch (DemoClock/timezone?) | LOW | ss_1648lhv9x |
| D3 | Upcoming-bookings customer col | walk-in row shows raw synthetic `walkin.500000099.…@walkin.navagoo.local` instead of a friendly "Walk-in" label/name | LOW-MED | ss_1648lhv9x |
| D4 | Branches | ✅ clean RTL empty state (no branches; building icon; create CTA) | — | ss_2780q30u9 |

## 3. Logic findings
| Action | Result |
|---|---|
| 🟢 dashboard widgets / KPIs / upcoming | render verified |
| 🟡 branch create/update/status/gallery · 🔴 delete · ⚠️ toggleApprovalStatus | NOT live-triggered (no branches; mutation); code-review covers (esp. self-approval) |

## 4. Code trace
- Dashboard = SiteController, read-only widgets (low risk; not separately agent-reviewed).
- **Branch code agent — 2 CRITICAL (self-approval), 2 HIGH, 2 MED:**

| Sev | File:line | Issue | Status |
|---|---|---|---|
| **CRIT** | BranchController.php:557 actionStatus | owner POSTs `Shop[status]=1` → `load()` (default scenario) + `save(false)` → **self-activates branch, bypassing admin moderation** (+ smuggles top_shop/commission/etc.) | ✅ FIXED (R-C002) |
| **CRIT** | BranchController.php:600 actionToggleApprovalStatus | owner POSTs `action=1` → `status=ACTIVE` + `save(false)` → **self-approves branch** | ✅ FIXED (R-C003) |
| HIGH | BranchController.php:557,570 | default-scenario `load()` + `save(false)` exposes all PRIVILEGED_FIELDS (subsumed by R-C002 role guard) | ✅ covered by R-C002 |
| MED | frontend/views/branch/ (no status.php) | `actionStatus` renders missing `status` view → ViewNotFoundException (action is now admin-guarded anyway) | ⚠️ LOGGED |
| MED | BranchController.php:638 actionDistricts | reads raw `$_POST` instead of `request->post()` (no SQLi — int-bound query — but code-quality) | ⚠️ LOGGED R-L21 |

**Confirmed clean:** `findModel` scoped to `['id'=>$id,'parent_shop_id'=>$parentShopId]` (IDOR blocked, null-shop guarded); create `applySystemFields()` overwrites status/parent/is_demo/commission after load; update uses `SCENARIO_SHOP_OWNER` + restores `parent_shop_id`; gallery upload validated (ext/mime/size + finfo + exec-sig scan); CSRF (VerbFilter POST); signup wrapped in a transaction.

## 5. Fixes applied
| Issue | File:line | Change | Re-verified |
|-------|-----------|--------|-------------|
| R-C002 branch self-activate (actionStatus) | BranchController.php:~552 | hard role guard: `ForbiddenHttpException` unless admin/manager | ✅ php -l clean |
| R-C003 branch self-approve (toggleApprovalStatus) | BranchController.php:~602 | same role guard (`Yii::t('yii', …)` bilingual message) | ✅ php -l clean |

## 6. Sign-off
- [x] Dashboard + Branches design verified (RTL)
- [x] branch code-review folded; **2 CRITICAL self-approval bypasses fixed** + lint clean
- [ ] D2/D3 dashboard copy (date, walk-in label) + MED logged
