npm installnpm run devOpen http://localhost:3000 to view the app.
npm run build
npm startThis project uses Vitest with React Testing Library for component testing.
# Run tests in watch mode
npm run test
# Run tests once (CI mode)
npm run test:runThis application demonstrates two approaches to state management, showcasing when to use each pattern. I included both intentionally to demonstrate my experience with each:
Location: src/app/context/SelectedServiceContext.tsx
Used for lightweight, component-tree-scoped state that doesn't require complex logic.
// Provider wraps the app in layout.tsx
<SelectedServiceProvider>
{children}
</SelectedServiceProvider>
// Usage in components
const { service, setService } = useSelectedService();Use Case: Sharing the currently selected service across components without prop drilling.
Why Context?
- Simple read/write state
- No complex state transitions
- Scoped to a specific feature (service selection)
- Avoids prop drilling through the component tree
Location: src/app/store/
store/
├── index.tsx # Store configuration
├── hooks.ts # Typed useDispatch and useSelector hooks
└── slices/
└── bookingSlice.tsx # Booking state slice
Used for more complex state that benefits from:
- Predictable state updates with reducers
- DevTools for debugging
- Middleware support
- Centralized state logic
When to choose Redux over Context:
- Complex state logic with multiple related actions
- Need for middleware (async thunks, logging, persistence)
- State shared across many unrelated components
- Debugging with Redux DevTools is valuable
- Large applications where predictable state updates matter
For this booking app, Context is totally fine since the state requirements are simple. Redux is included to demonstrate the setup pattern and familiarity with the tool for larger applications.
This ensures all client components have access to shared state while keeping the layout as a Server Component.
Pages (page.tsx) are Server Components that fetch data on the server
Each route has a loading.tsx file that displays skeleton UI during data fetching: