Expand HOM studio site with portfolio, blog, auth, and details video frame.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-12 22:52:38 +03:30
co-authored by Cursor
parent 73971bf6d9
commit 55dd94d2e4
45 changed files with 6056 additions and 651 deletions
+51 -63
View File
@@ -1,79 +1,67 @@
# HOM arch studio — AI / Project Context
Persian (fa, RTL) brochure website for **استودیو معماری هوم** (architecture + interior design group).
Persian (fa, RTL) brochure website for **استودیو معماری هوم** (architecture + interior design).
Keep this file current whenever routes, slots, data sources or design rules change.
## Source of truth
- Live domain: `https://homgroup.com` (`NEXT_PUBLIC_WEBSITE_DOMAIN`, default `homgroup.com`)
- Backend docs: https://api.meshkee.com/docs/website (OpenAPI: `/docs/website/openapi.json`)
- API base: `https://api.meshkee.com/api/v1` (`NEXT_PUBLIC_MESHKEE_API_BASE`)
- Tenant: `GET /tenants/homgroup.com` — **not registered yet** (404 as of 2026-10-11). Every Meshkee read has a local fallback, so the site renders fully without the tenant.
- Prompt reference: `https://api.meshkee.com/api/v1/public/ai-prompts/studio-hom/basic-create/36caa331616c4b1cd382f8cb`
- Original brief: homepage + about + contact only; `lang=fa`, `dir=rtl`, flat design, palette from the logo.
- Live domain: `https://homgroup.ir` (`NEXT_PUBLIC_WEBSITE_DOMAIN`, default `homgroup.ir`)
- Backend docs: https://api.meshkee.com/docs/website
- API base: `https://api.meshkee.com/api/v1`
- Tenant: `GET /tenants/homgroup.ir` → business id `53`
- Customer portal: `https://customer.homgroup.ir` (no storefront login pages)
- Business dashboard: `https://business.homgroup.ir`
- Placeholders prompt: `…/ai-prompts/studio-hom/placeholders/576804987514877ec206af60`
## Stack
- Next.js 15 App Router + React 19 + TypeScript, no Tailwind — all styles in `src/app/globals.css`
- `output: "standalone"` in `next.config.ts` (**required** for the Meshkee websites VM; deploy drops `node_modules`)
- `npm run build` runs `prebuild` → `scripts/generate-sitemap-config.mjs` (scans `src/app` for static pages)
- `npm run lint` = `tsc --noEmit`
- Font: IranYekan (local, `public/fonts/`, weights 300/400/500/700) via `next/font/local` → `--font-yekan`
- Next.js 15 App Router + React 19 + TypeScript; styles in `src/app/globals.css` (no Tailwind)
- `output: "standalone"` required for Meshkee websites VM
- Fonts: IranYekan (FA) + Montserrat (EN / `.latin`)
## Design language (flat)
- Palette: bg `#ffffff`, surface `#f4f3f1`, text `#111111`, muted `#6b6b6b`, hairline `#e2e0dc`, accent red `#e3002b` (the logo dot)
- Flat only: no shadows, no border-radius, 1px hairlines, solid fills. Red appears as a small square “dot” (eyebrows, hero title) and on hover.
- Latin labels use `.latin` (uppercase, letter-spaced, LTR). One dark section (`.section--dark`) for the process.
- Header is sticky, white, hairline bottom; mobile burger menu under 900px.
- Logo: `public/images/logo.png` (cropped from `assets/logo/logo.jpeg`); favicon `src/app/icon.png`
## Design
- Flat palette: bg `#fff`, surface `#f4f3f1`, fg `#111`, muted `#6b6b6b`, line `#e2e0dc`, accent `#e3002b`
- Sticky header + account menu; dark map band on contact; stats band with pattern
## Static image slots (`STATIC_IMAGE_SLOTS` → `/meshkee/static-image-slots`)
Fetch: `GET /tenants/{domain}/website/static-images?pageKey=…`. Match by **key**. Use all images if `kind=list`, first if `single`. `url` → `<img>`, `linkUrl` → wrap link, `titleFa` / `titleEn` / `subtext` per UI. Empty/fail → local placeholder. Prefer API `aspectRatio`. **If a frame’s size or ratio changes in development, update the matching catalog entry in the same change.**
| key | pageKey | kind | itemCount | aspectRatio | width | UI |
| --- | --- | --- | --- | --- | --- | --- |
| `slider` | home | list | null | `16:9` | 1920 | Full-viewport `HeroSlider` |
| `four-category` | home | list | 4 | `3:4` | 900 | Four hover-expand service panels (`titleFa` / `titleEn` / `subtext`) |
| `project-images` | home | list | null | `3:4` (masonry free height) | 1200 | Selected projects masonry |
| `sticky-frame-bg` | home | single | 1 | `16:9` | 1920 | `OpeningFrame` background |
| `instagram-row` | home | single | 1 | `9:16` | 720 | `InstagramFollow` phone frame (also used on contact) |
| `brands` | home | list | null | `3:2` | 480 | Partner logos row (hidden when empty) |
| `about-us-image` | about | list | 2 | `16:9` | 1600 | About hero (1st) + secondary (2nd) |
## Tags & badges (CMS)
From `GET /tenants/{domain}/website/business-info`:
1. `metaTags: [{ name, content }]` → root `<CmsMeta>` + `generateMetadata.other`
2. `trustBadges: [{ kind, embedHtml }]` → footer `dangerouslySetInnerHTML` (scripts stripped). Empty → omit. Do not hardcode eNamad/Samandehi.
## Auth
Shared cookies `meshkee_customer_*`; login → customer portal; `AccountMenu` shows `{name} عزیز` + پروفایل من / خروج / مدیریت وبسایت (admin + handoff).
## Routes
| Route | Purpose |
| Route | Notes |
| --- | --- |
| `/` | Hero (title + CMS hero image), intro, 4 services, 6 selected projects, studio block, 4-step process (dark), stats, CTA |
| `/about` | Page hero image, story (3 paragraphs from `business-info.about` or fallback), 3-image gallery, values, services list, stats, CTA |
| `/contact` | Facts (phones, email, Instagram, address), contact image, form → `POST /api/contact` |
| `/api/contact` | Proxies to `POST /tenants/{domain}/contact-submissions` (`title`, `name`, `text` required; `email`, `cellNumber` optional) |
| `/meshkee/static-image-slots` | Slot catalog (`STATIC_IMAGE_SLOTS`) for the dashboard “Refresh slots” |
| `/meshkee/sitemap-config.json` | Static pages for the Meshkee-generated sitemap |
| `/` | Slider, intro, four-category, project-images, sticky frame, process, stats, brands, Instagram, CTA |
| `/portfolio`, `/portfolio/[slug]` | Meshkee portfolios (+ local fallback); lightbox + zoom + similar |
| `/projects*` | Redirect → `/portfolio` |
| `/blog`, `/blog/[slug]` | Meshkee blogs (+ local stubs) |
| `/services*` | Local specialties |
| `/about` | `about-us-image` (2), story, values, stats, CTA |
| `/contact` | Facts + form (API + toast) + Instagram + dark map |
| `/api/contact`, `/api/auth/me`, `/api/auth/handoff` | Proxies |
| `/meshkee/*` | Slot catalog + sitemap-config |
No `/login`, `/register`, `/cart`, `/checkout`, no local `sitemap.xml` / `robots.txt` (nginx proxies those to the API).
## Meshkee integration (`src/lib/`)
- `config.ts` — domain / API base / portal URLs
- `meshkee.ts` — `meshkeeGet` (ISR 300s, returns fallback on any error), `getBusinessInfo`, `getFavicon`
- `static-images.ts` — slot catalog + `getPageSlots(pageKey)` (parses `{ items: [{ key, aspectRatio, images[] }] }`), `slotImages`, `slotRatio`
- `content.ts` — resolves name / logo / phones / emails / address / Instagram / about from `business-info` with fallbacks from `src/data/site.ts`; `parseHeadTag`, `stripScripts`, digit helpers
- Root layout: `metaTags` → `<CmsMeta>` in `<head>`; `schema.jsonLd` → `<JsonLd>` when `schema.enabled`; `trustBadges[].embedHtml` → footer (scripts stripped)
- Favicon: `GET /website/favicon` → `metadata.icons`; falls back to `src/app/icon.png`
### Static image slots
Fetch per page: `GET /tenants/{domain}/website/static-images?pageKey=…`. Empty/fail → local images from `gallery` in `src/data/site.ts`.
`url` → `<img>`, `linkUrl` → wrap in `<a>`, `titleFa`/`titleEn`/`subtext` → captions where the UI shows them.
Use the API `aspectRatio` first, catalog second. **If a frame’s size or ratio changes in development, update `STATIC_IMAGE_SLOTS` in the same change.**
| key | page | kind | itemCount | aspectRatio | width | UI |
| --- | --- | --- | --- | --- | --- | --- |
| `hero` | home | single | 1 | `16:9` | 1920 | Hero frame; `titleFa` shown in the caption row |
| `projects` | home | list | null | `4:5` | 1200 | Selected projects grid; `titleFa` = title, `titleEn` = Latin subtitle, `subtext` = meta (e.g. category · year). When empty → 6 local `projects` |
| `studio-image` | home | single | 1 | `4:3` | 1400 | “نگاه ما” block image |
| `about-hero` | about | single | 1 | `16:9` | 1920 | About page hero |
| `about-gallery` | about | list | 3 | `1:1` | 900 | 3-up square gallery |
| `contact-image` | contact | single | 1 | `4:5` | 1200 | Image under the contact facts |
## Local content / fallbacks (`src/data/site.ts`)
- `site` — names (`استودیو معماری هوم` / `HOM arch studio`), tagline, description, placeholder phone `۰۹۱۲ ۰۰۰ ۰۰۰۰`, email `info@homgroup.com`, Instagram `@hom.archstudio`, address `تهران`, 3 about paragraphs. **Replace placeholders once the business profile exists in Meshkee** (they are overridden automatically by `business-info` when present).
- `services` (4), `processSteps` (4), `stats` (4), `projects` (6), `gallery` (image paths), `NAV_LINKS`
- Images: `public/images/*.jpg` — resized (≤1800px, q82) from `assets/images/` (originals kept out of git). Mapping: `facade-sunset*` ← IMG_1455/1456/1458, `courtyard-brick*` ← IMG_0356/0357, `kitchen-cream*` ← IMG_0374/0375, `study-terracotta*` ← IMG_0376/0377, `kitchen-oak*` ← IMG_0394/0396, `facade-louver` ← IMG_0400, `garden-gate` ← IMG_1037
## SEO
- Unique `<title>` / description per page (`metadata` in each `page.tsx`; template `%s | {name}`)
- One `<h1>` per page; content images have `alt`
- Static pages published through `/meshkee/sitemap-config.json`; do not add `app/sitemap.ts` / `app/robots.ts`
## Components (`src/components/`)
`Header` (client; sticky, burger), `Footer`, `CmsMeta` (+ `JsonLd`), `SlotImage` (flat frame for slot/fallback images), `ContactForm` (client; posts to `/api/contact`, Persian status messages)
## Components
`Header`/`AccountMenu`, `Footer`, `Providers`, `CmsMeta`, `SlotImage`, `SectionTitle`, `OpeningFrame`, `HeroSlider`, `InstagramFollow`, `MediaGallery`, `ContactForm`
## Deploy
Standard Meshkee websites VM flow: push → Super Admin **Deploy**. Deploy log must say `standalone build detected`; PM2 script `.next/standalone/server.js`.
Standalone build on Meshkee websites VM; PM2 → `.next/standalone/server.js`.
## Changelog
- 2026-10-11 — Initial build: home / about / contact, Meshkee business-info + static-image slots + contact form, standalone runtime, sitemap-config generator.
- 2026-10-12 — Placeholders prompt: remapped slots (`slider`, `four-category`, `project-images`, `sticky-frame-bg`, `instagram-row`, `brands`, `about-us-image`); brands row; metaTags → `metadata.other`.
- 2026-10-12 — Backend prompt: auth, portfolio/blog, contact toast, Instagram frame, domain `homgroup.ir`.
- 2026-10-11 — Initial site scaffold.