Compare commits

..
4 Commits
19 changed files with 741 additions and 494 deletions
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
<changelist name="Uncommitted_changes_before_Checkout_at_9_20_26,_11_55 AM_[Changes]" date="1789898166789" recycled="true" deleted="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Checkout_at_9_20_26,_11_55 AM_[Changes]/shelved.patch" />
<option name="DESCRIPTION" value="Uncommitted changes before Checkout at 9/20/26, 11:55 AM [Changes]" />
</changelist>
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
<changelist name="Uncommitted_changes_before_Checkout_at_9_21_26,_1_17 PM_[Changes]" date="1789989456881" recycled="true" deleted="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Checkout_at_9_21_26,_1_17 PM_[Changes]/shelved.patch" />
<option name="DESCRIPTION" value="Uncommitted changes before Checkout at 9/21/26, 1:17 PM [Changes]" />
</changelist>
+130 -85
View File
@@ -4,8 +4,9 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/components/HorizontalShowcase.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/HorizontalShowcase.tsx" afterDir="false" />
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="refactor(products): organize product showcase into feature module">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tailwind.config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/tailwind.config.ts" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -27,61 +28,61 @@
</component>
<component name="NextEditCompletionFeaturesState">
<decayedCancelled>
<entry key="MS100" value="1.028605430515295" />
<entry key="MS500" value="2.124649469580447" />
<entry key="S2" value="5.757603075779661" />
<entry key="S5" value="9.075791406095624" />
<entry key="S10" value="11.490304279732543" />
<entry key="S30" value="14.544855509715473" />
<entry key="S60" value="15.928253142043944" />
<entry key="M2" value="17.05794166660894" />
<entry key="M5" value="18.240030198039076" />
<entry key="M10" value="19.085397144523835" />
<entry key="M15" value="19.539401536900623" />
<entry key="M30" value="20.15328332293758" />
<entry key="H1" value="20.794543104303607" />
<entry key="H2" value="24.217320209573487" />
<entry key="H4" value="33.81335646745446" />
<entry key="D1" value="60.18887647810417" />
<entry key="W1" value="68.4601968975454" />
<entry key="MS100" value="1.0" />
<entry key="MS500" value="1.000008943214075" />
<entry key="S2" value="1.2607914241876252" />
<entry key="S5" value="3.6321166926353086" />
<entry key="S10" value="7.187879480049254" />
<entry key="S30" value="12.833490057716963" />
<entry key="S60" value="15.367124944343653" />
<entry key="M2" value="17.200465165116825" />
<entry key="M5" value="18.867586649419653" />
<entry key="M10" value="19.889539626550388" />
<entry key="M15" value="20.405494126294794" />
<entry key="M30" value="21.08410736765042" />
<entry key="H1" value="21.75882388976373" />
<entry key="H2" value="25.19651197545119" />
<entry key="H4" value="34.79882663024789" />
<entry key="D1" value="61.184565108820564" />
<entry key="W1" value="69.45949632611399" />
</decayedCancelled>
<decayedSelected>
<entry key="MS100" value="0.0" />
<entry key="MS500" value="8.699625509373026E-98" />
<entry key="S2" value="5.430941684423347E-25" />
<entry key="S5" value="1.9676599943042008E-10" />
<entry key="S10" value="1.4027330445612952E-5" />
<entry key="S30" value="0.02411709586160527" />
<entry key="S60" value="0.1553011170701436" />
<entry key="M2" value="0.3961550964122234" />
<entry key="M5" value="0.7735594310297959" />
<entry key="M10" value="1.120827746179152" />
<entry key="M15" value="1.3221230513291828" />
<entry key="M30" value="1.6023719910246246" />
<entry key="H1" value="1.7960429930071773" />
<entry key="H2" value="2.045967012949897" />
<entry key="H4" value="2.5063189725869783" />
<entry key="D1" value="3.6098476224764395" />
<entry key="W1" value="3.9392268953128196" />
<entry key="MS100" value="7.962034587841035E-12" />
<entry key="MS500" value="0.006028438183823964" />
<entry key="S2" value="0.2786449669589018" />
<entry key="S5" value="0.5998193629466475" />
<entry key="S10" value="0.7744876138562475" />
<entry key="S30" value="0.9379611448586518" />
<entry key="S60" value="1.098380535936316" />
<entry key="M2" value="1.3551692202762455" />
<entry key="M5" value="1.7492815083633066" />
<entry key="M10" value="2.1050752944469457" />
<entry key="M15" value="2.3102267135979067" />
<entry key="M30" value="2.595453075209157" />
<entry key="H1" value="2.792248246685056" />
<entry key="H2" value="3.0438541717577623" />
<entry key="H4" value="3.5050645308767083" />
<entry key="D1" value="4.609559468032772" />
<entry key="W1" value="4.939182358578843" />
</decayedSelected>
<decayedShown>
<entry key="MS100" value="0.04871208675588815" />
<entry key="MS500" value="1.5761328815881446" />
<entry key="S2" value="5.568297668845106" />
<entry key="S5" value="8.989731845795651" />
<entry key="S10" value="11.439232203057065" />
<entry key="S30" value="14.54354096178068" />
<entry key="S60" value="16.064294199401182" />
<entry key="M2" value="17.43942775764285" />
<entry key="M5" value="19.00501566584518" />
<entry key="M10" value="20.200966633195225" />
<entry key="M15" value="20.857682272562908" />
<entry key="M30" value="21.753511294575368" />
<entry key="H1" value="22.589437021258547" />
<entry key="H2" value="26.262649646152585" />
<entry key="H4" value="36.319290758304845" />
<entry key="D1" value="63.798632595714466" />
<entry key="W1" value="72.39940953778996" />
<entry key="MS100" value="3.2612493671796914E-8" />
<entry key="MS500" value="0.03182913112034664" />
<entry key="S2" value="0.7198587400143126" />
<entry key="S5" value="3.605555500545043" />
<entry key="S10" value="7.540560604413133" />
<entry key="S30" value="13.59016436886081" />
<entry key="S60" value="16.363500468632758" />
<entry key="M2" value="18.498243290148842" />
<entry key="M5" value="20.590803130608784" />
<entry key="M10" value="21.980529817792103" />
<entry key="M15" value="22.705973658811303" />
<entry key="M30" value="23.6744528413458" />
<entry key="H1" value="24.548438225385052" />
<entry key="H2" value="28.238985379440273" />
<entry key="H4" value="38.30313469133441" />
<entry key="D1" value="65.7939710746819" />
<entry key="W1" value="74.39865557179122" />
</decayedShown>
</component>
<component name="ProjectColorInfo">{
@@ -89,43 +90,49 @@
&quot;associatedIndex&quot;: 2
}</component>
<component name="ProjectId" id="3HDEfy1HyzjDcELlwvnRRXxPltq" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ASKED_MARK_IGNORED_FILES_AS_EXCLUDED&quot;: &quot;true&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
&quot;com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;develop&quot;,
&quot;javascript.nodejs.core.library.configured.version&quot;: &quot;24.20.0&quot;,
&quot;javascript.nodejs.core.library.typings.version&quot;: &quot;24.13.3&quot;,
&quot;javascript.preferred.runtime.type.id&quot;: &quot;node&quot;,
&quot;js.debugger.nextJs.config.created.client&quot;: &quot;true&quot;,
&quot;js.debugger.nextJs.config.created.server&quot;: &quot;true&quot;,
&quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;,
&quot;kronk.content.tree.expanded.paths&quot;: &quot;&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/hamed/Documents/Projects/GItLabRepo/device_web&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;prettierjs.PrettierConfiguration.Package&quot;: &quot;/home/hamed/Documents/Projects/GIthubRepo/robot-market-landing/node_modules/prettier&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;settings.javascript.prettier&quot;,
&quot;to.speed.mode.migration.done&quot;: &quot;true&quot;,
&quot;ts.external.directory.path&quot;: &quot;/home/hamed/Documents/Projects/GIthubRepo/robot-market-landing/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"codeWithMe.voiceChat.enabledByDefault": "false",
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true",
"git-widget-placeholder": "develop",
"javascript.nodejs.core.library.configured.version": "24.20.0",
"javascript.nodejs.core.library.typings.version": "24.13.3",
"javascript.preferred.runtime.type.id": "node",
"js.debugger.nextJs.config.created.client": "true",
"js.debugger.nextJs.config.created.server": "true",
"junie.onboarding.icon.badge.shown": "true",
"kronk.content.tree.expanded.paths": "",
"last_opened_file_path": "/home/hamed/Documents/Projects/GItLabRepo/device_web",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.jshint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.jshint": "",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"prettierjs.PrettierConfiguration.Package": "/home/hamed/Documents/Projects/GIthubRepo/robot-market-landing/node_modules/prettier",
"settings.editor.selected.configurable": "settings.javascript.prettier",
"to.speed.mode.migration.done": "true",
"ts.external.directory.path": "/home/hamed/Documents/Projects/GIthubRepo/robot-market-landing/node_modules/typescript/lib",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
<recent name="D:\Project\GitHubRepo\robot-market-landing\public\images\icons" />
<recent name="D:\Project\GitHubRepo\robot-market-landing\public\images" />
<recent name="D:\Project\GitHubRepo\robot-market-landing\app" />
@@ -215,6 +222,10 @@
<workItem from="1789973458932" duration="1450000" />
<workItem from="1789981347522" duration="4504000" />
<workItem from="1790050380142" duration="326000" />
<workItem from="1790568898974" duration="91000" />
<workItem from="1790569032481" duration="788000" />
<workItem from="1790573950198" duration="16000" />
<workItem from="1790660862579" duration="2098000" />
</task>
<task id="LOCAL-00001" summary="update">
<option name="closed" value="true" />
@@ -552,7 +563,39 @@
<option name="project" value="LOCAL" />
<updated>1789974962919</updated>
</task>
<option name="localTasksCounter" value="43" />
<task id="LOCAL-00043" summary="Replace desktop showcase video with scroll-driven color backgrounds">
<option name="closed" value="true" />
<created>1790492715355</created>
<option name="number" value="00043" />
<option name="presentableId" value="LOCAL-00043" />
<option name="project" value="LOCAL" />
<updated>1790492715355</updated>
</task>
<task id="LOCAL-00044" summary="Replace desktop showcase video with scroll-driven color backgrounds">
<option name="closed" value="true" />
<created>1790661145102</created>
<option name="number" value="00044" />
<option name="presentableId" value="LOCAL-00044" />
<option name="project" value="LOCAL" />
<updated>1790661145102</updated>
</task>
<task id="LOCAL-00045" summary="refactor(products): organize product showcase into feature module">
<option name="closed" value="true" />
<created>1790662441910</created>
<option name="number" value="00045" />
<option name="presentableId" value="LOCAL-00045" />
<option name="project" value="LOCAL" />
<updated>1790662441910</updated>
</task>
<task id="LOCAL-00046" summary="refactor(products): organize product showcase into feature module">
<option name="closed" value="true" />
<created>1790662596270</created>
<option name="number" value="00046" />
<option name="presentableId" value="LOCAL-00046" />
<option name="project" value="LOCAL" />
<updated>1790662596270</updated>
</task>
<option name="localTasksCounter" value="47" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -581,6 +624,8 @@
<MESSAGE value="chore: configure prettier for consistent WebStorm formatting" />
<MESSAGE value="FAZ1" />
<MESSAGE value="Improve featured products layout and add support CTA" />
<option name="LAST_COMMIT_MESSAGE" value="Improve featured products layout and add support CTA" />
<MESSAGE value="Replace desktop showcase video with scroll-driven color backgrounds" />
<MESSAGE value="refactor(products): organize product showcase into feature module" />
<option name="LAST_COMMIT_MESSAGE" value="refactor(products): organize product showcase into feature module" />
</component>
</project>
+599
View File
@@ -0,0 +1,599 @@
# Next.js Frontend Architecture Guide
## Objective
This document defines the architecture rules for this Next.js project.
Before implementing new features, refactoring existing code, or creating new modules, read and follow this architecture.
The goal is to create a scalable, production-ready frontend suitable for a professional development team.
---
# Technology Stack
The project must use:
- Next.js (App Router)
- React
- TypeScript
- Redux Toolkit
- Axios
- Tailwind CSS
- Ant Design
Do not introduce alternative frameworks or libraries unless explicitly requested.
---
# Architecture Pattern
Use:
## Feature-Based Architecture
Organize code around business features, not technical file types.
Avoid:
```
src/
components/
pages/
services/
utils/
```
as the main structure.
Use:
```
src/
├── app/
│ ├── (routes)/
│ ├── api/
│ ├── providers/
│ ├── layout.tsx
│ ├── loading.tsx
│ ├── error.tsx
│ └── globals.css
│
├── features/
│ ├── auth/
│ ├── users/
│ ├── products/
│ ├── companies/
│ └── ...
│
├── components/
│ ├── ui/
│ ├── common/
│ └── layouts/
│
├── services/
│ ├── api/
│ ├── storage/
│ └── websocket/
│
├── store/
│
├── hooks/
│
├── lib/
│
├── types/
│
├── utils/
│
└── config/
```
---
# Next.js App Router Rules
Use Next.js App Router.
Routes must be created using:
```
app/
```
Example:
```
app/dashboard/page.tsx
```
creates:
```
/dashboard
```
Do not use React Router.
Do not manually create routing logic.
---
# Server Components vs Client Components
## Default Rule
All components should be Server Components by default.
Example:
```tsx
export default function UsersPage() {
return <div>Users</div>;
}
```
---
## Client Components
Use Client Components only when required.
Examples:
- useState
- useEffect
- Redux hooks
- Browser APIs
- Event handlers
Then add:
```tsx
'use client';
```
at the top.
Example:
```tsx
'use client';
export default function LoginForm() {}
```
Do not add `"use client"` unnecessarily.
---
# Feature Structure
Each business feature must be isolated.
Example:
```
features/auth/
├── api/
│ └── auth.api.ts
│
├── components/
│ └── LoginForm.tsx
│
├── hooks/
│ └── useLogin.ts
│
├── store/
│ └── auth.slice.ts
│
├── schemas/
│ └── auth.schema.ts
│
├── types.ts
│
└── index.ts
```
Example business features:
```
features/
auth
users
companies
machines
inventory
sales
payments
reports
settings
```
---
# Component Architecture
## Shared Components
Reusable components belong here:
```
components/ui/
```
Examples:
```
Button
Modal
Input
Table
Dropdown
Form
```
These components must not contain business logic.
---
Business components belong inside features:
Example:
```
features/products/components/ProductCard.tsx
```
Not:
```
components/ProductCard.tsx
```
---
# API Architecture
All API communication must go through Axios.
Never call Axios directly inside components.
Wrong:
```tsx
function Users() {
axios.get('/users');
}
```
Correct:
```
Component
↓
Hook
↓
Feature API
↓
Axios Service
↓
Backend API
```
---
Axios configuration:
```
services/api/
axios.ts
interceptors.ts
endpoints.ts
```
Example:
```ts
export const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
});
```
---
# Redux Toolkit Rules
Redux Toolkit is used only for global client state.
Examples:
- Authentication state
- User preferences
- Application settings
- Global UI state
Structure:
```
store/
├── index.ts
├── provider.tsx
└── slices/
```
Feature state:
```
features/auth/store/
auth.slice.ts
auth.selector.ts
```
---
Do not store everything in Redux.
Use:
```
useState
```
for local component state.
Use Redux only for shared application state.
---
# Redux Provider in Next.js
Redux Provider must be a Client Component.
Example:
```
store/provider.tsx
```
```tsx
'use client';
import {Provider} from 'react-redux';
export function ReduxProvider({children}) {
return <Provider store={store}>{children}</Provider>;
}
```
Register it in:
```
app/layout.tsx
```
---
# TypeScript Rules
The project must be strongly typed.
Avoid:
```ts
any;
```
Prefer:
```ts
interface;
type;
generics;
unknown;
```
Each feature owns its types:
Example:
```
features/users/types.ts
```
---
# Tailwind CSS Rules
Use Tailwind for:
- Layout
- Spacing
- Responsive design
- Custom styling
Example:
```tsx
<div className="flex items-center gap-4">
```
Avoid unnecessary CSS files.
Global CSS should only contain:
- Variables
- Theme
- Global styles
---
# Ant Design Rules
Use Ant Design for complex UI components:
Examples:
- Tables
- Forms
- Modal
- DatePicker
- Dropdown
- Pagination
- Notification
Use Tailwind for:
- Layout
- Positioning
- Spacing
- Responsive behavior
Do not rewrite Ant Design components unless required.
---
# Data Fetching Rules
Follow this structure:
```
Page
↓
Feature Component
↓
Custom Hook
↓
API Layer
↓
Axios
↓
Backend
```
Never put business API logic inside UI components.
---
# Dependency Rules
Features should remain independent.
Wrong:
```
features/products
imports
features/auth
```
Correct:
```
features/products
↓
services
components
utils
```
Shared logic belongs in:
```
components/
services/
utils/
lib/
```
---
# Refactoring Rules
When refactoring an existing project:
1. Analyze the current structure first.
2. Do not rewrite everything immediately.
3. Move code gradually into feature folders.
4. Preserve existing functionality.
5. Improve:
- Type safety
- Separation of concerns
- API abstraction
- Component reuse
- State management
---
# Code Quality Rules
Every file should have one responsibility.
Avoid:
```
User.tsx
- UI
- API
- Validation
- State
```
Prefer:
```
UserCard.tsx
user.api.ts
useUser.ts
user.schema.ts
```
---
# Final Architecture Goal
The final project must be:
- Scalable
- Maintainable
- Team-friendly
- Type-safe
- Easy to test
- Ready for future features
Always prefer clean architecture over fast implementation.
Before creating code, understand where the code belongs in this architecture.
+1 -1
View File
@@ -1,5 +1,5 @@
import LocationsSection from '@/components/LocationsSection';
import ShowcaseSection from '@/components/ShowcaseSection';
import {ShowcaseSection} from '@/features/products';
import FeaturesSection from '@/components/FeaturesSection';
import AboutSection from '@/components/AboutSection';
import HeroSection from '@/components/HeroSection';
+1 -1
View File
@@ -1,7 +1,7 @@
import type {Metadata} from 'next';
import {notFound} from 'next/navigation';
import {content} from '@/data/content';
import ProductDetails from '@/components/ProductDetails';
import {ProductDetails} from '@/features/products';
import ProtectedPage from '@/components/auth/ProtectedPage';
type Props = {params: {id: string}};
+1 -1
View File
@@ -1,6 +1,6 @@
import {Suspense} from 'react';
import type {Metadata} from 'next';
import ProductsPage from '@/components/ProductsPage';
import {ProductsPage} from '@/features/products';
export const metadata: Metadata = {title: 'همه محصولات | روبات مارکت'};
@@ -4,7 +4,7 @@ import {useEffect, useRef} from 'react';
import {gsap} from 'gsap';
import {ScrollTrigger} from 'gsap/ScrollTrigger';
import VendingCard from './VendingCard';
import {featuredMachines} from '@/data/products';
import {featuredMachines} from '@/features/products/data/catalog';
import {useLanguage} from '@/app/context/LanguageContext';
gsap.registerPlugin(ScrollTrigger);
@@ -1,7 +1,7 @@
'use client';
import {PhoneOutlined} from '@ant-design/icons';
import {featuredMachines} from '@/data/products';
import {featuredMachines} from '@/features/products/data/catalog';
import {useLanguage} from '@/app/context/LanguageContext';
import {commerceCopy} from '@/data/commerce';
@@ -5,7 +5,7 @@ import Link from 'next/link';
import {CheckCircleOutlined} from '@ant-design/icons';
import {useLanguage} from '@/app/context/LanguageContext';
import {commerceCopy} from '@/data/commerce';
import QuantityControl from './QuantityControl';
import QuantityControl from '@/components/QuantityControl';
export default function ProductDetails({id}: {id: string}) {
const {t, locale} = useLanguage();
@@ -4,8 +4,8 @@ import Link from 'next/link';
import {usePathname, useRouter, useSearchParams} from 'next/navigation';
import {useLanguage} from '@/app/context/LanguageContext';
import {commerceCopy} from '@/data/commerce';
import {filterMachines, productCategories} from '@/data/products';
import QuantityControl from './QuantityControl';
import {filterMachines, productCategories} from '@/features/products/data/catalog';
import QuantityControl from '@/components/QuantityControl';
export default function ProductsPage() {
const {t, locale} = useLanguage();
@@ -3,9 +3,6 @@
import type {Machine} from '@/types/domain';
import type {MouseEvent} from 'react';
import {useRef} from 'react';
import QuantityControl from './QuantityControl';
import ProtectedLink from './auth/ProtectedLink';
import {commerceCopy} from '@/data/commerce';
import {useLanguage} from '@/app/context/LanguageContext';
import FloatingItems from './FloatingItems';
import {floatingSnacks} from '@/data/content';
+3
View File
@@ -0,0 +1,3 @@
export {default as ProductDetails} from './components/ProductDetails';
export {default as ProductsPage} from './components/ProductsPage';
export {default as ShowcaseSection} from './components/ShowcaseSection';
+1 -1
View File
@@ -1,7 +1,7 @@
import type {Config} from 'tailwindcss';
const config: Config = {
darkMode: 'class',
content: ['./app/**/*.{js,jsx,ts,tsx}', './components/**/*.{js,jsx,ts,tsx}'],
content: ['./app/**/*.{js,jsx,ts,tsx}', './components/**/*.{js,jsx,ts,tsx}', './features/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {
colors: {