From 1271d96539adb78164a963bf46d896586d5c60fa Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Mon, 10 Aug 2026 00:23:03 +0330 Subject: [PATCH] Add customer and business user-product flows across dashboards. Ship my-products / customer-products UI with gallery uploads, status controls, module gating, and related shared UI polish. Co-authored-by: Cursor --- apps/business/src/App.tsx | 7 + .../src/components/BlogCard.module.css | 7 +- .../src/components/CategoryAiPromptModal.tsx | 62 +- .../src/components/CategoryModal.module.css | 5 + .../CategorySearchSelect.module.css | 159 ++++ .../src/components/CategorySearchSelect.tsx | 246 +++++ .../components/CitySearchSelect.module.css | 129 +++ .../src/components/CitySearchSelect.tsx | 146 +++ apps/business/src/components/Sidebar.tsx | 9 + .../components/TechnicalFormAiPromptModal.tsx | 61 +- .../components/TechnicalFormModal.module.css | 18 + .../src/components/TechnicalFormModal.tsx | 176 ++-- .../src/components/Tooltip.module.css | 25 +- apps/business/src/components/Tooltip.tsx | 69 +- .../src/components/UserProductCard.module.css | 252 +++++ .../src/components/UserProductCard.tsx | 181 ++++ .../UserProductStatusModal.module.css | 65 ++ .../src/components/UserProductStatusModal.tsx | 108 +++ apps/business/src/i18n/messages.ts | 381 ++++++++ .../pages/AddCustomerProductPage.module.css | 605 ++++++++++++ .../src/pages/AddCustomerProductPage.tsx | 891 ++++++++++++++++++ .../src/pages/BlogDetailsPage.module.css | 8 +- apps/business/src/pages/CategoriesPage.tsx | 1 + .../CustomerProductDetailsPage.module.css | 338 +++++++ .../src/pages/CustomerProductDetailsPage.tsx | 331 +++++++ .../src/pages/CustomerProductsPage.module.css | 84 ++ .../src/pages/CustomerProductsPage.tsx | 248 +++++ apps/business/src/services/citiesService.ts | 14 + .../src/services/userProductsService.ts | 218 +++++ apps/business/src/types/userProduct.ts | 30 + apps/business/src/utils/businessModules.ts | 23 +- apps/customer/package.json | 1 + apps/customer/src/App.tsx | 10 + .../components/AddMyProductLayout.module.css | 42 + .../src/components/AddMyProductLayout.tsx | 16 + .../CategorySearchSelect.module.css | 159 ++++ .../src/components/CategorySearchSelect.tsx | 246 +++++ .../components/CitySearchSelect.module.css | 129 +++ .../src/components/CitySearchSelect.tsx | 146 +++ .../FavoriteStoreItemCard.module.css | 6 +- .../customer/src/components/Header.module.css | 49 +- apps/customer/src/components/Header.tsx | 40 +- .../src/components/ImageCropper.module.css | 170 ++++ apps/customer/src/components/ImageCropper.tsx | 175 ++++ .../src/components/ImageUploader.module.css | 83 ++ .../customer/src/components/ImageUploader.tsx | 77 ++ .../src/components/OrderItemsModal.module.css | 4 +- .../src/components/PageContent.module.css | 33 +- apps/customer/src/components/Sidebar.tsx | 9 +- apps/customer/src/components/Toast.module.css | 2 +- .../src/components/Tooltip.module.css | 27 +- apps/customer/src/components/Tooltip.tsx | 69 +- .../src/components/UserProductCard.module.css | 236 +++++ .../src/components/UserProductCard.tsx | 161 ++++ .../src/components/VariationsModal.module.css | 10 +- .../CheckoutAddAddressPanel.module.css | 6 +- .../checkout/CheckoutLayout.module.css | 25 +- .../src/context/CustomerThemeContext.tsx | 10 + .../src/context/TenantBrandingContext.tsx | 15 +- apps/customer/src/i18n/messages.ts | 264 ++++++ apps/customer/src/index.css | 65 ++ apps/customer/src/lib/routeTitles.ts | 4 + .../src/pages/AddMyProductPage.module.css | 605 ++++++++++++ apps/customer/src/pages/AddMyProductPage.tsx | 872 +++++++++++++++++ .../src/pages/AddressesPage.module.css | 2 +- apps/customer/src/pages/HomePage.tsx | 157 ++- apps/customer/src/pages/LoginPage.module.css | 8 +- .../src/pages/MyProductDetailsPage.module.css | 327 +++++++ .../src/pages/MyProductDetailsPage.tsx | 285 ++++++ .../src/pages/MyProductsPage.module.css | 103 ++ apps/customer/src/pages/MyProductsPage.tsx | 190 ++++ apps/customer/src/pages/OrdersPage.module.css | 4 +- .../pages/checkout/CheckoutSteps.module.css | 14 +- apps/customer/src/services/citiesService.ts | 23 +- apps/customer/src/services/mediaService.ts | 155 +++ apps/customer/src/services/tenantService.ts | 5 + .../src/services/userProductsService.ts | 213 +++++ apps/customer/src/types/userProduct.ts | 26 + apps/customer/src/utils/businessModules.ts | 53 ++ apps/customer/src/utils/cropImage.ts | 46 + apps/customer/src/utils/imageUpload.ts | 84 ++ .../PrimaryColorSwatchControl.module.css | 67 +- .../components/PrimaryColorSwatchControl.tsx | 89 +- .../src/pages/BusinessesPage.module.css | 20 +- apps/super-admin/src/pages/BusinessesPage.tsx | 128 ++- .../src/services/businessSettingsService.ts | 16 + apps/super-admin/src/types/business.ts | 6 +- apps/super-admin/src/utils/businessModules.ts | 37 +- docs/PROJECT_CONTEXT.md | 20 +- package-lock.json | 7 +- packages/dashboard-core/src/styles/tokens.css | 2 + .../src/components/AddressListEditor.tsx | 2 +- .../src/components/LanguageSelect.module.css | 30 +- .../src/components/SectionCard.module.css | 65 +- .../src/components/SectionCard.tsx | 28 +- .../dashboard-ui/src/context/Toast.module.css | 2 +- 96 files changed, 10532 insertions(+), 345 deletions(-) create mode 100644 apps/business/src/components/CategorySearchSelect.module.css create mode 100644 apps/business/src/components/CategorySearchSelect.tsx create mode 100644 apps/business/src/components/CitySearchSelect.module.css create mode 100644 apps/business/src/components/CitySearchSelect.tsx create mode 100644 apps/business/src/components/UserProductCard.module.css create mode 100644 apps/business/src/components/UserProductCard.tsx create mode 100644 apps/business/src/components/UserProductStatusModal.module.css create mode 100644 apps/business/src/components/UserProductStatusModal.tsx create mode 100644 apps/business/src/pages/AddCustomerProductPage.module.css create mode 100644 apps/business/src/pages/AddCustomerProductPage.tsx create mode 100644 apps/business/src/pages/CustomerProductDetailsPage.module.css create mode 100644 apps/business/src/pages/CustomerProductDetailsPage.tsx create mode 100644 apps/business/src/pages/CustomerProductsPage.module.css create mode 100644 apps/business/src/pages/CustomerProductsPage.tsx create mode 100644 apps/business/src/services/userProductsService.ts create mode 100644 apps/business/src/types/userProduct.ts create mode 100644 apps/customer/src/components/AddMyProductLayout.module.css create mode 100644 apps/customer/src/components/AddMyProductLayout.tsx create mode 100644 apps/customer/src/components/CategorySearchSelect.module.css create mode 100644 apps/customer/src/components/CategorySearchSelect.tsx create mode 100644 apps/customer/src/components/CitySearchSelect.module.css create mode 100644 apps/customer/src/components/CitySearchSelect.tsx create mode 100644 apps/customer/src/components/ImageCropper.module.css create mode 100644 apps/customer/src/components/ImageCropper.tsx create mode 100644 apps/customer/src/components/ImageUploader.module.css create mode 100644 apps/customer/src/components/ImageUploader.tsx create mode 100644 apps/customer/src/components/UserProductCard.module.css create mode 100644 apps/customer/src/components/UserProductCard.tsx create mode 100644 apps/customer/src/pages/AddMyProductPage.module.css create mode 100644 apps/customer/src/pages/AddMyProductPage.tsx create mode 100644 apps/customer/src/pages/MyProductDetailsPage.module.css create mode 100644 apps/customer/src/pages/MyProductDetailsPage.tsx create mode 100644 apps/customer/src/pages/MyProductsPage.module.css create mode 100644 apps/customer/src/pages/MyProductsPage.tsx create mode 100644 apps/customer/src/services/mediaService.ts create mode 100644 apps/customer/src/services/userProductsService.ts create mode 100644 apps/customer/src/types/userProduct.ts create mode 100644 apps/customer/src/utils/businessModules.ts create mode 100644 apps/customer/src/utils/cropImage.ts create mode 100644 apps/customer/src/utils/imageUpload.ts diff --git a/apps/business/src/App.tsx b/apps/business/src/App.tsx index 3dee895..3275418 100644 --- a/apps/business/src/App.tsx +++ b/apps/business/src/App.tsx @@ -23,6 +23,9 @@ import { StorePage } from './pages/StorePage' import { StoreItemsPage } from './pages/StoreItemsPage' import { StoreSpecialsPage } from './pages/StoreSpecialsPage' import { CustomersPage } from './pages/CustomersPage' +import { CustomerProductsPage } from './pages/CustomerProductsPage' +import { CustomerProductDetailsPage } from './pages/CustomerProductDetailsPage' +import { AddCustomerProductPage } from './pages/AddCustomerProductPage' import { OrdersPage } from './pages/OrdersPage' import { LoginPage } from './pages/LoginPage' import { ShoppingCardsPage } from './pages/ShoppingCardsPage' @@ -83,6 +86,10 @@ function App() { } /> } /> } /> + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/apps/business/src/components/BlogCard.module.css b/apps/business/src/components/BlogCard.module.css index a686af7..a743a45 100644 --- a/apps/business/src/components/BlogCard.module.css +++ b/apps/business/src/components/BlogCard.module.css @@ -32,21 +32,20 @@ .imageWrap { position: relative; width: 100%; - aspect-ratio: 3 / 2; overflow: hidden; background: rgba(148, 163, 184, 0.08); border-bottom: 1px solid rgba(148, 163, 184, 0.12); } .image { + display: block; width: 100%; - height: 100%; - object-fit: cover; + height: auto; } .imagePlaceholder { width: 100%; - height: 100%; + aspect-ratio: 3 / 2; background: linear-gradient( 135deg, rgba(148, 163, 184, 0.12) 0%, diff --git a/apps/business/src/components/CategoryAiPromptModal.tsx b/apps/business/src/components/CategoryAiPromptModal.tsx index f505840..553551d 100644 --- a/apps/business/src/components/CategoryAiPromptModal.tsx +++ b/apps/business/src/components/CategoryAiPromptModal.tsx @@ -1,15 +1,28 @@ import { useEffect, useState } from 'react' import { createPortal } from 'react-dom' import { Sparkles, X } from 'lucide-react' +import { useLocale } from '@meshkee/dashboard-ui' import { ApiError } from '../lib/api' +import { useT } from '../i18n/useT' import modalStyles from './CategoryModal.module.css' import aiStyles from '../styles/ai.module.css' import styles from './CategoryAiPromptModal.module.css' -export const DEFAULT_CATEGORY_AI_PROMPT = `Create a practical product category tree for my store. +export function buildCategoryAiPrompt(locale: 'fa' | 'en') { + if (locale === 'fa') { + return `یک درخت دسته‌بندی محصول کاربردی برای فروشگاه من بساز. + +نام انگلیسی، نام فارسی با خط پارسی، و توضیح کوتاه انگلیسی را برای هر دسته اضافه کن. +۳ تا ۵ دسته اصلی با زیردسته‌های مرتبط بساز تا مرور برای خریداران راحت‌تر شود.` + } + + return `Create a practical product category tree for my store. Include English names, Farsi names in Persian script, and short English descriptions. Use 3-5 main categories with relevant subcategories where it helps shoppers browse.` +} + +export const DEFAULT_CATEGORY_AI_PROMPT = buildCategoryAiPrompt('en') interface CategoryAiPromptModalProps { open: boolean @@ -26,16 +39,19 @@ export function CategoryAiPromptModal({ onRun, isRunning, }: CategoryAiPromptModalProps) { + const t = useT() + const { locale } = useLocale() + const isFa = locale === 'fa' const [mounted, setMounted] = useState(open) const [closing, setClosing] = useState(false) - const [prompt, setPrompt] = useState(DEFAULT_CATEGORY_AI_PROMPT) + const [prompt, setPrompt] = useState(() => buildCategoryAiPrompt(isFa ? 'fa' : 'en')) const [error, setError] = useState('') useEffect(() => { if (open) { setMounted(true) setClosing(false) - setPrompt(DEFAULT_CATEGORY_AI_PROMPT) + setPrompt(buildCategoryAiPrompt(isFa ? 'fa' : 'en')) setError('') } else if (mounted) { setClosing(true) @@ -45,7 +61,7 @@ export function CategoryAiPromptModal({ }, ANIMATION_MS) return () => clearTimeout(timer) } - }, [open, mounted]) + }, [open, mounted, isFa]) useEffect(() => { if (!mounted || closing) return @@ -60,7 +76,7 @@ export function CategoryAiPromptModal({ e.preventDefault() const trimmed = prompt.trim() if (trimmed.length < 10) { - setError('Prompt must be at least 10 characters.') + setError(t('products.form.aiPromptTooShort')) return } @@ -73,7 +89,7 @@ export function CategoryAiPromptModal({ } else if (err instanceof Error) { setError(err.message) } else { - setError('Unable to generate categories with AI.') + setError(t('categories.ai.error')) } } } @@ -91,22 +107,22 @@ export function CategoryAiPromptModal({ role="dialog" aria-modal="true" aria-labelledby="category-ai-prompt-title" + lang={isFa ? 'fa' : 'en'} + dir={isFa ? 'rtl' : 'ltr'} >

- Fill Categories with AI + {t('categories.ai.title')}

-

- Edit the prompt below, then run it to generate and save a category tree. -

+

{t('categories.ai.subtitle')}

@@ -114,7 +130,7 @@ export function CategoryAiPromptModal({
void handleSubmit(e)}>
- +