Files
flyer-crawler.projectium.com/utils/unitConverter.ts

95 lines
3.3 KiB
TypeScript

import type { UnitPrice } from '../types';
const METRIC_UNITS = ['g', 'kg', 'ml', 'l'];
const IMPERIAL_UNITS = ['oz', 'lb', 'fl oz'];
const CONVERSIONS: Record<string, { to: string; factor: number }> = {
// metric to imperial
g: { to: 'oz', factor: 0.035274 },
kg: { to: 'lb', factor: 2.20462 },
ml: { to: 'fl oz', factor: 0.033814 },
l: { to: 'fl oz', factor: 33.814 },
// imperial to metric
oz: { to: 'g', factor: 28.3495 },
lb: { to: 'kg', factor: 0.453592 },
'fl oz': { to: 'ml', factor: 29.5735 },
};
interface FormattedPrice {
price: string;
unit: string | null;
}
/**
* Converts a unit price to the target system and formats it for display.
* @param unitPrice The structured unit price object from the database.
* @param system The target system ('metric' or 'imperial').
* @returns An object with formatted price and unit strings.
*/
export const formatUnitPrice = (unitPrice: UnitPrice | null | undefined, system: 'metric' | 'imperial'): FormattedPrice => {
if (!unitPrice || typeof unitPrice.value !== 'number' || !unitPrice.unit) {
return { price: '—', unit: null };
}
const { value, unit } = unitPrice;
const isMetric = METRIC_UNITS.includes(unit);
const isImperial = IMPERIAL_UNITS.includes(unit);
let displayValue = value;
let displayUnit = unit;
if (system === 'imperial' && isMetric) {
const conversion = CONVERSIONS[unit];
if (conversion) {
displayValue = value * conversion.factor;
displayUnit = conversion.to;
}
} else if (system === 'metric' && isImperial) {
const conversion = CONVERSIONS[unit];
if (conversion) {
displayValue = value * conversion.factor;
displayUnit = conversion.to;
}
}
// Smart formatting for price
const formattedPrice = displayValue < 0.10
? displayValue.toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 3, maximumFractionDigits: 3 })
: displayValue.toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 });
// Always show a unit if one exists for clarity
if (displayUnit === 'each') {
return { price: formattedPrice, unit: '/each' };
}
return { price: formattedPrice, unit: `/${displayUnit}` };
};
/**
* Converts an imperial unit price to its metric equivalent for database storage.
* @param unitPrice The structured unit price object, potentially in imperial units.
* @returns A unit price object with metric units, or the original if already metric or not applicable.
*/
export const convertToMetric = (unitPrice: UnitPrice | null | undefined): UnitPrice | null | undefined => {
if (!unitPrice || typeof unitPrice.value !== 'number' || !unitPrice.unit) {
return unitPrice;
}
const { value, unit } = unitPrice;
const isImperial = IMPERIAL_UNITS.includes(unit);
if (isImperial) {
const conversion = CONVERSIONS[unit];
if (conversion) {
return {
value: value * conversion.factor,
unit: conversion.to,
};
}
}
// Return original if it's already metric or not a weight/volume unit (like 'each')
return unitPrice;
};