Add pgroup handling in dewars and enhance ShipmentDetails UI
Introduced a new `pgroups` attribute for dewars in the backend with schema and model updates. Modified the frontend to display `pgroups` as chips, integrate new visual icons for pucks and crystals, and enhance the UI/UX in `ShipmentDetails` and `DewarStepper` components. Added reusable SVG components for better modularity and design consistency.
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box, Typography, Button, Stack, TextField, IconButton, Grid } from '@mui/material';
|
||||
import {Box, Typography, Button, Stack, TextField, IconButton, Grid, Chip} from '@mui/material';
|
||||
import QRCode from 'react-qr-code';
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
@ -8,6 +8,9 @@ import { Dewar, DewarsService, Shipment, Contact, ApiError, ShipmentsService } f
|
||||
import { SxProps } from "@mui/system";
|
||||
import CustomStepper from "./DewarStepper";
|
||||
import DewarDetails from './DewarDetails';
|
||||
import { PuckDetailsVisual } from '../assets/icons/SimplePuckIcon';
|
||||
import CrystalFacetedIcon from "../assets/icons/CrystalIcon.tsx";
|
||||
|
||||
|
||||
const MAX_COMMENTS_LENGTH = 200;
|
||||
|
||||
@ -183,6 +186,39 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
const isCommentsEdited = comments !== initialComments;
|
||||
const contact = selectedShipment?.contact;
|
||||
|
||||
const renderPgroupChips = () => {
|
||||
// Safely handle pgroups as an array
|
||||
const pgroupsArray = Array.isArray(selectedShipment?.pgroups)
|
||||
? selectedShipment.pgroups
|
||||
: selectedShipment?.pgroups?.split(",").map((pgroup: string) => pgroup.trim()) || [];
|
||||
|
||||
if (!pgroupsArray.length) {
|
||||
return <Typography variant="body2">No associated pgroups</Typography>;
|
||||
}
|
||||
|
||||
return pgroupsArray.map((pgroup: string) => (
|
||||
<Chip
|
||||
key={pgroup}
|
||||
label={pgroup}
|
||||
color={pgroup === activePgroup ? "primary" : "default"} // Highlight active pgroups
|
||||
sx={{
|
||||
margin: 0.5,
|
||||
backgroundColor: pgroup === activePgroup ? '#19d238' : '#b0b0b0',
|
||||
color: pgroup === activePgroup ? 'white' : 'black',
|
||||
fontWeight: 'bold',
|
||||
borderRadius: '8px',
|
||||
height: '20px',
|
||||
fontSize: '12px',
|
||||
boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)',
|
||||
//cursor: isAssociated ? 'default' : 'pointer', // Disable pointer for associated chips
|
||||
//'&:hover': { opacity: isAssociated ? 1 : 0.8 }, // Disable hover effect for associated chips
|
||||
mr: 1,
|
||||
mb: 1,
|
||||
}}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ ...sx, padding: 2, textAlign: 'left' }}>
|
||||
{!localSelectedDewar && !isAddingDewar && (
|
||||
@ -229,6 +265,9 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
<Grid item xs={12} md={6}>
|
||||
<Box sx={{ marginTop: 2, marginBottom: 2 }}>
|
||||
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||
{renderPgroupChips()}
|
||||
</Box>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
Main contact person: {contact ? `${contact.firstname} ${contact.lastname}` : 'N/A'}
|
||||
</Typography>
|
||||
@ -293,46 +332,78 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
border: localSelectedDewar?.id === dewar.id ? '2px solid #000' : undefined,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginRight: 2 }}>
|
||||
{dewar.unique_id ? (
|
||||
<QRCode value={dewar.unique_id} size={70} />
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: 70,
|
||||
height: 70,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px dashed #ccc',
|
||||
borderRadius: 1,
|
||||
color: 'text.secondary'
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">No QR Code</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex', // Flex container to align all items horizontally
|
||||
alignItems: 'center', // Vertically align items in the center
|
||||
justifyContent: 'space-between', // Distribute children evenly across the row
|
||||
width: '100%', // Ensure the container spans full width
|
||||
gap: 2, // Add consistent spacing between sections
|
||||
}}
|
||||
>
|
||||
{/* Left: QR Code */}
|
||||
<Box>
|
||||
{dewar.unique_id ? (
|
||||
<QRCode value={dewar.unique_id} size={60} />
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: 60,
|
||||
height: 60,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px dashed #ccc',
|
||||
borderRadius: 1,
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">No QR Code</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Middle-Left: Dewar Information */}
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Typography variant="h6" fontWeight="bold">
|
||||
{dewar.dewar_name}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<PuckDetailsVisual puckCount={dewar.number_of_pucks || 0} />
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<CrystalFacetedIcon size={20} />
|
||||
<Typography variant="body2">{dewar.number_of_samples || 0} Samples</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="body1">{dewar.dewar_name}</Typography>
|
||||
<Typography variant="body2">Number of Pucks: {dewar.number_of_pucks || 0}</Typography>
|
||||
<Typography variant="body2">Number of Samples: {dewar.number_of_samples || 0}</Typography>
|
||||
<Typography variant="body2">Tracking Number: {dewar.tracking_number}</Typography>
|
||||
<Typography variant="body2">
|
||||
Contact Person: {dewar.contact?.firstname ? `${dewar.contact.firstname} ${dewar.contact.lastname}` : 'N/A'}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<CustomStepper dewar={dewar} selectedDewarId={localSelectedDewar?.id ?? null} refreshShipments={refreshShipments} />
|
||||
</Box>
|
||||
{/* Middle-Right: Contact and Return Information */}
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, alignItems: 'flex-start' }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Contact Person: {dewar.contact?.firstname ? `${dewar.contact.firstname} ${dewar.contact.lastname}` : 'N/A'}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Return Address: {dewar.return_address?.house_number
|
||||
? `${dewar.return_address.street}, ${dewar.return_address.city}`
|
||||
: 'N/A'}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Right: Stepper */}
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1, // Allow the stepper to expand and use space effectively
|
||||
maxWidth: '400px', // Optional: Limit how wide the stepper can grow
|
||||
}}
|
||||
>
|
||||
<CustomStepper
|
||||
dewar={dewar}
|
||||
selectedDewarId={localSelectedDewar?.id ?? null}
|
||||
refreshShipments={refreshShipments}
|
||||
sx={{ width: '100%' }} // Make the stepper fill its container
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
{localSelectedDewar?.id === dewar.id && (
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
|
Reference in New Issue
Block a user