feat(phase1): "Download the board app" button + App Lab import steps
Replace BoardClaim's old `curl | bash` setup command with a download button (→ /download/apess-onboard.zip, served same-origin by the web container) and rewrite the steps to the App Lab flow: download → Import an app → Run → type the code the matrix scrolls. Works on prod and the LAN box (relative /download URL). Added a test asserting the link + download attr. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c02898a02d
commit
b335875509
@@ -16,14 +16,16 @@ export interface BoardClaimProps {
|
|||||||
initialCode?: string
|
initialCode?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The self-service bring-up steps — the board is already set up from the week. */
|
/** Self-service bring-up: download the App Lab app, import it, Run it, enter the code. */
|
||||||
const STEPS = [
|
const STEPS = [
|
||||||
'On your board, open a terminal and run the workshop setup script (below).',
|
'Download the board app below.',
|
||||||
'It checks your node is ready, registers it, and scrolls a code across the LED matrix.',
|
'On your Uno Q, open App Lab → “Import an app” → pick the downloaded file.',
|
||||||
'Type the code your board is showing to bind it to your team.',
|
'Click Run. The node comes up and scrolls a code across the LED matrix.',
|
||||||
|
'Type that code below to bind the board to your team.',
|
||||||
]
|
]
|
||||||
|
|
||||||
const SETUP_CMD = 'curl -fsSL https://apess.redclaw.dev/setup.sh | bash'
|
// Served same-origin from the web container (see deploy/nginx: location /download/).
|
||||||
|
const DOWNLOAD_URL = '/download/apess-onboard.zip'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The board bring-up wizard for pre-deployed devices: the attendee runs the
|
* The board bring-up wizard for pre-deployed devices: the attendee runs the
|
||||||
@@ -84,17 +86,14 @@ export function BoardClaim({ teamId, teamName, members, connected, port, onClaim
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ol>
|
</ol>
|
||||||
<div className="flex items-center gap-2 rounded-md border border-border bg-muted/40 px-3 py-2">
|
<a
|
||||||
<code className="font-mono text-[11px] text-foreground/90 select-all flex-1 truncate">{SETUP_CMD}</code>
|
href={DOWNLOAD_URL}
|
||||||
<button
|
download
|
||||||
type="button"
|
data-testid="download-board-app"
|
||||||
aria-label="Copy setup command"
|
className="flex items-center justify-center gap-2 rounded-md border border-primary/40 bg-primary/5 px-3 py-2.5 text-sm font-medium text-primary hover:bg-primary/10 transition-colors"
|
||||||
onClick={() => navigator.clipboard?.writeText(SETUP_CMD)}
|
>
|
||||||
className="font-mono text-[10px] uppercase tracking-widest text-muted-foreground hover:text-foreground shrink-0"
|
<span aria-hidden>↓</span> Download the board app
|
||||||
>
|
</a>
|
||||||
Copy
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
aria-label="Claim code"
|
aria-label="Claim code"
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ describe('TeamRegistration', () => {
|
|||||||
expect(screen.getByRole('heading', { name: /team registration/i })).toBeInTheDocument()
|
expect(screen.getByRole('heading', { name: /team registration/i })).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('offers the board-app download (same-origin /download route)', () => {
|
||||||
|
renderPage()
|
||||||
|
const link = screen.getByTestId('download-board-app')
|
||||||
|
expect(link).toHaveAttribute('href', '/download/apess-onboard.zip')
|
||||||
|
expect(link).toHaveAttribute('download')
|
||||||
|
})
|
||||||
|
|
||||||
it('persists the team name to the session store on input', async () => {
|
it('persists the team name to the session store on input', async () => {
|
||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
renderPage()
|
renderPage()
|
||||||
|
|||||||
Reference in New Issue
Block a user