Implemented automatic unit conversions for feeding and medicine tracking: - Created UnitInput component for automatic ml↔oz conversions - Updated Feeding page to use UnitInput for bottle amounts - Updated Medicine page to use UnitInput for liquid medicine dosages - All values stored in metric (ml) in database - Display values automatically converted based on user's measurement preference - Supports voice input with proper unit handling Component features: - Automatic conversion between metric and imperial - User preference-based display - Consistent metric storage - Type safety with TypeScript 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import { TextField, InputAdornment, TextFieldProps } from '@mui/material';
|
|
import { useAuth } from '@/lib/auth/AuthContext';
|
|
import {
|
|
convertVolume,
|
|
convertVolumeToMl,
|
|
convertWeight,
|
|
convertWeightToKg,
|
|
convertHeight,
|
|
convertHeightToCm,
|
|
getUnitSymbol
|
|
} from '@/lib/utils/unitConversion';
|
|
import { MeasurementSystem } from '@/hooks/useLocale';
|
|
|
|
type UnitType = 'volume' | 'weight' | 'height';
|
|
|
|
interface UnitInputProps extends Omit<TextFieldProps, 'onChange' | 'value'> {
|
|
/**
|
|
* The type of measurement (volume, weight, or height)
|
|
*/
|
|
type: UnitType;
|
|
|
|
/**
|
|
* The value in metric units (ml, kg, or cm)
|
|
*/
|
|
value: number | string;
|
|
|
|
/**
|
|
* Callback when value changes, always returns metric value
|
|
*/
|
|
onChange: (metricValue: number) => void;
|
|
}
|
|
|
|
/**
|
|
* UnitInput - A text field that automatically handles unit conversions
|
|
*
|
|
* Storage: Always stores values in metric (ml, kg, cm) in the database
|
|
* Display: Shows values in user's preferred measurement system
|
|
* Input: Accepts values in user's preferred units, converts to metric
|
|
*/
|
|
export function UnitInput({ type, value, onChange, ...textFieldProps }: UnitInputProps) {
|
|
const { user } = useAuth();
|
|
const measurementSystem: MeasurementSystem =
|
|
(user?.preferences?.measurementUnit as MeasurementSystem) || 'metric';
|
|
|
|
// Get the display unit symbol
|
|
const unitSymbol = getUnitSymbol(type, measurementSystem);
|
|
|
|
// Convert metric value to display value
|
|
const getDisplayValue = (metricValue: number | string): number => {
|
|
const numericValue = typeof metricValue === 'string' ? parseFloat(metricValue) : metricValue;
|
|
if (isNaN(numericValue)) return 0;
|
|
|
|
switch (type) {
|
|
case 'volume':
|
|
return convertVolume(numericValue, measurementSystem).value;
|
|
case 'weight':
|
|
return convertWeight(numericValue, measurementSystem).value;
|
|
case 'height':
|
|
return convertHeight(numericValue, measurementSystem).value;
|
|
default:
|
|
return numericValue;
|
|
}
|
|
};
|
|
|
|
// Convert display value back to metric
|
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
const displayValue = parseFloat(event.target.value);
|
|
if (isNaN(displayValue)) {
|
|
onChange(0);
|
|
return;
|
|
}
|
|
|
|
let metricValue: number;
|
|
switch (type) {
|
|
case 'volume':
|
|
metricValue = convertVolumeToMl(displayValue, measurementSystem);
|
|
break;
|
|
case 'weight':
|
|
metricValue = convertWeightToKg(displayValue, measurementSystem);
|
|
break;
|
|
case 'height':
|
|
metricValue = convertHeightToCm(displayValue, measurementSystem);
|
|
break;
|
|
default:
|
|
metricValue = displayValue;
|
|
}
|
|
|
|
onChange(metricValue);
|
|
};
|
|
|
|
const displayValue = getDisplayValue(value);
|
|
const roundedDisplayValue = Math.round(displayValue * 100) / 100; // Round to 2 decimals
|
|
|
|
return (
|
|
<TextField
|
|
{...textFieldProps}
|
|
type="number"
|
|
value={roundedDisplayValue || ''}
|
|
onChange={handleChange}
|
|
InputProps={{
|
|
endAdornment: <InputAdornment position="end">{unitSymbol}</InputAdornment>,
|
|
...textFieldProps.InputProps,
|
|
}}
|
|
inputProps={{
|
|
step: type === 'volume' ? '0.1' : '0.01',
|
|
min: 0,
|
|
...textFieldProps.inputProps,
|
|
}}
|
|
/>
|
|
);
|
|
}
|