Expand HOM studio site with portfolio, blog, auth, and details video frame.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
73971bf6d9
commit
55dd94d2e4
+51
-63
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user