diff --git a/components/Footer.tsx b/components/Footer.tsx
index c731efe..d4b1bf5 100644
--- a/components/Footer.tsx
+++ b/components/Footer.tsx
@@ -2,12 +2,13 @@
// Company information, quick links, social links, and contact details.
+import {commerceCopy} from '@/data/commerce';
import Link from 'next/link';
import { PhoneOutlined, EnvironmentOutlined } from '@ant-design/icons';
import { useLanguage } from '@/app/context/LanguageContext';
export default function Footer() {
- const { t } = useLanguage();
+ const { t, locale } = useLanguage();
const c = t.contactInfo;
return (
@@ -35,7 +36,7 @@ export default function Footer() {
{t.footer.quickLinks}
- {t.nav.home}
- - {t.nav.showcase}
+ - {commerceCopy[locale].allProducts}
- {t.nav.about}
- {t.nav.locations}
diff --git a/components/Header.tsx b/components/Header.tsx
index 28c48a8..a720a0a 100644
--- a/components/Header.tsx
+++ b/components/Header.tsx
@@ -11,19 +11,20 @@ import {ShoppingCartOutlined, MenuOutlined, CloseOutlined, UserOutlined} from '@
import {useAppSelector} from '@/store/hooks';
import {selectCartCount} from '@/store/cartSlice';
import {useLanguage} from '@/app/context/LanguageContext';
+import {commerceCopy} from '@/data/commerce';
import ThemeToggle from './ThemeToggle';
import LanguageToggle from './LanguageToggle';
export default function Header() {
const pathname = usePathname();
const cartCount = useAppSelector(selectCartCount);
- const {t} = useLanguage();
+ const {t, locale} = useLanguage();
const [scrolled, setScrolled] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const navLinks = [
{href: '/#hero', label: t.nav.home},
- {href: '/#showcase', label: t.nav.showcase},
+ {href: '/products', label: commerceCopy[locale].allProducts},
{href: '/#about', label: t.nav.about},
{href: '/#locations', label: t.nav.locations},
];
diff --git a/components/HorizontalShowcase.tsx b/components/HorizontalShowcase.tsx
index 86c4e32..e3aee16 100644
--- a/components/HorizontalShowcase.tsx
+++ b/components/HorizontalShowcase.tsx
@@ -1,10 +1,13 @@
'use client';
import {useEffect, useRef, useState} from 'react';
+import Link from 'next/link';
import {gsap} from 'gsap';
import {ScrollTrigger} from 'gsap/ScrollTrigger';
import VendingCard from './VendingCard';
import {showcaseVideoSrc} from '@/data/content';
+import {featuredMachines} from '@/data/products';
+import {commerceCopy} from '@/data/commerce';
import {useLanguage} from '@/app/context/LanguageContext';
gsap.registerPlugin(ScrollTrigger);
@@ -13,7 +16,8 @@ export default function HorizontalShowcase() {
const sectionRef = useRef
(null);
const trackRef = useRef(null);
const videoRef = useRef(null);
- const {t} = useLanguage();
+ const {t, locale} = useLanguage();
+ const copy = commerceCopy[locale];
const [videoAvailable, setVideoAvailable] = useState(true);
useEffect(() => {
@@ -90,8 +94,17 @@ export default function HorizontalShowcase() {
/>
-
- {t.machines.map((machine) => (
+
+
+
{copy.featuredProducts}
+ {copy.allProducts}
+
+
+
+
+ {featuredMachines(t.machines).map((machine) => (
))}
diff --git a/components/MobileShowcase.tsx b/components/MobileShowcase.tsx
index 1e50786..43fe3c1 100644
--- a/components/MobileShowcase.tsx
+++ b/components/MobileShowcase.tsx
@@ -1,6 +1,7 @@
'use client';
import Link from 'next/link';
+import {featuredMachines} from '@/data/products';
import {useLanguage} from '@/app/context/LanguageContext';
import {commerceCopy} from '@/data/commerce';
import QuantityControl from './QuantityControl';
@@ -10,9 +11,9 @@ export default function MobileShowcase() {
return (
-
- {t.machines.map(machine => (
-
+
+ {featuredMachines(t.machines).map(machine => (
+
diff --git a/components/ProductDetails.tsx b/components/ProductDetails.tsx
index 48b6e21..8d2c6da 100644
--- a/components/ProductDetails.tsx
+++ b/components/ProductDetails.tsx
@@ -19,7 +19,7 @@ export default function ProductDetails({id}: {id: string}) {
diff --git a/components/ProductsPage.tsx b/components/ProductsPage.tsx
new file mode 100644
index 0000000..078e2f1
--- /dev/null
+++ b/components/ProductsPage.tsx
@@ -0,0 +1,69 @@
+'use client';
+
+import Link from 'next/link';
+import {usePathname, useRouter, useSearchParams} from 'next/navigation';
+import {useLanguage} from '@/app/context/LanguageContext';
+import {commerceCopy} from '@/data/commerce';
+import {filterMachines, productCategories} from '@/data/products';
+import QuantityControl from './QuantityControl';
+
+export default function ProductsPage() {
+ const {t, locale} = useLanguage();
+ const copy = commerceCopy[locale];
+ const searchParams = useSearchParams();
+ const pathname = usePathname();
+ const router = useRouter();
+ const category = searchParams.get('category') ?? '';
+ const products = filterMachines(t.machines, category);
+ const knownCategory = productCategories.some(item => item.id === category);
+
+ const setCategory = (value: string) => {
+ const params = new URLSearchParams(searchParams.toString());
+ if (value) params.set('category', value);
+ else params.delete('category');
+ // Preserve other query parameters and allow browser Back to restore filters.
+ const query = params.toString();
+ router.push(query ? `${pathname}?${query}` : pathname, {scroll: false});
+ };
+
+ return (
+
+ {copy.allProducts}
+ {copy.productsDescription}
+
+
+
+
+
+ {category &&
}
+
+ {products.length.toLocaleString(locale === 'fa' ? 'fa-IR' : 'en-US')} {copy.results}
+
+
+ {products.length ? (
+
+ {products.map(machine => (
+
+
+
+
+ {productCategories.find(item => item.id === machine.category)?.label[locale]}
+ {machine.name}
+ {machine.description}
+ {machine.priceLabel}
+
+
+ {copy.details}
+
+
+ ))}
+
+ ) : {copy.noProducts}
}
+
+ );
+}
diff --git a/components/ShowcaseSection.tsx b/components/ShowcaseSection.tsx
index 3abab08..a75e18a 100644
--- a/components/ShowcaseSection.tsx
+++ b/components/ShowcaseSection.tsx
@@ -1,12 +1,17 @@
'use client';
import {useEffect, useState} from 'react';
+import Link from 'next/link';
+import {useLanguage} from '@/app/context/LanguageContext';
+import {commerceCopy} from '@/data/commerce';
import HorizontalShowcase from './HorizontalShowcase';
import MobileShowcase from './MobileShowcase';
const MOBILE_BREAKPOINT = 768;
export default function ShowcaseSection() {
+ const {locale} = useLanguage();
+ const copy = commerceCopy[locale];
const [isMobile, setIsMobile] = useState
(null);
useEffect(() => {
@@ -23,6 +28,15 @@ export default function ShowcaseSection() {
}, []);
if (isMobile === null) return null;
+ if (!isMobile) return ;
- return isMobile ? : ;
+ return (
+ <>
+
+
{copy.featuredProducts}
+ {copy.allProducts}
+
+
+ >
+ );
}
diff --git a/data/commerce.ts b/data/commerce.ts
index 3214703..bb07001 100644
--- a/data/commerce.ts
+++ b/data/commerce.ts
@@ -1,6 +1,15 @@
import type {Locale} from '@/types/domain';
const fa = {
+ allProducts: 'همه محصولات',
+ featuredProducts: 'محصولات منتخب',
+ productsDescription: 'دستگاه مناسب خود را انتخاب کنید و مشخصات آن را بررسی کنید.',
+ category: 'دستهبندی محصولات',
+ allCategories: 'همه دستهبندیها',
+ clearFilters: 'پاک کردن فیلتر',
+ noProducts: 'محصولی با این دستهبندی پیدا نشد.',
+ unknownCategory: 'دستهبندی نامعتبر',
+ results: 'محصول',
details: 'جزئیات محصول',
increase: 'افزایش تعداد',
decrease: 'کاهش تعداد',
@@ -27,6 +36,15 @@ const fa = {
export const commerceCopy: Record> = {
fa,
en: {
+ allProducts: 'All products',
+ featuredProducts: 'Featured products',
+ productsDescription: 'Choose a machine and explore its specifications.',
+ category: 'Product category',
+ allCategories: 'All categories',
+ clearFilters: 'Clear filter',
+ noProducts: 'No products found in this category.',
+ unknownCategory: 'Unknown category',
+ results: 'products',
details: 'Product details',
increase: 'Increase quantity',
decrease: 'Decrease quantity',
diff --git a/data/content.ts b/data/content.ts
index 647afe5..7287b74 100644
--- a/data/content.ts
+++ b/data/content.ts
@@ -9,6 +9,7 @@ import type {FloatingSnack, Locale, Machine, SiteContent} from '@/types/domain';
const machinesFa: Machine[] = [
{
id: 'rm-35',
+ category: 'warm-vending',
name: 'روبات مارکت ۳۵ کانال',
shortLabel: '۳۵ کانال',
priceLabel: '4,180,000,000 ریال',
@@ -21,6 +22,7 @@ const machinesFa: Machine[] = [
},
{
id: 'rm-48',
+ category: 'warm-vending',
name: 'روبات مارکت ۴۸ کانال',
shortLabel: '۴۸ کانال',
priceLabel: '4,980,000,000 ریال',
@@ -33,6 +35,7 @@ const machinesFa: Machine[] = [
},
{
id: 'rm-60',
+ category: 'warm-vending',
name: 'روبات مارکت ۶۰ کانال',
shortLabel: '۶۰ کانال',
priceLabel: '5,480,000,000 ریال',
@@ -48,6 +51,7 @@ const machinesFa: Machine[] = [
const machinesEn: Machine[] = [
{
id: 'rm-35',
+ category: 'warm-vending',
name: 'Robot Market RM-35',
shortLabel: '35 Channels',
priceLabel: '4,180,000,000 Rials',
@@ -60,6 +64,7 @@ const machinesEn: Machine[] = [
},
{
id: 'rm-48',
+ category: 'warm-vending',
name: 'Robot Market RM-48',
shortLabel: '48 Channels',
priceLabel: '4,980,000,000 Rials',
@@ -72,6 +77,7 @@ const machinesEn: Machine[] = [
},
{
id: 'rm-60',
+ category: 'warm-vending',
name: 'Robot Market RM-60',
shortLabel: '60 Channels',
priceLabel: '5,480,000,000 Rials',
diff --git a/data/products.ts b/data/products.ts
new file mode 100644
index 0000000..c99d729
--- /dev/null
+++ b/data/products.ts
@@ -0,0 +1,16 @@
+import type {Locale, Machine, ProductCategory} from '@/types/domain';
+
+export const productCategories: {id: ProductCategory; label: Record}[] = [
+ {id: 'warm-vending', label: {fa: 'وندینگ گرم', en: 'Warm vending'}},
+];
+
+// Keep the home showcase independent of future catalog additions.
+const featuredIds = ['rm-35', 'rm-48', 'rm-60'];
+
+export function featuredMachines(machines: Machine[]): Machine[] {
+ return featuredIds.flatMap(id => machines.filter(machine => machine.id === id));
+}
+
+export function filterMachines(machines: Machine[], category: string): Machine[] {
+ return category ? machines.filter(machine => machine.category === category) : machines;
+}
diff --git a/types/domain.ts b/types/domain.ts
index 0e155d4..aeea99c 100644
--- a/types/domain.ts
+++ b/types/domain.ts
@@ -1,9 +1,11 @@
export type Locale = 'fa' | 'en';
export type Direction = 'rtl' | 'ltr';
export type Theme = 'light' | 'dark';
+export type ProductCategory = 'warm-vending';
export interface Machine {
id: string;
+ category: ProductCategory;
name: string;
shortLabel: string;
priceLabel: string;