Add page templates, overhaul DESIGN.md, and fix SideNav text alignment

Introduce AppShell, DashboardPage, ListPage, and FormPage template
components with Storybook recipe stories for AI agent consumption.
Thoroughly update DESIGN.md with all missing components, corrected
token values, and page layout conventions. Fix SideNav button items
defaulting to centered text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 15:11:01 +10:00
parent d915443b8c
commit 95f72407f8
15 changed files with 1469 additions and 113 deletions

View File

@@ -0,0 +1,198 @@
import type { Meta, StoryObj } from '@storybook/react'
import { useState } from 'react'
import { FormPage } from './FormPage'
import { AppShell } from '@/components/templates/AppShell/AppShell'
import { TopBar } from '@/components/organisms/TopBar/TopBar'
import { SideNav, SideNavItem, SideNavGroup, SideNavDivider } from '@/components/organisms/SideNav/SideNav'
import { PageHeader } from '@/components/organisms/PageHeader/PageHeader'
import { Card, CardContent } from '@/components/molecules/Card/Card'
import { Alert } from '@/components/molecules/Alert/Alert'
import { Input } from '@/components/atoms/Input/Input'
import { Select } from '@/components/atoms/Select/Select'
import { Button } from '@/components/atoms/Button/Button'
import { Badge } from '@/components/atoms/Badge/Badge'
import { Avatar } from '@/components/atoms/Avatar/Avatar'
import { IconButton } from '@/components/atoms/IconButton/IconButton'
import { Menu, Bell, Home, FileText, LayoutGrid, Users, Link, ArrowRight } from 'lucide-react'
const NswLogo = () => (
<div className="flex size-7 items-center justify-center rounded bg-white/20 text-caption font-bold text-white">NSW</div>
)
const meta: Meta<typeof FormPage> = {
title: 'Templates/FormPage',
component: FormPage,
tags: ['autodocs', 'template'],
parameters: {
layout: 'fullscreen',
docs: {
description: {
component: 'Form page template with optional vertical stepper and constrained-width form content. Use inside AppShell for the full page layout.',
},
},
},
}
export default meta
type Story = StoryObj<typeof FormPage>
export const PDPDetails: Story = {
name: 'PDP Form',
render: () => {
const [collapsed, setCollapsed] = useState(false)
return (
<AppShell
topBar={
<TopBar
title="Performance and development plan"
leading={<IconButton icon={<Menu />} aria-label="Menu" variant="tertiary" onClick={() => setCollapsed(!collapsed)} />}
logo={<NswLogo />}
>
<IconButton icon={<Bell />} aria-label="Notifications" variant="tertiary" />
<Avatar initials="DW" size="sm" />
</TopBar>
}
sideNav={
<SideNav collapsed={collapsed}>
<SideNavItem icon={<Home />}>My status</SideNavItem>
<SideNavItem icon={<Users />}>My details</SideNavItem>
<SideNavItem icon={<LayoutGrid />}>Workspace</SideNavItem>
<SideNavItem icon={<Link />}>Resources</SideNavItem>
<SideNavItem icon={<FileText />}>My documents & links</SideNavItem>
<SideNavDivider />
<SideNavGroup icon={<FileText />} label="PDP" defaultOpen active>
<SideNavItem active>My PDP</SideNavItem>
<SideNavItem>PDP guide</SideNavItem>
<SideNavItem>Management</SideNavItem>
<SideNavItem>Useful links</SideNavItem>
<SideNavItem>Support</SideNavItem>
</SideNavGroup>
</SideNav>
}
sideNavCollapsed={collapsed}
>
<FormPage
header={
<PageHeader title="Siya Ram" subtitle="Role title goes here" theme="dark">
<div className="mt-2 flex items-center gap-4">
<Badge variant="warning">Plan - In progress</Badge>
<span className="text-small text-white/80">Date commenced: dd-mm-yyyy</span>
</div>
</PageHeader>
}
actions={
<>
<Select
label=""
variant="stacked"
options={[{ value: '2026', label: '2026 - PDP Siya Ram' }]}
defaultValue="2026"
/>
<Button variant="secondary">More actions</Button>
</>
}
steps={[
{ label: 'Your PDP details', status: 'current' },
{ label: 'Create your PDP', status: 'upcoming' },
{ label: 'Notify your PDP supervisor', status: 'upcoming' },
]}
>
<Card variant="surface">
<CardContent className="space-y-6 p-6">
<div>
<h2 className="text-h3 font-bold text-text">Welcome to your Performance and Development Plan (PDP)</h2>
<p className="mt-2 text-body text-text-secondary">
Once your goals are drafted and you're ready to share them, you can notify your PDP supervisor. Head to the Digital PDP page on the intranet to find key resources to help you complete your PDP.
</p>
</div>
<Alert variant="info" title="Your PDP details">
Fill in the details below to get started with your PDP.
</Alert>
<div className="space-y-4">
<Select
label="PDP year"
options={[
{ value: '2026', label: '2026' },
{ value: '2025', label: '2025' },
]}
defaultValue="2026"
/>
<div>
<p className="text-body font-semibold text-text">Middle leader role(s)</p>
<p className="mb-2 text-small text-text-secondary">Some text about middle leader roles</p>
<Select
label="Middle leader role type (optional)"
options={[
{ value: 'deputy', label: 'Deputy Principal' },
{ value: 'head', label: 'Head Teacher' },
{ value: 'asst', label: 'Assistant Principal' },
]}
defaultValue="deputy"
/>
</div>
<div>
<p className="text-body font-semibold text-text">Add your PDP supervisor's details here</p>
<p className="mb-2 text-small text-text-secondary">
Note: if your supervisor's name does not appear when you search for them, ask them to access the Digital PDP using their credentials, then try again.
</p>
<div className="space-y-4">
<Input label="PDP Supervisor's email" error="PDP Supervisor's email" defaultValue="dhoni.mahi@det.nsw.edu.au" />
<Input label="PDP Supervisor work location" error="PDP Supervisor work location" defaultValue="Work location goes here" />
</div>
</div>
<div>
<p className="text-body font-semibold text-text">Add your school or work location.</p>
<p className="mb-2 text-small text-text-secondary">
If you don't work in a school, add 'Education Office' as your work location.
</p>
<Input label="Your school or work location" error="Your school or work location" defaultValue="Work location goes here" />
</div>
<p className="text-small text-text-secondary">
<strong>Note:</strong> As the school leader, your principal can view all the POPs in the school.
</p>
<div className="flex justify-start pt-2">
<Button rightIcon={<ArrowRight size={18} />}>Proceed</Button>
</div>
</div>
</CardContent>
</Card>
</FormPage>
</AppShell>
)
},
}
export const SimpleForm: Story = {
name: 'Simple Form (no stepper)',
render: () => (
<FormPage
header={<PageHeader title="Create Account" subtitle="Set up your profile to get started" />}
>
<Card variant="surface">
<CardContent className="space-y-4 p-6">
<Input label="Full name" placeholder="Enter your full name" />
<Input label="Email address" type="email" placeholder="you@example.com" />
<Select
label="Role"
options={[
{ value: 'teacher', label: 'Teacher' },
{ value: 'principal', label: 'Principal' },
{ value: 'admin', label: 'Administrator' },
]}
/>
<div className="flex justify-end gap-3 pt-4">
<Button variant="tertiary">Cancel</Button>
<Button>Create Account</Button>
</div>
</CardContent>
</Card>
</FormPage>
),
}