Fix price formatting for PostgreSQL DECIMAL type

PostgreSQL returns DECIMAL as strings, not numbers.
Updated all price formatting functions to handle both.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
clucraft 2026-01-20 14:22:40 -05:00
parent 5263ac93a9
commit a2b0c2cc65
3 changed files with 29 additions and 15 deletions

View file

@ -41,13 +41,15 @@ export default function PriceChart({
const chartData = prices.map((p) => ({ const chartData = prices.map((p) => ({
date: new Date(p.recorded_at).getTime(), date: new Date(p.recorded_at).getTime(),
price: parseFloat(p.price.toString()), price: typeof p.price === 'string' ? parseFloat(p.price) : p.price,
})); }));
const minPrice = Math.min(...chartData.map((d) => d.price)); const priceValues = chartData.map((d) => d.price).filter((p) => !isNaN(p));
const maxPrice = Math.max(...chartData.map((d) => d.price)); const minPrice = priceValues.length > 0 ? Math.min(...priceValues) : 0;
const avgPrice = const maxPrice = priceValues.length > 0 ? Math.max(...priceValues) : 0;
chartData.reduce((sum, d) => sum + d.price, 0) / chartData.length; const avgPrice = priceValues.length > 0
? priceValues.reduce((sum, p) => sum + p, 0) / priceValues.length
: 0;
const formatDate = (timestamp: number) => { const formatDate = (timestamp: number) => {
const date = new Date(timestamp); const date = new Date(timestamp);
@ -55,6 +57,7 @@ export default function PriceChart({
}; };
const formatPrice = (value: number) => { const formatPrice = (value: number) => {
if (value === null || value === undefined || isNaN(value)) return 'N/A';
return `${currencySymbol}${value.toFixed(2)}`; return `${currencySymbol}${value.toFixed(2)}`;
}; };

View file

@ -7,11 +7,13 @@ interface ProductCardProps {
} }
export default function ProductCard({ product, onDelete }: ProductCardProps) { export default function ProductCard({ product, onDelete }: ProductCardProps) {
const formatPrice = (price: number | null, currency: string | null) => { const formatPrice = (price: number | string | null, currency: string | null) => {
if (price === null) return 'N/A'; if (price === null || price === undefined) return 'N/A';
const numPrice = typeof price === 'string' ? parseFloat(price) : price;
if (isNaN(numPrice)) return 'N/A';
const currencySymbol = const currencySymbol =
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$'; currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$';
return `${currencySymbol}${price.toFixed(2)}`; return `${currencySymbol}${numPrice.toFixed(2)}`;
}; };
const formatDate = (dateStr: string | null) => { const formatDate = (dateStr: string | null) => {

View file

@ -71,11 +71,13 @@ export default function ProductDetail() {
fetchData(days); fetchData(days);
}; };
const formatPrice = (price: number | null, currency: string | null) => { const formatPrice = (price: number | string | null, currency: string | null) => {
if (price === null) return 'N/A'; if (price === null || price === undefined) return 'N/A';
const numPrice = typeof price === 'string' ? parseFloat(price) : price;
if (isNaN(numPrice)) return 'N/A';
const currencySymbol = const currencySymbol =
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$'; currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$';
return `${currencySymbol}${price.toFixed(2)}`; return `${currencySymbol}${numPrice.toFixed(2)}`;
}; };
if (isLoading) { if (isLoading) {
@ -105,10 +107,17 @@ export default function ProductDetail() {
); );
} }
const priceChange = const priceChange = (() => {
product.stats && prices.length > 1 if (!product.stats || prices.length < 1) return null;
? ((product.current_price || 0) - prices[0].price) / prices[0].price const currentPrice = typeof product.current_price === 'string'
: null; ? parseFloat(product.current_price)
: (product.current_price || 0);
const firstPrice = typeof prices[0].price === 'string'
? parseFloat(prices[0].price)
: prices[0].price;
if (firstPrice === 0) return null;
return (currentPrice - firstPrice) / firstPrice;
})();
return ( return (
<Layout> <Layout>