diff --git a/backend/main.py b/backend/main.py index a75bcba..7b27575 100644 --- a/backend/main.py +++ b/backend/main.py @@ -21,6 +21,7 @@ app.add_middleware( class ContactPerson(BaseModel): + id: int firstname: str lastname: str phone_number: str @@ -28,6 +29,7 @@ class ContactPerson(BaseModel): class Address(BaseModel): + id: int street: str city: str zipcode: str @@ -83,30 +85,30 @@ class Shipment(BaseModel): # Example data for contacts contacts = [ - ContactPerson(firstname="Frodo", lastname="Baggins", phone_number="123-456-7890", email="frodo.baggins@lotr.com"), - ContactPerson(firstname="Samwise", lastname="Gamgee", phone_number="987-654-3210", email="samwise.gamgee@lotr.com"), - ContactPerson(firstname="Aragorn", lastname="Elessar", phone_number="123-333-4444", + ContactPerson(id=1, firstname="Frodo", lastname="Baggins", phone_number="123-456-7890", email="frodo.baggins@lotr.com"), + ContactPerson(id=2, firstname="Samwise", lastname="Gamgee", phone_number="987-654-3210", email="samwise.gamgee@lotr.com"), + ContactPerson(id=3, firstname="Aragorn", lastname="Elessar", phone_number="123-333-4444", email="aragorn.elessar@lotr.com"), - ContactPerson(firstname="Legolas", lastname="Greenleaf", phone_number="555-666-7777", + ContactPerson(id=4, firstname="Legolas", lastname="Greenleaf", phone_number="555-666-7777", email="legolas.greenleaf@lotr.com"), - ContactPerson(firstname="Gimli", lastname="Son of Gloin", phone_number="888-999-0000", + ContactPerson(id=5, firstname="Gimli", lastname="Son of Gloin", phone_number="888-999-0000", email="gimli.sonofgloin@lotr.com"), - ContactPerson(firstname="Gandalf", lastname="The Grey", phone_number="222-333-4444", + ContactPerson(id=6, firstname="Gandalf", lastname="The Grey", phone_number="222-333-4444", email="gandalf.thegrey@lotr.com"), - ContactPerson(firstname="Boromir", lastname="Son of Denethor", phone_number="111-222-3333", + ContactPerson(id=7, firstname="Boromir", lastname="Son of Denethor", phone_number="111-222-3333", email="boromir.sonofdenethor@lotr.com"), - ContactPerson(firstname="Galadriel", lastname="Lady of Lothlórien", phone_number="444-555-6666", + ContactPerson(id=8, firstname="Galadriel", lastname="Lady of Lothlórien", phone_number="444-555-6666", email="galadriel.lothlorien@lotr.com"), - ContactPerson(firstname="Elrond", lastname="Half-elven", phone_number="777-888-9999", + ContactPerson(id=9, firstname="Elrond", lastname="Half-elven", phone_number="777-888-9999", email="elrond.halfelven@lotr.com"), - ContactPerson(firstname="Eowyn", lastname="Shieldmaiden of Rohan", phone_number="000-111-2222", + ContactPerson(id=10, firstname="Eowyn", lastname="Shieldmaiden of Rohan", phone_number="000-111-2222", email="eowyn.rohan@lotr.com") ] # Example data for return addresses return_addresses = [ - Address(street='123 Hobbiton St', city='Shire', zipcode='12345', country='Middle Earth'), - Address(street='456 Rohan Rd', city='Edoras', zipcode='67890', country='Middle Earth') + Address(id=1, street='123 Hobbiton St', city='Shire', zipcode='12345', country='Middle Earth'), + Address(id=2, street='456 Rohan Rd', city='Edoras', zipcode='67890', country='Middle Earth') ] # Example data for dewars diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 17d80e8..cb85833 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,7 @@ "@fullcalendar/react": "^6.1.15", "@fullcalendar/timegrid": "^6.1.15", "@mui/icons-material": "^6.1.5", + "@mui/lab": "^6.0.0-beta.13", "@mui/material": "^6.1.5", "dayjs": "^1.11.13", "openapi-typescript-codegen": "^0.29.0", @@ -1103,6 +1104,40 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" + }, "node_modules/@fullcalendar/core": { "version": "6.1.15", "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz", @@ -1245,6 +1280,37 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.60", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.60.tgz", + "integrity": "sha512-w8twR3qCUI+uJHO5xDOuc1yB5l46KFbvNsTwIvEW9tQkKxVaiEFf2GAXHuvFJiHfZLqjzett6drZjghy8D1Z1A==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@floating-ui/react-dom": "^2.1.1", + "@mui/types": "^7.2.18", + "@mui/utils": "^6.1.5", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/core-downloads-tracker": { "version": "6.1.5", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.5.tgz", @@ -1279,10 +1345,55 @@ } } }, + "node_modules/@mui/lab": { + "version": "6.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.13.tgz", + "integrity": "sha512-gLcAL96KhV1aA7sCaganPitVb+NT42Y2KsmnHmCtCVqAgBgSmC4D6mcH7MjjR1UAQt+DfxeeoqrFIQjKTI/wmA==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/base": "5.0.0-beta.60", + "@mui/system": "^6.1.5", + "@mui/types": "^7.2.18", + "@mui/utils": "^6.1.5", + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": "^6.1.5", + "@mui/material-pigment-css": "^6.1.5", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { "version": "6.1.5", "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.5.tgz", "integrity": "sha512-rhaxC7LnlOG8zIVYv7BycNbWkC5dlm9A/tcDUp0CuwA7Zf9B9JP6M3rr50cNKxI7Z0GIUesAT86ceVm44quwnQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/core-downloads-tracker": "^6.1.5", diff --git a/frontend/package.json b/frontend/package.json index 450fba4..bc3aef2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "@fullcalendar/react": "^6.1.15", "@fullcalendar/timegrid": "^6.1.15", "@mui/icons-material": "^6.1.5", + "@mui/lab": "^6.0.0-beta.13", "@mui/material": "^6.1.5", "dayjs": "^1.11.13", "openapi-typescript-codegen": "^0.29.0", diff --git a/frontend/src/components/DewarDetails.tsx b/frontend/src/components/DewarDetails.tsx index f28bcfd..bb160c5 100644 --- a/frontend/src/components/DewarDetails.tsx +++ b/frontend/src/components/DewarDetails.tsx @@ -1,49 +1,41 @@ import * as React from 'react'; import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material'; import QRCode from 'react-qr-code'; -import {ContactPerson, Address, Dewar} from "../../openapi"; - +import { ContactPerson, Address, Dewar } from "../../openapi"; interface DewarDetailsProps { - dewar: Dewar | null; + dewar: Dewar; trackingNumber: string; setTrackingNumber: React.Dispatch>; - onGenerateQRCode: () => void; contactPersons: ContactPerson[]; returnAddresses: Address[]; - addNewContactPerson: (name: string) => void; - addNewReturnAddress: (address: string) => void; - ready_date?: string; - shipping_date?: string; - arrival_date?: string; } const DewarDetails: React.FC = ({ dewar, trackingNumber, setTrackingNumber, - onGenerateQRCode, contactPersons, - returnAddresses, - addNewContactPerson, - addNewReturnAddress, + returnAddresses }) => { - const [selectedContactPerson, setSelectedContactPerson] = React.useState(''); - const [selectedReturnAddress, setSelectedReturnAddress] = React.useState(''); + const [selectedContactPerson, setSelectedContactPerson] = React.useState(contactPersons[0]?.firstname || ''); + const [selectedReturnAddress, setSelectedReturnAddress] = React.useState(returnAddresses[0]?.id?.toString() || ''); + + const updateSelectedDetails = (contactPerson?: { firstname: string }, returnAddress?: Address) => { + if (contactPerson) setSelectedContactPerson(contactPerson.firstname); + if (returnAddress) setSelectedReturnAddress(returnAddress.id.toString()); + }; + + React.useEffect(() => { + updateSelectedDetails(contactPersons[0], returnAddresses[0]); + }, [contactPersons, returnAddresses]); + const [newContactPerson, setNewContactPerson] = React.useState(''); const [newReturnAddress, setNewReturnAddress] = React.useState(''); const [feedbackMessage, setFeedbackMessage] = React.useState(''); const [openSnackbar, setOpenSnackbar] = React.useState(false); - React.useEffect(() => { - if (contactPersons.length > 0) { - setSelectedContactPerson(contactPersons[0].firstname); // Default to the first contact person - } - if (returnAddresses.length > 0) { - setSelectedReturnAddress(returnAddresses[0].return_address); // Default to the first return address - } - }, [contactPersons, returnAddresses]); - + // Ensure dewar is defined before attempting to render the dewar details if (!dewar) { return No dewar selected.; } @@ -52,8 +44,7 @@ const DewarDetails: React.FC = ({ if (newContactPerson.trim() === '') { setFeedbackMessage('Please enter a valid contact person name.'); } else { - addNewContactPerson(newContactPerson); - setNewContactPerson(''); + setNewContactPerson(''); // Add logic to save the new contact person setFeedbackMessage('Contact person added successfully.'); } setOpenSnackbar(true); @@ -63,8 +54,7 @@ const DewarDetails: React.FC = ({ if (newReturnAddress.trim() === '') { setFeedbackMessage('Please enter a valid return address.'); } else { - addNewReturnAddress(newReturnAddress); - setNewReturnAddress(''); + setNewReturnAddress(''); // Add logic to save the new return address setFeedbackMessage('Return address added successfully.'); } setOpenSnackbar(true); @@ -91,7 +81,7 @@ const DewarDetails: React.FC = ({ No QR code available )} - @@ -107,10 +97,11 @@ const DewarDetails: React.FC = ({ displayEmpty fullWidth sx={{ marginBottom: 2 }} + variant={'outlined'} > Select Contact Person {contactPersons.map((person) => ( - {person.lastname} + {person.firstname + " " + person.lastname} ))} Add New Contact Person @@ -137,10 +128,11 @@ const DewarDetails: React.FC = ({ displayEmpty fullWidth sx={{ marginBottom: 2 }} + variant={'outlined'} > Select Return Address {returnAddresses.map((address) => ( - {address.address} + {address.street} ))} Add New Return Address @@ -170,4 +162,4 @@ const DewarDetails: React.FC = ({ ); }; -export default DewarDetails; +export default DewarDetails; \ No newline at end of file diff --git a/frontend/src/components/DewarStepper.tsx b/frontend/src/components/DewarStepper.tsx new file mode 100644 index 0000000..056ff5c --- /dev/null +++ b/frontend/src/components/DewarStepper.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { Stepper, Step, StepLabel, StepIconProps, Typography } from '@mui/material'; +import AirplanemodeActiveIcon from '@mui/icons-material/AirplanemodeActive'; +import StoreIcon from '@mui/icons-material/Store'; +import bottleIcon from '../assets/icons/bottle-svgrepo-com-grey.svg'; +import { Dewar } from "../../openapi"; + +// Constants +const ICON_STYLE = { width: 24, height: 24 }; + +// Define types for icons mapping. +const ICONS: { [key: number]: React.ReactElement } = { + 0: Bottle Icon, + 1: , + 2: , +}; + +// Define StepIconContainer to accept correct props and handle typing better +interface StepIconContainerProps extends React.HTMLAttributes { + color: string; +} + +const StepIconContainer: React.FC = ({ color, children, ...rest }) => ( +
+ {children} +
+); + +type StepIconComponentProps = { + icon: number; + dewar: Dewar; +} & StepIconProps; + +const StepIconComponent = ({ icon, dewar, ...props }: StepIconComponentProps) => { + const { iconIndex, color } = getIconProperties(icon, dewar); + + return ( + + {ICONS[iconIndex]} + + ); +}; + +// Extracted function to determine icon properties +const getIconProperties = (icon: number, dewar: Dewar) => { + const iconIndex = icon - 1; + const color = determineIconColor(dewar, iconIndex); + return { iconIndex, color }; +}; + +// Original determineIconColor function remains unchanged +const determineIconColor = (dewar: Dewar, index: number) => { + let color = 'grey'; + + if (index === 0) { + if (dewar.status === 'In Preparation') { + color = 'blue'; + } else if (dewar.status === 'Ready for Shipping') { + color = 'green'; + } + } + if (index === 1) { + if (dewar.status === 'Ready for Shipping' && dewar.shippingStatus !== 'shipped') { + color = 'blue'; + } else if (dewar.shippingStatus === 'shipped') { + color = 'green'; + } + } + if (index === 2) { + if (dewar.shippingStatus === 'shipped' && dewar.arrivalStatus !== 'arrived') { + color = 'blue'; + } else if (dewar.arrivalStatus === 'arrived') { + color = 'green'; + } + } + return color; +}; + +// Define your steps +const steps = ['In Preparation', 'Ready for Shipping', 'Arrived']; + +const CustomStepper = ({ dewar }: { dewar: Dewar }) => { + // Determine the current active step + const activeStep = steps.indexOf(dewar.status) !== -1 ? steps.indexOf(dewar.status) : 0; + + return ( + + {steps.map((label, index) => ( + + } + > + {label} + + + {index === 0 ? dewar.ready_date : + index === 1 ? dewar.shipping_date : + index === 2 ? dewar.arrival_date : ''} + + + ))} + + ); +}; + +export default CustomStepper; \ No newline at end of file diff --git a/frontend/src/components/ShipmentDetails.tsx b/frontend/src/components/ShipmentDetails.tsx index 64e5707..b21bbc6 100644 --- a/frontend/src/components/ShipmentDetails.tsx +++ b/frontend/src/components/ShipmentDetails.tsx @@ -1,96 +1,68 @@ import React from 'react'; -import {Box, Typography, Button, Stack, TextField, Stepper, Step, StepLabel} from '@mui/material'; -import DewarDetails from '../components/DewarDetails.tsx'; -import { SxProps } from '@mui/system'; +import {Box, Typography, Button, Stack, TextField} from '@mui/material'; import QRCode from 'react-qr-code'; -import bottleIcon from '../assets/icons/bottle-svgrepo-com-grey.svg'; -import AirplanemodeActiveIcon from "@mui/icons-material/AirplanemodeActive"; -import StoreIcon from "@mui/icons-material/Store"; import DeleteIcon from "@mui/icons-material/Delete"; -import {ContactPerson, Dewar, Proposal, Address, Shipment_Input, DefaultService} from "../../openapi"; // Import delete icon +import {Dewar, Shipment_Input, DefaultService} from "../../openapi"; +import {SxProps} from "@mui/system"; +import CustomStepper from "./DewarStepper"; +import DewarDetails from './DewarDetails'; + + interface ShipmentDetailsProps { - selectedShipment: Shipment_Input | null; - setSelectedDewar: React.Dispatch>; isCreatingShipment: boolean; - newShipment: Shipment_Input; - setNewShipment: React.Dispatch>; - handleSaveShipment: () => void; - contactPersons: ContactPerson[]; - proposals: Proposal[]; - returnAddresses: Address[]; + selectedShipment: Shipment_Input; + selectedDewar: Dewar | null; + setSelectedDewar: React.Dispatch>; sx?: SxProps; } const ShipmentDetails: React.FC = ({ selectedShipment, setSelectedDewar, - setNewShipment, - contactPersons, - returnAddresses, sx = {}, }) => { const [localSelectedDewar, setLocalSelectedDewar] = React.useState(null); - const [trackingNumber, setTrackingNumber] = React.useState(''); const [isAddingDewar, setIsAddingDewar] = React.useState(false); const [newDewar, setNewDewar] = React.useState>({ dewar_name: '', tracking_number: '', }); - // Step titles based on your status - const steps = ['Ready for Shipping', 'Shipped', 'Arrived']; const totalPucks = selectedShipment.dewars.reduce((acc, dewar) => acc + (dewar.number_of_pucks || 0), 0); const totalSamples = selectedShipment.dewars.reduce((acc, dewar) => acc + (dewar.number_of_samples || 0), 0); - // Handle dewar selection const handleDewarSelection = (dewar: Dewar) => { - setLocalSelectedDewar(prevDewar => (prevDewar?.tracking_number === dewar.tracking_number ? null : dewar)); - setSelectedDewar(prevDewar => (prevDewar?.tracking_number === dewar.tracking_number ? null : dewar)); + if (setSelectedDewar) { + const newSelection = localSelectedDewar?.id === dewar.id ? null : dewar; + setLocalSelectedDewar(newSelection); + setSelectedDewar(newSelection); + } }; - // Handle dewar deletion const handleDeleteDewar = () => { if (localSelectedDewar) { const confirmed = window.confirm('Are you sure you want to delete this dewar?'); if (confirmed) { const updatedDewars = selectedShipment.dewars.filter(dewar => dewar.tracking_number !== localSelectedDewar.tracking_number); - console.log('Updated Dewars:', updatedDewars); // Log or update state as needed - setLocalSelectedDewar(null); // Reset selection after deletion + console.log('Updated Dewars:', updatedDewars); + setLocalSelectedDewar(null); } } }; - // Handle form input changes for the new dewar const handleNewDewarChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; + const {name, value} = e.target; setNewDewar((prev) => ({ ...prev, [name]: value, })); }; - const createDewar = async (newDewar: Partial, shipmentId: string) => { - console.log("Payload being sent to the API:", newDewar); - try { - const response = await DefaultService.createDewarDewarsPost(shipmentId, newDewar); - console.log("Response from API:", response); - return response; - } catch (error) { - console.error("Error creating dewar:", error); - if (error.response) { - console.error("Validation error details:", error.response.data); - } - throw error; - } - }; - - // Handle adding a new dewar const handleAddDewar = async () => { if (selectedShipment && newDewar.dewar_name) { try { const newDewarToPost: Dewar = { - //id: `DEWAR${Date.now()}`, dewar_name: newDewar.dewar_name.trim() || 'Unnamed Dewar', number_of_pucks: newDewar.number_of_pucks ?? 0, number_of_samples: newDewar.number_of_samples ?? 0, @@ -100,25 +72,11 @@ const ShipmentDetails: React.FC = ({ shippingStatus: 'not shipped', arrivalStatus: 'not arrived', qrcode: newDewar.qrcode || 'N/A', - //tracking_number: newDewar.tracking_number?.trim() || `TN-${Date.now()}`, - //ready_date: newDewar.ready_date || 'N/A', - //shipping_date: newDewar.shipping_date || 'N/A', - //arrival_date: newDewar.arrival_date || 'N/A', }; - - // Post to backend - const createdDewar = await createDewar(newDewarToPost, selectedShipment.id); - - // Update state with the response from backend - setNewShipment(prev => ({ - ...prev, - dewars: [...prev.dewars, createdDewar], - })); - - // Reset form fields + await DefaultService.createDewarDewarsPost(newDewarToPost); setIsAddingDewar(false); - //setNewDewar({ dewar_name: '', number_of_pucks: 0, number_of_samples: 0, tracking_number: '' }); + setNewDewar({dewar_name: '', tracking_number: ''}); } catch (error) { alert("Failed to add dewar. Please try again."); console.error("Error adding dewar:", error); @@ -128,49 +86,20 @@ const ShipmentDetails: React.FC = ({ } }; - // Function to generate QR Code (Placeholder) - const generateQRCode = () => { - console.log('Generate QR Code'); - }; - - // Handle adding new contact person and return address - const addNewContactPerson = (name: string) => { - // Implementation to add a new contact person - console.log('Add new contact person:', name); - }; - - const addNewReturnAddress = (address: string) => { - // Implementation to add a new return address - console.log('Add new return address:', address); - }; - - // Function to determine the color of the step icon - const getStepIconColor = (dewar: Dewar) => { - const { status, shippingStatus, arrivalStatus } = dewar; - if (status === 'Ready for Shipping') return 'green'; // Bottle Icon - if (shippingStatus === 'shipped') return 'green'; // Plane Icon - if (shippingStatus === 'not shipped') return 'yellow'; // Plane Icon - if (arrivalStatus === 'arrived') return 'green'; // Store Icon - if (arrivalStatus === 'not arrived') return 'yellow'; // Store Icon - return 'grey'; // Default color - }; - return ( - - {/* Add Dewar Button - only visible if no dewar is selected */} + {!localSelectedDewar && !isAddingDewar && ( )} - {/* Add Dewar Form */} {isAddingDewar && ( - + Add New Dewar = ({ value={newDewar.dewar_name} onChange={handleNewDewarChange} fullWidth - sx={{ marginBottom: 2 }} + sx={{marginBottom: 2}} /> - )} @@ -327,4 +218,4 @@ const ShipmentDetails: React.FC = ({ ); }; -export default ShipmentDetails; +export default ShipmentDetails; \ No newline at end of file diff --git a/frontend/src/components/ShipmentForm.tsx b/frontend/src/components/ShipmentForm.tsx index bd10f94..175a653 100644 --- a/frontend/src/components/ShipmentForm.tsx +++ b/frontend/src/components/ShipmentForm.tsx @@ -19,7 +19,6 @@ const ShipmentForm: React.FC = ({ const [proposals, setProposals] = React.useState([]); const [isCreatingContactPerson, setIsCreatingContactPerson] = React.useState(false); const [isCreatingReturnAddress, setIsCreatingReturnAddress] = React.useState(false); - const [newContactPerson, setNewContactPerson] = React.useState({ firstName: '', lastName: '', @@ -168,7 +167,6 @@ const ShipmentForm: React.FC = ({ try { await DefaultService.createShipmentShipmentsPost(payload); setErrorMessage(null); - // Handle successful save action onCancel(); // close the form after saving } catch (error) { setErrorMessage('Failed to save shipment. Please try again.'); diff --git a/frontend/src/pages/ShipmentView.tsx b/frontend/src/pages/ShipmentView.tsx index d094617..9bcbd75 100644 --- a/frontend/src/pages/ShipmentView.tsx +++ b/frontend/src/pages/ShipmentView.tsx @@ -1,15 +1,16 @@ import React, { useState } from 'react'; -import Grid2 from '@mui/material/Grid2'; +import Grid from '@mui/material/Grid'; // Using Grid (deprecated but configurable) import ShipmentPanel from '../components/ShipmentPanel'; import ShipmentDetails from '../components/ShipmentDetails'; import ShipmentForm from '../components/ShipmentForm'; -import { Shipment_Input } from '../../openapi'; +import { Dewar, Shipment_Input } from '../../openapi'; -type ShipmentViewProps = {}; +type ShipmentViewProps = React.PropsWithChildren>; const ShipmentView: React.FC = () => { const [isCreatingShipment, setIsCreatingShipment] = useState(false); const [selectedShipment, setSelectedShipment] = useState(null); + const [selectedDewar, setSelectedDewar] = useState(null); const handleSelectShipment = (shipment: Shipment_Input | null) => { setSelectedShipment(shipment); @@ -25,17 +26,25 @@ const ShipmentView: React.FC = () => { return ; } if (selectedShipment) { - return ; + return ( + + ); } return
No shipment details available.
; }; return ( - - + = () => { setCreatingShipment={setIsCreatingShipment} selectShipment={handleSelectShipment} /> - - + = () => { }} > {renderShipmentContent()} - - + + ); };