Almost ready
This commit is contained in:
@@ -25,12 +25,12 @@ Maintain professional documentation standards without emoji usage.
|
||||
File: `frontend/package.json`
|
||||
- Add `"{package}": "{version}"` to dependencies
|
||||
- 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)
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
|
||||
@@ -125,18 +125,32 @@ export class StationsService {
|
||||
|
||||
async getUserSavedStations(userId: string) {
|
||||
const savedStations = await this.repository.getUserSavedStations(userId);
|
||||
|
||||
|
||||
// Enrich with cached station data
|
||||
const enriched = await Promise.all(
|
||||
savedStations.map(async (saved: SavedStation) => {
|
||||
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 {
|
||||
...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
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
return enriched;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Project documentation hub for the 5-container single-tenant architecture with in
|
||||
- Testing (containers only): `docs/TESTING.md`
|
||||
- Database Migration: `docs/DATABASE-MIGRATION.md`
|
||||
- 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):
|
||||
- `backend/src/features/admin/README.md` - Admin role management and oversight
|
||||
- `backend/src/features/platform/README.md` - Vehicle data and VIN decoding
|
||||
|
||||
@@ -84,14 +84,14 @@ export const FuelLogsPage: React.FC = () => {
|
||||
<FuelLogForm />
|
||||
</Grid>
|
||||
<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
|
||||
logs={fuelLogs}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
<Typography variant="h6" sx={{ mt: 3 }} gutterBottom>Summary</Typography>
|
||||
<FuelStatsCard logs={fuelLogs} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @ai-summary Reusable checkbox group for maintenance subtype selection
|
||||
* @ai-context Responsive grid layout with proper mobile touch targets
|
||||
* @ai-summary Multi-select dropdown for maintenance subtype selection
|
||||
* @ai-context Material-UI Autocomplete with proper mobile touch targets
|
||||
*/
|
||||
|
||||
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';
|
||||
|
||||
interface SubtypeCheckboxGroupProps {
|
||||
@@ -20,52 +20,44 @@ export const SubtypeCheckboxGroup: React.FC<SubtypeCheckboxGroupProps> = ({
|
||||
}) => {
|
||||
const availableSubtypes = getSubtypesForCategory(category);
|
||||
|
||||
const handleToggle = (subtype: string) => {
|
||||
const newSelected = selected.includes(subtype)
|
||||
? selected.filter((s) => s !== subtype)
|
||||
: [...selected, subtype];
|
||||
onChange(newSelected);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: {
|
||||
xs: '1fr',
|
||||
sm: 'repeat(2, 1fr)',
|
||||
md: 'repeat(3, 1fr)',
|
||||
},
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<FormGroup>
|
||||
{availableSubtypes.map((subtype) => (
|
||||
<FormControlLabel
|
||||
key={subtype}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={selected.includes(subtype)}
|
||||
onChange={() => handleToggle(subtype)}
|
||||
sx={{
|
||||
minWidth: 44,
|
||||
minHeight: 44,
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 24,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={subtype}
|
||||
<Autocomplete
|
||||
multiple
|
||||
options={Array.from(availableSubtypes)}
|
||||
value={selected}
|
||||
onChange={(_, newValue) => onChange(newValue)}
|
||||
disableCloseOnSelect
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Select Subtypes"
|
||||
placeholder={selected.length === 0 ? 'Choose one or more...' : ''}
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
minHeight: 56,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
renderTags={(value, getTagProps) =>
|
||||
value.map((option, index) => (
|
||||
<Chip
|
||||
label={option}
|
||||
{...getTagProps({ index })}
|
||||
key={option}
|
||||
sx={{
|
||||
minHeight: 44,
|
||||
'& .MuiFormControlLabel-label': {
|
||||
fontSize: { xs: 14, sm: 16 },
|
||||
},
|
||||
height: 32,
|
||||
fontSize: { xs: 13, sm: 14 },
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
</Box>
|
||||
))
|
||||
}
|
||||
sx={{
|
||||
'& .MuiAutocomplete-option': {
|
||||
minHeight: 44,
|
||||
fontSize: { xs: 14, sm: 16 },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -86,14 +86,14 @@ export const MaintenancePage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<FormSuspense>
|
||||
<Grid container spacing={2}>
|
||||
{/* Left Column: Form */}
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid container spacing={3}>
|
||||
{/* Top: Form */}
|
||||
<Grid item xs={12}>
|
||||
<MaintenanceRecordForm />
|
||||
</Grid>
|
||||
|
||||
{/* Right Column: Records List */}
|
||||
<Grid item xs={12} md={6}>
|
||||
{/* Bottom: Records List */}
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Recent Maintenance Records
|
||||
</Typography>
|
||||
|
||||
@@ -46,6 +46,7 @@ export const StationMap: React.FC<StationMapProps> = ({
|
||||
const infoWindows = useRef<google.maps.InfoWindow[]>([]);
|
||||
const currentLocationMarker = useRef<google.maps.marker.AdvancedMarkerElement | null>(null);
|
||||
const isInitializing = useRef<boolean>(false);
|
||||
const mapIdRef = useRef<string | null>(null);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -368,4 +369,3 @@ export const StationMap: React.FC<StationMapProps> = ({
|
||||
};
|
||||
|
||||
export default StationMap;
|
||||
const mapIdRef = useRef<string | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user