added pucks and samples

This commit is contained in:
GotthardG 2024-11-04 16:20:53 +01:00
parent 23e7ebb819
commit 9fa499a582
9 changed files with 189 additions and 173 deletions

View File

@ -1,20 +1,36 @@
import logging
from sqlalchemy.orm import Session, joinedload
from app.models import Shipment
def get_shipments(db: Session):
from app.models import Shipment
return db.query(Shipment).options(
logging.info("Fetching all shipments from the database.")
shipments = db.query(Shipment).options(
joinedload(Shipment.contact_person),
joinedload(Shipment.return_address),
joinedload(Shipment.proposal),
joinedload(Shipment.dewars)
).all()
logging.info(f"Total of {len(shipments)} shipments fetched.")
for shipment in shipments:
if shipment.proposal_id is None:
logging.warning(f"Shipment {shipment.shipment_id} is missing proposal ID.")
logging.debug(f"Shipment ID: {shipment.shipment_id}, Shipment Name: {shipment.shipment_name}")
return shipments
def get_shipment_by_id(db: Session, shipment_id: str):
from app.models import Shipment
logging.info(f"Fetching shipment with ID: {shipment_id}")
shipment = db.query(Shipment).options(
joinedload(Shipment.contact_person),
joinedload(Shipment.return_address),
joinedload(Shipment.proposal),
joinedload(Shipment.dewars)
).filter(Shipment.shipment_id == shipment_id).first()
if shipment:
if shipment.proposal_id is None:
logging.warning(f"Shipment {shipment.shipment_id} is missing proposal ID.")
logging.info(f"Shipment found: {shipment}")
else:
logging.warning(f"Shipment with ID {shipment_id} not found.")
return shipment

View File

@ -14,7 +14,7 @@ class Shipment(Base):
comments = Column(String, nullable=True)
contact_person_id = Column(Integer, ForeignKey("contact_persons.id"))
return_address_id = Column(Integer, ForeignKey("addresses.id"))
proposal_id = Column(Integer, ForeignKey("proposals.id"))
proposal_id = Column(Integer, ForeignKey('proposals.id'), nullable=True)
contact_person = relationship("ContactPerson", back_populates="shipments")
return_address = relationship("Address", back_populates="shipments")
@ -97,6 +97,7 @@ class Puck(Base):
positions = relationship("Sample", back_populates="puck")
dewar = relationship("Dewar", back_populates="pucks")
class Sample(Base):
__tablename__ = 'samples'

View File

@ -11,7 +11,8 @@ router = APIRouter()
@router.get("/", response_model=List[DewarSchema])
async def get_dewars(db: Session = Depends(get_db)):
return db.query(DewarModel).all()
dewars = db.query(DewarModel).options(joinedload(DewarModel.pucks)).all()
return dewars
@router.post("/", response_model=DewarSchema, status_code=status.HTTP_201_CREATED)
@ -22,8 +23,6 @@ async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> Dew
id=dewar_id,
dewar_name=dewar.dewar_name,
tracking_number=dewar.tracking_number,
number_of_pucks=dewar.number_of_pucks,
number_of_samples=dewar.number_of_samples,
status=dewar.status,
ready_date=dewar.ready_date,
shipping_date=dewar.shipping_date,
@ -37,6 +36,7 @@ async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> Dew
db.add(db_dewar)
db.commit()
db.refresh(db_dewar)
return db_dewar
@ -49,7 +49,12 @@ async def get_dewar(dewar_id: str, db: Session = Depends(get_db)):
if not dewar:
raise HTTPException(status_code=404, detail="Dewar not found")
return dewar
# Ensure dewar.pucks is an empty list if there are no pucks
dewar_dict = dewar.__dict__
if dewar_dict.get("pucks") is None:
dewar_dict["pucks"] = []
return DewarSchema.from_orm(dewar)
@router.put("/{dewar_id}", response_model=DewarSchema)
@ -60,7 +65,9 @@ async def update_dewar(dewar_id: str, dewar_update: DewarUpdate, db: Session = D
raise HTTPException(status_code=404, detail="Dewar not found")
for key, value in dewar_update.dict(exclude_unset=True).items():
setattr(dewar, key, value)
# Ensure we're only setting directly settable attributes
if hasattr(dewar, key):
setattr(dewar, key, value)
db.commit()
db.refresh(dewar)

View File

@ -4,9 +4,10 @@ from typing import List, Optional
import uuid
import json
from datetime import date
import logging
from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, Proposal as ProposalModel, Dewar as DewarModel
from app.schemas import ShipmentCreate, Shipment as ShipmentSchema, DewarUpdate, ContactPerson as ContactPersonSchema
from app.schemas import ShipmentCreate, UpdateShipmentComments, Shipment as ShipmentSchema, DewarUpdate, ContactPerson as ContactPersonSchema
from app.schemas import Sample as SampleSchema
from app.database import get_db
from app.crud import get_shipments, get_shipment_by_id
@ -23,9 +24,16 @@ async def fetch_shipments(shipment_id: Optional[str] = Query(None), db: Session
if shipment_id:
shipment = get_shipment_by_id(db, shipment_id)
if not shipment:
logging.error(f"Shipment with ID {shipment_id} not found")
raise HTTPException(status_code=404, detail="Shipment not found")
logging.info(f"Shipment found: {shipment}")
return [shipment]
return get_shipments(db)
shipments = get_shipments(db)
logging.info(f"Total shipments fetched: {len(shipments)}")
for shipment in shipments:
logging.info(f"Shipment ID: {shipment.shipment_id}, Shipment Name: {shipment.shipment_name}")
return shipments
@router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)):
@ -80,7 +88,8 @@ async def update_shipment(shipment_id: str, updated_shipment: ShipmentCreate, db
raise HTTPException(status_code=404, detail="Shipment not found")
# Validate relationships by IDs
contact_person = db.query(ContactPersonModel).filter(ContactPersonModel.id == updated_shipment.contact_person_id).first()
contact_person = db.query(ContactPersonModel).filter(
ContactPersonModel.id == updated_shipment.contact_person_id).first()
return_address = db.query(AddressModel).filter(AddressModel.id == updated_shipment.return_address_id).first()
if not contact_person:
raise HTTPException(status_code=404, detail="Contact person not found")
@ -101,17 +110,18 @@ async def update_shipment(shipment_id: str, updated_shipment: ShipmentCreate, db
if not dewar:
raise HTTPException(status_code=404, detail=f"Dewar with ID {dewar_data.dewar_id} not found")
# Dynamically update the dewar fields based on provided input
update_fields = dewar_data.dict(exclude_unset=True)
for key, value in update_fields.items():
if key == 'contact_person_id':
contact_person = db.query(ContactPersonModel).filter(ContactPersonModel.id == value).first()
if not contact_person:
raise HTTPException(status_code=404, detail=f"Contact person with ID {value} for Dewar {dewar_data.dewar_id} not found")
raise HTTPException(status_code=404,
detail=f"Contact person with ID {value} for Dewar {dewar_data.dewar_id} not found")
if key == 'return_address_id':
address = db.query(AddressModel).filter(AddressModel.id == value).first()
if not address:
raise HTTPException(status_code=404, detail=f"Address with ID {value} for Dewar {dewar_data.dewar_id} not found")
raise HTTPException(status_code=404,
detail=f"Address with ID {value} for Dewar {dewar_data.dewar_id} not found")
for key, value in update_fields.items():
if key != 'dewar_id':
@ -121,6 +131,7 @@ async def update_shipment(shipment_id: str, updated_shipment: ShipmentCreate, db
db.refresh(shipment)
return shipment
@router.post("/{shipment_id}/add_dewar", response_model=ShipmentSchema)
async def add_dewar_to_shipment(shipment_id: str, dewar_id: str, db: Session = Depends(get_db)):
shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first()
@ -179,4 +190,16 @@ def get_samples_in_dewar(shipment_id: str, dewar_id: str, db: Session = Depends(
for puck in dewar.pucks:
samples.extend(puck.positions)
return samples
return samples
@router.put("/{shipment_id}/comments", response_model=ShipmentSchema)
async def update_shipment_comments(shipment_id: str, comments_data: UpdateShipmentComments, db: Session = Depends(get_db)):
shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first()
if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found")
shipment.comments = comments_data.comments
db.commit()
db.refresh(shipment)
return shipment

View File

@ -107,8 +107,6 @@ class Dewar(DewarBase):
class DewarUpdate(BaseModel):
dewar_name: Optional[str] = None
tracking_number: Optional[str] = None
number_of_pucks: Optional[int] = None
number_of_samples: Optional[int] = None
status: Optional[str] = None
ready_date: Optional[date] = None
shipping_date: Optional[date] = None
@ -156,3 +154,7 @@ class ShipmentCreate(BaseModel):
class Config:
from_attributes = True
class UpdateShipmentComments(BaseModel):
comments: str

Binary file not shown.

View File

@ -1,20 +1,20 @@
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, ContactsService, AddressesService, ShipmentsService, Puck, Sample } from '../../openapi';
import { ContactPerson, Address, Dewar, ContactsService, AddressesService, DewarsService, ShipmentsService } from '../../openapi';
import Unipuck from '../components/Unipuck';
interface DewarDetailsProps {
dewar: Dewar;
trackingNumber: string;
setTrackingNumber: React.Dispatch<React.SetStateAction<string>>;
initialContactPersons: ContactPerson[];
initialReturnAddresses: Address[];
setTrackingNumber: (trackingNumber: string) => void;
initialContactPersons?: ContactPerson[];
initialReturnAddresses?: Address[];
defaultContactPerson?: ContactPerson;
defaultReturnAddress?: Address;
shipmentId: string;
refreshShipments: () => void;
selectedShipment: any;
selectedShipment?: any;
}
const DewarDetails: React.FC<DewarDetailsProps> = ({
@ -27,61 +27,48 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
defaultReturnAddress,
shipmentId,
refreshShipments,
selectedShipment
selectedShipment,
}) => {
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 [contactPersons, setContactPersons] = useState(initialContactPersons);
const [returnAddresses, setReturnAddresses] = useState(initialReturnAddresses);
const [selectedContactPerson, setSelectedContactPerson] = useState('');
const [selectedReturnAddress, setSelectedReturnAddress] = useState('');
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
const [puckStatuses, setPuckStatuses] = useState<string[][]>(dewar.pucks.map(() => Array(16).fill('empty')));
const [newContactPerson, setNewContactPerson] = useState({
id: 0,
firstName: '',
lastName: '',
phone_number: '',
email: '',
});
const [newReturnAddress, setNewReturnAddress] = useState<Address>({
id: 0,
street: '',
city: '',
zipcode: '',
country: '',
});
const [changesMade, setChangesMade] = useState<boolean>(false);
const [feedbackMessage, setFeedbackMessage] = useState<string>('');
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
const [newContactPerson, setNewContactPerson] = useState({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
const [newReturnAddress, setNewReturnAddress] = useState({ id: 0, street: '', city: '', zipcode: '', country: '' });
const [changesMade, setChangesMade] = useState(false);
const [feedbackMessage, setFeedbackMessage] = useState('');
const [openSnackbar, setOpenSnackbar] = useState(false);
useEffect(() => {
const setInitialContactPerson = () => {
const contactPersonId =
selectedShipment?.contact_person?.id?.toString() ||
setSelectedContactPerson(
dewar.contact_person?.id?.toString() ||
defaultContactPerson?.id?.toString() ||
'';
setSelectedContactPerson(contactPersonId);
''
);
};
const setInitialReturnAddress = () => {
const returnAddressId =
setSelectedReturnAddress(
dewar.return_address?.id?.toString() ||
defaultReturnAddress?.id?.toString() ||
'';
setSelectedReturnAddress(returnAddressId);
''
);
};
setLocalTrackingNumber(dewar.tracking_number || '');
setInitialContactPerson();
setInitialReturnAddress();
}, [dewar, defaultContactPerson, defaultReturnAddress, selectedShipment]);
}, [dewar, defaultContactPerson, defaultReturnAddress]);
useEffect(() => {
const getContacts = async () => {
try {
const c: ContactPerson[] = await ContactsService.getContactsContactsGet();
const c = await ContactsService.getContactsContactsGet();
setContactPersons(c);
} catch {
setFeedbackMessage('Failed to load contact persons. Please try again later.');
@ -91,7 +78,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
const getReturnAddresses = async () => {
try {
const a: Address[] = await AddressesService.getReturnAddressesAddressesGet();
const a = await AddressesService.getReturnAddressesAddressesGet();
setReturnAddresses(a);
} catch {
setFeedbackMessage('Failed to load return addresses. Please try again later.');
@ -105,17 +92,20 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
useEffect(() => {
const fetchSamples = async () => {
if (dewar.id) {
if (dewar.id && Array.isArray(dewar.pucks)) {
try {
const samples: Sample[] = await ShipmentsService.getSamplesInDewarShipmentsShipmentIdDewarsDewarIdSamplesGet(shipmentId, dewar.id);
const samples = await ShipmentsService.getSamplesInDewarShipmentsShipmentIdDewarsDewarIdSamplesGet(shipmentId, dewar.id);
const updatedPuckStatuses = dewar.pucks.map(puck => {
if (!Array.isArray(puck.positions)) return [];
return puck.positions.map(position => {
const isOccupied = samples.some(sample => sample.id === position.id);
return isOccupied ? 'filled' : 'empty';
});
});
setPuckStatuses(updatedPuckStatuses);
} catch {
} catch (error) {
setFeedbackMessage('Failed to load samples. Please try again later.');
setOpenSnackbar(true);
}
@ -129,9 +119,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
const validatePhoneNumber = (phone: string) => /^\+?[1-9]\d{1,14}$/.test(phone);
const validateZipCode = (zipcode: string) => /^\d{5}(?:[-\s]\d{4})?$/.test(zipcode);
if (!dewar) {
return <Typography>No dewar selected.</Typography>;
}
if (!dewar) return <Typography>No dewar selected.</Typography>;
const handleAddContact = async () => {
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) ||
@ -149,7 +137,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
};
try {
const c: ContactPerson = await ContactsService.createContactContactsPost(payload);
const c = await ContactsService.createContactContactsPost(payload);
setContactPersons([...contactPersons, c]);
setFeedbackMessage('Contact person added successfully.');
setNewContactPerson({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
@ -178,7 +166,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
};
try {
const a: Address = await AddressesService.createReturnAddressAddressesPost(payload);
const a = await AddressesService.createReturnAddressAddressesPost(payload);
setReturnAddresses([...returnAddresses, a]);
setFeedbackMessage('Return address added successfully.');
setNewReturnAddress({ id: 0, street: '', city: '', zipcode: '', country: '' });
@ -192,20 +180,8 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
setChangesMade(true);
};
const getShipmentById = async (shipmentId: string) => {
try {
const response = await ShipmentsService.fetchShipmentsShipmentsGet(shipmentId);
if (response && response.length > 0) {
return response[0];
}
throw new Error('Shipment not found');
} catch (error) {
throw error;
}
};
const handleSaveChanges = async () => {
const formatDate = (dateString: string | undefined): string | null => {
const formatDate = (dateString: string) => {
if (!dateString) return null;
const date = new Date(dateString);
if (isNaN(date.getTime())) return null;
@ -213,59 +189,42 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
};
if (!selectedContactPerson || !selectedReturnAddress) {
setFeedbackMessage('Please ensure all required fields are filled.');
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.');
const dewarId = dewar.id;
if (!dewarId) {
setFeedbackMessage("Invalid Dewar ID. Please ensure Dewar ID is provided.");
setOpenSnackbar(true);
return;
}
const updatedDewar = {
dewar_id: dewar.id,
dewar_name: dewar.dewar_name,
tracking_number: localTrackingNumber,
number_of_pucks: dewar.number_of_pucks,
number_of_samples: dewar.number_of_samples,
status: dewar.status,
ready_date: formatDate(dewar.ready_date ?? undefined),
shipping_date: formatDate(dewar.shipping_date ?? undefined),
arrival_date: dewar.arrival_date,
returning_date: dewar.returning_date,
qrcode: dewar.qrcode,
return_address_id: selectedReturnAddress,
contact_person_id: selectedContactPerson,
};
const payload = {
shipment_id: existingShipment.shipment_id,
shipment_name: existingShipment.shipment_name,
shipment_date: existingShipment.shipment_date,
shipment_status: existingShipment.shipment_status,
comments: existingShipment.comments,
contact_person_id: existingShipment.contact_person.id,
return_address_id: selectedReturnAddress,
proposal_id: existingShipment.proposal?.id,
dewars: [updatedDewar],
};
try {
await ShipmentsService.updateShipmentShipmentsShipmentIdPut(shipmentId, payload);
setFeedbackMessage('Changes saved successfully.');
const payload = {
dewar_id: dewarId,
dewar_name: dewar.dewar_name,
tracking_number: localTrackingNumber,
number_of_pucks: dewar.number_of_pucks,
number_of_samples: dewar.number_of_samples,
status: dewar.status,
ready_date: formatDate(dewar.ready_date),
shipping_date: formatDate(dewar.shipping_date),
arrival_date: dewar.arrival_date,
returning_date: dewar.returning_date,
qrcode: dewar.qrcode,
return_address_id: selectedReturnAddress,
contact_person_id: selectedContactPerson,
};
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
setFeedbackMessage("Changes saved successfully.");
setChangesMade(false);
refreshShipments();
} catch (error: any) {
if (error.response && error.response.data) {
setFeedbackMessage(`Failed to save shipment. Validation errors: ${JSON.stringify(error.response.data)}`);
} else {
setFeedbackMessage('Failed to save changes. Please try again later.');
}
} catch (error) {
setFeedbackMessage("Failed to save changes. Please try again later.");
setOpenSnackbar(true);
}
};
@ -276,7 +235,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Tracking Number"
value={localTrackingNumber}
onChange={(e) => {
onChange={e => {
setLocalTrackingNumber(e.target.value);
setTrackingNumber(e.target.value);
setChangesMade(true);
@ -296,23 +255,15 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
</Button>
</Box>
</Box>
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
<Box sx={{ marginTop: 2 }}>
{/* Other inputs and elements */}
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
{/* Here we integrate the Unipuck component with puck data */}
{puckStatuses && <Unipuck pucks={puckStatuses.length} samples={puckStatuses} />}
{dewar.number_of_pucks > 0 ? <Unipuck pucks={dewar.number_of_pucks} samples={puckStatuses} /> : <Typography>No pucks attached to the dewar.</Typography>}
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
{/* Rest of DewarDetails component */}
</Box>
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
<Typography variant="body1">Current Contact Person:</Typography>
<Select
value={selectedContactPerson}
onChange={(e) => {
onChange={e => {
const value = e.target.value;
setSelectedContactPerson(value);
setIsCreatingContactPerson(value === 'add');
@ -323,7 +274,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
variant="outlined"
displayEmpty
>
{Array.isArray(contactPersons) && contactPersons.map((person) => (
{contactPersons.map(person => (
<MenuItem key={person.id?.toString()} value={person.id?.toString() || ''}>
{person.firstname} {person.lastname}
</MenuItem>
@ -335,7 +286,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="First Name"
value={newContactPerson.firstName}
onChange={(e) => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
onChange={e => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -343,7 +294,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Last Name"
value={newContactPerson.lastName}
onChange={(e) => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
onChange={e => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -351,7 +302,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Phone"
value={newContactPerson.phone_number}
onChange={(e) => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
onChange={e => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -361,7 +312,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Email"
value={newContactPerson.email}
onChange={(e) => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
onChange={e => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -376,7 +327,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<Typography variant="body1">Current Return Address:</Typography>
<Select
value={selectedReturnAddress}
onChange={(e) => {
onChange={e => {
const value = e.target.value;
setSelectedReturnAddress(value);
setIsCreatingReturnAddress(value === 'add');
@ -387,7 +338,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
variant="outlined"
displayEmpty
>
{Array.isArray(returnAddresses) && returnAddresses.map((address) => (
{returnAddresses.map(address => (
<MenuItem key={address.id?.toString()} value={address.id?.toString() || ''}>
{address.street}, {address.city}
</MenuItem>
@ -399,7 +350,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Street"
value={newReturnAddress.street}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
onChange={e => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -407,7 +358,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="City"
value={newReturnAddress.city}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
onChange={e => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -415,7 +366,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Zip Code"
value={newReturnAddress.zipcode}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
onChange={e => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -425,7 +376,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<TextField
label="Country"
value={newReturnAddress.country}
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
onChange={e => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
variant="outlined"
fullWidth
sx={{ marginBottom: 1 }}
@ -436,7 +387,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
</Box>
)}
{changesMade && (
<Button variant="contained" color="primary" onClick={handleSaveChanges}>
<Button variant="contained" color="primary" onClick={handleSaveChanges} sx={{ marginTop: 2 }}>
Save Changes
</Button>
)}

View File

@ -44,13 +44,21 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
shipping_date: null,
arrival_date: null,
returning_date: null,
qrcode: 'N/A'
qrcode: 'N/A',
contact_person_id: selectedShipment?.contact_person?.id,
return_address_id: selectedShipment?.return_address?.id,
};
const [newDewar, setNewDewar] = useState<Partial<Dewar>>(initialNewDewarState);
useEffect(() => {
setLocalSelectedDewar(null);
// Ensure to update the default contact person and return address when the shipment changes
setNewDewar((prev) => ({
...prev,
contact_person_id: selectedShipment?.contact_person?.id,
return_address_id: selectedShipment?.return_address?.id
}));
}, [selectedShipment]);
useEffect(() => {
@ -97,12 +105,14 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
...initialNewDewarState,
...newDewar,
dewar_name: newDewar.dewar_name.trim(),
contact_person: selectedShipment?.contact_person,
contact_person_id: selectedShipment?.contact_person?.id,
return_address: selectedShipment?.return_address,
return_address_id: selectedShipment?.return_address?.id,
return_address_id: selectedShipment?.return_address?.id
} as Dewar;
if (!newDewarToPost.dewar_name || !newDewarToPost.status) {
throw new Error('Missing required fields');
}
const createdDewar = await DewarsService.createDewarDewarsPost(newDewarToPost);
if (createdDewar && selectedShipment) {
@ -127,27 +137,14 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
};
const handleSaveComments = async () => {
if (selectedShipment) {
if (selectedShipment && selectedShipment.shipment_id) {
try {
const updatedShipmentPayload = {
shipment_id: selectedShipment.shipment_id,
shipment_name: selectedShipment.shipment_name,
shipment_date: selectedShipment.shipment_date,
shipment_status: selectedShipment.shipment_status,
comments: comments,
contact_person_id: selectedShipment.contact_person?.id,
return_address_id: selectedShipment.return_address?.id,
proposal_id: selectedShipment.proposal?.id,
dewars: selectedShipment.dewars?.map(dewar => ({
...dewar,
dewar_id: dewar.id,
contact_person_id: dewar.contact_person?.id,
return_address_id: dewar.return_address?.id
}))
};
const payload = { comments };
const updatedShipment = await ShipmentsService.updateShipmentShipmentsShipmentIdPut(selectedShipment.shipment_id, updatedShipmentPayload);
setSelectedShipment(updatedShipment);
// Assuming `updateShipmentCommentsShipmentsShipmentIdCommentsPut` only needs the shipment ID
const updatedShipment = await ShipmentsService.updateShipmentCommentsShipmentsShipmentIdCommentsPut(selectedShipment.shipment_id, payload);
setSelectedShipment({ ...selectedShipment, comments: updatedShipment.comments });
setInitialComments(comments);
refreshShipments();
alert('Comments updated successfully.');
@ -155,6 +152,8 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
console.error('Failed to update comments:', error);
alert('Failed to update comments. Please try again.');
}
} else {
console.error("Selected shipment or shipment ID is undefined");
}
};
@ -188,10 +187,19 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
fullWidth
sx={{ marginBottom: 2 }}
/>
<Button variant="contained" color="primary" onClick={handleAddDewar} sx={{ marginRight: 2 }}>
<Button
variant="contained"
color="primary"
onClick={handleAddDewar}
sx={{ marginRight: 2 }}
>
Save Dewar
</Button>
<Button variant="outlined" color="secondary" onClick={() => setIsAddingDewar(false)}>
<Button
variant="outlined"
color="secondary"
onClick={() => setIsAddingDewar(false)}
>
Cancel
</Button>
</Box>
@ -333,10 +341,10 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
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}
initialContactPersons={localSelectedDewar?.contact_person ? [localSelectedDewar.contact_person] : []} // Focus on dewar contact person
initialReturnAddresses={localSelectedDewar?.return_address ? [localSelectedDewar.return_address] : []} // Focus on dewar return address
defaultContactPerson={localSelectedDewar?.contact_person}
defaultReturnAddress={localSelectedDewar?.return_address}
shipmentId={selectedShipment?.shipment_id || ''}
refreshShipments={refreshShipments}
/>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Box } from '@mui/material';
import { Box, Typography } from '@mui/material';
interface UnipuckProps {
pucks: number; // Number of pucks
@ -8,6 +8,10 @@ interface UnipuckProps {
const Unipuck: React.FC<UnipuckProps> = ({ pucks, samples }) => {
const renderPuck = (sampleStatus: string[]) => {
if (!sampleStatus) {
sampleStatus = Array(16).fill('empty');
}
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" />
@ -28,11 +32,15 @@ const Unipuck: React.FC<UnipuckProps> = ({ pucks, samples }) => {
return puckSVG;
};
if (pucks === 0) {
return <Typography variant="body1">No pucks attached to the dewar.</Typography>;
}
return (
<Box sx={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 2 }}>
{[...Array(pucks)].map((_, index) => (
<Box key={index} sx={{ margin: 1 }}>
{renderPuck(samples ? samples[index] : Array(16).fill('empty'))}
{renderPuck(samples && samples[index] ? samples[index] : Array(16).fill('empty'))}
</Box>
))}
</Box>