diff --git a/.claude/settings.local.json b/.claude/settings.local.json index dcce089..865ef85 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -37,7 +37,20 @@ "Bash(node -e \"const n = require\\('c:/Users/Lenovo/Desktop/balinyaar/client/node_modules/notistack'\\); console.log\\(Object.keys\\(n\\)\\)\")", "Bash(Get-ChildItem -Path \"c:\\\\Users\\\\Lenovo\\\\Desktop\\\\balinyaar\\\\client\" -Force)", "Bash(Select-Object Name, PSIsContainer)", - "Bash(npm audit *)" + "Bash(npm audit *)", + "Bash(*)", + "Read(*)", + "Edit(*)", + "Write(*)", + "Glob(*)", + "Grep(*)", + "WebFetch(*)", + "WebSearch(*)", + "Agent(*)", + "NotebookEdit(*)", + "Skill(*)", + "mcp__claude_ai_Mermaid_Chart__validate_and_render_mermaid_diagram", + "Edit(/.claude/skills/frontend-designer/**)" ], "defaultMode": "bypassPermissions" } diff --git a/.claude/skills/frontend-designer/SKILL.md b/.claude/skills/frontend-designer/SKILL.md new file mode 100644 index 0000000..2c02d3a --- /dev/null +++ b/.claude/skills/frontend-designer/SKILL.md @@ -0,0 +1,237 @@ +--- +name: frontend-designer +description: >- + Design and build UI for the Balinyaar client (Next.js 16 + MUI v9). Use when + creating or restyling any screen, page, component, layout, or visual in + client/ — turning a feature, mockup, or Figma design into branded, RTL-aware, + dark-mode-ready, i18n-complete React/MUI code. Covers the brand palette, + design tokens, typography, the App* component library, layout shells, icons, + and the hard rules every Balinyaar UI must follow. +--- + +# Balinyaar Frontend Designer + +Build UI that looks like Balinyaar and behaves correctly in both locales and both +color schemes on the first try. This skill is the design contract; the engineering +contract (providers, fetch, cookies, routing) lives in [client/CLAUDE.md](../../../client/CLAUDE.md) — read it +before touching layout/provider/data code, **don't restate it**, and never violate it. + +**Stack:** Next.js 16 (App Router, Turbopack) · React 19 · MUI v9 (`@mui/material`) · +Emotion (RTL via `stylis-plugin-rtl`) · next-intl v4 · notistack. Everything below +lives under `client/src/`. + +--- + +## 1. Brand identity + +Balinyaar is a **trust-first home-nursing marketplace in Iran**. The visual tone is +calm, warm, clinical-but-human — not a cold medical dashboard. Default audience is +Persian (RTL); English is secondary. + +**Logo mark** (`product/balinyaar.html` seed deck): deep-teal square, lowercase +display glyph in cream, a single terracotta dot. That trio — **teal ground, cream +text, terracotta accent** — is the whole identity. Use terracotta sparingly as the +single accent; teal carries everything else. + +| Role | Light | Dark | +|------|-------|------| +| Primary — deep teal | `#1d4a40` | `#6fc0ac` (lifted, readable on dark) | +| Secondary — terracotta | `#d98c6a` | `#e6a98a` | +| Page surface | `#faf9f5` cream | `#0f1c19` deep teal | +| Paper / card | `#ffffff` | `#16302a` teal surface | +| Text primary | `#1b2521` ink | `#f3efe9` cream | + +--- + +## 2. Design tokens — the two-layer system (read this before styling anything) + +Colors exist in **two mirrored places** that must stay in sync. Pick the right one: + +1. **MUI palette** — `src/theme/colors.ts` (`BRAND`, `LIGHT_PALETTE`, `DARK_PALETTE`). + Drives `--mui-palette-*` and all MUI component coloring. Reach it through MUI APIs: + `color="primary"`, `sx={{ color: 'text.secondary', bgcolor: 'background.paper' }}`. + **This is the default for component styling.** It auto-switches with the color scheme. + +2. **`--bal-*` CSS variables** — `src/theme/tokens.css`, defined under + `[data-mui-color-scheme='light'|'dark']`. The source of truth for **custom CSS + outside MUI's palette** and for **semantic feedback colors MUI doesn't define**: + `--bal-success`, `--bal-error`, `--bal-warning`, `--bal-info` (each `+ -contrast`). + Reference as `var(--bal-primary)`, `var(--bal-success-contrast)`, etc. + +**Rules:** +- Styling a MUI component → use palette keys (`color="primary"`, `sx` palette refs). +- Need success/error/warning/info → use `--bal-*` tokens, **not** MUI defaults — the + MUI palette has no semantic colors, and these tokens are brand-harmonized. +- Need a custom color in raw CSS → add a `--bal-*` token (in **both** scheme blocks), + then `var(--…)`. **Never hard-code a hex** in `sx`, `styled`, or a component. +- Adding/changing a color means editing `tokens.css` **and** `colors.ts` together (the + file headers call out the sync requirement). + +--- + +## 3. Typography & fonts + +- `shape.borderRadius: 10` (set in `src/theme/theme.ts`) — the house corner radius. + Don't override per-component unless deliberate; prefer multiples that read as related. +- Buttons: `textTransform: 'none'`, weight 600 (set globally in `typography.ts`). Never + re-uppercase button text. +- Headings (`h1`–`h6`) use the display font; `h6` is weight 600, the rest 700. +- **Fonts are loaded per-locale in `src/app/[locale]/layout.tsx` only** — Mikhak + (`--font-mikhak`) for `fa`, system stack for `en` (Space Grotesk `--font-space-grotesk` + is declared but not yet wired). **Never load a font in a component or page.** +- Use `` for text — it inherits the correct direction-aware family + (`TYPOGRAPHY_RTL` = Mikhak everywhere for full Persian glyph coverage; `TYPOGRAPHY_LTR`). + Import neither directly in components; let the theme apply them. + +--- + +## 4. The component library — reach for these before raw MUI + +Shared primitives live in `src/components/` (barrel: `@/components`). Prefer the `App*` +wrapper over the bare MUI component — the wrappers carry the house defaults. + +| Component | Use for | Notes | +|-----------|---------|-------| +| `AppButton` | all buttons & button-links | default `variant="contained"`; pass `to`/`href` to render as a link automatically; `startIcon`/`endIcon` accept an **icon name string** (e.g. `startIcon="search"`) or a node; non-MUI `color` strings become text color | +| `AppIconButton` | icon-only actions | takes an icon `name`, `title`, `to`/`onClick` | +| `AppIcon` | any icon | `icon="home"` by registered name (§6); `size`, `color` props | +| `AppLink` | internal/external links | locale-aware Next navigation; default underline `hover` | +| `AppAlert` | inline alerts | default `severity="error"`, `variant="filled"` | +| `AppImage` | images | wrapper around next/image conventions | +| `AppLoading` | loading state | default circular, `primary`, `3rem` | +| `ErrorBoundary` | wrapping fault-prone subtrees | already wraps page content in the shell | +| `UserInfo` | user avatar/identity block | feature component | + +Defaults for these live in `src/components/config.ts` (`APP_BUTTON_VARIANT`, +`APP_ICON_SIZE = 24`, `CONTENT_MAX_WIDTH = 800`, `CONTENT_MIN_WIDTH = 320`, alert/link/ +loading defaults). Change a default there, not per-call-site. + +For layout/spacing use MUI primitives directly: `Box`, `Stack`, `Container`, `Grid`, +`Paper`, `Card`. Use the `spacing`/`sx` system (theme spacing unit = 8px) — never inline +pixel margins for rhythm. + +**New shared component?** Put it in `src/components//.tsx` with an +`index.tsx` barrel, follow the `App*` prop-spreading + JSDoc style of `AppButton.tsx`, +and add a co-located `.test.tsx` (mandatory for anything imported in >1 place — see +CLAUDE.md "Unit Testing"; wrap with ``, never mock MUI). + +--- + +## 5. Layout & page shells + +- **Private (authenticated) screens** render inside `PrivateLayout` → + `TopBarAndSideBarLayout` (`src/layout/`): a `TopBar` + a `SideBar` (variant + `sidebarPersistentOnDesktop`: persistent ≥desktop, temporary drawer on mobile) + + a dark-mode toggle. Sidebar nav items are `{ title, path, icon }` arrays built with + `useTranslations('nav')`. Page content is auto-wrapped in `ErrorBoundary`. +- **Public screens** use `PublicLayout`. +- Shell dimensions are constants in `src/layout/config.ts` (`SIDE_BAR_WIDTH = 240px`, + top-bar `56px` mobile / `64px` desktop, anchors). Respect them; don't hard-code. +- A page is `src/app/[locale]/(private|public-routes)/…/page.tsx`. Keep page bodies to + composition + content; push reusable visuals into `src/components/`. +- Constrain reading width with `CONTENT_MAX_WIDTH` (800) for text-heavy views; full-bleed + is fine for dashboards/tables. +- Use `useIsMobile()` (`@/hooks`) for responsive branching, or MUI breakpoints in `sx`. + +--- + +## 6. Icons + +Icons are a **name registry**, not free imports. `src/components/common/AppIcon/config.ts` +maps lowercase names → MUI/SVG components. Render with `` or pass +the name to `AppButton`/`AppIconButton` (`icon="search"`). + +Currently registered: `default, logo, close, menu, settings, visibilityon, +visibilityoff, daynight, night, day, search, info, home, account, signup, login, +logout, notifications, error`. + +**Need a new icon:** import it into `config.ts`, add a **lowercase** key to `ICONS`, then +reference by that name. Custom SVGs go in `AppIcon/icons/`. An unregistered name logs a +warning and falls back to `default` — never pass a raw MUI icon where a name is expected. + +--- + +## 7. Non-negotiable rules for every Balinyaar UI + +Every screen/component you produce must satisfy **all** of these: + +1. **i18n — no hard-coded user-facing strings.** Add the key to **both** + `messages/en.json` and `messages/fa.json` (keep them in sync; top-level keys are + namespaces). Client: `useTranslations(ns)`. Server: `getTranslations(ns)`. +2. **RTL-safe.** `fa` is the default locale and is **RTL**. Never use directional + hard-coding (`marginLeft`, `left:`, `textAlign: 'left'`) for layout flow — use + logical/MUI-flipped props (`ml`→ MUI flips; prefer `marginInlineStart`, `start`/`end`, + `sx` shorthand that the RTL Emotion cache mirrors). Test the layout visually at `/fa`. + Do **not** pass `flexWrap`/`useFlexGap` as `Stack` props — use `sx={{ flexWrap: 'wrap' }}`. +3. **Dark-mode correct.** Pull every color from the palette or `--bal-*` tokens so it + switches automatically. Verify on both schemes — never assume a light background. +4. **Tokens, not hexes.** No raw color literals in `sx`/`styled`/components (§2). +5. **Constants, not magic values.** Cookie names, routes, repeated dimensions, event + names → named constants (CLAUDE.md "Constants"). +6. **Use the wrappers** (§4) and the **icon registry** (§6) before bare MUI. +7. **Shared component ⇒ co-located test** (§4). +8. **MUI v9 API only.** No v5/v6-era props (e.g. `Stack` `useFlexGap`, `storageWindow`). + Avoid deprecated APIs that throw. + +--- + +## 8. Workflow: turning a design or feature into a screen + +1. **Locate & scope.** Decide private vs public route; confirm the layout shell. Identify + which existing `App*` components and tokens already cover the design. +2. **Tokens first.** If the design needs a color/spacing not in the system, add the + `--bal-*` token (both schemes) + mirror in `colors.ts` if it's palette-level. +3. **Compose with primitives.** Build with `Box`/`Stack`/`Grid`/`Paper` + `App*` + wrappers. Keep raw MUI to layout/structural components. +4. **Wire copy through i18n.** Every label/placeholder/aria string → both message files. +5. **Verify the four axes:** `/fa` (RTL) and `/en` (LTR) × light and dark. The default + route is `/fa` — start there. +6. **Tests** for any new shared component; **never** add a layout above `[locale]` + (breaks locale/dir — see CLAUDE.md). +7. Data/fetch/auth/cookies/toasts → follow CLAUDE.md (`serverFetch`/`clientFetch`, + `@/lib/cookies/*`, `dispatchToast`/`useSnackbar`). Don't reinvent these. + +--- + +## 9. Anti-patterns (design-specific — CLAUDE.md has the full engineering list) + +- Hard-coded hex/rgb in components → use palette keys or `--bal-*` tokens. +- MUI default success/error colors for feedback → use `--bal-*` semantic tokens. +- `marginLeft`/`left`/`textAlign:'left'` for flow → breaks RTL; use logical props. +- Hard-coded English (or any) UI string → add to both message files. +- Loading a font in a component/page → fonts live only in `src/app/[locale]/layout.tsx`. +- `createTheme()` in a component → use `APP_THEME_LTR`/`APP_THEME_RTL`. +- Raw MUI icon where a registry name is expected → register it in `AppIcon/config.ts`. +- New shared component without a `.test.tsx`, or mocking MUI in tests. +- Re-introducing `src/app/layout.tsx` / any layout above `[locale]`. + +--- + +## 10. Design ↔ Figma (optional) + +A Figma MCP is connected. When the user provides a figma.com URL or asks to implement a +Figma frame, use the Figma tools (`get_design_context`, `get_screenshot`, `get_metadata`) +to pull the design, then map it onto **this** system: translate Figma colors to the +nearest `--bal-*`/palette token (don't introduce new hexes unless the design truly adds a +brand color), Figma type to `` variants, and Figma components to the `App*` +library. Follow the Figma plugin skills (`/figma-use`, `/figma-generate-design`) when +pushing code back into Figma. + +--- + +## Key files + +| Concern | File | +|---------|------| +| Brand color tokens (CSS) | `client/src/theme/tokens.css` | +| MUI palette (mirror) | `client/src/theme/colors.ts` | +| Typography & fonts | `client/src/theme/typography.ts` | +| Theme factory (shape, schemes, dir) | `client/src/theme/theme.ts` | +| Theme provider / RTL cache | `client/src/theme/ThemeProvider.tsx` | +| Component library | `client/src/components/` (`@/components`) | +| Component defaults | `client/src/components/config.ts` | +| Icon registry | `client/src/components/common/AppIcon/config.ts` | +| Layout shells | `client/src/layout/` | +| Layout dimensions | `client/src/layout/config.ts` | +| Messages (i18n) | `client/messages/{en,fa}.json` | +| Engineering contract | `client/CLAUDE.md` | diff --git a/AGENTS.md b/AGENTS.md index 9090a77..4c62336 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,40 +1,10 @@ -# AGENTS.md — Balinyaar +# AGENTS.md -Guidance for AI coding agents working in this repository. Read this first, then the project-specific `AGENTS.md` for whichever side you are editing. +The canonical guidance for AI coding agents in this repository lives in **[CLAUDE.md](CLAUDE.md)** +(same folder). Read that first, then the `CLAUDE.md` inside whichever project you are editing: -## What this repository is +- Frontend → [client/CLAUDE.md](client/CLAUDE.md) +- Backend → [server/CLAUDE.md](server/CLAUDE.md) -Balinyaar is a full-stack application split into two independent projects: - -| Path | Project | Stack | Detail | -| -------------------------- | -------------- | ----------------------------------------------------------- | ------------------------------- | -| [`client/`](client/) | Web frontend | Next.js (App Router), React, TypeScript, Material UI (MUI) | [client/AGENTS.md](client/AGENTS.md) | -| [`server/`](server/) | Backend API | ASP.NET Core (.NET 10), Clean Architecture, CQRS, EF Core | [server/AGENTS.md](server/AGENTS.md) | - -The two communicate over HTTP/JSON (and optionally gRPC). The client reads the API base URL from `NEXT_PUBLIC_API_URL`; the server runs on `https://localhost:5002` by default. - -There is **no root-level build** — each project is built and run on its own. `client/` and `server/` are not part of the same package/solution. - -## Working agreements - -- **Stay within one project per change** unless the task explicitly spans both. A frontend change rarely needs server files and vice-versa. -- **Match the surrounding style.** Each project has its own conventions (see its `AGENTS.md`). Mirror the existing patterns rather than introducing new ones. -- **Run the project's own checks** before declaring work done: - - client: `npm run lint` and `npm run type` (and `npm run test:ci` if tests are touched) - - server: `dotnet build` and `dotnet test` -- **Do not reintroduce template/starter scaffolding.** This repo was derived from two open-source starters; their branding, demo/showcase pages, NuGet template packaging, and `_TITLE_`/`_DESCRIPTION_` placeholders have been intentionally removed. Don't add them back. -- **Secrets**: never commit real connection strings, keys, or tokens. Use `.env` (client) and `appsettings.*.json` / user-secrets (server). - -## Quick start - -```bash -# Frontend -cd client && npm install && npm run dev # http://localhost:3000 - -# Backend -cd server && dotnet run --project src/API/Baya.Web.Api/Baya.Web.Api.csproj # https://localhost:5002/swagger -``` - -## Naming note - -The server's C# namespaces, projects, and solution file all use the `Baya*` prefix (e.g. `Baya.Web.Api`, `Baya.sln`). Keep new server code under the same `Baya.*` namespace convention. +`CLAUDE.md` is the single source of truth at every level of this repo; these `AGENTS.md` files are +just pointers so the convention is discoverable under either name. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7187b50 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,92 @@ +# Balinyaar — Repository Guide (root) + +This is the **shared, repo-wide** guide for AI coding agents. It is intentionally short. +Everything specific to one side of the stack lives in that project's own `CLAUDE.md`. + +> **Read the guide for the side you are editing — and only that one.** +> Working in `client/`? Read [client/CLAUDE.md](client/CLAUDE.md). +> Working in `server/`? Read [server/CLAUDE.md](server/CLAUDE.md) (+ [server/CONVENTIONS.md](server/CONVENTIONS.md)). +> You almost never need both. A frontend change does not touch server files, and vice-versa. + +> `AGENTS.md` files in this repo are thin pointers to the `CLAUDE.md` in the same folder. +> `CLAUDE.md` is the single source of truth at every level. + +--- + +## What Balinyaar is + +Balinyaar is a **trust-first home-nursing marketplace in Iran**. Independent nurses (and +nursing-company employees) list configurable services; families search, book, pay, and review. +The platform holds funds in an escrow-style ledger and pays nurses out weekly after a confirmed +check-out. + +Product/domain knowledge — business rules, the database model, payments/BNPL, escrow, the +verification pipeline — is **not** in the code. It lives in [`product/`](product/): + +| Doc | What it covers | +| --- | --- | +| [product/business-requirements.md](product/business-requirements.md) | Full functional/business requirements | +| [product/database-model.md](product/database-model.md) | ~50-table SQL Server schema + ER diagrams + rationale | +| [product/payments-and-installments.md](product/payments-and-installments.md) | BNPL, escrow ledger, settlement, VAT (with sources) | +| [product/Home-Nursing-Platform-Research*.md](product/) | Market research (EN + FA) | + +**Read the relevant `product/` doc before designing any schema, API, or feature.** Don't infer +business rules from code — the code is young and the docs are the source of truth. + +--- + +## Repository layout + +This is **two independent projects in one repo**. There is no root-level build, package, or +solution — each project is built, linted, and run on its own. + +| Path | Project | Stack | Guide | +| --- | --- | --- | --- | +| [`client/`](client/) | Web frontend | Next.js 16 (App Router) · React 19 · TypeScript · MUI v9 · next-intl | [client/CLAUDE.md](client/CLAUDE.md) | +| [`server/`](server/) | Backend API | ASP.NET Core (.NET 10) · Clean Architecture · CQRS · EF Core | [server/CLAUDE.md](server/CLAUDE.md) | +| [`product/`](product/) | Product docs | Markdown | — (see table above) | + +The two communicate over **HTTP/JSON** (optionally gRPC). The client reads the API base URL from +`NEXT_PUBLIC_API_URL`; the server listens on `https://localhost:5002` by default. + +--- + +## Working agreements (apply to both projects) + +1. **Stay within one project per change** unless the task explicitly spans both. +2. **Match the surrounding style.** Mirror existing patterns; don't introduce new ones. Each + project documents its conventions in its own `CLAUDE.md`. +3. **Run that project's own checks before declaring work done:** + - client: `npm run check` (type + lint), plus `npm run test:ci` if you touched a tested component. + - server: `dotnet build Baya.sln` and `dotnet test Baya.sln`. +4. **Read the product docs before changing behavior.** Business rules are decisions, not guesses. +5. **Don't reintroduce template/starter scaffolding.** Both projects were derived from open-source + starters; their branding, demo/showcase pages, and `_TITLE_`/`_DESCRIPTION_` placeholders were + intentionally removed. Don't add them back. +6. **Never commit secrets.** Use `.env` (client) and `appsettings.*.json` / user-secrets (server). + Real connection strings, keys, and tokens never enter git. +7. **Keep docs honest.** If you change how something works, update the `CLAUDE.md` that describes it + in the same change. Stale instructions are worse than none. + +--- + +## Naming + +- The **server**'s C# namespaces, projects, and solution all use the `Baya*` prefix + (`Baya.Web.Api`, `Baya.sln`). Keep new server code under the `Baya.*` convention. +- The **client** package is `balinyaar-client`; the `@/*` import alias maps to `client/src/*`. + +The product/brand name is **Balinyaar**; the server's `Baya*` prefix is a legacy code namespace — +do not rename it without explicit instruction. + +--- + +## Quick start + +```bash +# Frontend +cd client && npm install && npm run dev # http://localhost:3000 + +# Backend +cd server && dotnet run --project src/API/Baya.Web.Api/Baya.Web.Api.csproj # https://localhost:5002/swagger +``` diff --git a/client/.eslintrc.json b/client/.eslintrc.json deleted file mode 100644 index 75c45fb..0000000 --- a/client/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "next/core-web-vitals", - "rules": { - "import/no-cycle": "error" - } -} diff --git a/client/AGENTS.md b/client/AGENTS.md index c2cfe7f..efa8c38 100644 --- a/client/AGENTS.md +++ b/client/AGENTS.md @@ -1,66 +1,12 @@ # AGENTS.md — Balinyaar Web Client -Agent-oriented guide to the frontend. For human setup/run instructions see [README.md](README.md). +The canonical agent guide for the frontend is **[CLAUDE.md](CLAUDE.md)** (same folder). It is the +engineering contract: stack, commands, lint/type gates, routing, providers, data fetching, theming, +i18n, cookies, and the rules every change must follow. -## Stack +- Repo-wide context → [../CLAUDE.md](../CLAUDE.md) +- Human setup/run instructions → [README.md](README.md) +- UI/design work → the **frontend-designer** skill -- **Next.js** with the **App Router** (`src/app/`), statically exported (`output: 'export'` in `next.config.mjs` → builds to `out/`) -- **React** + **TypeScript** (`strict` mode) -- **Material UI (MUI)** for components and theming (Emotion under the hood) -- **Jest** + **Testing Library** for unit tests -- ESLint (`eslint-config-next`) + Prettier - -## Commands - -| Task | Command | -| ------------ | ------------------ | -| Dev server | `npm run dev` | -| Build | `npm run build` | -| Lint | `npm run lint` | -| Type-check | `npm run type` | -| Format | `npm run format` | -| Test (watch) | `npm test` | -| Test (CI) | `npm run test:ci` | - -Always run `npm run lint` and `npm run type` after a change. Run `npm run test:ci` when you touch a component that has a `*.test.tsx`. - -## Directory map - -``` -src/ -├── app/ Routes (App Router). Each folder = a route; page.tsx = the page. -│ ├── layout.tsx Root layout: store + theme providers, global -│ ├── page.tsx "/" entry (delegates to home) -│ ├── home/ about/ Content pages -│ ├── auth/login|signup Auth pages (LoginForm is currently a stub) -│ └── me/ Authenticated user page -├── components/ -│ ├── common/ Reusable App* primitives (see below) + ErrorBoundary -│ └── UserInfo/ Logged-in user summary -├── hooks/ useIsAuthenticated, useIsMobile, event hooks, useWindowSize -├── layout/ PublicLayout / PrivateLayout + TopBar, SideBar, BottomBar, config -├── store/ Global state: React context + reducer (AppStore, AppReducer) -├── theme/ ThemeProvider, light/dark palettes, colors, MUI-for-Next bridge -└── utils/ storage (local/session), navigation, environment, text, types -``` - -## Conventions (follow these) - -- **Imports**: use the `@/*` alias for anything under `src/` (e.g. `import { AppButton } from '@/components'`). The alias is defined in `tsconfig.json`. -- **Barrel files**: most folders export through an `index.ts(x)`. Add new public exports there (e.g. a new common component is re-exported from `src/components/common/index.tsx`, which `src/components/index.tsx` re-exports). -- **Common components** live in `src/components/common//` as a folder containing `.tsx`, `index.tsx` (re-export), and an optional `.test.tsx`. Existing ones: `AppAlert`, `AppButton`, `AppIcon`, `AppIconButton`, `AppImage`, `AppLink`, `AppLoading`. Prefer reusing these over raw MUI where one exists. -- **Icons**: reference icons by string name through `AppIcon`. The name→icon map is in `src/components/common/AppIcon/config.ts`; add new icons there (custom SVGs go in `AppIcon/icons/`). -- **Navigation**: sidebar/bottom-bar items are arrays of `LinkToPage` defined inside `PublicLayout.tsx` / `PrivateLayout.tsx`. Add a route → add an entry there to surface it in the nav. -- **Two layouts**: `PrivateLayout` (after auth) and `PublicLayout` (before auth); `CurrentLayout` picks between them based on auth state. The app name lives in the `TITLE_PRIVATE` / `TITLE_PUBLIC` constants in those files. -- **Auth is a stub.** `src/hooks/auth.ts` and `src/app/auth/login/LoginForm.tsx` fake login by writing a placeholder token to session storage (`// TODO: AUTH:`). When implementing real auth, replace those spots and point requests at `NEXT_PUBLIC_API_URL` (the `server` project's JWT endpoints). -- **Theming**: use the theme/palette via MUI's `sx` / `useTheme`; don't hardcode colors. Light/dark values are in `src/theme/light.ts` and `dark.ts`; dark-mode toggle flows through the store. -- **Client vs server components**: files needing hooks/browser APIs start with `'use client';` (see `LoginForm.tsx`). Pages that only render markup can stay server components. - -## Environment - -Browser-exposed config comes from `NEXT_PUBLIC_*` variables (copy `.env.sample` → `.env`). The ones actually read in code: `NEXT_PUBLIC_ENV`, `NEXT_PUBLIC_DEBUG`, `NEXT_PUBLIC_PUBLIC_URL` (see `src/config.ts`) and `NEXT_PUBLIC_VERSION` (see `src/utils/environment.ts`). `NEXT_PUBLIC_API_URL` is the backend base URL. - -## Gotchas - -- Static export (`output: 'export'`) means **no server-side runtime** — no API routes, no SSR-only features, `images.unoptimized` is on. -- Don't reintroduce the removed demo/showcase route (`src/app/dev`) or `Demo*` components — they were template content. +`CLAUDE.md` is the single source of truth; this file is just a pointer so the convention is +discoverable under the `AGENTS.md` name too. diff --git a/client/CLAUDE.md b/client/CLAUDE.md index 170264e..6f7e98d 100644 --- a/client/CLAUDE.md +++ b/client/CLAUDE.md @@ -1,5 +1,92 @@ # Balinyaar Client — Claude Code Guidelines +The web frontend of **Balinyaar**, a trust-first home-nursing marketplace in Iran. This file is the +**engineering contract** for everything under `client/`: providers, routing, data fetching, theming, +i18n, cookies, and the rules every change must follow. + +- Repo-wide context and the backend → root [CLAUDE.md](../CLAUDE.md). +- Product/domain rules (what to build) → [`product/`](../product/) — read the relevant doc before + designing a feature; don't infer business rules from code. +- Visual/design work (brand palette, tokens, component look-and-feel) → the **frontend-designer** + skill. It is the *design* contract and defers to this file for *engineering* rules. Don't restate + this file there. + +## Stack + +- **Next.js 16** — App Router, Turbopack, React Server Components. **Not a static export** — the app + relies on server components, middleware, and server-side cookies. (`next.config.mjs` only wires the + next-intl plugin + `reactStrictMode`.) +- **React 19** + **TypeScript** (`strict`). +- **MUI v9** (`@mui/material`) for components and theming; **Emotion** underneath (RTL via + `stylis-plugin-rtl`). +- **next-intl v4** for i18n — locales `fa` (default, RTL) and `en`. +- **TanStack Query v5** for server state; a small **AppStore** (React context + reducer, `src/store/`) + for client state. +- **notistack** for toasts; **js-cookie** (wrapped) for client cookies. +- **Jest** + **Testing Library** for unit tests. +- Quality gates: **tsc**, **ESLint 9** (flat config), **Prettier**. + +## Commands + +| Task | Command | +| --- | --- | +| Dev server | `npm run dev` | +| Production build | `npm run build` | +| Type-check | `npm run type` | +| Lint | `npm run lint` | +| Lint + autofix | `npm run lint:fix` | +| **Type + lint (the gate)** | `npm run check` | +| Format (Prettier) | `npm run format` | +| Test (watch) | `npm test` | +| Test (CI, once) | `npm run test:ci` | + +**Always run `npm run check` before declaring work done.** Run `npm run test:ci` as well when you +touch a component that has a co-located `*.test.tsx`. + +## Quality gates: lint & type (how they work) + +Both gates are plain CLI tools. **There is no `next lint`** — it was removed in Next 16; calling it +silently does nothing. + +- `npm run type` → `tsc --noEmit`. Config in `tsconfig.json`: `strict` on, `noEmit`, `@/*` → `src/*`. +- `npm run lint` → `eslint .` driven by **flat config** in `eslint.config.mjs`. That config spreads + `eslint-config-next` (core-web-vitals + typescript + react + react-hooks + jsx-a11y + import) and + applies `eslint-config-prettier` last so ESLint never fights Prettier on formatting. +- `npm run check` runs type then lint. Keep it green. + +Rules for this project: +- **This project is flat-config only.** Do not add `.eslintrc*` files — put any rule changes in + `eslint.config.mjs`. +- **ESLint owns correctness, Prettier owns formatting.** Don't add stylistic ESLint rules. +- **Prefer fixing code over silencing the linter.** When a disable is genuinely correct — e.g. a + deliberate browser-only read after mount that trips `react-hooks/set-state-in-effect` — use a + scoped `// eslint-disable-next-line ` with a one-line reason, never a file-wide disable. +- **Pin to ESLint 9.** ESLint 10 currently crashes with this Next 16 toolchain + (`scopeManager.addGlobals is not a function`). `import/no-cycle` is also disabled — its TS resolver + has an interface mismatch here (see the note in `eslint.config.mjs`). + +## Golden rules (the short list) + +A change is "done" only if it respects all of these — each has a full section below. + +1. **Never add a layout above `[locale]`.** `src/app/[locale]/layout.tsx` is the root layout (it + renders ``/``). A layout above it freezes `lang`/`dir`/messages on the default locale. +2. **Respect the server/client boundary.** Never import `next/headers`, `next-intl/server`, or + `@/lib/cookies/server` from a client component; never import `@/lib/cookies/client` from an RSC. +3. **No hard-coded UI strings.** Every user-visible string is a key in **both** `messages/en.json` + and `messages/fa.json`. +4. **Fetch only through `clientFetch`/`serverFetch`** (`@/lib/api`) — never raw `fetch()`. Domain + calls live in `src/services/{domain}/apis/`. +5. **Cookies only through the cookie manager** (`@/lib/cookies/*`) — never `document.cookie`, + `js-cookie`, `localStorage`, or `sessionStorage` for app/auth state. +6. **Colors come from `tokens.css`** (`var(--…)`), never hard-coded in `sx`. Use the pre-built + `APP_THEME_LTR`/`APP_THEME_RTL`; never call `createTheme()` in a component. +7. **MUI v9 API only.** Use `sx={{ mb: 4 }}`, not `mb={4}` as a direct prop. No MUI-v5/v6-only props + (`useFlexGap`, `flexWrap` on `Stack`, `storageWindow`, `InitColorSchemeScript`, …). +8. **Shared components get a co-located `*.test.tsx`.** (A component imported from >1 place.) +9. **Magic strings become named constants** (`src/constants/` or a co-located `constants.ts`). +10. **`npm run check` is green** and translations stay in sync before you finish. + ## Project Structure ``` @@ -11,11 +98,10 @@ client/ ├── next.config.mjs # createNextIntlPlugin wires i18n into Next.js └── src/ ├── app/ - │ ├── layout.tsx # Root RSC: reads locale + cookie → sets HTML attrs │ ├── globals.css │ ├── fonts/ # Local font files (woff2) — Mikhak for fa │ └── [locale]/ - │ ├── layout.tsx # RSC: setRequestLocale + NextIntlClientProvider + ThemeProvider + AppStoreProvider + │ ├── layout.tsx # ROOT RSC: renders + fonts + setRequestLocale + NextIntlClientProvider + ThemeProvider + AppStoreProvider │ ├── (private-routes)/ │ │ ├── layout.tsx # 'use client' — wraps PrivateLayout │ │ └── page.tsx @@ -62,14 +148,14 @@ client/ │ └── use{Action}.ts # One hook per file — useQuery or useMutation ├── store/ # AppStore (Redux-like client state) ├── theme/ - │ ├── ColorSchemeScript.tsx # Inline