fix: Submenu active state + currency symbols + flags integration

1. Fixed Submenu Active State 
   Problem: First submenu always active due to pathname.startsWith()
   - /dashboard matches /dashboard/analytics
   - Both items show as active

   Solution: Use exact match instead
   - const isActive = pathname === it.path
   - Only clicked item shows as active

   Files: DashboardSubmenuBar.tsx, SubmenuBar.tsx

2. Fixed Currency Symbol Display 
   Problem: HTML entities showing (ءإ)
   Solution: Use currency code when symbol has HTML entities

   Before: United Arab Emirates dirham (ءإ)
   After: United Arab Emirates dirham (AED)

   Logic:
   const displaySymbol = (!currency.symbol || currency.symbol.includes('&#'))
     ? currency.code
     : currency.symbol;

3. Integrated Flags.json 

   A. Moved flags.json to admin-spa/src/data/
   B. Added flag support to SearchableSelect component
      - New icon prop in Option interface
      - Displays flag before label in trigger
      - Displays flag before label in dropdown

   C. Currency select now shows flags
      - Flag icon next to each currency
      - Visual country identification
      - Better UX for currency selection

   D. Dynamic store summary with flag
      Before: 🇮🇩 Your store is located in Indonesia
      After: [FLAG] Your store is located in Indonesia

      - Flag based on selected currency
      - Country name from flags.json
      - Currency name (not just code)
      - Dynamic updates when currency changes

Benefits:
 Clear submenu navigation
 Readable currency symbols
 Visual country flags
 Better currency selection UX
 Dynamic store location display

Files Modified:
- DashboardSubmenuBar.tsx: Exact match for active state
- SubmenuBar.tsx: Exact match for active state
- Store.tsx: Currency symbol fix + flags integration
- searchable-select.tsx: Icon support
- flags.json: Moved to admin-spa/src/data/
This commit is contained in:
dwindown
2025-11-06 11:35:32 +07:00
parent cd644d339c
commit 2a679ffd15
9 changed files with 2276 additions and 36 deletions

View File

@@ -336,16 +336,6 @@ export default function PaymentsPage() {
</div>
{/* Footer outside scrollable area */}
<div className="border-t px-4 py-3 flex flex-col gap-2 shrink-0 bg-background">
<Button
onClick={() => {
const form = document.querySelector('form');
if (form) form.requestSubmit();
}}
disabled={saveMutation.isPending}
className="w-full"
>
{saveMutation.isPending ? 'Saving...' : 'Save Settings'}
</Button>
<Button
type="button"
variant="ghost"
@@ -362,6 +352,16 @@ export default function PaymentsPage() {
<ExternalLink className="h-4 w-4" />
</a>
</Button>
<Button
onClick={() => {
const form = document.querySelector('form');
if (form) form.requestSubmit();
}}
disabled={saveMutation.isPending}
className="w-full"
>
{saveMutation.isPending ? 'Saving...' : 'Save Settings'}
</Button>
<Button
type="button"
variant="outline"

View File

@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { SearchableSelect } from '@/components/ui/searchable-select';
import { toast } from 'sonner';
import flagsData from '@/data/flags.json';
interface StoreSettings {
storeName: string;
@@ -295,11 +296,24 @@ export default function StoreDetailsPage() {
<SearchableSelect
value={settings.currency}
onChange={(v) => updateSetting('currency', v)}
options={currencies.map((currency: { code: string; name: string; symbol: string }) => ({
value: currency.code,
label: `${currency.name} (${currency.symbol})`,
searchText: `${currency.code} ${currency.name} ${currency.symbol}`,
}))}
options={currencies.map((currency: { code: string; name: string; symbol: string }) => {
// Use currency code if symbol contains HTML entities (&#x...) or is empty
const displaySymbol = (!currency.symbol || currency.symbol.includes('&#'))
? currency.code
: currency.symbol;
// Find matching flag data
const flagInfo = flagsData.find((f: any) => f.code === currency.code);
return {
value: currency.code,
label: flagInfo
? `${currency.name} (${displaySymbol})`
: `${currency.name} (${displaySymbol})`,
searchText: `${currency.code} ${currency.name} ${displaySymbol}`,
icon: flagInfo?.flag, // Add flag as icon
};
})}
placeholder="Select currency..."
/>
</SettingsSection>
@@ -419,14 +433,33 @@ export default function StoreDetailsPage() {
</div>
</SettingsCard>
{/* Summary Card */}
{/* Summary Card - Dynamic with Flag */}
<div className="bg-primary/10 border border-primary/20 rounded-lg p-4">
<p className="text-sm font-medium">
🇮🇩 Your store is located in {settings.country === 'ID' ? 'Indonesia' : settings.country}
</p>
<p className="text-sm text-muted-foreground mt-1">
Prices will be displayed in {settings.currency} Timezone: {settings.timezone}
</p>
{(() => {
// Find flag for current currency
const currencyFlag = flagsData.find((f: any) => f.code === settings.currency);
const currencyInfo = currencies.find((c: any) => c.code === settings.currency);
return (
<>
<div className="flex items-center gap-2">
{currencyFlag?.flag && (
<img
src={currencyFlag.flag}
alt={currencyFlag.country}
className="w-6 h-4 object-cover rounded-sm"
/>
)}
<p className="text-sm font-medium">
Your store is located in {currencyFlag?.country || settings.country}
</p>
</div>
<p className="text-sm text-muted-foreground mt-1">
Prices will be displayed in {currencyInfo?.name || settings.currency} Timezone: {settings.timezone}
</p>
</>
);
})()}
</div>
</SettingsLayout>
);

View File

@@ -38,7 +38,7 @@ export function SettingsLayout({
{/* Sticky Header with Save Button */}
{onSave && (
<div className="sticky top-0 z-10 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container max-w-5xl mx-auto px-4 py-3 flex items-center justify-between">
<div className="container px-0 max-w-5xl mx-auto py-3 flex items-center justify-between">
<div>
<h1 className="text-lg font-semibold">{title}</h1>
</div>
@@ -61,7 +61,7 @@ export function SettingsLayout({
)}
{/* Content */}
<div className="container max-w-5xl mx-auto px-4 py-8">
<div className="container px-0 max-w-5xl mx-auto">
{!onSave && (
<div className="mb-8">
<div className="flex items-start justify-between gap-4">