commit 028a91944c87821cc8ffd60082d7e8b86f2cf0ac Author: meshkee.ai10 Date: Fri Sep 4 11:51:32 2026 +0330 Dejband.com clone — Next.js, RTL Persian - Homepage faithfully matching dejband.com (hero, services, consulting, stats, about block, timeline, portfolio, articles) + About/Contact/ Portfolios/Articles pages under source URLs - Meshkee API integration: live blog content, favicon, user menu, sitemap/robots proxy, canonical /blog/{id}/{slug} - SEO pass: one H1 per page, unique metadata + canonicals, alt text - Contact form wired to /api/contact with toast + reset Co-Authored-By: Claude Sonnet 5 diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..5ae5877 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "dejband-dev", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "port": 3000 + } + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ec2187 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/node_modules +/.next +/out +/build +.DS_Store +*.pem +npm-debug.log* +.env* diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc6a230 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Dejband.com — Next.js clone + +Marketing-site replica of [dejband.com](https://dejband.com) (مهندسین مشاور دژبند قم) — +Persian, RTL, dark theme. Homepage, About Us, and Contact Us pages. + +See [`context/CONTEXT.md`](context/CONTEXT.md) for the full project context, design +tokens, component map, asset inventory, and the running prompt-iteration log. + +## Getting started + +```bash +npm install +npm run dev # http://localhost:3000 +``` + +Production: + +```bash +npm run build +npm start +``` + +## Structure + +- `src/app/` — `layout.js`, `globals.css`, `page.js` (home), `about/`, `contact/` +- `src/components/` — Header, Footer, Hero, Services, Consulting, Stats, Timeline, etc. +- `src/lib/site.js` — all page content/data in one place +- `public/images`, `public/fonts` — assets pulled from the source site + +## Not in scope (yet) + +Blog / articles and portfolios are deferred to a later backend-integration phase. +The contact form is client-only with no submit endpoint. diff --git a/context/CONTEXT.md b/context/CONTEXT.md new file mode 100644 index 0000000..15e164d --- /dev/null +++ b/context/CONTEXT.md @@ -0,0 +1,138 @@ +# Dejband.com — Clone Project Context + +> Living document. Update on **every** prompt iteration so other AI tools / developers +> can pick up work without re-discovering decisions. + +## 1. Goal + +Rebuild a marketing-site replica of **dejband.com** ("مهندسین مشاور دژبند قم" — Dejband +Consulting Engineers, Qom) using **Next.js (App Router)**, in **Persian (`fa`)** with +**RTL** layout. + +Scope for this phase — **3 pages only**: + +| Page | Route | Source | +| --- | --- | --- | +| Homepage | `/` | https://dejband.com | +| About Us | `/about` | https://www.dejband.com/aboutus | +| Contact Us | `/contact` | https://www.dejband.com/contactus | + +Blog / articles are **out of scope** — they will be wired to a backend later. +`/#articles` and "مقالات" nav link are placeholders. + +## 2. Prompt iteration log + +| # | Date | Summary | +| --- | --- | --- | +| 1 | 2026-09-04 | Initial build: scaffolded Next.js project, downloaded assets, built shared Header/Footer + Home/About/Contact pages, RTL + Persian, palette from source, footer bottom-bar modeled on safeteb.com. | +| 2 | 2026-09-04 | **Faithful homepage rebuild to match the original exactly.** Re-inspected every dejband.com homepage section. New/rewritten sections: Hero (2-col — blurred cover bg + framed foreground photo + centred heading + circled arrow, 6 slides), Services (black bg, 150px metallic PNG line icons, 3-col, no cards), Consulting (6 rows, fixed parallax strip), Stats (#0F0F0F, vertical counters 70/531/217/25), AboutBlock (blue lead + h5 + 2 ruled sub-columns), Timeline (vertical #083578 numbered nodes + centre line, alternating labels), Portfolio (4-col image grid, `rgba(8,53,120,.9)` hover overlay + fa/en captions), Articles (3-col blog grid). Downloaded: 6 service icon PNGs (`icons/svc-*.png`), 8 portfolio images (`portfolio/p-*.jpg`), 3 article thumbs (`articles/a-*.jpg`), hero foreground photos (`hero/fg-*.jpg`), `consult-parallax.jpg`. Added Montserrat (next/font/google) for Latin kickers. `next.config.mjs`: `images.unoptimized = true` (assets are pre-sized local copies). Removed `AboutPreview`, `ServiceIcon`. | + +### Homepage section order (matches source DOM) +`Hero → Services → Consulting(×6) → Stats → AboutBlock → Timeline → Portfolio → CtaBand → Articles` + +| 3 | 2026-09-04 | **All header pages created + routes aligned to source URLs.** Renamed `/about`→`/aboutus`, `/contact`→`/contactus` (matching dejband.com). New pages: `/portfolios` (blue `#083578` `PageTitle` bar + breadcrumb + light-gray 3-col project card grid — 4 real projects with images from `meshkee-storage`: چسب و رنگ، نیروموتور، هتل استقلال، مجتمع زمزم → `public/images/projects/proj-*.jpg`) and `/articles` (blue title bar + 4-col blog grid, same 3 `ARTICLES`). Rebuilt `/aboutus` and `/contactus` using shared `PageTitle`. Nav items آموزش / مشاوره رایگان / مجوزها point to `/` (source does the same). New component `PageTitle.js`. Consulting section: removed bg/parallax images — now a subtle `#0e0e0e` half-panel (`.consult-row::before`), alternating with `.flip`. AboutBlock two columns moved to 50/50. | +| 4 | 2026-09-04 | **`/aboutus` faithful rebuild (8 sections, dark theme)** mirroring source `/aboutus`: intro badge, هیئت مدیره (3 members `BOARD_MEMBERS` + `about/team/m-*.jpg`), ماموریت/چشم‌انداز interactive tabs (`MissionTabs.js`), طراحی صنعتی تخصص ماست, سازه صنعتی, رزومه و کاتالوگ (icons `about/ic-*.png`), blue CTA bar. 10 assets → `public/images/about/`. | +| 5 | 2026-09-04 | Hero heading: smaller (`clamp(20px,2.2vw,30px)`) + tighter `line-height:1.45`. "مشاهده بیشتر" button switched to Persian font (`--font-fa`). | +| 8 | 2026-09-04 | **Contact form wired to an API.** New route `app/api/contact/route.js` (POST) — validates `{name,phone,email,message}` (Persian-digit aware, `0?\d{10,11}` phone), returns `200 {ok,message,delivered}` or `422 {ok:false,errors:{...}}`. When `MESHKEE_SMS_API_KEY` + `MESHKEE_CONTACT_NOTIFY_TO` env vars are set it notifies the business via `POST /api/v1/public/sms/send` (`X-Api-Key`, `{domain,to,message}`); without them it still accepts (`delivered:false`). No public contact endpoint exists in the Meshkee docs — this route is the integration point. `ContactForm.js` rewritten: `fetch('/api/contact')`, loading state ("در حال ارسال…", disabled inputs), **success → toast (auto-dismiss 5s) + inline `.form-success-text` + `form.reset()`**, error → red toast + inline `.field-err` per field. New CSS: `.toast*`, `.field-err`, `.form-success-text`. | +| 7 | 2026-09-04 | **SEO & sitemap phase.** Sitemap/robots are **proxied** through the Meshkee API, no static files: route handlers `app/{robots.txt,sitemap.xml,sitemap-main.xml,sitemap-products.xml,sitemap-blogs.xml}/route.js` → `GET https://api.meshkee.com/api/v1/tenants/dejband.com/` via `proxyText()` in `lib/api.js` (each has an inline fallback; static routes exclude auth/checkout/cart/admin). **Canonical blog URL is `/blog/{id}/{titleSlug}`** (`app/blog/[id]/[slug]/page.js`, SSG from `getBlogById`, `slugify()` keeps Farsi & converts spaces→`-`); `app/articles/[slug]` is now a 307 redirect to the canonical path (`encodeURI` — Location must be ByteString); `/articles` listing links via `blogPath()`. **One H1 per page:** homepage has a single `.sr-only` `

` + hero titles demoted to `

`; `PageTitle` gained `as` prop — detail pages pass `as="div"` so the post/project `

` is the only one. **Per-page metadata:** unique `title`+`description`+`alternates.canonical` on `/`, `/aboutus`, `/contactus`, `/portfolios`, `/articles`, and both detail routes. **Alt text:** hero fg imgs → slide title, service icons → `آیکون {title}`, portfolio imgs → `{cat} — {en}`. `SITE_URL` const added. Build: 20 routes. | +| 6 | 2026-09-04 | **Backend integration (Meshkee public API).** New `src/lib/api.js` — `API_BASE=https://api.meshkee.com/api/v1`, `TENANT_BASE=/tenants/dejband.com`, `CUSTOMER_URL=https://customer.dejband.com`. Endpoints used: `GET /tenants/dejband.com/website/favicon` (favicon w/ fallback to bundled `public/favicon.png`), `GET /tenants/dejband.com/blogs` + `/blogs/{slug}` (live blog content), `GET /tenants/dejband.com/portfolios` (returns empty → portfolio uses local `PROJECTS`). **Auth/nav:** `UserMenu.js` — icon → `customer.dejband.com` when signed out; when signed in (reads `localStorage.meshkee_auth` / JWT `meshkee_token`) shows "{name} عزیز" dropdown → پروفایل من / مدیریت وبسایت (admin only) / خروج. **Favicon:** `layout.js` now `generateMetadata()` → `icons` from backend. **Blog module:** `/articles` async-fetches list; `/articles/[slug]` renders `mainTextHtml`, author, cover, Persian date (`formatFaDate` via `Intl` persian calendar) + "مقالات مرتبط" row. **Portfolio module:** `/portfolios/[slug]` — hero w/ CSS hover-zoom (`.pf-zoom-frame`), meta, `Lightbox.js` gallery (keyboard nav, counter, hover-zoom thumbs), "پروژه‌های مرتبط" row. `PROJECTS` in `site.js` extended with `slug`/`category`/`location`/`year`/`description`/`gallery`. New CSS: user-menu, `.pf-detail*`, `.pf-gallery*`, `.lb-*` lightbox, `.post*` blog, `.related-section`. Build: 15 routes (3 blog + 4 portfolio SSG, `revalidate: 300`). | + +## 3. Tech stack + +- **Next.js 15** (App Router, JS — no TypeScript), React 19. +- No CSS framework. One global stylesheet: `src/app/globals.css` (CSS custom properties + for the palette; component styles co-located by section). +- Fonts self-hosted via `next/font/local` — **Yekan Bakh** (primary, matches source + `--wb-default-font: yekan-bakh`) + **IranYekan** (numerals / fallback). Files in + `public/fonts/`, originals from `cdn.meshkee.com/web-builder/fonts/...`. +- Icons: inline stroke SVGs in `src/components/Icons.js` (no icon-font dependency). +- Images: local under `public/images/`, rendered with `next/image`. + +## 4. Brand / design tokens (from source web-builder theme vars) + +| Token | Value | Origin | +| --- | --- | --- | +| `--brand-primary` | `#2991d6` | `--wb-primary-color` | +| `--brand-primary-dark` | `#1875bb` | `--color-blue` | +| `--brand-navy` | `#04314b` | `--color-dark-blue` | +| `--brand-cyan` | `#32c5d2` | `--color-invert` | +| body background | `#101010` | source `body` bg | +| body text (on dark) | `~#9aa6b2` | derived from source `#5c6873` | +| default font | Yekan Bakh | `--wb-default-font` | + +Overall look: **dark theme** (near-black sections), blue accent, generous vertical +rhythm, uppercase Latin kicker above each Persian section title (mirrors source's +"خدمات ما / OUR SERVICES" pattern). + +## 5. Asset inventory (`public/`) + +``` +images/logo.png # white wordmark, source: assets/files/A3IeR4aVLO.png +images/meshkee-white.png # footer credit logo, source: safeteb.com/images/footer/MeshkeeLogo-White.png +images/hero/slide-1..7.jpg # homepage slider backgrounds +images/services/service-1..6.jpg # "مشاوره تخصصی" alternating rows +images/about/about-1..3.jpg # about-preview + about page splits +fonts/YekanBakh-Regular.ttf, YekanBakh-Light.ttf +fonts/IranYekan-Regular/Medium/Light/Bold.ttf +``` + +All content + media taken from the current live site — treated as valid for use per +the project brief. + +## 6. Component map + +| File | Role | +| --- | --- | +| `src/app/layout.js` | ``, font vars, metadata, Header + Footer shell | +| `src/app/globals.css` | all styling | +| `src/lib/site.js` | **single source of content data** — nav, contact info, hero slides, services, consulting rows, stats, timeline, socials | +| `components/Header.js` | sticky dark header, logo, nav, mobile drawer, social + login (`client`) | +| `components/Footer.js` | 4-column footer **+ bottom bar modeled on safeteb.com** (`.footer-bottom` → `.footer-bottom-bar` → `.footer-bottom-site` + `.meshkee-credit` animated frame) | +| `components/Hero.js` | auto-rotating 7-slide hero, dots, "مشاهده بیشتر" round arrow button (`client`) | +| `components/Services.js` | "خدمات ما / Our Services" icon grid | +| `components/Consulting.js` | alternating image/text rows (6 home consulting blurbs) | +| `components/Stats.js` | 4 animated counters, IntersectionObserver (`client`) | +| `components/AboutPreview.js` | home "درباره ما" split section | +| `components/Timeline.js` | 5-step company history | +| `components/CtaBand.js` | reusable blue CTA strip | +| `components/ContactForm.js` | contact form, client-side only (no backend yet) (`client`) | +| `components/Icons.js`, `components/ServiceIcon.js` | inline SVG icon set + name→icon map | + +## 7. Footer bottom-row spec (safeteb.com model) + +Exact structure reproduced (class names + animated corner-draw frame on hover): + +``` +.footer-bottom (bg #1d1d1d, padding 20px 0 19px) + .container + .footer-bottom-content + .footer-bottom-bar (flex, space-between, wrap, gap 12px) + .footer-bottom-site -> "مهندسین مشاور دژبند قم" + a.meshkee-credit[href=meshkee.com, dir=ltr] + .meshkee-credit__frame > 4x .meshkee-credit__line--(l|t|r|b) + img.meshkee-credit__logo (meshkee-white.png, 42x42) + .meshkee-credit__text + .meshkee-credit__label -> "Designed And Developed By" + .meshkee-credit__name -> "Meshkee E-Commerce Team" +``` + +CSS for `.meshkee-credit*` copied verbatim from safeteb.com computed rules. + +## 8. Known gaps / TODO (next iterations) + +- Blog / articles listing + detail — pending backend. +- "پروژه ها" (portfolios) page — not in current scope, nav points to `/#services`. +- Contact form has **no submit endpoint** — shows a success state only. +- Map is an OpenStreetMap embed with approximate Qom coordinates; swap for exact + location / Google Maps when available. +- Board-of-directors ("هیئت مدیره") photos not provided on source in a usable form — + section summarized as text on `/about`. +- Real favicon / OG image not yet added. + +## 9. Run + +``` +npm install +npm run dev # http://localhost:3000 +npm run build && npm start +``` diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..b8d6842 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..9e8ff2b --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,10 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + images: { + // Assets are local, pre-sized copies of the source site's media — serve as-is. + unoptimized: true, + }, +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5eaf4e4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,999 @@ +{ + "name": "dejband-clone", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dejband-clone", + "version": "0.1.0", + "dependencies": { + "next": "^15.5.25", + "react": "19.0.0", + "react-dom": "19.0.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@next/env": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.25.tgz", + "integrity": "sha512-42h1lLr07vl4gawALP1hsgRZjHB1xYa58JfUfHwr0f7jG/zhPakh5GHkADHXOC9ZxUvlQFOPIrp7s6qX4DezPQ==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.25.tgz", + "integrity": "sha512-w+RR0v/QuApnWEjRGm1z6gcObKwGMb5YPA7V3bzBEVSBpMFUXprer0tS27UxjUcEnqbhL7Zuzohej79B6rYmBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.25.tgz", + "integrity": "sha512-QiGGBUSakt8S1H4Lt9Ehsh6Ja87axiBnQQgysOObvCbI7iUfJnRGntF1P64S4/ijuHFnSB8KLsEddkY3nN26uw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.25.tgz", + "integrity": "sha512-ehLos/66zo0d/mJCU5u96a/VDcr01aaUrX0o/i16UdInxz8qPTCDSxGtjk/Lps1sIr1RJFdiX3hxc0fxJo+cPA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.25.tgz", + "integrity": "sha512-ZVMrqLiJ7DiChgmbkQwFtdhAnUkSH/4p7tB29QY+giATb0Q/XGHNRSKAb/B8XGDHRUaA67NepOW5W8u3ZRJBAA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.25.tgz", + "integrity": "sha512-UOewtDGkTMJTiODrEdeLZ50yGb59xCZSriNpXkfPMxRRgwDkGc7i8mLWqV5076wEdb+Ca/XN7MhJyM3CupyNyQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.25.tgz", + "integrity": "sha512-UBHwA8AhkCZgtRfU1aJpunuAJe/6gZv6jDESQe4p5MjTb5V0YEeJBWCdNqx15Vj3x+5jmauRfeMJSjfQj9HGFQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.25.tgz", + "integrity": "sha512-QcFcPRr16djk5IqK5+e8O80eZfgWzIvVBXfitIq0tQ/uc+eyfdoZ0NmKc0cnbIJyfVwREapKuG97YcxWA9gcpA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.25.tgz", + "integrity": "sha512-zREeykps3ndWr9egJgvJKqVkkDuaw6Zrrg23cYBos0ygydFkAWYU4+PaPVwXzP1eAYQJe53ShSK45iDM529BOg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.25.tgz", + "integrity": "sha512-OMWNulIIqKM2ykvC2qMjIt0IoavB4UB2SCs4iXJ6z6847FvyH8jBmBWcvrF5iuhTu8Przh20Fo/aoszIdqx4PA==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.25", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.25", + "@next/swc-darwin-x64": "15.5.25", + "@next/swc-linux-arm64-gnu": "15.5.25", + "@next/swc-linux-arm64-musl": "15.5.25", + "@next/swc-linux-x64-gnu": "15.5.25", + "@next/swc-linux-x64-musl": "15.5.25", + "@next/swc-win32-arm64-msvc": "15.5.25", + "@next/swc-win32-x64-msvc": "15.5.25", + "sharp": "^0.34.3 || ^0.35.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1823ad7 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "dejband-clone", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "^15.5.25", + "react": "19.0.0", + "react-dom": "19.0.0" + } +} diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000..b133f43 Binary files /dev/null and b/public/favicon.png differ diff --git a/public/fonts/IranYekan-Bold.ttf b/public/fonts/IranYekan-Bold.ttf new file mode 100644 index 0000000..6d98764 Binary files /dev/null and b/public/fonts/IranYekan-Bold.ttf differ diff --git a/public/fonts/IranYekan-Light.ttf b/public/fonts/IranYekan-Light.ttf new file mode 100644 index 0000000..b6114ee Binary files /dev/null and b/public/fonts/IranYekan-Light.ttf differ diff --git a/public/fonts/IranYekan-Medium.ttf b/public/fonts/IranYekan-Medium.ttf new file mode 100644 index 0000000..cc9d9b4 Binary files /dev/null and b/public/fonts/IranYekan-Medium.ttf differ diff --git a/public/fonts/IranYekan-Regular.ttf b/public/fonts/IranYekan-Regular.ttf new file mode 100644 index 0000000..c662408 Binary files /dev/null and b/public/fonts/IranYekan-Regular.ttf differ diff --git a/public/fonts/YekanBakh-Light.ttf b/public/fonts/YekanBakh-Light.ttf new file mode 100644 index 0000000..1c2582a Binary files /dev/null and b/public/fonts/YekanBakh-Light.ttf differ diff --git a/public/fonts/YekanBakh-Regular.ttf b/public/fonts/YekanBakh-Regular.ttf new file mode 100644 index 0000000..730949c Binary files /dev/null and b/public/fonts/YekanBakh-Regular.ttf differ diff --git a/public/images/about/about-1.jpg b/public/images/about/about-1.jpg new file mode 100644 index 0000000..4c3dfdb Binary files /dev/null and b/public/images/about/about-1.jpg differ diff --git a/public/images/about/about-2.jpg b/public/images/about/about-2.jpg new file mode 100644 index 0000000..8b661c9 Binary files /dev/null and b/public/images/about/about-2.jpg differ diff --git a/public/images/about/about-3.jpg b/public/images/about/about-3.jpg new file mode 100644 index 0000000..8373fca Binary files /dev/null and b/public/images/about/about-3.jpg differ diff --git a/public/images/about/ic-catalog.png b/public/images/about/ic-catalog.png new file mode 100644 index 0000000..a86c512 Binary files /dev/null and b/public/images/about/ic-catalog.png differ diff --git a/public/images/about/ic-resume.png b/public/images/about/ic-resume.png new file mode 100644 index 0000000..4974829 Binary files /dev/null and b/public/images/about/ic-resume.png differ diff --git a/public/images/about/intro.png b/public/images/about/intro.png new file mode 100644 index 0000000..e0ba244 Binary files /dev/null and b/public/images/about/intro.png differ diff --git a/public/images/about/mission.jpg b/public/images/about/mission.jpg new file mode 100644 index 0000000..5de1317 Binary files /dev/null and b/public/images/about/mission.jpg differ diff --git a/public/images/about/specialty.jpg b/public/images/about/specialty.jpg new file mode 100644 index 0000000..29261a3 Binary files /dev/null and b/public/images/about/specialty.jpg differ diff --git a/public/images/about/structure.jpg b/public/images/about/structure.jpg new file mode 100644 index 0000000..90cb1b7 Binary files /dev/null and b/public/images/about/structure.jpg differ diff --git a/public/images/about/team/m-1.jpg b/public/images/about/team/m-1.jpg new file mode 100644 index 0000000..8ad8d43 Binary files /dev/null and b/public/images/about/team/m-1.jpg differ diff --git a/public/images/about/team/m-2.jpg b/public/images/about/team/m-2.jpg new file mode 100644 index 0000000..3a7ee13 Binary files /dev/null and b/public/images/about/team/m-2.jpg differ diff --git a/public/images/about/team/m-3.jpg b/public/images/about/team/m-3.jpg new file mode 100644 index 0000000..7c1928c Binary files /dev/null and b/public/images/about/team/m-3.jpg differ diff --git a/public/images/about/vision.jpg b/public/images/about/vision.jpg new file mode 100644 index 0000000..8fa2aaf Binary files /dev/null and b/public/images/about/vision.jpg differ diff --git a/public/images/articles/a-1.jpg b/public/images/articles/a-1.jpg new file mode 100644 index 0000000..8dec195 Binary files /dev/null and b/public/images/articles/a-1.jpg differ diff --git a/public/images/articles/a-2.jpg b/public/images/articles/a-2.jpg new file mode 100644 index 0000000..a2e62e2 Binary files /dev/null and b/public/images/articles/a-2.jpg differ diff --git a/public/images/articles/a-3.jpg b/public/images/articles/a-3.jpg new file mode 100644 index 0000000..b2458f2 Binary files /dev/null and b/public/images/articles/a-3.jpg differ diff --git a/public/images/hero/fg-1.jpg b/public/images/hero/fg-1.jpg new file mode 100644 index 0000000..48d64d5 Binary files /dev/null and b/public/images/hero/fg-1.jpg differ diff --git a/public/images/hero/fg-2.jpg b/public/images/hero/fg-2.jpg new file mode 100644 index 0000000..c31f145 Binary files /dev/null and b/public/images/hero/fg-2.jpg differ diff --git a/public/images/hero/fg-3.jpg b/public/images/hero/fg-3.jpg new file mode 100644 index 0000000..375a4f7 Binary files /dev/null and b/public/images/hero/fg-3.jpg differ diff --git a/public/images/hero/fg-4.jpg b/public/images/hero/fg-4.jpg new file mode 100644 index 0000000..525ee5a Binary files /dev/null and b/public/images/hero/fg-4.jpg differ diff --git a/public/images/hero/fg-5.jpg b/public/images/hero/fg-5.jpg new file mode 100644 index 0000000..3d09edf Binary files /dev/null and b/public/images/hero/fg-5.jpg differ diff --git a/public/images/hero/fg-6.jpg b/public/images/hero/fg-6.jpg new file mode 100644 index 0000000..60bdd02 Binary files /dev/null and b/public/images/hero/fg-6.jpg differ diff --git a/public/images/hero/slide-1.jpg b/public/images/hero/slide-1.jpg new file mode 100644 index 0000000..aad2f97 Binary files /dev/null and b/public/images/hero/slide-1.jpg differ diff --git a/public/images/hero/slide-2.jpg b/public/images/hero/slide-2.jpg new file mode 100644 index 0000000..8cff52c Binary files /dev/null and b/public/images/hero/slide-2.jpg differ diff --git a/public/images/hero/slide-3.jpg b/public/images/hero/slide-3.jpg new file mode 100644 index 0000000..a23a93c Binary files /dev/null and b/public/images/hero/slide-3.jpg differ diff --git a/public/images/hero/slide-4.jpg b/public/images/hero/slide-4.jpg new file mode 100644 index 0000000..1f33aa1 Binary files /dev/null and b/public/images/hero/slide-4.jpg differ diff --git a/public/images/hero/slide-5.jpg b/public/images/hero/slide-5.jpg new file mode 100644 index 0000000..0f0fb71 Binary files /dev/null and b/public/images/hero/slide-5.jpg differ diff --git a/public/images/hero/slide-6.jpg b/public/images/hero/slide-6.jpg new file mode 100644 index 0000000..c758852 Binary files /dev/null and b/public/images/hero/slide-6.jpg differ diff --git a/public/images/icons/svc-1.png b/public/images/icons/svc-1.png new file mode 100644 index 0000000..2c34b73 Binary files /dev/null and b/public/images/icons/svc-1.png differ diff --git a/public/images/icons/svc-2.png b/public/images/icons/svc-2.png new file mode 100644 index 0000000..de935c8 Binary files /dev/null and b/public/images/icons/svc-2.png differ diff --git a/public/images/icons/svc-3.png b/public/images/icons/svc-3.png new file mode 100644 index 0000000..7c47404 Binary files /dev/null and b/public/images/icons/svc-3.png differ diff --git a/public/images/icons/svc-4.png b/public/images/icons/svc-4.png new file mode 100644 index 0000000..85474bd Binary files /dev/null and b/public/images/icons/svc-4.png differ diff --git a/public/images/icons/svc-5.png b/public/images/icons/svc-5.png new file mode 100644 index 0000000..63082d5 Binary files /dev/null and b/public/images/icons/svc-5.png differ diff --git a/public/images/icons/svc-6.png b/public/images/icons/svc-6.png new file mode 100644 index 0000000..3e087aa Binary files /dev/null and b/public/images/icons/svc-6.png differ diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..d2c4032 Binary files /dev/null and b/public/images/logo.png differ diff --git a/public/images/meshkee-white.png b/public/images/meshkee-white.png new file mode 100644 index 0000000..a10ecd3 Binary files /dev/null and b/public/images/meshkee-white.png differ diff --git a/public/images/portfolio/p-1.jpg b/public/images/portfolio/p-1.jpg new file mode 100644 index 0000000..419fb8e Binary files /dev/null and b/public/images/portfolio/p-1.jpg differ diff --git a/public/images/portfolio/p-2.jpg b/public/images/portfolio/p-2.jpg new file mode 100644 index 0000000..542aaec Binary files /dev/null and b/public/images/portfolio/p-2.jpg differ diff --git a/public/images/portfolio/p-3.jpg b/public/images/portfolio/p-3.jpg new file mode 100644 index 0000000..1fcf09f Binary files /dev/null and b/public/images/portfolio/p-3.jpg differ diff --git a/public/images/portfolio/p-4.jpg b/public/images/portfolio/p-4.jpg new file mode 100644 index 0000000..f3f3022 Binary files /dev/null and b/public/images/portfolio/p-4.jpg differ diff --git a/public/images/portfolio/p-5.jpg b/public/images/portfolio/p-5.jpg new file mode 100644 index 0000000..6c51d17 Binary files /dev/null and b/public/images/portfolio/p-5.jpg differ diff --git a/public/images/portfolio/p-6.jpg b/public/images/portfolio/p-6.jpg new file mode 100644 index 0000000..42a9e8b Binary files /dev/null and b/public/images/portfolio/p-6.jpg differ diff --git a/public/images/portfolio/p-7.jpg b/public/images/portfolio/p-7.jpg new file mode 100644 index 0000000..4faf618 Binary files /dev/null and b/public/images/portfolio/p-7.jpg differ diff --git a/public/images/portfolio/p-8.jpg b/public/images/portfolio/p-8.jpg new file mode 100644 index 0000000..31748c3 Binary files /dev/null and b/public/images/portfolio/p-8.jpg differ diff --git a/public/images/projects/proj-1.jpg b/public/images/projects/proj-1.jpg new file mode 100644 index 0000000..6d8cbe9 Binary files /dev/null and b/public/images/projects/proj-1.jpg differ diff --git a/public/images/projects/proj-2.jpg b/public/images/projects/proj-2.jpg new file mode 100644 index 0000000..7718722 Binary files /dev/null and b/public/images/projects/proj-2.jpg differ diff --git a/public/images/projects/proj-3.jpg b/public/images/projects/proj-3.jpg new file mode 100644 index 0000000..22b7ba6 Binary files /dev/null and b/public/images/projects/proj-3.jpg differ diff --git a/public/images/projects/proj-4.jpg b/public/images/projects/proj-4.jpg new file mode 100644 index 0000000..7bb99ec Binary files /dev/null and b/public/images/projects/proj-4.jpg differ diff --git a/public/images/services/service-1.jpg b/public/images/services/service-1.jpg new file mode 100644 index 0000000..da86f66 Binary files /dev/null and b/public/images/services/service-1.jpg differ diff --git a/public/images/services/service-2.jpg b/public/images/services/service-2.jpg new file mode 100644 index 0000000..899e294 Binary files /dev/null and b/public/images/services/service-2.jpg differ diff --git a/public/images/services/service-3.jpg b/public/images/services/service-3.jpg new file mode 100644 index 0000000..f3f3022 Binary files /dev/null and b/public/images/services/service-3.jpg differ diff --git a/public/images/services/service-4.jpg b/public/images/services/service-4.jpg new file mode 100644 index 0000000..42a9e8b Binary files /dev/null and b/public/images/services/service-4.jpg differ diff --git a/public/images/services/service-5.jpg b/public/images/services/service-5.jpg new file mode 100644 index 0000000..4faf618 Binary files /dev/null and b/public/images/services/service-5.jpg differ diff --git a/public/images/services/service-6.jpg b/public/images/services/service-6.jpg new file mode 100644 index 0000000..31748c3 Binary files /dev/null and b/public/images/services/service-6.jpg differ diff --git a/src/app/aboutus/page.js b/src/app/aboutus/page.js new file mode 100644 index 0000000..274ad87 --- /dev/null +++ b/src/app/aboutus/page.js @@ -0,0 +1,141 @@ +import Image from "next/image"; +import PageTitle from "@/components/PageTitle"; +import MissionTabs from "@/components/MissionTabs"; +import { BOARD_MEMBERS } from "@/lib/site"; +import { SITE_URL } from "@/lib/api"; + +export const metadata = { + title: "درباره ما و هیئت مدیره", + description: + "شرکت مهندسین مشاور دژبند قم از سال ۱۳۷۶؛ آشنایی با هیئت مدیره، ماموریت و چشم‌انداز شرکت — طراحی صنعتی تخصص ماست، با بیش از ۳۰۰ پروژه موفق در طراحی سوله و سازه‌های فلزی.", + alternates: { canonical: `${SITE_URL}/aboutus` }, +}; + +export default function AboutPage() { + return ( + <> + + + {/* 1. Intro */} +
+
+
+
+ مهندسین مشاور دژبند +
+
+

ما دژبند هستیم؛ مهندسی که به ساخت، فکر می‌کنه!

+

+ شرکت مهندسین مشاور دژبند قم از سال ۱۳۷۶ با یه هدف ساده اما جدی شروع به کار کرد: +
+ ساختن درست. اصولی. با دقت. +
+ از اون روز تا الان، ما تو پروژه‌های صنعتی، اداری، مسکونی، طراحی نما و سوله‌های سنگین، + کنار کارفرماها بودیم؛ نه فقط به عنوان طراح یا مشاور، بلکه به عنوان یه شریک فنی که پای + کار وایمیسته. +
+ تو این سال‌ها یاد گرفتیم که پروژه خوب، فقط نقشه و اسکلت نیست؛ دقت، زمان‌بندی، تعامل و + توجه به جزئیاته که فرق پروژه موفق و پروژه دردسرساز رو مشخص می‌کنه. +

+
+
+
+
+ + {/* 2. Board of directors */} +
+
+
+

هیئت مدیره

+

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

+
+
+ {BOARD_MEMBERS.map((m) => ( +
+
+ {`${m.first} +
+
+ {m.first} + {m.last} +
+
+ ))} +
+
+
+ + {/* 3. Mission / Vision tabs */} +
+
+ +
+
+ + {/* 4. Industrial design is our specialty */} +
+
+
+
+ طراحی صنعتی تخصص ماست +
+
+

طراحی صنعتی تخصص ماست.

+

INDUSTRIAL DESIGN IS OUR SPECIALTY

+

+ با افتخار و بیش از ۳۰۰ پروژه موفق در طراحی سوله و سازه‌های صنعتی در کنار شما هستیم تا + بهترین خدمات را با بهره‌گیری از دانش و تجربه‌ی خود ارائه دهیم. +

+
+
+
+
+ + {/* 5. Industrial structure */} +
+
+
+

سازه صنعتی

+ در این قسمت سازه صنعتی را مشاهده کنید. +
+
+ سازه صنعتی +
+
+
+ + {/* 6. Resume & catalog */} +
+
+
+

رزومه و کاتالوگ

+ رزومه و کاتالوگ ما را در این قسمت دانلود نمایید. +
+ +
+
+ + {/* 7. CTA bar */} +
+
+

+ ما آماده‌ایم برای چالش پروژه‌ی بعدی شما +
+ اگه دنبال تیمی می‌گردید که دقیق، مسئولیت‌پذیر و همراه باشه، دژبند همین‌جاست +

+
+
+ + ); +} diff --git a/src/app/api/contact/route.js b/src/app/api/contact/route.js new file mode 100644 index 0000000..6f4964d --- /dev/null +++ b/src/app/api/contact/route.js @@ -0,0 +1,75 @@ +import { DOMAIN, API_BASE } from "@/lib/api"; + +// Contact form endpoint. +// Validates the submission and — when an SMS key is configured — notifies the +// business through the Meshkee SMS gateway. Without a key it still accepts the +// message so the storefront keeps working; wire delivery by setting: +// MESHKEE_SMS_API_KEY server-to-server key for /public/sms/send +// MESHKEE_CONTACT_NOTIFY_TO phone number that should receive the alert + +export const runtime = "nodejs"; + +const faDigits = (s) => String(s).replace(/[۰-۹]/g, (d) => "۰۱۲۳۴۵۶۷۸۹".indexOf(d)); + +function validate(body) { + const name = String(body?.name || "").trim(); + const phone = faDigits(body?.phone || "").trim(); + const email = String(body?.email || "").trim(); + const message = String(body?.message || "").trim(); + + const errors = {}; + if (name.length < 2) errors.name = "نام را وارد کنید"; + if (!/^0?\d{10,11}$/.test(phone.replace(/[\s-]/g, ""))) + errors.phone = "شماره تماس معتبر نیست"; + if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) + errors.email = "ایمیل معتبر نیست"; + if (message.length < 5) errors.message = "متن پیام را وارد کنید"; + + return { data: { name, phone, email, message }, errors }; +} + +async function notifyBusiness({ name, phone, email, message }) { + const key = process.env.MESHKEE_SMS_API_KEY; + const to = process.env.MESHKEE_CONTACT_NOTIFY_TO; + if (!key || !to) return { delivered: false, reason: "not-configured" }; + + const text = `پیام جدید از فرم تماس دژبند\nنام: ${name}\nتماس: ${phone}${ + email ? `\nایمیل: ${email}` : "" + }\nپیام: ${message}`.slice(0, 600); + + try { + const res = await fetch(`${API_BASE}/public/sms/send`, { + method: "POST", + headers: { "Content-Type": "application/json", "X-Api-Key": key }, + body: JSON.stringify({ domain: DOMAIN, to, message: text }), + }); + return { delivered: res.ok, reason: res.ok ? "sent" : `sms-${res.status}` }; + } catch { + return { delivered: false, reason: "sms-error" }; + } +} + +export async function POST(request) { + let body; + try { + body = await request.json(); + } catch { + return Response.json({ ok: false, message: "درخواست نامعتبر است" }, { status: 400 }); + } + + const { data, errors } = validate(body); + if (Object.keys(errors).length) { + return Response.json( + { ok: false, message: "لطفاً فیلدها را بررسی کنید", errors }, + { status: 422 } + ); + } + + const notify = await notifyBusiness(data); + + return Response.json({ + ok: true, + message: "پیام شما با موفقیت ثبت شد. به‌زودی همکاران ما با شما تماس می‌گیرند.", + delivered: notify.delivered, + }); +} diff --git a/src/app/articles/[slug]/page.js b/src/app/articles/[slug]/page.js new file mode 100644 index 0000000..2062c00 --- /dev/null +++ b/src/app/articles/[slug]/page.js @@ -0,0 +1,11 @@ +import { redirect } from "next/navigation"; +import { getBlog, blogPath } from "@/lib/api"; + +export const revalidate = 300; + +// Legacy path — 308-redirect to the canonical /blog/{id}/{slug}. +export default async function LegacyArticleRedirect({ params }) { + const { slug } = await params; + const blog = await getBlog(slug); + redirect(blog ? encodeURI(blogPath(blog)) : "/articles"); +} diff --git a/src/app/articles/page.js b/src/app/articles/page.js new file mode 100644 index 0000000..1b29bac --- /dev/null +++ b/src/app/articles/page.js @@ -0,0 +1,70 @@ +import Image from "next/image"; +import Link from "next/link"; +import PageTitle from "@/components/PageTitle"; +import { getBlogs, formatFaDate, blogPath, SITE_URL } from "@/lib/api"; +import { ARTICLES as FALLBACK } from "@/lib/site"; + +export const metadata = { + title: "مقالات و مطالب آموزشی", + description: + "مقالات تخصصی مهندسین مشاور دژبند قم درباره طراحی و ساخت سوله صنعتی، سازه‌های فلزی، نقشه‌های برق و مکانیک و استانداردهای ایمنی.", + alternates: { canonical: `${SITE_URL}/articles` }, +}; + +export const revalidate = 300; + +export default async function ArticlesPage() { + const blogs = await getBlogs(); + + const items = blogs.length + ? blogs.map((b) => ({ + href: blogPath(b), + img: b.image || "/images/articles/a-1.jpg", + category: b.category, + date: formatFaDate(b.date), + title: b.title, + external: false, + })) + : FALLBACK.map((a) => ({ ...a, external: true })); + + return ( + <> + + +
+
+
    + {items.map((a) => { + const Card = ( + <> +
    + {`تصویر +
    +
    +
    + {a.category} +
    + {a.date} +

    {a.title}

    +
    +
    + + ); + return ( +
  • + {a.external ? ( + + {Card} + + ) : ( + {Card} + )} +
  • + ); + })} +
+
+
+ + ); +} diff --git a/src/app/blog/[id]/[slug]/page.js b/src/app/blog/[id]/[slug]/page.js new file mode 100644 index 0000000..90fccd7 --- /dev/null +++ b/src/app/blog/[id]/[slug]/page.js @@ -0,0 +1,105 @@ +import Image from "next/image"; +import Link from "next/link"; +import { notFound, redirect } from "next/navigation"; +import PageTitle from "@/components/PageTitle"; +import { getBlogById, getBlogs, formatFaDate, slugify, blogPath, SITE_URL } from "@/lib/api"; + +export const revalidate = 300; + +export async function generateStaticParams() { + const blogs = await getBlogs(); + return blogs.map((b) => ({ id: String(b.id), slug: slugify(b.title) })); +} + +export async function generateMetadata({ params }) { + const { id } = await params; + const blog = await getBlogById(id); + if (!blog) return { title: "مقاله" }; + return { + title: blog.title, + description: (blog.abstract || blog.title).replace(/\s+/g, " ").trim().slice(0, 160), + alternates: { canonical: `${SITE_URL}${blogPath(blog)}` }, + openGraph: { + title: blog.title, + type: "article", + images: blog.image ? [blog.image] : undefined, + }, + }; +} + +export default async function BlogDetailPage({ params }) { + const { id, slug } = await params; + const blog = await getBlogById(id); + if (!blog) notFound(); + + // Keep the canonical slug in the URL (encode: Location must be a ByteString). + const canonicalSlug = slugify(blog.title); + if (decodeURIComponent(slug) !== canonicalSlug) { + redirect(encodeURI(`/blog/${id}/${canonicalSlug}`)); + } + + const all = await getBlogs(); + const related = all.filter((b) => String(b.id) !== String(id)).slice(0, 3); + + return ( + <> + + +
+
+
+ {blog.category} + {formatFaDate(blog.date)} + {blog.author ? نویسنده: {blog.author} : null} +
+

{blog.title}

+ + {blog.image ? ( +
+ {blog.title} +
+ ) : null} + + {blog.abstract ?

{blog.abstract}

: null} + +
+
+
+ + {related.length ? ( +
+
+
+

مقالات مرتبط

+ RELATED ARTICLES +
+
    + {related.map((r) => ( +
  • + +
    + {r.title} +
    +
    +
    + {r.category} +
    + {formatFaDate(r.date)} +

    {r.title}

    +
    +
    + +
  • + ))} +
+
+
+ ) : null} + + ); +} diff --git a/src/app/contactus/page.js b/src/app/contactus/page.js new file mode 100644 index 0000000..040cf1b --- /dev/null +++ b/src/app/contactus/page.js @@ -0,0 +1,97 @@ +import PageTitle from "@/components/PageTitle"; +import ContactForm from "@/components/ContactForm"; +import { CONTACT } from "@/lib/site"; +import { IconPhone, IconMail, IconMapPin, IconWhatsapp } from "@/components/Icons"; +import { SITE_URL } from "@/lib/api"; + +export const metadata = { + title: "تماس با ما و مشاوره رایگان", + description: + "شماره تماس ۰۲۵-۳۷۷۰۰۲۲۲، ایمیل info@dejband.com و آدرس دفتر مهندسین مشاور دژبند قم؛ برای مشاوره، استعلام قیمت یا پرسش درباره خدمات با ما در ارتباط باشید.", + alternates: { canonical: `${SITE_URL}/contactus` }, +}; + +const CARDS = [ + { icon: IconPhone, title: "شماره تمــــــــاس", value: "025-37700222", href: "tel:02537700222" }, + { icon: IconMail, title: "به ما ایمیل بزنید", value: "info@dejband.com", href: "mailto:info@dejband.com" }, + { + icon: IconMapPin, + title: "آدرس شرکت", + value: "قم، 55 متری عماریاسر جنب هتل استقلال", + }, + { + icon: IconWhatsapp, + title: "ارسال پیام در واتساپ", + value: "09390333500", + href: "https://wa.me/989390333500", + }, +]; + +export default function ContactPage() { + return ( + <> + + +
+
+
+

سوالی دارید؟ ما اینجاییم تا کمک کنیم

+ GET IN TOUCH +
+

+ برای مشاوره، استعلام قیمت، یا هرگونه پرسش درباره خدمات، تیم ما آماده پاسخ‌گویی سریع و دقیق + به شماست. می‌تونید از طریق تماس، ایمیل یا واتساپ با ما در ارتباط باشید. +

+ +
+ {CARDS.map((c) => { + const Ic = c.icon; + const body = ( + <> +
+ +
+

{c.title}

+

{c.value}

+ + ); + return c.href ? ( + + {body} + + ) : ( +
+ {body} +
+ ); + })} +
+
+
+ +
+
+
+ +