Fix Auth Errors

This commit is contained in:
Eric Gullickson
2025-09-22 10:27:10 -05:00
parent 3588372cef
commit 8fd7973656
19 changed files with 1342 additions and 174 deletions

View File

@@ -1,27 +1,241 @@
import React from 'react';
import { Card, CardContent, Typography, List, ListItem, ListItemText, Chip, Box } from '@mui/material';
import React, { useState } from 'react';
import {
Card,
CardContent,
Typography,
List,
ListItem,
ListItemText,
Chip,
Box,
IconButton,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
useTheme,
useMediaQuery
} from '@mui/material';
import { Edit, Delete } from '@mui/icons-material';
import { FuelLogResponse } from '../types/fuel-logs.types';
import { fuelLogsApi } from '../api/fuel-logs.api';
export const FuelLogsList: React.FC<{ logs?: FuelLogResponse[] }>= ({ logs }) => {
if (!logs || logs.length === 0) {
interface FuelLogsListProps {
logs?: FuelLogResponse[];
onEdit?: (log: FuelLogResponse) => void;
onDelete?: (logId: string) => void;
}
export const FuelLogsList: React.FC<FuelLogsListProps> = ({ logs, onEdit, onDelete }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [logToDelete, setLogToDelete] = useState<FuelLogResponse | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
const handleDeleteClick = (log: FuelLogResponse) => {
setLogToDelete(log);
setDeleteDialogOpen(true);
};
const handleDeleteConfirm = async () => {
if (!logToDelete) return;
try {
setIsDeleting(true);
await fuelLogsApi.delete(logToDelete.id);
onDelete?.(logToDelete.id);
setDeleteDialogOpen(false);
setLogToDelete(null);
} catch (error) {
console.error('Failed to delete fuel log:', error);
// TODO: Show error notification
} finally {
setIsDeleting(false);
}
};
const handleDeleteCancel = () => {
setDeleteDialogOpen(false);
setLogToDelete(null);
};
// Defensive check for logs data
if (!Array.isArray(logs) || logs.length === 0) {
return (
<Card variant="outlined"><CardContent><Typography variant="body2" color="text.secondary">No fuel logs yet.</Typography></CardContent></Card>
<Card variant="outlined">
<CardContent>
<Typography variant="body2" color="text.secondary">
No fuel logs yet.
</Typography>
</CardContent>
</Card>
);
}
return (
<List>
{logs.map((log) => (
<ListItem key={log.id} divider>
<ListItemText
primary={`${new Date(log.dateTime).toLocaleString()} $${(log.totalCost || 0).toFixed(2)}`}
secondary={`${(log.fuelUnits || 0).toFixed(3)} @ $${(log.costPerUnit || 0).toFixed(3)}${log.odometerReading ? `Odo: ${log.odometerReading}` : `Trip: ${log.tripDistance}`}`}
/>
{log.efficiency && typeof log.efficiency === 'number' && !isNaN(log.efficiency) && (
<Box><Chip label={`${log.efficiency.toFixed(1)} ${log.efficiencyLabel}`} size="small" color="primary" /></Box>
<>
<List>
{logs.map((log) => {
// Defensive checks for each log entry
if (!log || !log.id) {
console.warn('[FuelLogsList] Invalid log entry:', log);
return null;
}
try {
// Safe date formatting
const dateText = log.dateTime
? new Date(log.dateTime).toLocaleString()
: 'Unknown date';
// Safe cost formatting
const totalCost = typeof log.totalCost === 'number'
? log.totalCost.toFixed(2)
: '0.00';
// Safe fuel units and cost per unit
const fuelUnits = typeof log.fuelUnits === 'number'
? log.fuelUnits.toFixed(3)
: '0.000';
const costPerUnit = typeof log.costPerUnit === 'number'
? log.costPerUnit.toFixed(3)
: '0.000';
// Safe distance display
const distanceText = log.odometerReading
? `Odo: ${log.odometerReading}`
: log.tripDistance
? `Trip: ${log.tripDistance}`
: 'No distance';
return (
<ListItem
key={log.id}
divider
sx={{
flexDirection: isMobile ? 'column' : 'row',
alignItems: isMobile ? 'stretch' : 'center',
gap: isMobile ? 1 : 0,
py: isMobile ? 2 : 1
}}
>
<Box sx={{
display: 'flex',
flexDirection: isMobile ? 'column' : 'row',
alignItems: isMobile ? 'flex-start' : 'center',
flex: 1,
gap: isMobile ? 0.5 : 1
}}>
<ListItemText
primary={`${dateText} $${totalCost}`}
secondary={`${fuelUnits} @ $${costPerUnit}${distanceText}`}
sx={{ flex: 1, minWidth: 0 }}
/>
{log.efficiency &&
typeof log.efficiency === 'number' &&
!isNaN(log.efficiency) &&
log.efficiencyLabel && (
<Box sx={{ mr: isMobile ? 0 : 1 }}>
<Chip
label={`${log.efficiency.toFixed(1)} ${log.efficiencyLabel}`}
size="small"
color="primary"
/>
</Box>
)}
</Box>
<Box sx={{
display: 'flex',
gap: isMobile ? 1 : 0.5,
justifyContent: isMobile ? 'center' : 'flex-end',
width: isMobile ? '100%' : 'auto'
}}>
{onEdit && (
<IconButton
size={isMobile ? 'medium' : 'small'}
onClick={() => onEdit(log)}
sx={{
color: 'primary.main',
'&:hover': { backgroundColor: 'primary.main', color: 'white' },
minWidth: isMobile ? 48 : 'auto',
minHeight: isMobile ? 48 : 'auto',
...(isMobile && {
border: '1px solid',
borderColor: 'primary.main',
borderRadius: 2
})
}}
>
<Edit fontSize={isMobile ? 'medium' : 'small'} />
</IconButton>
)}
{onDelete && (
<IconButton
size={isMobile ? 'medium' : 'small'}
onClick={() => handleDeleteClick(log)}
sx={{
color: 'error.main',
'&:hover': { backgroundColor: 'error.main', color: 'white' },
minWidth: isMobile ? 48 : 'auto',
minHeight: isMobile ? 48 : 'auto',
...(isMobile && {
border: '1px solid',
borderColor: 'error.main',
borderRadius: 2
})
}}
>
<Delete fontSize={isMobile ? 'medium' : 'small'} />
</IconButton>
)}
</Box>
</ListItem>
);
} catch (error) {
console.error('[FuelLogsList] Error rendering log:', log, error);
return (
<ListItem key={log.id || Math.random()} divider>
<ListItemText
primary="Error displaying fuel log"
secondary="Data formatting issue"
/>
</ListItem>
);
}
}).filter(Boolean)}
</List>
{/* Delete Confirmation Dialog */}
<Dialog open={deleteDialogOpen} onClose={handleDeleteCancel}>
<DialogTitle>Delete Fuel Log</DialogTitle>
<DialogContent>
<Typography>
Are you sure you want to delete this fuel log entry? This action cannot be undone.
</Typography>
{logToDelete && (
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{new Date(logToDelete.dateTime).toLocaleString()} - ${logToDelete.totalCost.toFixed(2)}
</Typography>
)}
</ListItem>
))}
</List>
</DialogContent>
<DialogActions>
<Button onClick={handleDeleteCancel} disabled={isDeleting}>
Cancel
</Button>
<Button
onClick={handleDeleteConfirm}
color="error"
variant="contained"
disabled={isDeleting}
>
{isDeleting ? 'Deleting...' : 'Delete'}
</Button>
</DialogActions>
</Dialog>
</>
);
};