aaredb/frontend/src/components/ShipmentPanel.tsx
GotthardG c2215860bf Refactor Dewar service methods and improve field handling
Updated Dewar API methods to use protected endpoints for enhanced security and consistency. Added `pgroups` handling in various frontend components and modified the LogisticsView contact field for clarity. Simplified backend router imports for better readability.
2025-01-30 13:39:49 +01:00

209 lines
9.0 KiB
TypeScript

import React, { useState } from 'react';
import { Button, Box, Typography, IconButton } from '@mui/material';
import { Add as AddIcon, Delete as DeleteIcon, UploadFile as UploadFileIcon, Refresh as RefreshIcon } from '@mui/icons-material';
import UploadDialog from './UploadDialog';
import {ApiError, Dewar, Shipment, ShipmentsService} from '../../openapi';
import { SxProps } from '@mui/material';
import bottleGrey from '/src/assets/icons/bottle-svgrepo-com-grey.svg';
import bottleYellow from '/src/assets/icons/bottle-svgrepo-com-yellow.svg';
import bottleGreen from '/src/assets/icons/bottle-svgrepo-com-green.svg';
import bottleRed from '/src/assets/icons/bottle-svgrepo-com-red.svg';
interface ShipmentPanelProps {
setCreatingShipment: (value: boolean) => void;
activePgroup: string;
selectShipment: (shipment: Shipment | null) => void;
selectedShipment: Shipment | null;
sx?: SxProps;
shipments: Shipment[];
refreshShipments: () => void;
error: string | null;
}
const statusIconMap: Record<string, string> = {
'In Transit': bottleYellow,
'Delivered': bottleGreen,
'Pending': bottleGrey,
'Unknown': bottleRed,
};
const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
activePgroup,
setCreatingShipment,
selectShipment,
selectedShipment,
sx,
shipments,
refreshShipments,
error,
}) => {
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
console.log('Active Pgroup:', activePgroup);
const handleDeleteShipment = async () => {
if (selectedShipment) {
const confirmDelete = window.confirm(
`Are you sure you want to delete the shipment: ${selectedShipment.shipment_name}?`
);
if (!confirmDelete) return;
// Try to delete the shipment
await deleteShipment(selectedShipment.id);
}
};
const deleteShipment = async (shipmentId: number) => {
if (!shipmentId) return;
try {
await ShipmentsService.deleteShipmentProtectedShipmentsShipmentIdDelete(shipmentId);
refreshShipments();
selectShipment(null);
alert("Shipment deleted successfully.");
} catch (error: any) {
console.error("Failed to delete shipment:", error);
let errorMessage = "Failed to delete shipment.";
if (error instanceof ApiError && error.body) {
errorMessage = error.body.detail || errorMessage;
}
alert(errorMessage);
}
};
const handleShipmentSelection = (shipment: Shipment) => {
const isSelected = selectedShipment?.id === shipment.id;
selectShipment(isSelected ? null : shipment);
};
const openUploadDialog = (event: React.MouseEvent) => {
event.stopPropagation();
setUploadDialogOpen(true);
};
const closeUploadDialog = () => setUploadDialogOpen(false);
const getNumberOfDewars = (shipment: Shipment): number => shipment.dewars?.length || 0;
return (
<Box sx={{ width: '90%', borderRight: '1px solid #ccc', padding: 2, ...sx }}>
{error && <Typography color="error">{error}</Typography>}
<Typography variant="h6" sx={{ marginBottom: 2, fontWeight: 'bold' }}>
Shipments
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
onClick={() => setCreatingShipment(true)}
sx={{ padding: '10px 16px' }}
>
Create Shipment
</Button>
<IconButton
onClick={refreshShipments}
color="primary"
title="Refresh Shipments"
sx={{ marginLeft: 1 }}
>
<RefreshIcon />
</IconButton>
</Box>
{shipments.map((shipment) => {
const isSelected = selectedShipment?.id === shipment.id;
return (
<Box
key={shipment.id}
sx={{
marginBottom: 1,
padding: '10px 16px',
backgroundColor: isSelected ? '#52893e' : '#424242',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
color: 'white',
borderRadius: 1,
'&:hover': {
backgroundColor: isSelected ? '#9aca8c' : '#616161'
},
cursor: 'pointer'
}}
onClick={() => handleShipmentSelection(shipment)}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ position: 'relative', marginRight: 2 }}>
<img
src={statusIconMap[shipment.shipment_status] || bottleGrey}
alt={`Status: ${shipment.shipment_status}`}
width="24"
/>
<Typography
component="span"
sx={{
position: 'absolute',
top: '0%',
right: '0%',
transform: 'translate(50%, -50%)',
color: 'white',
fontWeight: 'bold',
fontSize: '0.6rem',
backgroundColor: 'transparent',
borderRadius: '50%',
padding: '0 2px',
}}
>
{getNumberOfDewars(shipment)}
</Typography>
</Box>
<Box>
<Typography>{shipment.shipment_name}</Typography>
<Typography sx={{ fontSize: '0.6rem', color: '#ccc' }}>{shipment.shipment_date}</Typography>
<Typography sx={{ fontSize: '0.6rem', color: '#ccc' }}>
Total Pucks: {shipment.dewars?.reduce((total, dewar: Dewar) => total + (dewar.number_of_pucks || 0), 0) ?? 0}
</Typography>
</Box>
</Box>
{isSelected && (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<IconButton
onClick={(event) => {
event.stopPropagation();
openUploadDialog(event);
}}
color="primary"
title="Upload Sample Data Sheet"
sx={{ marginLeft: 1 }}
>
<UploadFileIcon />
</IconButton>
<IconButton
onClick={(event) => {
event.stopPropagation();
handleDeleteShipment();
}}
color="error"
title="Delete Shipment"
sx={{ marginLeft: 1 }}
>
<DeleteIcon />
</IconButton>
</Box>
)}
</Box>
);
})}
<UploadDialog
activePgroup={activePgroup}
open={uploadDialogOpen}
onClose={closeUploadDialog}
selectedShipment={selectedShipment}
/>
</Box>
);
};
export default ShipmentPanel;