From b743c09e4d888094fe19830309c983b08c7ceb2f Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sat, 19 Sep 2026 08:20:34 +0330 Subject: [PATCH] Improve product AI UX, category search, and card actions. Co-authored-by: Cursor --- .../components/AddProductByAiModal.module.css | 18 ++ .../src/components/AddProductByAiModal.tsx | 73 +++---- .../CategoryAiPromptModal.module.css | 3 +- .../src/components/CategoryAiPromptModal.tsx | 8 +- .../src/components/ProductCard.module.css | 12 +- apps/business/src/components/ProductCard.tsx | 192 ++++++++++-------- .../components/ProductCardOverflowMenu.tsx | 156 ++++++++++++++ .../components/ProductFieldAiPromptModal.tsx | 2 +- .../components/TechnicalFormAiPromptModal.tsx | 9 +- apps/business/src/i18n/messages.ts | 44 ++++ .../src/pages/CategoriesPage.module.css | 64 ++++++ apps/business/src/pages/CategoriesPage.tsx | 110 +++++++--- apps/business/src/pages/MyProductsPage.tsx | 31 --- apps/business/src/styles/ai.module.css | 3 + apps/business/src/utils/categories.ts | 19 ++ 15 files changed, 558 insertions(+), 186 deletions(-) create mode 100644 apps/business/src/components/ProductCardOverflowMenu.tsx diff --git a/apps/business/src/components/AddProductByAiModal.module.css b/apps/business/src/components/AddProductByAiModal.module.css index 4c9f10a..0e70e68 100644 --- a/apps/business/src/components/AddProductByAiModal.module.css +++ b/apps/business/src/components/AddProductByAiModal.module.css @@ -1,3 +1,7 @@ +.modal { + width: min(560px, calc(100vw - 32px)); +} + .headerBlock { flex: 1; min-width: 0; @@ -10,6 +14,20 @@ line-height: 1.45; } +.prompt { + width: 100%; + min-height: 120px; + resize: vertical; + line-height: 1.55; +} + +.promptHint { + margin: 6px 0 0; + font-size: 12px; + line-height: 1.45; + color: var(--text-muted); +} + .error { margin: 0; padding: 10px 12px; diff --git a/apps/business/src/components/AddProductByAiModal.tsx b/apps/business/src/components/AddProductByAiModal.tsx index 30e6384..4298fea 100644 --- a/apps/business/src/components/AddProductByAiModal.tsx +++ b/apps/business/src/components/AddProductByAiModal.tsx @@ -1,7 +1,9 @@ import { useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' import { Sparkles, X } from 'lucide-react' +import { useLocale } from '@meshkee/dashboard-ui' import { useToast } from '../context/ToastContext' +import { useT } from '../i18n/useT' import { ApiError } from '../lib/api' import { listProductCategories, mapProductCategoryToUi } from '../services/productCategoryService' import { createProductByAi, type ProductAiLanguage } from '../services/productAiService' @@ -26,26 +28,29 @@ export function AddProductByAiModal({ onClose, onCreated, }: AddProductByAiModalProps) { + const t = useT() + const { locale } = useLocale() const { showToast } = useToast() const [mounted, setMounted] = useState(open) const [closing, setClosing] = useState(false) const [categories, setCategories] = useState([]) const [categoryId, setCategoryId] = useState('') - const [name, setName] = useState('') - const [language, setLanguage] = useState('en') + const [prompt, setPrompt] = useState('') + const [language, setLanguage] = useState(locale === 'fa' ? 'fa' : 'en') const [isLoadingCategories, setIsLoadingCategories] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState('') const categoryOptions = useMemo(() => flattenCategories(categories), [categories]) + const promptDir = language === 'fa' ? 'rtl' : 'ltr' useEffect(() => { if (open) { setMounted(true) setClosing(false) setCategoryId('') - setName('') - setLanguage('en') + setPrompt('') + setLanguage(locale === 'fa' ? 'fa' : 'en') setError('') } else if (mounted) { setClosing(true) @@ -55,7 +60,7 @@ export function AddProductByAiModal({ }, ANIMATION_MS) return () => clearTimeout(timer) } - }, [open, mounted]) + }, [open, mounted, locale]) useEffect(() => { if (!open) return @@ -86,7 +91,7 @@ export function AddProductByAiModal({ if (err instanceof ApiError) { setError(err.message) } else { - setError('Unable to load categories.') + setError(t('products.ai.loadCategoriesError')) } } finally { setIsLoadingCategories(false) @@ -95,7 +100,7 @@ export function AddProductByAiModal({ async function handleSubmit(e: React.FormEvent) { e.preventDefault() - if (!categoryId || !name.trim()) return + if (!categoryId || !prompt.trim()) return setIsSubmitting(true) setError('') @@ -103,7 +108,7 @@ export function AddProductByAiModal({ try { const result = await createProductByAi({ categoryId, - name: name.trim(), + name: prompt.trim(), language, }) showToast(result.message, 'success') @@ -115,7 +120,7 @@ export function AddProductByAiModal({ } else if (err instanceof Error) { setError(err.message) } else { - setError('Unable to create product with AI.') + setError(t('products.ai.createError')) } } finally { setIsSubmitting(false) @@ -131,7 +136,7 @@ export function AddProductByAiModal({ role="presentation" >

- Add Product by AI + {t('products.ai.title')}

-

- AI will generate product content and technical data. You can add images and - variations afterward. -

+

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

@@ -159,43 +161,44 @@ export function AddProductByAiModal({
void handleSubmit(e)}>
- +
- - setName(e.target.value)} - placeholder={ - language === 'fa' ? 'نام محصول را وارد کنید' : 'Enter product name' - } - dir={language === 'fa' ? 'rtl' : 'ltr'} - className={language === 'fa' ? 'faText' : undefined} + +