Almost ready
This commit is contained in:
@@ -25,12 +25,12 @@ Maintain professional documentation standards without emoji usage.
|
|||||||
File: `frontend/package.json`
|
File: `frontend/package.json`
|
||||||
- Add `"{package}": "{version}"` to dependencies
|
- Add `"{package}": "{version}"` to dependencies
|
||||||
- No npm install needed - handled by container rebuild
|
- No npm install needed - handled by container rebuild
|
||||||
- Testing: `make rebuild` then verify container starts
|
- Testing: Instruct user to rebuild the containers and report back build errors
|
||||||
|
|
||||||
### 2. Container-Validated Development Workflow (Production-only)
|
### 2. Container-Validated Development Workflow (Production-only)
|
||||||
```bash
|
```bash
|
||||||
# After each change:
|
# After each change:
|
||||||
make rebuild # Rebuilds containers with new dependencies
|
Instruct user to rebuild the containers and report back build errors
|
||||||
make logs # Monitor for build/runtime errors
|
make logs # Monitor for build/runtime errors
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -130,8 +130,22 @@ export class StationsService {
|
|||||||
const enriched = await Promise.all(
|
const enriched = await Promise.all(
|
||||||
savedStations.map(async (saved: SavedStation) => {
|
savedStations.map(async (saved: SavedStation) => {
|
||||||
const station = await this.repository.getCachedStation(saved.stationId);
|
const station = await this.repository.getCachedStation(saved.stationId);
|
||||||
|
|
||||||
|
// Flatten station data into top level to match frontend SavedStation type
|
||||||
|
// Frontend expects SavedStation to extend Station
|
||||||
return {
|
return {
|
||||||
...saved,
|
...saved,
|
||||||
|
// Merge cached station data at top level (with fallbacks if cache miss)
|
||||||
|
name: station?.name || saved.nickname || 'Saved Station',
|
||||||
|
address: station?.address || '',
|
||||||
|
latitude: station?.latitude || 0,
|
||||||
|
longitude: station?.longitude || 0,
|
||||||
|
rating: station?.rating,
|
||||||
|
photoUrl: station?.photoUrl,
|
||||||
|
priceRegular: station?.priceRegular,
|
||||||
|
pricePremium: station?.pricePremium,
|
||||||
|
priceDiesel: station?.priceDiesel,
|
||||||
|
// Keep nested station for compatibility
|
||||||
station
|
station
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Project documentation hub for the 5-container single-tenant architecture with in
|
|||||||
- Testing (containers only): `docs/TESTING.md`
|
- Testing (containers only): `docs/TESTING.md`
|
||||||
- Database Migration: `docs/DATABASE-MIGRATION.md`
|
- Database Migration: `docs/DATABASE-MIGRATION.md`
|
||||||
- Admin feature: `docs/ADMIN.md` - Role management, APIs, catalog CRUD, station oversight
|
- Admin feature: `docs/ADMIN.md` - Role management, APIs, catalog CRUD, station oversight
|
||||||
- Development commands: `Makefile`, `docker-compose.yml`
|
- Development Environment: `docker-compose.yml`
|
||||||
- Application features (start at each README):
|
- Application features (start at each README):
|
||||||
- `backend/src/features/admin/README.md` - Admin role management and oversight
|
- `backend/src/features/admin/README.md` - Admin role management and oversight
|
||||||
- `backend/src/features/platform/README.md` - Vehicle data and VIN decoding
|
- `backend/src/features/platform/README.md` - Vehicle data and VIN decoding
|
||||||
|
|||||||
@@ -84,14 +84,14 @@ export const FuelLogsPage: React.FC = () => {
|
|||||||
<FuelLogForm />
|
<FuelLogForm />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<Typography variant="h6" gutterBottom>Recent Fuel Logs</Typography>
|
<Typography variant="h6" gutterBottom>Summary</Typography>
|
||||||
|
<FuelStatsCard logs={fuelLogs} />
|
||||||
|
<Typography variant="h6" sx={{ mt: 3 }} gutterBottom>Recent Fuel Logs</Typography>
|
||||||
<FuelLogsList
|
<FuelLogsList
|
||||||
logs={fuelLogs}
|
logs={fuelLogs}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
/>
|
/>
|
||||||
<Typography variant="h6" sx={{ mt: 3 }} gutterBottom>Summary</Typography>
|
|
||||||
<FuelStatsCard logs={fuelLogs} />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* @ai-summary Reusable checkbox group for maintenance subtype selection
|
* @ai-summary Multi-select dropdown for maintenance subtype selection
|
||||||
* @ai-context Responsive grid layout with proper mobile touch targets
|
* @ai-context Material-UI Autocomplete with proper mobile touch targets
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormGroup, FormControlLabel, Checkbox, Box } from '@mui/material';
|
import { Autocomplete, TextField, Chip } from '@mui/material';
|
||||||
import { MaintenanceCategory, getSubtypesForCategory } from '../types/maintenance.types';
|
import { MaintenanceCategory, getSubtypesForCategory } from '../types/maintenance.types';
|
||||||
|
|
||||||
interface SubtypeCheckboxGroupProps {
|
interface SubtypeCheckboxGroupProps {
|
||||||
@@ -20,52 +20,44 @@ export const SubtypeCheckboxGroup: React.FC<SubtypeCheckboxGroupProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const availableSubtypes = getSubtypesForCategory(category);
|
const availableSubtypes = getSubtypesForCategory(category);
|
||||||
|
|
||||||
const handleToggle = (subtype: string) => {
|
|
||||||
const newSelected = selected.includes(subtype)
|
|
||||||
? selected.filter((s) => s !== subtype)
|
|
||||||
: [...selected, subtype];
|
|
||||||
onChange(newSelected);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Autocomplete
|
||||||
sx={{
|
multiple
|
||||||
display: 'grid',
|
options={Array.from(availableSubtypes)}
|
||||||
gridTemplateColumns: {
|
value={selected}
|
||||||
xs: '1fr',
|
onChange={(_, newValue) => onChange(newValue)}
|
||||||
sm: 'repeat(2, 1fr)',
|
disableCloseOnSelect
|
||||||
md: 'repeat(3, 1fr)',
|
renderInput={(params) => (
|
||||||
},
|
<TextField
|
||||||
gap: 1,
|
{...params}
|
||||||
}}
|
label="Select Subtypes"
|
||||||
>
|
placeholder={selected.length === 0 ? 'Choose one or more...' : ''}
|
||||||
<FormGroup>
|
sx={{
|
||||||
{availableSubtypes.map((subtype) => (
|
'& .MuiOutlinedInput-root': {
|
||||||
<FormControlLabel
|
minHeight: 56,
|
||||||
key={subtype}
|
},
|
||||||
control={
|
}}
|
||||||
<Checkbox
|
/>
|
||||||
checked={selected.includes(subtype)}
|
)}
|
||||||
onChange={() => handleToggle(subtype)}
|
renderTags={(value, getTagProps) =>
|
||||||
sx={{
|
value.map((option, index) => (
|
||||||
minWidth: 44,
|
<Chip
|
||||||
minHeight: 44,
|
label={option}
|
||||||
'& .MuiSvgIcon-root': {
|
{...getTagProps({ index })}
|
||||||
fontSize: 24,
|
key={option}
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={subtype}
|
|
||||||
sx={{
|
sx={{
|
||||||
minHeight: 44,
|
height: 32,
|
||||||
'& .MuiFormControlLabel-label': {
|
fontSize: { xs: 13, sm: 14 },
|
||||||
fontSize: { xs: 14, sm: 16 },
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))
|
||||||
</FormGroup>
|
}
|
||||||
</Box>
|
sx={{
|
||||||
|
'& .MuiAutocomplete-option': {
|
||||||
|
minHeight: 44,
|
||||||
|
fontSize: { xs: 14, sm: 16 },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,14 +86,14 @@ export const MaintenancePage: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormSuspense>
|
<FormSuspense>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={3}>
|
||||||
{/* Left Column: Form */}
|
{/* Top: Form */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12}>
|
||||||
<MaintenanceRecordForm />
|
<MaintenanceRecordForm />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Right Column: Records List */}
|
{/* Bottom: Records List */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12}>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
Recent Maintenance Records
|
Recent Maintenance Records
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export const StationMap: React.FC<StationMapProps> = ({
|
|||||||
const infoWindows = useRef<google.maps.InfoWindow[]>([]);
|
const infoWindows = useRef<google.maps.InfoWindow[]>([]);
|
||||||
const currentLocationMarker = useRef<google.maps.marker.AdvancedMarkerElement | null>(null);
|
const currentLocationMarker = useRef<google.maps.marker.AdvancedMarkerElement | null>(null);
|
||||||
const isInitializing = useRef<boolean>(false);
|
const isInitializing = useRef<boolean>(false);
|
||||||
|
const mapIdRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -368,4 +369,3 @@ export const StationMap: React.FC<StationMapProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default StationMap;
|
export default StationMap;
|
||||||
const mapIdRef = useRef<string | null>(null);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user