Next.js clone of the clinic site with Meshkee-backed articles and portfolio pages. Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
6.4 KiB
Markdown
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.
|