diff --git a/.env.example b/.env.example index ef357eb..123f66c 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -NEXT_PUBLIC_API_BASE_URL=https://api.robotmarket.ir/v1 +# Base URL for the future backend API +NEXT_PUBLIC_API_BASE_URL=https://api.robotmarket.example.com diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore index 3f799a9..18d2aec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules .next -.env*.local -.DS_Store out -build +.env* +.DS_Store *.log diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..ad9397e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/robot-market-landing.iml b/.idea/robot-market-landing.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/robot-market-landing.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index bd91bea..d7fe5dc 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,35 +1,6 @@ - - - - { - "associatedIndex": 2, - "fromUser": false -} - - - - { - "keyToString": { - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", - "RunOnceActivity.typescript.service.memoryLimit.init": "true", - "codeWithMe.voiceChat.enabledByDefault": "false", - "js.debugger.nextJs.config.created.client": "true", - "js.debugger.nextJs.config.created.server": "true", - "last_opened_file_path": "D:/Project/robot-market-landing/robot-market", - "nodejs_package_manager_path": "npm", - "vue.rearranger.settings.migration": "true" - } -} + {} @@ -45,26 +16,4 @@ - - - - - - - - - - 1785213528048 - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 7815471..9865636 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,138 @@ -# روبات مارکت — سایت نمایش محصولات +# روبات مارکت — Robot Market Landing -## نصب و اجرا +A cinematic, RTL, Persian-language landing page for **Robot Market**, a +smart refrigerated vending-machine brand. Built with Next.js (App Router), +Tailwind CSS, Ant Design, Redux Toolkit, Axios and GSAP + ScrollTrigger. + +The centerpiece of the page is a scroll-driven hero animation: scattered +SVG pieces fly together into a fully assembled vending machine as the user +scrolls, followed by a three-act "scroll story" introducing each of the +three product models. + +## Getting started ```bash npm install npm run dev ``` -سپس آدرس `http://localhost:3000` را در مرورگر باز کنید. +Open http://localhost:3000. -## ساختار پروژه +```bash +npm run build # production build +npm run start # serve the production build +npm run lint # eslint +``` + +## Tech stack + +| Concern | Library | +| ------------------- | -------------------------------- | +| Framework | Next.js 14 (App Router, JS only) | +| Styling | Tailwind CSS | +| Component library | Ant Design (RTL-configured) | +| State management | Redux Toolkit | +| HTTP client | Axios | +| Animation | GSAP + ScrollTrigger + @gsap/react | +| Icons | react-icons | +| Font | Vazirmatn (next/font/google) | + +## Project structure ``` src/ - app/ # layout.js و page.js (App Router) - components/ # کامپوننت‌های بخش‌های سایت - data/ # محتوای متنی و محصولات (content.js) - utils/api.js # نمونه Axios برای اتصال به API در آینده + app/ Routes (App Router): layout, page, dashboard, + robots.js, sitemap.js, manifest.js, loading, 404 + components/ + common/ Container, Section, GlassCard, MagneticButton + layout/ Navbar, Footer, About, ContactCta + hero/ Hero, HeroBackground, MachineAssembly, FloatingProducts + products/ ProductsShowcase, ProductSection, MachineVisual + animations/ ScrollReveal (shared scroll-in wrapper) + ui/ AntThemeProvider (Ant Design RTL + token bridge) + redux/ + store.js Store factory (per-request, App-Router-safe) + provider.jsx Client + slices/ auth, products, ui + services/ + axios.js Shared Axios instance + interceptors + auth.service.js // TODO: Connect Login API + product.service.js // TODO: Connect Product API + dashboard.service.js // TODO: Connect Dashboard API + hooks/ useReducedMotion, useIsomorphicLayoutEffect + utils/ cn, formatPrice, gsapConfig, breakpoints + constants/ theme tokens, nav links + data/ products.json, floatingProducts.json (mock data) +public/ + svg/machines/machine35|48|60/ Assembly pieces: base, body, door, panel, screen, shelves + svg/floating/ chip, cola, drink, cookie, snack, juice + icons/ Brand icon + OG image placeholders (SVG) ``` -## نکات +## Replacing placeholder assets -- رنگ اصلی برند در `tailwind.config.js` روی `#00a693` تنظیم شده است. -- فونت وزیرمتن به‌صورت خودکار از `next/font/google` بارگذاری می‌شود. -- برای جایگزینی عکس واقعی دستگاه به‌جای طرح SVG، فایل - `src/components/VendingMachineParts.js` را ویرایش کنید. -- برای اتصال فرم "سفارش و مشاوره" یا دکمه "افزودن به سبد" به بک‌اند واقعی، - از توابع آماده در `src/utils/api.js` استفاده کنید. +Every visual asset in `public/svg` is a deliberately simple placeholder — +swap the files in place (same filenames) and nothing else needs to change: + +- `public/svg/machines//*.svg` — the 6 pieces GSAP assembles/positions. + Keep the same `viewBox="0 0 400 700"` coordinate space so pieces still + line up when assembled, or update the piece list in `src/data/products.json`. +- `public/svg/floating/*.svg` — the floating product icons around the hero. +- `public/icons/*.svg` — brand icon and Open Graph placeholder. Replace the + OG placeholder with a real 1200×630 raster (`.png`/`.jpg`) before launch + and update the reference in `src/app/layout.js`. + +## Connecting the real API + +Nothing here talks to a real backend yet. Each service method already has +the exact call commented out — uncomment and remove the mock `Promise.resolve`: + +```js +// src/services/product.service.js +async getAll() { + // TODO: Connect Product API + return apiClient.get("/products").then((res) => res.data); +} +``` + +Set `NEXT_PUBLIC_API_BASE_URL` in `.env.local` (see `.env.example`). + +## Motion & accessibility notes + +- **Scroll-locked product story.** Each `ProductSection` reports its DOM + nodes up to `ProductsShowcase`, which builds and pins all three + machines' `ScrollTrigger` timelines together in one place (fixing a bug + where wide/desktop viewports could fail to reveal machines after the + first one — see the comment at the top of `ProductsShowcase.jsx`), then + calls `ScrollTrigger.refresh()` once every measurement has settled. + Scrolling literally cannot advance past a pinned section until its + reveal finishes — only then does the next machine begin. +- **Vivid, temperature-coded background per machine.** Each product + carries its own `bgColor` in `src/data/products.json`, and + `ProductsShowcase` cross-fades a single fixed color layer to that color + over the *exact same* scroll range the section uses to pin — so the + backdrop finishes changing exactly as the machine settles into place. + The three models deliberately alternate cool → warm → cool: a vivid icy + cyan-blue (`#12C4E0`) for the entry model, a warm coral-orange + (`#FF7A45`) for the mid-tier, and a cool emerald-teal (`#12D6A8`) for + the flagship — all bright, cheerful tones rather than muted brand + neutrals, chosen specifically for this section. +- **Floating drinks/snacks per section.** `SectionFloatingItems.jsx` + places a small set of product icons (soda, mineral water, juice, + snacks) drifting gently around each machine — which items appear is + configurable per product via `floatingItemIds` in `products.json`. +- All GSAP animations check `prefers-reduced-motion` and degrade to simple + opacity fades (or skip entirely) when it's set. +- `gsap.matchMedia()` is used in both `MachineAssembly` (hero) and + `ProductSection` to give desktop/tablet/mobile different scroll + distances/intensity. +- Focus states, semantic landmarks (`
`, `
`, `