From 574a18712ded3f1417c2b59d27f309dbe153c224 Mon Sep 17 00:00:00 2001 From: Hamed Date: Sun, 2 Aug 2026 08:52:45 +0400 Subject: [PATCH] update --- .idea/workspace.xml | 36 +++- app/context/LanguageContext.jsx | 2 + app/context/ThemeContext.jsx | 12 +- app/globals.css | 252 ++++++++++++-------------- app/layout.jsx | 9 +- app/page.jsx | 45 +++-- app/providers.jsx | 2 + components/AboutSection.jsx | 4 +- components/CartDrawer.jsx | 2 + components/CatalogSection.jsx | 5 +- components/ChatWidget.jsx | 2 + components/FeaturesSection.jsx | 4 +- components/FloatingItems.jsx | 2 + components/Footer.jsx | 4 +- components/Header.jsx | 8 +- components/HeroSection.jsx | 291 ++++++++++++++---------------- components/HorizontalShowcase.jsx | 226 +++++++++++------------ components/IntroVideoSection.jsx | 4 +- components/LanguageToggle.jsx | 2 + components/LocationsSection.jsx | 4 +- components/MapSection.jsx | 5 +- components/ThemeToggle.jsx | 2 + components/TicketForm.jsx | 4 +- components/VendingCard.jsx | 3 + data/content.js | 86 +++++++-- jsconfig.json | 3 +- package-lock.json | 6 +- 27 files changed, 560 insertions(+), 465 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index a9a9e75..2791c7b 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -3,14 +3,9 @@ - - - - - - - - + + + diff --git a/app/context/LanguageContext.jsx b/app/context/LanguageContext.jsx index 82f35f7..53a5eb2 100644 --- a/app/context/LanguageContext.jsx +++ b/app/context/LanguageContext.jsx @@ -1,5 +1,7 @@ 'use client'; +// context زبان سایت: نگه‌داری زبان فعلی (fa/en)، ذخیره در localStorage، و تنظیم جهت صفحه (rtl/ltr). + import { createContext, useContext, useEffect, useState } from 'react'; import { content } from '@/data/content'; diff --git a/app/context/ThemeContext.jsx b/app/context/ThemeContext.jsx index ad8d05e..1a15af8 100644 --- a/app/context/ThemeContext.jsx +++ b/app/context/ThemeContext.jsx @@ -5,16 +5,20 @@ import { createContext, useContext, useEffect, useState } from 'react'; const ThemeContext = createContext(null); export function ThemeProvider({ children }) { - const [theme, setTheme] = useState('dark'); + // پیش‌فرض سایت حالا «روشن» (light) است. اگر کاربر قبلاً تم را عوض کرده باشد + // (ذخیره‌شده در localStorage) همان ترجیح رو حفظ می‌کنیم؛ در غیر این صورت فقط + // اگر سیستم کاربر صراحتاً روی حالت تیره تنظیم شده باشد، تیره را انتخاب می‌کنیم. + const [theme, setTheme] = useState('light'); const [mounted, setMounted] = useState(false); - // On mount: read saved preference, else fall back to system preference. + // On mount: read saved preference, else fall back to system preference + // (but default stays light unless the OS explicitly prefers dark). useEffect(() => { const saved = window.localStorage.getItem('rm-theme'); if (saved === 'light' || saved === 'dark') { setTheme(saved); - } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { - setTheme('light'); + } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + setTheme('dark'); } setMounted(true); }, []); diff --git a/app/globals.css b/app/globals.css index a12d10e..2041fa2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -6,223 +6,203 @@ /* Theme tokens — dark is the default; .light overrides on */ /* ------------------------------------------------------------------ */ :root { - --brand: #00a693; - --color-bg-page: #04120f; - --color-bg-elevated: #0c2b24; - --color-bg-surface: rgba(255, 255, 255, 0.045); - --color-bg-surface-strong: rgba(255, 255, 255, 0.08); - --color-border: rgba(0, 166, 147, 0.28); - --color-text-primary: #ffffff; - --color-text-secondary: rgba(230, 247, 244, 0.72); - --color-scrollbar-track: #071e19; - --color-scrollbar-thumb: #0b5c50; - color-scheme: dark; + --brand: #00a693; + --color-bg-page: #04120f; + --color-bg-elevated: #0c2b24; + --color-bg-surface: rgba(255, 255, 255, 0.045); + --color-bg-surface-strong: rgba(255, 255, 255, 0.08); + --color-border: rgba(0, 166, 147, 0.28); + --color-text-primary: #ffffff; + --color-text-secondary: rgba(230, 247, 244, 0.72); + --color-scrollbar-track: #071e19; + --color-scrollbar-thumb: #0b5c50; + color-scheme: dark; } html.light { - --color-bg-page: #f5fbf9; - --color-bg-elevated: #ffffff; - --color-bg-surface: rgba(0, 89, 79, 0.045); - --color-bg-surface-strong: rgba(0, 89, 79, 0.08); - --color-border: rgba(0, 166, 147, 0.25); - --color-text-primary: #04231f; - --color-text-secondary: rgba(4, 35, 31, 0.68); - --color-scrollbar-track: #e1f3ef; - --color-scrollbar-thumb: #8fdccc; - color-scheme: light; + --color-bg-page: #f5fbf9; + --color-bg-elevated: #ffffff; + --color-bg-surface: rgba(0, 89, 79, 0.045); + --color-bg-surface-strong: rgba(0, 89, 79, 0.08); + --color-border: rgba(0, 166, 147, 0.25); + --color-text-primary: #04231f; + --color-text-secondary: rgba(4, 35, 31, 0.68); + --color-scrollbar-track: #e1f3ef; + --color-scrollbar-thumb: #8fdccc; + color-scheme: light; } html, body { - padding: 0; - margin: 0; - scroll-behavior: smooth; + padding: 0; + margin: 0; + scroll-behavior: smooth; } html[dir='rtl'] body { - font-family: var(--font-vazirmatn), Tahoma, sans-serif; + font-family: var(--font-vazirmatn), Tahoma, sans-serif; } * { - box-sizing: border-box; + box-sizing: border-box; } ::selection { - background: rgba(0, 166, 147, 0.35); - color: var(--color-text-primary); + background: rgba(0, 166, 147, 0.35); + color: var(--color-text-primary); } ::-webkit-scrollbar { - width: 8px; - height: 8px; + width: 8px; + height: 8px; } - ::-webkit-scrollbar-track { - background: var(--color-scrollbar-track); + background: var(--color-scrollbar-track); } - ::-webkit-scrollbar-thumb { - background: var(--color-scrollbar-thumb); - border-radius: 999px; + background: var(--color-scrollbar-thumb); + border-radius: 999px; } /* Semantic surface/text utilities — read the theme variables above so every component that uses them automatically supports dark + light mode. */ -.bg-page { - background-color: var(--color-bg-page); -} - -.bg-elevated { - background-color: var(--color-bg-elevated); -} - -.bg-surface { - background-color: var(--color-bg-surface); -} - -.bg-surface-strong { - background-color: var(--color-bg-surface-strong); -} - -.border-subtle { - border-color: var(--color-border); -} - -.text-primary { - color: var(--color-text-primary); -} - -.text-secondary { - color: var(--color-text-secondary); -} +.bg-page { background-color: var(--color-bg-page); } +.bg-elevated { background-color: var(--color-bg-elevated); } +.bg-surface { background-color: var(--color-bg-surface); } +.bg-surface-strong { background-color: var(--color-bg-surface-strong); } +.border-subtle { border-color: var(--color-border); } +.text-primary { color: var(--color-text-primary); } +.text-secondary { color: var(--color-text-secondary); } /* Opacity variants of the tokens above (Tailwind's /NN modifier only works on Tailwind-generated utilities, not these hand-written classes). */ -.bg-page-60 { - background-color: color-mix(in srgb, var(--color-bg-page) 60%, transparent); -} - -.bg-page-85 { - background-color: color-mix(in srgb, var(--color-bg-page) 85%, transparent); -} - -.bg-elevated-95 { - background-color: color-mix(in srgb, var(--color-bg-elevated) 95%, transparent); -} - -.text-primary-90 { - color: color-mix(in srgb, var(--color-text-primary) 90%, transparent); -} +.bg-page-60 { background-color: color-mix(in srgb, var(--color-bg-page) 60%, transparent); } +.bg-page-70 { background-color: color-mix(in srgb, var(--color-bg-page) 70%, transparent); } +.bg-page-85 { background-color: color-mix(in srgb, var(--color-bg-page) 85%, transparent); } +.bg-elevated-95 { background-color: color-mix(in srgb, var(--color-bg-elevated) 95%, transparent); } +.text-primary-90 { color: color-mix(in srgb, var(--color-text-primary) 90%, transparent); } /* ------------------------------------------------------------------ */ /* Horizontal scroll showcase (pinned via GSAP ScrollTrigger) */ /* ------------------------------------------------------------------ */ .horizontal-track { - display: flex; - height: 100vh; - /*width: 100vw;*/ - will-change: transform; + display: flex; + height: 100vh; + width: max-content; + will-change: transform; } .horizontal-panel { - height: 100vh; - width: 100vw; - flex-shrink: 0; - position: relative; - overflow: hidden; + height: 100vh; + width: 100vw; + flex-shrink: 0; + position: relative; + overflow: hidden; } /* ------------------------------------------------------------------ */ /* Cursor spotlight / lens reveal effect over the vending machine */ /* ------------------------------------------------------------------ */ .spotlight-stage { - position: relative; - cursor: none; - isolation: isolate; + position: relative; + cursor: none; + isolation: isolate; } .spotlight-stage .base-layer, .spotlight-stage .reveal-layer { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - object-fit: contain; - pointer-events: none; - user-select: none; + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + pointer-events: none; + user-select: none; } .spotlight-stage .reveal-layer { - clip-path: circle(0px at 50% 50%); - transition: clip-path 0.05s linear; + clip-path: circle(0px at 50% 50%); + transition: clip-path 0.05s linear; } .spotlight-lens { - position: absolute; - top: 0; - left: 0; - width: 190px; - height: 190px; - border-radius: 999px; - border: 2px solid rgba(0, 166, 147, 0.9); - box-shadow: 0 0 0 4px rgba(0, 166, 147, 0.15), + position: absolute; + top: 0; + left: 0; + width: 190px; + height: 190px; + border-radius: 999px; + border: 2px solid rgba(0, 166, 147, 0.9); + box-shadow: + 0 0 0 4px rgba(0, 166, 147, 0.15), 0 0 40px 6px rgba(0, 166, 147, 0.45); - pointer-events: none; - transform: translate(-50%, -50%); - opacity: 0; - transition: opacity 0.15s ease; - z-index: 30; + pointer-events: none; + transform: translate(-50%, -50%); + opacity: 0; + transition: opacity 0.15s ease; + z-index: 30; } .spotlight-stage:hover .spotlight-lens { - opacity: 1; + opacity: 1; } /* Utility: hide native scrollbar but keep scroll functionality */ .no-scrollbar::-webkit-scrollbar { - display: none; + display: none; } - .no-scrollbar { - -ms-overflow-style: none; - scrollbar-width: none; + -ms-overflow-style: none; + scrollbar-width: none; } /* Gradient text helper for headings */ .brand-gradient-text { - background: linear-gradient(90deg, #6fe0c9 0%, #00a693 55%, #017065 100%); - -webkit-background-clip: text; - background-clip: text; - color: transparent; + background: linear-gradient(90deg, #6fe0c9 0%, #00a693 55%, #017065 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; } /* ------------------------------------------------------------------ */ /* Typewriter caret for the hero headline */ /* ------------------------------------------------------------------ */ .typewriter-caret { - display: inline-block; - width: 3px; - margin-inline-start: 4px; - background-color: var(--brand); - animation: caretBlink 0.9s steps(1) infinite; + display: inline-block; + width: 3px; + margin-inline-start: 4px; + background-color: var(--brand); + animation: caretBlink 0.9s steps(1) infinite; } @keyframes caretBlink { - 0%, 49% { - opacity: 1; - } - 50%, 100% { - opacity: 0; - } + 0%, 49% { opacity: 1; } + 50%, 100% { opacity: 0; } +} + +/* ------------------------------------------------------------------ */ +/* نوار لوگوی بی‌پایان (trusted-by marquee) */ +/* ------------------------------------------------------------------ */ +@keyframes marqueeScroll { + from { transform: translateX(0); } + to { transform: translateX(-50%); } /* دقیقاً نصف عرض، چون لیست دو برابر شده */ +} + +.animate-marquee { + animation: marqueeScroll 28s linear infinite; +} + +.animate-marquee:hover { + animation-play-state: paused; } /* ------------------------------------------------------------------ */ /* Chat widget */ /* ------------------------------------------------------------------ */ .chat-bubble { - max-width: 80%; - padding: 10px 14px; - border-radius: 14px; - font-size: 0.875rem; - line-height: 1.6; + max-width: 80%; + padding: 10px 14px; + border-radius: 14px; + font-size: 0.875rem; + line-height: 1.6; } diff --git a/app/layout.jsx b/app/layout.jsx index 058da35..7087432 100644 --- a/app/layout.jsx +++ b/app/layout.jsx @@ -15,11 +15,12 @@ export const metadata = { }; export default function RootLayout({ children }) { - // Default server-rendered shell: Persian + dark. ThemeProvider/LanguageProvider - // read localStorage on mount and correct lang/dir/class client-side if needed — - // suppressHydrationWarning avoids a noisy (harmless) mismatch warning for that swap. + // پوسته پیش‌فرض سمت سرور: فارسی + حالت روشن (Light). + // ThemeProvider/LanguageProvider بعد از mount شدن، اگر کاربر قبلاً تم/زبان + // دیگری را انتخاب کرده بود (ذخیره‌شده در localStorage)، آن را روی اعمال + // می‌کنند؛ suppressHydrationWarning جلوی هشدار بی‌ضرر عدم‌تطابق را می‌گیرد. return ( - + {children} diff --git a/app/page.jsx b/app/page.jsx index 39c1da9..de941e1 100644 --- a/app/page.jsx +++ b/app/page.jsx @@ -1,6 +1,7 @@ import Header from '@/components/Header'; import HeroSection from '@/components/HeroSection'; import HorizontalShowcase from '@/components/HorizontalShowcase'; +import TrustedBySection from '@/components/TrustedBySection'; import FeaturesSection from '@/components/FeaturesSection'; import AboutSection from '@/components/AboutSection'; import CatalogSection from '@/components/CatalogSection'; @@ -12,36 +13,40 @@ import Footer from '@/components/Footer'; import CartDrawer from '@/components/CartDrawer'; import ChatWidget from '@/components/ChatWidget'; +// چیدمان کامل صفحه اصلی. ترتیب سکشن‌ها از بالا به پایین: +// هیرو (اسلایدر) -> ویترین محصولات (اسکرول افقی + ویدیو) -> مشتریان ما -> +// مزایا -> درباره ما -> کاتالوگ -> مکان‌های نصب -> ویدیوی معرفی -> نقشه -> +// فرم تیکت -> فوتر. همه‌ی سکشن‌ها به‌جز هیرو و ویترین محصولات، رنگ زمینه‌شان +// به‌ترتیب بین سه تن روشن/متوسط/برجسته (bg-page / bg-surface / bg-elevated) +// می‌چرخد تا هنگام اسکرول یک تغییر رنگ ظریف حس شود. export default function HomePage() { - return ( -
+ return ( +
+
+ + -
+ - + - + - + - + - + - + - + - + - + - - - - -
- -
- ); +
+
+ ); } diff --git a/app/providers.jsx b/app/providers.jsx index 0320a39..f1a5178 100644 --- a/app/providers.jsx +++ b/app/providers.jsx @@ -1,5 +1,7 @@ 'use client'; +// پل ارتباطی بین Redux، حالت تیره/روشن، و زبان فا/en با تنظیمات ConfigProvider آنت‌دیزاین. + import { Provider as ReduxProvider } from 'react-redux'; import { ConfigProvider, theme as antdThemeApi } from 'antd'; import faIR from 'antd/locale/fa_IR'; diff --git a/components/AboutSection.jsx b/components/AboutSection.jsx index 1b9c1aa..57857b4 100644 --- a/components/AboutSection.jsx +++ b/components/AboutSection.jsx @@ -1,5 +1,7 @@ 'use client'; +// سکشن معرفی شرکت با متن بلند؛ به‌خاطر طولانی بودن متن، یک دکمه «ادامه مطلب» برای باز/بسته کردن دارد. + import { useState } from 'react'; import { useLanguage } from '@/app/context/LanguageContext'; @@ -9,7 +11,7 @@ export default function AboutSection() { const visibleParagraphs = expanded ? t.about.paragraphs : t.about.paragraphs.slice(0, 2); return ( -
+
{t.about.eyebrow}

diff --git a/components/CartDrawer.jsx b/components/CartDrawer.jsx index e19bed2..1df8152 100644 --- a/components/CartDrawer.jsx +++ b/components/CartDrawer.jsx @@ -1,5 +1,7 @@ 'use client'; +// کشوی (Drawer) سبد خرید: نمایش آیتم‌های انتخابی، تغییر تعداد، جمع کل، و دکمه پرداخت (placeholder). + import { Drawer, Button, Empty, InputNumber, message } from 'antd'; import { DeleteOutlined } from '@ant-design/icons'; import { useSelector, useDispatch } from 'react-redux'; diff --git a/components/CatalogSection.jsx b/components/CatalogSection.jsx index a6704e3..c4ef1b8 100644 --- a/components/CatalogSection.jsx +++ b/components/CatalogSection.jsx @@ -1,5 +1,8 @@ 'use client'; +// کاتالوگ محصولات جانبی (زعفران، تنقلات، نوشیدنی و ...) که مشتریان دستگاه با قیمت کمتر از بازار +// می‌توانند سفارش دهند. دکمه «افزودن به سفارش» از همان سبد خرید Redux دستگاه‌ها استفاده می‌کند. + import { Button, message } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; import { useDispatch } from 'react-redux'; @@ -29,7 +32,7 @@ export default function CatalogSection() { }; return ( -
+
{t.catalog.eyebrow} diff --git a/components/ChatWidget.jsx b/components/ChatWidget.jsx index d6544dd..03a6e0a 100644 --- a/components/ChatWidget.jsx +++ b/components/ChatWidget.jsx @@ -1,5 +1,7 @@ 'use client'; +// آیکون شناور چت گوشه صفحه؛ با کلیک یک مودال گفتگوی ساده باز می‌شود (فعلاً بدون اتصال به بک‌اند واقعی). + import { useEffect, useRef, useState } from 'react'; import { Modal, Input, Button, Tooltip } from 'antd'; import { MessageOutlined, SendOutlined, CloseOutlined } from '@ant-design/icons'; diff --git a/components/FeaturesSection.jsx b/components/FeaturesSection.jsx index 12cf2fa..71777b1 100644 --- a/components/FeaturesSection.jsx +++ b/components/FeaturesSection.jsx @@ -1,5 +1,7 @@ 'use client'; +// نوار مزایا: ضمانت کالا، مشاوره رایگان، خرید اقساطی، بسته‌بندی ایمن. + import { SafetyCertificateOutlined, CustomerServiceOutlined, @@ -19,7 +21,7 @@ export default function FeaturesSection() { const { t } = useLanguage(); return ( -
+
{t.features.map((feature) => { const Icon = ICONS[feature.icon]; diff --git a/components/FloatingItems.jsx b/components/FloatingItems.jsx index 78a8fc3..e312870 100644 --- a/components/FloatingItems.jsx +++ b/components/FloatingItems.jsx @@ -1,5 +1,7 @@ 'use client'; +// رندر آیتم‌های شناور (چیپس/نوشابه) دور دستگاه با حرکت تصادفی و ملایم GSAP. + import { useEffect, useRef } from 'react'; import { gsap } from 'gsap'; diff --git a/components/Footer.jsx b/components/Footer.jsx index 73b2c59..1337775 100644 --- a/components/Footer.jsx +++ b/components/Footer.jsx @@ -1,5 +1,7 @@ 'use client'; +// فوتر سایت: درباره ما کوتاه، لینک‌های شبکه اجتماعی، دسترسی سریع، و اطلاعات تماس/آدرس. + import { PhoneOutlined, EnvironmentOutlined } from '@ant-design/icons'; import { useLanguage } from '@/app/context/LanguageContext'; @@ -8,7 +10,7 @@ export default function Footer() { const c = t.contactInfo; return ( -