Lesson 13 Guide — Review / Buffer: The Feature-Folder Pattern¶
This is a review / buffer lesson — there is no lab. You've now built the complete TableServe front end — five entities (Staff, Categories, MenuItems, Orders, OrderItems), each the same feature folder. This lesson is catch-up time plus a guided look back at that repeated pattern, using Categories — your simplest entity, built in the Lesson 9–10 labs — as the clearest example. The goal is a rock-solid mental model before the PRS capstone, where you build the same pattern five more times.
Goal: open any entity's folder and explain each file's role, and confirm every screen works in the browser. If you fell behind anywhere, this is the lesson to catch up.
Why look at Categories? It's the simplest full CRUD you built — a card grid, a skeleton, and a no-FK shared form — so the feature-folder pattern shows through with the least noise. Everything here you also did (with a wrinkle or two) for Menu Items, Staff, and Orders. On PRS, Vendors is Categories' twin.
1. The feature folder, file by file¶
Every entity you built follows the same shape. Here it is for Categories — open your own
src/categories/ folder and read along:
| File | Role | Lesson it draws on |
|---|---|---|
ICategory.ts |
The TypeScript interface (id, name, sortOrder) |
3 |
CategoryAPI.ts |
list / find / post / put / delete, built up one method at a time (via checkStatus/parseJSON after Lesson 12) |
7–10, 12 |
CategoriesPage.tsx |
Route target — heading + Add Category button; fetches in useEffect, holds state, .map()s cards, skeletons |
4, 5, 6 |
CategoryCard.tsx |
One card (props category, onRemove) with the ⋮ dropdown |
5, 6 |
CategoryCardSkeleton.tsx |
Grey placeholder card shown while loading | 6 |
CategoryForm.tsx |
Shared create/edit form (no FK), react-hook-form | 7, 10 |
CategoryCreatePage.tsx / CategoryEditPage.tsx |
Thin wrappers around the form | 7, 10 |
The table is in reading order — open ICategory.ts first and work down it. Each file leans
on the one above: the API returns objects in the interface's shape, the page calls the API, the
card renders one of the items the page maps over, and the form writes back through the API.
Every file is just its lesson's pattern applied to Categories.
The page fetches and maps directly — like your
MenuItemsPage/StaffPage/OrdersPage. There's no separateListcomponent in this app; the page owns the state. (The PRS spec does split the.map()into a{Entity}Listcomponent — one extra props hand-off, no new idea. Lesson 15 covers the swap.)
2. The same pattern, five times — what changed each time¶
Categories is the plain version. Each of your other entities is the same folder with one twist — and that's the whole map of the front end:
| Entity | The twist over the plain Categories pattern |
|---|---|
| Staff | role-flag badges on the card, checkboxes (no FK) on the form; feeds Sign In |
| Menu Items | a Category FK dropdown on the form |
| Orders | a table (not cards) with status badges + a useSearchParams filter; a detail page with status-driven workflow buttons and a Cancel modal; a Staff FK form pre-filled from Context |
| Order Items | a nested child form (scoped to an order) with derived fields (watch) and a parent Total the API recalculates |
If you can see Categories as the base case and each of the others as "base + one idea," you understand the whole front end.
3. Wiring recap¶
Every entity is reachable the same way — routes under Layout plus a nav item:
{ path: "categories", element: <CategoriesPage /> },
{ path: "categories/create", element: <CategoryCreatePage /> },
{ path: "categories/edit/:id", element: <CategoryEditPage /> },
…plus its Nav.Link in AppNav. Confirm the four top-level entities — Orders, Menu
Items, Categories, Staff — have routes and a nav link.
Order Items is the deliberate exception: it has routes
(orders/detail/:id/orderitem/create and …/edit/:itemId) but no nav link, because a
child collection is only reachable through its parent — you get there from an order's detail
page, never from the sidebar. On PRS, RequestLines works exactly the same way.
4. Catch-up checklist — the complete app¶
Use the buffer to close any gaps. In the browser, you should have all of:
- Sign In + Context — login, localStorage persistence, role-based UI, the Cancel ownership check
- Staff — list, card grid with role badges + skeletons, shared form (checkboxes)
- Categories — list + card grid + skeletons, shared no-FK form (Lesson 9–10 labs)
- Menu Items — list, card grid, shared form with the Category FK dropdown
- Orders — table with status badges + filter + 3-dots menu; create/edit form (Lesson 11 lab)
- Order Detail — definition-list summary, workflow buttons, Cancel modal
- Order Items — nested form (derived Price/Amount), items table, delete-confirm modal, parent total recalculating
- Toasts +
checkStatusacross every CRUD action
Anything unchecked, fix it now — the capstone assumes the whole app works.
5. Verifying in the browser¶
Prerequisite: your API is running on the HTTP profile (
http, nothttps) with all seed data loaded.
Do a quick pass on Categories (your simplest entity) to confirm the pattern end to end:
- Open
/categories— the card grid renders (skeletons first on a slow network), each card showing name + sort order with a ⋮ menu. - Add Category → empty form; Save → back to the list with the new card. ⋮ → Edit → pre-filled; Save → updated. Delete → confirm → card removed, success toast.
- Compare side by side with Menu Items and Staff — identical behavior, because they're the same patterns.
- Console clean.
The General Pattern (what to take away)¶
- Every entity is the same feature folder: interface, API module, page (fetch + map + skeletons), card (or row), skeleton, shared form, thin create/edit wrappers.
- A new entity introduces no new concept — it's the Lessons 3–12 patterns again. You proved it five times.
- Categories is the base case; every other entity is "base + one idea" (a badge, an FK dropdown, a table, a nested child).
On PRS you build this five more times — Users, Vendors, Products, Requests, RequestLines — the same folders, the same twists. Vendors is Categories' twin.