mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-12 06:40:57 +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
@@ -12,6 +12,7 @@ import { StoreSpecialCarousel } from '../components/StoreSpecialCarousel'
|
||||
import { StoreSpecialModal } from '../components/StoreSpecialModal'
|
||||
import { useDraftCart } from '../context/DraftCartContext'
|
||||
import { useToast } from '../context/ToastContext'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { ApiError } from '../lib/api'
|
||||
import {
|
||||
createStoreSpecial,
|
||||
@@ -27,6 +28,7 @@ import fabStyles from './StoreItemsPage.module.css'
|
||||
import styles from './StoreSpecialsPage.module.css'
|
||||
|
||||
export function StoreSpecialsPage() {
|
||||
const t = useT()
|
||||
const { itemCount, hasItems, addVariant } = useDraftCart()
|
||||
const { showToast } = useToast()
|
||||
const [specials, setSpecials] = useState<StoreSpecial[]>([])
|
||||
@@ -73,7 +75,7 @@ export function StoreSpecialsPage() {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to load special categories.')
|
||||
setError(t('website.specialItems.errorLoad'))
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
@@ -141,45 +143,49 @@ export function StoreSpecialsPage() {
|
||||
)
|
||||
}
|
||||
|
||||
async function handleCreateSpecial(title: string) {
|
||||
async function handleCreateSpecial(values: { key: string; title: string }) {
|
||||
setIsSaving(true)
|
||||
setError('')
|
||||
|
||||
try {
|
||||
const result = await createStoreSpecial({
|
||||
title,
|
||||
key: values.key,
|
||||
title: values.title,
|
||||
sortOrder: specials.length,
|
||||
})
|
||||
setSpecials((prev) => [...prev, result.special])
|
||||
setCreateOpen(false)
|
||||
showToast('Special category created.', 'success')
|
||||
showToast(t('website.specialItems.toastCreated'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to create special category.')
|
||||
setError(t('website.specialItems.errorCreate'))
|
||||
}
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleEditSpecial(title: string) {
|
||||
async function handleEditSpecial(values: { key: string; title: string }) {
|
||||
if (!editSpecialTarget) return
|
||||
|
||||
setIsSaving(true)
|
||||
setError('')
|
||||
|
||||
try {
|
||||
const result = await updateStoreSpecial(editSpecialTarget.id, { title })
|
||||
const result = await updateStoreSpecial(editSpecialTarget.id, {
|
||||
key: values.key,
|
||||
title: values.title,
|
||||
})
|
||||
replaceSpecial(result.special)
|
||||
setEditSpecialTarget(null)
|
||||
showToast('Special category updated.', 'success')
|
||||
showToast(t('website.specialItems.toastUpdated'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to update special category.')
|
||||
setError(t('website.specialItems.errorUpdate'))
|
||||
}
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
@@ -196,12 +202,12 @@ export function StoreSpecialsPage() {
|
||||
await deleteStoreSpecial(deleteSpecialTarget.id)
|
||||
setSpecials((prev) => prev.filter((entry) => entry.id !== deleteSpecialTarget.id))
|
||||
setDeleteSpecialTarget(null)
|
||||
showToast('Special category deleted.', 'success')
|
||||
showToast(t('website.specialItems.toastDeleted'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to delete special category.')
|
||||
setError(t('website.specialItems.errorDelete'))
|
||||
}
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
@@ -223,12 +229,12 @@ export function StoreSpecialsPage() {
|
||||
})
|
||||
replaceSpecial(result.special)
|
||||
setPickItemsTarget(null)
|
||||
showToast('Store items added to special category.', 'success')
|
||||
showToast(t('website.specialItems.toastAdded'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to add store items.')
|
||||
setError(t('website.specialItems.errorAdd'))
|
||||
}
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
@@ -253,12 +259,12 @@ export function StoreSpecialsPage() {
|
||||
})
|
||||
replaceSpecial(result.special)
|
||||
setRemoveTarget(null)
|
||||
showToast('Removed from special category.', 'success')
|
||||
showToast(t('website.specialItems.toastRemoved'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to remove store item.')
|
||||
setError(t('website.specialItems.errorRemove'))
|
||||
}
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
@@ -280,7 +286,7 @@ export function StoreSpecialsPage() {
|
||||
return
|
||||
}
|
||||
|
||||
showToast('Added to shopping cart.', 'success')
|
||||
showToast(t('storeItems.addedToCart'), 'success')
|
||||
}
|
||||
|
||||
function handleVariantPicked(variant: StoreItem) {
|
||||
@@ -290,7 +296,7 @@ export function StoreSpecialsPage() {
|
||||
showToast(feedback, 'error')
|
||||
return
|
||||
}
|
||||
showToast('Added to shopping cart.', 'success')
|
||||
showToast(t('storeItems.addedToCart'), 'success')
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -305,22 +311,17 @@ export function StoreSpecialsPage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>Special Items</h2>
|
||||
<p className={pageStyles.pageSubtitle}>
|
||||
Curate featured store items into categories for your website carousels.
|
||||
</p>
|
||||
<h2 className={pageStyles.pageTitle}>{t('title.specialItems')}</h2>
|
||||
<p className={pageStyles.pageSubtitle}>{t('website.specialItems.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className={styles.error}>{error}</p>}
|
||||
|
||||
{isLoading ? (
|
||||
<p className={styles.empty}>Loading special categories...</p>
|
||||
<p className={styles.empty}>{t('website.specialItems.loading')}</p>
|
||||
) : specials.length === 0 ? (
|
||||
<p className={styles.empty}>
|
||||
No special categories yet. Use the + button to create one, then add store items to each
|
||||
carousel.
|
||||
</p>
|
||||
<p className={styles.empty}>{t('website.specialItems.empty')}</p>
|
||||
) : (
|
||||
<div className={styles.carousels}>
|
||||
{specials.map((special) => (
|
||||
@@ -349,11 +350,11 @@ export function StoreSpecialsPage() {
|
||||
type="button"
|
||||
className={fabStyles.cartFabStrip}
|
||||
onClick={() => setCartOpen(true)}
|
||||
aria-label={`Open shopping cart, ${itemCount} items`}
|
||||
aria-label={t('storeItems.cartOpen', { count: itemCount })}
|
||||
>
|
||||
<span className={fabStyles.cartFabCount}>{itemCount}</span>
|
||||
<span className={fabStyles.cartFabLabel}>
|
||||
{itemCount === 1 ? 'item in cart' : 'items in cart'}
|
||||
{itemCount === 1 ? t('storeItems.cartOne') : t('storeItems.cartMany')}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
@@ -361,7 +362,7 @@ export function StoreSpecialsPage() {
|
||||
type="button"
|
||||
className={fabStyles.addFab}
|
||||
onClick={() => setCreateOpen(true)}
|
||||
aria-label="Add special category"
|
||||
aria-label={t('website.specialItems.addFab')}
|
||||
>
|
||||
<Plus size={24} />
|
||||
</button>
|
||||
@@ -371,6 +372,8 @@ export function StoreSpecialsPage() {
|
||||
open={createOpen}
|
||||
onClose={() => !isSaving && setCreateOpen(false)}
|
||||
onSubmit={handleCreateSpecial}
|
||||
title={t('website.specialItems.createTitle')}
|
||||
submitLabel={t('website.create')}
|
||||
isSubmitting={isSaving}
|
||||
/>
|
||||
|
||||
@@ -378,9 +381,10 @@ export function StoreSpecialsPage() {
|
||||
open={!!editSpecialTarget}
|
||||
onClose={() => !isSaving && setEditSpecialTarget(null)}
|
||||
onSubmit={handleEditSpecial}
|
||||
initialKey={editSpecialTarget?.key ?? ''}
|
||||
initialTitle={editSpecialTarget?.title ?? ''}
|
||||
title="Edit Special Category"
|
||||
submitLabel="Save"
|
||||
title={t('website.specialItems.editTitle')}
|
||||
submitLabel={t('website.save')}
|
||||
isSubmitting={isSaving}
|
||||
/>
|
||||
|
||||
@@ -420,10 +424,10 @@ export function StoreSpecialsPage() {
|
||||
|
||||
<ConfirmDeleteModal
|
||||
open={!!deleteSpecialTarget}
|
||||
title="Delete Special Category"
|
||||
title={t('website.specialItems.deleteTitle')}
|
||||
message={
|
||||
deleteSpecialTarget
|
||||
? `Delete "${deleteSpecialTarget.title}"? Store items will remain in your catalog.`
|
||||
? t('website.specialItems.deleteMessage', { title: deleteSpecialTarget.title })
|
||||
: ''
|
||||
}
|
||||
onConfirm={() => void confirmDeleteSpecial()}
|
||||
@@ -432,10 +436,13 @@ export function StoreSpecialsPage() {
|
||||
|
||||
<ConfirmDeleteModal
|
||||
open={!!removeTarget}
|
||||
title="Remove from Special"
|
||||
title={t('website.specialItems.removeTitle')}
|
||||
message={
|
||||
removeTarget
|
||||
? `Remove "${removeTarget.listing.productTitle}" from "${removeTarget.special.title}"?`
|
||||
? t('website.specialItems.removeMessage', {
|
||||
name: removeTarget.listing.productTitle,
|
||||
group: removeTarget.special.title,
|
||||
})
|
||||
: ''
|
||||
}
|
||||
onConfirm={() => void confirmRemoveFromSpecial()}
|
||||
|
||||
Reference in New Issue
Block a user