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)}>
- +