@@ -129,10 +154,27 @@ export function CoasterStatsEditor({
updateStat(index, 'stat_value', parseFloat(e.target.value) || 0)}
+ value={getDisplayValue(stat)}
+ onChange={(e) => {
+ const inputValue = e.target.value;
+ // If unit is recognized, convert to metric for storage
+ if (stat.unit) {
+ const numValue = parseFloat(inputValue);
+ if (!isNaN(numValue)) {
+ const metricValue = convertValueToMetric(numValue, stat.unit);
+ updateStat(index, 'stat_value', metricValue);
+ } else {
+ updateStat(index, 'stat_value', 0);
+ }
+ } else {
+ updateStat(index, 'stat_value', parseFloat(inputValue) || 0);
+ }
+ }}
placeholder="0"
/>
+
+ Using {preferences.measurement_system} units
+
diff --git a/src/components/admin/editors/TechnicalSpecsEditor.tsx b/src/components/admin/editors/TechnicalSpecsEditor.tsx
index c9569b82..c754fcd8 100644
--- a/src/components/admin/editors/TechnicalSpecsEditor.tsx
+++ b/src/components/admin/editors/TechnicalSpecsEditor.tsx
@@ -4,6 +4,13 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Card } from "@/components/ui/card";
+import { useUnitPreferences } from "@/hooks/useUnitPreferences";
+import {
+ convertValueToMetric,
+ convertValueFromMetric,
+ detectUnitType,
+ getMetricUnit
+} from "@/lib/units";
interface TechnicalSpec {
spec_name: string;
@@ -30,6 +37,7 @@ export function TechnicalSpecsEditor({
categories = DEFAULT_CATEGORIES,
commonSpecs = []
}: TechnicalSpecsEditorProps) {
+ const { preferences } = useUnitPreferences();
const addSpec = () => {
onChange([
@@ -57,6 +65,23 @@ export function TechnicalSpecsEditor({
onChange(newSpecs);
};
+ // Get display value (convert from metric if needed)
+ const getDisplayValue = (spec: TechnicalSpec): string => {
+ if (!spec.spec_value || !spec.unit || spec.spec_type !== 'number') return spec.spec_value;
+
+ const numValue = parseFloat(spec.spec_value);
+ if (isNaN(numValue)) return spec.spec_value;
+
+ const unitType = detectUnitType(spec.unit);
+ if (unitType === 'unknown') return spec.spec_value;
+
+ // Assume stored value is in metric, convert to user's preferred units
+ const metricUnit = getMetricUnit(spec.unit);
+ const displayValue = convertValueFromMetric(numValue, spec.unit, metricUnit);
+
+ return String(displayValue);
+ };
+
const moveSpec = (index: number, direction: 'up' | 'down') => {
if ((direction === 'up' && index === 0) || (direction === 'down' && index === specs.length - 1)) {
return;
@@ -107,11 +132,30 @@ export function TechnicalSpecsEditor({
updateSpec(index, 'spec_value', e.target.value)}
+ value={getDisplayValue(spec)}
+ onChange={(e) => {
+ const inputValue = e.target.value;
+ // If type is number and unit is recognized, convert to metric for storage
+ if (spec.spec_type === 'number' && spec.unit) {
+ const numValue = parseFloat(inputValue);
+ if (!isNaN(numValue)) {
+ const metricValue = convertValueToMetric(numValue, spec.unit);
+ updateSpec(index, 'spec_value', String(metricValue));
+ } else {
+ updateSpec(index, 'spec_value', inputValue);
+ }
+ } else {
+ updateSpec(index, 'spec_value', inputValue);
+ }
+ }}
placeholder="Value"
type={spec.spec_type === 'number' ? 'number' : 'text'}
/>
+ {spec.spec_type === 'number' && spec.unit && (
+
+ Using {preferences.measurement_system} units
+
+ )}
diff --git a/src/lib/units.ts b/src/lib/units.ts
index 58dd38de..dfa52122 100644
--- a/src/lib/units.ts
+++ b/src/lib/units.ts
@@ -75,4 +75,133 @@ export const IMPERIAL_COUNTRIES = ['US', 'LR', 'MM'];
// Detect measurement system from country code
export function getMeasurementSystemFromCountry(countryCode: string): MeasurementSystem {
return IMPERIAL_COUNTRIES.includes(countryCode.toUpperCase()) ? 'imperial' : 'metric';
+}
+
+// Unit type detection
+export type UnitType = 'speed' | 'distance' | 'height' | 'weight' | 'unknown';
+
+export function detectUnitType(unit: string): UnitType {
+ const normalized = unit.toLowerCase().trim();
+
+ // Speed units
+ if (['km/h', 'kmh', 'kph', 'mph', 'm/s', 'ms'].includes(normalized)) {
+ return 'speed';
+ }
+
+ // Distance units (meters/feet)
+ if (['m', 'meter', 'meters', 'metre', 'metres', 'ft', 'feet', 'foot'].includes(normalized)) {
+ return 'distance';
+ }
+
+ // Height units (cm/inches)
+ if (['cm', 'centimeter', 'centimeters', 'in', 'inch', 'inches'].includes(normalized)) {
+ return 'height';
+ }
+
+ // Weight units
+ if (['kg', 'kilogram', 'kilograms', 'lb', 'lbs', 'pound', 'pounds'].includes(normalized)) {
+ return 'weight';
+ }
+
+ return 'unknown';
+}
+
+// Convert any value to metric based on its unit
+export function convertValueToMetric(value: number, unit: string): number {
+ const normalized = unit.toLowerCase().trim();
+
+ // Speed conversions to km/h
+ if (normalized === 'mph') {
+ return Math.round(value / 0.621371);
+ }
+ if (['m/s', 'ms'].includes(normalized)) {
+ return Math.round(value * 3.6);
+ }
+ if (['km/h', 'kmh', 'kph'].includes(normalized)) {
+ return Math.round(value);
+ }
+
+ // Distance conversions to meters
+ if (['ft', 'feet', 'foot'].includes(normalized)) {
+ return Math.round(value / 3.28084);
+ }
+ if (['m', 'meter', 'meters', 'metre', 'metres'].includes(normalized)) {
+ return Math.round(value);
+ }
+
+ // Height conversions to cm
+ if (['in', 'inch', 'inches'].includes(normalized)) {
+ return Math.round(value / 0.393701);
+ }
+ if (['cm', 'centimeter', 'centimeters'].includes(normalized)) {
+ return Math.round(value);
+ }
+
+ // Weight conversions to kg
+ if (['lb', 'lbs', 'pound', 'pounds'].includes(normalized)) {
+ return Math.round(value / 2.20462);
+ }
+ if (['kg', 'kilogram', 'kilograms'].includes(normalized)) {
+ return Math.round(value);
+ }
+
+ // Unknown unit, return as-is
+ return value;
+}
+
+// Convert metric value to target unit
+export function convertValueFromMetric(value: number, targetUnit: string, metricUnit: string): number {
+ const normalized = targetUnit.toLowerCase().trim();
+ const metricNormalized = metricUnit.toLowerCase().trim();
+
+ // Speed conversions from km/h
+ if (metricNormalized === 'km/h' || metricNormalized === 'kmh' || metricNormalized === 'kph') {
+ if (normalized === 'mph') {
+ return Math.round(value * 0.621371);
+ }
+ if (normalized === 'm/s' || normalized === 'ms') {
+ return Math.round(value / 3.6);
+ }
+ }
+
+ // Distance conversions from meters
+ if (metricNormalized === 'm' || metricNormalized === 'meter' || metricNormalized === 'meters') {
+ if (['ft', 'feet', 'foot'].includes(normalized)) {
+ return Math.round(value * 3.28084);
+ }
+ }
+
+ // Height conversions from cm
+ if (metricNormalized === 'cm' || metricNormalized === 'centimeter' || metricNormalized === 'centimeters') {
+ if (['in', 'inch', 'inches'].includes(normalized)) {
+ return Math.round(value * 0.393701);
+ }
+ }
+
+ // Weight conversions from kg
+ if (metricNormalized === 'kg' || metricNormalized === 'kilogram' || metricNormalized === 'kilograms') {
+ if (['lb', 'lbs', 'pound', 'pounds'].includes(normalized)) {
+ return Math.round(value * 2.20462);
+ }
+ }
+
+ return value;
+}
+
+// Get metric unit for a given unit type
+export function getMetricUnit(unit: string): string {
+ const unitType = detectUnitType(unit);
+
+ switch (unitType) {
+ case 'speed':
+ return 'km/h';
+ case 'distance':
+ return 'm';
+ case 'height':
+ return 'cm';
+ case 'weight':
+ return 'kg';
+ default:
+ return unit;
+ }
}
\ No newline at end of file