UX Improvements
This commit is contained in:
@@ -2,7 +2,10 @@ import React, { useEffect, useMemo, useState, useRef, memo } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Grid, Card, CardHeader, CardContent, TextField, Switch, FormControlLabel, Box, Button, CircularProgress } from '@mui/material';
|
||||
import { Grid, Card, CardHeader, CardContent, TextField, Box, Button, CircularProgress, ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
|
||||
import { VehicleSelector } from './VehicleSelector';
|
||||
import { DistanceInput } from './DistanceInput';
|
||||
import { FuelTypeSelector } from './FuelTypeSelector';
|
||||
@@ -94,6 +97,19 @@ const FuelLogFormComponent: React.FC<{ onSuccess?: () => void; initial?: Partial
|
||||
return units > 0 && cost > 0 ? units * cost : 0;
|
||||
}, [fuelUnits, costPerUnit]);
|
||||
|
||||
// Watch for distance and fuel units to calculate efficiency
|
||||
const watchedDistance = watch(useOdometer ? 'odometerReading' : 'tripDistance');
|
||||
const distanceValue = typeof watchedDistance === 'string' ? parseFloat(watchedDistance) : watchedDistance;
|
||||
|
||||
const calculatedEfficiency = useMemo(() => {
|
||||
const distance = distanceValue && !isNaN(distanceValue) ? distanceValue : 0;
|
||||
const units = fuelUnits && !isNaN(fuelUnits) ? fuelUnits : 0;
|
||||
if (distance > 0 && units > 0) {
|
||||
return distance / units;
|
||||
}
|
||||
return 0;
|
||||
}, [distanceValue, fuelUnits]);
|
||||
|
||||
const onSubmit = async (data: CreateFuelLogRequest) => {
|
||||
const payload: CreateFuelLogRequest = {
|
||||
...data,
|
||||
@@ -123,29 +139,107 @@ const FuelLogFormComponent: React.FC<{ onSuccess?: () => void; initial?: Partial
|
||||
}, [useOdometer, setValue]);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title="Add Fuel Log" subheader={<UnitSystemDisplay unitSystem={userSettings?.unitSystem} showLabel="Displaying in" />} />
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={2}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Card>
|
||||
<CardHeader title="Add Fuel Log" subheader={<UnitSystemDisplay unitSystem={userSettings?.unitSystem} showLabel="Displaying in" />} />
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={2}>
|
||||
{/* Row 1: Select Vehicle */}
|
||||
<Grid item xs={12}>
|
||||
<Controller name="vehicleId" control={control} render={({ field }) => (
|
||||
<VehicleSelector value={field.value} onChange={field.onChange} error={errors.vehicleId?.message} required />
|
||||
)} />
|
||||
</Grid>
|
||||
|
||||
{/* Row 2: Date/Time | MPG/km/L */}
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller name="dateTime" control={control} render={({ field }) => (
|
||||
<TextField {...field} label="Date & Time" type="datetime-local" fullWidth error={!!errors.dateTime} helperText={errors.dateTime?.message} InputLabelProps={{ shrink: true }} />
|
||||
<DateTimePicker
|
||||
label="Date & Time"
|
||||
value={field.value ? new Date(field.value) : null}
|
||||
onChange={(newValue) => field.onChange(newValue?.toISOString() || '')}
|
||||
format="MM/dd/yyyy hh:mm a"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
error: !!errors.dateTime,
|
||||
helperText: errors.dateTime?.message,
|
||||
sx: {
|
||||
'& .MuiOutlinedInput-root': {
|
||||
minHeight: '56px',
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)} />
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<FormControlLabel control={<Switch checked={useOdometer} onChange={(e) => setUseOdometer(e.target.checked)} />} label={`Use ${useOdometer ? 'Odometer' : 'Trip Distance'}`} />
|
||||
<TextField
|
||||
label={`${userSettings?.unitSystem === 'metric' ? 'km/L' : 'MPG'}`}
|
||||
value={calculatedEfficiency > 0 ? calculatedEfficiency.toFixed(3) : ''}
|
||||
fullWidth
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
sx: {
|
||||
backgroundColor: 'grey.50',
|
||||
'& .MuiOutlinedInput-input': {
|
||||
cursor: 'default',
|
||||
},
|
||||
},
|
||||
}}
|
||||
helperText="Calculated from distance ÷ fuel amount"
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
minHeight: '56px',
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Row 3: Odometer | Distance Input Method */}
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller name={useOdometer ? 'odometerReading' : 'tripDistance'} control={control} render={({ field }) => (
|
||||
<DistanceInput type={useOdometer ? 'odometer' : 'trip'} value={field.value as any} onChange={field.onChange as any} unitSystem={userSettings?.unitSystem} error={useOdometer ? (errors.odometerReading?.message as any) : (errors.tripDistance?.message as any)} />
|
||||
)} />
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<ToggleButtonGroup
|
||||
value={useOdometer ? 'odometer' : 'trip'}
|
||||
exclusive
|
||||
onChange={(_, newValue) => {
|
||||
if (newValue !== null) {
|
||||
setUseOdometer(newValue === 'odometer');
|
||||
}
|
||||
}}
|
||||
aria-label="distance input method"
|
||||
fullWidth
|
||||
sx={{
|
||||
height: '56px', // Match input field height
|
||||
'& .MuiToggleButton-root': {
|
||||
textTransform: 'none',
|
||||
fontWeight: 500,
|
||||
borderRadius: '8px',
|
||||
height: '56px', // Ensure button height matches
|
||||
'&.Mui-selected': {
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
'&:hover': {
|
||||
backgroundColor: 'primary.dark',
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ToggleButton value="trip" aria-label="trip distance">
|
||||
Trip Distance
|
||||
</ToggleButton>
|
||||
<ToggleButton value="odometer" aria-label="odometer reading">
|
||||
Odometer Reading
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Controller name="fuelType" control={control} render={({ field: fuelTypeField }) => (
|
||||
<Controller name="fuelGrade" control={control} render={({ field: fuelGradeField }) => (
|
||||
@@ -205,6 +299,7 @@ const FuelLogFormComponent: React.FC<{ onSuccess?: () => void; initial?: Partial
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user