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
@@ -5,6 +5,7 @@ import { StepColorPicker } from '../components/StepColorPicker'
|
||||
import { Switch } from '../components/Switch'
|
||||
import { Tooltip } from '../components/Tooltip'
|
||||
import { useToast } from '../context/ToastContext'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { ApiError } from '../lib/api'
|
||||
import {
|
||||
getSettings,
|
||||
@@ -53,6 +54,7 @@ function stepsAreEqual(a: OrderProcessStep[], b: OrderProcessStep[]) {
|
||||
}
|
||||
|
||||
export function StoreSettingsPage() {
|
||||
const t = useT()
|
||||
const { showToast } = useToast()
|
||||
const [settings, setSettings] = useState<StoreSettings>(DEFAULT_STORE_SETTINGS)
|
||||
const [draftSteps, setDraftSteps] = useState<OrderProcessStep[]>(
|
||||
@@ -81,7 +83,7 @@ export function StoreSettingsPage() {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to load settings.')
|
||||
setError(t('productSettings.errorLoad'))
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
@@ -95,12 +97,12 @@ export function StoreSettingsPage() {
|
||||
try {
|
||||
const data = await updateStoreSettings({ onlineSellEnabled: checked })
|
||||
setSettings(data.settings.store)
|
||||
showToast('Settings saved.', 'success')
|
||||
showToast(t('productSettings.saved'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to save settings.')
|
||||
setError(t('productSettings.errorSave'))
|
||||
}
|
||||
} finally {
|
||||
setSavingKey(null)
|
||||
@@ -163,11 +165,11 @@ export function StoreSettingsPage() {
|
||||
const normalized = normalizeSteps(draftSteps)
|
||||
const hasEmptyLabel = normalized.some((step) => !step.label || !step.labelFa)
|
||||
if (!normalized.length) {
|
||||
setError('Add at least one order process step.')
|
||||
setError(t('storeSettings.error.minSteps'))
|
||||
return
|
||||
}
|
||||
if (hasEmptyLabel) {
|
||||
setError('Every order step needs an English and Farsi label.')
|
||||
setError(t('storeSettings.error.labels'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -178,12 +180,12 @@ export function StoreSettingsPage() {
|
||||
const data = await updateStoreSettings({ orderProcessSteps: normalized })
|
||||
setSettings(data.settings.store)
|
||||
setDraftSteps(data.settings.store.orderProcessSteps)
|
||||
showToast('Order process steps saved.', 'success')
|
||||
showToast(t('storeSettings.stepsSaved'), 'success')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to save order process steps.')
|
||||
setError(t('storeSettings.error.saveSteps'))
|
||||
}
|
||||
} finally {
|
||||
setSavingKey(null)
|
||||
@@ -213,10 +215,8 @@ export function StoreSettingsPage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>Store settings</h2>
|
||||
<p className={pageStyles.pageSubtitle}>
|
||||
Control online sales and define how orders move through fulfillment.
|
||||
</p>
|
||||
<h2 className={pageStyles.pageTitle}>{t('storeSettings.title')}</h2>
|
||||
<p className={pageStyles.pageSubtitle}>{t('storeSettings.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -227,27 +227,26 @@ export function StoreSettingsPage() {
|
||||
)}
|
||||
|
||||
<section className={sharedStyles.panel}>
|
||||
<h3 className={sharedStyles.sectionTitle}>Sales</h3>
|
||||
<h3 className={sharedStyles.sectionTitle}>{t('storeSettings.sales')}</h3>
|
||||
|
||||
{isLoading ? (
|
||||
<p className={sharedStyles.status}>Loading settings...</p>
|
||||
<p className={sharedStyles.status}>{t('productSettings.loading')}</p>
|
||||
) : (
|
||||
<div className={sharedStyles.list}>
|
||||
<div className={sharedStyles.row}>
|
||||
<div className={sharedStyles.rowText}>
|
||||
<label htmlFor="online-sell" className={sharedStyles.rowLabel}>
|
||||
Online sell
|
||||
{t('storeSettings.onlineSell')}
|
||||
</label>
|
||||
<p className={sharedStyles.rowDescription}>
|
||||
When disabled, all sales on your website are turned off. Customers
|
||||
will not be able to place new orders online.
|
||||
{t('storeSettings.onlineSellDesc')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="online-sell"
|
||||
checked={settings.onlineSellEnabled}
|
||||
disabled={savingKey === 'onlineSell'}
|
||||
aria-label="Online sell"
|
||||
aria-label={t('storeSettings.onlineSell')}
|
||||
onChange={(checked) => void handleOnlineSellChange(checked)}
|
||||
/>
|
||||
</div>
|
||||
@@ -258,16 +257,13 @@ export function StoreSettingsPage() {
|
||||
<section className={`${sharedStyles.panel} ${styles.stepsPanel}`}>
|
||||
<div className={styles.stepsHeader}>
|
||||
<div>
|
||||
<h3 className={sharedStyles.sectionTitle}>Order process</h3>
|
||||
<p className={styles.stepsDescription}>
|
||||
Define the steps an order can move through — for example: under
|
||||
processing, ready for shipping, shipped, delivered.
|
||||
</p>
|
||||
<h3 className={sharedStyles.sectionTitle}>{t('storeSettings.orderProcess')}</h3>
|
||||
<p className={styles.stepsDescription}>{t('storeSettings.orderProcessDesc')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<p className={sharedStyles.status}>Loading settings...</p>
|
||||
<p className={sharedStyles.status}>{t('productSettings.loading')}</p>
|
||||
) : (
|
||||
<>
|
||||
<div className={styles.stepsList}>
|
||||
@@ -277,14 +273,14 @@ export function StoreSettingsPage() {
|
||||
<StepColorPicker
|
||||
value={normalizeStepColor(step.color, defaultStepColorForId(step.id, index))}
|
||||
onChange={(color) => updateStepColor(step.id, color)}
|
||||
ariaLabel={`Color for step ${index + 1}`}
|
||||
ariaLabel={t('storeSettings.stepColorAria', { index: index + 1 })}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className={styles.stepInput}
|
||||
value={step.label}
|
||||
placeholder="Label (EN)"
|
||||
aria-label={`Order step ${index + 1} English label`}
|
||||
placeholder={t('storeSettings.labelEn')}
|
||||
aria-label={t('storeSettings.stepEnAria', { index: index + 1 })}
|
||||
dir="ltr"
|
||||
lang="en"
|
||||
onChange={(e) => updateStepLabel(step.id, e.target.value)}
|
||||
@@ -293,42 +289,42 @@ export function StoreSettingsPage() {
|
||||
type="text"
|
||||
className={`${styles.stepInput} ${styles.stepInputFa}`}
|
||||
value={step.labelFa ?? ''}
|
||||
placeholder="عنوان (فارسی)"
|
||||
aria-label={`Order step ${index + 1} Farsi label`}
|
||||
placeholder={t('storeSettings.labelFa')}
|
||||
aria-label={t('storeSettings.stepFaAria', { index: index + 1 })}
|
||||
dir="rtl"
|
||||
lang="fa"
|
||||
onChange={(e) => updateStepLabelFa(step.id, e.target.value)}
|
||||
/>
|
||||
<div className={styles.stepControls}>
|
||||
<Tooltip label="Move step up">
|
||||
<Tooltip label={t('storeSettings.moveUp')}>
|
||||
<button
|
||||
type="button"
|
||||
className={controlStyles.controlBtn}
|
||||
onClick={() => moveStep(step.id, -1)}
|
||||
disabled={index === 0}
|
||||
aria-label="Move step up"
|
||||
aria-label={t('storeSettings.moveUp')}
|
||||
>
|
||||
<ChevronUp size={16} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip label="Move step down">
|
||||
<Tooltip label={t('storeSettings.moveDown')}>
|
||||
<button
|
||||
type="button"
|
||||
className={controlStyles.controlBtn}
|
||||
onClick={() => moveStep(step.id, 1)}
|
||||
disabled={index === draftSteps.length - 1}
|
||||
aria-label="Move step down"
|
||||
aria-label={t('storeSettings.moveDown')}
|
||||
>
|
||||
<ChevronDown size={16} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip label="Remove step">
|
||||
<Tooltip label={t('storeSettings.removeStep')}>
|
||||
<button
|
||||
type="button"
|
||||
className={removeStyles.removeRowBtn}
|
||||
onClick={() => removeStep(step.id)}
|
||||
disabled={draftSteps.length <= 1}
|
||||
aria-label="Remove step"
|
||||
aria-label={t('storeSettings.removeStep')}
|
||||
>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
@@ -338,15 +334,13 @@ export function StoreSettingsPage() {
|
||||
))}
|
||||
|
||||
{!draftSteps.length && (
|
||||
<p className={styles.emptyText}>
|
||||
No order steps yet. Add the first step to define your workflow.
|
||||
</p>
|
||||
<p className={styles.emptyText}>{t('storeSettings.emptySteps')}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button type="button" className={styles.addStepBtn} onClick={addStep}>
|
||||
<Plus size={18} />
|
||||
Add step
|
||||
{t('storeSettings.addStep')}
|
||||
</button>
|
||||
|
||||
<div className={styles.stepsActions}>
|
||||
@@ -356,7 +350,9 @@ export function StoreSettingsPage() {
|
||||
onClick={() => void handleSaveSteps()}
|
||||
disabled={!canSaveSteps || savingKey === 'orderSteps'}
|
||||
>
|
||||
{savingKey === 'orderSteps' ? 'Saving...' : 'Save steps'}
|
||||
{savingKey === 'orderSteps'
|
||||
? t('storeSettings.saving')
|
||||
: t('storeSettings.saveSteps')}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user