added update to comments with characters counter

This commit is contained in:
GotthardG
2024-11-03 21:42:42 +01:00
parent 0becdf9337
commit a9b8925be8
6 changed files with 242 additions and 157 deletions

View File

@ -12,8 +12,9 @@ import QRCode from 'react-qr-code';
import {
ContactPerson,
Address,
Dewar, ContactsService, AddressesService, ShipmentsService, DewarsService,
Dewar, ContactsService, AddressesService, ShipmentsService,
} from '../../openapi';
import Unipuck from '../components/Unipuck';
interface DewarDetailsProps {
dewar: Dewar;
@ -252,10 +253,10 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
shipment_date: existingShipment.shipment_date,
shipment_status: existingShipment.shipment_status,
comments: existingShipment.comments,
contact_person_id: existingShipment.contact_person.id, // Keep main shipment contact person
contact_person_id: existingShipment.contact_person.id,
return_address_id: selectedReturnAddress,
proposal_id: existingShipment.proposal?.id,
dewars: [updatedDewar], // Updated dewars array
dewars: [updatedDewar],
};
console.log('Payload for update:', JSON.stringify(payload, null, 2));
@ -292,18 +293,19 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
sx={{ width: '300px', marginBottom: 2 }}
/>
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
<Box sx={{ width: 80, height: 80, backgroundColor: '#e0e0e0', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' }}>
{dewar.qrcode ? (
<QRCode value={dewar.qrcode} size={70} />
) : (
<Typography>No QR code available</Typography>
)}
<Button variant="contained" sx={{ marginTop: 1 }} onClick={() => { /** Add logic to generate QR Code */ }}>
Generate QR Code
</Button>
</Box>
<Button variant="contained" onClick={() => { /** Add logic to generate QR Code */ }}>
Generate QR Code
</Button>
</Box>
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
<Unipuck pucks={dewar.number_of_pucks ?? 0} />
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
<Typography variant="body1">Current Contact Person:</Typography>
<Select

View File

@ -23,14 +23,11 @@ interface ShipmentDetailsProps {
}
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
isCreatingShipment,
sx,
selectedShipment,
selectedDewar,
setSelectedDewar,
setSelectedShipment,
refreshShipments,
defaultContactPerson
}) => {
const [localSelectedDewar, setLocalSelectedDewar] = useState<Dewar | null>(null);
const [isAddingDewar, setIsAddingDewar] = useState<boolean>(false);
@ -200,146 +197,152 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
</Box>
)}
{
selectedShipment
? (
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Box sx={{ marginTop: 2, marginBottom: 2 }}>
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
<Typography variant="body1" color="textSecondary">
Main contact person: {contactPerson ? `${contactPerson.firstname} ${contactPerson.lastname}` : 'N/A'}
</Typography>
<Typography variant="body1">Number of Pucks: {totalPucks}</Typography>
<Typography variant="body1">Number of Samples: {totalSamples}</Typography>
<Typography variant="body1">Shipment Date: {selectedShipment.shipment_date}</Typography>
</Box>
</Grid>
<Grid item xs={12} md={6}>
<Box sx={{ position: 'relative' }}>
<TextField
label="Comments"
fullWidth
multiline
rows={4}
value={comments}
onChange={(e) => setComments(e.target.value)}
sx={{
marginBottom: 2,
'& .MuiInputBase-root': {
color: isCommentsEdited ? 'inherit' : 'rgba(0, 0, 0, 0.6)',
},
}}
helperText={`${MAX_COMMENTS_LENGTH - comments.length} characters remaining`}
error={comments.length > MAX_COMMENTS_LENGTH}
/>
<Box sx={{ position: 'absolute', bottom: 8, right: 8, display: 'flex', gap: 1 }}>
<IconButton
color="primary"
onClick={handleSaveComments}
disabled={comments.length > MAX_COMMENTS_LENGTH}
>
<CheckIcon />
</IconButton>
<IconButton color="secondary" onClick={handleCancelEdit}>
<CloseIcon />
</IconButton>
</Box>
</Box>
</Grid>
</Grid>
)
: <Typography variant="h5" color="error">No shipment selected</Typography>
}
{localSelectedDewar && !isAddingDewar && (
<DewarDetails
dewar={localSelectedDewar}
trackingNumber={localSelectedDewar.tracking_number || ''}
setTrackingNumber={(value) => {
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
}}
initialContactPersons={selectedShipment?.contact_person ? [selectedShipment.contact_person] : []}
initialReturnAddresses={selectedShipment?.return_address ? [selectedShipment.return_address] : []}
defaultContactPerson={contactPerson}
defaultReturnAddress={selectedShipment?.return_address}
shipmentId={selectedShipment?.shipment_id || ''}
refreshShipments={refreshShipments}
/>
{selectedShipment ? (
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Box sx={{ marginTop: 2, marginBottom: 2 }}>
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
<Typography variant="body1" color="textSecondary">
Main contact person: {contactPerson ? `${contactPerson.firstname} ${contactPerson.lastname}` : 'N/A'}
</Typography>
<Typography variant="body1">Number of Pucks: {totalPucks}</Typography>
<Typography variant="body1">Number of Samples: {totalSamples}</Typography>
<Typography variant="body1">Shipment Date: {selectedShipment.shipment_date}</Typography>
</Box>
</Grid>
<Grid item xs={12} md={6}>
<Box sx={{ position: 'relative' }}>
<TextField
label="Comments"
fullWidth
multiline
rows={4}
value={comments}
onChange={(e) => setComments(e.target.value)}
sx={{
marginBottom: 2,
'& .MuiInputBase-root': {
color: isCommentsEdited ? 'inherit' : 'rgba(0, 0, 0, 0.6)',
},
}}
helperText={`${MAX_COMMENTS_LENGTH - comments.length} characters remaining`}
error={comments.length > MAX_COMMENTS_LENGTH}
/>
<Box sx={{ position: 'absolute', bottom: 8, right: 8, display: 'flex', gap: 1 }}>
<IconButton
color="primary"
onClick={handleSaveComments}
disabled={comments.length > MAX_COMMENTS_LENGTH}
>
<CheckIcon />
</IconButton>
<IconButton color="secondary" onClick={handleCancelEdit}>
<CloseIcon />
</IconButton>
</Box>
</Box>
</Grid>
</Grid>
) : (
<Typography variant="h5" color="error">No shipment selected</Typography>
)}
<Stack spacing={1}>
{selectedShipment?.dewars?.map((dewar) => (
<Button
key={dewar.id}
onClick={() => handleDewarSelection(dewar)}
variant="outlined"
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: 1,
border: '1px solid #ccc',
borderRadius: 1,
backgroundColor: localSelectedDewar?.id === dewar.id ? '#f0f0f0' : '#fff',
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginRight: 2 }}>
{dewar.qrcode ? (
<QRCode value={dewar.qrcode} 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>
)}
</Box>
<React.Fragment key={dewar.id}>
<Button
onClick={() => handleDewarSelection(dewar)}
variant="outlined"
sx={{
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
padding: localSelectedDewar?.id === dewar.id ? 2 : 1,
textTransform: 'none',
width: '100%',
backgroundColor: localSelectedDewar?.id === dewar.id ? '#f0f0f0' : '#fff',
transition: 'all 0.3s',
overflow: 'hidden',
border: localSelectedDewar?.id === dewar.id ? '2px solid #000' : undefined,
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginRight: 2 }}>
{dewar.qrcode ? (
<QRCode value={dewar.qrcode} 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>
)}
</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_person?.firstname ? `${dewar.contact_person.firstname} ${dewar.contact_person.lastname}` : 'N/A'}
</Typography>
</Box>
<Box sx={{
flexGrow: 1,
display: 'flex',
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between'
}}>
<CustomStepper dewar={dewar} />
</Box>
<Box sx={{ flexGrow: 1, marginRight: 0 }}>
<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_person?.firstname ? `${dewar.contact_person.firstname} ${dewar.contact_person.lastname}` : 'N/A'}
</Typography>
</Box>
<Box sx={{
flexGrow: 1,
display: 'flex',
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between'
}}>
<CustomStepper dewar={dewar} />
{localSelectedDewar?.id === dewar.id && (
<Button
onClick={() => handleDeleteDewar(dewar.id)}
<IconButton
onClick={(e) => {
e.stopPropagation();
handleDeleteDewar(dewar.id);
}}
color="error"
sx={{
minWidth: '40px',
height: '40px',
marginLeft: 2,
padding: 0,
alignSelf: 'center',
}}
>
<DeleteIcon />
</Button>
</IconButton>
)}
</Box>
</Button>
</Button>
{localSelectedDewar?.id === dewar.id && (
<Box sx={{ padding: 2, border: '1px solid #ccc', borderRadius: '4px' }}>
<DewarDetails
dewar={localSelectedDewar}
trackingNumber={localSelectedDewar.tracking_number || ''}
setTrackingNumber={(value) => {
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
}}
initialContactPersons={selectedShipment?.contact_person ? [selectedShipment.contact_person] : []}
initialReturnAddresses={selectedShipment?.return_address ? [selectedShipment.return_address] : []}
defaultContactPerson={contactPerson}
defaultReturnAddress={selectedShipment?.return_address}
shipmentId={selectedShipment?.shipment_id || ''}
refreshShipments={refreshShipments}
/>
</Box>
)}
</React.Fragment>
))}
</Stack>
</Box>

View File

@ -33,7 +33,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
sx,
shipments,
refreshShipments,
error
error,
}) => {
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
@ -160,27 +160,29 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
</Box>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<IconButton
onClick={openUploadDialog}
color="primary"
title="Upload Sample Data Sheet"
sx={{ marginLeft: 1 }}
>
<UploadFileIcon />
</IconButton>
{selectedShipment?.shipment_id === shipment.shipment_id && (
<IconButton
onClick={(event) => {
event.stopPropagation();
console.log('Delete button clicked'); // debug log
handleDeleteShipment();
}}
color="error"
title="Delete Shipment"
sx={{ marginLeft: 1 }}
>
<DeleteIcon />
</IconButton>
<>
<IconButton
onClick={openUploadDialog}
color="primary"
title="Upload Sample Data Sheet"
sx={{ marginLeft: 1 }}
>
<UploadFileIcon />
</IconButton>
<IconButton
onClick={(event) => {
event.stopPropagation();
console.log('Delete button clicked'); // debug log
handleDeleteShipment();
}}
color="error"
title="Delete Shipment"
sx={{ marginLeft: 1 }}
>
<DeleteIcon />
</IconButton>
</>
)}
</Box>
</Button>

View File

@ -0,0 +1,42 @@
// app/components/Unipuck.tsx
import React from 'react';
import { Box } from '@mui/material';
interface UnipuckProps {
pucks: number; // Number of pucks, assuming each puck follows the same layout
}
const Unipuck: React.FC<UnipuckProps> = ({ pucks }) => {
const renderPuck = () => {
const puckSVG = (
<svg width="100" height="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="black" strokeWidth="2" fill="none" />
{[...Array(11)].map((_, index) => {
const angle = (index * (360 / 11)) * (Math.PI / 180);
const x = 50 + 35 * Math.cos(angle);
const y = 50 + 35 * Math.sin(angle);
return <circle key={index} cx={x} cy={y} r="5" fill="black" />;
})}
{[...Array(5)].map((_, index) => {
const angle = (index * (360 / 5) + 36) * (Math.PI / 180);
const x = 50 + 15 * Math.cos(angle);
const y = 50 + 15 * Math.sin(angle);
return <circle key={index} cx={x} cy={y} r="5" fill="black" />;
})}
</svg>
);
return puckSVG;
};
return (
<Box sx={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 2 }}>
{[...Array(pucks)].map((_, index) => (
<Box key={index} sx={{ margin: 1 }}>
{renderPuck()}
</Box>
))}
</Box>
);
};
export default Unipuck;