Files
dr-shahab-mousavi/CONTEXT.md
T
alireza-hassaniandCursor 56c4df0d18 Initial commit of the Dr Shahab Mousavi website.
Next.js clone of the clinic site with Meshkee-backed articles and portfolio pages.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 12:09:31 +03:30

136 lines
6.4 KiB
Markdown

# 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: <https://api.meshkee.com/docs/website> · OpenAPI:
<https://api.meshkee.com/docs/website/openapi.json>
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.<site domain>` |
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.