This commit is contained in:
Hamed
2026-08-02 15:31:09 +04:00
parent 85f3f1c5f5
commit 183769d198
11 changed files with 262 additions and 257 deletions
+28 -18
View File
@@ -3,22 +3,15 @@
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update"> <list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/globals.css" beforeDir="false" afterPath="$PROJECT_DIR$/app/globals.css" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/context/LanguageContext.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/app/context/LanguageContext.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/components/Header.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/Header.jsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/context/ThemeContext.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/app/context/ThemeContext.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/components/HeroSection.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/HeroSection.jsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/layout.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/app/layout.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/data/content.js" beforeDir="false" afterPath="$PROJECT_DIR$/data/content.js" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/providers.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/app/providers.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/jsconfig.json" beforeDir="false" afterPath="$PROJECT_DIR$/jsconfig.json" afterDir="false" /> <change beforePath="$PROJECT_DIR$/components/HorizontalShowcase.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/HorizontalShowcase.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/logo.svg" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/public/images/machine-slide-0.png" beforeDir="false" afterPath="$PROJECT_DIR$/public/images/machine-slide-0.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/machine-35-base.svg" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/public/images/machine-slide-1.png" beforeDir="false" afterPath="$PROJECT_DIR$/public/images/machine-slide-1.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/machine-35-white.svg" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/public/video/company-intro-placeholder.mp4" beforeDir="false" afterPath="$PROJECT_DIR$/public/video/company-intro-placeholder.mp4" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/machine-48-base.svg" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/public/video/showcase-placeholder.mp4" beforeDir="false" afterPath="$PROJECT_DIR$/public/video/showcase-placeholder.mp4" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/machine-48-white.svg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/machine-60-base.svg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/machine-60-white.svg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/snacks/chips-1.svg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/snacks/chips-2.svg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/snacks/soda-1.svg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/public/images/snacks/soda-2.svg" beforeDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -51,6 +44,7 @@
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;, &quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;, &quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;, &quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;javascript.preferred.runtime.type.id&quot;: &quot;node&quot;,
&quot;js.debugger.nextJs.config.created.client&quot;: &quot;true&quot;, &quot;js.debugger.nextJs.config.created.client&quot;: &quot;true&quot;,
&quot;js.debugger.nextJs.config.created.server&quot;: &quot;true&quot;, &quot;js.debugger.nextJs.config.created.server&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Project/GitHubRepo/robot-market-landing&quot;, &quot;last_opened_file_path&quot;: &quot;D:/Project/GitHubRepo/robot-market-landing&quot;,
@@ -99,7 +93,7 @@
<workItem from="1785583486761" duration="9000" /> <workItem from="1785583486761" duration="9000" />
<workItem from="1785583517424" duration="145000" /> <workItem from="1785583517424" duration="145000" />
<workItem from="1785645096071" duration="26000" /> <workItem from="1785645096071" duration="26000" />
<workItem from="1785646314677" duration="10534000" /> <workItem from="1785646314677" duration="16547000" />
</task> </task>
<task id="LOCAL-00001" summary="update"> <task id="LOCAL-00001" summary="update">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -141,7 +135,23 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1785646373594</updated> <updated>1785646373594</updated>
</task> </task>
<option name="localTasksCounter" value="6" /> <task id="LOCAL-00006" summary="update">
<option name="closed" value="true" />
<created>1785661529816</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1785661529816</updated>
</task>
<task id="LOCAL-00007" summary="update">
<option name="closed" value="true" />
<created>1785661545228</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1785661545228</updated>
</task>
<option name="localTasksCounter" value="8" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
+4 -5
View File
@@ -1,13 +1,12 @@
'use client'; 'use client';
// context زبان سایت: نگه‌داری زبان فعلی (fa/en)، ذخیره در localStorage، و تنظیم جهت صفحه (rtl/ltr).
import { createContext, useContext, useEffect, useState } from 'react'; import {createContext, useContext, useEffect, useState} from 'react';
import { content } from '@/data/content'; import {content} from '@/data/content';
const LanguageContext = createContext(null); const LanguageContext = createContext(null);
export function LanguageProvider({ children }) { export function LanguageProvider({children}) {
const [locale, setLocale] = useState('fa'); const [locale, setLocale] = useState('fa');
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
@@ -28,7 +27,7 @@ export function LanguageProvider({ children }) {
return ( return (
<LanguageContext.Provider <LanguageContext.Provider
value={{ locale, setLocale, toggleLocale, t: content[locale], dir: content[locale].dir, mounted }} value={{locale, setLocale, toggleLocale, t: content[locale], dir: content[locale].dir, mounted}}
> >
{children} {children}
</LanguageContext.Provider> </LanguageContext.Provider>
+6 -9
View File
@@ -1,18 +1,15 @@
'use client'; 'use client';
import { createContext, useContext, useEffect, useState } from 'react'; import {createContext, useContext, useEffect, useState} from 'react';
const ThemeContext = createContext(null); const ThemeContext = createContext(null);
export function ThemeProvider({ children }) { export function ThemeProvider({children}) {
// پیش‌فرض سایت حالا «روشن» (light) است. اگر کاربر قبلاً تم را عوض کرده باشد
// (ذخیره‌شده در localStorage) همان ترجیح رو حفظ می‌کنیم؛ در غیر این صورت فقط
// اگر سیستم کاربر صراحتاً روی حالت تیره تنظیم شده باشد، تیره را انتخاب می‌کنیم.
const [theme, setTheme] = useState('light'); const [theme, setTheme] = useState('light');
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
// On mount: read saved preference, else fall back to system preference
// (but default stays light unless the OS explicitly prefers dark).
useEffect(() => { useEffect(() => {
const saved = window.localStorage.getItem('rm-theme'); const saved = window.localStorage.getItem('rm-theme');
if (saved === 'light' || saved === 'dark') { if (saved === 'light' || saved === 'dark') {
@@ -23,7 +20,7 @@ export function ThemeProvider({ children }) {
setMounted(true); setMounted(true);
}, []); }, []);
// Reflect theme on <html> class + persist.
useEffect(() => { useEffect(() => {
const root = document.documentElement; const root = document.documentElement;
root.classList.remove('light', 'dark'); root.classList.remove('light', 'dark');
@@ -34,7 +31,7 @@ export function ThemeProvider({ children }) {
const toggleTheme = () => setTheme((t) => (t === 'dark' ? 'light' : 'dark')); const toggleTheme = () => setTheme((t) => (t === 'dark' ? 'light' : 'dark'));
return ( return (
<ThemeContext.Provider value={{ theme, setTheme, toggleTheme, mounted }}> <ThemeContext.Provider value={{theme, setTheme, toggleTheme, mounted}}>
{children} {children}
</ThemeContext.Provider> </ThemeContext.Provider>
); );
+3 -6
View File
@@ -1,4 +1,4 @@
import { Vazirmatn } from 'next/font/google'; import {Vazirmatn} from 'next/font/google';
import './globals.css'; import './globals.css';
import Providers from './providers'; import Providers from './providers';
@@ -14,11 +14,8 @@ export const metadata = {
'روبات مارکت؛ دستگاه‌های فروش خودکار هوشمند با مدیریت آنلاین، تحویل نرم و طراحی مدرن.', 'روبات مارکت؛ دستگاه‌های فروش خودکار هوشمند با مدیریت آنلاین، تحویل نرم و طراحی مدرن.',
}; };
export default function RootLayout({ children }) { export default function RootLayout({children}) {
// پوسته پیش‌فرض سمت سرور: فارسی + حالت روشن (Light).
// ThemeProvider/LanguageProvider بعد از mount شدن، اگر کاربر قبلاً تم/زبان
// دیگری را انتخاب کرده بود (ذخیره‌شده در localStorage)، آن را روی <html> اعمال
// می‌کنند؛ suppressHydrationWarning جلوی هشدار بی‌ضرر عدم‌تطابق را می‌گیرد.
return ( return (
<html lang="fa" dir="rtl" className={`${vazirmatn.variable} light`} suppressHydrationWarning> <html lang="fa" dir="rtl" className={`${vazirmatn.variable} light`} suppressHydrationWarning>
<body className="bg-page text-primary antialiased transition-colors duration-300"> <body className="bg-page text-primary antialiased transition-colors duration-300">
+11 -11
View File
@@ -2,17 +2,17 @@
// پل ارتباطی بین Redux، حالت تیره/روشن، و زبان فا/en با تنظیمات ConfigProvider آنت‌دیزاین. // پل ارتباطی بین Redux، حالت تیره/روشن، و زبان فا/en با تنظیمات ConfigProvider آنت‌دیزاین.
import { Provider as ReduxProvider } from 'react-redux'; import {Provider as ReduxProvider} from 'react-redux';
import { ConfigProvider, theme as antdThemeApi } from 'antd'; import {ConfigProvider, theme as antdThemeApi} from 'antd';
import faIR from 'antd/locale/fa_IR'; import faIR from 'antd/locale/fa_IR';
import enUS from 'antd/locale/en_US'; import enUS from 'antd/locale/en_US';
import { store } from '@/store/store'; import {store} from '@/store/store';
import { ThemeProvider, useTheme } from './context/ThemeContext'; import {ThemeProvider, useTheme} from './context/ThemeContext';
import { LanguageProvider, useLanguage } from './context/LanguageContext'; import {LanguageProvider, useLanguage} from './context/LanguageContext';
function AntdBridge({ children }) { function AntdBridge({children}) {
const { theme } = useTheme(); const {theme} = useTheme();
const { locale, dir } = useLanguage(); const {locale, dir} = useLanguage();
const antdTheme = { const antdTheme = {
algorithm: theme === 'dark' ? antdThemeApi.darkAlgorithm : antdThemeApi.defaultAlgorithm, algorithm: theme === 'dark' ? antdThemeApi.darkAlgorithm : antdThemeApi.defaultAlgorithm,
@@ -28,8 +28,8 @@ function AntdBridge({ children }) {
colorTextSecondary: theme === 'dark' ? 'rgba(230,247,244,0.7)' : 'rgba(4,35,31,0.65)', colorTextSecondary: theme === 'dark' ? 'rgba(230,247,244,0.7)' : 'rgba(4,35,31,0.65)',
}, },
components: { components: {
Button: { controlHeight: 44, fontWeight: 600 }, Button: {controlHeight: 44, fontWeight: 600},
Badge: { colorError: '#ff6b57' }, Badge: {colorError: '#ff6b57'},
Modal: { Modal: {
contentBg: theme === 'dark' ? '#0c2b24' : '#ffffff', contentBg: theme === 'dark' ? '#0c2b24' : '#ffffff',
headerBg: theme === 'dark' ? '#0c2b24' : '#ffffff', headerBg: theme === 'dark' ? '#0c2b24' : '#ffffff',
@@ -48,7 +48,7 @@ function AntdBridge({ children }) {
); );
} }
export default function Providers({ children }) { export default function Providers({children}) {
return ( return (
<ReduxProvider store={store}> <ReduxProvider store={store}>
<ThemeProvider> <ThemeProvider>
+15 -13
View File
@@ -4,16 +4,16 @@
// و یک ویدیوی پس‌زمینه دارد که با پیشرفت اسکرول جلو/عقب می‌رود. طبق درخواست کاربر، این سکشن // و یک ویدیوی پس‌زمینه دارد که با پیشرفت اسکرول جلو/عقب می‌رود. طبق درخواست کاربر، این سکشن
// و هیرو از چرخه‌ی رنگ‌بندی متناوب بقیه سکشن‌ها مستثنا هستند. // و هیرو از چرخه‌ی رنگ‌بندی متناوب بقیه سکشن‌ها مستثنا هستند.
import { useEffect, useRef, useState } from 'react'; import {useEffect, useRef, useState} from 'react';
import { gsap } from 'gsap'; import {gsap} from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger'; import {ScrollTrigger} from 'gsap/ScrollTrigger';
import { Modal, Button, message } from 'antd'; import {Modal, Button, message} from 'antd';
import { ShoppingCartOutlined } from '@ant-design/icons'; import {ShoppingCartOutlined} from '@ant-design/icons';
import { useDispatch } from 'react-redux'; import {useDispatch} from 'react-redux';
import VendingCard from './VendingCard'; import VendingCard from './VendingCard';
import { showcaseVideoSrc } from '@/data/content'; import {showcaseVideoSrc} from '@/data/content';
import { useLanguage } from '@/app/context/LanguageContext'; import {useLanguage} from '@/app/context/LanguageContext';
import { addToCart } from '@/store/cartSlice'; import {addToCart} from '@/store/cartSlice';
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
@@ -22,7 +22,7 @@ export default function HorizontalShowcase() {
const trackRef = useRef(null); const trackRef = useRef(null);
const videoRef = useRef(null); const videoRef = useRef(null);
const dispatch = useDispatch(); const dispatch = useDispatch();
const { t } = useLanguage(); const {t} = useLanguage();
const [quickViewMachine, setQuickViewMachine] = useState(null); const [quickViewMachine, setQuickViewMachine] = useState(null);
const [videoAvailable, setVideoAvailable] = useState(true); const [videoAvailable, setVideoAvailable] = useState(true);
@@ -102,7 +102,7 @@ export default function HorizontalShowcase() {
<div ref={trackRef} className="horizontal-track relative z-10" dir="ltr"> <div ref={trackRef} className="horizontal-track relative z-10" dir="ltr">
{t.machines.map((machine) => ( {t.machines.map((machine) => (
<VendingCard key={machine.id} machine={machine} onQuickView={setQuickViewMachine} /> <VendingCard key={machine.id} machine={machine} onQuickView={setQuickViewMachine}/>
))} ))}
</div> </div>
@@ -115,10 +115,12 @@ export default function HorizontalShowcase() {
> >
{quickViewMachine && ( {quickViewMachine && (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<img src={quickViewMachine.image} alt={quickViewMachine.name} className="w-full h-64 object-contain" /> <img src={quickViewMachine.image} alt={quickViewMachine.name}
className="w-full h-64 object-contain"/>
<p className="text-secondary leading-7">{quickViewMachine.description}</p> <p className="text-secondary leading-7">{quickViewMachine.description}</p>
<p className="text-xl font-bold text-primary">{quickViewMachine.priceLabel}</p> <p className="text-xl font-bold text-primary">{quickViewMachine.priceLabel}</p>
<Button type="primary" size="large" icon={<ShoppingCartOutlined />} onClick={handleModalAddToCart}> <Button type="primary" size="large" icon={<ShoppingCartOutlined/>}
onClick={handleModalAddToCart}>
{t.machineActions.addToCart} {t.machineActions.addToCart}
</Button> </Button>
</div> </div>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.
Binary file not shown.