ui/src/ tree is organised into three layers — app/, features/, and shared/ — each with a package-local import alias defined in ui/package.json. Components follow the pattern of separating stateful page components from presentational layout components, and data-fetching hooks from UI state.
Feature structure
coffee-shop feature
Page and layout components
CoffeeShopPage (features/coffee-shop/components/CoffeeShopPage.tsx) is the stateful root. It calls useCoffeeShopState() to collect all derived data and mutations, then passes them as props into the purely presentational CoffeeShopLayout:
CoffeeShopLayout renders the full page grid: PageHeader, HeroBanner, an optional Alert for backend errors, ViewModeTabs (switching between customer and barista views), and ReceiptDialog.
Customer panel
CustomerPanel (features/coffee-shop/components/customer/CustomerPanel.tsx) contains two cards:
OrderComposerCard— drink selector, order fields (customer name, size, extras), price preview, and submit button.MenuCatalogCard— grid ofMenuItemCardtiles that highlight the currently selected drink. Clicking a tile callsonSelectDrink.
Alert with status "info" while the menu query is still loading.
Barista panel
BaristaPanel (features/coffee-shop/components/barista/BaristaPanel.tsx) contains four components:
| Component | Purpose |
|---|---|
QueueSummary | Metric strip: active count, ready count, queue load, history count |
QueueBoardCard | Table of active orders with inline QueueRowActions for lifecycle transitions |
RecentActivityCard | Up to 4 completed or cancelled orders |
OrderDetailsDrawer | Radix UI drawer that opens when a barista selects a ticket; shows full order details and action buttons |
start-brewing, mark-ready, pick-up, and cancel, defined in features/coffee-shop/api/coffee.ts.
TanStack Query data layer
All server state lives infeatures/coffee-shop/hooks/useCoffeeQueries.ts. The hooks use stable query keys so mutations can invalidate exactly the right cache entries:
useOrderActionMutation follows the same pattern for the start-brewing, mark-ready, pick-up, and cancel actions.
useCoffeeShopState (features/coffee-shop/hooks/useCoffeeShopState.ts) composes all four hooks with local state for the selected order, receipt overlay, view mode, and order draft to produce a single state object consumed by CoffeeShopPage.
AppProviders (app/AppProviders.tsx) wraps the tree in QueryClientProvider and configures the global defaults:
Shared retroui primitives
Theshared/ui/retroui/ directory contains the project’s shadcn/ui-derived component library. All components are authored directly in the repo rather than auto-generated:
| Component | Radix UI dependency |
|---|---|
Alert | none (custom) |
Badge | none (custom) |
Button | @radix-ui/react-slot |
Card | none (custom) |
Dialog | @radix-ui/react-dialog |
Drawer | vaul |
Input | none (custom) |
Label | @radix-ui/react-label |
Progress | @radix-ui/react-progress |
Select | @radix-ui/react-select |
Sonner | sonner |
Spinner | none (custom) |
Tab | none (custom) |
Table | none (custom) |
Text | none (custom) |
Textarea | none (custom) |
SelectField, StatusBadge, TextAreaField, and TextField.
Storybook and component testing
Stories are co-located alongside components and follow the*.stories.{ts,tsx} naming convention. The Storybook config (ui/.storybook/main.ts) uses @storybook/react-vite and shares the same Vite + Tailwind config as the app build.
Components with stories in the coffee-shop feature include:
CoffeeShopLayout.stories.tsxPageHeader.stories.tsxOrderComposerCard.stories.tsx
BrowserMcpLandingView.stories.tsxDemoComposer.stories.tsxDemoStatusPanel.stories.tsx
Button.stories.tsx, Input.stories.tsx, Select.stories.tsx, and several field components.
Running tests
Vitest browser mode powers story-level interaction tests via@storybook/addon-vitest:
- Single run
- Watch mode
@vitest/browser with Playwright as the runner, so component behaviour is exercised in a real DOM rather than jsdom.