Localize order modals, add dual logos, and improve admin order tools.
Farsi business order/variation UI, light/dark logo upload on business profile, payment order steps, variation editing, super-admin site links, and checkout/auth fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
dc570c1e7f
commit
5e799eddf9
@@ -44,6 +44,26 @@
|
||||
grid-column: span 12;
|
||||
}
|
||||
|
||||
.logoModeRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logoModeLabel {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.logoModeLabelActive {
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.basicAside {
|
||||
grid-column: span 12;
|
||||
display: grid;
|
||||
|
||||
@@ -14,6 +14,7 @@ import { PageTitle } from '../components/PageTitle'
|
||||
import { ImageCropper } from '../components/ImageCropper'
|
||||
import { MultiSelectDropdown } from '../components/MultiSelectDropdown'
|
||||
import { RichTextEditor } from '../components/RichTextEditor'
|
||||
import { Switch } from '../components/Switch'
|
||||
import { useToast } from '../context/ToastContext'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { ApiError } from '../lib/api'
|
||||
@@ -80,6 +81,9 @@ export function BusinessProfilePage() {
|
||||
const [vision, setVision] = useState('')
|
||||
const [logo, setLogo] = useState<string | null>(null)
|
||||
const [logoMediaId, setLogoMediaId] = useState<string | null>(null)
|
||||
const [logoDark, setLogoDark] = useState<string | null>(null)
|
||||
const [logoDarkMediaId, setLogoDarkMediaId] = useState<string | null>(null)
|
||||
const [logoModeDark, setLogoModeDark] = useState(false)
|
||||
const [addresses, setAddresses] = useState<AddressDraft[]>([createEmptyAddressItem()])
|
||||
const [phoneNumbers, setPhoneNumbers] = useState<BusinessPhoneNumber[]>([
|
||||
createEmptyPhone(),
|
||||
@@ -153,6 +157,8 @@ export function BusinessProfilePage() {
|
||||
setVision(profileData.profile.vision)
|
||||
setLogo(profileData.profile.logoUrl)
|
||||
setLogoMediaId(profileData.profile.logoMediaId)
|
||||
setLogoDark(profileData.profile.logoDarkUrl)
|
||||
setLogoDarkMediaId(profileData.profile.logoDarkMediaId)
|
||||
|
||||
const nextAddresses =
|
||||
profileData.addresses.length > 0
|
||||
@@ -273,6 +279,11 @@ export function BusinessProfilePage() {
|
||||
'business-logo.png',
|
||||
logoMediaId,
|
||||
)
|
||||
const nextLogoDarkMediaId = await resolveDataUrlToMediaId(
|
||||
logoDark,
|
||||
'business-logo-dark.png',
|
||||
logoDarkMediaId,
|
||||
)
|
||||
|
||||
const payloadAddresses = addresses
|
||||
.filter(
|
||||
@@ -309,6 +320,7 @@ export function BusinessProfilePage() {
|
||||
phoneNumbers: payloadPhones,
|
||||
socialMedia,
|
||||
logoMediaId: nextLogoMediaId,
|
||||
logoDarkMediaId: nextLogoDarkMediaId,
|
||||
categoryIds,
|
||||
addresses: payloadAddresses,
|
||||
})
|
||||
@@ -358,9 +370,27 @@ export function BusinessProfilePage() {
|
||||
<div className={formStyles.formGrid}>
|
||||
<div className={`${formStyles.field} ${styles.logoCol}`}>
|
||||
<label>{t('bizProfile.logo')}</label>
|
||||
<div className={styles.logoModeRow}>
|
||||
<span
|
||||
className={`${styles.logoModeLabel} ${!logoModeDark ? styles.logoModeLabelActive : ''}`}
|
||||
>
|
||||
{t('bizProfile.logo.light')}
|
||||
</span>
|
||||
<Switch
|
||||
checked={logoModeDark}
|
||||
onChange={setLogoModeDark}
|
||||
aria-label={t('bizProfile.logo.modeSwitch')}
|
||||
/>
|
||||
<span
|
||||
className={`${styles.logoModeLabel} ${logoModeDark ? styles.logoModeLabelActive : ''}`}
|
||||
>
|
||||
{t('bizProfile.logo.dark')}
|
||||
</span>
|
||||
</div>
|
||||
<ImageCropper
|
||||
value={logo}
|
||||
onChange={setLogo}
|
||||
key={logoModeDark ? 'logo-dark' : 'logo-light'}
|
||||
value={logoModeDark ? logoDark : logo}
|
||||
onChange={logoModeDark ? setLogoDark : setLogo}
|
||||
outputFormat="png"
|
||||
accept="image/png"
|
||||
uploadLabel={t('bizProfile.logo.upload')}
|
||||
|
||||
@@ -40,6 +40,17 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.masterOffHint {
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
background: rgba(217, 119, 6, 0.1);
|
||||
border: 1px solid rgba(217, 119, 6, 0.28);
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.credentials {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
|
||||
@@ -157,6 +157,8 @@ export function SettingsPage() {
|
||||
async function handleMellatToggle(checked: boolean) {
|
||||
await patchEPayment('mellat-enabled', {
|
||||
...ePayment,
|
||||
// Turning a gateway on implies online payments are allowed at checkout.
|
||||
enabled: checked ? true : ePayment.enabled,
|
||||
gateways: {
|
||||
...ePayment.gateways,
|
||||
mellat: { ...ePayment.gateways.mellat, enabled: checked },
|
||||
@@ -168,6 +170,7 @@ export function SettingsPage() {
|
||||
async function handleZarinpalToggle(checked: boolean) {
|
||||
await patchEPayment('zarinpal-enabled', {
|
||||
...ePayment,
|
||||
enabled: checked ? true : ePayment.enabled,
|
||||
gateways: {
|
||||
...ePayment.gateways,
|
||||
zarinpal: { ...ePayment.gateways.zarinpal, enabled: checked },
|
||||
@@ -265,6 +268,12 @@ export function SettingsPage() {
|
||||
onChange={(checked) => void handleMasterToggle(checked)}
|
||||
/>
|
||||
</div>
|
||||
{!ePayment.enabled &&
|
||||
(ePayment.gateways.mellat.enabled || ePayment.gateways.zarinpal.enabled) ? (
|
||||
<p className={styles.masterOffHint} role="status">
|
||||
{t('website.ePayment.masterOffHint')}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user