# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
npm run dev      # Dev server at http://yazd-nobat.localhost:3000 (sets HOST via cross-env)
npm run build    # Production build
npm run start    # Start production server
npm run lint     # ESLint
```

The dev script forces `HOST=yazd-nobat.localhost` so multi-domain detection works locally. To test a different city subdomain, temporarily change `HOST` in the script.

## Environment Variables

```env
NEXT_PUBLIC_API_URL=https://api.clinic-pro.ir   # Backend API base URL
NEXT_PUBLIC_CLIENT_ID=...                        # OAuth client ID
NEXT_PUBLIC_CLIENT_SECRET=...                    # OAuth client secret
DEV_MODE=TRUE                                    # TRUE → blocks all crawlers + noindex
```

`DEV_MODE=TRUE` disables robots indexing and adds `noindex` metadata. Set to `FALSE` in production.

## Architecture Overview

**Next.js 15 App Router** with MUI v5 + Tailwind CSS, RTL (Persian/Farsi), Jalali calendar.

### Multi-Domain System (Core Concept)

Each city has its own domain (e.g. `yazd-nobat.ir`, `tabriz-nobat.ir`). The codebase serves all cities from one deployment, detecting which city to show via subdomain.

- **`data/city.json`** — source of truth: every city's `domain`, `title`, `description`, `keywords`, `site_name`, `socialMedia`, etc.
- **`lib/getStateInfo.js`** — server-side: reads `host` header, extracts subdomain, matches against `city.json`. Use in Server Components and `generateMetadata`.
- **`context/ProvinceProvider.js`** — client-side equivalent using `window.location.hostname`. Exposes `useProvince()` → `{ isProvinceInclude }`.
- **`lib/getCanonicalUrl.js`** — reads `x-pathname` header set by middleware for canonical URL generation.
- **`middleware.js`** — injects `x-pathname` header into every response so `getCanonicalUrl` can read it.

### Page Metadata Pattern

Every page must export `generateMetadata`. Layout-level metadata is the fallback:

```js
// app/layout.js — sets title/description/OG/Twitter from matchedCity
export async function generateMetadata() {
  const { matchedCity } = await getStateInfo();
  // ...
}
```

Page-level `generateMetadata` overrides layout for dynamic pages (doctor, blog, clinic):

```js
export async function generateMetadata({ params }) {
  const { slug } = await params; // Always await params in Next.js 15
  // fetch data, build title/description, return metadata object
}
```

### Data Fetching

**Server Components** use:
- `lib/req.js` → `fetchReq(url)` — axios with SSL verification disabled (needed for dev backend)
- `axios` directly for cases needing more control

**Client Components** use:
- `services/api.js` — axios instance with `baseURL = NEXT_PUBLIC_API_URL`
- `services/response.js` → `request.*` — all API call wrappers. Pass `{ requireAuth: true }` to attach the `access_token` cookie as `Authorization: Bearer`.

### Authentication & Authorization

Auth uses JWT stored in cookies: `access_token`, `refresh_token`, `uuid`, `userInfo`.

- **`lib/auth.js`** → `getUser()` — reads cookies server-side
- **`lib/ability.js`** → `defineAbilitiesFor(user)` — CASL rules. Roles: `"representation"` → Panel access
- Protected pages call `getUser()` + `defineAbilitiesFor()` and redirect if unauthorized

Login flow: POST `/api/v1/user/send-code` (OTP) → POST `oauth/token` → set cookies.

### Styling

- **Tailwind CSS** with `darkMode: "class"` — public pages use `data-theme` attribute, panel uses `class`
- **MUI theme** configured in `mui/index.js` with RTL direction and Vazir font
- **Font**: Vazir only — defined in `app/globals.css` via `@font-face` with `font-display: swap`. No other fonts.
- Custom CSS classes in `globals.css`: `.bg-banner-home`, `.bg-banner-footer`, `.padding-responsive`, etc.
- Dark mode toggled by `next-themes` in `app/Providers.js`: public = `attribute="data-"`, panel = `attribute="class"`

### Routing Structure

```
app/
  layout.js                  # Root layout: metadata, ThemeRegistry, ProvinceProvider
  page.js                    # Home → components/home/
  robots.js                  # Blocks all when DEV_MODE=TRUE
  sitemap.js                 # Fetches doctors/clinics/blogs from API at runtime
  doctor/[slug]/page.js      # generateMetadata + JSON-LD (Physician schema)
  clinic/[slug]/page.js      # generateMetadata + JSON-LD (MedicalClinic schema)
  blog/[slug]/page.js        # generateMetadata + JSON-LD (Article schema)
  doctors/page.js            # generateMetadata using matchedCity
  clinics/page.js            # generateMetadata using matchedCity
  panel/(layout)/            # Route group — requires "representation" role
```

All public pages wrap content in `<Layout name="/path">` from `components/layout/StLayout.js` (header + footer). Panel pages use `components/layoutPanel/`.

### Key Data Files

- `data/city.json` — city configs including domain, SEO fields, social media links
- `data/state.json` — province/state data, joined to city via `province_id`
- `data/specialties.json` — medical specialties; items with `parent` field are sub-specialties shown in FrequentSearches

### Doctor & Clinic Slugs

Both use `uuid` as the URL slug: `/doctor/${doctor.uuid}` and `/clinic/${clinic.uuid}`.

### JSON-LD Structured Data

Added directly in page JSX (not via metadata API):
```jsx
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
```
Present on `/doctor/[slug]` (Physician), `/clinic/[slug]` (MedicalClinic), `/blog/[slug]` (Article).
