commit 8724072983d5ef743d79d8532870080e108ce639 Author: alirez.br Date: Mon May 11 15:30:57 2026 +0330 first project version mrDecor diff --git a/.cursor/debug-3a1cb1.log b/.cursor/debug-3a1cb1.log new file mode 100644 index 0000000..1f3e280 --- /dev/null +++ b/.cursor/debug-3a1cb1.log @@ -0,0 +1,3 @@ +{"sessionId":"3a1cb1","location":"feature-243-gold-store.tsx","message":"Feature243GoldStore mounted","data":{"innerWidth":1920,"isMobile":false,"dir":"rtl"},"hypothesisId":"A","timestamp":1771973317022} +{"sessionId":"3a1cb1","location":"feature-243-gold-store.tsx","message":"Feature243GoldStore mounted","data":{"innerWidth":1920,"isMobile":false,"dir":"rtl"},"hypothesisId":"A","timestamp":1771973385387} +{"sessionId":"3a1cb1","location":"feature-243-gold-store.tsx","message":"Section vs cards wrapper layout","data":{"sectionWidth":1905,"wrapperWidth":1232,"leftGap":336.5,"rightGap":336.5,"viewportWidth":1920},"hypothesisId":"B","timestamp":1771973385465} diff --git a/.cursor/debug-9e190c.log b/.cursor/debug-9e190c.log new file mode 100644 index 0000000..5806981 --- /dev/null +++ b/.cursor/debug-9e190c.log @@ -0,0 +1 @@ +{"sessionId":"9e190c","location":"hero-91:secondary","message":"secondary clicked","data":{},"timestamp":1771929704065} diff --git a/.cursor/debug-b131a6.log b/.cursor/debug-b131a6.log new file mode 100644 index 0000000..0bc6cce --- /dev/null +++ b/.cursor/debug-b131a6.log @@ -0,0 +1,5 @@ +{"sessionId":"b131a6","location":"hero-247.tsx:AnimatedButton","message":"hero button inner clicked","data":{"url":"#mozakerat"},"timestamp":1771775120502,"hypothesisId":"A"} +{"sessionId":"b131a6","location":"hero-247.tsx:AnimatedButton","message":"hero anchor clicked","data":{"url":"#mozakerat","targetExists":true},"timestamp":1771775120503,"hypothesisId":"A_E"} +{"sessionId":"b131a6","location":"hero-247.tsx:AnimatedButton","message":"hero button inner clicked","data":{"url":"#rahavarha"},"timestamp":1771775210149,"hypothesisId":"A"} +{"sessionId":"b131a6","location":"hero-247.tsx:AnimatedButton","message":"hero anchor clicked","data":{"url":"#rahavarha","targetExists":true,"variant":"outline"},"timestamp":1771775210149,"hypothesisId":"A_E"} +{"sessionId":"b131a6","location":"hero-91.tsx:Link","message":"hero91 link clicked","data":{"href":"#rahavarha","targetExists":true},"timestamp":1771775671865,"hypothesisId":"B_C"} diff --git a/.cursor/debug-e841fd.log b/.cursor/debug-e841fd.log new file mode 100644 index 0000000..b68b592 --- /dev/null +++ b/.cursor/debug-e841fd.log @@ -0,0 +1,90 @@ +{"sessionId":"e841fd","timestamp":1771996173461,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996173462,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1771996183342,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771996183342,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996183537,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996183537,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771996486694,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996486696,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771996486705,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996486706,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771996487218,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1771996487219,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":0,"h":0},"vLineRect":{"w":2,"h":297}}} +{"sessionId":"e841fd","timestamp":1771996487221,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1771996502765,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996502767,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771996502780,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771996502781,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771996503292,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1771996503293,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":0,"h":0},"vLineRect":{"w":2,"h":297}}} +{"sessionId":"e841fd","timestamp":1771996503294,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1771997195052,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997195054,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771997195064,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997195066,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1771997195581,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1771997195582,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":0,"h":0},"vLineRect":{"w":2,"h":297}}} +{"sessionId":"e841fd","timestamp":1771997195583,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1771997587327,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997587328,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1771997587338,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997587339,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1771997587846,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1771997587847,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":313.90625,"h":2},"vLineRect":{"w":2,"h":309.5}}} +{"sessionId":"e841fd","timestamp":1771997587847,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1771997602265,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":2133,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997602265,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":2133}} +{"sessionId":"e841fd","timestamp":1771997616330,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997616330,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1771997707672,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1771997707675,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1771997708927,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1771997708928,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":313.90625,"h":2},"vLineRect":{"w":2,"h":309.5}}} +{"sessionId":"e841fd","timestamp":1771997708929,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1772008672649,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008672649,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772008672660,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008672660,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772008673163,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1772008673164,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":313.90625,"h":2},"vLineRect":{"w":2,"h":309.5}}} +{"sessionId":"e841fd","timestamp":1772008673165,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1772008684861,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008684861,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772008688438,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008688439,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1772008693291,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008693292,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1772008693358,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008693359,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772008955109,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008955110,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1772008956960,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":357,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008956961,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":357}} +{"sessionId":"e841fd","timestamp":1772008956988,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772008956989,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772015086456,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772015086458,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772015087518,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1772015087519,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":313.90625,"h":2},"vLineRect":{"w":2,"h":309.5}}} +{"sessionId":"e841fd","timestamp":1772015087520,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1772015089592,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772015089592,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772015092826,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772015092826,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772015096183,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772015096184,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1772016039773,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772016039774,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772016040953,"hypothesisId":"H2","location":"feature-243-gold-store.tsx:overflow","message":"section/container overflow","data":{"sectionOverflow":"visible visible visible","containerOverflow":"visible"}} +{"sessionId":"e841fd","timestamp":1772016040953,"hypothesisId":"H3","location":"feature-243-gold-store.tsx:rects","message":"line dimensions","data":{"hLineRect":{"w":313.90625,"h":2},"vLineRect":{"w":2,"h":309.5}}} +{"sessionId":"e841fd","timestamp":1772016040954,"hypothesisId":"H4","location":"feature-243-gold-store.tsx:zindex","message":"z-index line vs card","data":{"lineZ":"auto","cardZ":"10"}} +{"sessionId":"e841fd","timestamp":1772016096211,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772016096211,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} +{"sessionId":"e841fd","timestamp":1772017219980,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":357,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772017219980,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":357}} +{"sessionId":"e841fd","timestamp":1772017220300,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":430,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772017220301,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":430}} +{"sessionId":"e841fd","timestamp":1772017225619,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":357,"breakpoint":"xs","horizontalVisible":{"index0":false,"index1":false,"index2":false,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772017225619,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":357}} +{"sessionId":"e841fd","timestamp":1772017225656,"hypothesisId":"H1","location":"feature-243-gold-store.tsx:run","message":"viewport and line visibility","data":{"innerWidth":1920,"breakpoint":"2xl","horizontalVisible":{"index0":true,"index1":true,"index2":true,"index3":false}}} +{"sessionId":"e841fd","timestamp":1772017225657,"hypothesisId":"H5","location":"feature-243-gold-store.tsx:run","message":"design: index3 always hidden, index2 only 2xl","data":{"innerWidth":1920}} diff --git a/.cursor/rules/components-organization.mdc b/.cursor/rules/components-organization.mdc new file mode 100644 index 0000000..12e0531 --- /dev/null +++ b/.cursor/rules/components-organization.mdc @@ -0,0 +1,148 @@ +--- +alwaysApply: true +description: استانداردهای سازماندهی و مدیریت کامپوننت‌ها در پروژه +--- + +# استاندارد سازماندهی کامپوننت‌ها + +این پروژه از یک ساختار پوشه‌بندی استاندارد برای سازماندهی کامپوننت‌ها استفاده می‌کند. + +## ساختار پوشه‌بندی + +کامپوننت‌ها در پوشه `components/` بر اساس نوع و کاربردشان سازماندهی شده‌اند: + +### 1. `components/layout/` +کامپوننت‌های مرتبط با ساختار کلی صفحه: +- `conditional-layout.tsx` - لایه‌بندی شرطی برای صفحات +- `theme-provider.tsx` - ارائه‌دهنده تم +- `top-progress-bar.tsx` - نوار پیشرفت بالا + +### 2. `components/navigation/` +کامپوننت‌های مرتبط با ناوبری و هدر/فوتر: +- `header.tsx` - هدر اصلی +- `header-main.tsx` - هدر اصلی (نسخه tailark) +- `footer.tsx` - فوتر +- `logo.tsx` - لوگو +- `logo-main.tsx` - لوگو اصلی (نسخه tailark) +- `theme-toggle.tsx` - دکمه تغییر تم + +### 3. `components/sections/` +کامپوننت‌های بخش‌های مختلف صفحه که به زیرپوشه‌ها تقسیم می‌شوند: + +#### `sections/hero/` +بخش‌های Hero: +- `hero-product.tsx` - Hero صفحه محصول +- `hero-partners.tsx` - Hero صفحه همکاران +- `hero-home.tsx` - Hero صفحه اصلی +- `hero-section-main.tsx` - Hero اصلی (نسخه tailark) +- `hero-alternative.tsx` - Hero جایگزین + +#### `sections/feature/` +بخش‌های ویژگی‌ها: +- `feature-text-section.tsx` - بخش متن ویژگی +- `feature-grid-with-icons.tsx` - شبکه ویژگی با آیکون +- `feature-carousel-grid.tsx` - شبکه کاروسل ویژگی +- `feature-list-section.tsx` - بخش لیست ویژگی +- `feature-carousel.tsx` - کاروسل ویژگی +- `features-card-grid.tsx` - شبکه کارت ویژگی +- `features-icon-grid.tsx` - شبکه آیکون ویژگی +- `features-primary.tsx` - ویژگی‌های اصلی + +#### `sections/cta/` +بخش‌های Call-to-Action: +- `cta-product.tsx` - CTA صفحه محصول +- `cta-home.tsx` - CTA صفحه اصلی +- `cta-default.tsx` - CTA پیش‌فرض +- `cta-alternative.tsx` - CTA جایگزین + +#### `sections/other/` +سایر بخش‌های صفحه: +- `timeline-section.tsx` - بخش تایم‌لاین +- `pricing-section.tsx` - بخش قیمت‌گذاری +- `pricing-comparator.tsx` - مقایسه‌کننده قیمت +- `faqs-section.tsx` - بخش سوالات متداول +- `logo-cloud.tsx` - ابر لوگو +- `solutions-showcase.tsx` - نمایش راهکارها +- `apple-cards-carousel.tsx` - کاروسل کارت‌های اپل +- `content-section.tsx` - بخش محتوا + +### 4. `components/blog/` +کامپوننت‌های مرتبط با وبلاگ: +- `blog-blockquote.tsx` - نقل قول وبلاگ +- `blog-card.tsx` - کارت وبلاگ +- `blog-collapse-section.tsx` - بخش تاشو وبلاگ +- `blog-post-card.tsx` - کارت پست وبلاگ +- `blog-post-meta.tsx` - متادیتای پست وبلاگ +- `blog-pullquote.tsx` - نقل قول کشیده وبلاگ +- `blog-tip.tsx` - نکته وبلاگ +- `blog-toc.tsx` - فهرست مطالب وبلاگ +- `featured-posts-grid.tsx` - شبکه پست‌های ویژه + +### 5. `components/utilities/` +کامپوننت‌های کمکی: +- `scroll-to-top.tsx` - اسکرول به بالا + +### 6. `components/ui/` +کامپوننت‌های UI پایه (از shadcn/ui) - این پوشه دست نخورده باقی می‌ماند. + +### 7. `components/home-page/` +کامپوننت‌های اختصاصی صفحه اصلی - این پوشه دست نخورده باقی می‌ماند. + +### 8. `components/motion-primitives/` +پریمییتیوهای انیمیشن - این پوشه دست نخورده باقی می‌ماند. + +## قوانین نام‌گذاری + +### نام فایل‌ها +- از نام‌های توصیفی و معنادار استفاده کنید +- از camelCase برای نام فایل‌ها استفاده کنید +- نام فایل باید نوع کامپوننت را مشخص کند (مثلاً `hero-product.tsx` نه `hero47.tsx`) +- برای کامپوننت‌های مشابه، از پسوندهای واضح استفاده کنید (مثلاً `-home`, `-product`, `-alternative`) + +### نام Export ها +- نام export باید با نام فایل همخوانی داشته باشد و معنادار باشد +- از PascalCase برای نام کامپوننت‌ها استفاده کنید +- از نام‌های عددی یا غیرمعنادار (مثل Hero47، Feature55) خودداری کنید +- نام export باید نوع و کاربرد کامپوننت را مشخص کند (مثلاً `HeroProduct` نه `Hero47`) + +## قوانین Import + +هنگام import کردن کامپوننت‌ها، از مسیرهای کامل و نام‌های معنادار استفاده کنید: + +```typescript +// ✅ درست +import { HeroProduct } from '@/components/sections/hero/hero-product' +import { FeatureTextSection } from '@/components/sections/feature/feature-text-section' +import { FeatureCarousel } from '@/components/sections/feature/feature-carousel' +import Footer5 from '@/components/navigation/footer' +import CtaHome from '@/components/sections/cta/cta-home' + +// ❌ اشتباه +import { Hero47 } from '@/components/hero47' +import { Feature55 } from '@/components/feature55' +import { Feature240 } from '@/components/feature240' +``` + +## افزودن کامپوننت جدید + +هنگام افزودن کامپوننت جدید: + +1. **تعیین نوع کامپوننت**: کامپوننت شما در کدام دسته قرار می‌گیرد؟ + - Layout → `components/layout/` + - Navigation → `components/navigation/` + - Hero Section → `components/sections/hero/` + - Feature Section → `components/sections/feature/` + - CTA Section → `components/sections/cta/` + - Other Section → `components/sections/other/` + - Blog Component → `components/blog/` + - Utility → `components/utilities/` + +2. **نام‌گذاری مناسب**: نام فایل باید توصیفی باشد و نوع کامپوننت را مشخص کند. + +3. **به‌روزرسانی این فایل**: اگر دسته جدیدی ایجاد کردید، این فایل را به‌روزرسانی کنید. + +## نکات مهم + +- **پوشه‌های موجود**: پوشه‌های `ui/`, `home-page/`, و `motion-primitives/` دست نخورده باقی می‌مانند. +- **سازماندهی مداوم**: همیشه کامپوننت‌های جدید را در پوشه مناسب قرار دهید. +- **یکپارچگی**: از قرار دادن فایل‌ها در root پوشه `components/` خودداری کنید (به جز پوشه‌های خاص). diff --git a/.cursor/rules/typography-standards.mdc b/.cursor/rules/typography-standards.mdc new file mode 100644 index 0000000..780bfaa --- /dev/null +++ b/.cursor/rules/typography-standards.mdc @@ -0,0 +1,59 @@ +--- +description: استاندارد تایپوگرافی در همه صفحات – سایز، وزن و سلسله‌مراتب +alwaysApply: true +--- + +# استاندارد تایپوگرافی + +در همه صفحات از مقیاس تم و **سلسله‌مراتب تایپوگرافی** استفاده کنید: یک سطح غالب (h1)، سپس سطوح پایین‌تر برای بخش‌ها و زیربخش‌ها. + +## سلسله‌مراتب (الزامی) + +| سطح | تگ | کلاس سایز | وزن | کاربرد | +|-----|-----|------------|-----|--------| +| ۱ | h1 | `text-heading-1` | `font-bold` | **فقط یک بار در صفحه** – عنوان اصلی/ Hero | +| ۲ | h2 | `text-heading-2` | `font-semibold` | عنوان هر **بخش** (Section) | +| ۳ | h3 | `text-heading-3` | `font-semibold` | عنوان **زیربخش** یا کارت | +| ۴ | h4 | `text-heading-4` | `font-medium` | عنوان ریز (آیتم، لیست) | +| ۵ | h5 | `text-heading-5` | `font-medium` | عنوان خرد | +| متن | p/span | `text-body` | `font-normal` | متن بدن | +| فرعی | p/span | `text-body-sm` | `font-normal` | توضیح کوتاه، زیرمتن | +| برچسب | span | `text-caption` | `font-medium` | زیرنویس، label | + +قانون: **هر صفحه فقط یک h1**. بعد از آن فقط h2 برای بخش‌ها، سپس h3 برای کارت/زیربخش، و h4 برای عنوان‌های ریز. از پرش سطح (مثلاً h1 بعد h4) خودداری کنید. + +## فونت + +- **فونت اصلی**: IRANSansX (ارث‌بری از `body`). از فونت‌های دیگر (مثل `font-playfair`, `font-calSans`) استفاده نکنید. + +## نمونه درست (با رعایت سلسله‌مراتب) + +```tsx +// ✅ درست – یک h1، سپس h2 برای بخش، h3 برای کارت +

عنوان صفحه

+

عنوان بخش

+

عنوان کارت

+

متن توضیح.

+برچسب +``` + +```tsx +// ❌ اشتباه – چند h1 در صفحه، یا سایز دستی +

...

+

...

// فقط یک h1 مجاز +

...

// از text-heading-* یا text-body استفاده کن +``` + +## رنگ متن (الزامی) + +- **متن اصلی**: `text-foreground` +- **متن توضیح/فرعی**: `text-muted-foreground` +- **تأکید (قیمت، لینک، CTA)**: `text-primary` +- **متن روی پس‌زمینه تیره (overlay)**: `text-overlay-foreground` (نه `text-white`) +- از رنگ‌های هاردکد (مثل `text-white`, `text-black`, `text-gray-*`) استفاده نکنید. + +## نکات + +- تگ‌های `h1`–`h6` در `globals.css` سایز/وزن پیش‌فرض دارند؛ در صورت نیاز فقط کلاس‌های تم را اضافه کنید. +- از `text-heading-1` تا `text-caption` استفاده کنید؛ از `text-4xl`, `text-2xl` و مانند آن برای عناوین/متن بدن استفاده نکنید. +- مقیاس تایپوگرافی در `globals.css` با فونت‌های کمی کوچک‌تر و فاصله خط مناسب برای حس لوکس تعریف شده است. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..66dfd91 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,56 @@ +# Dependencies +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# Testing +coverage +.nyc_output + +# Next.js +.next +out +dist +build + +# Production +*.log + +# Misc +.DS_Store +*.pem + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Local env files +.env*.local +.env + +# Vercel +.vercel + +# TypeScript +*.tsbuildinfo +next-env.d.ts + +# IDE +.vscode +.idea +*.swp +*.swo +*~ + +# Git +.git +.gitignore + +# Docker +Dockerfile +docker-compose.yml +.dockerignore +DOCKER.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a3067dd --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# ربات تلگرام برای دریافت درخواست‌های مشاوره آنلاین +# از @BotFather ربات بسازید و توکن را اینجا قرار دهید. + +# آیدی چت (عدد) یا @username کانال/گروهی که پیام‌ها ارسال شوند. +# برای گرفتن chat_id یک پیام به ربات بفرستید و این آدرس را باز کنید: +# https://api.telegram.org/bot/getUpdates +TELEGRAM_BOT_TOKEN=1925675433:uIq2vpidQoH8YJRldjGyiQP0haoeqGOj67Q +TELEGRAM_CHAT_ID=5419302943 +TELEGRAM_URL=https://tapi.bale.ai diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb2abe1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* +!.env.example + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts +.yarn diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..a63a48f --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,120 @@ +# راهنمای استفاده از Docker + +## نصب Docker + +اگر Docker روی سیستم شما نصب نیست، می‌توانید از یکی از روش‌های زیر استفاده کنید: + +### Windows + +1. **Docker Desktop برای Windows** را دانلود و نصب کنید: + - دانلود از: https://www.docker.com/products/docker-desktop/ + - پس از نصب، Docker Desktop را اجرا کنید + - منتظر بمانید تا Docker شروع به کار کند + +2. **بررسی نصب:** + ```powershell + docker --version + docker compose version + ``` + +### Linux + +```bash +# Ubuntu/Debian +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh + +# اضافه کردن کاربر به گروه docker +sudo usermod -aG docker $USER +``` + +### macOS + +1. **Docker Desktop برای Mac** را دانلود و نصب کنید: + - دانلود از: https://www.docker.com/products/docker-desktop/ + +## ساخت و اجرای کانتینر + +### استفاده از Docker Compose (پیشنهادی) + +**نکته:** در نسخه‌های جدید Docker، از `docker compose` (با فاصله) استفاده می‌شود به جای `docker-compose` (با خط تیره). + +```bash +# ساخت و اجرای کانتینر +docker compose up -d + +# مشاهده لاگ‌ها +docker compose logs -f + +# توقف کانتینر +docker compose down + +# ساخت مجدد و اجرا +docker compose up -d --build +``` + +**اگر دستور بالا کار نکرد، از نسخه قدیمی استفاده کنید:** +```bash +docker-compose up -d +``` + +### استفاده مستقیم از Docker + +```bash +# ساخت image +docker build -t montazeran . + +# اجرای کانتینر +docker run -p 3000:3000 montazeran + +# اجرا در حالت detached +docker run -d -p 3000:3000 --name montazeran-app montazeran +``` + +## دسترسی به اپلیکیشن + +پس از اجرای کانتینر، اپلیکیشن در آدرس زیر در دسترس است: +- http://localhost:3000 + +## عیب‌یابی + +### مشکل: دستور docker شناسایی نمی‌شود + +**راه حل:** +1. مطمئن شوید Docker Desktop نصب و در حال اجرا است +2. PowerShell را بسته و دوباره باز کنید +3. یا از Command Prompt استفاده کنید +4. بررسی کنید که Docker در PATH سیستم قرار دارد + +### مشکل: Port 3000 در حال استفاده است + +**راه حل:** +```bash +# تغییر پورت در docker-compose.yml +ports: + - "3001:3000" # حالا از پورت 3001 استفاده کنید +``` + +یا کانتینر دیگری که از پورت 3000 استفاده می‌کند را متوقف کنید. + +## متغیرهای محیطی + +اگر نیاز به تنظیم متغیرهای محیطی دارید، می‌توانید فایل `.env` را ایجاد کنید و در `docker-compose.yml` آن را اضافه کنید: + +```yaml +services: + next-app: + env_file: + - .env +``` + +یا به صورت مستقیم: + +```yaml +services: + next-app: + environment: + - NODE_ENV=production + - NEXT_PUBLIC_API_URL=https://api.example.com +``` + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2a5e31a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +# Stage 1: Dependencies +FROM node:20-alpine AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Copy package files +COPY package.json package-lock.json* yarn.lock* ./ + +# Install dependencies +RUN \ + if [ -f yarn.lock ]; then yarn ; \ + elif [ -f package-lock.json ]; then npm ci; \ + else npm i; \ + fi + +# Stage 2: Builder +FROM node:20-alpine AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Set environment variables for build +ENV NEXT_TELEMETRY_DISABLED 1 + +# Build the application +RUN npm run build + +# Stage 3: Runner +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production +ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy necessary files from standalone build +COPY --from=builder /app/public ./public + +# Copy standalone build files +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 +ENV HOSTNAME "0.0.0.0" + +CMD ["node", "server.js"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..ab3fb1c --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,52 @@ +import React from 'react' +import type { Metadata } from 'next' +import { getContentSync } from '@/lib/content' +import { AboutCompanySection } from '@/components/sections/other/about-company-section' + +export async function generateMetadata(): Promise { + const content = getContentSync() + const meta = content.meta.pages.about + return { + title: meta.title, + description: meta.description, + } +} + +export default function AboutPage() { + const content = getContentSync() + const about = content.pages.about as { + heading: string + description: string + title: string + subtitle: string + bio: string + vision: string + philosophyHeading: string + outro: string + } + + const imageLogo = content.images?.logo + ? { src: content.images.logo, alt: content.meta.siteName } + : undefined + const imageOffice = { + src: '/assets/about-us/f97a15aa-cf15-4508-ae5d-d3e5211b433e.png', + alt: 'نمای دفتر مستر دکور', + } + + return ( +
+ +
+ ) +} diff --git a/app/after-sales/page.tsx b/app/after-sales/page.tsx new file mode 100644 index 0000000..cb4e52e --- /dev/null +++ b/app/after-sales/page.tsx @@ -0,0 +1,147 @@ +import type { Metadata } from "next"; +import { Clock3, Paintbrush2, ShieldCheck, Wrench } from "lucide-react"; +import { MeteorTitleBackground } from "@/components/utilities/meteor-title-background"; + +export const metadata: Metadata = { + title: "خدمات پس از فروش", + description: + "خدمات پس از فروش مستر دکور برای استند و ملزومات ویترین طلافروشی شامل تعویض روکش، تعمیرات تخصصی و پشتیبانی اجرایی است.", +}; + +const serviceItems = [ + "تعویض روکش استندهای دکوری با هماهنگی رنگ و متریال جدید", + "ترمیم خط و خش، لب پریدگی و بازسازی نقاط آسیب دیده بدنه", + "تعمیر کشوهای ریلی، رگلاژ، تعویض ریل و رفع گیرکردن یا لقی", + "تنظیم و تعمیر یراق آلات درب و محفظه های نمایشی", + "بازدید دوره ای برای پیشگیری از خرابی و افزایش طول عمر تجهیزات", +]; + +const supportStats = [ + { + icon: Paintbrush2, + title: "تعویض روکش", + description: "بازسازی ظاهر استندها با حفظ هویت بصری فروشگاه", + }, + { + icon: Wrench, + title: "تعمیر کشو", + description: "رفع خرابی ریل و مکانیزم برای کارکرد روان روزانه", + }, + { + icon: Clock3, + title: "زمان بندی منعطف", + description: "هماهنگی مراجعه در ساعات کم تردد فروشگاه", + }, + { + icon: ShieldCheck, + title: "کیفیت اجرا", + description: "تحویل نهایی پس از تست عملکرد و کنترل کیفی", + }, +]; + +export default function AfterSalesPage() { + return ( +
+
+ +
+
+
+

+ خدمات پس از فروش مستر دکور +

+

+ کیفیت ویترین فقط به روز تحویل محدود نمی شود. خدمات پس از فروش مستر + دکور کمک می کند استندهای شما همیشه حرفه ای، منظم و آماده نمایش موثر + سرویس های طلا و جواهر باقی بمانند. +

+
+
+
+
+ +
+
+
+
+

+ تعویض روکش استندهای دکوری +

+

+ روکش استندها در فضای پررفت وآمد طلافروشی به مرور زمان دچار افت + کیفیت ظاهری می شود. تیم ما با بررسی دقیق نوع متریال، شرایط نور و + سبک چیدمان فروشگاه، روکش جدید را انتخاب و اجرا می کند تا ظاهر + استندها دوباره یکدست، تمیز و هماهنگ با برند شما شود. +

+

+ در فرآیند تعویض روکش، تلاش می کنیم کمترین اختلال در فعالیت روزانه + فروشگاه ایجاد شود و نتیجه نهایی علاوه بر زیبایی، مقاومت مناسب در + برابر سایش و نظافت مداوم داشته باشد. +

+
+ +
+

+ تعمیر کشو و مکانیزم حرکتی +

+

+ کشوهای استند در استفاده مداوم ممکن است دچار گیرکردن، صدای اضافی، + لقی یا بسته نشدن کامل شوند. خدمات تعمیر کشو شامل رگلاژ تخصصی، + تعمیر یا تعویض ریل، بررسی اتصالات داخلی و تنظیم مجدد حرکات کشو است + تا عملکرد روان و قابل اعتماد به سیستم برگردد. +

+

+ پس از پایان تعمیر، همه بخش ها تست می شوند تا هنگام استفاده سریع + در محیط فروش، کشو بدون تنش و با ایمنی کامل باز و بسته شود. +

+
+ +
+

+ خدمات قابل ارائه در یک نگاه +

+
+ {serviceItems.map((item) => ( +
+

{item}

+
+ ))} +
+
+
+ + +
+
+
+ ); +} diff --git a/app/consultation/actions.ts b/app/consultation/actions.ts new file mode 100644 index 0000000..118f783 --- /dev/null +++ b/app/consultation/actions.ts @@ -0,0 +1,95 @@ +'use server' + +const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN +const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID +const TELEGRAM_URL = process.env.TELEGRAM_URL + +export type ConsultationFormData = { + name: string + phone: string + email?: string + subject?: string + message?: string +} + +function buildTelegramMessage(data: ConsultationFormData): string { + const lines = [ + '📩 *درخواست مشاوره آنلاین*', + '', + `👤 *نام:* ${data.name}`, + `📱 *تلفن:* ${data.phone}`, + ] + if (data.email?.trim()) { + lines.push(`📧 *ایمیل:* ${data.email.trim()}`) + } + if (data.subject?.trim()) { + lines.push(`📌 *موضوع:* ${data.subject.trim()}`) + } + if (data.message?.trim()) { + lines.push('', '*توضیحات:*', data.message.trim()) + } + return lines.join('\n') +} + +export async function submitConsultation( + data: ConsultationFormData +): Promise<{ success: boolean; error?: string }> { + const name = data.name?.trim() + const phone = data.phone?.trim().replace(/\s/g, '') + + if (!name || name.length < 2) { + return { success: false, error: 'نام و نام خانوادگی را وارد کنید.' } + } + if (!phone || phone.length < 10) { + return { success: false, error: 'شماره تماس معتبر وارد کنید.' } + } + + if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) { + console.error('TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID is not set') + return { + success: false, + error: 'پیکربندی ارسال پیام انجام نشده است. لطفاً با پشتیبانی تماس بگیرید.', + } + } + + const text = buildTelegramMessage({ + name, + phone, + email: data.email, + subject: data.subject, + message: data.message, + }) + + const url = `${TELEGRAM_URL}/bot${TELEGRAM_BOT_TOKEN}/sendMessage` + const body = { + chat_id: TELEGRAM_CHAT_ID, + text, + parse_mode: 'Markdown', + } + + try { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) + + const result = await res.json() + + if (!res.ok || !result.ok) { + console.error('Telegram API error:', result) + return { + success: false, + error: 'ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.', + } + } + + return { success: true } + } catch (e) { + console.error('Failed to send to Telegram', e) + return { + success: false, + error: 'ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.', + } + } +} diff --git a/app/consultation/page.tsx b/app/consultation/page.tsx new file mode 100644 index 0000000..0963e88 --- /dev/null +++ b/app/consultation/page.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import type { Metadata } from 'next' +import { ConsultationForm } from '@/components/sections/other/consultation-form' +import { getContentSync } from '@/lib/content' + +export async function generateMetadata(): Promise { + const content = getContentSync() + const meta = content.meta.pages.consultation + return { + title: meta.title, + description: meta.description, + } +} + +export default function ConsultationPage() { + const content = getContentSync() + const { consultation } = content.pages + + return ( +
+
+
+
+

+ {consultation.heading} +

+

+ {consultation.description} +

+
+ +
+
+
+ ) +} + diff --git a/app/contact/actions.ts b/app/contact/actions.ts new file mode 100644 index 0000000..fca1ab3 --- /dev/null +++ b/app/contact/actions.ts @@ -0,0 +1,95 @@ +'use server' + +const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN +const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID +const TELEGRAM_URL = process.env.TELEGRAM_URL ?? 'https://api.telegram.org' + +export type ContactFormData = { + name: string + phone: string + email?: string + subject?: string + message?: string +} + +function buildTelegramMessage(data: ContactFormData): string { + const lines = [ + '📞 *تماس با ما*', + '', + `👤 *نام:* ${data.name}`, + `📱 *تلفن:* ${data.phone}`, + ] + if (data.email?.trim()) { + lines.push(`📧 *ایمیل:* ${data.email.trim()}`) + } + if (data.subject?.trim()) { + lines.push(`📌 *موضوع:* ${data.subject.trim()}`) + } + if (data.message?.trim()) { + lines.push('', '*پیام:*', data.message.trim()) + } + return lines.join('\n') +} + +export async function submitContact( + data: ContactFormData +): Promise<{ success: boolean; error?: string }> { + const name = data.name?.trim() + const phone = data.phone?.trim().replace(/\s/g, '') + + if (!name || name.length < 2) { + return { success: false, error: 'نام و نام خانوادگی را وارد کنید.' } + } + if (!phone || phone.length < 10) { + return { success: false, error: 'شماره تماس معتبر وارد کنید.' } + } + + if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) { + console.error('TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID is not set') + return { + success: false, + error: 'پیکربندی ارسال پیام انجام نشده است. لطفاً با پشتیبانی تماس بگیرید.', + } + } + + const text = buildTelegramMessage({ + name, + phone, + email: data.email, + subject: data.subject, + message: data.message, + }) + + const url = `${TELEGRAM_URL}/bot${TELEGRAM_BOT_TOKEN}/sendMessage` + const body = { + chat_id: TELEGRAM_CHAT_ID, + text, + parse_mode: 'Markdown', + } + + try { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) + + const result = await res.json() + + if (!res.ok || !result.ok) { + console.error('Telegram API error:', result) + return { + success: false, + error: 'ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.', + } + } + + return { success: true } + } catch (e) { + console.error('Failed to send to Telegram', e) + return { + success: false, + error: 'ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.', + } + } +} diff --git a/app/contact/page.tsx b/app/contact/page.tsx new file mode 100644 index 0000000..1f866d5 --- /dev/null +++ b/app/contact/page.tsx @@ -0,0 +1,88 @@ +import React from 'react' +import type { Metadata } from 'next' +import { ContactInfoGrid } from '@/components/sections/other/contact-info-grid' +import { ContactForm } from '@/components/sections/other/contact-form' +import { getContentSync } from '@/lib/content' + +export async function generateMetadata(): Promise { + try { + const content = getContentSync() + const meta = content.meta?.pages?.contact + if (meta?.title) { + return { title: meta.title, description: meta.description ?? undefined } + } + } catch { + // fallback if content fails + } + return { title: 'تماس با ما', description: 'برای ارتباط با مستر دکور با ما تماس بگیرید.' } +} + +export default function ContactPage() { + const content = getContentSync() + const contact = content.pages?.contact + // const siteName = content.meta?.siteName ?? 'مستر دکور' + const heading = contact?.heading ?? 'تماس با ما' + const description = + contact?.description ?? 'برای دریافت مشاوره، استعلام قیمت و انتخاب بهترین دکور ویترین طلافروشی با ما در ارتباط باشید.' + const contactItems = [ + { + label: 'ایمیل', + description: 'برای دریافت پیش فاکتور، کاتالوگ یا پاسخ تخصصی، درخواست خود را ایمیل کنید.', + value: 'info@mrdecorr.com', + href: 'mailto:info@mrdecorr.com', + icon: 'mail' as const, + }, + { + label: 'دفتر', + description: 'برای هماهنگی جلسه حضوری و بررسی دقیق نیاز فروشگاه، به دفتر ما مراجعه کنید.', + value: 'مشاهده موقعیت روی نقشه', + href: 'https://maps.google.com', + icon: 'map' as const, + }, + { + label: 'تلفن', + description: 'برای مشاوره سریع و ثبت سفارش، از شنبه تا پنجشنبه پاسخگوی شما هستیم.', + value: '۰۹۱۲ ۹۴ ۹۷ ۳۱۵', + href: 'tel:09129497315', + icon: 'phone' as const, + }, + { + label: 'پشتیبانی واتساپ', + description: 'برای ارسال عکس ویترین و دریافت پیشنهاد اولیه، در واتساپ پیام دهید.', + value: 'شروع گفتگو', + href: 'https://wa.me/989129497315', + icon: 'chat' as const, + }, + ] + + return ( +
+ +
+
+
+
+

+ تکمیل فرم +

+

+ اطلاعات فروشگاه و نیاز خود را ثبت کنید تا کارشناسان مستر دکور در کوتاه ترین زمان با شما تماس بگیرند. +

+
+
+ +
+
+
+
+
+ ) +} + diff --git a/app/demo/actions.ts b/app/demo/actions.ts new file mode 100644 index 0000000..de5792e --- /dev/null +++ b/app/demo/actions.ts @@ -0,0 +1,98 @@ +'use server' + +const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN +const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID +const TELEGRAM_URL = process.env.TELEGRAM_URL ?? 'https://api.telegram.org' + +export type DemoRequestFormData = { + name: string + phone: string + product: string + email?: string + message?: string +} + +function buildTelegramMessage(data: DemoRequestFormData): string { + const lines = [ + '🎬 *درخواست دمو*', + '', + `👤 *نام:* ${data.name}`, + `📱 *تلفن:* ${data.phone}`, + `📦 *نرم‌افزار مورد نظر:* ${data.product}`, + ] + if (data.email?.trim()) { + lines.push(`📧 *ایمیل:* ${data.email.trim()}`) + } + if (data.message?.trim()) { + lines.push('', '*توضیحات:*', data.message.trim()) + } + return lines.join('\n') +} + +export async function submitDemoRequest( + data: DemoRequestFormData +): Promise<{ success: boolean; error?: string }> { + const name = data.name?.trim() + const phone = data.phone?.trim().replace(/\s/g, '') + + if (!name || name.length < 2) { + return { success: false, error: 'نام و نام خانوادگی را وارد کنید.' } + } + if (!phone || phone.length < 10) { + return { success: false, error: 'شماره تماس معتبر وارد کنید.' } + } + + if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) { + console.error('TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID is not set') + return { + success: false, + error: 'پیکربندی ارسال پیام انجام نشده است. لطفاً با پشتیبانی تماس بگیرید.', + } + } + + const product = data.product?.trim() + if (!product || product === '__empty__') { + return { success: false, error: 'نرم‌افزار مورد نظر را انتخاب کنید.' } + } + + const text = buildTelegramMessage({ + name, + phone, + product, + email: data.email, + message: data.message, + }) + + const url = `${TELEGRAM_URL}/bot${TELEGRAM_BOT_TOKEN}/sendMessage` + const body = { + chat_id: TELEGRAM_CHAT_ID, + text, + parse_mode: 'Markdown', + } + + try { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) + + const result = await res.json() + + if (!res.ok || !result.ok) { + console.error('Telegram API error:', result) + return { + success: false, + error: 'ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.', + } + } + + return { success: true } + } catch (e) { + console.error('Failed to send to Telegram', e) + return { + success: false, + error: 'ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.', + } + } +} diff --git a/app/demo/page.tsx b/app/demo/page.tsx new file mode 100644 index 0000000..a81fa90 --- /dev/null +++ b/app/demo/page.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import type { Metadata } from 'next' +import { Hero204 } from '@/components/sections/hero/hero-204' +import { DemoRequestForm } from '@/components/sections/other/demo-request-form' + +export const metadata: Metadata = { + title: 'درخواست دمو', + description: + 'درخواست دمو محصولات و خدمات مستر دکور. سامانه‌های تخصصی بازار طلا را از نزدیک ببینید؛ فرم را پر کنید تا برای هماهنگی دمو با شما تماس بگیریم.', +} + +export default function DemoRequestPage() { + return ( +
+ +
+
+
+

+ تکمیل فرم +

+

+ اطلاعات خود را در فرم زیر وارد کنید تا برای هماهنگی جلسه دمو با شما تماس بگیریم. +

+
+ +
+
+
+ ) +} diff --git a/app/faq/page.tsx b/app/faq/page.tsx new file mode 100644 index 0000000..ab60993 --- /dev/null +++ b/app/faq/page.tsx @@ -0,0 +1,16 @@ +import type { Metadata } from "next"; + +import { Faq16 } from "@/components/sections/other/faq-16"; + +export const metadata: Metadata = { + title: "سوالات متداول", + description: "پاسخ به سوالات متداول درباره طراحی، اجرا، سفارش و پشتیبانی خدمات مستر دکور.", +}; + +export default function FaqPage() { + return ( +
+ +
+ ); +} diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000..7a4685b Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..a3819c4 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,703 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +/* IRANSansX Font Faces */ +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 100; + + src: url("/assets/irsansx/Woff/IRANSansXFaNum-Thin.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-Thin.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 200; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-UltraLight.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-UltraLight.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 300; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-Light.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-Light.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 500; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-Medium.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-Medium.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 600; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-DemiBold.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-DemiBold.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 800; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-ExtraBold.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-ExtraBold.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: 900; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-Black.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-Black.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: bold; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-Bold.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-Bold.woff2") format("woff2"); +} + +@font-face { + font-family: IRANSansX; + font-style: normal; + font-weight: normal; + src: url("/assets/irsansx/Woff/IRANSansXFaNum-Regular.woff") format("woff"), + url("/assets/irsansx/Woff2/IRANSansXFaNum-Regular.woff2") format("woff2"); +} + +@custom-variant dark (&:is(.dark *)); + +/* + * تم واحد (بدون لایت/دارک) — پالت اصلاح‌شده + * پایه: شرابی-مشکی گرم | کارت: بورگوندی کنترل‌شده (کمتر ماژنتا) | متن: کرم گرم (نه خاکستری آبی) + * برند: طلایی #c99d4d | ثانویه: سرمه‌ای | طلایی/زمردی برای گرادیان و border انیمیشن + */ + +@theme inline { + /* Design tokens → Tailwind colors */ + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-overlay: var(--overlay); + --color-overlay-foreground: var(--overlay-foreground); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + /* Gold & Emerald scales for utility use */ + --color-gold-50: var(--gold-50); + --color-gold-100: var(--gold-100); + --color-gold-200: var(--gold-200); + --color-gold-300: var(--gold-300); + --color-gold-400: var(--gold-400); + --color-gold-500: var(--gold-500); + --color-gold-600: var(--gold-600); + --color-gold-700: var(--gold-700); + --color-gold-800: var(--gold-800); + --color-gold-900: var(--gold-900); + --color-gold-950: var(--gold-950); + --color-emerald-50: var(--emerald-50); + --color-emerald-100: var(--emerald-100); + --color-emerald-200: var(--emerald-200); + --color-emerald-300: var(--emerald-300); + --color-emerald-400: var(--emerald-400); + --color-emerald-500: var(--emerald-500); + --color-emerald-600: var(--emerald-600); + --color-emerald-700: var(--emerald-700); + --color-emerald-800: var(--emerald-800); + --color-emerald-900: var(--emerald-900); + --color-emerald-950: var(--emerald-950); + --font-sans: var(--font-iran-sans); + --font-mono: var(--font-geist-mono); + --font-iran-sans: "IRANSansX", "system-ui", "Avenir", "Helvetica", "Arial", + "sans-serif"; + --font-size-heading-1: clamp(2rem, 4vw + 1rem, 3.75rem); + --font-size-heading-2: clamp(1.5rem, 3vw + 0.5rem, 2.25rem); + --font-size-heading-3: clamp(1.25rem, 2vw + 0.5rem, 1.875rem); + --font-size-heading-4: 1.25rem; + --font-size-heading-5: 1.125rem; + --font-size-body: 1rem; + --font-size-body-sm: 0.875rem; + --font-size-caption: 0.75rem; + --font-weight-heading: 700; + --font-weight-subheading: 600; + --font-weight-body: 400; + --font-weight-medium: 500; + --line-height-heading: 1.25; + --line-height-body: 1.6; + --line-height-relaxed: 1.75; + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --animate-line-shadow: line-shadow 15s linear infinite; + @keyframes line-shadow { + 0% { background-position: 0 0; } + 100% { background-position: 100% -100%; } + } +} + +:root { + /* ----- Master palette from provided swatch ----- */ + --tone-1: #110507; + --tone-2: #230a0d; + --tone-3: #340e14; + --tone-4: #46131a; + --tone-5: #571821; + --tone-6: #682f37; + --tone-7: #895d64; + --tone-8: #db8c90; + --tone-9: #ddd1d3; + + /* ----- طلایی (برند؛ هم‌راستا با --primary) ----- */ + --gold-50: #fdf9f2; + --gold-100: #f8eed9; + --gold-200: #efd9b0; + --gold-300: #e4c27e; + --gold-400: #d4ad5a; + --gold-500: #c99d4d; + --gold-600: #a67e3c; + --gold-700: #85632f; + --gold-800: #5c4522; + --gold-900: #3d2f18; + --gold-950: #261c10; + + /* ----- زمردی-فیروزه (تزئینات؛ کمی عمیق‌تر برای کنار بورگوندی) ----- */ + --emerald-50: #e6f4f1; + --emerald-100: #c0e6df; + --emerald-200: #8fd4c8; + --emerald-300: #5cb9a9; + --emerald-400: #3a9d8c; + --emerald-500: #2a7f72; + --emerald-600: #23695f; + --emerald-700: #1d534c; + --emerald-800: #163e39; + --emerald-900: #0f2b28; + --emerald-950: #081916; + + --font-iran-sans: "IRANSansX", var(--font-geist-sans); + --radius: 0.625rem; + + /* ----- Semantic tokens based on exact provided palette ----- */ + --background: var(--tone-2); + --foreground: var(--tone-9); + + --card: var(--tone-4); + --card-foreground: var(--tone-9); + --popover: var(--tone-3); + --popover-foreground: var(--tone-9); + + --primary: var(--tone-8); + --primary-foreground: var(--tone-1); + + --secondary: var(--tone-6); + --secondary-foreground: var(--tone-9); + + --muted: var(--tone-3); + --muted-foreground: var(--tone-7); + + --accent: var(--tone-5); + --accent-foreground: var(--tone-9); + + --destructive: oklch(0.62 0.2 22); + --destructive-foreground: oklch(0.99 0.01 25); + + /* حاشیه: طلای ملایم روی پایهٔ تیرهٔ گرم */ + --border: color-mix(in srgb, var(--tone-8) 28%, var(--tone-2)); + --input: var(--tone-5); + --ring: var(--tone-8); + + /* متن/اسکرین روی تصویر و اورلی مودال */ + --overlay: var(--tone-1); + --overlay-foreground: var(--tone-9); + + --chart-1: var(--tone-8); + --chart-2: var(--tone-6); + --chart-3: var(--tone-7); + --chart-4: var(--tone-5); + --chart-5: var(--tone-4); + + --sidebar: var(--tone-2); + --sidebar-foreground: var(--tone-9); + --sidebar-primary: var(--tone-8); + --sidebar-primary-foreground: var(--tone-1); + --sidebar-accent: var(--tone-5); + --sidebar-accent-foreground: var(--tone-9); + --sidebar-border: var(--tone-6); + --sidebar-ring: var(--tone-8); + + /* ----- دکمهٔ پریمیوم (طلایی + گرادیان آبی ثانویه) ----- */ + --btn-radius: 0.625rem; + --btn-transition: 280ms ease-out; + --btn-gold-from: #d8b056; + --btn-gold-via: #c99d4d; + --btn-gold-to: #b08a3f; + --btn-gold-text: #fffaf0; + --btn-gold-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.2) inset, 0 2px 8px rgba(0, 0, 0, 0.35), + 0 4px 16px rgba(224, 168, 48, 0.2); + --btn-gold-shadow-hover: 0 1px 0 0 rgba(255, 255, 255, 0.3) inset, 0 6px 20px rgba(0, 0, 0, 0.3), + 0 0 10px rgba(242, 217, 122, 0.2); + --btn-gold-shadow-active: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 2px 6px rgba(0, 0, 0, 0.4); + --btn-gold-glow-hover: 0 0 10px rgba(242, 217, 122, 0.22); + --btn-sapphire-from: #2a5ab8; + --btn-sapphire-to: #153875; + --btn-sapphire-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.14) inset, 0 2px 8px rgba(0, 0, 0, 0.35), + 0 4px 16px rgba(30, 74, 180, 0.25); + --btn-sapphire-shadow-hover: 0 1px 0 0 rgba(255, 255, 255, 0.2) inset, 0 6px 20px rgba(0, 0, 0, 0.28), + 0 0 10px rgba(90, 140, 220, 0.22); + --btn-sapphire-shadow-active: 0 0 0 1px rgba(0, 0, 0, 0.12) inset, 0 2px 6px rgba(0, 0, 0, 0.35); + --btn-sapphire-glow-hover: 0 0 10px rgba(90, 140, 220, 0.28); +} + +@keyframes btn-shine { + 0% { transform: translateX(-100%) skewX(-12deg); } + 100% { transform: translateX(200%) skewX(-12deg); } +} + +@layer base { + html { + scroll-behavior: smooth; + color-scheme: dark; + } + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + font-family: "IRANSansX"; + letter-spacing: normal; + line-height: 1.6; + overflow-x: hidden; + /* Single subtle overlay for premium feel – no banding */ + background-image: + radial-gradient( + ellipse 95% 62% at 50% -10%, + color-mix(in srgb, var(--tone-8) 18%, transparent) 0%, + transparent 54% + ), + radial-gradient( + ellipse 85% 48% at 50% 115%, + color-mix(in srgb, var(--tone-7) 16%, transparent) 0%, + transparent 56% + ); + background-attachment: fixed; + background-repeat: no-repeat; + transition: background-color 0.3s ease; + } + /* Standard typography – same sizes/weights across all pages */ + h1, h2, h3, h4, h5, h6 { + font-family: inherit; + letter-spacing: normal; + line-height: var(--line-height-heading); + font-weight: var(--font-weight-heading); + transition: color 0.3s ease; + } + h1 { + font-size: var(--font-size-heading-1); + font-weight: 700; + } + h2 { + font-size: var(--font-size-heading-2); + font-weight: 600; + } + h3 { + font-size: var(--font-size-heading-3); + font-weight: 600; + } + h4 { + font-size: var(--font-size-heading-4); + font-weight: 500; + } + h5 { + font-size: var(--font-size-heading-5); + font-weight: 500; + } + h6 { + font-size: var(--font-size-body); + font-weight: 500; + } + p { + font-size: var(--font-size-body); + font-weight: var(--font-weight-body); + letter-spacing: normal; + line-height: var(--line-height-body); + transition: color 0.3s ease; + } + a { + transition: all 0.3s ease; + color: var(--primary); + } + a:hover { + color: var(--primary); + opacity: 0.9; + } + button { + transition: all 0.3s ease; + } +} + +/* WordPress table block (figure.wp-block-table > table) */ +.wp-block-table { + margin: 1.5rem 0; + overflow-x: auto; + border-radius: var(--radius); +} + +.wp-block-table table { + width: 100%; + border-collapse: collapse; + font-size: 0.9375rem; + +} + +.wp-block-table table.has-fixed-layout { + table-layout: fixed; +} + +.wp-block-table th, +.wp-block-table td { + padding: 0.75rem 1rem; + border: 1px solid var(--border); + text-align: right; + min-width: 850px; +} + +.wp-block-table tbody tr:first-child th, +.wp-block-table tbody tr:first-child td { + background: var(--muted); + font-weight: 600; + color: var(--foreground); +} + +.wp-block-table tbody tr:not(:first-child):hover td { + background: var(--muted); +} + +.wp-block-table thead th { + background: var(--muted); + font-weight: 600; + color: var(--foreground); +} + +.wp-block-button > .wp-element-button { + @apply inline-flex bg-primary text-primary-foreground px-4 py-2 rounded-md hover:bg-primary/80 hover:text-primary-foreground/80 cursor-pointer no-underline; +} + +.wp-block-button { + @apply inline-flex; +} + +.wp-block-button.is-style-outline > .wp-element-button { + @apply bg-transparent text-foreground border border-2 border-primary hover:text-primary cursor-pointer no-underline; +} + +.cta-block { + @apply flex flex-col gap-4 border rounded-md border-primary/50 p-4 items-center my-4 shadow-sm bg-primary/5 text-primary; +} + +.wp-block-image { + @apply rounded-md overflow-hidden shadow-sm; +} + +/* ========== Animated Gradient Border ========== */ + +@property --bag-angle { + syntax: ''; + initial-value: 0deg; + inherits: false; +} + +@keyframes bag-spin { + to { --bag-angle: 360deg; } +} + + +.border-animated-gradient { + --bag-width: 2px; + --bag-speed: 5s; + --bag-bg: var(--card); + --bag-c1: var(--gold-300); + --bag-c2: var(--emerald-500); + --bag-c3: var(--gold-500); + --bag-c4: var(--emerald-300); + + border: var(--bag-width) solid transparent; + background: + var(--bag-bg) padding-box, + conic-gradient( + from var(--bag-angle), + var(--bag-c1) 0%, + var(--bag-c2) 25%, + var(--bag-c3) 50%, + var(--bag-c4) 75%, + var(--bag-c1) 100% + ) border-box; + animation: bag-spin var(--bag-speed) linear infinite; +} + +/* ── ورایانت رنگ: فقط طلایی ── */ +.border-animated-gradient.bag-gold { + --bag-c1: var(--gold-200); + --bag-c2: var(--gold-500); + --bag-c3: var(--gold-300); + --bag-c4: var(--gold-700); +} + +/* ── ورایانت رنگ: فقط زمردی ── */ +.border-animated-gradient.bag-emerald { + --bag-c1: var(--emerald-200); + --bag-c2: var(--emerald-500); + --bag-c3: var(--emerald-300); + --bag-c4: var(--emerald-700); +} + +/* ── ورایانت پس‌زمینه ── */ +.border-animated-gradient.bag-bg-base { --bag-bg: var(--background); } +.border-animated-gradient.bag-bg-muted { --bag-bg: var(--muted); } + +/* ── ورایانت سرعت ── */ +.border-animated-gradient.bag-slow { --bag-speed: 10s; } +.border-animated-gradient.bag-fast { --bag-speed: 2s; } + +/* ── ورایانت ضخامت ── */ +.border-animated-gradient.bag-thick { --bag-width: 3px; } +.border-animated-gradient.bag-thin { --bag-width: 1px; } + +/* ========== Unified section surfaces & blur separators ========== */ +.section-unified-block { + border: 1px solid color-mix(in srgb, var(--border) 82%, transparent); + background: + linear-gradient( + 145deg, + color-mix(in srgb, var(--tone-5) 62%, transparent) 0%, + color-mix(in srgb, var(--tone-3) 74%, transparent) 100% + ); + -webkit-backdrop-filter: blur(14px); + backdrop-filter: blur(14px); + box-shadow: 0 10px 30px color-mix(in srgb, var(--tone-1) 55%, transparent); +} + +.frosted-panel { + border: 1px solid color-mix(in srgb, var(--tone-8) 24%, transparent); + background: color-mix(in srgb, var(--tone-4) 72%, transparent); + -webkit-backdrop-filter: blur(16px) saturate(115%); + backdrop-filter: blur(16px) saturate(115%); +} + +.tone-gradient-line { + height: 1px; + width: 8rem; + border-radius: 999px; + margin-inline: auto; + background: linear-gradient( + to right, + var(--tone-3) 0%, + var(--tone-6) 35%, + var(--tone-8) 65%, + var(--tone-9) 100% + ); + filter: blur(0.2px); +} + +.section-blur-separator { + position: relative; +} + +.section-blur-separator::before { + content: ""; + position: absolute; + left: 50%; + top: 0; + width: min(78rem, 92vw); + height: 1px; + transform: translateX(-50%); + background: linear-gradient( + to right, + transparent 0%, + color-mix(in srgb, var(--tone-6) 65%, transparent) 20%, + color-mix(in srgb, var(--tone-8) 78%, transparent) 50%, + color-mix(in srgb, var(--tone-7) 65%, transparent) 80%, + transparent 100% + ); + filter: blur(1.6px); + opacity: 0.95; +} + +@keyframes meteor-title-fall { + 0% { + transform: translate3d(64px, -120px, 0) rotate(300deg) scaleX(0.82); + opacity: 0; + } + 9% { + opacity: 0.9; + } + 58% { + opacity: 0.62; + } + 82% { + opacity: 0.28; + } + 100% { + transform: translate3d(-146px, var(--meteor-distance, 340px), 0) rotate(300deg) scaleX(0.96); + opacity: 0; + } +} + +.meteor-title-streak { + position: absolute; + top: -18%; + width: 56px; + height: 1.2px; + border-radius: 999px; + background: linear-gradient( + 90deg, + color-mix(in srgb, var(--tone-9) 0%, transparent) 0%, + color-mix(in srgb, var(--gold-200) 56%, transparent) 30%, + color-mix(in srgb, var(--gold-400) 72%, transparent) 55%, + color-mix(in srgb, var(--tone-9) 60%, transparent) 100% + ); + box-shadow: + 0 0 10px color-mix(in srgb, var(--gold-400) 44%, transparent), + 0 0 20px color-mix(in srgb, var(--gold-200) 34%, transparent), + 0 0 28px color-mix(in srgb, var(--tone-9) 20%, transparent); + transform-origin: right center; + transform: translate3d(64px, -120px, 0) rotate(300deg) scaleX(0.82); + animation-name: meteor-title-fall; + animation-delay: -2s; + animation-duration: 4.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + will-change: transform, opacity; +} + +.meteor-title-streak-1 { + left: 2%; + --meteor-distance: 320px; + animation-delay: -0.6s; + animation-duration: 4.1s; +} +.meteor-title-streak-2 { + left: 9%; + --meteor-distance: 360px; + animation-delay: -1.7s; + animation-duration: 4.8s; +} +.meteor-title-streak-3 { + left: 15%; + --meteor-distance: 340px; + animation-delay: -2.9s; + animation-duration: 4.3s; +} +.meteor-title-streak-4 { + left: 22%; + --meteor-distance: 390px; + animation-delay: -3.8s; + animation-duration: 5.1s; +} +.meteor-title-streak-5 { + left: 29%; + --meteor-distance: 330px; + animation-delay: -1.2s; + animation-duration: 4.2s; +} +.meteor-title-streak-6 { + left: 36%; + --meteor-distance: 370px; + animation-delay: -2.4s; + animation-duration: 4.9s; +} +.meteor-title-streak-7 { + left: 43%; + --meteor-distance: 350px; + animation-delay: -3.5s; + animation-duration: 4.6s; +} +.meteor-title-streak-8 { + left: 50%; + --meteor-distance: 400px; + animation-delay: -0.9s; + animation-duration: 5.2s; +} +.meteor-title-streak-9 { + left: 57%; + --meteor-distance: 335px; + animation-delay: -2.1s; + animation-duration: 4.4s; +} +.meteor-title-streak-10 { + left: 64%; + --meteor-distance: 375px; + animation-delay: -3.1s; + animation-duration: 5s; +} +.meteor-title-streak-11 { + left: 71%; + --meteor-distance: 345px; + animation-delay: -1.4s; + animation-duration: 4.5s; +} +.meteor-title-streak-12 { + left: 78%; + --meteor-distance: 385px; + animation-delay: -2.8s; + animation-duration: 5.1s; +} +.meteor-title-streak-13 { + left: 84%; + --meteor-distance: 325px; + animation-delay: -3.7s; + animation-duration: 4.2s; +} +.meteor-title-streak-14 { + left: 90%; + --meteor-distance: 365px; + animation-delay: -1.9s; + animation-duration: 4.8s; +} +.meteor-title-streak-15 { + left: 95%; + --meteor-distance: 340px; + animation-delay: -2.6s; + animation-duration: 4.6s; +} +.meteor-title-streak-16 { + left: 98%; + --meteor-distance: 380px; + animation-delay: -3.3s; + animation-duration: 5.2s; +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..0148b91 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,38 @@ +import type { Metadata } from "next"; +import "./globals.css"; +import { ConditionalLayout } from "@/components/layout/conditional-layout"; +import { ContentProvider } from "@/components/layout/content-provider"; +import { ThemeProvider } from "@/components/layout/theme-provider"; +import { TopProgressBar } from "@/components/layout/top-progress-bar"; +import { getContent } from "@/lib/content"; + +export const metadata: Metadata = { + title: { + default: "مستر دکور", + template: "%s | مستر دکور", + }, + description: "مستر دکور — راهکارهای نرم‌افزاری برای کسب‌وکار", + icons: { + icon: "/assets/mainLogo_montzar.png", + }, +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + const content = await getContent(); + return ( + + + + + + {children} + + + + + ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..be0ee3f --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from "next"; + +import { Hero174 } from "@/components/sections/hero/hero-174"; +import { ProductCategories2 } from "@/components/sections/feature/product-categories-2"; +import { Logos21 } from "@/components/sections/other/logos-21"; +import { FeatureWorkspaceTabs } from "@/components/sections/feature/feature-workspace-tabs"; +import { BusinessStatsSection } from "@/components/sections/other/business-stats-section"; +import { Cta6 } from "@/components/sections/cta/cta-6"; + +export const metadata: Metadata = { + title: "صفحه اصلی", + description: + "مستر دکور؛ تامین کننده تخصصی استند و ملزومات دکور ویترین طلافروشی برای نمایش حرفه ای سرویس های طلا و جواهر.", +}; + +export default function Home() { + return ( +
+ + + + + + +
+ ); +} diff --git a/app/product/[productId]/page.tsx b/app/product/[productId]/page.tsx new file mode 100644 index 0000000..eefe41a --- /dev/null +++ b/app/product/[productId]/page.tsx @@ -0,0 +1,57 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; + +import { ProductDetailView } from "@/components/sections/feature/product-detail-view"; +import { Button } from "@/components/ui/button"; +import { getAllProductIds, getProductById, getProductDetail } from "@/lib/catalog-products"; + +type PageParams = { productId: string }; + +export function generateStaticParams(): PageParams[] { + return getAllProductIds().map((productId) => ({ productId })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise; +}): Promise { + const { productId } = await params; + const product = getProductById(productId); + if (!product) { + return { title: "محصول" }; + } + return { + title: product.name, + description: `${product.name} — ${product.categoryLabel} | مستر دکور`, + }; +} + +export default async function ProductDetailPage({ + params, +}: { + params: Promise; +}) { + const { productId } = await params; + const detail = getProductDetail(productId); + if (!detail) { + notFound(); + } + + return ( +
+
+
+ + +
+
+ +
+ ); +} diff --git a/app/product/page.tsx b/app/product/page.tsx new file mode 100644 index 0000000..3b9557f --- /dev/null +++ b/app/product/page.tsx @@ -0,0 +1,33 @@ +import type { Metadata } from 'next' +import { ProductList10Catalog } from '@/components/sections/feature/product-list10-catalog' + +export const metadata: Metadata = { + title: "محصولات", + description: + "محصولات مستر دکور شامل استند و ملزومات دکور ویترین طلافروشی در دسته های طلا، خردسال، جواهر و مانکن های تخصصی.", +} + +type ProductSearchParams = + | { category?: string | string[] } + | Promise<{ category?: string | string[] }> + +export default async function ProductsPage({ + searchParams, +}: { + searchParams?: ProductSearchParams +}) { + const resolvedSearchParams = + searchParams && typeof (searchParams as Promise<{ category?: string | string[] }>).then === 'function' + ? await (searchParams as Promise<{ category?: string | string[] }>) + : (searchParams as { category?: string | string[] } | undefined) + + const rawCategory = resolvedSearchParams?.category + const initialCategory = Array.isArray(rawCategory) ? rawCategory[0] : rawCategory + + return ( +
+ +
+ ) +} + diff --git a/app/warranty/page.tsx b/app/warranty/page.tsx new file mode 100644 index 0000000..b73b8f6 --- /dev/null +++ b/app/warranty/page.tsx @@ -0,0 +1,150 @@ +import type { Metadata } from "next"; +import { BadgeCheck, Clock3, ShieldCheck, Wrench } from "lucide-react"; +import { MeteorTitleBackground } from "@/components/utilities/meteor-title-background"; + +export const metadata: Metadata = { + title: "گارانتی", + description: + "گارانتی مستر دکور برای استند و ملزومات ویترین طلافروشی، شامل پوشش ایرادهای ساخت، یراق آلات اصلی و پشتیبانی فنی است.", +}; + +const warrantyCoverage = [ + "پوشش عیوب ساخت و مونتاژ بدنه استند در بازه گارانتی", + "بازبینی و رفع اشکال عملکردی کشو، ریل و یراق آلات اصلی", + "تنظیم مجدد قطعات متحرک که در استفاده عادی دچار اختلال شده اند", + "ارائه راهکار اصلاحی یا تعویض قطعه در صورت تایید کارشناس فنی", + "پشتیبانی تلفنی و ثبت درخواست برای هماهنگی بازدید حضوری", +]; + +const warrantyHighlights = [ + { + icon: ShieldCheck, + title: "پوشش فنی معتبر", + description: "تعهد کتبی نسبت به کیفیت اجرای سازه و قطعات اصلی", + }, + { + icon: Wrench, + title: "رسیدگی تخصصی", + description: "عیب یابی و رفع مشکل توسط تیم آشنا با استندهای طلافروشی", + }, + { + icon: Clock3, + title: "پاسخگویی زمان دار", + description: "پیگیری درخواست ها بر اساس اولویت و زمان بندی هماهنگ شده", + }, + { + icon: BadgeCheck, + title: "کنترل کیفی نهایی", + description: "تحویل خدمت پس از تست عملکرد و تایید کارایی", + }, +]; + +export default function WarrantyPage() { + return ( +
+
+ +
+
+
+

+ گارانتی خدمات و محصولات +

+

+ گارانتی مستر دکور برای حفظ کیفیت واقعی استندها طراحی شده است تا با + اطمینان بیشتری ویترین خود را مدیریت کنید و عملکرد تجهیزات در فروش + روزانه پایدار بماند. +

+
+
+
+
+ +
+
+
+
+

+ دامنه پوشش گارانتی +

+

+ در دوره گارانتی، مشکلاتی که ناشی از ایراد ساخت، مونتاژ یا عملکرد + قطعات اصلی باشند توسط تیم فنی بررسی و رفع می شوند. هدف این است که + استندها در شرایط عادی فروشگاه، کارکرد پایدار و ظاهری حرفه ای خود + را حفظ کنند. +

+

+ جزئیات نهایی هر پروژه شامل مدت زمان، شرایط نگهداری و موارد خارج از + پوشش، همزمان با تحویل پروژه و مستندات فنی در اختیار شما قرار + می گیرد. +

+
+ +
+

+ روند رسیدگی به درخواست گارانتی +

+

+ پس از ثبت درخواست، ابتدا وضعیت به صورت تلفنی یا تصویری ارزیابی + می شود. در صورت نیاز به بازدید حضوری، زمان مناسب با مجموعه شما + هماهنگ می گردد تا بدون ایجاد اختلال در فعالیت فروشگاه، اقدامات + اصلاحی انجام شود. +

+

+ در پایان، عملکرد قطعه یا بخش تعمیر شده مجدد تست می شود و تحویل + نهایی پس از تایید کیفیت اجرا انجام می گیرد. +

+
+ +
+

+ موارد تحت پوشش +

+
+ {warrantyCoverage.map((item) => ( +
+

{item}

+
+ ))} +
+
+
+ + +
+
+
+ ); +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..3515ade --- /dev/null +++ b/components.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": { + "@tailark": "https://tailark.com/r/{name}.json", + "@shadcnblocks": { + "url": "https://shadcnblocks.com/r/{name}", + "headers": { + "Authorization": "Bearer sk_live_2kofDNyO7SAxm0tk6pDChCUwbOXkSo-w" + } + }, + "@aceternity": "https://ui.aceternity.com/registry/{name}.json" + } +} \ No newline at end of file diff --git a/components/aceternity/3d-card.tsx b/components/aceternity/3d-card.tsx new file mode 100644 index 0000000..05126a6 --- /dev/null +++ b/components/aceternity/3d-card.tsx @@ -0,0 +1,154 @@ +"use client"; + +import { cn } from "@/lib/utils"; +import React, { + createContext, + useContext, + useEffect, + useRef, + useState, +} from "react"; + +const MouseEnterContext = createContext< + [boolean, React.Dispatch>] | undefined +>(undefined); + +export const CardContainer = ({ + children, + className, + containerClassName, +}: { + children?: React.ReactNode; + className?: string; + containerClassName?: string; +}) => { + const containerRef = useRef(null); + const [isMouseEntered, setIsMouseEntered] = useState(false); + + const handleMouseMove = (e: React.MouseEvent) => { + if (!containerRef.current) return; + const { left, top, width, height } = + containerRef.current.getBoundingClientRect(); + const x = (e.clientX - left - width / 2) / 25; + const y = (e.clientY - top - height / 2) / 25; + containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`; + }; + + const handleMouseEnter = (e: React.MouseEvent) => { + setIsMouseEntered(true); + if (!containerRef.current) return; + }; + + const handleMouseLeave = () => { + if (!containerRef.current) return; + setIsMouseEntered(false); + containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`; + }; + return ( + +
+
+ {children} +
+
+
+ ); +}; + +export const CardBody = ({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) => { + return ( +
*]:[transform-style:preserve-3d]", + className + )} + > + {children} +
+ ); +}; + +export const CardItem = ({ + as: Tag = "div", + children, + className, + translateX = 0, + translateY = 0, + translateZ = 0, + rotateX = 0, + rotateY = 0, + rotateZ = 0, + ...rest +}: { + as?: React.ElementType; + children: React.ReactNode; + className?: string; + translateX?: number | string; + translateY?: number | string; + translateZ?: number | string; + rotateX?: number | string; + rotateY?: number | string; + rotateZ?: number | string; +}) => { + const ref = useRef(null); + const [isMouseEntered] = useMouseEnter(); + + useEffect(() => { + handleAnimations(); + }, [isMouseEntered]); + + const handleAnimations = () => { + if (!ref.current) return; + if (isMouseEntered) { + ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`; + } else { + ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`; + } + }; + + return ( + + {children} + + ); +}; + +// Create a hook to use the context +export const useMouseEnter = () => { + const context = useContext(MouseEnterContext); + if (context === undefined) { + throw new Error("useMouseEnter must be used within a MouseEnterProvider"); + } + return context; +}; + diff --git a/components/feature278.tsx b/components/feature278.tsx new file mode 100644 index 0000000..0ad25ea --- /dev/null +++ b/components/feature278.tsx @@ -0,0 +1,157 @@ +"use client"; + +import { AnimatePresence, motion } from "framer-motion"; +import React, { useState } from "react"; +import { + FaCog, + FaDollarSign, + FaPalette, + FaRocket, + FaShieldAlt, + FaUsers, +} from "react-icons/fa"; + +import { cn } from "@/lib/utils"; + +interface Feature278Props { + className?: string; +} + +const Feature278 = ({ className }: Feature278Props) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + + const items = [ + { + title: "Fast Performance", + description: "Lightning quick loading times and smooth interactions", + icon: FaRocket, + color: "text-primary", + bgColor: "bg-primary/10", + }, + { + title: "Easy Setup", + description: "Simple configuration with minimal code required", + icon: FaCog, + color: "text-secondary", + bgColor: "bg-secondary/10", + }, + { + title: "Beautiful Design", + description: "Modern aesthetics with stunning visual appeal", + icon: FaPalette, + color: "text-primary", + bgColor: "bg-primary/10", + }, + { + title: "Great Value", + description: "Affordable pricing with premium features included", + icon: FaDollarSign, + color: "text-secondary", + bgColor: "bg-secondary/10", + }, + { + title: "Secure", + description: "Enterprise-grade security with data protection", + icon: FaShieldAlt, + color: "text-primary", + bgColor: "bg-primary/10", + }, + { + title: "Team Support", + description: "Dedicated support team available around the clock", + icon: FaUsers, + color: "text-secondary", + bgColor: "bg-secondary/10", + }, + ]; + + return ( +
+
+
+

+ Features That speak for
{" "} + Themselves +

+ +
+ {items.map((item, idx) => ( +
setHoveredIndex(idx)} + onMouseLeave={() => setHoveredIndex(null)} + > + + {hoveredIndex === idx && ( + + )} + + + +
+ ))} +
+
+
+
+ ); +}; + +export { Feature278 }; + +const Card = ({ + className, + title, + description, + bgColor, + icon: Icon, + color, +}: { + className?: string; + title: string; + description: string; + icon: React.ComponentType<{ className?: string }>; + bgColor: string; + color: string; +}) => { + return ( +
+
+ +
+

{title}

+

{description}

+
+ ); +}; diff --git a/components/footer-5.tsx b/components/footer-5.tsx new file mode 100644 index 0000000..e95b629 --- /dev/null +++ b/components/footer-5.tsx @@ -0,0 +1,91 @@ +import React from 'react' +import Link from 'next/link' +import Image from 'next/image' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' + + + +export default function Footer5() { + return ( +
+
+
+
+ مستر دکور +
+
+ + + + + + + + + + + + + + + +
+
+
+ © مستر دکور 1404 +
+
+
+ ) +} + diff --git a/components/layout/conditional-layout.tsx b/components/layout/conditional-layout.tsx new file mode 100644 index 0000000..35c1c18 --- /dev/null +++ b/components/layout/conditional-layout.tsx @@ -0,0 +1,22 @@ +'use client' + +import { usePathname } from 'next/navigation' +import FooterSection from '@/components/navigation/footer' +import { HeroHeader } from '@/components/navigation/header' +import { HeroHeaderTailark } from '@/components/navigation/header-main' +import Footer5 from '@/components/navigation/footer' + +export function ConditionalLayout({ children }: { children: React.ReactNode }) { + const pathname = usePathname() + const isLoginPage = pathname === '/login' + const isHomePage = pathname === '/' + + return ( + <> + {!isLoginPage && } + {children} + {!isLoginPage && } + + ) +} + diff --git a/components/layout/content-provider.tsx b/components/layout/content-provider.tsx new file mode 100644 index 0000000..5423531 --- /dev/null +++ b/components/layout/content-provider.tsx @@ -0,0 +1,37 @@ +'use client' + +import React, { createContext, useContext, useMemo } from 'react' +import type { SiteContent } from '@/lib/content' + +const ContentContext = createContext(null) + +export function ContentProvider({ + initialContent, + children, +}: { + initialContent: SiteContent + children: React.ReactNode +}) { + const value = useMemo(() => initialContent, [initialContent]) + return ( + + {children} + + ) +} + +export function useContent(): SiteContent { + const ctx = useContext(ContentContext) + if (!ctx) { + throw new Error('useContent must be used within ContentProvider') + } + return ctx +} + +/** + * استفاده در کامپوننت‌هایی که خارج از ContentProvider هستند (مثلاً سرور). + * در آن صورت از getContentSync() یا getContent() استفاده کنید. + */ +export function useContentOptional(): SiteContent | null { + return useContext(ContentContext) +} diff --git a/components/layout/theme-provider.tsx b/components/layout/theme-provider.tsx new file mode 100644 index 0000000..54af94b --- /dev/null +++ b/components/layout/theme-provider.tsx @@ -0,0 +1,6 @@ +'use client' + +/** تم ثابت؛ بدون next-themes و بدون سوئیچ لایت/دارک */ +export function ThemeProvider({ children }: { children: React.ReactNode }) { + return <>{children} +} diff --git a/components/layout/top-progress-bar.tsx b/components/layout/top-progress-bar.tsx new file mode 100644 index 0000000..4f17ffe --- /dev/null +++ b/components/layout/top-progress-bar.tsx @@ -0,0 +1,104 @@ +"use client"; + +import { usePathname } from "next/navigation"; +import { useCallback, useEffect, useRef, useState } from "react"; + +const TOP_BAR_HEIGHT = 3; +const MAX_PERCENT = 90; +const COMPLETE_PERCENT = 100; +const TICK_MS = 50; +const INCREMENT = 2; +const COMPLETE_DURATION = 200; + +export function TopProgressBar() { + const pathname = usePathname(); + const [visible, setVisible] = useState(false); + const [percent, setPercent] = useState(0); + const timerRef = useRef | null>(null); + const completeTimeoutRef = useRef | null>(null); + const prevPathnameRef = useRef(pathname); + + const clearTimers = useCallback(() => { + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + if (completeTimeoutRef.current) { + clearTimeout(completeTimeoutRef.current); + completeTimeoutRef.current = null; + } + }, []); + + const startProgress = useCallback(() => { + clearTimers(); + setVisible(true); + setPercent(0); + timerRef.current = setInterval(() => { + setPercent((p) => { + if (p >= MAX_PERCENT) { + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + return MAX_PERCENT; + } + return Math.min(p + INCREMENT, MAX_PERCENT); + }); + }, TICK_MS); + }, [clearTimers]); + + const completeProgress = useCallback(() => { + clearTimers(); + setPercent(COMPLETE_PERCENT); + completeTimeoutRef.current = setTimeout(() => { + setVisible(false); + setPercent(0); + completeTimeoutRef.current = null; + }, COMPLETE_DURATION); + }, [clearTimers]); + + // Start bar when user clicks an internal link + useEffect(() => { + const handleClick = (e: MouseEvent) => { + const target = (e.target as Element).closest("a"); + if (!target?.href) return; + try { + const url = new URL(target.href); + if (url.origin === window.location.origin && url.pathname !== pathname) { + startProgress(); + } + } catch { + // ignore invalid URLs + } + }; + document.addEventListener("click", handleClick, true); + return () => document.removeEventListener("click", handleClick, true); + }, [pathname, startProgress]); + + // Complete bar when pathname changes (route loaded) + useEffect(() => { + if (prevPathnameRef.current !== pathname) { + prevPathnameRef.current = pathname; + if (visible) completeProgress(); + } + }, [pathname, visible, completeProgress]); + + useEffect(() => { + return clearTimers; + }, [clearTimers]); + + if (!visible) return null; + + return ( +
+
+
+ ); +} diff --git a/components/motion-primitives/infinite-slider.tsx b/components/motion-primitives/infinite-slider.tsx new file mode 100644 index 0000000..e9aaede --- /dev/null +++ b/components/motion-primitives/infinite-slider.tsx @@ -0,0 +1,113 @@ +'use client'; +import { cn } from '@/lib/utils'; +import { useMotionValue, animate, motion } from 'motion/react'; +import { useState, useEffect } from 'react'; +import useMeasure from 'react-use-measure'; + +export type InfiniteSliderProps = { + children: React.ReactNode; + gap?: number; + speed?: number; + speedOnHover?: number; + direction?: 'horizontal' | 'vertical'; + reverse?: boolean; + className?: string; +}; + +export function InfiniteSlider({ + children, + gap = 16, + speed = 100, + speedOnHover, + direction = 'horizontal', + reverse = false, + className, +}: InfiniteSliderProps) { + const [currentSpeed, setCurrentSpeed] = useState(speed); + const [ref, { width, height }] = useMeasure(); + const translation = useMotionValue(0); + const [isTransitioning, setIsTransitioning] = useState(false); + const [key, setKey] = useState(0); + + useEffect(() => { + let controls; + const size = direction === 'horizontal' ? width : height; + const contentSize = size + gap; + const from = reverse ? -contentSize / 2 : 0; + const to = reverse ? 0 : -contentSize / 2; + + const distanceToTravel = Math.abs(to - from); + const duration = distanceToTravel / currentSpeed; + + if (isTransitioning) { + const remainingDistance = Math.abs(translation.get() - to); + const transitionDuration = remainingDistance / currentSpeed; + + controls = animate(translation, [translation.get(), to], { + ease: 'linear', + duration: transitionDuration, + onComplete: () => { + setIsTransitioning(false); + setKey((prevKey) => prevKey + 1); + }, + }); + } else { + controls = animate(translation, [from, to], { + ease: 'linear', + duration: duration, + repeat: Infinity, + repeatType: 'loop', + repeatDelay: 0, + onRepeat: () => { + translation.set(from); + }, + }); + } + + return controls?.stop; + }, [ + key, + translation, + currentSpeed, + width, + height, + gap, + isTransitioning, + direction, + reverse, + ]); + + const hoverProps = speedOnHover + ? { + onHoverStart: () => { + setIsTransitioning(true); + setCurrentSpeed(speedOnHover); + }, + onHoverEnd: () => { + setIsTransitioning(true); + setCurrentSpeed(speed); + }, + } + : {}; + + return ( +
+ + {children} + {children} + +
+ ); +} + diff --git a/components/motion-primitives/progressive-blur.tsx b/components/motion-primitives/progressive-blur.tsx new file mode 100644 index 0000000..dd38b8f --- /dev/null +++ b/components/motion-primitives/progressive-blur.tsx @@ -0,0 +1,64 @@ +'use client'; +import { cn } from '@/lib/utils'; +import { HTMLMotionProps, motion } from 'motion/react'; + +export const GRADIENT_ANGLES = { + top: 0, + right: 90, + bottom: 180, + left: 270, +}; + +export type ProgressiveBlurProps = { + direction?: keyof typeof GRADIENT_ANGLES; + blurLayers?: number; + className?: string; + blurIntensity?: number; +} & HTMLMotionProps<'div'>; + +export function ProgressiveBlur({ + direction = 'bottom', + blurLayers = 8, + className, + blurIntensity = 0.25, + ...props +}: ProgressiveBlurProps) { + const layers = Math.max(blurLayers, 2); + const segmentSize = 1 / (blurLayers + 1); + + return ( +
+ {Array.from({ length: layers }).map((_, index) => { + const angle = GRADIENT_ANGLES[direction]; + const gradientStops = [ + index * segmentSize, + (index + 1) * segmentSize, + (index + 2) * segmentSize, + (index + 3) * segmentSize, + ].map( + (pos, posIndex) => + `rgba(255, 255, 255, ${posIndex === 1 || posIndex === 2 ? 1 : 0}) ${pos * 100}%` + ); + + const gradient = `linear-gradient(${angle}deg, ${gradientStops.join( + ', ' + )})`; + + return ( + + ); + })} +
+ ); +} + diff --git a/components/navigation/footer.tsx b/components/navigation/footer.tsx new file mode 100644 index 0000000..95972bf --- /dev/null +++ b/components/navigation/footer.tsx @@ -0,0 +1,87 @@ +'use client' + +import React from 'react' +import Link from 'next/link' +import Image from 'next/image' +import { Instagram, MessageCircle, Linkedin } from 'lucide-react' +import { useContent } from '@/components/layout/content-provider' + +export default function Footer5() { + const content = useContent() + const { footer } = content.navigation + const logoAlt = footer.logoAlt + const logoUrl = content.images.logo + + return ( + + ) +} + diff --git a/components/navigation/header-main.tsx b/components/navigation/header-main.tsx new file mode 100644 index 0000000..590c19f --- /dev/null +++ b/components/navigation/header-main.tsx @@ -0,0 +1,446 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' +import { usePathname } from 'next/navigation' +import { BadgeCheck, BriefcaseBusiness, Gauge, Info, Menu, PhoneCall, ShieldCheck, Sparkles, Store, X } from 'lucide-react' +import { Button } from '@/components/ui/button' +import React from 'react' +import { cn } from '@/lib/utils' +import { useScroll } from 'motion/react' +import { useContent } from '@/components/layout/content-provider' +import { AnimatePresence, motion } from 'framer-motion' +import { + NavigationMenu, + NavigationMenuContent, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + NavigationMenuTrigger, +} from '@/components/ui/navigation-menu' + +function isRouteActive(pathname: string, href: string): boolean { + if (href === '/') return pathname === '/' + return pathname === href || pathname.startsWith(`${href}/`) +} + +const HOME_HREF = '/' + +export const HeroHeaderTailark = () => { + const [menuState, setMenuState] = React.useState(false) + const [scrolled, setScrolled] = React.useState(false) + const pathname = usePathname() ?? '' + const content = useContent() + const { header } = content.navigation + const { images } = content + const menuItems = header.menuItems + const productItems = header.productItems + const wordmark = header.wordmark ?? content.meta.siteName + const homeItem = menuItems.find((i) => i.href === HOME_HREF) + const trailItems = menuItems.filter((i) => i.href !== HOME_HREF) + const productMenuItems = productItems.map((item, index) => ({ + ...item, + icon: [Sparkles, Gauge, ShieldCheck][index] ?? BadgeCheck, + })) + const solutionMenuItems = [ + { + name: 'درباره ما', + href: '/about', + description: 'آشنایی با چشم انداز، تجربه و مسیر توسعه مستر دکور', + icon: Info, + }, + { + name: 'تماس با ما', + href: '/contact', + description: 'ارتباط مستقیم با تیم برای دریافت پاسخ سریع', + icon: PhoneCall, + }, + { + name: 'مشاوره و قیمت', + href: '/consultation', + description: 'بررسی دقیق نیاز شما و پیشنهاد مسیر اجرایی مناسب', + icon: BriefcaseBusiness, + }, + ] + + const { scrollYProgress } = useScroll() + + React.useEffect(() => { + const unsubscribe = scrollYProgress.on('change', (latest) => { + setScrolled(latest > 0.05) + }) + return () => unsubscribe() + }, [scrollYProgress]) + + return ( +
+ +
+ ) +} diff --git a/components/navigation/header.tsx b/components/navigation/header.tsx new file mode 100644 index 0000000..742cdf3 --- /dev/null +++ b/components/navigation/header.tsx @@ -0,0 +1,107 @@ +'use client' +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { Logo } from '@/components/navigation/logo' +import { Menu, X } from 'lucide-react' +import { Button } from '@/components/ui/button' +import React from 'react' +import { cn } from '@/lib/utils' + +const menuItems = [ + { name: 'صفحه اصلی', href: '/' }, + { name: 'محصولات', href: '/product' }, + { name: 'تماس با ما', href: '/contact' }, +] + + + +function isLinkActive(pathname: string, href: string): boolean { + if (href === '/') return pathname === '/' + return pathname === href || pathname.startsWith(href + '/') +} + +export const HeroHeader = () => { + const pathname = usePathname() + const [menuState, setMenuState] = React.useState(false) + const [isScrolled, setIsScrolled] = React.useState(false) + + React.useEffect(() => { + const handleScroll = () => { + setIsScrolled(window.scrollY > 50) + } + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, []) + return ( +
+ +
+ ) +} diff --git a/components/navigation/logo-main.tsx b/components/navigation/logo-main.tsx new file mode 100644 index 0000000..784fd71 --- /dev/null +++ b/components/navigation/logo-main.tsx @@ -0,0 +1,36 @@ +import { cn } from '@/lib/utils' + +export const LogoTailark = ({ className, uniColor }: { className?: string; uniColor?: boolean }) => { + return ( + + + + + + + + + + + ) +} + diff --git a/components/navigation/logo.tsx b/components/navigation/logo.tsx new file mode 100644 index 0000000..ebcba41 --- /dev/null +++ b/components/navigation/logo.tsx @@ -0,0 +1,66 @@ +import { cn } from "@/lib/utils"; +import Image from "next/image"; +import Link from "next/link"; +import { Separator } from "./ui/separator"; + +export const Logo = ({ + className, + uniColor, + hasCaption +}: { + className?: string; + uniColor?: boolean; + hasCaption?: boolean; +}) => { + const _logoText = مستر دکور + return ( + + + {hasCaption ? + + {_logoText} + + زیرساخت دیجیتال بازار طلا + + + + : _logoText} + + ); +}; + +export const LogoIcon = ({ + className, + uniColor, +}: { + className?: string; + uniColor?: boolean; +}) => { + return ( + Logo + ); +}; + +export const LogoStroke = ({ className }: { className?: string }) => { + return ( + + + + ); +}; diff --git a/components/navigation/theme-toggle.tsx b/components/navigation/theme-toggle.tsx new file mode 100644 index 0000000..7355ae3 --- /dev/null +++ b/components/navigation/theme-toggle.tsx @@ -0,0 +1,41 @@ +'use client' + +import { Moon, Sun } from 'lucide-react' +import { useTheme } from 'next-themes' +import { useEffect, useState } from 'react' +import { Button } from '@/components/ui/button' +import { cn } from '@/lib/utils' + +export function ThemeToggle() { + const { theme, setTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) { + return ( + + ) + } + + return ( + + ) +} + diff --git a/components/sections/cta/cta-10.tsx b/components/sections/cta/cta-10.tsx new file mode 100644 index 0000000..8a2bec6 --- /dev/null +++ b/components/sections/cta/cta-10.tsx @@ -0,0 +1,63 @@ +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; + +interface Cta10Props { + heading: string; + description: string; + buttons?: { + primary?: { + text: string; + url: string; + }; + secondary?: { + text: string; + url: string; + }; + }; + className?: string; +} + +const Cta10 = ({ + heading = "Call to Action", + description = "Build faster with our collection of pre-built blocks. Speed up your development and ship features in record time.", + buttons = { + primary: { + text: "Buy Now", + url: "https://www.shadcnblocks.com", + }, + }, + className, +}: Cta10Props) => { + return ( +
+
+
+
+

+ {heading} +

+

+ {description} +

+
+
+ {buttons.secondary && ( + + )} + {buttons.primary && ( + + )} +
+
+
+
+ ); +}; + +export { Cta10 }; + diff --git a/components/sections/cta/cta-14.tsx b/components/sections/cta/cta-14.tsx new file mode 100644 index 0000000..1201952 --- /dev/null +++ b/components/sections/cta/cta-14.tsx @@ -0,0 +1,38 @@ +"use client"; + +import Link from "next/link"; + +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { useContent } from "@/components/layout/content-provider"; + +interface Cta14Props { + className?: string; +} + +const Cta14 = ({ className }: Cta14Props) => { + const content = useContent(); + const cta = content.pages.product.abeshdeHamkar.cta14; + + return ( +
+
+
+
+

+ {cta.text} +

+
+ +
+
+
+
+
+ ); +}; + +export { Cta14 }; diff --git a/components/sections/cta/cta-17-gold-store.tsx b/components/sections/cta/cta-17-gold-store.tsx new file mode 100644 index 0000000..c89a50f --- /dev/null +++ b/components/sections/cta/cta-17-gold-store.tsx @@ -0,0 +1,119 @@ +"use client"; + +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { useContent } from "@/components/layout/content-provider"; +import Link from "next/link"; + +interface Cta17GoldStoreProps { + className?: string; +} + +const CirclesPattern = () => ( + + + + + + + + + + +); + +const Cta17GoldStore = ({ className }: Cta17GoldStoreProps) => { + const content = useContent(); + const cta = content.pages.product.forushgahSazTala.cta17; + return ( +
+
+ +
+
+

+ {cta.heading} +

+

+ {cta.description} +

+
+ +
+
+
+
+
+ ); +}; + +export { Cta17GoldStore }; diff --git a/components/sections/cta/cta-6.tsx b/components/sections/cta/cta-6.tsx new file mode 100644 index 0000000..3bca2ee --- /dev/null +++ b/components/sections/cta/cta-6.tsx @@ -0,0 +1,49 @@ +"use client"; + +import Link from "next/link"; +import { ArrowRight } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; + +interface Cta6Props { + className?: string; +} + +const Cta6 = ({ className }: Cta6Props) => { + return ( +
+
+ ); +}; + +export { Cta6 }; diff --git a/components/sections/cta/cta-alternative.tsx b/components/sections/cta/cta-alternative.tsx new file mode 100644 index 0000000..8abfbe4 --- /dev/null +++ b/components/sections/cta/cta-alternative.tsx @@ -0,0 +1,40 @@ +import { Button } from '@/components/ui/button' +import { Mail, SendHorizonal } from 'lucide-react' + +export default function CallToAction3() { + return ( +
+
+
+

Start Building

+

Libero sapiente aliquam quibusdam aspernatur.

+ +
+
+ + + + +
+ +
+
+
+
+
+
+ ) +} + diff --git a/components/sections/cta/cta-default.tsx b/components/sections/cta/cta-default.tsx new file mode 100644 index 0000000..49d6e6b --- /dev/null +++ b/components/sections/cta/cta-default.tsx @@ -0,0 +1,28 @@ +import { Button } from '@/components/ui/button' +import Link from 'next/link' + +export default function CallToAction() { + return ( +
+
+
+

Start Building

+

Libero sapiente aliquam quibusdam aspernatur.

+ +
+ + +
+
+
+
+ ) +} diff --git a/components/sections/cta/cta-home.tsx b/components/sections/cta/cta-home.tsx new file mode 100644 index 0000000..c434627 --- /dev/null +++ b/components/sections/cta/cta-home.tsx @@ -0,0 +1,35 @@ +'use client' + +import { Button } from '@/components/ui/button' +import Link from 'next/link' +import { useContent } from '@/components/layout/content-provider' + +export default function CtaHome() { + const content = useContent() + const { ctaHome } = content.pages.home + + return ( +
+
+
+

{ctaHome.heading}

+

{ctaHome.description}

+ +
+ + +
+
+
+
+ ) +} + diff --git a/components/sections/cta/cta-product-final.tsx b/components/sections/cta/cta-product-final.tsx new file mode 100644 index 0000000..53e5d79 --- /dev/null +++ b/components/sections/cta/cta-product-final.tsx @@ -0,0 +1,28 @@ +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; + +interface CtaProductFinalProps { + className?: string; +} + +export default function CtaProductFinal({ className }: CtaProductFinalProps) { + return ( +
+
+
+

+ اگر حجم معاملات شما بالاست و به دنبال کاهش تماس‌های غیرضروری و مدیریت دقیق حساب‌ها هستید، + برای دریافت معرفی کامل سامانه با ما در تماس باشید. +

+ +
+
+
+ ); +} + diff --git a/components/sections/cta/cta-product.tsx b/components/sections/cta/cta-product.tsx new file mode 100644 index 0000000..aa207b4 --- /dev/null +++ b/components/sections/cta/cta-product.tsx @@ -0,0 +1,27 @@ +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; + +interface CtaProductProps { + className?: string; +} + +const CtaProduct = ({ className }: CtaProductProps) => { + return ( +
+
+
+

Transform your business today.

+
+ + + +
+
+
+
+ ); +}; + +export { CtaProduct }; diff --git a/components/sections/feature/feature-104.tsx b/components/sections/feature/feature-104.tsx new file mode 100644 index 0000000..82fdc61 --- /dev/null +++ b/components/sections/feature/feature-104.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { + Handshake, + Download, + GraduationCap, + Headphones, + RefreshCw, + BarChart3, +} from "lucide-react"; + +import { Badge } from "@/components/ui/badge"; +import { cn } from "@/lib/utils"; + +const cooperationSteps = [ + { + title: "برگزاری جلسه معرفی و نیازسنجی", + description: "شناسایی نیازها و تعیین اهداف همکاری", + icon: Handshake, + }, + { + title: "نصب نسخه اختصاصی", + description: "راه‌اندازی سامانه متناسب با کسب‌وکار شما", + icon: Download, + }, + { + title: "آموزش و راه‌اندازی", + description: "آموزش تیم و راه‌اندازی عملیاتی", + icon: GraduationCap, + }, + { + title: "۶ ماه پشتیبانی رایگان", + description: "پشتیبانی فنی در ماه‌های اول همکاری", + icon: Headphones, + }, + { + title: "ادامه پشتیبانی و توسعه ماهانه", + description: "پشتیبانی مستمر و به‌روزرسانی‌های دوره‌ای", + icon: RefreshCw, + }, + { + title: "ارزیابی و بهینه‌سازی مستمر", + description: "بررسی عملکرد و پیشنهاد بهبود", + icon: BarChart3, + }, +]; + +interface Feature104Props { + className?: string; +} + +const Feature104 = ({ className }: Feature104Props) => { + return ( +
+
+
+ + مسیر همکاری + +

+ فرآیند همکاری +

+

+ از اولین تماس تا راه‌اندازی و پشتیبانی، در هر مرحله همراه شما + هستیم تا همکاری با مستر دکور ساده و شفاف باشد. +

+
+ +
+
+ +
+
+ +
+ {cooperationSteps.map((step, index) => ( +
+
+ +
+

+ {step.title} +

+

+ {step.description} +

+
+ ))} +
+
+
+ ); +}; + +export { Feature104 }; diff --git a/components/sections/feature/feature-188.tsx b/components/sections/feature/feature-188.tsx new file mode 100644 index 0000000..0b38468 --- /dev/null +++ b/components/sections/feature/feature-188.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { ArrowLeft } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +interface Feature188Resource { + title: string; + description: string; + href: string; + imageSrc: string; +} + +interface Feature188Props { + className?: string; + title?: string; + resources?: Feature188Resource[]; +} + +const defaultResources: Feature188Resource[] = [ + { + title: "نصب نسخه اختصاصی برای هر مجموعه", + description: + "هر سازمان نسخهٔ جداگانه و مستقل از سامانه را روی سرور خود دریافت می‌کند. داده‌ها کاملاً در اختیار شما می‌مانند و امنیت و کنترل کامل بر اطلاعات خود دارید.", + href: "#", + imageSrc: + "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-8-wide.svg", + }, + { + title: "امکان شخصی‌سازی کامل بر اساس مدل کاری", + description: + "ماژول‌ها، فیلدها و گردش‌کارها متناسب با فرایندهای داخلی مجموعهٔ شما تنظیم می‌شوند. سامانه دقیقاً با نیاز و مدل کاری شما هماهنگ می‌شود.", + href: "#", + imageSrc: + "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-8-wide.svg", + }, + { + title: "پشتیبانی مستقیم و توسعه مستمر", + description: + "تیم فنی ما به‌صورت مستقیم پاسخگوی سوالات و مشکلات شماست. امکانات جدید و به‌روزرسانی‌های امنیتی و عملکردی به‌طور منظم ارائه می‌شود.", + href: "#", + imageSrc: + "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-8-wide.svg", + }, +]; + +const Feature188 = ({ + className, + title = "چرا این سامانه انتخاب مجموعه‌های حرفه‌ای است؟", + resources = defaultResources, +}: Feature188Props) => { + return ( +
+
+

+ {title} +

+ +
+ {resources.map((resource, index) => ( + + ))} +
+
+
+ ); +}; + +export { Feature188 }; diff --git a/components/sections/feature/feature-189-abeshde-hamkar.tsx b/components/sections/feature/feature-189-abeshde-hamkar.tsx new file mode 100644 index 0000000..c0f0a1c --- /dev/null +++ b/components/sections/feature/feature-189-abeshde-hamkar.tsx @@ -0,0 +1,107 @@ +"use client"; + +import Link from "next/link"; +import { ArrowRight } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; + +interface Feature189AbeshdeHamkarProps { + className?: string; +} + +const Feature189AbeshdeHamkar = ({ className }: Feature189AbeshdeHamkarProps) => { + const features = [ + { + title: "بهره‌وری بیشتر", + description: + "ثبت و پیگیری معاملات آبشده بدون تماس‌های مکرر؛ دسترسی لحظه‌ای به مانده و ریز تراکنش‌ها تا روی کار اصلی تمرکز کنید.", + }, + { + title: "همکاری و شفافیت", + description: + "همه طرفین معامله به‌صورت شفاف به داده‌ها دسترسی دارند؛ همکاری بدون ابهام و تسویه بر اساس اعداد واقعی.", + }, + { + title: "گزارش و تحلیل", + description: + "گزارش‌های تفصیلی و ابزارهای تحلیلی برای ارزیابی عملکرد و تصمیم‌گیری بر اساس داده.", + }, + ]; + + return ( +
+
+
+ {/* تصاویر سکشن آبشده همکار */} +
+
+ Professional Business Collaboration Meeting abshodeh-hamkar +
+
+ Financial Management Productivity Workspace abshodeh-hamkar +
+
+ Advanced Data Analysis Dashboard Office abshodeh-hamkar +
+
+ + {/* محتوا */} +
+

+ با ابزارهای حرفه‌ای، کسب‌وکارتان را متحول کنید +

+ +
+ {features.map((feature) => ( +
+
+

+ {feature.title} +

+

+ {feature.description} +

+
+
+ ))} +
+ + +
+
+
+
+ ); +}; + +export { Feature189AbeshdeHamkar }; diff --git a/components/sections/feature/feature-243-gold-store.tsx b/components/sections/feature/feature-243-gold-store.tsx new file mode 100644 index 0000000..a4d70d3 --- /dev/null +++ b/components/sections/feature/feature-243-gold-store.tsx @@ -0,0 +1,87 @@ +"use client"; + +import { Layout, UserPlus, Users, Zap } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { useContent } from "@/components/layout/content-provider"; +import { Card, CardContent } from "@/components/ui/card"; + +const iconClass = "size-10 stroke-white transition-all ease-in-out group-hover:scale-105"; +const stepIcons = [ + , + , + , + , +]; + +interface Feature243GoldStoreProps { + className?: string; +} + +const Feature243GoldStore = ({ className }: Feature243GoldStoreProps) => { + const content = useContent(); + const { heading, steps: stepsContent } = content.pages.product.forushgahSazTala.feature243; + const steps = stepsContent.map((step, index) => ({ + title: step.title, + description: step.description, + icon: stepIcons[index] ?? stepIcons[0], + })); + + return ( +
+
+
+

+ {heading} +

+
+ +
+ {steps.map((step, index) => ( +
+
+
+
+
+
+ + + +
+ {step.icon} +
+ +
+

+ {step.title} +

+ {step.description ? ( +

+ {step.description} +

+ ) : null} +
+
+
+
+ ))} +
+
+
+ ); +}; + +export { Feature243GoldStore }; diff --git a/components/sections/feature/feature-246.tsx b/components/sections/feature/feature-246.tsx new file mode 100644 index 0000000..50db527 --- /dev/null +++ b/components/sections/feature/feature-246.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { BadgeCheck } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +interface Feature246Props { + className?: string; +} + +const Feature246 = ({ className }: Feature246Props) => { + const benefits = [ + "برگزاری جلسه معرفی و نیازسنجی", + "نصب نسخه اختصاصی", + "آموزش و راه‌اندازی", + "۶ ماه پشتیبانی رایگان", + "ادامه پشتیبانی و توسعه ماهانه", + ]; + + return ( +
+
+
+

+ بخش فرآیند همکاری +

+
+ {benefits.map((benefit, index) => ( +
+
+ +
+ + {benefit} + +
+ ))} +
+
+
+ +
+
+
+ ); +}; + +export { Feature246 }; diff --git a/components/sections/feature/feature-247.tsx b/components/sections/feature/feature-247.tsx new file mode 100644 index 0000000..01814be --- /dev/null +++ b/components/sections/feature/feature-247.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { motion, MotionValue, useScroll, useTransform } from "motion/react"; +import React from "react"; +import { ComponentPropsWithoutRef, FC, ReactNode, useRef } from "react"; + +import { cn } from "@/lib/utils"; +import { useContent } from "@/components/layout/content-provider"; + +const Feature247 = () => { + const content = useContent(); + const { title, description } = content.pages.product.forushgahSazTala.feature247; + return ( +
+
+
+ + {description} + +
+
+
+ ); +}; + +export { Feature247 }; + +// Below is the modified component from Magic UI +// Original source: https://magicui.design/docs/components/text-reveal +// Modified to follow our coding standards and design system +// We respect copyright and attribution to the original creators + +interface TextRevealProps extends ComponentPropsWithoutRef<"div"> { + children: string; + title?: string; + maxWidth?: string; +} + +const TextReveal: FC = ({ + children, + title, + className, + maxWidth, +}) => { + const targetRef = useRef(null); + const { scrollYProgress } = useScroll({ + target: targetRef, + }); + + const words = children.split(" "); + + return ( +
+
+
+ + {title} + + + {words.map((word, i) => { + const start = i / words.length; + const end = start + 1 / words.length; + return ( + + {word} + + ); + })} + +
+
+
+ ); +}; + +interface WordProps { + children: ReactNode; + progress: MotionValue; + range: [number, number]; +} + +const Word: FC = ({ children, progress, range }) => { + const opacity = useTransform(progress, range, [0, 1]); + return ( + + {children} + + {children} + + + ); +}; diff --git a/components/sections/feature/feature-268.tsx b/components/sections/feature/feature-268.tsx new file mode 100644 index 0000000..cb12ec6 --- /dev/null +++ b/components/sections/feature/feature-268.tsx @@ -0,0 +1,138 @@ +import { ArrowRight, Check } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { + CardBody, + CardContainer, + CardItem, +} from "@/components/aceternity/3d-card"; +import { Button } from "@/components/ui/button"; + +const defaultFeatures = [ + { title: "Instant Implementation", description: "Get access to your blocks the moment you pay." }, + { title: "One-Time Payment", description: "No subscriptions. Just pay once and own it forever." }, + { title: "Developer Friendly", description: "Clean, copy-paste-ready code with full Tailwind + shadcn support." }, + { title: "Fully Responsive", description: "Every block looks perfect on all screen sizes, out of the box." }, + { title: "Production Ready", description: "Battle-tested components you can drop into real projects today." }, +]; + +interface Feature268Props { + className?: string; + title?: string; + description?: string; + showButtons?: boolean; + listTitle?: string; + features?: { title: string; description?: string }[]; + cardTitle?: string; + cardWelcome?: string; + cardImage?: string; + showTryNow?: boolean; +} + +const Feature268 = ({ + className, + title = "Drop-In Ready Blocks to Supercharge Your App", + description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", + showButtons = true, + listTitle = "ShadcnBlocks Perks", + features = defaultFeatures, + cardTitle = "#1 Biggest Shadcn Blocks Collection", + cardWelcome = "Welcome to SHADCN BLOCKS", + cardImage, + showTryNow = true, +}: Feature268Props) => { + const defaultCardImage = "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img27.jpeg"; + return ( +
+
+
+
+

+ {title} +

+

+ {description} +

+ {showButtons && ( +
+ + +
+ )} +
    +
  • +

    + {listTitle} +

    +
  • + {features.map((feature) => ( +
  • + +

    + {feature.title} + {feature.description != null && feature.description !== "" && ( + {feature.description} + )} +

    +
  • + ))} +
+
+ + + {cardTitle != null && cardTitle !== "" && ( + + {cardTitle} + + )} + {cardWelcome != null && cardWelcome !== "" && ( + + Welcome to + SHADCN BLOCKS + + )} + + thumbnail + + +
+ {showTryNow && ( + + + + )} + +
+
+
+
+
+
+ ); +}; + +export { Feature268 }; diff --git a/components/sections/feature/feature-269-gold-store.tsx b/components/sections/feature/feature-269-gold-store.tsx new file mode 100644 index 0000000..3fba853 --- /dev/null +++ b/components/sections/feature/feature-269-gold-store.tsx @@ -0,0 +1,111 @@ +"use client"; + +import Link from "next/link"; +import { Check } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { + CardBody, + CardContainer, + CardItem, +} from "@/components/aceternity/3d-card"; +import { Button } from "@/components/ui/button"; +import { useContent } from "@/components/layout/content-provider"; + +interface Feature269GoldStoreProps { + className?: string; + cardImage?: string; +} + +const defaultCardImage = + "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img7.jpeg"; + +const Feature269GoldStore = ({ className, cardImage }: Feature269GoldStoreProps) => { + const content = useContent(); + const { heading, description, featuresLabel, cardTitle1, cardTitle2, cardWelcome, cardWelcomeBrand, features } = content.pages.product.forushgahSazTala.feature269; + return ( +
+
+
+
+

+ {heading} +

+

+ {description} +

+
+ + +
+
    +
  • +

    {featuresLabel}

    +
  • + {features.map((featureTitle) => ( +
  • + + + {featureTitle} + +
  • + ))} +
+
+ + + + {cardTitle1} + {cardTitle2} + + + {cardWelcome} + {cardWelcomeBrand} + + + فروشگاه آنلاین طلا + +
+ مستر دکور +
+
+
+
+
+
+ ); +}; + +export { Feature269GoldStore }; diff --git a/components/sections/feature/feature-269.tsx b/components/sections/feature/feature-269.tsx new file mode 100644 index 0000000..fa73712 --- /dev/null +++ b/components/sections/feature/feature-269.tsx @@ -0,0 +1,93 @@ +import { BadgeCheck } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { + CardBody, + CardContainer, + CardItem, +} from "@/components/aceternity/3d-card"; + +interface Feature269Props { + className?: string; +} + +const Feature269 = ({ className }: Feature269Props) => { + const features = [ + "Instant Implementation", + "One-Time Payment", + "Developer Friendly", + "Fully Responsive", + "Production Ready", + "Premium Support", + "Regular Updates", + "Customizable Design", + "Performance Optimized", + "Accessibility Compliant", + "Cross-Browser ", + "Documentation Included", + ]; + + return ( +
+
+
+
+

+ Why Choose Shadcn Blocks? +

+
+ +

FEATURES

+ +
+
    + {features.map((feature) => ( +
  • + +

    + {feature} +

    +
  • + ))} +
+
+ + + + #1 Biggest Shadcn Blocks Collection + + + Welcome to + SHADCN BLOCKS + + + thumbnail + + + +
+
+
+ ); +}; + +export { Feature269 }; + diff --git a/components/sections/feature/feature-270.tsx b/components/sections/feature/feature-270.tsx new file mode 100644 index 0000000..db0284e --- /dev/null +++ b/components/sections/feature/feature-270.tsx @@ -0,0 +1,324 @@ +"use client"; +import { motion } from "motion/react"; +import React, { useState } from "react"; + +import { cn } from "@/lib/utils"; + +const defaultFeatures = [ + { + number: "01", + title: "Instant Implementation", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et", + image: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img11.jpeg", + imageOrder: "order-1", + contentOrder: "order-2", + }, + { + number: "02", + title: "One time payment", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et", + image: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img12.jpeg", + imageOrder: "order-2", + contentOrder: "order-1", + }, + { + number: "03", + title: "Developer Friendly", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et", + image: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img14.jpeg", + imageOrder: "order-1", + contentOrder: "order-2", + }, +]; + +interface Feature270Item { + number: string; + title: string; + description: string; + image: string; + imageOrder: string; + contentOrder: string; +} + +interface Feature270Props { + className?: string; + heading?: string; + features?: Feature270Item[]; + headingAlign?: "left" | "right" | "center"; + contentAlign?: "left" | "center" | "right"; + showNumbers?: boolean; + /** وقتی false باشد، دکمه/لینک «Learn More» روی کارت‌ها نمایش داده نمی‌شود. */ + showLearnMore?: boolean; +} + +const Feature270 = ({ + className, + heading = "Why Choose Shadcn Blocks?", + features = defaultFeatures, + headingAlign = "left", + contentAlign = "left", + showNumbers = true, + showLearnMore = true, +}: Feature270Props) => { + const headingClass = { + left: "text-left", + right: "text-right", + center: "text-center", + }[headingAlign]; + const contentClass = { + left: "text-left", + center: "text-center", + right: "text-right", + }[contentAlign]; + + return ( +
+
+
+

+ {heading} +

+
+
+ {features.map((feature, index) => { + const cardContent = ( +
+
+ thumbnail +
+
+ {showNumbers && ( +

+ {feature.number} +

+ )} +

+ {feature.title} +

+

+ {feature.description} +

+
+
+ ); + const cardClassName = "w-full h-full min-h-[28rem] rounded-3xl !bg-muted/70 p-4 flex flex-col"; + return ( + + {cardContent} + + ); + })} +
+
+
+ ); +}; + +export { Feature270 }; + +// Below is the modified component from Aceternity UI +// Original source: npx shadcn@latest add https://ui.aceternity.com/registry/3d-pin.json +// Modified to follow our coding standards and design system +// We respect copyright and attribution to the original creators + +export const PinContainer = ({ + children, + title, + href, + className, + containerClassName, + showPerspective = true, +}: { + children: React.ReactNode; + title?: string; + href?: string; + className?: string; + containerClassName?: string; + /** وقتی false باشد، دکمه/برچسب «Learn More» روی هاور نمایش داده نمی‌شود. افکت سه‌بعدی هاور حفظ می‌شود. */ + showPerspective?: boolean; +}) => { + const [transform, setTransform] = useState( + "translate(-50%,-50%) rotateX(0deg)", + ); + + const onMouseEnter = () => { + setTransform("translate(-50%,-50%) rotateX(40deg) scale(0.8)"); + }; + const onMouseLeave = () => { + setTransform("translate(-50%,-50%) rotateX(0deg) scale(1)"); + }; + + const wrapperClassName = cn( + "group/pin relative z-10", + href ? "cursor-pointer" : "cursor-default", + containerClassName, + ); + + const outerTransform = showPerspective + ? "rotateX(70deg) translateZ(0deg)" + : "rotateX(0deg) translateZ(0deg)"; + + const content = showPerspective ? ( + <> +
+
+
{children}
+
+
+ {title != null && } + + ) : ( +
{children}
+ ); + + if (href) { + return ( + + {content} + + ); + } + + return ( +
+ {content} +
+ ); +}; + +export const PinPerspective = ({ + title, +}: { + title?: string; + href?: string; +}) => { + return ( + +
+
+ + + {title} + + + + +
+ +
+ <> + + + + +
+ + <> + + + + + +
+
+ ); +}; + diff --git a/components/sections/feature/feature-294.tsx b/components/sections/feature/feature-294.tsx new file mode 100644 index 0000000..31c25ee --- /dev/null +++ b/components/sections/feature/feature-294.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { AudioLines, Box, Globe, LayoutDashboard, ShieldUser, Users } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { SparklesCore } from "@/components/shadcnblocks/sparkles"; + +const features = [ + { + icon: Globe, + title: "یکپارچه‌سازی آسان", + description: "ساخته‌شده با فناوری‌های روز و APIهای تمیز برای ادغام بی‌دردسر با سامانه‌های شما.", + }, + { + icon: Box, + title: "عملکرد سریع", + description: "بهینه‌سازی شده برای بارگذاری فوری و حداقل مصرف منابع.", + }, + { + icon: ShieldUser, + title: "امنیت پیش‌فرض", + description: "امنیت در سطح سازمان با حفاظت داخلی و رعایت استانداردها.", + }, + { + icon: Users, + title: "همکاری تیمی", + description: "طراحی‌شده برای تیم‌ها با همکاری لحظه‌ای و کنترل دسترسی.", + }, + { + icon: AudioLines, + title: "یکپارچگی صوتی", + description: "پردازش پیشرفته با پشتیبانی از درک زبان طبیعی.", + }, + { + icon: LayoutDashboard, + title: "امکانات جامع پلتفرم", + description: "قیمت لحظه‌ای، مدیریت سفارش و مانده حساب آنلاین، ریز معاملات ۲۴/۷، کنترل وضعیت بازار و پنل ادمین.", + }, +]; + +interface Feature294Props { + className?: string; +} + +const Feature294 = ({ className }: Feature294Props) => { + return ( +
+
+
+

+ راهکار +

+

+ یک پلتفرم اختصاصی +
+ برای مدیریت کامل معاملات +

+
+
+ {/* Gradients */} +
+
+
+
+ + {/* Core component */} + + + {/* Radial Gradient to prevent sharp edges */} +
+
+ +

+ ویژگی‌های کلیدی: +

+ +
+ {features.map((feature, index) => { + const IconComponent = feature.icon; + return ( +
+ +
+

{feature.title}

+

+ {feature.description} +

+
+
+ ); + })} +
+
+
+ ); +}; + +export { Feature294 }; diff --git a/components/sections/feature/feature-carousel-grid.tsx b/components/sections/feature/feature-carousel-grid.tsx new file mode 100644 index 0000000..a94b783 --- /dev/null +++ b/components/sections/feature/feature-carousel-grid.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { ArrowRight, ChevronDown } from "lucide-react"; +import { useEffect, useState } from "react"; + +import { cn } from "@/lib/utils"; + +import type { CarouselApi } from "@/components/ui/carousel"; +import { + Carousel, + CarouselContent, + CarouselItem, +} from "@/components/ui/carousel"; + +const features = [ + { + id: "feature-1", + title: "Feature 1", + description: + "Nam vitae molestie arcu. Quisque eu libero orci. Aliquam imperdiet magna nec massa consectetur, id interdum ante congue.", + href: "#", + image: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-1.svg", + }, + { + id: "feature-2", + title: "Feature 2", + description: + "Nam vitae molestie arcu. Quisque eu libero orci. Aliquam imperdiet magna nec massa consectetur, id interdum ante congue.", + href: "#", + image: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-2.svg", + }, + { + id: "feature-3", + title: "Feature 3", + description: + "Nam vitae molestie arcu. Quisque eu libero orci. Aliquam imperdiet magna nec massa consectetur, id interdum ante congue.", + href: "#", + image: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-3.svg", + }, +]; + +interface FeatureCarouselGridProps { + className?: string; +} + +const FeatureCarouselGrid = ({ className }: FeatureCarouselGridProps) => { + const [selection, setSelection] = useState(0); + const [carouselApi, setCarouselApi] = useState(); + useEffect(() => { + if (!carouselApi) { + return; + } + carouselApi.scrollTo(selection); + }, [carouselApi, selection]); + useEffect(() => { + if (!carouselApi) { + return; + } + const updateSelection = () => { + setSelection(carouselApi.selectedScrollSnap()); + }; + carouselApi.on("select", updateSelection); + return () => { + carouselApi.off("select", updateSelection); + }; + }, [carouselApi]); + return ( +
+
+
+
+ + + {features.map((feature) => ( + + {feature.title} + + ))} + + +
+
+

+ Features +

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Elig + doloremque mollitia fugiat omnis! Porro facilis quo animi + consequatur. Explicabo. +

+
    + {features.map((feature, i) => ( +
  • setSelection(i)} + > +
    +
    + {feature.title} +
    +
    + +
    +
    +
    +

    + {feature.description} +

    + + Learn more{" "} + + +
    +
  • + ))} +
+
+
+
+
+ ); +}; + +export { FeatureCarouselGrid }; diff --git a/components/sections/feature/feature-carousel.tsx b/components/sections/feature/feature-carousel.tsx new file mode 100644 index 0000000..cef721d --- /dev/null +++ b/components/sections/feature/feature-carousel.tsx @@ -0,0 +1,170 @@ +"use client"; + +import { ChevronUp } from "lucide-react"; +import React, { useState, useEffect } from "react"; +import Link from "next/link"; + +import { cn } from "@/lib/utils"; +import { useContent } from "@/components/layout/content-provider"; + +import type { CarouselApi } from "@/components/ui/carousel"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; + +const MOBILE_BREAKPOINT = 768; +const AUTOPLAY_DELAY_MS = 3200; + +const FeatureCarousel = () => { + const content = useContent(); + const { featureCarousel } = content.pages.home; + const carouselItems = featureCarousel.items; + + const [openCardIndex, setOpenCardIndex] = useState(null); + const [isMobile, setIsMobile] = useState(false); + const [carouselApi, setCarouselApi] = useState(null); + + useEffect(() => { + const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); + const handler = () => setIsMobile(mq.matches); + handler(); + mq.addEventListener("change", handler); + return () => mq.removeEventListener("change", handler); + }, []); + + useEffect(() => { + if (!isMobile || !carouselApi) return; + const timer = setInterval(() => { + carouselApi.scrollNext(); + }, AUTOPLAY_DELAY_MS); + return () => clearInterval(timer); + }, [isMobile, carouselApi]); + + const handleCardClick = (index: number) => { + if (!isMobile) return; + setOpenCardIndex((prev) => (prev === index ? null : index)); + }; + + return ( +
+
+

+ {featureCarousel.heading} +

+ + + + {carouselItems.map((item, index) => ( + + +
handleCardClick(index)} + role={isMobile ? "button" : undefined} + aria-expanded={isMobile ? openCardIndex === index : undefined} + > +
+ {item.title} + {/* Overlay: hover on desktop, open state on mobile */} +
+ + {/* Text overlay */} +
+
+

+ {item.title} +

+
+

+ {item.description} +

+
+ e.stopPropagation()} + className="pointer-events-auto group/cta mb-2 flex cursor-pointer flex-col items-center justify-center transition-transform duration-300 ease-out group-hover/cta:-translate-y-1" + > + +

+ {featureCarousel.viewDetails} +

+ +
+
+
+ + + ))} + + +
+ + +
+ +
+
+ ); +}; + +export { FeatureCarousel }; + +const DottedDiv = ({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) => ( +
+
+
+
+
+
+
+
+
+ {children} +
+); diff --git a/components/sections/feature/feature-grid-with-icons.tsx b/components/sections/feature/feature-grid-with-icons.tsx new file mode 100644 index 0000000..d780bbf --- /dev/null +++ b/components/sections/feature/feature-grid-with-icons.tsx @@ -0,0 +1,64 @@ +import { + ShoppingCart, + FileText, + Settings, +} from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const features = [ + { + id: "feature-1", + title: "ثبت و مدیریت سفارش ها", + description: "ثبت و پیگیری سفارش‌ها", + icon: ShoppingCart, + }, + { + id: "feature-2", + title: "دسترسی کامل به گزارش گیری و معاملات", + description: "گزارش گیری کامل و معاملات ۲۴ ساعته", + icon: FileText, + }, + { + id: "feature-3", + title: "پنل مدیریت با ابزارهای پیشرفته", + description: "ابزارهای پیشرفته برای مدیریت ادمین و کنترل سیستم", + icon: Settings, + }, +]; + +interface FeatureGridWithIconsProps { + className?: string; +} + +const FeatureGridWithIcons = ({ className }: FeatureGridWithIconsProps) => { + return ( +
+
+

+ یک پلتفرم اختصاصی برای مدیریت کامل معاملات +

+
+ {features.map((feature) => ( +
+ +
+

+ {feature.title} +

+

+ {feature.description} +

+
+
+ ))} +
+
+
+ ); +}; + +export { FeatureGridWithIcons }; diff --git a/components/sections/feature/feature-list-section.tsx b/components/sections/feature/feature-list-section.tsx new file mode 100644 index 0000000..030880a --- /dev/null +++ b/components/sections/feature/feature-list-section.tsx @@ -0,0 +1,85 @@ +import { + Infinity as InfinityIcon, + MessagesSquare, + Zap, + ZoomIn, +} from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const feature = [ + { + title: "Quality", + description: + "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi necessitatibus, culpa at vitae molestias tenetur explicabo.", + icon: , + }, + { + title: "Innovation", + description: + "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi necessitatibus, culpa at vitae molestias tenetur explicabo.", + icon: , + }, + { + title: "Customer Support", + description: + "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi necessitatibus, culpa at vitae molestias tenetur explicabo.", + icon: , + }, + { + title: "Reliability", + description: + "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi necessitatibus, culpa at vitae molestias tenetur explicabo.", + icon: , + }, +]; + +interface FeatureListSectionProps { + className?: string; +} + +const FeatureListSection = ({ className }: FeatureListSectionProps) => { + return ( +
+
+
+
+

WHY WE ARE UNIQUE

+

+ Bringing the best to you by the best in the industry +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi + necessitatibus, culpa at vitae molestias tenetur explicabo. + Voluptatum amet architecto suscipit pariatur eligendi repellendus + mollitia dolore unde sint? +

+
+
+
+ {feature.map((feature, idx) => ( +
+ + {feature.icon} + +
+

+ {feature.title} +

+

+ {feature.description} +

+
+
+ ))} +
+
+
+ ); +}; + +export { FeatureListSection }; diff --git a/components/sections/feature/feature-partnership-highlights.tsx b/components/sections/feature/feature-partnership-highlights.tsx new file mode 100644 index 0000000..51f90cf --- /dev/null +++ b/components/sections/feature/feature-partnership-highlights.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { + ChartArea, + ChartNoAxesCombined, + Layout, + Medal, + Monitor, + Target, + TrendingUp, +} from "lucide-react"; + +import { cn } from "@/lib/utils"; + +import { Badge } from "@/components/ui/badge"; + +interface FeaturePartnershipHighlightsProps { + className?: string; + /** مسیر تصویر وسط سکشن (مثلاً برای صفحه آبشده همکار) */ + centerImageSrc?: string; + /** متن alt تصویر وسط */ + centerImageAlt?: string; +} + +const defaultCenterImage = + "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-1.svg"; + +const FeaturePartnershipHighlights = ({ + className, + centerImageSrc, + centerImageAlt = "", +}: FeaturePartnershipHighlightsProps) => { + const leftCards = [ + { + icon: Target, + title: "راهکارهای هدف‌مند", + description: + "امکانات متناسب با چالش‌های خاص پروژه شما برای رسیدن به نتیجه دقیق.", + }, + { + icon: Layout, + title: "یکپارچه‌سازی روان", + description: + "ادغام بی‌دردسر با سامانه‌های موجود برای حفظ تداوم و عملکرد.", + }, + { + icon: ChartArea, + title: "بینش داده لحظه‌ای", + description: + "مانیتورینگ داده در زمان واقعی برای تصمیم‌گیری بهتر و تنظیم سریع‌تر.", + }, + ]; + + const rightCards = [ + { + icon: TrendingUp, + title: "رشد راهبردی", + description: + "ابزارهای راهبردی برای مقیاس‌پذیری کسب‌وکار و پاسخ به نیازهای در حال تحول بازار.", + }, + { + icon: ChartNoAxesCombined, + title: "تحلیل‌های پیشرفته", + description: + "بینش عمیق‌تر با تحلیل‌های پیشرفته برای پیشتازی در تصمیم‌گیری.", + }, + { + icon: Monitor, + title: "مدیریت یکپارچه", + description: + "مدیریت و کنترل متمرکز همه جنبه‌های گردش کار از یک پلتفرم.", + }, + ]; + + return ( +
+
+
+ + + برجسته‌ها + +

+ توانایی همکاری خود را آزاد کنید +

+

+ مزایای فرآیند ساده‌شده را تجربه کنید؛ طراحی‌شده برای بهره‌وری و + موفقیت بیشتر. +

+
+ +
+ {/* ستون چپ — موبایل: اول */} +
+ {leftCards.map((card, index) => ( +
+
+ + + +

+ {card.title} +

+
+

+ {card.description} +

+
+ ))} +
+ + {/* تصویر وسط — فقط دسکتاپ */} +
+ {centerImageAlt} +
+ + {/* ستون راست — موبایل: دوم */} +
+ {rightCards.map((card, index) => ( +
+
+ + + +

+ {card.title} +

+
+

+ {card.description} +

+
+ ))} +
+
+
+
+ ); +}; + +export { FeaturePartnershipHighlights }; diff --git a/components/sections/feature/feature-product-detail-showcase.tsx b/components/sections/feature/feature-product-detail-showcase.tsx new file mode 100644 index 0000000..f0c8dba --- /dev/null +++ b/components/sections/feature/feature-product-detail-showcase.tsx @@ -0,0 +1,105 @@ +"use client"; + +import Image from "next/image"; +import { Circle } from "lucide-react"; + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { cn } from "@/lib/utils"; + +interface FeatureProductDetailShowcaseProps { + className?: string; +} + +const productDetails = { + brand: "مستر دکور", + name: "ویترین طلا", + description: + "نمایش حرفه ای محصولات طلا با تجربه کاربری روان، تمرکز بر اعتماد مشتری و مدیریت یکپارچه در یک صفحه محصول کامل.", + image: "/assets/main-pic/product/sampleghaleb.png", + features: [ + "نمایش شفاف اطلاعات محصول با ساختار خوانا", + "هماهنگ با رابط کاربری مدرن و واکنش گرا", + "تمرکز بر تجربه مشتری در تصمیم خرید", + "آماده اتصال به جریان های فروش و مشاوره", + ], + info: [ + { + title: "معرفی محصول", + content: + "این قالب برای نمایش یک محصول به صورت کامل طراحی شده است تا مخاطب در همان نگاه اول، تصویر دقیق، ویژگی ها و ارزش پیشنهادی محصول را درک کند.", + }, + { + title: "مخاطب هدف", + content: + "مناسب فروشگاه ها و کسب وکارهای فعال در بازار طلا که به دنبال نمایش حرفه ای محصولات و افزایش اعتماد کاربران در صفحه محصول هستند.", + }, + { + title: "مزیت برای کسب وکار", + content: + "ساختار ساده و تمیز این بخش باعث می شود تمرکز کاربر روی ارزش محصول باقی بماند و فرآیند آشنایی تا اقدام کاربر کوتاه تر شود.", + }, + ], +}; + +const FeatureProductDetailShowcase = ({ + className, +}: FeatureProductDetailShowcaseProps) => { + return ( +
+
+
+
+ {productDetails.name} +
+ +
+

+ {productDetails.brand} +

+

+ {productDetails.name} +

+

+ {productDetails.description} +

+ +
    + {productDetails.features.map((feature) => ( +
  • + + {feature} +
  • + ))} +
+ + + {productDetails.info.map((item) => ( + + + {item.title} + + + {item.content} + + + ))} + +
+
+
+
+ ); +}; + +export { FeatureProductDetailShowcase }; diff --git a/components/sections/feature/feature-text-section.tsx b/components/sections/feature/feature-text-section.tsx new file mode 100644 index 0000000..ff935cd --- /dev/null +++ b/components/sections/feature/feature-text-section.tsx @@ -0,0 +1,28 @@ +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; + +interface FeatureTextSectionProps { + className?: string; +} + +const FeatureTextSection = ({ className }: FeatureTextSectionProps) => { + return ( +
+
+
+

+ روش سنتی، سرعت بازار امروز را ندارد +

+

+ در معاملات آبشده، تماس‌های مکرر برای دریافت قیمت، هماهنگی خرید و فروش و ارسال مانده حساب به مشتریان، زمان‌بر و پرریسک است. +ثبت دستی یا پراکنده اطلاعات، احتمال اختلاف حساب و سوءتفاهم را افزایش می‌دهد. +

+ +
+
+
+ ); +}; + +export { FeatureTextSection }; diff --git a/components/sections/feature/feature-workspace-tabs.tsx b/components/sections/feature/feature-workspace-tabs.tsx new file mode 100644 index 0000000..e280320 --- /dev/null +++ b/components/sections/feature/feature-workspace-tabs.tsx @@ -0,0 +1,149 @@ +"use client"; + +import { AnimatePresence, motion } from "framer-motion"; +import Image from "next/image"; +import { useMemo, useState } from "react"; + +import { ScrollableTabsList } from "@/components/shadcnblocks/scrollable-tabslist"; +import { ScrollBar } from "@/components/ui/scroll-area"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { cn } from "@/lib/utils"; + +interface FeatureWorkspaceTabsProps { + className?: string; +} + +const tabItems = [ + { + value: "display", + label: "نمایش حرفه‌ای", + eyebrow: "ویترین و استند", + title: "ویترینی بسازید که فروش را سریع تر می کند", + description: + "چیدمان اصولی استندها باعث می شود سرویس های طلا و جواهر بهتر دیده شوند، تصمیم خرید سریع تر گرفته شود ", + image: "/assets/main-pic/feature/feature1.webp", + stats: [ + { value: "۳۵۰+", label: "پروژه اجراشده در فروشگاه" }, + { value: "۱۵+", label: "سال تجربه در دکور طلافروشی" }, + ], + }, + { + value: "identity", + label: "هماهنگ با برند", + eyebrow: "دکور مجموعه شما", + title: "دکور هماهنگ با هویت برند طلافروشی شما", + description: + "روکش، متریال و جزئیات اجرایی را بر اساس سبک فروشگاه شما انتخاب می کنیم تا هر استند بخشی از هویت برندتان باشد و در ذهن مشتری ماندگار بماند.", + image: "/assets/main-pic/feature/feature1.webp", + stats: [ + { value: "۹۳٪", label: "رضایت مشتریان در نظرسنجی‌ها" }, + { value: "۴۸ ساعت", label: "شروع پشتیبانی پس از تحویل" }, + ], + }, + { + value: "lifecycle", + label: "از مشاوره تا پس از فروش", + eyebrow: "همراهی مستر دکور", + title: "از مشاوره تا نصب، یک مسیر شفاف و قابل اتکا", + description: + "از بازدید و نیازسنجی تا تولید و نصب، برنامه زمان بندی مشخص ارائه می کنیم و بعد از تحویل نیز با خدمات پس از فروش کنار شما می مانیم .", + image: "/assets/main-pic/feature/feature1.webp", + stats: [ + { value: "۱۰۰٪", label: "هماهنگی با ابعاد واقعی فضا" }, + { value: "گارانتی", label: "تعهد کیفی روی اجرای سازه" }, + ], + }, +]; + +const FeatureWorkspaceTabs = ({ className }: FeatureWorkspaceTabsProps) => { + const [activeTab, setActiveTab] = useState("display"); + const activeItem = useMemo( + () => tabItems.find((item) => item.value === activeTab) ?? tabItems[0], + [activeTab], + ); + + return ( +
+
+ +
+
+ + + + + {activeItem.eyebrow} + +
+

+ {activeItem.title} +

+
+

{activeItem.description}

+
+ + + +
+ + + + {tabItems.map((item) => ( + + {item.label} + + ))} + + + +
+ +
+ + + +
+ {activeItem.title} +
+
+
+
+
+ +
+
+ ); +}; + +export { FeatureWorkspaceTabs }; diff --git a/components/sections/feature/features-card-grid.tsx b/components/sections/feature/features-card-grid.tsx new file mode 100644 index 0000000..5800e43 --- /dev/null +++ b/components/sections/feature/features-card-grid.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { Card, CardHeader } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import { Settings2, Sparkles, Zap } from "lucide-react"; +import { ReactNode } from "react"; +import { useContent } from "@/components/layout/content-provider"; + +const featureIcons = [Zap, Settings2, Sparkles]; + +export default function FeaturesCardGrid() { + const content = useContent(); + const { featuresCardGrid } = content.pages.home; + const features = featuresCardGrid.items.map((item, i) => ({ + ...item, + icon: featureIcons[i] ?? Zap, + })); + + return ( +
+
+
+

+ {featuresCardGrid.heading} +

+
+
+ {features.map((item) => { + const Icon = item.icon; + return ( + + + + + +

+ {item.title} +

+

+ {item.description} +

+
+
+ ); + })} +
+
+
+ ); +} + +const CardDecorator = ({ children }: { children: ReactNode }) => ( +
+
+
+ {children} +
+
+); diff --git a/components/sections/feature/features-icon-grid.tsx b/components/sections/feature/features-icon-grid.tsx new file mode 100644 index 0000000..1366b3a --- /dev/null +++ b/components/sections/feature/features-icon-grid.tsx @@ -0,0 +1,38 @@ +'use client' + +import { Target, CheckCircle, Shield, Briefcase, Headphones, TrendingUp } from 'lucide-react' +import { useContent } from '@/components/layout/content-provider' + +const icons = [Target, CheckCircle, Shield, Briefcase, Headphones, TrendingUp] + +export default function FeaturesIconGrid() { + const content = useContent() + const { featuresIconGrid } = content.pages.home + + return ( +
+
+
+

{featuresIconGrid.heading}

+

{featuresIconGrid.description}

+
+ +
+ {featuresIconGrid.items.map((item, index) => { + const Icon = icons[index] ?? Target + return ( +
+
+ +

{item}

+
+
+ ) + })} +
+
+
+ ) +} + + diff --git a/components/sections/feature/features-primary.tsx b/components/sections/feature/features-primary.tsx new file mode 100644 index 0000000..d13dae4 --- /dev/null +++ b/components/sections/feature/features-primary.tsx @@ -0,0 +1,18 @@ +export default function FeaturesPrimary() { + + return ( + <> +
+
+
+

+ ما چه می‌کنیم؟ +

+

ما به‌صورت تخصصی برای فعالان بازار طلا و فلزات گران‌بها نرم‌افزار طراحی می‌کنیم.محصولات ما بر اساس نیاز عملیاتی این بازار توسعه یافته‌اند، نه صرفاً بر اساس الگوهای عمومی نرم‌افزاری. سامانه‌های ما در حال حاضر توسط مجموعه‌های فعال بازار مورد استفاده قرار می‌گیرند و در شرایط واقعی معامله آزموده شده‌اند.

+
+
+
+ + + ) +} diff --git a/components/sections/feature/product-categories-2.tsx b/components/sections/feature/product-categories-2.tsx new file mode 100644 index 0000000..6db0103 --- /dev/null +++ b/components/sections/feature/product-categories-2.tsx @@ -0,0 +1,127 @@ +"use client"; + +import Link from "next/link"; +import { cn } from "@/lib/utils"; + +type ProductCategory = { + title: string; + image: { + src: string; + alt: string; + }; + summary: string; + link: string; +}; + +type ProductCategories2Props = { + title?: string; + productCategories?: ProductCategory[]; + className?: string; +}; + +const PRODUCT_CATEGORIES = { + title: "ما چه خدماتی ارائه میدیم؟", + productCategories: [ + { + title: "ویترین طلا", + summary: "استندها و ملزوماتی برای نمایش لوکس سرویس های طلا با چیدمان حرفه ای", + image: { + src: "/assets/main-pic/service 1.png", + alt: "", + }, + link: "/product?category=vitrin-tala", + }, + { + title: "ویترین خردسال", + summary: "چیدمان هدفمند برای نمایش امن و منظم محصولات خردسال", + image: { + src: "/assets/main-pic/service 2.jpeg", + alt: "", + }, + link: "/product?category=vitrin-khorde-sal", + }, + { + title: "ویترین جواهر", + summary: "طراحی ویترین جواهر با تاکید بر درخشش، جزئیات و تجربه بصری مشتری", + image: { + src: "/assets/main-pic/service 3.jpeg", + alt: "", + }, + link: "/product?category=vitrin-javaher", + }, + ], +}; + +const ProductCategories2 = ({ + title = PRODUCT_CATEGORIES.title, + productCategories = PRODUCT_CATEGORIES.productCategories, + className, +}: ProductCategories2Props) => { + return ( +
+
+
+

+ {title} +

+
+ {productCategories?.map((item, index) => ( +
+ +
+ ))} +
+
+
+
+ ); +}; + +const ProductCategoryCard = ({ + title, + image, + summary, + link, +}: ProductCategory) => { + return ( +
+
+
+
+
+ +
+ {image.alt} +
+
+

+ {title} +

+

+ {summary} +

+
+ +
+
+
+ ); +}; + +export { ProductCategories2 }; diff --git a/components/sections/feature/product-detail-gallery.tsx b/components/sections/feature/product-detail-gallery.tsx new file mode 100644 index 0000000..797eaf3 --- /dev/null +++ b/components/sections/feature/product-detail-gallery.tsx @@ -0,0 +1,113 @@ +"use client"; + +import Image from "next/image"; +import { useState } from "react"; + +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import { cn } from "@/lib/utils"; + +export interface ProductDetailGalleryProps { + images: { src: string; alt: string }[]; + className?: string; +} + +/** موبایل: کاروسل با زوم هاور. دسکتاپ: تصویر بزرگ + ردیف بندانگشتی؛ کلیک بندانگشتی → جابه‌جایی به نمای بزرگ */ +export function ProductDetailGallery({ images, className }: ProductDetailGalleryProps) { + const [activeIndex, setActiveIndex] = useState(0); + + if (!images.length) return null; + + const safeIndex = Math.min(activeIndex, images.length - 1); + const main = images[safeIndex] ?? images[0]!; + + const zoomImageClass = + "object-cover object-center transition-transform duration-500 ease-out motion-safe:group-hover:scale-[1.04]"; + + return ( +
+ {/* موبایل: کاروسل */} +
+ + + {images.map((img, index) => ( + +
+ {img.alt} +
+
+ ))} +
+
+ + +
+
+
+ + {/* دسکتاپ: بزرگ + بندانگشتی‌ها */} +
+
+ {main.alt} +
+ + {images.length > 1 ? ( +
+ {images.map((img, index) => { + if (index === safeIndex) return null; + return ( + + ); + })} +
+ ) : null} +
+
+ ); +} diff --git a/components/sections/feature/product-detail-specs.tsx b/components/sections/feature/product-detail-specs.tsx new file mode 100644 index 0000000..c6077ea --- /dev/null +++ b/components/sections/feature/product-detail-specs.tsx @@ -0,0 +1,55 @@ +"use client"; + +import type { ProductDetail } from "@/lib/catalog-products"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { cn } from "@/lib/utils"; + +export interface ProductDetailSpecsProps { + product: ProductDetail; + className?: string; +} + +/** + * بلوک توضیحات محصول: نام، رنگ، متریال، توضیح و ویژگی‌ها + * (الهام از product-detail2 بدون قیمت، فرم، تعداد و دکمه خرید) + */ +export function ProductDetailSpecs({ product, className }: ProductDetailSpecsProps) { + return ( +
+

{product.name}

+ +
+ رنگ + {product.color} +
+ +
+ متریال + {product.material} +
+ +

{product.description}

+ +
+

ویژگی‌های محصول

+ + {product.features.map((item, index) => ( + + +

{item.title}

+
+ +

{item.content}

+
+
+ ))} +
+
+
+ ); +} diff --git a/components/sections/feature/product-detail-view.tsx b/components/sections/feature/product-detail-view.tsx new file mode 100644 index 0000000..2febe40 --- /dev/null +++ b/components/sections/feature/product-detail-view.tsx @@ -0,0 +1,26 @@ +"use client"; + +import type { ProductDetail } from "@/lib/catalog-products"; +import { ProductDetailGallery } from "@/components/sections/feature/product-detail-gallery"; +import { ProductDetailSpecs } from "@/components/sections/feature/product-detail-specs"; +import { cn } from "@/lib/utils"; + +export interface ProductDetailViewProps { + product: ProductDetail; + className?: string; +} + +export function ProductDetailView({ product, className }: ProductDetailViewProps) { + return ( +
+
+
+ +
+ +
+
+
+
+ ); +} diff --git a/components/sections/feature/product-list10-catalog.tsx b/components/sections/feature/product-list10-catalog.tsx new file mode 100644 index 0000000..f564cb5 --- /dev/null +++ b/components/sections/feature/product-list10-catalog.tsx @@ -0,0 +1,355 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { Eye } from "lucide-react"; +import { useEffect, useMemo, useState } from "react"; + +import { AspectRatio } from "@/components/ui/aspect-ratio"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import type { ProductCategory, ProductItem } from "@/lib/catalog-products"; +import { CATALOG_PRODUCTS } from "@/lib/catalog-products"; +import { cn } from "@/lib/utils"; + +interface CategorySectionMeta { + category: ProductCategory; + categoryHeading: string; + promo: { + kicker: string; + title: string; + ctaLabel: string; + href: string; + image: string; + imageAlt: string; + }; +} + +interface ProductList10CatalogProps { + initialCategory?: string; + className?: string; +} + +const CATEGORY_OPTIONS: Array<{ value: ProductCategory; label: string }> = [ + { value: "vitrin-tala", label: "ویترین طلا" }, + { value: "vitrin-khorde-sal", label: "ویترین خردسال" }, + { value: "vitrin-javaher", label: "ویترین جواهر" }, + { value: "mankan-service", label: "مانکن سرویس" }, + { value: "mankan-ring", label: "مانکن انگشتر" }, + { value: "mankan-earring", label: "مانکن گوشواره" }, +]; + +const CATEGORY_SECTIONS: CategorySectionMeta[] = [ + { + category: "vitrin-tala", + categoryHeading: "ویترین طلا", + promo: { + kicker: "خط ویترین طلا", + title: "استندها و ویترین هایی برای نمایش حرفه ای سرویس های طلا با چیدمان لوکس و منظم.", + ctaLabel: "مشاهده این دسته", + href: "/product?category=vitrin-tala", + image: "/assets/main-pic/product/card-header/محصول 1.jpg", + imageAlt: "نمونه ویترین طلا", + }, + }, + { + category: "vitrin-khorde-sal", + categoryHeading: "ویترین خردسال", + promo: { + kicker: "ویترین خردسال", + title: "چیدمان امن، کاربردی و چشم نواز برای بخش خردسال.", + ctaLabel: "مشاهده این دسته", + href: "/product?category=vitrin-khorde-sal", + image: "/assets/main-pic/product/card-header/محصول 2.jpg", + imageAlt: "نمونه ویترین خردسال", + }, + }, + { + category: "vitrin-javaher", + categoryHeading: "ویترین جواهر", + promo: { + kicker: "ویترین جواهر", + title: "نمایش جواهر با تاکید بر درخشش سنگ ها و ظرافت جزئیات.", + ctaLabel: "مشاهده این دسته", + href: "/product?category=vitrin-javaher", + image: "/assets/main-pic/product/card-header/محصول 3.jpg", + imageAlt: "نمونه ویترین جواهر", + }, + }, + { + category: "mankan-service", + categoryHeading: "مانکن سرویس", + promo: { + kicker: "مانکن سرویس", + title: "مانکن های تخصصی برای ارائه کامل سرویس و افزایش جذابیت ویترین.", + ctaLabel: "مشاهده این دسته", + href: "/product?category=mankan-service", + image: "/assets/main-pic/product/card-header/2محصول 4.jpg", + imageAlt: "نمونه مانکن سرویس", + }, + }, + { + category: "mankan-ring", + categoryHeading: "مانکن انگشتر", + promo: { + kicker: "مانکن انگشتر", + title: "مانکن انگشتر با زاویه دید درست برای نمایش بهتر نگین و فرم رکاب.", + ctaLabel: "مشاهده این دسته", + href: "/product?category=mankan-ring", + image: "/assets/main-pic/product/card-header/محصول 2.jpg", + imageAlt: "نمونه مانکن انگشتر", + }, + }, + { + category: "mankan-earring", + categoryHeading: "مانکن گوشواره", + promo: { + kicker: "مانکن گوشواره", + title: "مانکن گوشواره برای نمایش واضح تر مدل ها در ویترین های پرتعداد.", + ctaLabel: "مشاهده این دسته", + href: "/product?category=mankan-earring", + image: "/assets/main-pic/product/card-header/محصول 1.jpg", + imageAlt: "نمونه مانکن گوشواره", + }, + }, +]; + +const PRODUCTS: ProductItem[] = CATALOG_PRODUCTS; + +/** هر ۶ محصول = دو ردیف × سه ستون در گرید ۵ ستونه کنار پروموی ۲×۲ */ +const PRODUCTS_PER_PROMO_CHUNK = 6; + +function chunkItems(items: T[], chunkSize: number): T[][] { + if (chunkSize <= 0) return items.length ? [items] : []; + const chunks: T[][] = []; + for (let i = 0; i < items.length; i += chunkSize) { + chunks.push(items.slice(i, i + chunkSize)); + } + return chunks; +} + +function CatalogProductCard({ product }: { product: ProductItem }) { + const href = `/product/${product.id}`; + const quickViewHref = href; + + return ( + + +
+ + {product.name} + +
+
+ +
+
+
+ + {product.categoryLabel} +

+ {product.name} +

+ +
+
+ ); +} + +function FeaturedPromotionCard({ + className, + categoryHeading, + kicker, + title, + ctaLabel, + href, + image, + imageAlt, +}: CategorySectionMeta["promo"] & { categoryHeading: string; className?: string }) { + return ( + + {imageAlt} +
+ +

{categoryHeading}

+

{kicker}

+

{title}

+
+ +
+
+ + ); +} + +export function ProductList10Catalog({ initialCategory, className }: ProductList10CatalogProps) { + const validInitialCategory = CATEGORY_OPTIONS.some((item) => item.value === initialCategory) + ? (initialCategory as ProductCategory) + : undefined; + + const [selectedCategories, setSelectedCategories] = useState( + validInitialCategory ? [validInitialCategory] : [], + ); + + useEffect(() => { + if (validInitialCategory) { + setSelectedCategories([validInitialCategory]); + return; + } + setSelectedCategories([]); + }, [validInitialCategory]); + + const toggleCategory = (value: ProductCategory) => { + setSelectedCategories((prev) => + prev.includes(value) ? prev.filter((item) => item !== value) : [...prev, value], + ); + }; + + const visibleSections = useMemo(() => { + return CATEGORY_SECTIONS.map((section) => { + const items = PRODUCTS.filter((p) => { + if (p.category !== section.category) return false; + if (selectedCategories.length === 0) return true; + return selectedCategories.includes(p.category); + }); + return { ...section, items }; + }).filter((s) => s.items.length > 0); + }, [selectedCategories]); + + return ( +
+
+
+

محصولات

+

+ مجموعه استندها و ملزومات دکور ویترین طلافروشی را بر اساس دسته بندی بررسی کنید و متناسب با نیاز فروشگاه خود، + بهترین گزینه را برای نمایش حرفه ای سرویس های طلا و جواهر انتخاب کنید. +

+
+ +
+
+ + {CATEGORY_OPTIONS.map((item) => { + const active = selectedCategories.includes(item.value); + return ( + + ); + })} +
+
+ +
+
+ {visibleSections.map((section) => { + const itemChunks = chunkItems(section.items, PRODUCTS_PER_PROMO_CHUNK); + return ( +
+ {itemChunks.map((chunk, chunkIndex) => { + /* RTL: ستون‌های ۱–۲ = سمت راست گرید، ۴–۵ = سمت چپ */ + const promoOnVisualRight = chunkIndex % 2 === 0; + return ( +
+ +
+ {chunk.map((product) => ( + + ))} +
+
+ ); + })} +
+ ); + })} +
+ + {visibleSections.length === 0 ? ( +
+

محصولی پیدا نشد

+

فیلتر دسته‌بندی را تغییر دهید یا همه دسته‌ها را نمایش دهید.

+
+ ) : null} +
+
+
+ ); +} diff --git a/components/sections/feature/solution-section.tsx b/components/sections/feature/solution-section.tsx new file mode 100644 index 0000000..f21129e --- /dev/null +++ b/components/sections/feature/solution-section.tsx @@ -0,0 +1,75 @@ +import { cn } from "@/lib/utils"; +import { Clock, ShoppingCart, Eye, FileText, ToggleLeft, Settings } from "lucide-react"; + +interface SolutionSectionProps { + className?: string; +} + +export default function SolutionSection({ className }: SolutionSectionProps) { + const features = [ + { + icon: Clock, + title: "نمایش قیمت لحظه‌ای به مشتریان", + description: "دسترسی آنی به آخرین قیمت‌های بازار" + }, + { + icon: ShoppingCart, + title: "ثبت و مدیریت سفارش‌ها", + description: "سیستم جامع ثبت و پیگیری سفارش‌ها" + }, + { + icon: Eye, + title: "مشاهده آنلاین مانده حساب هر مشتری", + description: "دسترسی لحظه‌ای به وضعیت حساب مشتریان" + }, + { + icon: FileText, + title: "دسترسی به ریز کامل معاملات در هر ساعت از شبانه‌روز", + description: "گزارش‌گیری دقیق و کامل از تمام معاملات" + }, + { + icon: ToggleLeft, + title: "کنترل وضعیت بازار (باز / بسته)", + description: "مدیریت وضعیت بازار به صورت لحظه‌ای" + }, + { + icon: Settings, + title: "پنل مدیریت پیشرفته برای ادمین", + description: "ابزارهای کامل مدیریت و کنترل سیستم" + } + ]; + + return ( +
+
+
+

+ یک پلتفرم اختصاصی برای مدیریت کامل معاملات +

+
+ {features.map((feature, index) => { + const Icon = feature.icon; + return ( +
+ +
+
+ {feature.title} +
+
+ {feature.description} +
+
+
+ ); + })} +
+
+
+
+ ); +} + diff --git a/components/sections/hero/hero-174.tsx b/components/sections/hero/hero-174.tsx new file mode 100644 index 0000000..3d54308 --- /dev/null +++ b/components/sections/hero/hero-174.tsx @@ -0,0 +1,76 @@ +import Image from "next/image"; +import Link from "next/link"; + +import { Button } from "@/components/ui/button"; + +interface Hero174Props { + pageHeading?: string; + title?: string; + description?: string; + imageSrc?: string; + imageAlt?: string; + ctaLabel?: string; + ctaHref?: string; +} + +const defaultProps: Required = { + pageHeading: "مستر دکور", + title: "طراحی و تولید استند ویترین طلافروشی برای فروش بیشتر", + description: + "مستر دکور، تامین کننده تخصصی استند و ملزومات دکور ویترین طلا و جواهر است؛ راهکاری که چیدمان فروشگاه شما را حرفه ای تر می کند و توجه مشتری را دقیقاً روی سرویس های شما نگه می دارد.", + imageSrc: "/assets/main-pic/mr_decor-mainpic.jpg", + imageAlt: "نمونه ویترین و دکور فروشگاهی", + ctaLabel: "مشاهده محصولات", + ctaHref: "/product", +}; + +export function Hero174(props: Hero174Props) { + const { + pageHeading, + title, + description, + imageSrc, + imageAlt, + ctaLabel, + ctaHref, + } = { + ...defaultProps, + ...props, + }; + + return ( +
+

+ {pageHeading} +

+ +
+ {imageAlt} +
+
+
+
+ +
+
+
+

{title}

+

{description}

+ +
+
+ +
+
+ ); +} diff --git a/components/sections/hero/hero-197.tsx b/components/sections/hero/hero-197.tsx new file mode 100644 index 0000000..469e5bf --- /dev/null +++ b/components/sections/hero/hero-197.tsx @@ -0,0 +1,65 @@ +import { Sparkles } from "lucide-react"; +import Link from "next/link"; + +import { cn } from "@/lib/utils"; + +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; + +interface Hero197Props { + className?: string; + badge?: string; + title?: string; + description?: string; + showButtons?: boolean; +} + +const Hero197 = ({ + className, + badge = "Launching Soon", + title = "Accelerate your development workflow", + description = "Our intelligent platform helps teams automate routine tasks, streamline collaboration, and deliver higher quality code faster.", + showButtons = true, +}: Hero197Props) => { + return ( +
+
+ {/* Background pattern with dots */} +
+ +
+
+ + {badge} + + +

+ {title} +

+ +

+ {description} +

+ + {showButtons && ( +
+ + +
+ )} +
+
+
+
+ ); +}; + +export { Hero197 }; + diff --git a/components/sections/hero/hero-200.tsx b/components/sections/hero/hero-200.tsx new file mode 100644 index 0000000..d398c4d --- /dev/null +++ b/components/sections/hero/hero-200.tsx @@ -0,0 +1,95 @@ +"use client"; + +import { motion } from "framer-motion"; +import Link from "next/link"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { useContent } from "@/components/layout/content-provider"; + +interface Hero200Props { + className?: string; + /** تصویر داخل قاب موبایل (پیش‌نمایش اپ) */ + phoneImage?: { src: string; alt: string }; +} + +const Hero200 = ({ className, phoneImage }: Hero200Props) => { + const content = useContent(); + const hero = content.pages.product.forushgahSazTala.hero200; + const appPreviewSrc = phoneImage?.src ?? "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/block-white-1.svg"; + const appPreviewAlt = phoneImage?.alt ?? "Gold store app"; + return ( +
+
+
+

+ {hero.subtitle} +

+
+

+ {hero.titleLine1} +
+ {hero.titleLine2} +
+ {hero.titleLine3} +

+
+ + + {/* Iphone mockup with content */} +
+ + Gold phone frame +
+ {phoneImage ? ( + {appPreviewAlt} + ) : ( + <> +
+
+ طلا +
+
+
+

+ {hero.dateLabel} +

+

+ {hero.appLabel} +

+
+
+ {appPreviewAlt} + + )} +
+ +
+
+
+
+ ); +}; + +export { Hero200 }; diff --git a/components/sections/hero/hero-201.tsx b/components/sections/hero/hero-201.tsx new file mode 100644 index 0000000..3eeb94a --- /dev/null +++ b/components/sections/hero/hero-201.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { motion } from "framer-motion"; + +import { cn } from "@/lib/utils"; + +interface Hero201Props { + className?: string; + smallTitle?: string; + mainTitle?: string; + description?: string; +} + +const Hero201 = ({ + className, + smallTitle = "Just Copy Paste", + mainTitle = "Amazing components", + description = "Discover our collection of beautifully designed, ready-to-use components that you can easily integrate into your projects.", +}: Hero201Props) => { + return ( +
+
+ {/* دایره‌های تزئینی پشت متن تا لرزش ایجاد نکنند */} +
+ + + + + + +
+ +
+

+ {smallTitle} +

+ +

+ {mainTitle} +

+ +

+ {description} +

+
+
+
+ ); +}; + +export { Hero201 }; diff --git a/components/sections/hero/hero-204.tsx b/components/sections/hero/hero-204.tsx new file mode 100644 index 0000000..99aa591 --- /dev/null +++ b/components/sections/hero/hero-204.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { motion } from "framer-motion"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +interface Hero204Props { + className?: string; +} + +const Hero204 = ({ className }: Hero204Props) => { + return ( +
+
+ + درخواست دمو + + + سامانه‌های تخصصی مستر دکور را قبل از تصمیم‌گیری از نزدیک ببینید. فرم را پر کنید تا در اسرع وقت برای هماهنگی جلسه دمو و نمایش امکانات با شما تماس بگیریم. + +
+
+ ); +}; + +export { Hero204 }; diff --git a/components/sections/hero/hero-226.tsx b/components/sections/hero/hero-226.tsx new file mode 100644 index 0000000..e2221a5 --- /dev/null +++ b/components/sections/hero/hero-226.tsx @@ -0,0 +1,235 @@ +"use client"; + +import { motion } from "framer-motion"; +import { ArrowRight } from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; +import { useEffect, useState } from "react"; + +import { Button } from "@/components/ui/button"; +import { + Carousel, + type CarouselApi, + CarouselContent, + CarouselItem, +} from "@/components/ui/carousel"; +import { cn } from "@/lib/utils"; + +export interface HeroCarouselSlide { + src: string; + alt: string; + name?: string; + title?: string; + description?: string; + href?: string; +} + +export interface Hero226Props { + slides: HeroCarouselSlide[]; + slideCtaLabel: string; + /** برای دسترسی‌پذیری: تنها h1 معنادار صفحه (معمولاً نام برند) */ + pageHeading: string; + className?: string; +} + +type Props = Partial; + +const defaultSlides: HeroCarouselSlide[] = [ + { + src: "/assets/main-pic/banner-master-decoor.jpg", + alt: "نمونه ویترین و دکور", + title: "ویترین طلا", + description: "نمایش حرفه‌ای و مدیریت ویترین محصولات طلا.", + href: "/product?category=vitrin-tala", + }, + { + src: "/assets/main-pic/banner-master-decoor.jpg", + alt: "نمونه ویترین خردسال", + title: "ویترین خردسال", + description: "راهکار ویترین و فرایندهای مرتبط با خردسال.", + href: "/product?category=vitrin-khorde-sal", + }, + { + src: "/assets/main-pic/banner-master-decoor.jpg", + alt: "نمونه ویترین جواهر", + title: "ویترین جواهر", + description: "ویترین تخصصی برای جواهر و اقلام پرارزش.", + href: "/product?category=vitrin-javaher", + }, + { + src: "/assets/main-pic/banner-master-decoor.jpg", + alt: "نمونه دکور فروشگاه", + title: "دکور و جعبه", + description: "ترکیبی از تنوع، ظرافت و کیفیت برای فضای فروش شما.", + href: "/about", + }, + { + src: "/assets/main-pic/banner-master-decoor.jpg", + alt: "نمونه راهکار سفارشی", + title: "راهکار سفارشی", + description: "طراحی و تولید متناسب با مدل کاری مجموعه شما.", + href: "/consultation", + }, +]; + +const defaultProps: Required> = { + slides: defaultSlides, + slideCtaLabel: "مشاهده", + pageHeading: "مستر دکور", +}; + +function isExternalHref(href: string) { + return href.startsWith("http://") || href.startsWith("https://"); +} + +function SlideCtaLink({ + href, + slideCtaLabel, +}: { + href: string; + slideCtaLabel: string; +}) { + const className = + "group gap-2 rounded-full border border-gold-400/55 bg-background/15 px-4 py-2 text-body font-medium tracking-tight text-gold-300 backdrop-blur-sm hover:border-gold-400/75 hover:bg-gold-950/35 hover:text-gold-200"; + + const icon = ( + + ); + + if (isExternalHref(href)) { + return ( + + ); + } + + return ( + + ); +} + +export function Hero226(props: Props) { + const [api, setApi] = useState(); + const [current, setCurrent] = useState(0); + + const { slides, slideCtaLabel, pageHeading, className } = { + ...defaultProps, + ...props, + }; + + useEffect(() => { + if (!api) return; + const onSelect = () => setCurrent(api.selectedScrollSnap()); + onSelect(); + api.on("select", onSelect); + return () => { + api.off("select", onSelect); + }; + }, [api]); + + return ( +
+

+ {pageHeading} +

+
+ + + + {slides.map((img, index) => ( + +
+
+
+ {img.alt} +
+ +
+
+ {/* + هر اسلاید h2 است؛ h1 فقط sr-only بالای سکشن (استاندارد تایپوگرافی) + */} +

+ {img.title ?? ""} +

+

+ {img.description ?? ""} +

+
+
+ +
+
+
+
+
+ ))} +
+ +
+ {slides.map((_, index) => ( +
+
+
+
+
+ ); +} diff --git a/components/sections/hero/hero-247.tsx b/components/sections/hero/hero-247.tsx new file mode 100644 index 0000000..05608aa --- /dev/null +++ b/components/sections/hero/hero-247.tsx @@ -0,0 +1,98 @@ +"use client"; + +import Link from "next/link"; +import { motion } from "framer-motion"; + +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { useContent } from "@/components/layout/content-provider"; + +interface ButtonItem { + text: string; + url: string; +} + +interface Hero247Props { + heading?: string; + description?: string; + secondaryButton?: ButtonItem; + primaryButton?: ButtonItem; + className?: string; +} + +const Hero247 = ({ + className, + heading, + description, + secondaryButton: secondaryButtonProp, + primaryButton: primaryButtonProp, +}: Hero247Props) => { + const content = useContent() + const defaults = content.pages.home.hero247 + const headingText = heading ?? defaults.heading + const descriptionText = description ?? defaults.description + const secondaryButton = secondaryButtonProp ?? { text: defaults.secondaryButton, url: "#rahavarha" } + const primaryButton = primaryButtonProp ?? { text: defaults.primaryButton, url: "#mozakerat" } + return ( +
+
+
+ + {headingText} + + + {descriptionText} + +
+ + + + + + +
+
+
+
+ ); +}; + +export { Hero247 }; diff --git a/components/sections/hero/hero-28.tsx b/components/sections/hero/hero-28.tsx new file mode 100644 index 0000000..07f2b03 --- /dev/null +++ b/components/sections/hero/hero-28.tsx @@ -0,0 +1,169 @@ +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; + +interface Hero28Props { + className?: string; + title?: string; + description?: string; + showButton?: boolean; +} + +const Hero28 = ({ + className, + title = "Welcome to Our Website", + description = "Elig doloremque mollitia fugiat omnis! Porro facilis quo animi consequatur.", + showButton = true, +}: Hero28Props) => { + return ( +
+ {/* Background pattern */} +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* سکه طلا */} + + + + + + {/* شمش طلا */} + + + + + + + {/* سکه طلا */} + + + + + + {/* شمش طلا */} + + + + + + + {/* سکه طلا */} + + + + + + +
+
+
+

+ {title} +

+

+ {description} +

+ {showButton && ( +
+ +
+ )} +
+
+ ); +}; + +export { Hero28 }; + diff --git a/components/sections/hero/hero-49.tsx b/components/sections/hero/hero-49.tsx new file mode 100644 index 0000000..d36bb03 --- /dev/null +++ b/components/sections/hero/hero-49.tsx @@ -0,0 +1,73 @@ +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; + +interface Hero49Props { + className?: string; + title?: string; + description?: string; + showButton?: boolean; + /** تصویر داخل فریم موبایل (مثلاً اسکرین‌شات اپ) */ + phoneImage?: string; +} + +const Hero49 = ({ + className, + title = "Master your monetary matters", + description = "Minimize financial stress and build wealth with our intuitive, AI-enhanced budgeting tool. Our platform analyzes your spending and offers insights for smarter financial decisions.", + showButton = true, + phoneImage, +}: Hero49Props) => { + const phoneImageSrc = + phoneImage ?? "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-dark-7-tall.svg"; + return ( +
+
+
+

+ {title} +

+

+ {description} +

+ {showButton && ( + + )} +
+
+ {/* Image inside Iphone */} +
+ +
+ {/* Iphone Image */} + +
+
+
+ ); +}; + +export { Hero49 }; + diff --git a/components/sections/hero/hero-91.tsx b/components/sections/hero/hero-91.tsx new file mode 100644 index 0000000..5b13fd8 --- /dev/null +++ b/components/sections/hero/hero-91.tsx @@ -0,0 +1,79 @@ +"use client"; + +import Link from "next/link"; +import Image from "next/image"; + +import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { useContent } from "@/components/layout/content-provider"; + +interface Hero91Props { + className?: string; +} + +const Hero91 = ({ className }: Hero91Props) => { + const content = useContent(); + const { hero91 } = content.pages.home; + const heroImageSrc = content.images.hero91.image; + const heroVideoSrc = content.images.hero91.video; + + return ( +
+
+
+

+ {hero91.title} +

+

+ {hero91.description} +

+
+ + +
+
+
+
+ {heroVideoSrc ? ( +
+
+
+
+ ); +}; + +export { Hero91 }; diff --git a/components/sections/hero/hero-alternative.tsx b/components/sections/hero/hero-alternative.tsx new file mode 100644 index 0000000..f90d799 --- /dev/null +++ b/components/sections/hero/hero-alternative.tsx @@ -0,0 +1,79 @@ +"use client"; + +import { motion } from "framer-motion"; +import { ArrowRight } from "lucide-react"; +import React from "react"; + +import { cn } from "@/lib/utils"; +import { useIsMobile } from "@/hooks/use-mobile"; + +import { LineShadowText } from "@/components/ui/line-shadow-text"; +import { Button } from "@/components/ui/button"; + +interface Hero233Props { + className?: string; +} + +const Hero233 = ({ className }: Hero233Props) => { + const isMobile = useIsMobile(); + + return ( +
+
+
+ +
+

+ + The only app you Need to Stay + + Productive + ever + . +

+
+ +

+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ipsum animi, + ipsam provident optio delectus neque aliquid cumque. Beatae, odio! +

+
+ + +
+
+ +
+ {Array.from({ length: isMobile ? 7 : 18 }).map((_, i) => ( + + ))} +
+
+ ); +}; + +export { Hero233 }; diff --git a/components/sections/hero/hero-contact.tsx b/components/sections/hero/hero-contact.tsx new file mode 100644 index 0000000..dcd4e03 --- /dev/null +++ b/components/sections/hero/hero-contact.tsx @@ -0,0 +1,188 @@ +"use client"; + +import { Fragment, useState } from "react"; +import { Play } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { AspectRatio } from "@/components/ui/aspect-ratio"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +export interface HeroContactProps { + className?: string; + /** برچسب بالای عنوان (مثلاً نام شرکت) */ + eyebrow?: string; + /** عنوان اصلی - فقط یک h1 در صفحه */ + heading: string; + /** توضیح زیر عنوان */ + description: string; + /** متن دکمه اصلی (مثلاً «فرم تماس») */ + ctaLabel?: string; + /** آدرس اسکرول به فرم (مثلاً #contact-form) */ + ctaHref?: string; + /** مسیر تصویر هیرو (مثلاً /assets/contact-us/contact-us.jpg) */ + imageSrc?: string; + /** متن جایگزین تصویر */ + imageAlt?: string; + /** متن دکمه ویدیو (اختیاری - در صورت خالی بودن دکمه ویدیو نمایش داده نمی‌شود) */ + videoLabel?: string; + /** آیدی ویدیو یوتیوب برای embed (اختیاری) */ + videoId?: string; +} + +const HeroContact = ({ + className, + eyebrow, + heading, + description, + ctaLabel = "فرم تماس", + ctaHref = "#contact-form", + imageSrc, + imageAlt = "تماس با ما", + videoLabel, + videoId, +}: HeroContactProps) => { + const [isVideoOpen, setIsVideoOpen] = useState(false); + + const handleCtaClick = (e: React.MouseEvent) => { + if (ctaHref.startsWith("#")) { + e.preventDefault(); + const el = document.getElementById(ctaHref.slice(1)); + el?.scrollIntoView({ behavior: "smooth" }); + } + }; + + return ( + +
+
+
+
+ {eyebrow && ( +

+ {eyebrow} +

+ )} +
+

+ {heading} +

+

+ {description} +

+
+
+ + {videoLabel && videoId && ( + + )} +
+
+
+ {imageSrc ? ( +
+ {imageAlt} +
+ ) : ( +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ )} +
+
+
+
+ {videoId && ( + + + + ویدیو معرفی + +
+