# Validation Report: URS-036

**Title:** Mobile device support
**Date:** 2026-08-03T23:03:36.090Z
**Duration:** 156.2s
**Overall Status:** ✅ PASS

## User Requirement

> The system shall support access and use on mobile devices.

*Source: `User_Requirement_Specifications_Vantis_DeviceFlow.xlsx` — the run below proves the system meets this requirement.*

## Environment

- **Inbox URL:** http://localhost:38375
- **Database:** localhost:43939/cc_repinbox_dev

## Setup

Status: ✅ PASS

## Test Steps

Each step below corresponds to one Playwright test that ran sequentially. Screenshots and video recordings provide visual evidence of the UI behaviour.

### 1. Step 1a: Login form on iPhone 12 (empty) — ✅ PASS

**What this step proves:**

Confirms the login form is rendered and accessible on an iPhone 12 viewport (390×844 px) before credentials are entered. Verifies the form fields are visible and that no horizontal scroll occurs at the phone viewport width.

**Screenshots:**

![step 01 iphone 12 login empty](screenshots/step-01-iphone-12-login-empty.png)

**Video recording:**

[▶ Watch step recording](videos/step-01-iphone-12-login.webm)

---

### 2. Step 1b: Login form on iPhone 12 (filled) — ✅ PASS

**What this step proves:**

Shows the login form with credentials filled in, immediately before the submit button is tapped. Confirms all fields are reachable and readable on a mobile viewport.

**Screenshots:**

![step 01 iphone 12 login filled](screenshots/step-01-iphone-12-login-filled.png)

---

### 3. Step 1c: Home page on iPhone 12 after login — ✅ PASS

**What this step proves:**

Confirms a successful login redirected the browser away from /login and that the post-login home page renders without horizontal overflow on the iPhone 12 viewport.

**Screenshots:**

![step 01 iphone 12 home](screenshots/step-01-iphone-12-home.png)

---

### 4. Step 2a: Navigation drawer open on iPhone 12 — ✅ PASS

**What this step proves:**

Taps the hamburger menu button to open the mobile navigation drawer, then captures the full open state. Proves the primary navigation destinations are reachable by touch on a 390×844 px phone viewport.

**Screenshots:**

![step 02 drawer open iphone 12](screenshots/step-02-drawer-open-iphone-12.png)

**Video recording:**

[▶ Watch step recording](videos/step-02-nav-drawer.webm)

---

### 5. Step 2b: Billing page after drawer navigation — ✅ PASS

**What this step proves:**

Taps the Bill-Only nav link inside the open drawer and confirms the app routes to /billing. Also confirms the drawer auto-closes after navigation, matching the NavigationSidebar setOpenMobile(false) behavior.

**Screenshots:**

![step 02 billing after nav](screenshots/step-02-billing-after-nav.png)

---

### 6. Step 3a: Accounts cards on iPhone 12 — ✅ PASS

**What this step proves:**

Confirms the /sales-accounts page renders a vertical card list (not a table) on the iPhone 12 viewport. Asserts the three seeded Vantis account names appear inside the mobile-only card container and that no horizontal scroll is present.

**Screenshots:**

![step 03 accounts cards iphone 12](screenshots/step-03-accounts-cards-iphone-12.png)

**Video recording:**

[▶ Watch step recording](videos/step-03-accounts-mobile-cards.webm)

---

### 7. Step 3b: Account detail on iPhone 12 — ✅ PASS

**What this step proves:**

Taps a mobile card link and confirms the app navigates to the account detail page. Verifies the detail page loads without horizontal overflow on a phone viewport.

**Screenshots:**

![step 03 account detail iphone 12](screenshots/step-03-account-detail-iphone-12.png)

---

### 8. Step 4a: Billing cards on iPhone 12 — ✅ PASS

**What this step proves:**

Confirms the /billing page renders all four seeded Bill-Only Orders as tappable links on the iPhone 12 viewport, and that no horizontal scroll is present.

**Screenshots:**

![step 04 billing cards iphone 12](screenshots/step-04-billing-cards-iphone-12.png)

**Video recording:**

[▶ Watch step recording](videos/step-04-billing-mobile-cards.webm)

---

### 9. Step 4b: Billing detail on iPhone 12 — ✅ PASS

**What this step proves:**

Taps a billing card link and confirms the app navigates to the order detail page. Verifies the detail page loads without horizontal overflow on a phone viewport.

**Screenshots:**

![step 04 billing detail iphone 12](screenshots/step-04-billing-detail-iphone-12.png)

---

### 10. Step 5a: iPhone 12 /billing?filter=all&dateFrom=2020-01-01&dateTo=2035-12-31 — ✅ PASS

**Screenshots:**

![step 05 iphone 12 billing filter all datefrom 2020 01 01 dateto 2035 12 31](screenshots/step-05-iphone-12-billing-filter-all-datefrom-2020-01-01-dateto-2035-12-31.png)

**Video recording:**

[▶ Watch step recording](videos/step-05-galaxy-s21-no-hscroll.webm)

---

### 11. Step 5b: iPhone 12 /sales-accounts — ✅ PASS

**What this step proves:**

Verifies the /sales-accounts page renders without horizontal overflow on iPhone 12 (390×844 px). Part of the full-matrix no-horizontal-scroll sweep.

**Screenshots:**

![step 05 iphone 12 sales accounts](screenshots/step-05-iphone-12-sales-accounts.png)

**Video recording:**

[▶ Watch step recording](videos/step-05-iphone-12-no-hscroll.webm)

---

### 12. Step 5c: iPhone 12 /orders/requests — ✅ PASS

**What this step proves:**

Verifies the /orders/requests page renders without horizontal overflow on iPhone 12 (390×844 px). Covers the order-request queue a field rep reaches from the mobile drawer.

**Screenshots:**

![step 05 iphone 12 orders requests](screenshots/step-05-iphone-12-orders-requests.png)

**Video recording:**

[▶ Watch step recording](videos/step-05-iphone-se-no-hscroll.webm)

---

### 13. Step 5d: iPhone 12 /packages — ✅ PASS

**What this step proves:**

Verifies the /packages page renders without horizontal overflow on iPhone 12 (390×844 px). Uses the DataTable mobile-row layout below the md breakpoint.

**Screenshots:**

![step 05 iphone 12 packages](screenshots/step-05-iphone-12-packages.png)

**Video recording:**

[▶ Watch step recording](videos/step-05-pixel-5-no-hscroll.webm)

---

### 14. Step 5e: iPhone 12 /inventory — ✅ PASS

**What this step proves:**

Verifies the /inventory page renders without horizontal overflow on iPhone 12 (390×844 px). Uses the DataTable mobile-row layout below the md breakpoint.

**Screenshots:**

![step 05 iphone 12 inventory](screenshots/step-05-iphone-12-inventory.png)

---

### 15. Step 5f: iPhone SE /billing?filter=all&dateFrom=2020-01-01&dateTo=2035-12-31 — ✅ PASS

**Screenshots:**

![step 05 iphone se billing filter all datefrom 2020 01 01 dateto 2035 12 31](screenshots/step-05-iphone-se-billing-filter-all-datefrom-2020-01-01-dateto-2035-12-31.png)

---

### 16. Step 5g: iPhone SE /sales-accounts — ✅ PASS

**What this step proves:**

Verifies the /sales-accounts page renders without horizontal overflow on iPhone SE (375×667 px), the narrowest iOS viewport in the test matrix.

**Screenshots:**

![step 05 iphone se sales accounts](screenshots/step-05-iphone-se-sales-accounts.png)

---

### 17. Step 5h: iPhone SE /orders/requests — ✅ PASS

**What this step proves:**

Verifies the /orders/requests page renders without horizontal overflow on iPhone SE (375×667 px), the narrowest iOS viewport in the test matrix.

**Screenshots:**

![step 05 iphone se orders requests](screenshots/step-05-iphone-se-orders-requests.png)

---

### 18. Step 5i: iPhone SE /packages — ✅ PASS

**What this step proves:**

Verifies the /packages page renders without horizontal overflow on iPhone SE (375×667 px), the narrowest iOS viewport in the test matrix.

**Screenshots:**

![step 05 iphone se packages](screenshots/step-05-iphone-se-packages.png)

---

### 19. Step 5j: iPhone SE /inventory — ✅ PASS

**What this step proves:**

Verifies the /inventory page renders without horizontal overflow on iPhone SE (375×667 px), the narrowest iOS viewport in the test matrix.

**Screenshots:**

![step 05 iphone se inventory](screenshots/step-05-iphone-se-inventory.png)

---

### 20. Step 5k: Android Pixel 5 /billing?filter=all&dateFrom=2020-01-01&dateTo=2035-12-31 — ✅ PASS

**Screenshots:**

![step 05 pixel 5 billing filter all datefrom 2020 01 01 dateto 2035 12 31](screenshots/step-05-pixel-5-billing-filter-all-datefrom-2020-01-01-dateto-2035-12-31.png)

---

### 21. Step 5l: Android Pixel 5 /sales-accounts — ✅ PASS

**What this step proves:**

Verifies the /sales-accounts page renders without horizontal overflow on Android Pixel 5 (432×864 px). Tests responsive layout on an Android Chrome user agent.

**Screenshots:**

![step 05 pixel 5 sales accounts](screenshots/step-05-pixel-5-sales-accounts.png)

---

### 22. Step 5m: Android Pixel 5 /orders/requests — ✅ PASS

**What this step proves:**

Verifies the /orders/requests page renders without horizontal overflow on Android Pixel 5 (432×864 px). Tests responsive layout on an Android Chrome user agent.

**Screenshots:**

![step 05 pixel 5 orders requests](screenshots/step-05-pixel-5-orders-requests.png)

---

### 23. Step 5n: Android Pixel 5 /packages — ✅ PASS

**What this step proves:**

Verifies the /packages page renders without horizontal overflow on Android Pixel 5 (432×864 px). Tests responsive layout on an Android Chrome user agent.

**Screenshots:**

![step 05 pixel 5 packages](screenshots/step-05-pixel-5-packages.png)

---

### 24. Step 5o: Android Pixel 5 /inventory — ✅ PASS

**What this step proves:**

Verifies the /inventory page renders without horizontal overflow on Android Pixel 5 (432×864 px). Tests responsive layout on an Android Chrome user agent.

**Screenshots:**

![step 05 pixel 5 inventory](screenshots/step-05-pixel-5-inventory.png)

---

### 25. Step 5p: Android Galaxy S21 /billing?filter=all&dateFrom=2020-01-01&dateTo=2035-12-31 — ✅ PASS

**Screenshots:**

![step 05 galaxy s21 billing filter all datefrom 2020 01 01 dateto 2035 12 31](screenshots/step-05-galaxy-s21-billing-filter-all-datefrom-2020-01-01-dateto-2035-12-31.png)

---

### 26. Step 5q: Android Galaxy S21 /sales-accounts — ✅ PASS

**What this step proves:**

Verifies the /sales-accounts page renders without horizontal overflow on Android Galaxy S21 (360×800 px), the narrowest viewport in the full mobile matrix.

**Screenshots:**

![step 05 galaxy s21 sales accounts](screenshots/step-05-galaxy-s21-sales-accounts.png)

---

### 27. Step 5r: Android Galaxy S21 /orders/requests — ✅ PASS

**What this step proves:**

Verifies the /orders/requests page renders without horizontal overflow on Android Galaxy S21 (360×800 px), the narrowest viewport in the full mobile matrix.

**Screenshots:**

![step 05 galaxy s21 orders requests](screenshots/step-05-galaxy-s21-orders-requests.png)

---

### 28. Step 5s: Android Galaxy S21 /packages — ✅ PASS

**What this step proves:**

Verifies the /packages page renders without horizontal overflow on Android Galaxy S21 (360×800 px), the narrowest viewport in the full mobile matrix.

**Screenshots:**

![step 05 galaxy s21 packages](screenshots/step-05-galaxy-s21-packages.png)

---

### 29. Step 5t: Android Galaxy S21 /inventory — ✅ PASS

**What this step proves:**

Verifies the /inventory page renders without horizontal overflow on Android Galaxy S21 (360×800 px), the narrowest viewport in the full mobile matrix.

**Screenshots:**

![step 05 galaxy s21 inventory](screenshots/step-05-galaxy-s21-inventory.png)

---

### 30. Step 6a: Touch targets on /billing?filter=all&dateFrom=2020-01-01&dateTo=2035-12-31 — ✅ PASS

**Screenshots:**

![step 06 touch targets billing filter all datefrom 2020 01 01 dateto 2035 12 31](screenshots/step-06-touch-targets-billing-filter-all-datefrom-2020-01-01-dateto-2035-12-31.png)

**Video recording:**

[▶ Watch step recording](videos/step-06-touch-targets.webm)

---

### 31. Step 6b: Touch targets on /sales-accounts — ✅ PASS

**What this step proves:**

Measures every visible interactive element inside <main> on /sales-accounts on the iPhone 12 viewport. Fails if any element falls below the 24 px WCAG 2.5.8 hard minimum on both axes.

**Screenshots:**

![step 06 touch targets sales accounts](screenshots/step-06-touch-targets-sales-accounts.png)

---

### 32. Step 6c: Touch targets on /orders/requests — ✅ PASS

**What this step proves:**

Measures every visible interactive element inside <main> on /orders/requests on the iPhone 12 viewport. Fails if any element falls below the 24 px WCAG 2.5.8 hard minimum on both axes.

**Screenshots:**

![step 06 touch targets orders requests](screenshots/step-06-touch-targets-orders-requests.png)

---

### 33. Step 6d: Touch targets on /packages — ✅ PASS

**What this step proves:**

Measures every visible interactive element inside <main> on /packages on the iPhone 12 viewport. Fails if any element falls below the 24 px WCAG 2.5.8 hard minimum on both axes.

**Screenshots:**

![step 06 touch targets packages](screenshots/step-06-touch-targets-packages.png)

---

### 34. Step 6e: Touch targets on /inventory — ✅ PASS

**What this step proves:**

Measures every visible interactive element inside <main> on /inventory on the iPhone 12 viewport. Fails if any element falls below the 24 px WCAG 2.5.8 hard minimum on both axes.

**Screenshots:**

![step 06 touch targets inventory](screenshots/step-06-touch-targets-inventory.png)

---

### 35. Step 7a: Pixel 5 billing — ✅ PASS

**What this step proves:**

Smoke test on Android Pixel 5: navigates to /billing after login and confirms the first seeded order is visible without horizontal overflow. Console errors are captured and fail the step if any are found.

**Screenshots:**

![step 07 pixel 5 billing](screenshots/step-07-pixel-5-billing.png)

**Video recording:**

[▶ Watch step recording](videos/step-07-pixel-5-smoke.webm)

---

### 36. Step 7b: Pixel 5 accounts — ✅ PASS

**What this step proves:**

Smoke test on Android Pixel 5: navigates to /sales-accounts and confirms the ROSS account card is visible inside the mobile-only wrapper without horizontal overflow.

**Screenshots:**

![step 07 pixel 5 accounts](screenshots/step-07-pixel-5-accounts.png)

---

## Database Validations

The following SQL queries ran against the application database after the Playwright scenarios completed. Each query asserts a specific condition that proves the feature under test persisted its data correctly.

### Login user alex.admin@vantismedical.com exists — ✅ PASS

**Assertion:** Exactly one user with id=alexAdmin should be seeded

```sql

    SELECT id, email, name
      FROM users
     WHERE id = $1
```

| id | email | name |
| --- | --- | --- |
| f6a7b8c9-d0e1-2345-f123-456789012345 | alex.admin@vantismedical.com | Alex Admin |

### At least 4 seeded billing orders exist — ✅ PASS

**Assertion:** >= 4 VBO-2025-* orders should be seeded by vantisDemo

```sql

    SELECT order_number, status
      FROM billing_orders
     WHERE order_number = ANY($1)
     ORDER BY order_number
```

| order_number | status |
| --- | --- |
| VBO-2025-001 | draft |
| VBO-2025-002 | submitted |
| VBO-2025-003 | invoice_sent |
| VBO-2025-004 | invoice_voided |

### At least 3 active Vantis sales accounts exist — ✅ PASS

**Assertion:** >= 3 active sales_accounts rows for Vantis

```sql

    SELECT id, name, status
      FROM sales_accounts
     WHERE manufacturer_organization_id = $1 AND status = 'active'
     ORDER BY name
```

| id | name | status |
| --- | --- | --- |
| 1fb8c9d0-e1f2-3456-1234-567890123456 | Cedar Crest Hospital Account Request | active |
| 2ac9d0e1-f2a3-4567-2345-678901234567 | Lakeside Ortho Account Request | active |
| fea7b8c9-d0e1-2345-0123-456789012345 | ROSS Surgical Account Request | active |

### At least 8 Vantis products exist — ✅ PASS

**Assertion:** >= 8 products rows for Vantis

```sql

    SELECT count(*)::int AS product_count
      FROM org_products
     WHERE organization_id = $1
```

| product_count |
| --- |
| 13 |

## Audit Log Events

Every row written to `audit_events` while this test was running (scoped to the demo organizations). Provides compliance evidence that user actions are traced end-to-end (URS-003).

**Capture window start:** 2026-08-03T23:03:34.202Z

<details><summary>Query used to capture events</summary>

```sql
SELECT
    ae.created_at,
    ae.event_type,
    ae.action,
    ae.user_id,
    u.email AS user_email,
    ae.organization_id,
    o.name AS organization_name,
    ae.object_id,
    ae.secondary_object_id,
    ae.payload,
    ae.route,
    ae.trace_id
  FROM audit_events ae
  LEFT JOIN users u ON u.id = ae.user_id
  LEFT JOIN organizations o ON o.id = ae.organization_id
  WHERE ae.created_at >= $1
    AND ae.organization_id = ANY($2::uuid[])
  ORDER BY ae.created_at ASC
```
</details>

12 event(s) captured:

| Time | Type | Action | User | Org | Object ID | Performed | Reason |
|------|------|--------|------|-----|-----------|-----------|--------|
| 2026-08-03 23:03:35Z | checklist | checklists.create | — | Corveta Surgical Group | 019fc9de-45ac-7ec4-8146-bc79e0adb446 | — |  |
| 2026-08-03 23:03:35Z | decision | ensure_lot_expiration_checklist | — | Corveta Surgical Group | b2c3d4e5-f6a7-8901-bcde-f12345678901 | yes | created_1_reopened_0_already_open_1 |
| 2026-08-03 23:03:44Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:03:46Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:03:56Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:04:03Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:04:43Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:05:00Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:05:16Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:05:33Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:05:49Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
| 2026-08-03 23:06:08Z | user_log | user:login | alex.admin@vantismedical.com | Vantis | — | — |  |
