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:
Alireza Hassani
2026-08-03 00:06:44 +03:30
co-authored by Cursor
parent 66004a0fba
commit 672091d1f5
113 changed files with 4154 additions and 1451 deletions
+42 -35
View File
@@ -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()}