added pucks and samples
This commit is contained in:
parent
23e7ebb819
commit
9fa499a582
@ -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
|
@ -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'
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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
|
BIN
backend/test.db
BIN
backend/test.db
Binary file not shown.
@ -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>
|
||||
)}
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user