What you will build
Milestone 13: Use the typed Ticket and TicketCard files from Byte 12. Create a form that refuses blank subjects and a reducer that adds or resolves issues. This is the first interactive milestone of the project.
A dashboard becomes useful when an agent can create, update and resolve tickets.
Visible checkpoint: Create adds a card; dispatching a status action updates its status without mutating the original array. You can study this Byte by itself, though the project can also be built progressively.
Understand the concept first
Controlled form fields keep typed values in React state. useReducer can express create, status change and assignment as explicit actions. Reject empty subject values and preserve immutable updates.
In plain language, an interface should show useful information and respond predictably to users. We begin with one small technical change before introducing more features.
Architecture: what happens underneath
BUILD THE PROJECT · CHECKPOINT 1/5
From project files to browser output
Root mounting point
Expected outcome: The first app screen appears once you run npm run dev.
Execution path
01Form input → validation → dispatch action → next state → card updatesFollow the arrows and explain which step is React state, which is a browser action, and which would require a real backend.
Explore a realistic preview
BUILD ONE APPLICATION · BYTE 1/5
Setup & layout
Explore the UI that this Byte helps you build. Demo data only; no actual customer records.
Support Desk
Customer support ticket overview
Click a ticket to inspect its selected state.
Stage 1: Setup & layout. This preview illustrates the progression, not a connected backend.
Use the visual to predict what a user would see. The preview uses simulated demo data and does not represent a connected enterprise service.
Coding walkthrough step by step
Complete, ordered project checkpoints: Read the source-of-truth build guide. Use the full replacement files from that guide alongside the focused explanations below. Some snippets below demonstrate an insertion rather than a complete file.
Step 1 — Create a reducer for changes
Edit or create the indicated file.
01// src/ticketReducer.ts02import type {Ticket} from './data/tickets';03export type Action={type:'add';ticket:Ticket}|{type:'status';id:number;status:Ticket['status']};04export function ticketReducer(state:Ticket[],action:Action):Ticket[]{05switch(action.type){06case 'add':return [...state,action.ticket];07case 'status':return state.map(t=>t.id===action.id?{...t,status:action.status}:t);08}09}
What this step accomplishes: A dashboard becomes useful when an agent can create, update and resolve tickets.
Check: Run your app or inspect the updated component before continuing.
Step 2 — Add reducer and controlled input
Edit or create the indicated file.
01// src/App.tsx — inside App, replacing the static ticket source02const [tickets,dispatch]=useReducer(ticketReducer,initialTickets);03const [subject,setSubject]=useState('');04function addTicket(){const title=subject.trim();if(!title)return;05dispatch({type:'add',ticket:{id:Date.now(),subject:title,priority:'Low',status:'Open'}});setSubject('');}
What this step accomplishes: Create adds a card; dispatching a status action updates its status without mutating the original array.
Check: Run your app or inspect the updated component before continuing.
Step 3 — Connect controls to the screen
Edit or create the indicated file.
01// Add above ticket cards inside the JSX:02<label htmlFor="subject">Issue subject</label>03<input id="subject" value={subject} onChange={e=>setSubject(e.target.value)} className="border p-2"/>04<button type="button" onClick={addTicket}>Create ticket</button>05// Pass tickets.map(...) to cards; to resolve a ticket dispatch:06dispatch({type:'status',id:101,status:'Resolved'});
What this step accomplishes: Create adds a card; dispatching a status action updates its status without mutating the original array.
Check: Run your app or inspect the updated component before continuing.
Expected screen and execution
What you should observe: Create adds a card; dispatching a status action updates its status without mutating the original array.
Trace it: Form input → validation → dispatch action → next state → card updates. A user-visible output is stronger evidence than a code snippet alone.
Interactive decision and diagnosis
If the expected result does not appear, inspect the relevant file and resolve the first console error. Compare your current code to the preceding checkpoint.
Typical issue: The code fragments modify Byte 02; import useReducer, useState and ticketReducer in App. The dispatch call belongs in an event handler, not inside render.
How teams use this pattern
A frontend developer implements the interface, a tester verifies accessible controls and failure behavior, and the product owner describes the expected result. This is a simplified demonstration of that collaboration, not a representation of a particular company's internal infrastructure.
Compare UI and implementation
| What the user sees | What the application does |
|---|---|
| Create adds a card; dispatching a status action updates its status without mutating the original array. | Form input → validation → dispatch action → next state → card updates |
| An action or failure | A typed handler, state transition or error boundary determines the visible response |
Hands-on practice
Add a status button to TicketCard using an onResolve callback and validate a blank title.
Evidence: Capture the visible result, explain the code you changed, and reproduce one failure case.
Interview Bit
Question: How does this milestone change what the learner can actually do?
Answer: It connects form validation and reducer actions to visible issue creation and resolution.
Key Takeaways
- Controlled inputs keep form values in React state.
- Validation rejects an empty issue title.
- useReducer centralizes related ticket transitions.
- Actions describe an add or resolve request.
- Reducers must return new state rather than mutate old data.
- A ticket id identifies the record to update.
- Derived Open counts change when ticket status changes.
- Errors must be readable and recoverable.
Knowledge Check and Primary Sources
PRACTISE AND CHECK
Knowledge Check · 5 questions
Choose one answer per question, check your results, and retry.
0 of 5 answered
Frequently asked: Is a real backend required for this Byte? No; this handbook uses a mock boundary until you supply a real API. Does the demo contain real customer data? No.
Primary sources: Extracting state logic into a reducer · React forms.