mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Improve portfolio title images, branding, and super-admin domains UI.
Add aspect-ratio crop presets for portfolio title images, fixed card image placeholders with contained centering, Meshkee favicon/logo fallbacks, login branding updates, category FAB alignment, and super-admin website/domain SSL tooling. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
f566387c61
commit
db52a83f98
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 9.3 KiB |
Regular → Executable
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 12 KiB |
@@ -5,6 +5,7 @@ import { useAuth } from '../context/AuthContext'
|
||||
import { getActiveBusinessDomain } from '../lib/businessContext'
|
||||
import { isAbortError } from '../lib/api'
|
||||
import { getWebsiteBusinessInfo } from '../services/websiteService'
|
||||
import meshkeeLogo from '../assets/meshkee-logo.png'
|
||||
import styles from './Sidebar.module.css'
|
||||
|
||||
const navItems = [
|
||||
@@ -25,7 +26,6 @@ export function Sidebar() {
|
||||
const businessDomain = getActiveBusinessDomain()
|
||||
const fallbackBusinessName = user?.customerBusinesses[0]?.name ?? 'Store'
|
||||
const displayName = brandName || fallbackBusinessName
|
||||
const initial = displayName.trim().charAt(0) || businessDomain.charAt(0) || 'S'
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
@@ -35,7 +35,7 @@ export function Sidebar() {
|
||||
const info = await getWebsiteBusinessInfo(businessDomain, controller.signal)
|
||||
if (controller.signal.aborted) return
|
||||
setBrandName(info.nameFa?.trim() || info.name.trim() || fallbackBusinessName)
|
||||
setLogoUrl(info.logoUrl)
|
||||
setLogoUrl(info.logoUrl?.trim() || null)
|
||||
} catch (err) {
|
||||
if (isAbortError(err)) return
|
||||
setBrandName(fallbackBusinessName)
|
||||
@@ -53,17 +53,11 @@ export function Sidebar() {
|
||||
return (
|
||||
<aside className={styles.sidebar}>
|
||||
<div className={styles.brand}>
|
||||
{logoUrl ? (
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={displayName}
|
||||
className={styles.brandLogo}
|
||||
/>
|
||||
) : (
|
||||
<span className={styles.brandFallback} aria-hidden>
|
||||
{initial.toUpperCase()}
|
||||
</span>
|
||||
)}
|
||||
<img
|
||||
src={logoUrl || meshkeeLogo}
|
||||
alt={displayName}
|
||||
className={styles.brandLogo}
|
||||
/>
|
||||
<div className={styles.brandText}>
|
||||
<span className={styles.brandDomain}>{businessDomain}</span>
|
||||
<span className={styles.brandName}>{displayName}</span>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isAbortError } from '../../lib/api'
|
||||
import { getTenantDomain } from '../../lib/config'
|
||||
import { CheckoutProvider } from '../../context/CheckoutContext'
|
||||
import { getWebsiteBusinessInfo, getWebsiteUrl } from '../../services/websiteService'
|
||||
import meshkeeLogo from '../../assets/meshkee-logo.png'
|
||||
import styles from './CheckoutLayout.module.css'
|
||||
|
||||
export function CheckoutLayout() {
|
||||
@@ -20,7 +21,7 @@ export function CheckoutLayout() {
|
||||
const info = await getWebsiteBusinessInfo(tenantDomain, controller.signal)
|
||||
if (controller.signal.aborted) return
|
||||
setBrandName(info.nameFa?.trim() || info.name.trim() || tenantDomain)
|
||||
setLogoUrl(info.logoUrl)
|
||||
setLogoUrl(info.logoUrl?.trim() || null)
|
||||
} catch (err) {
|
||||
if (isAbortError(err)) return
|
||||
setBrandName(tenantDomain)
|
||||
@@ -33,24 +34,17 @@ export function CheckoutLayout() {
|
||||
}, [tenantDomain])
|
||||
|
||||
const displayName = brandName || tenantDomain
|
||||
const initial = displayName.trim().charAt(0) || 'S'
|
||||
|
||||
return (
|
||||
<CheckoutProvider>
|
||||
<div className={styles.checkoutPage} lang="fa" dir="rtl">
|
||||
<header className={styles.header}>
|
||||
<a href={websiteUrl} className={styles.brandLink}>
|
||||
{logoUrl ? (
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={displayName}
|
||||
className={styles.logo}
|
||||
/>
|
||||
) : (
|
||||
<span className={styles.logoFallback} aria-hidden>
|
||||
{initial.toUpperCase()}
|
||||
</span>
|
||||
)}
|
||||
<img
|
||||
src={logoUrl || meshkeeLogo}
|
||||
alt={displayName}
|
||||
className={styles.logo}
|
||||
/>
|
||||
<div className={styles.brandText}>
|
||||
<span className={styles.brandTitle}>{displayName}</span>
|
||||
<span className={styles.subtitle}>سبد خرید</span>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { resolveTenantByDomain } from '../services/tenantService'
|
||||
|
||||
interface TenantBrandingContextValue {
|
||||
businessName: string
|
||||
logoUrl: string | null
|
||||
faviconUrl: string | null
|
||||
}
|
||||
|
||||
@@ -31,6 +32,7 @@ function pickBusinessName(
|
||||
|
||||
export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
||||
const [businessName, setBusinessName] = useState('')
|
||||
const [logoUrl, setLogoUrl] = useState<string | null>(null)
|
||||
const [faviconUrl, setFaviconUrl] = useState<string | null>(null)
|
||||
const domain = getTenantDomain()
|
||||
|
||||
@@ -54,19 +56,22 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
||||
domain,
|
||||
)
|
||||
|
||||
const nextLogo =
|
||||
info?.logoUrl?.trim() || tenant.logoUrl?.trim() || null
|
||||
const nextFavicon =
|
||||
info?.faviconUrl?.trim() ||
|
||||
tenant.faviconUrl?.trim() ||
|
||||
info?.logoUrl?.trim() ||
|
||||
tenant.logoUrl?.trim() ||
|
||||
nextLogo ||
|
||||
null
|
||||
|
||||
setBusinessName(name || domain)
|
||||
setLogoUrl(nextLogo)
|
||||
setFaviconUrl(nextFavicon)
|
||||
applyDocumentFavicon(nextFavicon)
|
||||
} catch (err) {
|
||||
if (isAbortError(err) || controller.signal.aborted) return
|
||||
setBusinessName(domain)
|
||||
setLogoUrl(null)
|
||||
setFaviconUrl(null)
|
||||
applyDocumentFavicon(null)
|
||||
}
|
||||
@@ -80,8 +85,8 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
||||
}, [domain])
|
||||
|
||||
const value = useMemo(
|
||||
() => ({ businessName, faviconUrl }),
|
||||
[businessName, faviconUrl],
|
||||
() => ({ businessName, logoUrl, faviconUrl }),
|
||||
[businessName, logoUrl, faviconUrl],
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
CUSTOMER_ACCESS_MESSAGE,
|
||||
VERIFICATION_REQUIRED_MESSAGE,
|
||||
} from '../context/AuthContext'
|
||||
import { useTenantBranding } from '../context/TenantBrandingContext'
|
||||
import { ApiError } from '../lib/api'
|
||||
import { toE164CellNumber } from '../lib/cellNumber'
|
||||
import { getTenantDomain } from '../lib/config'
|
||||
@@ -33,6 +34,7 @@ export function LoginPage() {
|
||||
const [searchParams] = useSearchParams()
|
||||
const redirectTo = safeRedirectPath(searchParams.get('redirect'))
|
||||
const { login } = useAuth()
|
||||
const { businessName, logoUrl } = useTenantBranding()
|
||||
const tenantDomain = getTenantDomain()
|
||||
|
||||
const [view, setView] = useState<AuthView>('login')
|
||||
@@ -236,15 +238,13 @@ export function LoginPage() {
|
||||
<div className={styles.page}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles.brand}>
|
||||
<img src={meshkeeLogo} alt="Meshkee" className={styles.logo} />
|
||||
<img src={logoUrl || meshkeeLogo} alt="" className={styles.logo} />
|
||||
<div className={styles.brandText}>
|
||||
<span className={styles.domain}>{tenantDomain}</span>
|
||||
<span className={styles.appName}>Customer Dashboard</span>
|
||||
<span className={styles.domain}>{businessName || tenantDomain}</span>
|
||||
<span className={styles.appName}>powered by Meshkee.app</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className={styles.domainHint}>Store domain: {tenantDomain}</p>
|
||||
|
||||
{view === 'login' && (
|
||||
<>
|
||||
<h1 className={styles.title}>Welcome back</h1>
|
||||
|
||||
Reference in New Issue
Block a user