From 42dd0595e55a4e869926c79b926386a557d2cdff Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sat, 8 Aug 2026 18:42:12 +0330 Subject: [PATCH] Connect menu to Meshkee store-items API for oaktasty.com. Replace hardcoded products with live tenant catalog data so the menu stays in sync with the backend. Co-authored-by: Cursor --- .env.example | 2 + next.config.ts | 6 + src/app/page.tsx | 6 +- src/components/MenuHome.tsx | 60 +++++-- src/components/ProductModal.tsx | 22 ++- src/data/products.ts | 295 +------------------------------- src/lib/api/categories.ts | 13 ++ src/lib/api/client.ts | 18 ++ src/lib/api/config.ts | 5 + src/lib/api/products.ts | 23 +++ src/lib/api/store-items.ts | 23 +++ src/lib/api/types.ts | 70 ++++++++ src/lib/menu.ts | 132 ++++++++++++++ 13 files changed, 361 insertions(+), 314 deletions(-) create mode 100644 .env.example create mode 100644 src/lib/api/categories.ts create mode 100644 src/lib/api/client.ts create mode 100644 src/lib/api/config.ts create mode 100644 src/lib/api/products.ts create mode 100644 src/lib/api/store-items.ts create mode 100644 src/lib/api/types.ts create mode 100644 src/lib/menu.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c2605fb --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_API_BASE_URL=https://api.meshkee.com/api/v1 +NEXT_PUBLIC_DOMAIN=oaktasty.com diff --git a/next.config.ts b/next.config.ts index fedd784..cfd95ab 100644 --- a/next.config.ts +++ b/next.config.ts @@ -5,6 +5,12 @@ const nextConfig: NextConfig = { dangerouslyAllowSVG: true, contentDispositionType: "attachment", contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", + remotePatterns: [ + { + protocol: "https", + hostname: "meshkee-storage.sas.amin.parminstorage.ir", + }, + ], }, }; diff --git a/src/app/page.tsx b/src/app/page.tsx index 3c8f89c..bd17b84 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,7 @@ import { MenuHome } from "@/components/MenuHome"; +import { getMenuSections } from "@/lib/menu"; -export default function HomePage() { - return ; +export default async function HomePage() { + const sections = await getMenuSections(); + return ; } diff --git a/src/components/MenuHome.tsx b/src/components/MenuHome.tsx index bd233f2..8a07b06 100644 --- a/src/components/MenuHome.tsx +++ b/src/components/MenuHome.tsx @@ -4,26 +4,36 @@ import Image from "next/image"; import { useEffect, useRef, useState } from "react"; import { formatPrice, - menuSections, type CategoryId, + type MenuSection, type Product, } from "@/data/products"; import { ProductModal } from "@/components/ProductModal"; import { SiteChrome } from "@/components/SiteChrome"; -export function MenuHome() { +type MenuHomeProps = { + sections: MenuSection[]; +}; + +export function MenuHome({ sections }: MenuHomeProps) { const [activeCategory, setActiveCategory] = useState( - menuSections[0]?.id ?? "hot-drinks", + sections[0]?.id ?? "", ); const [selectedProduct, setSelectedProduct] = useState(null); const tabRefs = useRef>({}); useEffect(() => { - const sections = menuSections + if (!sections.some((section) => section.id === activeCategory)) { + setActiveCategory(sections[0]?.id ?? ""); + } + }, [activeCategory, sections]); + + useEffect(() => { + const observed = sections .map((section) => document.getElementById(`category-${section.id}`)) .filter((el): el is HTMLElement => Boolean(el)); - if (sections.length === 0) return; + if (observed.length === 0) return; const observer = new IntersectionObserver( (entries) => { @@ -49,9 +59,9 @@ export function MenuHome() { }, ); - sections.forEach((section) => observer.observe(section)); + observed.forEach((section) => observer.observe(section)); return () => observer.disconnect(); - }, []); + }, [sections]); useEffect(() => { const activeTab = tabRefs.current[activeCategory]; @@ -70,11 +80,21 @@ export function MenuHome() { setActiveCategory(id); }; + if (sections.length === 0) { + return ( + +
+

هنوز آیتمی در منو ثبت نشده است.

+
+
+ ); + } + return (