Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 3x 3x 3x 3x 120x 120x 120x 120x 120x 22x 98x 9x 120x 89x 31x 120x 120x 3x 121x 8x 113x 113x 113x 113x 113x 121x 2x 111x 3x 9x 2x 7x | // src/utils/unitConverter.ts
import type { UnitPrice } from '../types';
const CONVERSIONS = {
metric: {
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: {
oz: { to: 'g', factor: 28.3495 },
lb: { to: 'kg', factor: 0.453592 },
'fl oz': { to: 'ml', factor: 29.5735 },
},
};
const METRIC_UNITS = Object.keys(CONVERSIONS.metric);
const IMPERIAL_UNITS = Object.keys(CONVERSIONS.imperial);
interface FormattedPrice {
price: string;
unit: string | null;
}
/**
* Internal helper to convert a unit price to a target system if necessary.
* @param unitPrice The unit price to potentially convert.
* @param targetSystem The desired unit system.
* @returns A new UnitPrice object if converted, otherwise the original object.
*/
const convertUnitPrice = (unitPrice: UnitPrice, targetSystem: 'metric' | 'imperial'): UnitPrice => {
const { value, unit } = unitPrice;
const isMetric = METRIC_UNITS.includes(unit);
const isImperial = IMPERIAL_UNITS.includes(unit);
let needsConversion = false;
if (targetSystem === 'imperial' && isMetric) {
needsConversion = true;
} else if (targetSystem === 'metric' && isImperial) {
needsConversion = true;
}
if (!needsConversion) {
return unitPrice; // Return original object if no conversion is needed
}
// The source system is determined by which list the unit belongs to.
// We access the specific map based on the detected system to satisfy TypeScript.
const conversion = isMetric
? CONVERSIONS.metric[unit as keyof typeof CONVERSIONS.metric]
: CONVERSIONS.imperial[unit as keyof typeof CONVERSIONS.imperial];
// When converting price per unit, the factor logic is inverted.
// e.g., to get price per lb from price per kg, you divide by the kg-to-lb factor.
// Price/kg * (1 kg / 2.20462 lb) = (Price / 2.20462) per lb.
const convertedValue = value / conversion.factor;
return {
value: convertedValue,
unit: conversion.to,
};
};
/**
* 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 };
}
// First, convert the unit price to the correct system.
const convertedUnitPrice = convertUnitPrice(unitPrice, system);
const displayValue = convertedUnitPrice.value;
const displayUnit = convertedUnitPrice.unit;
// Convert the final calculated value (which is in cents) to dollars for formatting.
const valueInDollars = displayValue / 100;
// Smart formatting for price
const formattedPrice =
valueInDollars < 0.1
? valueInDollars.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 3,
maximumFractionDigits: 3,
})
: valueInDollars.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.
* This is used to standardize units before saving them.
* @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;
}
// The logic is now simply a call to the centralized converter.
return convertUnitPrice(unitPrice, 'metric');
};
|