# Project context — dr-mousavi.com clone Next.js rebuild of the ENT clinic site **dr-mousavi.com** (Dr Sayed Shahab Mousavipour). Static marketing pages are a faithful clone of the original theme; articles and portfolio entries come from the Meshkee Website API. Locale is Farsi (`fa`) and the layout is RTL. Keep this file current when the project changes — other AI tools and systems read it first. ## Stack - Next.js 15 (App Router, Turbopack) + React 19 + TypeScript - No UI framework: the original theme's CSS is mirrored verbatim and reused - Dev server: `npm run dev` (this workspace usually runs it on port 3001) ## Routes | Route | Source of content | | --- | --- | | `/` | `src/components/generated/HomeSections.tsx` (mirrored markup) | | `/about-us` | `AboutSections.tsx` (mirrored markup) | | `/contact-us` | `ContactSections.tsx` (mirrored markup) | | `/ourservices` | `ServicesSections.tsx` (mirrored markup) — nav label «تخصص ها» | | `/surgeries` | Meshkee API `portfolios` — nav label «نمونه عمل ها» | | `/surgeries/[slug]` | `portfolios/{slug}` + lightbox gallery + comments | | `/articles` | Meshkee API `blogs` — nav label «مجله سلامت» | | `/articles/[slug]` | `blogs/{slug}` + comments | Header and footer live in `SiteHeader.tsx` / `SiteFooter.tsx` (also generated). The account icon in the header points at the customer portal. ## Cloned theme (static pages) - `public/theme/**` — the original CSS, fonts, icon font (Fontello), images and `aos.js`, mirrored at their original paths so the theme CSS resolves. - `src/components/generated/**` — JSX converted from the original HTML by `tools/clone_page.py`. **Do not hand-edit these files**; they are overwritten. Put fixes in the converter or in `src/app/globals.css`. - `src/app/globals.css` — fonts, theme variables, and the overrides the clone needs (each one carries a comment explaining what it outranks and why). Regenerate everything (downloads source HTML into `tools/.cache`, mirrors any new asset into `public/theme`, rewrites the components): ```bash python3 tools/clone_page.py # all pages python3 tools/clone_page.py services # one page ``` Delete `tools/.cache` to pull fresh markup from the live site. ### Conversion rules worth knowing - Internal links become `next/link`; the account icon becomes `{CUSTOMER_URL}`. - `data-aos` attributes are preserved: `ScrollAnimations.tsx` loads the theme's own `aos.js` and calls `AOS.init()`, exactly like the original's `main.js`. - React style objects cannot express `!important`, so inline `direction: ltr !important` (how the theme alternates image/text sides) becomes the `wb-force-ltr` class defined in `globals.css`. - The theme's `.wb-convert-date` spans keep their timestamp as `data-date`; `SiteBehaviour.tsx` formats them as Jalali dates. - Forms rendered by React components carry `data-react-form` so the theme's generic form handler in `SiteBehaviour.tsx` leaves them alone. ## Meshkee Website API Docs: · OpenAPI: Only public website endpoints are used — never dashboard/CMS routes. Client: `src/lib/meshkee.ts`. | Purpose | Endpoint | | --- | --- | | Tenant (businessId `20`, modules: portfolio, blog, videos, instructions) | `GET /tenants/{domain}` | | Article list | `GET /tenants/{domain}/blogs?page&pageSize&categoryId&title` | | Article detail | `GET /tenants/{domain}/blogs/{slug}` → `{ blog }` | | Portfolio list | `GET /tenants/{domain}/portfolios?page&pageSize&categoryId&title` | | Portfolio detail | `GET /tenants/{domain}/portfolios/{slug}` → `{ portfolio }` | | Farsi category names | `GET /tenants/{domain}/categories?entityType=blog\|portfolio` | | Approved comments | `GET /tenants/{domain}/comments?entityType&entityId` | | Submit comment | `POST /tenants/{domain}/comments` `{ entityType, entityId, authorName, authorEmail, text }` | Notes on the payloads: - Lists return `{ items, total, page, pageSize }`; details are wrapped in a single key (`blog` / `portfolio`). - Portfolios add `titleFa` / `titleEn` and a `gallery` array; the Farsi title is preferred. `gallery` is empty for every current entry, so detail pages fall back to `titleImageUrl`. - Blog and portfolio items expose only the English `categoryName`, hence the separate categories lookup for Farsi labels. - `mainTextHtml` is authored in the business dashboard and rendered with `dangerouslySetInnerHTML`, styled by `.content-body` in `globals.css`. - Requests are cached for 300s (`revalidate`). A failed request logs and returns an empty result so the page shows its empty state instead of erroring. ### Comments and sign-in - Comments are gated: signed-in visitors get the form, everyone else gets a link to the customer portal (`CommentForm.tsx`). - `useCustomerSession.ts` looks for a `meshkee_access_token` cookie (the portal shares the apex domain) or the same key in `localStorage`, then confirms it with `GET /auth/me` before trusting it. - Submitted comments are held for approval in the dashboard, so they do not appear immediately; the UI says so. - The API itself accepts anonymous comments — the sign-in requirement is this site's rule, not the API's. ## Environment Defaults are in `src/lib/meshkee.ts`; override in `.env.local` (see `.env.example`). | Variable | Default | | --- | --- | | `NEXT_PUBLIC_MESHKEE_API_BASE` | `https://api.meshkee.com/api/v1` | | `NEXT_PUBLIC_SITE_DOMAIN` | `dr-mousavi.com` (apex only — no `www`) | | `NEXT_PUBLIC_CUSTOMER_URL` | `https://customer.` | Pointing `NEXT_PUBLIC_SITE_DOMAIN` at another Meshkee tenant is a quick way to test the API-driven pages against a different content set. ## Known gaps - The homepage «مقالات داغ و جذاب» carousel still shows the articles that were hardcoded in the original markup; its cards link to `/articles`. Wiring it to the API needs a slot mechanism in the converter. - Comment item fields (`id`, `authorName`, `text`, `createdAt`) are inferred: no tenant has an approved comment yet to confirm them. - Portfolio galleries are empty upstream, so the lightbox has only ever been exercised with the single title image. - `www.dr-mousavi.com` currently serves a certificate for an unrelated domain and its HTML now belongs to another site, so re-running the converter against the live site will fail until that hosting issue is fixed. The cached HTML in `tools/.cache` is the last good copy.