fix: Refine Store Details UX and currency display
## Changes ### 1. Split Store Identity and Brand Cards ✅ **Before:** Single tall "Store Identity" card **After:** Two focused cards **Store Identity Card:** - Store name - Store tagline - Contact email - Customer support email - Store phone **Brand Card:** - Store logo - Store icon - Brand colors (Primary, Accent, Error) - Reset to default button **Result:** Better organization, easier to scan --- ### 2. Fix Currency Symbol Fallback ✅ **Issue:** When currency has no symbol (like AUD), showed € instead **Screenshot:** Preview showed "€1.234.568" for Australian dollar **Fix:** ```typescript // Get currency symbol from currencies data, fallback to currency code const currencyInfo = currencies.find((c: any) => c.code === settings.currency); let symbol = settings.currency; // Default to currency code if (currencyInfo?.symbol && !currencyInfo.symbol.includes('&#')) { // Use symbol only if it exists and doesn't contain HTML entities symbol = currencyInfo.symbol; } ``` **Result:** - AUD → Shows "AUD1234" instead of "€1234" - IDR → Shows "Rp1234" (has symbol) - USD → Shows "$1234" (has symbol) - Currencies without symbols → Show currency code --- ### 3. Move Overview Card to First Position ✅ **Before:** Overview card at the bottom **After:** Overview card at the top **Rationale:** - Quick glance at store location, currency, timezone - Sets context for the rest of the settings - Industry standard (Shopify shows overview first) **Card Content:** ``` 📍 Store Location: Australia Currency: Australian dollar • Timezone: Australia/Sydney ``` --- ## Final Card Order 1. **Store Overview** (new position) 2. **Store Identity** (name, tagline, contacts) 3. **Brand** (logo, icon, colors) 4. **Store Address** 5. **Currency & Formatting** 6. **Standards & Formats** **Result:** Logical flow, better UX, professional layout
This commit is contained in:
@@ -200,7 +200,14 @@ export default function StoreDetailsPage() {
|
|||||||
.replace('.', settings.decimalSep)
|
.replace('.', settings.decimalSep)
|
||||||
.replace(/\B(?=(\d{3})+(?!\d))/g, settings.thousandSep);
|
.replace(/\B(?=(\d{3})+(?!\d))/g, settings.thousandSep);
|
||||||
|
|
||||||
const symbol = settings.currency === 'IDR' ? 'Rp' : settings.currency === 'USD' ? '$' : '€';
|
// Get currency symbol from currencies data, fallback to currency code
|
||||||
|
const currencyInfo = currencies.find((c: any) => c.code === settings.currency);
|
||||||
|
let symbol = settings.currency; // Default to currency code
|
||||||
|
|
||||||
|
if (currencyInfo?.symbol && !currencyInfo.symbol.includes('&#')) {
|
||||||
|
// Use symbol only if it exists and doesn't contain HTML entities
|
||||||
|
symbol = currencyInfo.symbol;
|
||||||
|
}
|
||||||
|
|
||||||
switch (settings.currencyPosition) {
|
switch (settings.currencyPosition) {
|
||||||
case 'left':
|
case 'left':
|
||||||
@@ -223,6 +230,26 @@ export default function StoreDetailsPage() {
|
|||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
|
{/* Store Overview */}
|
||||||
|
<div className="bg-primary/10 border border-primary/20 rounded-lg p-4">
|
||||||
|
{(() => {
|
||||||
|
const currencyFlag = flagsData.find((f: any) => f.code === settings.currency);
|
||||||
|
const currencyInfo = currencies.find((c: any) => c.code === settings.currency);
|
||||||
|
const countryName = currencyFlag?.country || settings.country;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
📍 Store Location: {countryName}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Currency: {currencyInfo?.name || settings.currency} • Timezone: {settings.timezone}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Store Identity */}
|
{/* Store Identity */}
|
||||||
<SettingsCard
|
<SettingsCard
|
||||||
title="Store Identity"
|
title="Store Identity"
|
||||||
@@ -237,6 +264,19 @@ export default function StoreDetailsPage() {
|
|||||||
/>
|
/>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
|
<SettingsSection
|
||||||
|
label="Store tagline"
|
||||||
|
description="A short tagline or slogan for your store"
|
||||||
|
htmlFor="storeTagline"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
id="storeTagline"
|
||||||
|
value={settings.storeTagline}
|
||||||
|
onChange={(e) => updateSetting('storeTagline', e.target.value)}
|
||||||
|
placeholder="Quality products, delivered fast"
|
||||||
|
/>
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
<SettingsSection
|
<SettingsSection
|
||||||
label="Contact email"
|
label="Contact email"
|
||||||
description="Customers will use this email to contact you"
|
description="Customers will use this email to contact you"
|
||||||
@@ -278,20 +318,13 @@ export default function StoreDetailsPage() {
|
|||||||
placeholder="+62 812 3456 7890"
|
placeholder="+62 812 3456 7890"
|
||||||
/>
|
/>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
</SettingsCard>
|
||||||
|
|
||||||
<SettingsSection
|
{/* Brand */}
|
||||||
label="Store tagline"
|
<SettingsCard
|
||||||
description="A short tagline or slogan for your store"
|
title="Brand"
|
||||||
htmlFor="storeTagline"
|
description="Logo, icon, and colors for your store"
|
||||||
>
|
>
|
||||||
<Input
|
|
||||||
id="storeTagline"
|
|
||||||
value={settings.storeTagline}
|
|
||||||
onChange={(e) => updateSetting('storeTagline', e.target.value)}
|
|
||||||
placeholder="Quality products, delivered fast"
|
|
||||||
/>
|
|
||||||
</SettingsSection>
|
|
||||||
|
|
||||||
<SettingsSection label="Store logo" description="Recommended: 200x60px PNG with transparent background">
|
<SettingsSection label="Store logo" description="Recommended: 200x60px PNG with transparent background">
|
||||||
<ImageUpload
|
<ImageUpload
|
||||||
value={settings.storeLogo}
|
value={settings.storeLogo}
|
||||||
@@ -309,53 +342,50 @@ export default function StoreDetailsPage() {
|
|||||||
maxSize={1}
|
maxSize={1}
|
||||||
/>
|
/>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
</SettingsCard>
|
|
||||||
|
|
||||||
{/* Brand Colors */}
|
<div className="pt-4 border-t">
|
||||||
<SettingsCard
|
<h4 className="text-sm font-medium mb-4">Brand Colors</h4>
|
||||||
title="Brand Colors"
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
description="Customize your admin interface colors"
|
<ColorPicker
|
||||||
>
|
label="Primary Color"
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
description="Main brand color"
|
||||||
<ColorPicker
|
value={settings.primaryColor}
|
||||||
label="Primary Color"
|
onChange={(color) => updateSetting('primaryColor', color)}
|
||||||
description="Main brand color"
|
/>
|
||||||
value={settings.primaryColor}
|
|
||||||
onChange={(color) => updateSetting('primaryColor', color)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
label="Accent Color"
|
label="Accent Color"
|
||||||
description="Success and highlights"
|
description="Success and highlights"
|
||||||
value={settings.accentColor}
|
value={settings.accentColor}
|
||||||
onChange={(color) => updateSetting('accentColor', color)}
|
onChange={(color) => updateSetting('accentColor', color)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
label="Error Color"
|
label="Error Color"
|
||||||
description="Errors and warnings"
|
description="Errors and warnings"
|
||||||
value={settings.errorColor}
|
value={settings.errorColor}
|
||||||
onChange={(color) => updateSetting('errorColor', color)}
|
onChange={(color) => updateSetting('errorColor', color)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 mt-4 pt-4 border-t">
|
<div className="flex items-center gap-2 mt-4">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateSetting('primaryColor', '#3b82f6');
|
updateSetting('primaryColor', '#3b82f6');
|
||||||
updateSetting('accentColor', '#10b981');
|
updateSetting('accentColor', '#10b981');
|
||||||
updateSetting('errorColor', '#ef4444');
|
updateSetting('errorColor', '#ef4444');
|
||||||
toast.success('Colors reset to default');
|
toast.success('Colors reset to default');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Reset to Default
|
Reset to Default
|
||||||
</Button>
|
</Button>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Changes apply after saving
|
Changes apply after saving
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
||||||
@@ -562,26 +592,6 @@ export default function StoreDetailsPage() {
|
|||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
</div>
|
</div>
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
||||||
{/* Summary Card - Professional, No Flag */}
|
|
||||||
<div className="bg-primary/10 border border-primary/20 rounded-lg p-4">
|
|
||||||
{(() => {
|
|
||||||
const currencyFlag = flagsData.find((f: any) => f.code === settings.currency);
|
|
||||||
const currencyInfo = currencies.find((c: any) => c.code === settings.currency);
|
|
||||||
const countryName = currencyFlag?.country || settings.country;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p className="text-sm font-medium">
|
|
||||||
📍 Store Location: {countryName}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
|
||||||
Currency: {currencyInfo?.name || settings.currency} • Timezone: {settings.timezone}
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
</SettingsLayout>
|
</SettingsLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user