Improve business profile branding uploads and super-admin link menus.

Show separate light/dark logo and favicon croppers in a compact grid, support favicon saves, and keep super-admin overflow dashboard links in English.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-26 00:51:12 +03:30
co-authored by Cursor
parent 5e799eddf9
commit 468bb87fcf
8 changed files with 163 additions and 79 deletions
+45 -24
View File
@@ -14,7 +14,6 @@ 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'
@@ -83,7 +82,8 @@ export function BusinessProfilePage() {
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 [favicon, setFavicon] = useState<string | null>(null)
const [faviconMediaId, setFaviconMediaId] = useState<string | null>(null)
const [addresses, setAddresses] = useState<AddressDraft[]>([createEmptyAddressItem()])
const [phoneNumbers, setPhoneNumbers] = useState<BusinessPhoneNumber[]>([
createEmptyPhone(),
@@ -159,6 +159,10 @@ export function BusinessProfilePage() {
setLogoMediaId(profileData.profile.logoMediaId)
setLogoDark(profileData.profile.logoDarkUrl)
setLogoDarkMediaId(profileData.profile.logoDarkMediaId)
setFavicon(
profileData.profile.faviconMediaId ? profileData.profile.faviconUrl : null,
)
setFaviconMediaId(profileData.profile.faviconMediaId)
const nextAddresses =
profileData.addresses.length > 0
@@ -284,6 +288,11 @@ export function BusinessProfilePage() {
'business-logo-dark.png',
logoDarkMediaId,
)
const nextFaviconMediaId = await resolveDataUrlToMediaId(
favicon,
'business-favicon.png',
faviconMediaId,
)
const payloadAddresses = addresses
.filter(
@@ -321,6 +330,7 @@ export function BusinessProfilePage() {
socialMedia,
logoMediaId: nextLogoMediaId,
logoDarkMediaId: nextLogoDarkMediaId,
faviconMediaId: nextFaviconMediaId,
categoryIds,
addresses: payloadAddresses,
})
@@ -368,29 +378,12 @@ export function BusinessProfilePage() {
<section className={styles.section}>
<h3 className={styles.sectionTitle}>{t('bizProfile.section.basic')}</h3>
<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>
<div className={`${formStyles.field} ${styles.logoLightCol}`}>
<label className={styles.mediaFieldLabel}>{t('bizProfile.logo.lightTheme')}</label>
<ImageCropper
key={logoModeDark ? 'logo-dark' : 'logo-light'}
value={logoModeDark ? logoDark : logo}
onChange={logoModeDark ? setLogoDark : setLogo}
compact
value={logo}
onChange={setLogo}
outputFormat="png"
accept="image/png"
uploadLabel={t('bizProfile.logo.upload')}
@@ -399,6 +392,34 @@ export function BusinessProfilePage() {
/>
</div>
<div className={`${formStyles.field} ${styles.logoDarkCol}`}>
<label className={styles.mediaFieldLabel}>{t('bizProfile.logo.darkTheme')}</label>
<ImageCropper
compact
value={logoDark}
onChange={setLogoDark}
outputFormat="png"
accept="image/png"
uploadLabel={t('bizProfile.logo.upload')}
hint={t('bizProfile.logo.hint')}
changeLabel={t('bizProfile.logo.change')}
/>
</div>
<div className={`${formStyles.field} ${styles.faviconCol}`}>
<label className={styles.mediaFieldLabel}>{t('bizProfile.favicon')}</label>
<ImageCropper
compact
value={favicon}
onChange={setFavicon}
outputFormat="png"
accept="image/png"
uploadLabel={t('bizProfile.favicon.upload')}
hint={t('bizProfile.favicon.hint')}
changeLabel={t('bizProfile.favicon.change')}
/>
</div>
<div className={styles.basicAside}>
<div className={`${formStyles.field} ${styles.fullRow}`}>
<label htmlFor="activity-categories">{t('bizProfile.categories')}</label>