changing contact person and address of a specific dewar is now possible

This commit is contained in:
GotthardG 2024-11-01 12:27:16 +01:00
parent dc31eec66e
commit 579e769bb0
4 changed files with 430 additions and 104 deletions

View File

@ -1,5 +1,6 @@
from fastapi import FastAPI, HTTPException, status
from fastapi import FastAPI, HTTPException, status, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.logger import logger
from pydantic import BaseModel
from typing import List, Optional
import logging
@ -265,7 +266,12 @@ async def get_proposals():
@app.get("/shipments", response_model=List[Shipment])
async def get_shipments():
async def get_shipments(shipment_id: Optional[str] = Query(None, description="ID of the specific shipment to retrieve")):
if shipment_id:
shipment = next((sh for sh in shipments if sh.shipment_id == shipment_id), None)
if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found")
return [shipment]
return shipments
@ -274,21 +280,17 @@ async def delete_shipment(shipment_id: str):
global shipments # Use global variable to access the shipments list
shipments = [shipment for shipment in shipments if shipment.shipment_id != shipment_id]
@app.post("/shipments/{shipment_id}/add_dewar", response_model=Shipment)
async def add_dewar_to_shipment(shipment_id: str, dewar_id: str):
# Log received parameters for debugging
logging.info(f"Received request to add dewar {dewar_id} to shipment {shipment_id}")
# Find the shipment by id
shipment = next((sh for sh in shipments if sh.shipment_id == shipment_id), None)
if not shipment:
logging.error("Shipment not found")
raise HTTPException(status_code=404, detail="Shipment not found")
# Find the dewar by id
dewar = next((dw for dw in dewars if dw.id == dewar_id), None)
if not dewar:
logging.error("Dewar not found")
raise HTTPException(status_code=404, detail="Dewar not found")
# Add the dewar to the shipment
@ -298,6 +300,54 @@ async def add_dewar_to_shipment(shipment_id: str, dewar_id: str):
return shipment
@app.put("/shipments/{shipment_id}", response_model=Shipment)
async def update_shipment(shipment_id: str, updated_shipment: Shipment):
global shipments
shipment = next((sh for sh in shipments if sh.shipment_id == shipment_id), None)
if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found")
logger.info(f"Updating shipment: {shipment_id}")
logger.info(f"Updated shipment data: {updated_shipment}")
# Create a dictionary of existing dewars for fast lookup
existing_dewar_dict = {dewar.id: dewar for dewar in shipment.dewars}
# Update or add dewars from the updated shipment data
for updated_dewar in updated_shipment.dewars:
if updated_dewar.id in existing_dewar_dict:
# Update existing dewar
existing_dewar_dict[updated_dewar.id].dewar_name = updated_dewar.dewar_name
existing_dewar_dict[updated_dewar.id].tracking_number = updated_dewar.tracking_number
existing_dewar_dict[updated_dewar.id].number_of_pucks = updated_dewar.number_of_pucks
existing_dewar_dict[updated_dewar.id].number_of_samples = updated_dewar.number_of_samples
existing_dewar_dict[updated_dewar.id].return_address = updated_dewar.return_address
existing_dewar_dict[updated_dewar.id].contact_person = updated_dewar.contact_person
existing_dewar_dict[updated_dewar.id].status = updated_dewar.status
existing_dewar_dict[updated_dewar.id].ready_date = updated_dewar.ready_date
existing_dewar_dict[updated_dewar.id].shipping_date = updated_dewar.shipping_date
existing_dewar_dict[updated_dewar.id].arrival_date = updated_dewar.arrival_date
existing_dewar_dict[updated_dewar.id].returning_date = updated_dewar.returning_date
existing_dewar_dict[updated_dewar.id].qrcode = updated_dewar.qrcode
else:
# Add new dewar
shipment.dewars.append(updated_dewar)
# Update the shipment's fields
shipment.shipment_name = updated_shipment.shipment_name
shipment.shipment_date = updated_shipment.shipment_date
shipment.shipment_status = updated_shipment.shipment_status
shipment.contact_person = updated_shipment.contact_person
shipment.proposal_number = updated_shipment.proposal_number
shipment.return_address = updated_shipment.return_address
shipment.comments = updated_shipment.comments
logger.info(f"Shipment after update: {shipment}")
return shipment
@app.get("/dewars", response_model=List[Dewar])
async def get_dewars():
return dewars
@ -312,16 +362,13 @@ async def create_dewar(dewar: Dewar) -> Dewar:
return dewar # Return the newly created dewar
@app.delete("/shipments/{shipment_id}/remove_dewar/{dewar_id}", response_model=Shipment)
async def remove_dewar_from_shipment(shipment_id: str, dewar_id: str):
"""Remove a dewar from a shipment."""
# Log parameters
logging.info(f"Received request to remove dewar {dewar_id} from shipment {shipment_id}")
# Find the shipment by ID
shipment = next((sh for sh in shipments if sh.shipment_id == shipment_id), None)
if not shipment:
logging.error(f"Shipment with ID {shipment_id} not found")
raise HTTPException(status_code=404, detail="Shipment not found")
# Remove the dewar from the shipment
@ -350,7 +397,6 @@ async def create_shipment(shipment: Shipment):
# Creation of a new contact
@app.post("/contacts", response_model=ContactPerson, status_code=status.HTTP_201_CREATED)
async def create_contact(contact: ContactPerson):
logging.info(f"Received contact creation request: {contact}")
# Check for duplicate contact by email (or other unique fields)
if any(c.email == contact.email for c in contacts):
raise HTTPException(
@ -372,7 +418,6 @@ async def create_contact(contact: ContactPerson):
# Creation of a return address
@app.post("/return_addresses", response_model=Address, status_code=status.HTTP_201_CREATED)
async def create_return_address(address: Address):
logging.info(f"Received address creation request: {address}")
# Check for duplicate address by city
if any(a.city == address.city for a in return_addresses):
raise HTTPException(
@ -388,4 +433,4 @@ async def create_return_address(address: Address):
address.id = 1 if address.id is None else address.id
return_addresses.append(address)
return address
return address

View File

@ -1,85 +1,271 @@
import * as React from 'react';
import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material';
import React, { useState, useEffect } 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,
DefaultService
} from '../../openapi';
interface DewarDetailsProps {
dewar: Dewar;
trackingNumber: string;
setTrackingNumber: React.Dispatch<React.SetStateAction<string>>;
contactPersons: ContactPerson[];
returnAddresses: Address[];
initialContactPersons: ContactPerson[];
initialReturnAddresses: Address[];
defaultContactPerson?: ContactPerson;
defaultReturnAddress?: Address;
shipmentId: string;
refreshShipments: () => void;
}
const DewarDetails: React.FC<DewarDetailsProps> = ({
dewar,
trackingNumber,
setTrackingNumber,
contactPersons,
returnAddresses
//setTrackingNumber,
initialContactPersons = [],
initialReturnAddresses = [],
defaultContactPerson,
defaultReturnAddress,
shipmentId,
refreshShipments,
}) => {
const [selectedContactPerson, setSelectedContactPerson] = React.useState<string>(contactPersons[0]?.firstname || '');
const [selectedReturnAddress, setSelectedReturnAddress] = React.useState<string>(returnAddresses[0]?.id?.toString() || '');
const [localTrackingNumber, setLocalTrackingNumber] = useState(trackingNumber);
const [contactPersons, setContactPersons] = useState<ContactPerson[]>(initialContactPersons);
const [returnAddresses, setReturnAddresses] = useState<Address[]>(initialReturnAddresses);
const [selectedContactPerson, setSelectedContactPerson] = useState<string>('');
const [selectedReturnAddress, setSelectedReturnAddress] = useState<string>('');
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
const [newContactPerson, setNewContactPerson] = useState({
firstName: '',
lastName: '',
phone_number: '',
email: '',
});
const [newReturnAddress, setNewReturnAddress] = useState<Address>({
street: '',
city: '',
zipcode: '',
country: '',
});
const [changesMade, setChangesMade] = useState<boolean>(false);
const [feedbackMessage, setFeedbackMessage] = useState<string>('');
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
const [updatedDewar, setUpdatedDewar] = useState<Dewar>(dewar);
const updateSelectedDetails = (contactPerson?: { firstname: string }, returnAddress?: Address) => {
if (contactPerson) setSelectedContactPerson(contactPerson.firstname);
if (returnAddress?.id != null) {
setSelectedReturnAddress(returnAddress.id.toString());
useEffect(() => {
setSelectedContactPerson(
(dewar.contact_person?.[0]?.id?.toString() || defaultContactPerson?.id?.toString() || '')
);
setSelectedReturnAddress(
(dewar.return_address?.[0]?.id?.toString() || defaultReturnAddress?.id?.toString() || '')
);
setLocalTrackingNumber(dewar.tracking_number || '');
}, [dewar, defaultContactPerson, defaultReturnAddress]);
useEffect(() => {
console.log('DewarDetails - dewar updated:', dewar);
}, [dewar]);
useEffect(() => {
const getContacts = async () => {
try {
const c: ContactPerson[] = await DefaultService.getContactsContactsGet();
setContactPersons(c);
} catch {
setFeedbackMessage('Failed to load contact persons. Please try again later.');
setOpenSnackbar(true);
}
};
};
React.useEffect(() => {
updateSelectedDetails(contactPersons[0], returnAddresses[0]);
}, [contactPersons, returnAddresses]);
const getReturnAddresses = async () => {
try {
const a: Address[] = await DefaultService.getReturnAddressesReturnAddressesGet();
setReturnAddresses(a);
} catch {
setFeedbackMessage('Failed to load return addresses. Please try again later.');
setOpenSnackbar(true);
}
};
const [newContactPerson, setNewContactPerson] = React.useState<string>('');
const [newReturnAddress, setNewReturnAddress] = React.useState<string>('');
const [feedbackMessage, setFeedbackMessage] = React.useState<string>('');
const [openSnackbar, setOpenSnackbar] = React.useState<boolean>(false);
getContacts();
getReturnAddresses();
}, []);
const validateEmail = (email: string) => /\S+@\S+\.\S+/.test(email);
const validatePhoneNumber = (phone: string) => /^\+?[1-9]\d{1,14}$/.test(phone);
const validateZipCode = (zipcode: string) => /^\d{5}(?:[-\s]\d{4})?$/.test(zipcode);
// Ensure dewar is defined before attempting to render the dewar details
if (!dewar) {
return <Typography>No dewar selected.</Typography>;
}
const handleAddContact = () => {
if (newContactPerson.trim() === '') {
setFeedbackMessage('Please enter a valid contact person name.');
} else {
setNewContactPerson(''); // Add logic to save the new contact person
setFeedbackMessage('Contact person added successfully.');
const handleAddContact = async () => {
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) ||
!newContactPerson.firstName || !newContactPerson.lastName) {
setFeedbackMessage('Please fill in all new contact person fields correctly.');
setOpenSnackbar(true);
return;
}
const payload = {
firstname: newContactPerson.firstName,
lastname: newContactPerson.lastName,
phone_number: newContactPerson.phone_number,
email: newContactPerson.email,
};
try {
const c: ContactPerson = await DefaultService.createContactContactsPost(payload);
setContactPersons([...contactPersons, c]);
setFeedbackMessage('Contact person added successfully.');
setNewContactPerson({ firstName: '', lastName: '', phone_number: '', email: '' });
setSelectedContactPerson(c.id?.toString() || '');
} catch {
setFeedbackMessage('Failed to create a new contact person. Please try again later.');
}
setOpenSnackbar(true);
setIsCreatingContactPerson(false);
setChangesMade(true);
};
const handleAddAddress = () => {
if (newReturnAddress.trim() === '') {
setFeedbackMessage('Please enter a valid return address.');
} else {
setNewReturnAddress(''); // Add logic to save the new return address
const handleAddAddress = async () => {
if (!validateZipCode(newReturnAddress.zipcode) || !newReturnAddress.street || !newReturnAddress.city ||
!newReturnAddress.country) {
setFeedbackMessage('Please fill in all new return address fields correctly.');
setOpenSnackbar(true);
return;
}
const payload = {
street: newReturnAddress.street.trim(),
city: newReturnAddress.city.trim(),
zipcode: newReturnAddress.zipcode.trim(),
country: newReturnAddress.country.trim(),
};
try {
const a: Address = await DefaultService.createReturnAddressReturnAddressesPost(payload);
setReturnAddresses([...returnAddresses, a]);
setFeedbackMessage('Return address added successfully.');
setNewReturnAddress({ street: '', city: '', zipcode: '', country: '' });
setSelectedReturnAddress(a.id?.toString() || '');
} catch {
setFeedbackMessage('Failed to create a new return address. Please try again later.');
}
setOpenSnackbar(true);
setIsCreatingReturnAddress(false);
setChangesMade(true);
};
const getShipmentById = async (shipmentId: string) => {
try {
const response = await DefaultService.getShipmentsShipmentsGet(shipmentId);
if (response && response.length > 0) {
return response[0]; // Since the result is an array, we take the first element
}
throw new Error('Shipment not found');
} catch (error) {
console.error('Error fetching shipment:', error);
throw error;
}
};
const handleSaveChanges = async () => {
const formatDate = (dateString: string | undefined): string => {
if (!dateString) return '2024-01-01'; // Default date if undefined
const date = new Date(dateString);
if (isNaN(date.getTime())) return '2024-01-01'; // Default date if invalid
return date.toISOString().split('T')[0];
};
if (!dewar.dewar_name || !selectedContactPerson || !selectedReturnAddress || !trackingNumber) {
setFeedbackMessage('Please ensure all required fields are filled.');
setOpenSnackbar(true);
return;
}
let existingShipment;
try {
existingShipment = await getShipmentById(shipmentId);
} catch {
setFeedbackMessage('Failed to fetch existing shipment data. Please try again later.');
setOpenSnackbar(true);
return;
}
const updatedDewar = {
id: dewar.id, // Ensure dewar ID is included
dewar_name: dewar.dewar_name,
return_address: returnAddresses.find((a) => a.id?.toString() === selectedReturnAddress)
? [returnAddresses.find((a) => a.id?.toString() === selectedReturnAddress)]
: [],
contact_person: contactPersons.find((c) => c.id?.toString() === selectedContactPerson)
? [contactPersons.find((c) => c.id?.toString() === selectedContactPerson)]
: [],
number_of_pucks: dewar.number_of_pucks,
number_of_samples: dewar.number_of_samples,
qrcode: dewar.qrcode,
ready_date: formatDate(dewar.ready_date),
shipping_date: formatDate(dewar.shipping_date),
status: dewar.status,
tracking_number: trackingNumber,
};
const payload = {
...existingShipment,
dewars: existingShipment.dewars?.map(d => d.id === dewar.id ? updatedDewar : d) || [], // Update specific dewar in the dewars array
};
try {
await DefaultService.updateShipmentShipmentsShipmentIdPut(shipmentId, payload);
setFeedbackMessage('Changes saved successfully.');
setChangesMade(false);
setUpdatedDewar(updatedDewar);
console.log('Calling refreshShipments');
refreshShipments(); // Trigger refresh shipments after saving changes
} catch (error) {
if (error.response) {
console.error('Server Response:', error.response.data);
} else {
console.error('Update Shipment Error:', error);
setFeedbackMessage('Failed to save changes. Please try again later.');
}
setOpenSnackbar(true);
return;
}
setOpenSnackbar(true);
};
return (
<Box sx={{ marginTop: 2 }}>
<Typography variant="h6">Selected Dewar: {dewar.dewar_name}</Typography>
<Typography variant="h6">Selected Dewar: {updatedDewar.dewar_name}</Typography>
<TextField
label="Tracking Number"
value={trackingNumber}
onChange={(e) => setTrackingNumber(e.target.value)}
value={localTrackingNumber}
onChange={(e) => {
setLocalTrackingNumber(e.target.value);
setChangesMade(true);
}}
variant="outlined"
sx={{ width: '300px', marginBottom: 2 }}
/>
{/* QR Code display */}
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
<Box sx={{ width: 80, height: 80, backgroundColor: '#e0e0e0', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{dewar.qrcode ? (
<QRCode value={dewar.qrcode} size={70} />
{updatedDewar.qrcode ? (
<QRCode value={updatedDewar.qrcode} size={70} />
) : (
<Typography>No QR code available</Typography>
)}
@ -88,74 +274,137 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
Generate QR Code
</Button>
</Box>
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
{/* Dropdown for Contact Person */}
<Typography variant="body1">Number of Pucks: {updatedDewar.number_of_pucks}</Typography>
<Typography variant="body1">Number of Samples: {updatedDewar.number_of_samples}</Typography>
<Typography variant="body1">Current Contact Person:</Typography>
<Select
value={selectedContactPerson}
onChange={(e) => setSelectedContactPerson(e.target.value)}
displayEmpty
onChange={(e) => {
setSelectedContactPerson(e.target.value);
setIsCreatingContactPerson(e.target.value === 'add');
setChangesMade(true);
}}
fullWidth
sx={{ marginBottom: 2 }}
variant={'outlined'}
variant="outlined"
displayEmpty
>
<MenuItem value="" disabled>Select Contact Person</MenuItem>
{contactPersons.map((person) => (
<MenuItem key={person.id} value={person.firstname}>{person.firstname + " " + person.lastname}</MenuItem>
{contactPersons?.map((person) => (
<MenuItem key={person.id?.toString()} value={person.id?.toString() || ''}>
{person.firstname} {person.lastname}
</MenuItem>
))}
<MenuItem value="add">Add New Contact Person</MenuItem>
</Select>
{selectedContactPerson === "add" && (
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
{isCreatingContactPerson && (
<Box sx={{ marginBottom: 2 }}>
<TextField
label="New Contact Person"
value={newContactPerson}
onChange={(e) => setNewContactPerson(e.target.value)}
label="First Name"
value={newContactPerson.firstName}
onChange={(e) => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
variant="outlined"
sx={{ marginRight: 1, flexGrow: 1 }}
fullWidth
sx={{ marginBottom: 1 }}
/>
<TextField
label="Last Name"
value={newContactPerson.lastName}
onChange={(e) => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
/>
<TextField
label="Phone"
value={newContactPerson.phone_number}
onChange={(e) => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
error={!validatePhoneNumber(newContactPerson.phone_number)}
helperText={!validatePhoneNumber(newContactPerson.phone_number) ? "Invalid phone number" : ""}
/>
<TextField
label="Email"
value={newContactPerson.email}
onChange={(e) => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
error={!validateEmail(newContactPerson.email)}
helperText={!validateEmail(newContactPerson.email) ? "Invalid email" : ""}
/>
<Button variant="contained" onClick={handleAddContact}>
Add
Save Contact Person
</Button>
</Box>
)}
{/* Dropdown for Return Address */}
<Typography variant="body1">Current Return Address:</Typography>
<Select
value={selectedReturnAddress}
onChange={(e) => setSelectedReturnAddress(e.target.value)}
displayEmpty
onChange={(e) => {
setSelectedReturnAddress(e.target.value);
setIsCreatingReturnAddress(e.target.value === 'add');
setChangesMade(true);
}}
fullWidth
sx={{ marginBottom: 2 }}
variant={'outlined'}
variant="outlined"
displayEmpty
>
<MenuItem value="" disabled>Select Return Address</MenuItem>
{returnAddresses.map((address) => (
<MenuItem key={address.id ?? 'unknown'} value={address.id?.toString() ?? 'unknown'}>
{address.street} </MenuItem>
{returnAddresses?.map((address) => (
<MenuItem key={address.id?.toString()} value={address.id?.toString() || ''}>
{address.street}, {address.city}
</MenuItem>
))}
<MenuItem value="add">Add New Return Address</MenuItem>
</Select>
{selectedReturnAddress === "add" && (
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
{isCreatingReturnAddress && (
<Box sx={{ marginBottom: 2 }}>
<TextField
label="New Return Address"
value={newReturnAddress}
onChange={(e) => setNewReturnAddress(e.target.value)}
label="Street"
value={newReturnAddress.street}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
variant="outlined"
sx={{ marginRight: 1, flexGrow: 1 }}
fullWidth
sx={{ marginBottom: 1 }}
/>
<TextField
label="City"
value={newReturnAddress.city}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
/>
<TextField
label="Zip Code"
value={newReturnAddress.zipcode}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
error={!validateZipCode(newReturnAddress.zipcode)}
helperText={!validateZipCode(newReturnAddress.zipcode) ? "Invalid zip code" : ""}
/>
<TextField
label="Country"
value={newReturnAddress.country}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
/>
<Button variant="contained" onClick={handleAddAddress}>
Add
Save Return Address
</Button>
</Box>
)}
{/* Snackbar for feedback messages */}
{changesMade && (
<Button variant="contained" color="primary" onClick={handleSaveChanges}>
Save Changes
</Button>
)}
<Snackbar
open={openSnackbar}
autoHideDuration={6000}

View File

@ -14,6 +14,7 @@ interface ShipmentDetailsProps {
setSelectedDewar: React.Dispatch<React.SetStateAction<Dewar | null>>;
setSelectedShipment: React.Dispatch<React.SetStateAction<Shipment_Input>>;
sx?: SxProps;
refreshShipments: () => void;
}
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
@ -21,12 +22,12 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
setSelectedDewar,
setSelectedShipment,
sx = {},
refreshShipments,
}) => {
const [localSelectedDewar, setLocalSelectedDewar] = React.useState<Dewar | null>(null);
const [isAddingDewar, setIsAddingDewar] = React.useState<boolean>(false);
const [newDewar, setNewDewar] = React.useState<Partial<Dewar>>({
dewar_name: '',
tracking_number: '',
});
// To reset localSelectedDewar when selectedShipment changes
@ -34,6 +35,10 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
setLocalSelectedDewar(null);
}, [selectedShipment]);
React.useEffect(() => {
console.log('ShipmentDetails - selectedShipment updated:', selectedShipment);
}, [selectedShipment]);
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);
@ -111,6 +116,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
setIsAddingDewar(false);
setNewDewar({ dewar_name: '', tracking_number: '' });
refreshShipments()
} catch (error) {
alert('Failed to add dewar or update shipment. Please try again.');
@ -164,10 +170,14 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
dewar={localSelectedDewar}
trackingNumber={localSelectedDewar.tracking_number || ''}
setTrackingNumber={(value) => {
setLocalSelectedDewar((prev) => prev ? { ...prev, tracking_number: value as string } : prev);
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
}}
contactPersons={selectedShipment.contact_person}
returnAddresses={selectedShipment.return_address}
initialContactPersons={selectedShipment.contact_person}
initialReturnAddresses={selectedShipment.return_address}
defaultContactPerson={selectedShipment.contact_person[0]}
defaultReturnAddress={selectedShipment.return_address[0]}
shipmentId={selectedShipment.shipment_id}
refreshShipments={refreshShipments}
/>
)}
@ -220,6 +230,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
<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[0].firstname} ${dewar.contact_person[0].lastname}`}</Typography>
</Box>
<Box sx={{

View File

@ -1,9 +1,9 @@
import React, { useState, useEffect } from 'react';
import Grid from '@mui/material/Grid'; // Using Grid (deprecated but configurable)
import Grid from '@mui/material/Grid';
import ShipmentPanel from '../components/ShipmentPanel';
import ShipmentDetails from '../components/ShipmentDetails';
import ShipmentForm from '../components/ShipmentForm';
import { Dewar, Shipment_Input, DefaultService, OpenAPI } from '../../openapi';
import { Dewar, Shipment_Input, DefaultService, OpenAPI, ContactPerson } from '../../openapi';
type ShipmentViewProps = React.PropsWithChildren<Record<string, never>>;
@ -16,21 +16,38 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
const [shipments, setShipments] = useState<Shipment_Input[]>([]);
const [error, setError] = useState<string | null>(null);
const [defaultContactPerson, setDefaultContactPerson] = useState<ContactPerson | undefined>();
const fetchAndSetShipments = async () => {
try {
const shipmentsData: Shipment_Input[] = await DefaultService.getShipmentsShipmentsGet();
shipmentsData.sort((a, b) => new Date(b.shipment_date).getTime() - new Date(a.shipment_date).getTime());
setShipments(shipmentsData);
console.log('Fetched and set shipments:', shipmentsData);
} catch (error) {
console.error('Failed to fetch shipments:', error);
setError('Failed to fetch shipments. Please try again later.');
}
};
const fetchDefaultContactPerson = async () => {
try {
const c: ContactPerson[] = await DefaultService.getContactsContactsGet();
setDefaultContactPerson(c[0]);
} catch {
setError('Failed to load contact persons. Please try again later.');
}
};
useEffect(() => {
fetchAndSetShipments();
fetchDefaultContactPerson();
}, []);
useEffect(() => {
console.log('Updated shipments:', shipments);
}, [shipments]);
const handleSelectShipment = (shipment: Shipment_Input | null) => {
setSelectedShipment(shipment);
setIsCreatingShipment(false);
@ -42,11 +59,13 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
const renderShipmentContent = () => {
if (isCreatingShipment) {
return <ShipmentForm
sx={{ flexGrow: 1 }}
onCancel={handleCancelShipmentForm}
refreshShipments={fetchAndSetShipments} // Pass the fetch function to refresh shipments
/>;
return (
<ShipmentForm
sx={{ flexGrow: 1 }}
onCancel={handleCancelShipmentForm}
refreshShipments={fetchAndSetShipments}
/>
);
}
if (selectedShipment) {
return (
@ -57,6 +76,8 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
selectedDewar={selectedDewar}
setSelectedDewar={setSelectedDewar}
setSelectedShipment={setSelectedShipment}
defaultContactPerson={defaultContactPerson}
refreshShipments={fetchAndSetShipments} // Ensure refreshShipments is passed here
/>
);
}