From 9a59bc8f6531e3f664a3837355fdca1a7f7a6533 Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sat, 15 Aug 2026 14:56:08 +0330 Subject: [PATCH] Rename store items to store products and add an empty-state CTA. Co-authored-by: Cursor --- apps/business/src/i18n/messages.ts | 57 +++++++++----- .../src/pages/StoreItemsPage.module.css | 76 +++++++++++++++++++ apps/business/src/pages/StoreItemsPage.tsx | 20 ++++- 3 files changed, 130 insertions(+), 23 deletions(-) diff --git a/apps/business/src/i18n/messages.ts b/apps/business/src/i18n/messages.ts index 50f5262..7777364 100644 --- a/apps/business/src/i18n/messages.ts +++ b/apps/business/src/i18n/messages.ts @@ -662,7 +662,8 @@ const en = { 'storeItems.search': 'Search', 'storeItems.clearFilters': 'Clear filters', 'storeItems.loading': 'Loading store items...', - 'storeItems.empty': 'No store items yet. Use the + button to add products from your catalog.', + 'storeItems.empty': "You don't have any products for sale yet.", + 'storeItems.emptyHint': 'Add products from your catalog to start selling.', 'storeItems.emptyFiltered': 'No store items match your filters.', 'storeItems.pagination': 'Page {page} / {totalPages} · {perPage} per page · {total} total', @@ -1471,11 +1472,17 @@ const en = { 'website.ePayment.gateway.digipay': 'DigiPay', 'website.ePayment.gateway.digipayDesc': 'DigiPay checkout — coming soon.', 'website.ePayment.gateway.zarinpal': 'Zarinpal', - 'website.ePayment.gateway.zarinpalDesc': 'Zarinpal gateway — coming soon.', + 'website.ePayment.gateway.zarinpalDesc': + 'Enter your Merchant ID from the ZarinPal panel. Customers can choose ZarinPal or Mellat at checkout when both are enabled.', 'website.ePayment.comingSoon': 'Coming soon', 'website.ePayment.terminalId': 'Terminal ID', 'website.ePayment.username': 'Username', 'website.ePayment.password': 'Password', + 'website.ePayment.merchantId': 'Merchant ID', + 'website.ePayment.merchantIdPlaceholder': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'website.ePayment.sandbox': 'Sandbox mode', + 'website.ePayment.sandboxDesc': + 'Use ZarinPal sandbox endpoints for testing. Turn off for live payments.', 'website.ePayment.passwordSet': 'saved', 'website.ePayment.passwordPlaceholder': 'Enter gateway password', 'website.ePayment.passwordPlaceholderSet': 'Leave blank to keep current password', @@ -1484,6 +1491,7 @@ const en = { 'website.ePayment.saveCredentials': 'Save credentials', 'website.ePayment.error.credentials': 'Terminal ID and username are required when Mellat is enabled.', 'website.ePayment.error.passwordRequired': 'Password is required the first time you enable Mellat.', + 'website.ePayment.error.zarinpalMerchant': 'Merchant ID is required when ZarinPal is enabled.', 'title.signIn': 'Sign in', 'title.home': 'Home', @@ -1859,7 +1867,7 @@ const fa: Record = { 'nav.products.settings': 'تنظیمات', 'nav.store': 'فروشگاه', 'nav.store.overview': 'نمای کلی', - 'nav.store.items': 'اقلام فروشگاه', + 'nav.store.items': 'محصولات فروشگاه', 'nav.store.orders': 'سفارش‌های من', 'nav.store.shipping': 'هزینه ارسال', 'nav.store.cards': 'کارت‌های خرید', @@ -2474,9 +2482,9 @@ const fa: Record = { 'productSettings.reviewsAutoDesc': 'بررسی‌های کارشناسی جدید بلافاصله پس از ثبت منتشر می‌شوند. در صورت نیاز بعداً می‌توانید آن‌ها را رد کنید.', - 'store.page.subtitle': 'اقلام، سفارش‌ها و تنظیمات فروشگاه را مدیریت کنید.', - 'store.card.items.desc': 'همه اقلام فهرست‌شده در فروشگاه را ببینید و مدیریت کنید.', - 'store.card.items.link': 'مشاهده اقلام', + 'store.page.subtitle': 'محصولات، سفارش‌ها و تنظیمات فروشگاه را مدیریت کنید.', + 'store.card.items.desc': 'همه محصولات فهرست‌شده در فروشگاه را ببینید و مدیریت کنید.', + 'store.card.items.link': 'مشاهده محصولات', 'store.card.orders.desc': 'سفارش‌های مشتریان و ارسال را پیگیری و مدیریت کنید.', 'store.card.orders.link': 'مشاهده سفارش‌ها', 'store.card.shipping.desc': 'نرخ‌ها، مناطق و گزینه‌های ارسال را پیکربندی کنید.', @@ -2495,14 +2503,15 @@ const fa: Record = { 'storeItems.onlyDiscounted': 'فقط تخفیف‌دار', 'storeItems.search': 'جستجو', 'storeItems.clearFilters': 'پاک کردن فیلترها', - 'storeItems.loading': 'در حال بارگذاری اقلام فروشگاه...', - 'storeItems.empty': 'هنوز قلمی در فروشگاه نیست. با دکمه + از کاتالوگ اضافه کنید.', - 'storeItems.emptyFiltered': 'هیچ قلمی با فیلترهای شما مطابقت ندارد.', + 'storeItems.loading': 'در حال بارگذاری محصولات فروشگاه...', + 'storeItems.empty': 'هنوز هیچ محصولی برای فروش ندارید', + 'storeItems.emptyHint': 'از کاتالوگ محصول اضافه کنید.', + 'storeItems.emptyFiltered': 'هیچ محصولی با فیلترهای شما مطابقت ندارد.', 'storeItems.pagination': 'صفحه {page} / {totalPages} · {perPage} در هر صفحه · {total} مجموع', - 'storeItems.errorLoad': 'بارگذاری اقلام فروشگاه ممکن نشد.', - 'storeItems.errorRemove': 'حذف قلم از فروشگاه ممکن نشد.', - 'storeItems.add': 'افزودن اقلام فروشگاه', + 'storeItems.errorLoad': 'بارگذاری محصولات فروشگاه ممکن نشد.', + 'storeItems.errorRemove': 'حذف محصول از فروشگاه ممکن نشد.', + 'storeItems.add': 'افزودن محصول به فروشگاه', 'storeItems.cartOpen': 'باز کردن سبد خرید، {count} قلم', 'storeItems.cartOne': 'قلم در سبد', 'storeItems.cartMany': 'قلم در سبد', @@ -2521,7 +2530,7 @@ const fa: Record = { 'storeItems.card.addToCart': 'افزودن به سبد خرید', 'storeItems.pick.title': 'انتخاب تنوع', 'storeItems.pick.inStock': '{count} موجود', - 'storeItems.create.title': 'افزودن اقلام فروشگاه', + 'storeItems.create.title': 'افزودن محصول به فروشگاه', 'storeItems.create.subtitle': 'تنوع‌های قابل فروش را از تنوع‌های یک محصول بسازید.', 'storeItems.create.productPlaceholder': 'محصول — حداقل ۳ کاراکتر برای جستجو', 'storeItems.create.loadingVariations': 'در حال بارگذاری تنوع‌های محصول...', @@ -2533,7 +2542,7 @@ const fa: Record = { 'storeItems.create.addAllProducts': 'افزودن همه محصولات', 'storeItems.create.addingAll': 'در حال ایجاد برای همه محصولات…', 'storeItems.create.addAllDone': 'ایجاد شد {created}، رد شد {skipped}، ناموفق {failed}', - 'storeItems.create.addAllConfirmTitle': 'افزودن اقلام برای همه محصولات؟', + 'storeItems.create.addAllConfirmTitle': 'افزودن همه محصولات به فروشگاه؟', 'storeItems.create.addAllConfirmIntro': 'برای هر محصولی که هنوز به فروشگاه اضافه نشده است، تنوع‌های قابل فروش ساخته می‌شود. فقط وقتی می‌خواهید همهٔ محصولات یک‌جا به فروشگاه اضافه شود از این گزینه استفاده کنید.', 'storeItems.create.addAllConfirmMax': @@ -2547,16 +2556,16 @@ const fa: Record = { 'storeItems.create.addAllResultTitle': 'ایجاد گروهی تمام شد', 'storeItems.create.addAllDoneBtn': 'باشه', 'storeItems.create.cancel': 'انصراف', - 'storeItems.create.submit': 'ایجاد اقلام فروشگاه', - 'storeItems.create.submitting': 'در حال ایجاد…', + 'storeItems.create.submit': 'افزودن محصول به فروشگاه', + 'storeItems.create.submitting': 'در حال افزودن…', 'storeItems.create.errorProducts': 'بارگذاری محصولات ممکن نشد.', 'storeItems.create.errorVariations': 'بارگذاری تنوع‌های محصول ممکن نشد.', - 'storeItems.create.errorCreate': 'ایجاد اقلام فروشگاه ممکن نشد.', + 'storeItems.create.errorCreate': 'افزودن محصول به فروشگاه ممکن نشد.', 'storeItems.price.contact': 'برای قیمت، تماس بگیرید', 'storeItems.edit.title': 'ویرایش محصول فروشگاه', 'storeItems.edit.save': 'ذخیره تغییرات', 'storeItems.edit.saving': 'در حال ذخیره…', - 'storeItems.edit.errorSave': 'ذخیره اقلام فروشگاه ممکن نشد.', + 'storeItems.edit.errorSave': 'ذخیره محصول فروشگاه ممکن نشد.', 'storeItems.search.clear': 'پاک کردن انتخاب', 'storeItems.search.noProducts': 'محصولی موجود نیست.', 'storeItems.search.noMatch': 'محصولی یافت نشد.', @@ -3303,11 +3312,17 @@ const fa: Record = { 'website.ePayment.gateway.digipay': 'دیجی‌پی', 'website.ePayment.gateway.digipayDesc': 'پرداخت دیجی‌پی — به‌زودی.', 'website.ePayment.gateway.zarinpal': 'زرین‌پال', - 'website.ePayment.gateway.zarinpalDesc': 'درگاه زرین‌پال — به‌زودی.', + 'website.ePayment.gateway.zarinpalDesc': + 'کد درگاه (Merchant ID) را از پنل زرین‌پال وارد کنید. اگر ملت و زرین‌پال هر دو فعال باشند، مشتری در مرحله پرداخت یکی را انتخاب می‌کند.', 'website.ePayment.comingSoon': 'به‌زودی', 'website.ePayment.terminalId': 'شناسه ترمینال', 'website.ePayment.username': 'نام کاربری', 'website.ePayment.password': 'رمز عبور', + 'website.ePayment.merchantId': 'کد درگاه (Merchant ID)', + 'website.ePayment.merchantIdPlaceholder': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'website.ePayment.sandbox': 'حالت آزمایشی (Sandbox)', + 'website.ePayment.sandboxDesc': + 'برای تست از محیط سندباکس زرین‌پال استفاده می‌شود. برای پرداخت واقعی خاموش کنید.', 'website.ePayment.passwordSet': 'ذخیره‌شده', 'website.ePayment.passwordPlaceholder': 'رمز عبور درگاه را وارد کنید', 'website.ePayment.passwordPlaceholderSet': 'برای حفظ رمز فعلی خالی بگذارید', @@ -3318,6 +3333,8 @@ const fa: Record = { 'با فعال بودن ملت، شناسه ترمینال و نام کاربری الزامی است.', 'website.ePayment.error.passwordRequired': 'برای اولین بار فعال‌سازی ملت، رمز عبور الزامی است.', + 'website.ePayment.error.zarinpalMerchant': + 'با فعال بودن زرین‌پال، کد درگاه (Merchant ID) الزامی است.', 'title.signIn': 'ورود', 'title.home': 'خانه', @@ -3332,7 +3349,7 @@ const fa: Record = { 'title.brands': 'برندها', 'title.settings': 'تنظیمات', 'title.store': 'فروشگاه', - 'title.storeItems': 'اقلام فروشگاه', + 'title.storeItems': 'محصولات فروشگاه', 'title.orders': 'سفارش‌های من', 'title.shoppingCards': 'کارت‌های خرید', 'title.customers': 'کاربران', diff --git a/apps/business/src/pages/StoreItemsPage.module.css b/apps/business/src/pages/StoreItemsPage.module.css index 68fe921..413f154 100644 --- a/apps/business/src/pages/StoreItemsPage.module.css +++ b/apps/business/src/pages/StoreItemsPage.module.css @@ -14,6 +14,82 @@ border-radius: var(--radius); } +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + padding: 56px 28px; + text-align: center; + background: var(--glass-bg); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + border: 1px solid var(--glass-border); + border-radius: var(--radius); + box-shadow: var(--glass-shadow); +} + +.emptyIcon { + width: 56px; + height: 56px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 6px; + border-radius: 16px; + color: var(--primary); + background: linear-gradient( + 145deg, + rgba(255, 255, 255, 0.72) 0%, + rgba(var(--primary-rgb) / 0.12) 100% + ); + border: 1px solid rgba(255, 255, 255, 0.45); + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06); +} + +.emptyTitle { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--text-primary); + line-height: 1.4; +} + +.emptyHint { + margin: 0 0 8px; + max-width: 360px; + font-size: 13px; + line-height: 1.65; + color: var(--text-secondary); +} + +.emptyCta { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + min-height: 42px; + padding: 10px 20px; + font-size: 14px; + font-weight: 600; + color: #fff; + text-align: center; + background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); + border-radius: 999px; + box-shadow: 0 8px 20px rgba(var(--primary-rgb) / 0.32); + transition: transform 0.2s, box-shadow 0.2s; +} + +button.emptyCta { + text-align: center; +} + +.emptyCta:hover { + transform: translateY(-1px); + box-shadow: 0 12px 26px rgba(var(--primary-rgb) / 0.4); +} + .error { margin-bottom: 16px; padding: 12px 14px; diff --git a/apps/business/src/pages/StoreItemsPage.tsx b/apps/business/src/pages/StoreItemsPage.tsx index c31a358..5cd9d8a 100644 --- a/apps/business/src/pages/StoreItemsPage.tsx +++ b/apps/business/src/pages/StoreItemsPage.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from 'react' import { useSearchParams } from 'react-router-dom' -import { Plus, RotateCcw, Search } from 'lucide-react' +import { Package, Plus, RotateCcw, Search } from 'lucide-react' import { Breadcrumbs } from '../components/Breadcrumbs' import { PageTitle } from '../components/PageTitle' import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal' @@ -283,7 +283,7 @@ function StoreItemsPageContent() {
- {t('title.storeItems')} + {t('title.storeItems')}

{t('storeItems.subtitle')}

@@ -370,7 +370,21 @@ function StoreItemsPageContent() { {isLoading ? (

{t('storeItems.loading')}

) : listings.length === 0 ? ( -

{t('storeItems.empty')}

+
+ +

{t('storeItems.empty')}

+

{t('storeItems.emptyHint')}

+ +
) : filteredListings.length === 0 ? (

{t('storeItems.emptyFiltered')}

) : (