Architecture
Next.js, but customized
This runs on Next.js 16 (App Router + Turbopack). Two things differ from a stock Next app, and both are load‑bearing:
- Middleware is
src/proxy.ts, notmiddleware.ts. In this Next version themiddlewareconvention was renamed toproxy. It still runs before routes and can redirect/rewrite. See auth-and-roles.md. - All app code lives under
src/. The path alias@/*maps to./src/*(tsconfig.json).public/, config files and.envstay at the repo root.
Because the framework behaves differently from stock Next, prefer the bundled docs in
node_modules/next/dist/docs/over general Next knowledge when touching routing, middleware, or file conventions.
Locale‑prefixed routing
Every user‑facing route is nested under a [locale] segment, so URLs start with
/en, /uz, or /ru:
src/app/
[locale]/
layout.tsx ROOT layout: <html lang>, wraps LanguageProvider, generateStaticParams(en/uz/ru)
page.tsx redirects to /{locale}/dashboard
not-found.tsx
login/ /{locale}/login (public)
(dashboard)/ auth-gated group; layout requires a session
layout.tsx QueryProvider + ToastProvider + DraftProvider + DashboardContainer
dashboard/ calendar/ book/ clients/ contracts/ notifications/
settings/ (admins, services, hotels, client-groups)
api/ route handlers — NOT under [locale]
globals.css favicon.icoThe proxy adds the locale prefix on any locale‑less request and enforces auth. API routes and static files (anything with a file extension) bypass it. Full details in i18n.md and auth-and-roles.md.
The features/ convention
Each screen is a folder under src/features/<name>/:
features/dashboard/
DashboardPage.tsx composes the pieces (the route re-exports this)
useDashboardPage.ts one hook: all state, effects, handlers, derived data
components/ presentational pieces, each takes the hook's state
constants.tsx utils.ts types.ts- The route file (
src/app/[locale]/(dashboard)/dashboard/page.tsx) is a one‑line re‑export of the feature’s page component. - The
useXxxPage()hook returns one big object; presentational components take it (often assorw) and calluseTranslation()themselves. - This keeps route files tiny and makes each screen a self‑contained unit.
Feature‑specific components live inside the feature (features/*/components/);
only genuinely shared components live in src/components/.
Providers (where state lives)
LanguageProvider—src/app/[locale]/layout.tsx(root). Wraps the whole app so both login and dashboard can translate. Language is driven by the URL locale segment.QueryProvider(React Query),ToastProvider,DraftProvider— the dashboard group layout only.DraftProviderautosaves in‑progress forms (e.g. the new‑service form) tolocalStorage.
Design system
src/components/ui/ holds the reusable primitives:
Button.tsx— variants (primary/secondary/danger/ghost) and sizes;mdis the 38px control height that matches…Dropdown.tsx— the reusable select (38px trigger). Toolbars mix these two, so they share a height.InfoHint.tsx— an “i” icon with a hover/focus tooltip (used on form fields).Select.tsx,Input.tsx,Skeleton.tsx,Spinner.tsx.
Icons are lucide-react everywhere in the UI (no emoji “stickers”). Emoji are only used in Telegram messages and CLI scripts.
Data fetching
- Some screens use React Query hooks in
src/hooks/(useServices,useHotels,useBookings,useContracts,useNotifications). - Others fetch directly in their
useXxxPagehook (fetch('/api/…')). - API clients for a few resources live in
src/lib/api/.
Server code
src/lib/mongodb.ts— cached Mongoose connection (connectDB()), safe to call per request.src/lib/session.ts— session encode/decode + auth guards (see auth doc).src/lib/telegram.ts— all Telegram Bot API calls.- Route handlers under
src/app/api/**/route.ts(Node runtime, use Mongoose).