Files

165 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Kaman Cable — AI / Project Context
## Source of truth
- Live reference: https://www.kamancable.ir/
- Backend / Meshkee Website API: https://api.meshkee.com/docs/website
- Domain: `kamancable.ir` (`NEXT_PUBLIC_WEBSITE_DOMAIN`)
- Tenant business id: `24` (`GET /tenants/kamancable.ir`)
- Customer portal: `https://customer.kamancable.ir`
- Business dashboard: `https://business.kamancable.ir`
- Catalog and blog content come from Meshkee (`enabledModules`: products, blog). Business-info / logos in the API may be empty — brand copy and some homepage photos still live in `src/data/site.ts` and `public/images/`.
## Stack
- Next.js 15 (App Router) + React 19 + TypeScript
- Tailwind CSS v4 (utility import) + custom CSS in `src/app/globals.css`
- Language: `fa` (default, RTL), `en` (LTR), `ar` (RTL)
- Static copy lives in `src/i18n/fa.ts`, `src/i18n/en.ts`, `src/i18n/ar.ts`
- Locale cookie: `kaman-locale` (also mirrored in localStorage)
- Products and blog are **FA-only** for now (hidden in EN/AR nav, homepage, and redirected away)
- Fonts:
- `iran-yekan` from Meshkee CDN (Persian UI / body)
- `Rajdhani` via `next/font/google` (English UI, class `.en`)
- `Oswald` via `next/font/google` (`--font-en-display` for section titles / phone)
## Color palette
- Dark navy: `#1C1E40`
- White: `#FFFFFF`
- Gold accent: `#E1A34C`
- Highlight yellow: `#FFDE30`
- Muted text: `#C8CCE0`
## Pages
| Route | Purpose |
|-------|---------|
| `/` | Homepage (hero, app CTA, about teaser, contact bar, category cards, blog carousel, clients) |
| `/products` | Product catalog from `GET /tenants/{domain}/products` |
| `/products/{id}/{nameFaSlug}` | Product detail (`GET .../products/by-id/{id}`) |
| `/products/category/{id}/{nameFaSlug}` | Category (`GET .../categories/by-id/{id}`, then products with `categoryId`) |
| `/blog` | Blog index from `GET /tenants/{domain}/blogs` |
| `/blog/{id}/{titleSlug}` | Blog detail (`GET .../blogs/by-id/{id}`) |
| `/about` | درباره ما |
| `/contact` | تماس با ما |
Header (RTL for FA/AR, LTR for EN): logo + menu on the inline-start; search, language (FA/EN/AR), and account on the inline-end.
- Signed out: icon links to `https://customer.kamancable.ir`
- Signed in: `{firstName} عزیز` dropdown — پروفایل من, خروج, and مدیریت وبسایت if the user is a website admin
- Session: shared cookies `meshkee_customer_access_token` / refresh, hydrated into localStorage; `/api/auth/me` proxies `GET /auth/me`
Homepage product cards and blog carousel are FA-only. EN and AR hide products/blog (nav, homepage, and `/products` `/blog` redirect home). The full catalog remains `/products` in FA.
Homepage blog carousel prefers Meshkee blogs, with `src/data/site.ts` as fallback.
## Meshkee API integration
Base: `{NEXT_PUBLIC_MESHKEE_API_BASE}/tenants/{domain}/...`
Helpers: `src/lib/meshkee.ts` → `meshkeeGet`, `meshkeeGetOrNull`, `tenantPath`, `getTenant`, `getBusinessId`
### Products
- List: `GET /tenants/{domain}/products?page&pageSize&categoryId`
- Detail: `GET /tenants/{domain}/products/by-id/{id}` → `{ product, relatedProducts }`
- Category: `GET /tenants/{domain}/categories?entityType=product` and `GET .../categories/by-id/{id}`
- Specs: `GET /tenants/{domain}/products/by-id/{id}/technical-info` — join `form.fields[].id` ↔ `values[].fieldId`. Do not invent field labels from the detail payload.
- Helpers: `src/lib/products.ts`, `src/lib/categories.ts`, `src/lib/technical.ts`
- Detail UI: lightbox gallery, hover zoom, tabs (توضیحات / مشخصات فنی / نظرات), similar products row
- Public slugs are built from `nameFa` / title (Farsi kept, spaces → `-`). Never use the legacy DB `slug` in the URL.
### Blog
- List: `GET /tenants/{domain}/blogs?page&pageSize`
- Detail: `GET /tenants/{domain}/blogs/by-id/{id}` → `{ blog }`
- Helpers: `src/lib/blog.ts`
- Empty API → local posts in `src/data/site.ts`
### Comments
- List: `GET /tenants/{domain}/comments?entityType=product|blog&entityId=`
- Submit: website proxy `POST /api/comments` → Meshkee comments
- UI: authenticated users get a form; guests see a login box to the customer portal (`src/components/CommentBox.tsx`)
### Contact
- Public POST: `/tenants/kamancable.ir/contact-submissions`
- Website proxy: `POST /api/contact`
- Body: `{ title, name, text, email?, cellNumber? }`
- Homepage callback strip also posts through this route
### Auth proxies
- `GET /api/auth/me` → `GET /auth/me`
- `POST /api/auth/handoff` → `POST /auth/handoff` (admin dashboard)
## Static image slots
API: `GET /tenants/{domain}/website/static-images?pageKey=home|about|contact|products|blog`
Docs: https://api.meshkee.com/docs/website (not http://meshkee.com/docs/website)
Catalog: `GET /meshkee/static-image-slots` → `STATIC_IMAGE_SLOTS` in `src/lib/static-images.ts`
Helpers: `fetchStaticImageSlots`, `pickSlot` (aliases for renamed keys), `overlaySlotImages`, `slotRatioStyle`
Homepage placeholders (match Meshkee dashboard keys exactly):
| key | kind | itemCount | aspectRatio | recommendedWidth | section |
| --- | --- | --- | --- | --- | --- |
| `slider` | list (duplicatable) | null | `1920:830` | 1440 | Homepage hero (72vh cover crop) |
| `about-us-image` | single | 1 | `440:609` | 480 | Homepage about photo |
| `categories` | list (duplicatable) | null | `3:4` | 480 | Homepage product cards (`.product-card .thumb`) |
| `partners` | list (duplicatable) | null | `3:2` | 280 | Homepage partner logos |
| `home-blog` | list | 6 | `16:10` | 720 | Homepage blog carousel |
| `about-hero` | single | 1 | `1920:640` | 1440 | About hero |
| `about-gallery` | list | 6 | `4:3` | 720 | About gallery |
| `contact-hero` | single | 1 | `1920:480` | 1440 | Contact hero |
| `products-hero` | single | 1 | `1920:480` | 1440 | Products page hero |
| `blog-hero` | single | 1 | `1920:480` | 1440 | Blog page hero |
Legacy aliases (still resolved by `pickSlot` if the API has not been re-synced): `home-about-image` → `about-us-image`, `home-products` → `categories`, `home-clients` → `partners`.
Rules:
- Use `slot.images` (all if `kind=list`, first if `kind=single`)
- `url` → image src; `linkUrl` → wrap in `SlotLink`
- Empty / fail → keep local fallbacks from `src/data/site.ts` / `public/images/`
- Apply `slot.aspectRatio` as CSS `--slot-ratio` (`slotRatioStyle`); do not invent upload APIs
- **If a slot’s on-page size or aspect ratio changes in CSS, update `STATIC_IMAGE_SLOTS` in the same change** (Meshkee’s static-image reader uses this catalog)
## Content & assets
- Site copy, phones, address, category blurbs: `src/data/site.ts`
- Logo: `public/images/logo/logo.png`
- Hero banners: `public/images/slider/bg1.jpg` … `bg3.jpg`
- Favicon: `/favicon.png`
- Contact: `021-242624` · دفتر تهران، خیابان کریم‌خان زند · کارخانه شهر صنعتی شماره یک زنجان
- App: https://kamancable.com/applanding/
## Important paths
- Layout / shell: `src/app/layout.tsx`, `src/components/Header.tsx`, `src/components/AccountMenu.tsx`, `src/components/Footer.tsx`
- Home: `src/app/page.tsx`, `src/components/HeroSlider.tsx`, `src/components/ContactBar.tsx`
- i18n: `src/i18n/config.ts`, `src/i18n/fa.ts`, `src/i18n/en.ts`, `src/i18n/ar.ts`, `src/i18n/locale-context.tsx`
- Products: `src/app/products/page.tsx`, `src/app/products/[id]/[slug]/page.tsx`
- Blog: `src/app/blog/page.tsx`, `src/app/blog/[id]/[slug]/page.tsx`
- Config: `src/lib/config.ts`
## Env
```bash
NEXT_PUBLIC_WEBSITE_DOMAIN=kamancable.ir
NEXT_PUBLIC_MESHKEE_API_BASE=https://api.meshkee.com/api/v1
```
## Commands
```bash
npm install
npm run dev
npm run build
```
## Sitemap + robots
- **Do not** add `public/sitemap.xml` or `public/robots.txt` (nginx proxies both to the Meshkee API)
- Live sitemap is an index: `/sitemap.xml` → `/sitemap-main.xml` + `/sitemap-products.xml`
- Publish `GET /meshkee/sitemap-config.json` (generated at build from app routes)
- Includes public static routes automatically; excludes `api` / `meshkee` / `auth` / `checkout` / `cart` / `admin` / `account` / `login`
- Generated at build: `scripts/generate-sitemap-config.mjs` → `src/lib/sitemap-config.generated.json`
- Run manually: `npm run generate:sitemap-config`
- After deploy: business dashboard → Website → Settings → Sync sitemap config (needed for static pages)
### Canonical CMS URLs (id is canonical; slug is decorative and redirects if stale)
- Product: `/products/{id}/{nameFaSlug}` → `GET /tenants/{domain}/products/by-id/{id}`
- Category: `/products/category/{id}/{nameFaSlug}` → `GET /tenants/{domain}/categories/by-id/{id}` then list products with `categoryId`
- Blog: `/blog/{id}/{titleSlug}` → `GET /tenants/{domain}/blogs/by-id/{id}`
- Portfolio is not enabled for this tenant (`enabledModules`: products, blog, finance)
### On-page SEO rules
- Unique `<title>` + meta description on every crawlable page (`src/lib/seo.ts` → `pageMetadata`); paginated list pages include the page number
- Prefer CMS `nameFa` / `title` / `abstract` / `summary` / `descriptionHtml`; never leave Next.js defaults
- Exactly one `<h1>` per page; section headings use `<h2>` / `<h3>`
- Content images have meaningful `alt` (CMS title when available)
- Slugify: Farsi letters kept, spaces → `-`, other punctuation stripped (`src/lib/slug.ts`)