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

6.4 KiB

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):

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.