Restaurant floor plan and reservation manager. Lay out tables, assign zones, track bookings — all on an interactive SVG map.
- Interactive floor map — 3 floors, 6 zones, 24 tables in SVG. Pan, cursor-anchored zoom, fit-to-screen.
- Table editor — move, resize and rotate tables, with snapping to zones.
- Reservations — create, edit, change status, filter by date and status, jump from a booking to its table.
- AI translation — guest comments between Russian, English, Kazakh and Chinese via OpenRouter.
- Swappable data layer —
localStorageby default, optional in-memory Mock API, one interface behind both. - Demo reset — restore the seed dataset behind a confirmation.
![]() Table in focus — status, capacity, zone and every booking for the selected day. |
![]() Plan editor — drag, resize and rotate tables, switch shapes, edit floors and zones. |
![]() New reservation — table, guest, party size, time and duration, validated with zod. |
![]() Comment translation — guest notes in Russian, English, Kazakh or Chinese. |
The interface ships in Russian; screenshots use the bundled demo dataset.
| Area | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| UI | React 19, Tailwind CSS 4, shadcn/ui on Base UI |
| Icons | lucide-react |
| Forms & validation | react-hook-form, zod |
| Dates | date-fns, react-day-picker |
| AI | Vercel AI SDK + OpenRouter |
| Testing | Jest (12 suites, pure logic) |
| Language | TypeScript 5 (strict) |
Requires Node.js ≥ 20.9 and pnpm.
pnpm install
cp .env.sample .env.local
pnpm devOpen http://localhost:3000. npm works too.
| Variable | Required | Default | Purpose |
|---|---|---|---|
NEXT_PUBLIC_ENABLE_MOCK_API |
no | false |
Shows the data-source switch in Settings. Leave off to stay on localStorage. |
OPENROUTER_API_KEY |
no | — | Server-only key for comment translation. Every other feature works without it; translation just reports that it is not configured. |
Warning
Never expose the OpenRouter key with a NEXT_PUBLIC_ prefix. It is read only inside the /api/translate route handler.
| Command | Does |
|---|---|
pnpm dev |
Dev server with Turbopack |
pnpm build |
Production build |
pnpm start |
Serve the production build |
pnpm test |
Run the Jest suites |
pnpm lint |
ESLint |
npx tsc --noEmit |
Type check |
Drag empty space to pan, wheel to zoom at the cursor, click a table to select it. Edit opens plan mode: drag a table to move it, use the handle above it to rotate freely, or the panel below for ±15° steps and exact angles. Save commits staged changes, Cancel drops them.
src/
├─ app/ # App Router pages and /api routes (mock API, translation)
├─ client/ # repository implementations — localStorage and HTTP
├─ components/ # floor map, reservations sidebar, shadcn/ui primitives
├─ entities/ # domain models, zod schemas, seed data
├─ features/ # comment translation, reservation management
├─ lib/ # floor-plan geometry and camera math
└─ server/ # in-memory store backing the mock API
tests/ # Jest suites: geometry, camera, state transitions
- The UI depends on a single
RestaurantRepositoryinterface, so swapping storage never touches components. - State lives in
localStorageunder the versioned keyhoststand.restaurant.v1. Missing or corrupt data falls back to the seed: 3 floors, 6 zones, 24 tables, 15 reservations. Bump the key version whenrestaurantStateSchemachanges incompatibly. - The map is plain SVG — no canvas library. Camera math, geometry, zone snapping and status transitions are pure functions, which is exactly what the test suites cover.
See docs/architecture.md for the full breakdown.
- Chairs around tables are not computed or drawn.
- No two-finger pinch zoom.
- Saving several entities is not yet one atomic repository transaction.
- Overlapping reservations on the same table are not blocked.
- Zones are created at a fixed size — there is no drag-to-draw mode.




