diff --git a/website/src/utils/bomUtils.js b/website/src/utils/bomUtils.js index 927d2af..256df24 100644 --- a/website/src/utils/bomUtils.js +++ b/website/src/utils/bomUtils.js @@ -108,18 +108,45 @@ export const getPriceDisplayFromLinks = (item, targetCurrency = null) => { const isSingleCurrency = currencies.length === 1; // Extract numeric values for min/max calculation - const numericPrices = priceObjects.map(p => { + // For overall min: use the minimum values from each price (min from ranges, single prices, etc.) + // For overall max: use the maximum values from each price (max from ranges, single prices, etc.) + const minValues = priceObjects.map(p => { if (typeof p === 'object' && 'amount' in p) { const amount = p.amount; - return typeof amount === 'object' && 'min' in amount ? amount.min : (typeof amount === 'number' ? amount : 0); + // For ranges, use the min value; for single prices, use the amount + if (typeof amount === 'object' && 'min' in amount) { + return amount.min; + } + if (typeof amount === 'number') { + return amount; + } } - return extractNumericPrice(p); + const extracted = extractNumericPrice(p); + return extracted != null ? extracted : 0; }).filter(p => p != null && p > 0); - if (numericPrices.length === 0) return 'C$0.00'; + const maxValues = priceObjects.map(p => { + if (typeof p === 'object' && 'amount' in p) { + const amount = p.amount; + // For ranges, use the max value; for single prices, use the amount + if (typeof amount === 'object' && 'max' in amount) { + return amount.max; + } + if (typeof amount === 'object' && 'min' in amount) { + return amount.min; // If no max, use min (single value range) + } + if (typeof amount === 'number') { + return amount; + } + } + const extracted = extractNumericPrice(p); + return extracted != null ? extracted : 0; + }).filter(p => p != null && p > 0); - const minPrice = Math.min(...numericPrices); - const maxPrice = Math.max(...numericPrices); + if (minValues.length === 0 || maxValues.length === 0) return 'C$0.00'; + + const minPrice = Math.min(...minValues); + const maxPrice = Math.max(...maxValues); if (minPrice === maxPrice) { // Single price - format with currency from the first link @@ -132,17 +159,36 @@ export const getPriceDisplayFromLinks = (item, targetCurrency = null) => { const currencySymbol = currency === 'CAD' ? 'C$' : currency === 'USD' ? '$' : currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : currency; return `${currencySymbol}${minPrice.toFixed(2)} - ${currencySymbol}${maxPrice.toFixed(2)}`; } else { - // Multiple currencies - format each with its currency or target currency + // Multiple currencies - find the price objects that contain the overall min and max const minPriceObj = priceObjects.find(p => { - const amount = p?.amount; - const val = typeof amount === 'object' && 'min' in amount ? amount.min : (typeof amount === 'number' ? amount : 0); - return val === minPrice; - }); + if (typeof p === 'object' && 'amount' in p) { + const amount = p.amount; + if (typeof amount === 'object' && 'min' in amount) { + return amount.min === minPrice; + } + if (typeof amount === 'number') { + return amount === minPrice; + } + } + return extractNumericPrice(p) === minPrice; + }) || priceObjects[0]; // Fallback to first if not found + const maxPriceObj = priceObjects.find(p => { - const amount = p?.amount; - const val = typeof amount === 'object' && 'max' in amount ? amount.max : (typeof amount === 'object' && 'min' in amount ? amount.min : (typeof amount === 'number' ? amount : 0)); - return val === maxPrice; - }); + if (typeof p === 'object' && 'amount' in p) { + const amount = p.amount; + if (typeof amount === 'object' && 'max' in amount) { + return amount.max === maxPrice; + } + if (typeof amount === 'object' && 'min' in amount) { + return amount.min === maxPrice; // Single value range + } + if (typeof amount === 'number') { + return amount === maxPrice; + } + } + return extractNumericPrice(p) === maxPrice; + }) || priceObjects[priceObjects.length - 1]; // Fallback to last if not found + return `${formatPrice(minPriceObj, targetCurrency || 'CAD')} - ${formatPrice(maxPriceObj, targetCurrency || 'CAD')}`; } } @@ -173,18 +219,45 @@ export const getPriceDisplayFromLinksAsync = async (item, targetCurrency = 'CAD' if (convertedPrices.length === 0) return 'C$0.00'; // Extract numeric values for min/max calculation - const numericPrices = convertedPrices.map(p => { + // For overall min: use the minimum values from each price (min from ranges, single prices, etc.) + // For overall max: use the maximum values from each price (max from ranges, single prices, etc.) + const minValues = convertedPrices.map(p => { if (typeof p === 'object' && 'amount' in p) { const amount = p.amount; - return typeof amount === 'object' && 'min' in amount ? amount.min : (typeof amount === 'number' ? amount : 0); + // For ranges, use the min value; for single prices, use the amount + if (typeof amount === 'object' && 'min' in amount) { + return amount.min; + } + if (typeof amount === 'number') { + return amount; + } } - return extractNumericPrice(p); + const extracted = extractNumericPrice(p); + return extracted != null ? extracted : 0; }).filter(p => p != null && p > 0); - if (numericPrices.length === 0) return 'C$0.00'; + const maxValues = convertedPrices.map(p => { + if (typeof p === 'object' && 'amount' in p) { + const amount = p.amount; + // For ranges, use the max value; for single prices, use the amount + if (typeof amount === 'object' && 'max' in amount) { + return amount.max; + } + if (typeof amount === 'object' && 'min' in amount) { + return amount.min; // If no max, use min (single value range) + } + if (typeof amount === 'number') { + return amount; + } + } + const extracted = extractNumericPrice(p); + return extracted != null ? extracted : 0; + }).filter(p => p != null && p > 0); - const minPrice = Math.min(...numericPrices); - const maxPrice = Math.max(...numericPrices); + if (minValues.length === 0 || maxValues.length === 0) return 'C$0.00'; + + const minPrice = Math.min(...minValues); + const maxPrice = Math.max(...maxValues); if (minPrice === maxPrice) { return await formatPriceWithConversion(convertedPrices[0], targetCurrency, exchangeRates); diff --git a/website/src/utils/exportUtils.js b/website/src/utils/exportUtils.js index 2d84627..8312e19 100644 --- a/website/src/utils/exportUtils.js +++ b/website/src/utils/exportUtils.js @@ -12,11 +12,40 @@ export const generateMarkdownOverview = (config, printedParts, hardwareParts, fi const getPriceDisplay = (item) => { if (!item) return 'N/A'; if (item.links && item.links.length > 0) { - const prices = item.links.map(link => extractNumericPrice(link.price)).filter(p => p != null && p > 0); - if (prices.length === 0) return 'N/A'; - if (prices.length === 1) return formatPrice(prices[0]); - const minPrice = Math.min(...prices); - const maxPrice = Math.max(...prices); + // Extract min and max values separately to handle price ranges correctly + const minPrices = item.links.map(link => { + if (link.price && typeof link.price === 'object' && 'amount' in link.price) { + const amount = link.price.amount; + if (typeof amount === 'object' && 'min' in amount) { + return amount.min; + } + if (typeof amount === 'number') { + return amount; + } + } + return extractNumericPrice(link.price); + }).filter(p => p != null && p > 0); + + const maxPrices = item.links.map(link => { + if (link.price && typeof link.price === 'object' && 'amount' in link.price) { + const amount = link.price.amount; + if (typeof amount === 'object' && 'max' in amount) { + return amount.max; + } + if (typeof amount === 'object' && 'min' in amount) { + return amount.min; // Single value range + } + if (typeof amount === 'number') { + return amount; + } + } + return extractNumericPrice(link.price); + }).filter(p => p != null && p > 0); + + if (minPrices.length === 0 || maxPrices.length === 0) return 'N/A'; + + const minPrice = Math.min(...minPrices); + const maxPrice = Math.max(...maxPrices); return minPrice === maxPrice ? formatPrice(minPrice) : `${formatPrice(minPrice)} - ${formatPrice(maxPrice)}`; } return item.price ? formatPrice(item.price) : 'N/A';