commit dee487ad73bd3a907bf9c939770779ab4c4fb028 Author: amirhosein.ashourloo Date: Tue Sep 15 20:37:49 2026 +0330 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a488b7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +.next/ +out/ +.env* +!.env.example +*.log +.npm-cache/ +asset-preview.jpg diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2b8f22a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + + +## Project-specific requirements + +Read `CONTEXT.md` before making changes. It contains the authoritative user requirements, source findings, static route map, asset paths, API boundaries, and current verification status. Update it whenever a prompt changes meaningful requirements or implementation state. Preserve the real Next.js static-export architecture, Persian RTL, Swiper, user-supplied images, and local IRANYekan typography (Bold main headings; Light elsewhere). Never fabricate real portfolio records or successful form submissions. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..0070a8e --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,131 @@ +# Sanaz Goodarzi — project context + +Last updated: 2026-09-15. Read this file before making changes. Update it on each prompt when requirements, decisions, implementation, or verification status change. Never replace the original migration objective with a follow-up styling request. + +## User requirements (authoritative) + +- Rebuild the site running at `http://sanaz-goodarzi.local:8000/` in **real Next.js**, Persian (`lang="fa"`), RTL (`dir="rtl"`). +- Treat original site content as valid; correct obvious visual, navigation, spelling, and UX defects. +- Implement all public pages as static pages now. The user will supply API documentation later. +- Use **Swiper** for sliders/carousels. Do not substitute a custom slider. +- Keep the original charcoal, gold, and white palette. Improve mobile hamburger spacing. +- Fully responsive layouts for mobile, tablet, desktop; support narrow screens, touch targets, keyboard focus, and reduced motion. +- User supplied images in root `img/`, and permitted copying/moving them. Render the copy in `public/img/` with the original filenames/subdirectories. Keep the supplied originals intact. +- Dynamic areas use clearly marked thumbnails/previews until real API content is provided. Never present invented projects, project dates, locations, metrics, or successful form submissions as real. +- Footer bottom Meshkee credit must follow `https://safeteb.com/`, including the logo, exact English text, link, and animated border. +- Latest typography instruction: use the user's files in **`public/fonts/`** (the user called it “font”). Main headings use **Bold 700**; everything else uses **Light 300**. Do not return to Vazirmatn, Shabnam, or a remote font CDN. +- Keep development and output in this local workspace. No publication was requested. +- This architecture website has no product catalog or store. Ignore all product, store-item, cart, checkout, order, payment, marketplace, and shop-login instructions in this and future backend prompts unless the user explicitly reverses this requirement. + +## Stack and running + +- Next.js App Router, React, plain CSS, lucide-react icons, Swiper. +- `next.config.mjs`: `output: 'export'`, `trailingSlash: true`, unoptimized image output. +- `npm run dev`: development at `http://localhost:3000` (original site remains on port 8000). +- `npm run build`: exports production files into `out/`. +- `npm start`: serves `out/` locally; optional `PORT` environment variable. +- `npm run lint`: ESLint / Next recommended rules. +- Use Node 24 LTS (`.nvmrc`), minimum 20.9. This machine's default Laragon Node is 18.8. `scripts/runtime.mjs` uses the already-installed Codex Node runtime as a Windows fallback when needed. No runtime is downloaded. +- `package-lock.json` is authoritative for installed versions. Do not run a new scaffold or replace this project with Vinext/Sites templates. + +## Public routes + +| Route | Implementation | +|---|---| +| `/` | Full-viewport hero plus five source-matched category panels; vertical Swiper on desktop, normal stacked scrolling on mobile; no footer | +| `/ourservices/` | Five text cards in a two-column grid overlapping the 690px source subheader; one column on mobile | +| `/aboutus/` | Source bird-image hero with three colored icons, biography with circular photo, image/progress section, accessible FAQ | +| `/contactus/` | Three source-matched contact columns and centered single-column static form; four offices and real contacts | +| `/portfolios/` | Category filtering with URL query state; clearly labeled category thumbnails | +| `/portfolios/residential/` | Static residential preview | +| `/portfolios/beauty/` | Static beauty salon preview | +| `/portfolios/commercial/` | Static office/commercial preview | +| `/portfolios/landscape/` | Static landscape preview | +| `/portfolios/facade/` | Static facade preview | +| `/portfolios/[id]/[titleFaSlug]/` | Canonical API portfolio details generated at build time from `by-id`, with hover zoom, lightbox gallery, and related work | +| `/blog/` | API blog listing; a thumbnail empty state appears while no posts are published | +| `/blog/[id]/[titleSlug]/` | Canonical API blog details generated at build time from `by-id`, with CMS SEO and JSON-LD when supplied | +| unknown route | Persian 404 page | + +No blog link/page was found in the original public navigation. Do not invent a blog or articles simply because the original brief mentioned blogs as an example of dynamic content. + +## Source-matched visual structure (2026-09-06) + +- The user rejected the earlier free redesign. Preserve the original layout, imagery, section order and proportions; fix only clear UX/content defects and apply explicit follow-ups. +- Home: full-height image 1 hero; five half-image/half-text panels, images on the left, text on the right, alternating white/black backgrounds and large gold V/B/O/L/F letters at the seam. No extra introduction, about strip or contact CTA. +- Use hamburger navigation at every viewport size, with comfortable top/right spacing and the supplied gold logo at the left. Keep the animated dark fullscreen menu. +- About progress values 68, 25, 36 and 82 were verified in the original DOM; they are source data, not invented metrics. +- Portfolio source eventually returned a backend TLS connection error to its upstream portfolio service. Do not copy its debug output or fabricate unavailable project records. + +## Source findings + +- Read original home, about, and services in browser, and contact page through HTTP. Original navigation exposes the five main routes above. +- Original `/portfolios` failed to finish loading in the browser and a bounded HTTP request timed out. Portfolio records therefore were **not verified**. Current images represent categories only, not named projects. +- Original home incorrectly labeled the facade service as office/commercial. Corrected to طراحی نما. +- Original services page repeated office copy under landscape. Replaced with the matching landscape copy from original home. +- Original footer/navigation/category/social links frequently pointed to `/` or `#`; replaced with meaningful routes and verified contact destinations. +- Original admin/editor controls are not public site functionality and are intentionally excluded. +- Original logo URL returned a Git LFS pointer, not an image. It was removed. The user subsequently supplied the actual `public/Logo.png` (case-sensitive path), now rendered by the shared Brand component in the header, mobile menu, and footer with the header wordmark hidden to match the source. Preserve its gold colors, transparency, and aspect ratio. +- Original Shabnam CDN did not resolve. User then supplied IRANYekan fonts; these supersede all prior font decisions. + +## Files and boundaries + +- `lib/content.js`: verified source text, navigation, five services, biography, FAQs, four offices, three phones, two emails, Instagram. +- `lib/portfolio.js`: explicit `isPlaceholder: true` records and `getPortfolioItems()` adapter boundary. +- `components/home-experience.js`: active home implementation. Vertical Swiper at min-width 900px and min-height 650px; otherwise stacked sections. Mode changes remount Swiper so mousewheel initialization remains correct. Six navigation dots, keyboard support, reduced motion, and inactive desktop slides marked inert. `components/hero.js` is a legacy unused component. +- `components/shell.js`: shared header, native dialog mobile menu, footer. Dialog provides modal keyboard behavior; closes on Escape, backdrop, close button, or navigation. Scroll is locked while open. +- `components/contact-form.js`: validates locally; **never sends or stores data**. Status explicitly states no message was sent. The send API is pending. +- `components/portfolio-grid.js`: category filter and URL query sync, including browser back/forward. +- `app/globals.css`: base tokens, accessibility, shared components and animation rules. `app/reference.css`, imported afterward, owns source-matching layout and responsive overrides at 1200, 899 and 650px. Shared subheaders use the original image without any overlay and dark titles. +- `app/layout.js`: `next/font/local` with `iranyekanweblightfanum.woff` at 300 and `iranyekanwebboldfanum.woff` at 700. +- `public/meshkee-logo.png`: genuine logo retrieved from safeteb.com. Credit links to `https://meshkee.com` and reads `Designed And Developed By` / `Meshkee E-Commerce Team`. +- `public/img/slider/1.jpg`: charcoal/gold hero; 2 residential; 3 beauty; 4 office/commercial; 5 landscape; 6 facade. Other images retain original paths. `img/frames/` holds supplied frame references, not project portfolio records. + +## API integration next steps + +Backend contract received 2026-09-15: base `https://api.meshkee.com/api/v1`, tenant `sanazgoodarzi.ir`, official documentation `https://api.meshkee.com/docs/website`. + +Tenant SEO prompt `6d011d374a2bb32e88d778c8` supersedes the earlier generic slug-only detail URL guidance. Public CMS detail URLs use numeric/string ID plus a Persian title slug: `/blog/{id}/{titleSlug}` and `/portfolios/{id}/{titleFaSlug}`. The page resolves content through the matching `by-id` endpoint; the title segment is for readable canonical URLs. + +1. Read the user's API documentation; do not invent URLs, tokens, schemas, or authentication. +2. Replace the portfolio adapter and remove preview badges only for actual records. Preserve category IDs or map API values explicitly. +3. Current detail slugs are exported by `generateStaticParams`. New API slugs require a rebuild, or an explicitly agreed change to client-side routing/server rendering. +4. Keep client secrets out of browser bundles. Decide static fetch-at-build vs client fetch based on the real API and freshness requirements. +5. Contact form is connected to `POST /tenants/sanazgoodarzi.ir/contact-submissions` with title/name/email/cellNumber/text. It resets and displays success text plus toast only after HTTP success; failures preserve entered values. +6. Mark preview detail pages `noindex` until populated. Review metadata when real content is connected. + +## Verification status + +- Dependencies installed; npm audit at install reported zero vulnerabilities. +- Development home compiled and HTTP returned 200; preview opened at localhost:3000. +- Initial lint found no errors and one config export warning; the warning has been corrected. +- Final `npm run build` passed using Next.js 16.3.4, including all 10 public content routes and the generated not-found route. All are static/SSG exports. +- Final `npm run lint` passed with zero errors and zero warnings. +- `scripts/verify-export.mjs` passed: 10 HTTP routes, 37 linked local assets/files, valid signatures for both IRANYekan WOFF files, Persian RTL markup, the Meshkee credit, and a real HTTP 404 with the Persian not-found page. +- Preview tab at `http://localhost:3000/` was marked as the deliverable; the source site on port 8000 was preserved. Use `npm run dev` for continued editing, or `npm start` for the exported static preview. Do not start both on the same port. +- Browser QA performed on desktop home hero/category slides (including mousewheel), source-matched about/services, and 390x844 mobile home, services, contact and fullscreen menu. Mobile services/contact showed no horizontal overflow. Home footer absence and inner-page footer presence checked. Build, lint and export verification passed again after the latest footer/subheader changes. +- SEO prompt verification on 2026-09-15: production build and lint passed. `scripts/verify-seo.mjs` passed for all 6 crawlable static pages (unique title/description, exactly one H1, canonical URL, meaningful image alt) and confirmed the generated config contains exactly 6 static routes. `scripts/verify-export.mjs` passed all 11 current exported/preview routes. API blogs and portfolios were empty, so placeholder static params are noindex and will be replaced by real ID/title params on a build after CMS publication. + +## Change log + +- SEO prompt `6d011d374a2bb32e88d778c8`: changed CMS detail links and static params to tenant-specific ID + Persian-title canonical URLs; added unique descriptions and canonical metadata to all crawlable static pages; added CMS-first detail metadata and meaningful OG image alt text; confirmed one H1 per implemented page; and added build-time route scanning that writes `public/meshkee/sitemap-config.json`. Never add local `public/sitemap.xml` or `public/robots.txt`; production nginx proxies them to Meshkee. Product/category sitemap instructions remain excluded by explicit user requirement. + +- Backend prompt `579d72949e25b19a25256699`: connected blog and portfolio public APIs with empty-state thumbnails; added API detail routes, portfolio lightbox/hover zoom/related row, contact submission behavior, customer account detection from shared cookies plus `/auth/me`, and API branding with local `Logo.png` fallback. API state at implementation: branding URLs null, blogs 0, portfolios 0. Product/store features were explicitly excluded by the user. + +- Latest steering during reference matching: remove Footer entirely from the home route only; retain it on all other routes. Remove the subheader overlay, keep the source photo unobscured, and use dark titles/English subtitles. This explicitly supersedes the earlier 80% black overlay instruction. + +- Logo follow-up: integrated user-supplied `public/Logo.png` in the shared header/footer/mobile-menu brand. Responsive widths preserve the original 462×473 aspect ratio; intrinsic dimensions reserve layout space and alt text identifies the brand. + +- Latest subheader instruction supersedes the light-overlay design: all shared page headings now use a uniform black `rgba(0,0,0,0.8)` photo overlay and white titles, descriptions, English eyebrows, breadcrumbs, and link states at every breakpoint. Font weights and responsive sizing are preserved. + +- Scroll animation correction: replaced late WAAPI opacity resets with pre-armed CSS transitions on offscreen elements only. Already-visible content is never hidden; entrances begin 48px before viewport entry, move 16px, and run once. Mutation processing checks added subtrees instead of rescanning the full page on Swiper updates. Reduced-motion changes and cleanup reveal prepared content; focused content stays visible. No Animate.css dependency needed for these small opacity/transform transitions. This supersedes the previous viewport WAAPI implementation. Production build and lint passed after the scroll correction. + +- Animation follow-up: added progressively enhanced, once-per-element viewport entrances (including filtered portfolio cards), hero/subheader text entrances, and subtle button/icon feedback. Mobile dialog slides in from the right in 440ms and closes in 260ms; backdrop fades and links enter sequentially. Native dialog stays modal and scrolling stays locked until exit completes; Escape is intercepted to animate before closing. Reduced-motion users get immediate menu changes and no reveal effects. `components/page-motion.js` owns observers and cancels animations on unmount/preference changes. Production build and lint both passed after this change. + +- Subheader readability follow-up: added a consistent high-opacity warm-white photo overlay at every breakpoint, explicit charcoal heading/body colors, darker gold eyebrow, and readable 13px breadcrumb/eyebrow and 16px description sizes. Preserved IRANYekan Bold headings / Light remaining text. This applies to all shared `PageHeading` sections. + +- Initial request: created the Next.js static migration, source content mapping, Swiper, shared navigation/footer, responsive public pages, and API placeholders. +- Image follow-up: copied supplied `img/` to `public/img/` without renaming source files. +- Responsive follow-up: mobile/tablet/desktop layouts, hamburger spacing, dialog navigation, touch targets, fluid grids/images. +- Font follow-up: switched to supplied IRANYekan local Light/Bold fonts and recorded the weight rule. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..acd25dc --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# گروه معماری ساناز گودرزی + +نسخهٔ استاتیک فارسی و راست‌به‌چپ با Next.js و Swiper. + +## اجرا + +Node.js 24 LTS پیشنهاد می‌شود. + +```sh +npm install +npm run dev +``` + +پیش‌نمایش: `http://localhost:3000` + +```sh +npm run lint +npm run build +npm start +``` + +خروجی استاتیک در `out/` ایجاد می‌شود. برای انتشار روی هاست استاتیک، همین پوشه باید سرو شود. همهٔ مسیرها فایل `index.html` مستقل دارند و مسیر ناشناخته باید `404.html` را با وضعیت 404 دریافت کند. + +## ادامهٔ توسعه + +ابتدا [CONTEXT.md](./CONTEXT.md) را بخوانید و با تغییر نیازها به‌روزرسانی کنید. متن و اطلاعات تماس در `lib/content.js` و داده‌های نمایشی نمونه‌کار در `lib/portfolio.js` قرار دارند. + +عکس‌ها با نام‌های اصلی در `public/img/` و فونت‌های IRANYekan در `public/fonts/` هستند. عنوان‌های اصلی Bold و باقی متن‌ها Light هستند. + +نمونه‌کارها فعلاً پیش‌نمایش حوزه‌های طراحی‌اند. فرم تماس هنوز به API متصل نیست و هیچ پیامی ارسال یا ذخیره نمی‌کند. داکیومنت API برای اتصال واقعی لازم است. diff --git a/app/aboutus/page.js b/app/aboutus/page.js new file mode 100644 index 0000000..409b6d4 --- /dev/null +++ b/app/aboutus/page.js @@ -0,0 +1,15 @@ +import { aboutParagraphs, faqs } from '../../lib/content'; +export const metadata = { title: 'دربارهٔ گروه معماری ساناز گودرزی', description:'با رویکرد، تجربه و حوزه‌های فعالیت گروه معماری و دکوراسیون ساناز گودرزی در طراحی، نظارت و اجرای پروژه‌های معماری آشنا شوید.', alternates:{canonical:'/aboutus/'} }; +const indicators = [['رضایت مشتریان',68],['تنوع خدمات',25],['زیبایی بصری',36],['نمونه‌کارها',82]]; +export default function About() { + return <> +

About Us

رویای شما
تبدیل به واقعیت می‌شود

+
{[1,2,3].map((number)=>{['نظارت)}
+
+

گروه معماری و دکوراسیون
ساناز گودرزی

ARCHITECT & INTERIOR DESIGN

{aboutParagraphs.join(' ')}

طراحی داخلی فضای غذاخوری
+
طراحی فضای نشیمن

رویای شما تبدیل به واقعیت می‌شود؛ این تمام چیزی است که ما انجام می‌دهیم.

TURN YOUR DREAMS INTO REALITY

رویای شما تبدیل به واقعیت می‌شود؛ این تمام چیزی است که ما انجام می‌دهیم.

+ {indicators.map(([label,value],index)=>
{value}%
)} +
+

با ما بیشتر آشنا شوید

MORE ABOUT US

این مجموعه با اولویت قرار دادن خواسته‌های کارفرما و رسیدگی پیگیرانه به موضوعات پیش‌آمده، تا حصول نتیجهٔ رضایت‌بخش دست از تلاش و همکاری بر نخواهد داشت.

{faqs.map(([question,answer])=>
{question}

{answer}

)}
+ ; +} diff --git a/app/blog/[slug]/[titleSlug]/page.js b/app/blog/[slug]/[titleSlug]/page.js new file mode 100644 index 0000000..7a603a9 --- /dev/null +++ b/app/blog/[slug]/[titleSlug]/page.js @@ -0,0 +1,11 @@ +import { notFound } from 'next/navigation'; +import Link from 'next/link'; +import { faSlug, getBlogById, getBlogs, imageUrlOf, plainText, titleOf } from '../../../../lib/meshkee-api'; + +export const dynamicParams = false; +export async function generateStaticParams() { const params=(await getBlogs()).map((post)=>({slug:String(post.id),titleSlug:faSlug(titleOf(post))})); return params.length?params:[{slug:'__placeholder__',titleSlug:'بدون-عنوان'}]; } +export async function generateMetadata({params}) { const {slug}=await params; const post=await getBlogById(slug); if(!post)return{robots:{index:false,follow:false}}; const title=titleOf(post,'بلاگ'); const description=post.seoMetaDescription||plainText(post.abstract||post.summary||post.mainTextHtml||'').slice(0,160)||`مقالهٔ ${title} از گروه معماری ساناز گودرزی.`; const canonical=`/blog/${post.id}/${faSlug(title)}/`; return {title:post.seoMetaTitle||title,description,alternates:{canonical},openGraph:{title,description,url:canonical,type:'article',images:imageUrlOf(post)?[{url:imageUrlOf(post),alt:title}]:[]}}; } +export default async function BlogDetail({params}) { const {slug}=await params; const post=await getBlogById(slug); if(!post)notFound(); const title=titleOf(post); const image=imageUrlOf(post); return
+ {post.schema?.jsonLd&&