fix: currency suggestion uses region detection, seed adds market prices
All checks were successful
CI / ci (push) Successful in 1m24s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 15s

- Currency auto-suggestion now uses locale region subtag (en-US → US → USD,
  en-DE → DE → EUR) instead of language prefix. Fixes wrong suggestion for
  users with English browser locale in European countries.
- Added dismiss button (X) to suggestion banner
- Dev seed script now clears existing dev data before re-seeding (safe to
  run repeatedly without manual DB cleanup)
- Added DEV_MARKET_PRICES with multi-market UVP data for 10 global items
  (EU/US/UK prices) and community prices for 5 owned items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 21:27:57 +02:00
parent 51c8703a3d
commit 23027551b4
3 changed files with 356 additions and 30 deletions

View File

@@ -14,6 +14,7 @@ import { useUpdateSetting } from "../hooks/useSettings";
import { useWeightUnit } from "../hooks/useWeightUnit";
import type { Currency, WeightUnit } from "../lib/formatters";
import i18n from "../lib/i18n";
import { LucideIcon } from "../lib/iconData";
const LANGUAGES = [
{ value: "en", label: "English" },
@@ -215,27 +216,52 @@ function ImportExportSection() {
);
}
const LOCALE_CURRENCY_MAP: Record<string, Currency> = {
// Map region codes (from navigator.language) to currencies.
// Region is more reliable than language for currency detection:
// en-US → US → USD, en-GB → GB → GBP, en-DE → DE → EUR
const REGION_CURRENCY_MAP: Record<string, Currency> = {
US: "USD",
GB: "GBP",
AU: "AUD",
CA: "CAD",
JP: "JPY",
// EU countries
DE: "EUR",
FR: "EUR",
ES: "EUR",
IT: "EUR",
NL: "EUR",
PT: "EUR",
AT: "EUR",
BE: "EUR",
IE: "EUR",
FI: "EUR",
GR: "EUR",
};
// Fallback: language prefix → currency (when no region subtag)
const LANG_CURRENCY_MAP: Record<string, Currency> = {
de: "EUR",
fr: "EUR",
es: "EUR",
it: "EUR",
nl: "EUR",
pt: "EUR",
en: "USD",
ja: "JPY",
};
function getSuggestedCurrency(): Currency | null {
try {
const lang = navigator.language;
// Check full locale first (e.g., en-GB → GBP)
if (lang.startsWith("en-GB")) return "GBP";
if (lang.startsWith("en-AU")) return "AUD";
if (lang.startsWith("en-CA") || lang.startsWith("fr-CA")) return "CAD";
const locale = navigator.language; // e.g., "en-US", "de-DE", "en"
const parts = locale.split("-");
// Try region subtag first (more accurate for currency)
if (parts.length > 1) {
const region = parts[parts.length - 1].toUpperCase();
const fromRegion = REGION_CURRENCY_MAP[region];
if (fromRegion) return fromRegion;
}
// Fall back to language prefix
const prefix = lang.split("-")[0];
return LOCALE_CURRENCY_MAP[prefix] ?? null;
const lang = parts[0];
return LANG_CURRENCY_MAP[lang] ?? null;
} catch {
return null;
}
@@ -267,9 +293,9 @@ function SettingsPage() {
</div>
{showSuggestion && (
<div className="bg-blue-50 border border-blue-100 rounded-xl px-4 py-3 mb-4 flex items-center justify-between">
<span className="text-sm text-blue-700">
Based on your location, we suggest{" "}
<div className="bg-blue-50 border border-blue-100 rounded-xl px-4 py-3 mb-4 flex items-center gap-3">
<span className="text-sm text-blue-700 flex-1">
Based on your region, we suggest{" "}
{CURRENCIES.find((c) => c.value === suggestedCurrency)?.label ??
suggestedCurrency}{" "}
({suggestedCurrency})
@@ -283,11 +309,17 @@ function SettingsPage() {
});
setSuggestionDismissed(true);
}}
className="text-sm font-medium text-blue-700 hover:text-blue-800 underline ml-3"
className="text-sm font-medium text-blue-700 hover:text-blue-800 underline shrink-0"
>
Use{" "}
{CURRENCIES.find((c) => c.value === suggestedCurrency)?.label ??
suggestedCurrency}
Switch
</button>
<button
type="button"
onClick={() => setSuggestionDismissed(true)}
className="p-1 text-blue-400 hover:text-blue-600 rounded shrink-0"
aria-label="Dismiss"
>
<LucideIcon name="x" size={16} />
</button>
</div>
)}