mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Polish business FA layout and wire special-group keys plus SSL sync.
RTL carousels/FABs, special key+title fields, slider gallery fixes, and dashboard SSL sync agent for super-admin. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
66004a0fba
commit
672091d1f5
@@ -31,6 +31,8 @@ import {
|
||||
type StoreProductListing,
|
||||
} from '../utils/storeProductGroups'
|
||||
import { formatIrtInput, parseIrtInput } from '../utils/irtPrice'
|
||||
import { useLocale } from '@meshkee/dashboard-ui'
|
||||
import { useT } from '../i18n/useT'
|
||||
import filterStyles from '../components/ListFiltersPanel.module.css'
|
||||
import pageStyles from '../components/PageContent.module.css'
|
||||
import styles from './StoreItemsPage.module.css'
|
||||
@@ -40,6 +42,8 @@ export function StoreItemsPage() {
|
||||
}
|
||||
|
||||
function StoreItemsPageContent() {
|
||||
const t = useT()
|
||||
const { locale } = useLocale()
|
||||
const { itemCount, hasItems, addVariant, loadShoppingCard } = useDraftCart()
|
||||
const { showToast } = useToast()
|
||||
const [items, setItems] = useState<StoreItem[]>([])
|
||||
@@ -139,7 +143,7 @@ function StoreItemsPageContent() {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to load store items.')
|
||||
setError(t('storeItems.errorLoad'))
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
@@ -188,7 +192,7 @@ function StoreItemsPageContent() {
|
||||
return
|
||||
}
|
||||
|
||||
showToast('Added to shopping cart.', 'success')
|
||||
showToast(t('storeItems.addedToCart'), 'success')
|
||||
}
|
||||
|
||||
function handleVariantPicked(variant: StoreItem) {
|
||||
@@ -198,7 +202,7 @@ function StoreItemsPageContent() {
|
||||
showToast(feedback, 'error')
|
||||
return
|
||||
}
|
||||
showToast('Added to shopping cart.', 'success')
|
||||
showToast(t('storeItems.addedToCart'), 'success')
|
||||
}
|
||||
|
||||
async function confirmDelete() {
|
||||
@@ -215,7 +219,7 @@ function StoreItemsPageContent() {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to remove store item.')
|
||||
setError(t('storeItems.errorRemove'))
|
||||
}
|
||||
} finally {
|
||||
setIsDeleting(false)
|
||||
@@ -234,65 +238,60 @@ function StoreItemsPageContent() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>My Store Items</h2>
|
||||
<p className={pageStyles.pageSubtitle}>
|
||||
Products for sale with one or more priced variants.
|
||||
</p>
|
||||
<h2 className={pageStyles.pageTitle}>{t('title.storeItems')}</h2>
|
||||
<p className={pageStyles.pageSubtitle}>{t('storeItems.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={filterStyles.filtersPanel}>
|
||||
<div className={filterStyles.filtersTitle}>Filters</div>
|
||||
<div className={filterStyles.filtersTitle}>{t('storeItems.filters')}</div>
|
||||
<div className={filterStyles.filtersGrid}>
|
||||
<div className={filterStyles.filtersInputs}>
|
||||
<div className={`${filterStyles.field} ${filterStyles.fieldCol3}`}>
|
||||
<label htmlFor="filter-store-name">Name</label>
|
||||
<input
|
||||
id="filter-store-name"
|
||||
value={draftName}
|
||||
onChange={(e) => setDraftName(e.target.value)}
|
||||
placeholder="Search by product name"
|
||||
placeholder={t('storeItems.filterNamePlaceholder')}
|
||||
aria-label={t('storeItems.filterName')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${filterStyles.field} ${filterStyles.fieldCol2} ${filterStyles.fieldHalfWidth}`}>
|
||||
<label htmlFor="filter-store-min-price">Min price (IRT)</label>
|
||||
<input
|
||||
id="filter-store-min-price"
|
||||
inputMode="numeric"
|
||||
value={draftMinPrice}
|
||||
onChange={(e) => setDraftMinPrice(formatIrtInput(e.target.value))}
|
||||
placeholder="0"
|
||||
placeholder={t('storeItems.minPrice')}
|
||||
aria-label={t('storeItems.minPrice')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${filterStyles.field} ${filterStyles.fieldCol2} ${filterStyles.fieldHalfWidth}`}>
|
||||
<label htmlFor="filter-store-max-price">Max price (IRT)</label>
|
||||
<input
|
||||
id="filter-store-max-price"
|
||||
inputMode="numeric"
|
||||
value={draftMaxPrice}
|
||||
onChange={(e) => setDraftMaxPrice(formatIrtInput(e.target.value))}
|
||||
placeholder="0"
|
||||
placeholder={t('storeItems.maxPrice')}
|
||||
aria-label={t('storeItems.maxPrice')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${filterStyles.switchField} ${filterStyles.fieldCol3}`}>
|
||||
<span className={filterStyles.switchFieldSpacer} aria-hidden="true">
|
||||
Only discounted
|
||||
</span>
|
||||
<div className={filterStyles.switchInline}>
|
||||
<ToggleSwitch
|
||||
checked={draftOnlyDiscounted}
|
||||
onChange={setDraftOnlyDiscounted}
|
||||
disabled={isLoading}
|
||||
ariaLabel="Only discounted"
|
||||
ariaLabel={t('storeItems.onlyDiscounted')}
|
||||
/>
|
||||
<span
|
||||
className={filterStyles.switchLabel}
|
||||
onClick={() => !isLoading && setDraftOnlyDiscounted(!draftOnlyDiscounted)}
|
||||
>
|
||||
Only discounted
|
||||
{t('storeItems.onlyDiscounted')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -303,8 +302,8 @@ function StoreItemsPageContent() {
|
||||
className={`${filterStyles.iconActionBtn} ${filterStyles.iconActionBtnPrimary}`}
|
||||
onClick={applyFilters}
|
||||
disabled={isLoading}
|
||||
aria-label="Search"
|
||||
title="Search"
|
||||
aria-label={t('storeItems.search')}
|
||||
title={t('storeItems.search')}
|
||||
>
|
||||
<Search size={18} />
|
||||
</button>
|
||||
@@ -313,8 +312,8 @@ function StoreItemsPageContent() {
|
||||
className={`${filterStyles.iconActionBtn} ${filterStyles.iconActionBtnGhost}`}
|
||||
onClick={clearFilters}
|
||||
disabled={isLoading}
|
||||
aria-label="Clear filters"
|
||||
title="Clear filters"
|
||||
aria-label={t('storeItems.clearFilters')}
|
||||
title={t('storeItems.clearFilters')}
|
||||
>
|
||||
<RotateCcw size={18} />
|
||||
</button>
|
||||
@@ -325,13 +324,11 @@ function StoreItemsPageContent() {
|
||||
{error && <p className={styles.error}>{error}</p>}
|
||||
|
||||
{isLoading ? (
|
||||
<p className={styles.empty}>Loading store items...</p>
|
||||
<p className={styles.empty}>{t('storeItems.loading')}</p>
|
||||
) : listings.length === 0 ? (
|
||||
<p className={styles.empty}>
|
||||
No store items yet. Use the + button to add products from your catalog.
|
||||
</p>
|
||||
<p className={styles.empty}>{t('storeItems.empty')}</p>
|
||||
) : filteredListings.length === 0 ? (
|
||||
<p className={styles.empty}>No store items match your filters.</p>
|
||||
<p className={styles.empty}>{t('storeItems.emptyFiltered')}</p>
|
||||
) : (
|
||||
<div className={styles.grid}>
|
||||
{filteredListings.map((listing) => (
|
||||
@@ -355,11 +352,11 @@ function StoreItemsPageContent() {
|
||||
type="button"
|
||||
className={styles.cartFabStrip}
|
||||
onClick={() => setCartOpen(true)}
|
||||
aria-label={`Open shopping cart, ${itemCount} items`}
|
||||
aria-label={t('storeItems.cartOpen', { count: itemCount })}
|
||||
>
|
||||
<span className={styles.cartFabCount}>{itemCount}</span>
|
||||
<span className={styles.cartFabLabel}>
|
||||
{itemCount === 1 ? 'item in cart' : 'items in cart'}
|
||||
{itemCount === 1 ? t('storeItems.cartOne') : t('storeItems.cartMany')}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
@@ -367,7 +364,7 @@ function StoreItemsPageContent() {
|
||||
type="button"
|
||||
className={styles.addFab}
|
||||
onClick={() => setCreateOpen(true)}
|
||||
aria-label="Add store items"
|
||||
aria-label={t('storeItems.add')}
|
||||
>
|
||||
<Plus size={24} />
|
||||
</button>
|
||||
@@ -408,10 +405,13 @@ function StoreItemsPageContent() {
|
||||
|
||||
<ConfirmDeleteModal
|
||||
open={!!deleteTarget}
|
||||
title="Remove from Store"
|
||||
title={t('storeItems.deleteTitle')}
|
||||
message={
|
||||
deleteTarget
|
||||
? `Remove "${deleteTarget.productTitle}" and all ${formatVariantCount(deleteTarget.variantCount)} from your store? This cannot be undone.`
|
||||
? t('storeItems.deleteMessage', {
|
||||
name: deleteTarget.productNameFa || deleteTarget.productTitle,
|
||||
variants: formatVariantCount(deleteTarget.variantCount, locale),
|
||||
})
|
||||
: ''
|
||||
}
|
||||
onConfirm={() => void confirmDelete()}
|
||||
|
||||
Reference in New Issue
Block a user