# Regression Matrix — VisoAdmin

This matrix tells you what to re-test when you touch a given feature. **Read this before merging.**

Severity legend: **Critical** = data corruption / silent prod break. **High** = workflow broken. **Medium** = degraded UX. **Low** = cosmetic.

---

## Cross-Feature Coupling

|  | Auth | RBAC | Admin Users | Vendor Mgmt | Event Oversight | RM Requests | RM Create Event | Coordinator | Master Data — Services | Master Data — Locations | Analytics | Critical Events | Notifications | Reports | Activity Log |
|--|------|------|-------------|-------------|-----------------|-------------|------------------|-------------|----|----|----|----|----|----|----|
| **Auth** (sessions, login) | — | **Critical** | High | High | High | High | High | High | High | High | High | High | High | High | High |
| **RBAC** (permissions, roles) | Low | — | **Critical** | High | High | High | High | High | Medium | Medium | Medium | Medium | Medium | Medium | Low |
| **Admin Users** | Low | High | — | Low | Low | High | High | High | Low | Low | Low | Low | Low | Low | Medium |
| **Vendor Mgmt** (esp. delete cascade) | — | — | — | — | High | Medium | Medium | High | Medium | Medium | Medium | Medium | Medium | High | — |
| **Event Oversight** | — | — | — | Medium | — | **Critical** | **Critical** | High | — | — | High | High | Medium | Medium | — |
| **RM Requests** | — | — | Medium | — | High | — | **Critical** | — | — | — | Low | Low | High | — | — |
| **RM Create Event** | — | — | Medium | Medium | High | High | — | High | High | High | Medium | High | High | — | — |
| **Master Data — Services** | — | — | — | High | High | — | High | High | — | — | High | — | — | — | — |
| **Master Data — Locations** | — | — | — | High | High | High | High | High | — | — | Medium | Medium | — | — | — |
| **App Settings** | — | — | — | Medium | — | — | — | — | — | — | — | — | Medium | — | — |
| **Notifications** | — | — | — | Medium | Medium | Medium | High | — | — | — | — | — | — | — | — |

---

## Feature-Level Risk Notes

### 01 Admin Authentication
- Touches the `admins` table and Laravel's `web` middleware group. Breaking login = full ops outage.
- The `UserPermission` middleware does a `redirect()->guest('/login')` on empty `auth()->user()` — any session driver change (file → redis) will silently lose admins.
- **Re-test**: login + remember-me + 8hr session-lifetime expiry, password reset email send.

### 02 Admin RBAC
- `permissionGroup()` is a giant hard-coded map. Adding new routes requires adding them here, otherwise the menu hides but the route is reachable directly.
- `auth()->user()->superadmin` has THREE values (`0`, `1`, `2`). Many controllers only check `== 1` — coordinator (`2`) silently bypasses checks elsewhere.
- **Re-test**: log in as each of 8 roles; verify menu render; verify 403 on disallowed routes.

### 03 Admin User Management
- `addAdmin` & `editAdmin` both share the `users.addAdmin` view. Renaming that blade affects both.
- `deleteAdmin` uses raw `DB::delete("... WHERE id=".$id)` — **SQL injection risk** if `$id` ever becomes user-supplied via something other than route param.
- **Re-test**: create / edit / delete superadmin and non-superadmin; permission gating; phone optional.

### 04 Client Management
- Read-only oversight. List + detail. No mutation surface — low blast radius.
- **Re-test**: client search by phone; client detail page (event list, status badges).

### 05 Vendor Management
- **Delete cascade is dangerous.** `UsersController::deleteVendor()` issues 14 raw `DB::delete(...)` statements in one transaction. If any FK or table schema changes upstream (OotboAPI), the cascade silently fails or leaves orphans.
- The cascade deletes `chat`, `chat_service`, `conversation`, `event_services_vendor_quote`, `event_vendor_discount`, `favorite_vendors`, `vendor_feedbacks`, `fcm_tokens`, `user_reports`, `notification_setting`, `call_verification`, `vendor_services`, `vendor_services_locations`, and `users`.
- **Re-test on any schema change to these tables**: delete a vendor with active chats, active quotes, active discounts, S3 images, and confirm no orphan rows.

### 06 Event Oversight
- `EventModel::getEventList()` uses raw `whereBetween('events.created_at', [start, end])` parsed from a date-range string. Malformed input could break the query.
- `updateEvent` is a validated update of 6 columns. Anything else is read-only.
- **Re-test**: each filter (status, creation range, event date range, mobile, event id), pagination, detail view.

### 07 Coordinator Console
- Only accessible when `superadmin == 2` OR permission 7. Coordinator-only users land here from `/welcome` (see `WelcomeController`).
- Looks up events by **`human_usable_id`** (not numeric id). Renaming ID schemes breaks this.
- **Re-test**: bad event ID handling (redirect with flash), vendor list per service/city, conversation lookup.

### 08 RM Requests
- `RmController::getRmData()` scopes by `admins.country_code`. **An RM without `country_code` set sees an empty list silently** — there is no warning.
- `record_type == 'event'` → join uses `record_id`. Any drift between `record_id` and `event_id` causes orphan rows.
- **Re-test**: pagination, status badges (8 statuses), payment status badges (3 statuses), "Event Created" rows show event link.

### 09 RM Create Event
- The most cross-feature-coupled write surface. Touches `events`, `event_services`, `client_rm_requests`, `notifications`, AND POSTs to Viso-Chat socket.
- **Hard-pinned to UAE** (`Countries::where('name','United Arab Emirates')`). Removing UAE from the catalog hard-fails the form.
- Generates `human_usable_id` with `mt_rand(1, 999_999_999)` — birthday-collision risk grows with volume but `do-while-exists` guards it.
- Wrapped in `DB::beginTransaction()` — but the notification + socket calls are *outside* the transaction. Event commits even if notify fails (intentional; logged).
- **Re-test**: full happy path, validation, UAE-missing edge case, socket down (event still creates), `human_usable_id` collision retry.

### 10 Dashboard — Critical Events
- Pure read: events with **zero quotes** AND `event_date >= today`. Heavy SQL with nested `IN (SELECT GROUP_CONCAT(...))`.
- **Re-test**: shows only future, only-no-quote events; pagination; cancelled events excluded? — actually code does NOT filter `is_cancelled` here (verify with ops).

### 11 Analytics
- `AnalyticsController::ajaxAnalyticsData()` runs a giant single-SQL with 12 subqueries. Performance degrades with `users` / `events` size — currently fine.
- `barChartData()` uses raw string interpolation of `$startDate` and `$endDate` into SQL. **SQL injection risk** if the request bypasses the AJAX UI.
- **Re-test**: each KPI card, date range filter, pie chart per service.

### 12 Master Data — Locations
- Three independent CRUDs (countries / states / cities). Disabling a country does NOT cascade to its states/cities — they still show as active but with an inactive parent.
- **Re-test**: add / edit / cascade dropdown; uniqueness validation for new entries.

### 13 Master Data — Services
- Status toggle controls discoverability across both apps. Renaming a service does not migrate vendor service bindings — they still reference the same id.
- **Re-test**: add / edit; status toggle reflected in OotboAPI catalogue endpoints.

### 14 FAQ Management
- **Read-only.** No add/edit UI in VisoAdmin — FAQs are manually seeded into the `faqs` table. Image URLs are loaded from `config/viso.php`.
- **Risk**: if you point `viso.clientfaq` to the wrong host, all FAQ images 404.

### 15 App Settings
- Updates `app_settings` via raw `DB::update(DB::raw("UPDATE ... WHERE app_key='$key'"))`. Keys are server-controlled, so injection is bounded — but any new key added must use exact spelling.
- **Re-test**: each settings group (contact, app-store links).

### 16 Notifications (Admin Inbox)
- `getNotificationCount()` polls on every page load (via `comman.js`). Wrapped in try/catch — silently logs and returns 0 on schema mismatch.
- **Re-test**: badge shows unread count; "see all" link; degraded behaviour with malformed `data_props` JSON.

### 17 Notification Settings
- `notification_setting` is a routing table: which email/phone gets notified for which vendor. Used by external email/SMS senders (NOT by VisoAdmin code, which only adds/deletes rows).
- **Re-test**: add a row + delete; validates email.

### 18 User Reports
- Read-only display of `user_reports`. No actions taken from VisoAdmin — vendor isn't auto-suspended.
- **Re-test**: list pagination, detail view shows full message.

### 19 Suggestions
- Read-only display of `app_suggestions`. The "Take Action" button is present in the template but has no backend.
- **Risk**: button looks functional but doesn't work.

### 20 User Activity Log
- Written by `UserPermission` middleware on every non-AJAX, non-default-URL request. `config('app.activity_log')` toggles it off.
- The list view doesn't filter by user — every admin sees everyone's activity.

---

## High-Risk Cross-Feature Pairs

| Pair | Why High Risk |
|------|---------------|
| **Vendor Mgmt × Event Oversight** | Delete vendor cascades through `event_services_vendor_quote` and `event_vendor_discount`. Past event records lose their quote history. |
| **Vendor Mgmt × Coordinator** | Coordinator dropdown of "vendors for service X in city Y" filters out vendors who already quoted. Deleting a vendor un-files their `quote` row, so they re-appear in the dropdown. |
| **RM Create Event × Event Oversight** | Event created by RM has `saved_vendor_id = null` initially. Make sure `getEventList` and `getEventDetails` handle nulls. |
| **RBAC × every menu** | Adding a route without updating `permissionGroup()` makes it accessible only to superadmin. Adding it with wrong group hides it from the team it belongs to. |
| **Master Data — Locations × RM Create Event** | RM form is UAE-only. If UAE state/city is disabled, RM form silently shows empty dropdowns. |
| **Notifications × RM Create Event** | If notification insert fails inside the controller's try/catch, the event still commits but the client never sees the in-app message. Logged only. |
| **Socket URL × RM Create Event** | If `SOCKET_URL` is wrong / unreachable, the realtime push is silently lost. The DB notification row still exists; client only sees it on next list-refresh. |

---

## Schema Migration Safety (Tables Owned Elsewhere)

These tables live in OotboAPI's migration tree but are read/written by VisoAdmin. Any change there is a regression risk here:

| Table | Drift Risk |
|-------|-----------|
| `users` | `display_name`, `phone`, `email_id`, `company_name`, `alternet_number`, `last_login_from`, `status`, `country_code` columns — all read by `UsersController`. |
| `events` | `client_id`, `cities_id`, `state_id`, `country_id`, `category`, `area`, `description`, `is_cancelled`, `human_usable_id`, `saved_vendor_id` — all read/written by `EventModel`. |
| `event_services` | `event_id`, `service_id`, `amount`, `expected_event_date`, `expected_start_time` — RM Create Event writes these as UUIDs not IDs. |
| `client_rm_requests` | `record_id`, `record_type`, `status`, `category`, `country_id`, `state_id`, `city_id`, `budget`, `payment_status`, `client_id`, `uuid`, `request_human_id` — RM lifecycle reads/writes. |
| `notifications` | `module`, `message`, `created_by`, `created_type`, `created_for`, `created_for_type`, `is_broadcast`, `is_seen`, `notification_type`, `payload`, `data_props` — both notify-create (RM Create Event) and notify-list (admin inbox) use this. Note: `data_props` vs `payload` — both are referenced in code paths. **Verify column name with ops.** |
| `conversation` / `chat_service` / `chat` | Deleted on vendor cascade. Read by coordinator console. |

---

## Cross-Project Coordination Checklist

Before merging changes in **OotboAPI** that touch any shared table, run through:

- [ ] Does `App\Models\EventModel` in VisoAdmin still match the shape?
- [ ] Does `UsersController::deleteVendor()` still cascade through all FKs?
- [ ] Does `AnalyticsController::ajaxAnalyticsData()` still find the same column names?
- [ ] Does `permissionGroup()` know about any new admin route?
- [ ] Does the RM form still produce a valid event row that OotboAPI's lead-matching can consume?
- [ ] Did you update `00-overview/cross-app-interactions.md` if shared-table ownership changed?
