From 399a5ae0c6dc12cbe3e9c59d13fe59798e30e250 Mon Sep 17 00:00:00 2001 From: Duchan Date: Sun, 18 Oct 2026 16:13:42 +0900 Subject: [PATCH 1/2] fix(dashboard): platform badges say iOS, not Ios The badge printed the API's lowercase platform and let CSS capitalize it, which cannot produce iOS. A PLATFORM_LABEL map names each platform the way AddAppDialog's select already does. Co-Authored-By: Claude Opus 5.5 --- .changeset/dashboard-platform-label.md | 5 +++++ CHANGELOG.md | 2 ++ packages/dashboard/components/app-center/BuildRow.tsx | 6 +++--- packages/dashboard/lib/build-format.ts | 9 ++++++++- packages/dashboard/src/__tests__/BuildRow.test.tsx | 9 +++++++++ .../src/__tests__/DefaultSettings.load.test.tsx | 7 +++++++ packages/dashboard/src/pages/settings/Default.tsx | 4 ++-- 7 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 .changeset/dashboard-platform-label.md diff --git a/.changeset/dashboard-platform-label.md b/.changeset/dashboard-platform-label.md new file mode 100644 index 000000000..68e828f21 --- /dev/null +++ b/.changeset/dashboard-platform-label.md @@ -0,0 +1,5 @@ +--- +"@tapflowio/relay": patch +--- + +Dashboard: platform badges in App Center's build rows and in Settings → Apps read `iOS`, `Android` and `Both`. They read `Ios`, because the lowercase API value was capitalized by CSS. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf03aa01..b50f7e865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Platform badges say iOS, not Ios.** App Center's build rows and Settings → Apps now name the platform `iOS`, `Android` or `Both`. + - **Long dashboard pages no longer scroll the whole window.** On Settings → Default and other long forms the window grew a second scrollbar and the header could scroll out of view, because a screen-reader-only status line near the bottom of a form was stretching the page. Only the page area scrolls now. - **The dashboard no longer flashes light before switching to dark mode.** With dark mode set, or the system theme dark, each page load first painted light while the app loaded. The theme is now applied before the first paint. diff --git a/packages/dashboard/components/app-center/BuildRow.tsx b/packages/dashboard/components/app-center/BuildRow.tsx index 6c66d7133..552cebb7f 100644 --- a/packages/dashboard/components/app-center/BuildRow.tsx +++ b/packages/dashboard/components/app-center/BuildRow.tsx @@ -24,7 +24,7 @@ import { Separator } from '@/components/ui/separator'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Play, SquarePlay, Trash2, TimerOff } from 'lucide-react'; import type { Build } from '@/lib/types'; -import { STATUS_TONE, buildRowName, formatDeletionCountdown } from '@/lib/build-format'; +import { PLATFORM_LABEL, STATUS_TONE, buildRowName, formatDeletionCountdown } from '@/lib/build-format'; interface Props { build: Build; @@ -105,9 +105,9 @@ export function BuildRow({ - {build.platform} + {PLATFORM_LABEL[build.platform]} {build.status_label && ( diff --git a/packages/dashboard/lib/build-format.ts b/packages/dashboard/lib/build-format.ts index 8fa1c3105..914967449 100644 --- a/packages/dashboard/lib/build-format.ts +++ b/packages/dashboard/lib/build-format.ts @@ -1,4 +1,4 @@ -import type { Build } from '@/lib/types' +import type { App, Build } from '@/lib/types' export const STATUS_TONE = { Backlog: 'backlog', @@ -7,6 +7,13 @@ export const STATUS_TONE = { Rejected: 'rejected', } as const satisfies Record +/** How a platform is named on screen. The API value is lowercase, and CSS `capitalize` would make it "Ios". */ +export const PLATFORM_LABEL = { + ios: 'iOS', + android: 'Android', + both: 'Both', +} as const satisfies Record + export function buildLabel(build: Build): string { if (build.version_name && build.build_number) return `${build.version_name} · build ${build.build_number}` return build.version_name ?? build.version_label ?? (build.build_number ? `build ${build.build_number}` : 'Build') diff --git a/packages/dashboard/src/__tests__/BuildRow.test.tsx b/packages/dashboard/src/__tests__/BuildRow.test.tsx index 3723ed023..999ca32c5 100644 --- a/packages/dashboard/src/__tests__/BuildRow.test.tsx +++ b/packages/dashboard/src/__tests__/BuildRow.test.tsx @@ -141,3 +141,12 @@ describe('BuildRow — Viewer (canWrite false)', () => { expect(screen.getByRole('button', { name: 'Cancel scheduled deletion of ios build 1, 1.0.0' })).toBeTruthy() }) }) + +// The platform comes from the API in lowercase. CSS `capitalize` used to dress it up, which drew "Ios". +describe('BuildRow — platform badge', () => { + it.each([['ios', 'iOS'], ['android', 'Android']] as const)('%s reads %s', (platform, label) => { + renderRow(makeBuild({ platform })) + expect(screen.getByText(label, { exact: true })).toBeTruthy() + cleanup() + }) +}) diff --git a/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx b/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx index f51985f19..2e553e518 100644 --- a/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx +++ b/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx @@ -60,6 +60,13 @@ describe('Default settings — what it loads', () => { // QA manages apps like Developer; Viewer is the one role that does not. // Mutation: drop 'QA' from `canEditApps` → this fails; the Viewer twin below holds the other side. + // `platform` arrives lowercase; the badge names it the way the platform does ("Ios" was CSS `capitalize`). + it('names the platform of each app', async () => { + auth.user = user('Developer') + renderPage() + expect(await screen.findByText('iOS', { exact: true })).toBeInTheDocument() + }) + it('lists the apps for QA', async () => { auth.user = user('QA') renderPage() diff --git a/packages/dashboard/src/pages/settings/Default.tsx b/packages/dashboard/src/pages/settings/Default.tsx index be46b6e09..eed1a4b19 100644 --- a/packages/dashboard/src/pages/settings/Default.tsx +++ b/packages/dashboard/src/pages/settings/Default.tsx @@ -14,6 +14,7 @@ import { FieldHint } from '@/components/ui/field-hint' import { PASSWORD_HINT, PASSWORD_MIN_LENGTH, PASSWORD_TOO_SHORT, describedBy } from '@/lib/password' import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' +import { PLATFORM_LABEL } from '@/lib/build-format' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Separator } from '@/components/ui/separator' import { @@ -361,9 +362,8 @@ export function DefaultSettings() { - {app.platform} + {PLATFORM_LABEL[app.platform]}
From e731f1f7655847250ab39063079f6ea39cbd5c28 Mon Sep 17 00:00:00 2001 From: Duchan Date: Sun, 18 Oct 2026 16:16:20 +0900 Subject: [PATCH 2/2] fix(dashboard): the QA session's no-agent line names the platform too Review of the badge fix: "No agents available for ios." printed the same raw value. Also moves the new settings test above the comment that belongs to the QA test. Co-Authored-By: Claude Opus 5.5 --- .changeset/dashboard-platform-label.md | 2 +- CHANGELOG.md | 2 +- .../src/__tests__/DefaultSettings.load.test.tsx | 4 ++-- .../src/__tests__/QASession.aiHolder.test.tsx | 14 ++++++++++++++ packages/dashboard/src/pages/QASession.tsx | 4 ++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.changeset/dashboard-platform-label.md b/.changeset/dashboard-platform-label.md index 68e828f21..b16e1e130 100644 --- a/.changeset/dashboard-platform-label.md +++ b/.changeset/dashboard-platform-label.md @@ -2,4 +2,4 @@ "@tapflowio/relay": patch --- -Dashboard: platform badges in App Center's build rows and in Settings → Apps read `iOS`, `Android` and `Both`. They read `Ios`, because the lowercase API value was capitalized by CSS. +Dashboard: platform badges in App Center's build rows and in Settings → Apps, and the QA session's "No agents available for …" line, read `iOS`, `Android` and `Both`. They read `Ios`, or `ios` in the QA session line, because the lowercase API value was shown as it came, with only CSS capitalization on the badges. diff --git a/CHANGELOG.md b/CHANGELOG.md index b50f7e865..8a7527097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- **Platform badges say iOS, not Ios.** App Center's build rows and Settings → Apps now name the platform `iOS`, `Android` or `Both`. +- **Platform badges say iOS, not Ios.** App Center's build rows, Settings → Apps and the QA session's "No agents available" line now name the platform `iOS`, `Android` or `Both`. - **Long dashboard pages no longer scroll the whole window.** On Settings → Default and other long forms the window grew a second scrollbar and the header could scroll out of view, because a screen-reader-only status line near the bottom of a form was stretching the page. Only the page area scrolls now. diff --git a/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx b/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx index 2e553e518..2bf1d8afa 100644 --- a/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx +++ b/packages/dashboard/src/__tests__/DefaultSettings.load.test.tsx @@ -58,8 +58,6 @@ describe('Default settings — what it loads', () => { expect(await screen.findByDisplayValue('Coffee')).toBeInTheDocument() }) - // QA manages apps like Developer; Viewer is the one role that does not. - // Mutation: drop 'QA' from `canEditApps` → this fails; the Viewer twin below holds the other side. // `platform` arrives lowercase; the badge names it the way the platform does ("Ios" was CSS `capitalize`). it('names the platform of each app', async () => { auth.user = user('Developer') @@ -67,6 +65,8 @@ describe('Default settings — what it loads', () => { expect(await screen.findByText('iOS', { exact: true })).toBeInTheDocument() }) + // QA manages apps like Developer; Viewer is the one role that does not. + // Mutation: drop 'QA' from `canEditApps` → this fails; the Viewer twin below holds the other side. it('lists the apps for QA', async () => { auth.user = user('QA') renderPage() diff --git a/packages/dashboard/src/__tests__/QASession.aiHolder.test.tsx b/packages/dashboard/src/__tests__/QASession.aiHolder.test.tsx index 7dcc281bc..707b928e9 100644 --- a/packages/dashboard/src/__tests__/QASession.aiHolder.test.tsx +++ b/packages/dashboard/src/__tests__/QASession.aiHolder.test.tsx @@ -56,3 +56,17 @@ describe('QASession — a device an AI agent is driving', () => { expect(screen.getByRole('button', { name: /iPhone SE/ }).textContent).toContain('In use') }) }) + +describe('QASession — no agent for the build', () => { + // The platform arrives lowercase; the sentence names it the way the platform does. + it('names the platform as iOS', async () => { + render(withQuery( + + + , + )) + await vi.waitFor(() => expect(deliver).not.toBeNull()) + await act(async () => { deliver!({ type: 'agents:listed', sessions: [] }) }) + expect(await screen.findByText('No agents available for iOS.', { exact: true })).toBeTruthy() + }) +}) diff --git a/packages/dashboard/src/pages/QASession.tsx b/packages/dashboard/src/pages/QASession.tsx index 12145f377..20cbd59ef 100644 --- a/packages/dashboard/src/pages/QASession.tsx +++ b/packages/dashboard/src/pages/QASession.tsx @@ -24,7 +24,7 @@ import { BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; import { cn } from '@/lib/utils'; -import { STATUS_TONE, buildLabel } from '@/lib/build-format'; +import { PLATFORM_LABEL, STATUS_TONE, buildLabel } from '@/lib/build-format'; import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; @@ -319,7 +319,7 @@ export function QASession() { {agentGroups.length === 0 ? (

- {connected ? `No agents available for ${os}.` : 'Connecting to relay…'} + {connected ? `No agents available for ${PLATFORM_LABEL[os]}.` : 'Connecting to relay…'}

) : (