Skip to content

Commit df5a25d

Browse files
authored
feat: improve mobile dashboard responsiveness (#31)
* Improve mobile dashboard responsiveness * Address mobile dashboard review feedback * Add accessible mobile table labels
1 parent cbe6675 commit df5a25d

24 files changed

Lines changed: 1282 additions & 257 deletions

File tree

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
NEXT_PUBLIC_APP_URL=http://localhost:3000
33
# Optional comma-separated hostnames for proxied Next.js dev server origins.
44
NEXT_ALLOWED_DEV_ORIGINS=
5+
# Enables local Playwright-only mock sessions. Never set in production.
6+
E2E_AUTH_ENABLED=false
57

68
# Server-only configuration
79
DATABASE_URL=

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
# testing
1414
/coverage
15+
/playwright-report
16+
/test-results
1517

1618
# next.js
1719
/.next/

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,43 @@ Keep machine-specific proxy hostnames and URLs out of committed source.
5050
- `pnpm build`: create a production build.
5151
- `pnpm start`: run the production build.
5252
- `pnpm lint`: run ESLint.
53+
- `pnpm test:e2e`: run Playwright responsive smoke tests.
54+
- `pnpm test:e2e:install`: install the Playwright Chromium browser.
5355
- `pnpm db:generate`: generate Drizzle SQL migrations from the schema.
5456
- `pnpm db:migrate`: apply Drizzle migrations to `DATABASE_URL`.
5557
- `pnpm db:reset:local`: drop and recreate the local `public` schema, then run migrations.
5658
- `pnpm db:studio`: open Drizzle Studio for local database inspection.
5759

60+
## End-to-End QA
61+
62+
The Playwright suite checks public and gated dashboard routes across mobile,
63+
tablet, and desktop viewports. It uses the local database state that is already
64+
present. Detail-page checks skip with a clear message when the local database
65+
does not contain a matching quarter or report.
66+
67+
On a fresh Ubuntu server, install browser system dependencies once:
68+
69+
```bash
70+
pnpm exec playwright install-deps chromium
71+
```
72+
73+
Then install the Chromium browser bundle:
74+
75+
```bash
76+
pnpm test:e2e:install
77+
```
78+
79+
Run the suite:
80+
81+
```bash
82+
pnpm test:e2e
83+
```
84+
85+
The tests start the Next.js dev server with `E2E_AUTH_ENABLED=true` and use a
86+
local-only mock session endpoint for member, cleric, and admin route coverage.
87+
That endpoint returns 404 unless `E2E_AUTH_ENABLED=true` and the app is not
88+
running in production.
89+
5890
## Stack
5991

6092
- Next.js App Router.

eslint.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const eslintConfig = defineConfig([
1111
".next/**",
1212
"out/**",
1313
"build/**",
14+
"playwright-report/**",
15+
"test-results/**",
1416
"next-env.d.ts",
1517
]),
1618
]);

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"build": "next build",
1313
"start": "next start",
1414
"lint": "eslint .",
15+
"test:e2e": "playwright test",
16+
"test:e2e:install": "playwright install chromium",
1517
"db:generate": "drizzle-kit generate",
1618
"db:migrate": "drizzle-kit migrate",
1719
"db:reset:local": "node scripts/reset-local-db.mjs",
@@ -43,6 +45,7 @@
4345
"wagmi": "^3.6.16"
4446
},
4547
"devDependencies": {
48+
"@playwright/test": "^1.61.1",
4649
"@tailwindcss/postcss": "^4",
4750
"@types/node": "^20",
4851
"@types/pg": "^8.18.0",

playwright.config.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
3+
const port = Number(process.env.PLAYWRIGHT_PORT ?? 3131);
4+
const host = "127.0.0.1";
5+
const baseURL = `http://${host}:${port}`;
6+
7+
export default defineConfig({
8+
expect: {
9+
timeout: 10_000,
10+
},
11+
fullyParallel: false,
12+
outputDir: "test-results/e2e",
13+
reporter: [["list"], ["html", { open: "never" }]],
14+
testDir: "./tests/e2e",
15+
timeout: 45_000,
16+
use: {
17+
baseURL,
18+
trace: "retain-on-failure",
19+
},
20+
webServer: {
21+
command: `E2E_AUTH_ENABLED=true COREPACK_HOME=/tmp/corepack corepack pnpm exec next dev -H ${host} -p ${port}`,
22+
reuseExistingServer: false,
23+
timeout: 120_000,
24+
url: baseURL,
25+
},
26+
projects: [
27+
{
28+
name: "mobile",
29+
use: {
30+
...devices["Pixel 5"],
31+
viewport: { height: 844, width: 390 },
32+
},
33+
},
34+
{
35+
name: "small-mobile",
36+
use: {
37+
...devices["Pixel 5"],
38+
viewport: { height: 740, width: 360 },
39+
},
40+
},
41+
{
42+
name: "tablet",
43+
use: {
44+
...devices["iPad (gen 7)"],
45+
browserName: "chromium",
46+
viewport: { height: 1024, width: 768 },
47+
},
48+
},
49+
{
50+
name: "desktop",
51+
use: {
52+
viewport: { height: 900, width: 1440 },
53+
},
54+
},
55+
],
56+
});

pnpm-lock.yaml

Lines changed: 47 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/admin/providers/page.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,8 @@ function ProviderRankingTable({
479479
</span>
480480
</div>
481481
{providers.length > 0 ? (
482-
<div className="overflow-x-auto">
483-
<table className="w-full min-w-[680px] text-left text-sm">
482+
<div className="rounded-lg border border-border bg-card p-3 md:p-0">
483+
<table className="mobile-card-table">
484484
<thead className="border-b border-border text-xs uppercase text-muted-foreground">
485485
<tr>
486486
<th className="px-3 py-3 font-medium">Provider</th>
@@ -501,7 +501,8 @@ function ProviderRankingTable({
501501
key={provider.id}
502502
className="transition-colors hover:bg-muted/50"
503503
>
504-
<td className="p-0">
504+
<td data-label="Provider" data-full="true" className="p-0">
505+
<span className="sr-only">Provider: </span>
505506
<Link
506507
href={href}
507508
scroll={false}
@@ -510,7 +511,8 @@ function ProviderRankingTable({
510511
{provider.name}
511512
</Link>
512513
</td>
513-
<td className="p-0">
514+
<td data-align="right" data-label="Spend" className="p-0">
515+
<span className="sr-only">Spend: </span>
514516
<Link
515517
href={href}
516518
scroll={false}
@@ -519,7 +521,8 @@ function ProviderRankingTable({
519521
{formatCurrency(spend?.totalUsd ?? "0")}
520522
</Link>
521523
</td>
522-
<td className="p-0">
524+
<td data-align="right" data-label="Entries" className="p-0">
525+
<span className="sr-only">Entries: </span>
523526
<Link
524527
href={href}
525528
scroll={false}
@@ -528,7 +531,8 @@ function ProviderRankingTable({
528531
{spend?.entryCount ?? 0}
529532
</Link>
530533
</td>
531-
<td className="p-0">
534+
<td data-label="Website" className="p-0">
535+
<span className="sr-only">Website: </span>
532536
<Link
533537
href={href}
534538
scroll={false}
@@ -537,7 +541,8 @@ function ProviderRankingTable({
537541
{provider.website || "Not recorded"}
538542
</Link>
539543
</td>
540-
<td className="p-0">
544+
<td data-align="right" data-label="Addresses" className="p-0">
545+
<span className="sr-only">Addresses: </span>
541546
<Link
542547
href={href}
543548
scroll={false}
@@ -546,7 +551,8 @@ function ProviderRankingTable({
546551
{provider.addresses.length}
547552
</Link>
548553
</td>
549-
<td className="p-0">
554+
<td data-label="Status" className="p-0">
555+
<span className="sr-only">Status: </span>
550556
<Link
551557
href={href}
552558
scroll={false}

0 commit comments

Comments
 (0)