Next.js (App Router) + React 19 + Tailwind v4 frontend for the HR Zen Rails API.
| Concern | Choice |
|---|---|
| Framework | Next.js App Router (src/app) |
| UI | shadcn/ui (new-york) + Radix + Tailwind v4 (CSS-first) |
| Forms | react-hook-form + zod |
| State | zustand |
| Icons | lucide-react + @tabler/icons-react |
| Toast | sonner |
| Theme | next-themes (.dark class, dark default) |
app/
(auth)/ # public: sign-in, sign-up (split-screen layout)
(app)/ # authenticated shell: sidebar + header
components/
ui/ # shadcn primitives (vendored verbatim)
*.tsx # app-wide components (sidebar, nav, theme)
constants/ # routes, env, response messages
hooks/ # use-mobile, ...
lib/ # api client, serializers, validations, utils
store/ # zustand stores (auth, sign-up, ui)
types/ # shared, "I"-prefixed types
- Path alias
@/*→src/*. - Import order enforced by
simple-import-sort: React/Next → third-party →@/components/ui→@/constants→@/store→ other@/*→ relative. - One zustand store per domain in
src/store/; read with selectors anduseStore.getState()inside async callbacks. - All API calls go through
src/lib/api.ts(apiRequest), which unwraps the Rails envelope{ success, data, meta }and throwsApiErrorwith fielddetails. - API payloads are snake_case; map to camelCase with
src/lib/serializers.ts. - zod schemas in
src/lib/validations/mirror the Rails validations.
pnpm dev # start dev server (http://localhost:3100)
pnpm build # production build
pnpm lint # eslint --max-warnings 0
pnpm typecheck # tsc --noEmit.env.local (copy .env.example):
NEXT_PUBLIC_API_URL=http://localhost:3001/api/v1
Rails API lives in ../hr_zen_be. The frontend calls:
POST /api/v1/sign_up— create workspacePOST /api/v1/sign_in— returns{ user, organization, token }GET /api/v1/users/me— current user (Bearer token)
- Persist + attach the JWT on authenticated requests (store has
token). - Email verification screen + resend.
- Dashboard widgets, employees, roles/permissions (Auth Matrix) UI.