
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.
209 lines
9.0 KiB
TypeScript
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; |