From 28dfef555c96e30cb0d6dafcd3ae537e91bb0501 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 13 Apr 2026 21:44:32 +0200 Subject: [PATCH] feat: wire currency conversion into price display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useFormatters().price() now accepts an optional sourceCurrency param. When showConversions is enabled and the source differs from the user's currency, it converts via ECB rates and shows dual format: "€200.00 (~$218.00)". ItemCard and CollectionView pass priceCurrency through from API data. Setup detail items also pass priceCurrency. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/client/components/CollectionView.tsx | 1 + src/client/components/ItemCard.tsx | 4 ++- src/client/hooks/useFormatters.ts | 38 ++++++++++++++++++++++-- src/client/routes/setups/$setupId.tsx | 1 + 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/client/components/CollectionView.tsx b/src/client/components/CollectionView.tsx index 06da1ba..d764378 100644 --- a/src/client/components/CollectionView.tsx +++ b/src/client/components/CollectionView.tsx @@ -235,6 +235,7 @@ export function CollectionView() { cropZoom={item.cropZoom} cropX={item.cropX} cropY={item.cropY} + priceCurrency={item.priceCurrency} /> ))} diff --git a/src/client/components/ItemCard.tsx b/src/client/components/ItemCard.tsx index abbca67..009e908 100644 --- a/src/client/components/ItemCard.tsx +++ b/src/client/components/ItemCard.tsx @@ -26,6 +26,7 @@ interface ItemCardProps { classification?: string; onClassificationCycle?: () => void; linkTo?: string | null; + priceCurrency?: string | null; } export function ItemCard({ @@ -48,6 +49,7 @@ export function ItemCard({ classification, onClassificationCycle, linkTo, + priceCurrency, }: ItemCardProps) { const { weight, price } = useFormatters(); const navigate = useNavigate(); @@ -232,7 +234,7 @@ export function ItemCard({ )} {priceCents != null && ( - {price(priceCents)} + {price(priceCents, priceCurrency)} )} diff --git a/src/client/hooks/useFormatters.ts b/src/client/hooks/useFormatters.ts index 77767c9..001addb 100644 --- a/src/client/hooks/useFormatters.ts +++ b/src/client/hooks/useFormatters.ts @@ -1,15 +1,47 @@ -import { formatPrice, formatWeight } from "../lib/formatters"; +import { + type Currency, + formatDualPrice, + formatPrice, + formatWeight, +} from "../lib/formatters"; import { useCurrency } from "./useCurrency"; +import { convertClientPrice, useExchangeRates } from "./useExchangeRates"; import { useLanguage } from "./useLanguage"; import { useWeightUnit } from "./useWeightUnit"; export function useFormatters() { const unit = useWeightUnit(); - const { currency } = useCurrency(); + const { currency, showConversions } = useCurrency(); const locale = useLanguage(); + const { data: ratesData } = useExchangeRates(); + const rates = ratesData?.rates; + return { weight: (grams: number | null) => formatWeight(grams, unit, locale), - price: (cents: number | null) => formatPrice(cents, currency, locale), + /** + * Format a price. If sourceCurrency differs from user's currency and + * showConversions is enabled, returns dual format: "€200.00 (~$218.00)". + * Otherwise returns price in source currency (or user's currency if no source given). + */ + price: ( + cents: number | null, + sourceCurrency?: Currency | string | null, + ) => { + if (cents == null) return "--"; + const source = (sourceCurrency as Currency) || currency; + if (showConversions && rates && source !== currency) { + const converted = convertClientPrice(cents, source, currency, rates); + const dual = formatDualPrice({ + sourceCents: cents, + sourceCurrency: source as Currency, + targetCurrency: currency, + convertedCents: converted, + locale, + }); + return `${dual.source} (${dual.converted})`; + } + return formatPrice(cents, source as Currency, locale); + }, unit, currency, locale, diff --git a/src/client/routes/setups/$setupId.tsx b/src/client/routes/setups/$setupId.tsx index f6741b8..a7caa50 100644 --- a/src/client/routes/setups/$setupId.tsx +++ b/src/client/routes/setups/$setupId.tsx @@ -358,6 +358,7 @@ function SetupDetailPage() { name={item.name} weightGrams={item.weightGrams} priceCents={item.priceCents} + priceCurrency={item.priceCurrency} quantity={item.quantity} categoryName={categoryName} categoryIcon={categoryIcon}