Interactive preview, sandboxed: nothing you type leaves this page.
import { useState } from "react";type SetupStep = { title: string; consequence: string;};const steps: SetupStep[] = [ { title: "Import the timetable", consequence: "Without it, the lesson slots for the term will not be created.", }, { title: "Invite teaching staff", consequence: "Without staff access, teachers cannot review or manage their classes.", }, { title: "Map rooms to departments", consequence: "Without room assignments, departments cannot direct lessons to the right spaces.", }, { title: "Set the term dates", consequence: "Without dates, the school calendar and parent portal cannot show the right term.", }, { title: "Publish the parent portal", consequence: "Until it is published, families cannot see the term information online.", },];export default function Block() { const [completed, setCompleted] = useState<boolean[]>(steps.map(() => false)); const [firstDay, setFirstDay] = useState(""); const [roomPrefix, setRoomPrefix] = useState(""); const completedCount = completed.filter(Boolean).length; function toggleStep(index: number) { setCompleted((current) => current.map((value, i) => (i === index ? !value : value))); } return ( <section className="w-full bg-slate-50 px-4 py-8 text-slate-900 dark:bg-neutral-950 dark:text-slate-100 sm:px-8 sm:py-12"> <div className="mx-auto max-w-4xl"> <header className="mb-7 border-b border-slate-200 pb-6 dark:border-neutral-800 sm:mb-8 sm:pb-7"> <p className="mb-3 text-xs font-semibold uppercase tracking-[0.12em] text-slate-600 dark:text-slate-400"> School administration <span className="px-1.5 text-slate-400 dark:text-slate-600">/</span> New term setup </p> <div className="flex flex-col gap-5 sm:flex-row sm:items-end sm:justify-between"> <div> <h1 className="text-2xl font-semibold tracking-tight sm:text-[30px]">Prepare the new term</h1> <p className="mt-2 max-w-xl text-sm leading-6 text-slate-700 dark:text-slate-300"> Complete the setup tasks before term begins. Changes take effect as you work. </p> </div> <div className="w-full sm:w-52 sm:shrink-0"> <div className="mb-2 flex items-center justify-between gap-3 text-sm"> <span className="font-medium text-slate-800 dark:text-slate-200">Setup progress</span> <span aria-live="polite" className="tabular-nums text-slate-700 dark:text-slate-300"> {completedCount} of {steps.length} complete </span> </div> <div aria-label={`${completedCount} of ${steps.length} steps complete`} aria-valuemax={steps.length} aria-valuemin={0} aria-valuenow={completedCount} className="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-neutral-800" role="progressbar" > <div className="h-full rounded-full bg-blue-700 transition-[width] duration-200 dark:bg-blue-400" style={{ width: `${(completedCount / steps.length) * 100}%` }} /> </div> </div> </div> </header> <div className="overflow-hidden rounded-lg border border-slate-300 bg-white dark:border-neutral-700 dark:bg-neutral-900"> <ul className="divide-y divide-slate-200 dark:divide-neutral-800"> {steps.map((step, index) => { const isCurrent = index === 3; const isComplete = completed[index]; return ( <li key={step.title} className={isCurrent ? "bg-slate-50/80 dark:bg-neutral-900" : ""}> <div className="flex items-start gap-3 px-4 py-4 sm:gap-4 sm:px-6 sm:py-5"> <div className="flex min-h-7 min-w-7 items-center justify-center pt-0.5"> <input checked={isComplete} onChange={() => toggleStep(index)} aria-label={`${isComplete ? "Mark incomplete" : "Mark complete"}: ${step.title}`} className="h-6 w-6 cursor-pointer rounded border-slate-400 accent-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 dark:border-neutral-500 dark:accent-blue-400 dark:focus-visible:outline-blue-300" type="checkbox" /> </div> <div className="min-w-0 flex-1"> <div className="flex flex-wrap items-center gap-x-3 gap-y-1.5"> <h2 className={`text-base font-semibold leading-6 ${isComplete ? "text-slate-600 line-through dark:text-slate-400" : "text-slate-900 dark:text-slate-100"}`}> {step.title} </h2> {isCurrent && ( <span className="rounded-sm border border-blue-300 bg-blue-50 px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-blue-900 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-200"> Current step </span> )} {isComplete && <span className="text-xs font-medium text-emerald-800 dark:text-emerald-300">Complete</span>} </div> <p className="mt-1 text-sm leading-5 text-slate-700 dark:text-slate-300">{step.consequence}</p> {isCurrent && ( <div className="mt-5 border-t border-slate-200 pt-4 dark:border-neutral-800 sm:mt-6 sm:pt-5"> <p className="mb-4 text-sm font-medium text-slate-800 dark:text-slate-200">Term details</p> <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-5"> <div> <label htmlFor="term-first-day" className="mb-1.5 block text-sm font-medium text-slate-800 dark:text-slate-200"> First day of term </label> <input id="term-first-day" type="date" value={firstDay} onChange={(event) => setFirstDay(event.target.value)} className="h-11 w-full rounded-md border border-slate-400 bg-white px-3 text-sm text-slate-900 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 dark:border-neutral-600 dark:bg-neutral-950 dark:text-slate-100 dark:focus-visible:outline-blue-300" /> </div> <div> <label htmlFor="room-prefix" className="mb-1.5 block text-sm font-medium text-slate-800 dark:text-slate-200"> Room naming prefix <span className="font-normal text-slate-600 dark:text-slate-400">(for room mapping)</span> </label> <input id="room-prefix" type="text" value={roomPrefix} onChange={(event) => setRoomPrefix(event.target.value)} placeholder="e.g. B — rooms become B101, B102" className="h-11 w-full rounded-md border border-slate-400 bg-white px-3 text-sm text-slate-900 shadow-sm placeholder:text-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 dark:border-neutral-600 dark:bg-neutral-950 dark:text-slate-100 dark:placeholder:text-slate-400 dark:focus-visible:outline-blue-300" /> </div> </div> </div> )} </div> </div> </li> ); })} </ul> </div> <p className="mt-4 text-xs leading-5 text-slate-600 dark:text-slate-400"> Mark a task complete when it has been finished. You can update this checklist as setup progresses. </p> </div> </section> );}