now creating dewars from spreadsheet
This commit is contained in:
parent
5e6eb40033
commit
86883133a7
@ -25,7 +25,7 @@ class Shipment(Base):
|
||||
class ContactPerson(Base):
|
||||
__tablename__ = "contact_persons"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
firstname = Column(String)
|
||||
lastname = Column(String)
|
||||
phone_number = Column(String)
|
||||
@ -37,7 +37,7 @@ class ContactPerson(Base):
|
||||
class Address(Base):
|
||||
__tablename__ = "addresses"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
street = Column(String)
|
||||
city = Column(String)
|
||||
zipcode = Column(String)
|
||||
@ -81,7 +81,7 @@ class Dewar(Base):
|
||||
class Proposal(Base):
|
||||
__tablename__ = "proposals"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
number = Column(String)
|
||||
|
||||
shipments = relationship("Shipment", back_populates="proposal")
|
||||
@ -90,21 +90,21 @@ class Proposal(Base):
|
||||
class Puck(Base):
|
||||
__tablename__ = 'pucks'
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
puck_name = Column(String, index=True)
|
||||
puck_type = Column(String)
|
||||
puck_location_in_dewar = Column(Integer)
|
||||
|
||||
# Foreign keys and relationships
|
||||
dewar_id = Column(Integer, ForeignKey('dewars.id'))
|
||||
dewar = relationship("Dewar", back_populates="pucks") # Properly define the other side of the relationship
|
||||
dewar = relationship("Dewar", back_populates="pucks")
|
||||
samples = relationship("Sample", back_populates="puck")
|
||||
|
||||
|
||||
class Sample(Base):
|
||||
__tablename__ = 'samples'
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
sample_name = Column(String, index=True) # Matches `sample_name` in data creation
|
||||
position = Column(Integer) # Matches `position` in data creation script
|
||||
|
||||
|
@ -2,8 +2,8 @@ from fastapi import APIRouter, HTTPException, status, Depends
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
from typing import List
|
||||
import uuid
|
||||
from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate, Sample as SampleSchema, Puck as PuckSchema
|
||||
from app.models import Dewar as DewarModel, Puck as PuckModel, Sample as SampleModel
|
||||
from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate
|
||||
from app.models import Dewar as DewarModel, Puck as PuckModel
|
||||
from app.dependencies import get_db
|
||||
|
||||
router = APIRouter()
|
||||
@ -38,7 +38,7 @@ async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> Dew
|
||||
|
||||
|
||||
@router.get("/{dewar_id}", response_model=DewarSchema)
|
||||
async def get_dewar(dewar_id: str, db: Session = Depends(get_db)):
|
||||
async def get_dewar(dewar_id: int, db: Session = Depends(get_db)):
|
||||
dewar = db.query(DewarModel).options(
|
||||
joinedload(DewarModel.pucks).joinedload(PuckModel.positions)
|
||||
).filter(DewarModel.id == dewar_id).first()
|
||||
@ -55,7 +55,7 @@ async def get_dewar(dewar_id: str, db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.put("/{dewar_id}", response_model=DewarSchema)
|
||||
async def update_dewar(dewar_id: str, dewar_update: DewarUpdate, db: Session = Depends(get_db)) -> DewarSchema:
|
||||
async def update_dewar(dewar_id: int, dewar_update: DewarUpdate, db: Session = Depends(get_db)) -> DewarSchema:
|
||||
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||
|
||||
if not dewar:
|
||||
@ -70,3 +70,14 @@ async def update_dewar(dewar_id: str, dewar_update: DewarUpdate, db: Session = D
|
||||
db.refresh(dewar)
|
||||
|
||||
return dewar
|
||||
|
||||
|
||||
@router.delete("/{dewar_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_dewar(dewar_id: int, db: Session = Depends(get_db)):
|
||||
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||
if not dewar:
|
||||
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||
|
||||
db.delete(dewar)
|
||||
db.commit()
|
||||
return
|
||||
|
@ -1,13 +1,13 @@
|
||||
# app/routers/shipment.py
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status, Query, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from datetime import date
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, \
|
||||
Proposal as ProposalModel, Dewar as DewarModel
|
||||
Proposal as ProposalModel, Dewar as DewarModel, Puck as PuckModel, Sample as SampleModel
|
||||
from app.schemas import ShipmentCreate, UpdateShipmentComments, Shipment as ShipmentSchema, DewarUpdate, \
|
||||
ContactPerson as ContactPersonSchema, Sample as SampleSchema, DewarCreate, PuckCreate, SampleCreate
|
||||
from app.database import get_db
|
||||
@ -74,8 +74,8 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
|
||||
|
||||
|
||||
@router.delete("/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_shipment(id: int, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
||||
async def delete_shipment(shipment_id: int, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if not shipment:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
db.delete(shipment)
|
||||
@ -84,10 +84,10 @@ async def delete_shipment(id: int, db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.put("/{shipment_id}", response_model=ShipmentSchema)
|
||||
async def update_shipment(id: int, updated_shipment: ShipmentCreate, db: Session = Depends(get_db)):
|
||||
async def update_shipment(shipment_id: int, updated_shipment: ShipmentCreate, db: Session = Depends(get_db)):
|
||||
print("Received payload:", json.dumps(updated_shipment.dict(), indent=2, default=default_serializer))
|
||||
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if not shipment:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
|
||||
@ -137,8 +137,8 @@ async def update_shipment(id: int, updated_shipment: ShipmentCreate, db: Session
|
||||
|
||||
|
||||
@router.post("/{shipment_id}/add_dewar", response_model=ShipmentSchema)
|
||||
async def add_dewar_to_shipment(id: int, dewar_id: int, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
||||
async def add_dewar_to_shipment(shipment_id: int, dewar_id: int, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if not shipment:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||
@ -175,8 +175,8 @@ async def get_shipment_contact_persons(db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.get("/{shipment_id}/samples", response_model=List[SampleSchema])
|
||||
def get_samples_in_shipment(id: int, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
||||
async def get_samples_in_shipment(shipment_id: int, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if shipment is None:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
|
||||
@ -189,7 +189,7 @@ def get_samples_in_shipment(id: int, db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.get("/shipments/{shipment_id}/dewars/{dewar_id}/samples", response_model=List[SampleSchema])
|
||||
def get_samples_in_dewar(shipment_id: int, dewar_id: int, db: Session = Depends(get_db)):
|
||||
async def get_samples_in_dewar(shipment_id: int, dewar_id: int, db: Session = Depends(get_db)):
|
||||
shipment = get_shipment_by_id(db, shipment_id)
|
||||
if not shipment:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
@ -207,8 +207,9 @@ def get_samples_in_dewar(shipment_id: int, dewar_id: int, db: Session = Depends(
|
||||
|
||||
|
||||
@router.put("/{shipment_id}/comments", response_model=ShipmentSchema)
|
||||
async def update_shipment_comments(id: int, comments_data: UpdateShipmentComments, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
||||
async def update_shipment_comments(shipment_id: int, comments_data: UpdateShipmentComments,
|
||||
db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if not shipment:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
|
||||
@ -219,72 +220,30 @@ async def update_shipment_comments(id: int, comments_data: UpdateShipmentComment
|
||||
|
||||
|
||||
@router.post("/{shipment_id}/add_dewar_puck_sample", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
|
||||
async def add_dewar_puck_sample_to_shipment(shipment_id: int, payload: DewarCreate, db: Session = Depends(get_db)):
|
||||
async def add_dewar_puck_sample_to_shipment(
|
||||
shipment_id: int,
|
||||
payload: DewarCreate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if not shipment:
|
||||
logging.error(f"Shipment not found with ID: {shipment_id}")
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
|
||||
for dewar_data in payload.dewars:
|
||||
dewar = Dewar(
|
||||
shipment_id=shipment_id,
|
||||
dewar_name=dewar_data.dewar_name,
|
||||
tracking_number=dewar_data.tracking_number,
|
||||
status=dewar_data.status,
|
||||
contact_person_id=dewar_data.contact_person_id,
|
||||
return_address_id=dewar_data.return_address_id,
|
||||
)
|
||||
db.add(dewar)
|
||||
db.commit()
|
||||
db.refresh(dewar)
|
||||
|
||||
for puck_data in dewar_data.pucks:
|
||||
puck = Puck(
|
||||
dewar_id=dewar.id,
|
||||
puck_name=puck_data.puck_name,
|
||||
puck_type=puck_data.puck_type,
|
||||
puck_location_in_dewar=puck_data.puck_location_in_dewar,
|
||||
)
|
||||
db.add(puck)
|
||||
db.commit()
|
||||
db.refresh(puck)
|
||||
|
||||
for sample_data in puck_data.samples:
|
||||
sample = Sample(
|
||||
puck_id=puck.id,
|
||||
sample_name=sample_data.sample_name,
|
||||
position=sample_data.position,
|
||||
data_collection_parameters=DataCollectionParameters(
|
||||
**sample_data.data_collection_parameters
|
||||
),
|
||||
)
|
||||
db.add(sample)
|
||||
db.commit()
|
||||
db.refresh(sample)
|
||||
|
||||
db.refresh(shipment)
|
||||
return shipment
|
||||
|
||||
|
||||
@router.post("/{shipment_id}/add_dewar_puck_sample", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
|
||||
async def add_dewar_puck_sample_to_shipment(shipment_id: int, payload: ShipmentCreate, db: Session = Depends(get_db)):
|
||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||
if not shipment:
|
||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||
|
||||
for dewar_data in payload.dewars:
|
||||
try:
|
||||
dewar = DewarModel(
|
||||
shipment_id=shipment_id,
|
||||
dewar_name=dewar_data.dewar_name, # Ensure this field aligns with the frontend
|
||||
tracking_number=dewar_data.tracking_number,
|
||||
status=dewar_data.status,
|
||||
contact_person_id=dewar_data.contact_person_id,
|
||||
return_address_id=dewar_data.return_address_id,
|
||||
dewar_name=payload.dewar_name,
|
||||
tracking_number=payload.tracking_number,
|
||||
status=payload.status,
|
||||
contact_person_id=payload.contact_person_id,
|
||||
return_address_id=payload.return_address_id,
|
||||
)
|
||||
db.add(dewar)
|
||||
db.commit()
|
||||
db.refresh(dewar)
|
||||
|
||||
for puck_data in dewar_data.pucks:
|
||||
for puck_data in payload.pucks:
|
||||
puck = PuckModel(
|
||||
dewar_id=dewar.id,
|
||||
puck_name=puck_data.puck_name,
|
||||
@ -298,13 +257,21 @@ async def add_dewar_puck_sample_to_shipment(shipment_id: int, payload: ShipmentC
|
||||
for sample_data in puck_data.samples:
|
||||
sample = SampleModel(
|
||||
puck_id=puck.id,
|
||||
sample_name=sample_data.crystalname,
|
||||
position=sample_data.positioninpuck,
|
||||
data_collection_parameters=sample_data.data_collection_parameters
|
||||
sample_name=sample_data.sample_name,
|
||||
position=sample_data.position,
|
||||
data_collection_parameters=sample_data.data_collection_parameters,
|
||||
)
|
||||
db.add(sample)
|
||||
db.commit()
|
||||
db.refresh(sample)
|
||||
|
||||
db.refresh(shipment)
|
||||
except SQLAlchemyError as e:
|
||||
logging.error(f"Database error occurred: {e}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
except ValidationError as e:
|
||||
logging.error(f"Validation error occurred: {e}")
|
||||
raise HTTPException(status_code=400, detail="Validation error")
|
||||
|
||||
logging.info(f"Successfully added dewar, puck, and sample for shipment ID: {shipment_id}")
|
||||
return shipment
|
@ -91,7 +91,7 @@ async function fetchAndGenerate() {
|
||||
}
|
||||
}
|
||||
|
||||
const backendDirectory = '/Users/gotthardg/PycharmProjects/heidi-v2/backend/';
|
||||
const backendDirectory = '/Users/gotthardg/PycharmProjects/heidi-v2/backend/app';
|
||||
console.log(`👀 Watching for changes in ${backendDirectory}`);
|
||||
const watcher = chokidar.watch(backendDirectory, { persistent: true, ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY] });
|
||||
|
||||
|
1592
frontend/package-lock.json
generated
1592
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,8 @@
|
||||
"@fullcalendar/react": "^6.1.15",
|
||||
"@fullcalendar/timegrid": "^6.1.15",
|
||||
"@mui/icons-material": "^6.1.5",
|
||||
"@mui/material": "^6.1.5",
|
||||
"@mui/material": "^6.1.6",
|
||||
"@mui/system": "^6.1.6",
|
||||
"axios": "^1.7.7",
|
||||
"chokidar": "^4.0.1",
|
||||
"dayjs": "^1.11.13",
|
||||
@ -33,7 +34,7 @@
|
||||
"react-big-calendar": "^1.15.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"react-scheduler": "^0.1.0",
|
||||
"rimraf": "^5.0.10"
|
||||
},
|
||||
|
@ -1,8 +1,9 @@
|
||||
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, DewarsService, ShipmentsService } from '../../openapi';
|
||||
import Unipuck from '../components/Unipuck';
|
||||
import { ContactPerson, Address, Dewar, ContactsService, AddressesService, DewarsService, ShipmentsService } from '../../openapi'; // Adjust path if necessary
|
||||
import Unipuck from '../components/Unipuck'; // This path should be checked and corrected if necessary
|
||||
import { Shipment } from "../types.ts"; // Correct or adjust as needed
|
||||
|
||||
interface DewarDetailsProps {
|
||||
dewar: Dewar;
|
||||
@ -12,9 +13,24 @@ interface DewarDetailsProps {
|
||||
initialReturnAddresses?: Address[];
|
||||
defaultContactPerson?: ContactPerson;
|
||||
defaultReturnAddress?: Address;
|
||||
shipmentId: string;
|
||||
refreshShipments: () => void;
|
||||
selectedShipment?: any;
|
||||
shipmentId: number;
|
||||
selectedShipment?: Shipment;
|
||||
}
|
||||
|
||||
interface NewContactPerson {
|
||||
id: number;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
phone_number: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface NewReturnAddress {
|
||||
id: number;
|
||||
street: string;
|
||||
city: string;
|
||||
zipcode: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
@ -26,24 +42,20 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
defaultContactPerson,
|
||||
defaultReturnAddress,
|
||||
shipmentId,
|
||||
refreshShipments,
|
||||
selectedShipment,
|
||||
}) => {
|
||||
const [localTrackingNumber, setLocalTrackingNumber] = useState(trackingNumber);
|
||||
const [contactPersons, setContactPersons] = useState(initialContactPersons);
|
||||
const [returnAddresses, setReturnAddresses] = useState(initialReturnAddresses);
|
||||
const [selectedContactPerson, setSelectedContactPerson] = useState('');
|
||||
const [selectedReturnAddress, setSelectedReturnAddress] = useState('');
|
||||
const [selectedContactPerson, setSelectedContactPerson] = useState<string>('');
|
||||
const [selectedReturnAddress, setSelectedReturnAddress] = useState<string>('');
|
||||
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
|
||||
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(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 [newContactPerson, setNewContactPerson] = useState<NewContactPerson>({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
|
||||
const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({ id: 0, street: '', city: '', zipcode: '', country: '' });
|
||||
const [changesMade, setChangesMade] = useState(false);
|
||||
const [feedbackMessage, setFeedbackMessage] = useState('');
|
||||
const [openSnackbar, setOpenSnackbar] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const setInitialContactPerson = () => {
|
||||
@ -99,17 +111,14 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(shipmentId, dewar.id);
|
||||
console.log("Fetched Samples: ", fetchedSamples);
|
||||
|
||||
const updatedPuckStatuses = dewar.pucks.map(puck => {
|
||||
// Filter samples for the current puck
|
||||
const updatedPuckStatuses = (dewar.pucks ?? []).map(puck => {
|
||||
const puckSamples = fetchedSamples.filter(sample => sample.puck_id === puck.id);
|
||||
|
||||
// Initialize positions as 'empty'
|
||||
const statusArray = Array(16).fill('empty');
|
||||
|
||||
// Update positions based on puckSamples' positions
|
||||
puckSamples.forEach(sample => {
|
||||
if (sample.position >= 1 && sample.position <= 16) {
|
||||
statusArray[sample.position - 1] = 'filled'; // Adjust for 0-based index
|
||||
statusArray[sample.position - 1] = 'filled'; // Corrected line
|
||||
}
|
||||
});
|
||||
|
||||
@ -118,9 +127,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
|
||||
setPuckStatuses(updatedPuckStatuses);
|
||||
} catch (error) {
|
||||
setError('Failed to load samples. Please try again later.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
console.error("Error fetching samples:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -194,7 +201,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
};
|
||||
|
||||
const handleSaveChanges = async () => {
|
||||
const formatDate = (dateString: string) => {
|
||||
const formatDate = (dateString: string | null) => {
|
||||
if (!dateString) return null;
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) return null;
|
||||
@ -228,14 +235,13 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
arrival_date: dewar.arrival_date,
|
||||
returning_date: dewar.returning_date,
|
||||
qrcode: dewar.qrcode,
|
||||
return_address_id: selectedReturnAddress,
|
||||
contact_person_id: selectedContactPerson,
|
||||
return_address_id: parseInt(selectedReturnAddress ?? '', 10),
|
||||
contact_person_id: parseInt(selectedContactPerson ?? '', 10),
|
||||
};
|
||||
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
||||
setFeedbackMessage("Changes saved successfully.");
|
||||
setChangesMade(false);
|
||||
refreshShipments();
|
||||
} catch (error) {
|
||||
setFeedbackMessage("Failed to save changes. Please try again later.");
|
||||
setOpenSnackbar(true);
|
||||
@ -270,7 +276,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
</Box>
|
||||
<Box sx={{ marginTop: 2 }}>
|
||||
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
|
||||
{dewar.number_of_pucks > 0 ? <Unipuck pucks={dewar.number_of_pucks} samples={puckStatuses} /> : <Typography>No pucks attached to the dewar.</Typography>}
|
||||
{(dewar.pucks ?? []).length > 0
|
||||
? <Unipuck pucks={(dewar.pucks ?? []).length} samples={puckStatuses} />
|
||||
: <Typography>No pucks attached to the dewar.</Typography>}
|
||||
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
|
||||
</Box>
|
||||
<Typography variant="body1">Current Contact Person:</Typography>
|
||||
|
@ -1,21 +1,21 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Stepper, Step, StepLabel, StepIconProps, Typography, Menu, MenuItem } from '@mui/material';
|
||||
import { Stepper, Step, StepLabel, Typography, Menu, MenuItem } from '@mui/material';
|
||||
import AirplanemodeActiveIcon from '@mui/icons-material/AirplanemodeActive';
|
||||
import StoreIcon from '@mui/icons-material/Store';
|
||||
import RecycleIcon from '@mui/icons-material/Restore';
|
||||
import { Dewar, DewarsService } from "../../openapi";
|
||||
import { DewarStatus, getStatusStepIndex, determineIconColor } from './statusUtils'; // Utilities moved to a new file
|
||||
import { DewarStatus, getStatusStepIndex, determineIconColor } from './statusUtils';
|
||||
|
||||
const ICON_STYLE = { width: 24, height: 24 };
|
||||
|
||||
// Inline SVG Component
|
||||
// Custom SVG Icon Component
|
||||
const BottleIcon: React.FC<{ fill: string }> = ({ fill }) => (
|
||||
<svg fill={fill} height="24px" width="24px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276.777 276.777">
|
||||
<svg fill={fill} height="24px" width="24px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276.777 276.777">
|
||||
<path d="M190.886,82.273c-3.23-2.586-7.525-7.643-7.525-21.639V43h8.027V0h-106v43h8.027v17.635c0,11.66-1.891,17.93-6.524,21.639 c-21.813,17.459-31.121,36.748-31.121,64.5v100.088c0,16.496,13.42,29.916,29.916,29.916h105.405 c16.496,0,29.916-13.42,29.916-29.916V146.773C221.007,121.103,210.029,97.594,190.886,82.273z M191.007,246.777H85.77V146.773 c0-18.589,5.199-29.339,19.867-41.078c15.758-12.612,17.778-30.706,17.778-45.061V43h29.945v17.635 c0,19.927,6.318,35.087,18.779,45.061c11.99,9.597,18.867,24.568,18.867,41.078V246.777z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Define types for icons mapping.
|
||||
// Icons Mapping
|
||||
const ICONS: { [key: number]: React.ReactElement } = {
|
||||
0: <BottleIcon fill="grey" />,
|
||||
1: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'blue' }} />,
|
||||
@ -25,24 +25,38 @@ const ICONS: { [key: number]: React.ReactElement } = {
|
||||
5: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'orange' }} />,
|
||||
};
|
||||
|
||||
interface StepIconContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
color: string;
|
||||
// StepIconContainer Component
|
||||
interface StepIconContainerProps {
|
||||
completed?: boolean;
|
||||
active?: boolean;
|
||||
error?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const StepIconContainer: React.FC<StepIconContainerProps> = ({ color, children, ...rest }) => (
|
||||
<div style={{ color }} {...rest}>
|
||||
const StepIconContainer: React.FC<StepIconContainerProps> = ({ completed, active, error, children }) => {
|
||||
const className = [
|
||||
completed ? 'completed' : '',
|
||||
active ? 'active' : '',
|
||||
error ? 'error' : '',
|
||||
].join(' ').trim();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
// StepIconComponent Props
|
||||
type StepIconComponentProps = {
|
||||
icon: number;
|
||||
dewar: Dewar;
|
||||
isSelected: boolean;
|
||||
refreshShipments: () => void;
|
||||
} & StepIconProps;
|
||||
} & Omit<React.HTMLAttributes<HTMLDivElement>, 'icon'>;
|
||||
|
||||
const StepIconComponent = ({ icon, dewar, isSelected, refreshShipments, ...props }: StepIconComponentProps) => {
|
||||
// StepIconComponent
|
||||
const StepIconComponent: React.FC<StepIconComponentProps> = ({ icon, dewar, isSelected, refreshShipments, ...rest }) => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
const handleIconEnter = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
@ -74,9 +88,9 @@ const StepIconComponent = ({ icon, dewar, isSelected, refreshShipments, ...props
|
||||
contact_person_id: dewar.contact_person_id,
|
||||
};
|
||||
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id || '', payload);
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id, payload);
|
||||
setAnchorEl(null);
|
||||
refreshShipments(); // Refresh shipments after status update
|
||||
refreshShipments();
|
||||
} catch (error) {
|
||||
console.error('Failed to update dewar status:', error);
|
||||
alert('Failed to update dewar status. Please try again.');
|
||||
@ -85,17 +99,21 @@ const StepIconComponent = ({ icon, dewar, isSelected, refreshShipments, ...props
|
||||
|
||||
const { iconIndex, color } = getIconProperties(icon, dewar);
|
||||
const IconComponent = ICONS[iconIndex];
|
||||
const iconProps = iconIndex === 0 ? { fill: color } : {};
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={handleIconEnter}
|
||||
onMouseLeave={handleIconLeave}
|
||||
style={{ position: 'relative' }}
|
||||
{...rest}
|
||||
>
|
||||
<StepIconContainer
|
||||
completed={rest['aria-activedescendant'] ? true : undefined}
|
||||
active={Boolean(rest['aria-activedescendant'])}
|
||||
error={rest.role === 'error'}
|
||||
>
|
||||
<StepIconContainer color={color} {...props}>
|
||||
{IconComponent
|
||||
? React.cloneElement(IconComponent, iconProps)
|
||||
? React.cloneElement(IconComponent, iconIndex === 0 ? { fill: color } : {})
|
||||
: <Typography variant="body2" color="error">Invalid icon</Typography>
|
||||
}
|
||||
</StepIconContainer>
|
||||
@ -119,23 +137,26 @@ const StepIconComponent = ({ icon, dewar, isSelected, refreshShipments, ...props
|
||||
);
|
||||
};
|
||||
|
||||
// Icon properties retrieval based on the status and icon number
|
||||
const getIconProperties = (icon: number, dewar: Dewar) => {
|
||||
const status = dewar.status as DewarStatus;
|
||||
const iconIndex = status === 'Delayed' && icon === 1 ? 5 : icon;
|
||||
const color = determineIconColor(icon, status);
|
||||
const fill = status === 'In Preparation' ? color : undefined;
|
||||
return { iconIndex, color, fill };
|
||||
return { iconIndex, color };
|
||||
};
|
||||
|
||||
// Steps of the stepper
|
||||
const steps = ['In-House', 'Transit', 'At SLS', 'Returned'];
|
||||
|
||||
// Props for the CustomStepper
|
||||
type CustomStepperProps = {
|
||||
dewar: Dewar;
|
||||
selectedDewarId: string | null;
|
||||
refreshShipments: () => void; // Add refreshShipments prop
|
||||
}
|
||||
selectedDewarId: number | null;
|
||||
refreshShipments: () => void;
|
||||
};
|
||||
|
||||
const CustomStepper = ({ dewar, selectedDewarId, refreshShipments }: CustomStepperProps) => {
|
||||
// CustomStepper Component
|
||||
const CustomStepper: React.FC<CustomStepperProps> = ({ dewar, selectedDewarId, refreshShipments }) => {
|
||||
const activeStep = getStatusStepIndex(dewar.status as DewarStatus);
|
||||
const isSelected = dewar.id === selectedDewarId;
|
||||
|
||||
|
@ -163,12 +163,14 @@ const ResponsiveAppBar: React.FC<ResponsiveAppBarProps> = ({ onOpenAddressManage
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
) : (
|
||||
item.path && (
|
||||
<MenuItem key={item.name} onClick={handleCloseUserMenu}>
|
||||
<Link to={item.path} style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
{item.name}
|
||||
</Link>
|
||||
</MenuItem>
|
||||
)
|
||||
)
|
||||
)}
|
||||
</Menu>
|
||||
</Box>
|
||||
|
@ -4,7 +4,7 @@ import QRCode from 'react-qr-code';
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { Dewar, DewarsService, ShipmentsService, ContactPerson, ApiError } from "../../openapi";
|
||||
import { Dewar, DewarsService, Shipment, ContactPerson, ApiError, ShipmentsService } from "../../openapi";
|
||||
import { SxProps } from "@mui/system";
|
||||
import CustomStepper from "./DewarStepper";
|
||||
import DewarDetails from './DewarDetails';
|
||||
@ -14,10 +14,10 @@ const MAX_COMMENTS_LENGTH = 200;
|
||||
interface ShipmentDetailsProps {
|
||||
isCreatingShipment: boolean;
|
||||
sx?: SxProps;
|
||||
selectedShipment: ShipmentsService | null;
|
||||
selectedShipment: Shipment | null;
|
||||
selectedDewar: Dewar | null;
|
||||
setSelectedDewar: React.Dispatch<React.SetStateAction<Dewar | null>>;
|
||||
setSelectedShipment: React.Dispatch<React.SetStateAction<ShipmentsService | null>>;
|
||||
setSelectedShipment: React.Dispatch<React.SetStateAction<Shipment | null>>;
|
||||
refreshShipments: () => void;
|
||||
defaultContactPerson?: ContactPerson;
|
||||
}
|
||||
@ -75,7 +75,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
setSelectedDewar(newSelection);
|
||||
};
|
||||
|
||||
const handleDeleteDewar = async (dewarId: string) => {
|
||||
const handleDeleteDewar = async (dewarId: number) => {
|
||||
const confirmed = window.confirm('Are you sure you want to delete this dewar?');
|
||||
if (confirmed && selectedShipment) {
|
||||
try {
|
||||
@ -311,10 +311,9 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<CustomStepper dewar={dewar} selectedDewarId={localSelectedDewar?.id} refreshShipments={refreshShipments} />
|
||||
<CustomStepper dewar={dewar} selectedDewarId={localSelectedDewar?.id ?? null} refreshShipments={refreshShipments} />
|
||||
</Box>
|
||||
|
||||
|
||||
{localSelectedDewar?.id === dewar.id && (
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
@ -333,21 +332,19 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
</Button>
|
||||
|
||||
{localSelectedDewar?.id === dewar.id && (
|
||||
<Box sx={{ padding: 2, border: '1px solid #ccc', borderRadius: '4px' }}>
|
||||
<DewarDetails
|
||||
dewar={localSelectedDewar}
|
||||
trackingNumber={localSelectedDewar.tracking_number || ''}
|
||||
trackingNumber={localSelectedDewar?.tracking_number || ''}
|
||||
setTrackingNumber={(value) => {
|
||||
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
|
||||
}}
|
||||
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?.id || ''}
|
||||
defaultContactPerson={localSelectedDewar?.contact_person ?? undefined} // Use `?? undefined`
|
||||
defaultReturnAddress={localSelectedDewar?.return_address ?? undefined} // Use `?? undefined`
|
||||
shipmentId={selectedShipment?.id ?? null}
|
||||
refreshShipments={refreshShipments}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
import { SelectChangeEvent } from '@mui/material';
|
||||
import { SxProps } from '@mui/system';
|
||||
import {
|
||||
ContactPersonCreate, ContactPerson, Address, Proposal, ContactsService, AddressesService, ProposalsService,
|
||||
ContactPersonCreate, ContactPerson, Address, Proposal, ContactsService, AddressesService, AddressCreate, ProposalsService,
|
||||
OpenAPI, ShipmentCreate, ShipmentsService
|
||||
} from '../../openapi';
|
||||
import { useEffect } from 'react';
|
||||
@ -98,7 +98,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshS
|
||||
};
|
||||
|
||||
const isFormValid = () => {
|
||||
const { shipment_name, shipment_status } = newShipment;
|
||||
const { shipment_name } = newShipment;
|
||||
|
||||
if (!shipment_name) return false;
|
||||
if (!selectedContactPersonId || !selectedReturnAddressId || !selectedProposalId) return false;
|
||||
@ -431,7 +431,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshS
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleSaveShipment}
|
||||
disabled={newShipment.comments?.length > MAX_COMMENTS_LENGTH}
|
||||
disabled={(newShipment.comments?.length ?? 0) > MAX_COMMENTS_LENGTH}
|
||||
>
|
||||
Save Shipment
|
||||
</Button>
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { Button, Box, Typography, IconButton } from '@mui/material';
|
||||
import { Add as AddIcon, Delete as DeleteIcon, UploadFile as UploadFileIcon, Refresh as RefreshIcon } from '@mui/icons-material';
|
||||
import UploadDialog from './UploadDialog';
|
||||
import { Dewar, ShipmentsService } from '../../openapi';
|
||||
import { Dewar, Shipment, ShipmentsService } from '../../openapi';
|
||||
import { SxProps } from '@mui/material';
|
||||
import bottleGrey from '/src/assets/icons/bottle-svgrepo-com-grey.svg';
|
||||
import bottleYellow from '/src/assets/icons/bottle-svgrepo-com-yellow.svg';
|
||||
@ -11,10 +11,10 @@ import bottleRed from '/src/assets/icons/bottle-svgrepo-com-red.svg';
|
||||
|
||||
interface ShipmentPanelProps {
|
||||
setCreatingShipment: (value: boolean) => void;
|
||||
selectShipment: (shipment: ShipmentsService | null) => void;
|
||||
selectedShipment: ShipmentsService | null;
|
||||
selectShipment: (shipment: Shipment | null) => void;
|
||||
selectedShipment: Shipment | null;
|
||||
sx?: SxProps;
|
||||
shipments: ShipmentsService[];
|
||||
shipments: Shipment[];
|
||||
refreshShipments: () => void;
|
||||
error: string | null;
|
||||
}
|
||||
@ -46,7 +46,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const deleteShipment = async (shipmentId: string | undefined) => {
|
||||
const deleteShipment = async (shipmentId: number) => {
|
||||
if (!shipmentId) return;
|
||||
try {
|
||||
await ShipmentsService.deleteShipmentShipmentsShipmentIdDelete(shipmentId);
|
||||
@ -57,27 +57,19 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleShipmentSelection = (shipment: ShipmentsService) => {
|
||||
const handleShipmentSelection = (shipment: Shipment) => {
|
||||
const isSelected = selectedShipment?.id === shipment.id;
|
||||
const updatedShipment = isSelected ? null : shipment;
|
||||
console.log("Shipment selected:", updatedShipment); // debug log
|
||||
if (updatedShipment) {
|
||||
console.log("Contact Person ID:", updatedShipment.contact_person_id);
|
||||
console.log("Return Address ID:", updatedShipment.return_address_id);
|
||||
}
|
||||
selectShipment(updatedShipment);
|
||||
selectShipment(isSelected ? null : shipment);
|
||||
};
|
||||
|
||||
const openUploadDialog = (event: React.MouseEvent) => {
|
||||
event.stopPropagation(); // Prevent event bubbling
|
||||
event.stopPropagation();
|
||||
setUploadDialogOpen(true);
|
||||
};
|
||||
|
||||
const closeUploadDialog = () => setUploadDialogOpen(false);
|
||||
|
||||
const getNumberOfDewars = (shipment: ShipmentsService): number => shipment.dewars?.length || 0;
|
||||
|
||||
console.log("Current selected shipment:", selectedShipment); // debug log
|
||||
const getNumberOfDewars = (shipment: Shipment): number => shipment.dewars?.length || 0;
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '90%', borderRight: '1px solid #ccc', padding: 2, ...sx }}>
|
||||
@ -104,34 +96,29 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
{shipments.map((shipment) => (
|
||||
<Button
|
||||
{shipments.map((shipment) => {
|
||||
const isSelected = selectedShipment?.id === shipment.id;
|
||||
return (
|
||||
<Box
|
||||
key={shipment.id}
|
||||
onClick={() => handleShipmentSelection(shipment)}
|
||||
sx={{
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
marginBottom: 1,
|
||||
color: 'white',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
padding: '10px 16px',
|
||||
fontSize: '0.7rem',
|
||||
backgroundColor: isSelected ? '#52893e' : '#424242',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: selectedShipment?.id === shipment.id ? '#52893e' : '#424242',
|
||||
alignItems: 'center',
|
||||
color: 'white',
|
||||
borderRadius: 1,
|
||||
'&:hover': {
|
||||
backgroundColor: selectedShipment?.id === shipment.id ? '#9aca8c' : '#616161',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: selectedShipment?.id === shipment.id ? '#915151' : '#212121',
|
||||
backgroundColor: isSelected ? '#9aca8c' : '#616161'
|
||||
},
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={() => handleShipmentSelection(shipment)}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Box sx={{ position: 'relative', marginRight: '8px' }}>
|
||||
<Box sx={{ position: 'relative', marginRight: 2 }}>
|
||||
<img
|
||||
src={statusIconMap[shipment.shipment_status] || bottleGrey}
|
||||
alt={`Status: ${shipment.shipment_status}`}
|
||||
@ -159,15 +146,17 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
<Typography>{shipment.shipment_name}</Typography>
|
||||
<Typography sx={{ fontSize: '0.6rem', color: '#ccc' }}>{shipment.shipment_date}</Typography>
|
||||
<Typography sx={{ fontSize: '0.6rem', color: '#ccc' }}>
|
||||
Total Pucks: {shipment.dewars.reduce((total, dewar: Dewar) => total + dewar.number_of_pucks, 0)}
|
||||
Total Pucks: {shipment.dewars?.reduce((total, dewar: Dewar) => total + (dewar.number_of_pucks || 0), 0) ?? 0}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{isSelected && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{selectedShipment?.id === shipment.id && (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={openUploadDialog}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
openUploadDialog(event);
|
||||
}}
|
||||
color="primary"
|
||||
title="Upload Sample Data Sheet"
|
||||
sx={{ marginLeft: 1 }}
|
||||
@ -177,7 +166,6 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
<IconButton
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
console.log('Delete button clicked'); // debug log
|
||||
handleDeleteShipment();
|
||||
}}
|
||||
color="error"
|
||||
@ -186,15 +174,15 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Button>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
<UploadDialog
|
||||
open={uploadDialogOpen}
|
||||
onClose={closeUploadDialog}
|
||||
selectedShipment={selectedShipment} // <-- Pass selectedShipment here
|
||||
selectedShipment={selectedShipment}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
@ -17,12 +17,6 @@ import { SpreadsheetService, ShipmentsService, DewarsService, ApiError } from '.
|
||||
import * as ExcelJS from 'exceljs';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
const HeaderMapping = {
|
||||
'dewarname': 'dewar_name',
|
||||
'trackingnumber': 'tracking_number',
|
||||
'status': 'status'
|
||||
};
|
||||
|
||||
const SpreadsheetTable = ({
|
||||
raw_data,
|
||||
errors,
|
||||
@ -50,6 +44,7 @@ const SpreadsheetTable = ({
|
||||
dewar_name: '',
|
||||
tracking_number: 'UNKNOWN',
|
||||
status: 'In preparation',
|
||||
pucks: []
|
||||
};
|
||||
|
||||
const [newDewar, setNewDewar] = useState(initialNewDewarState);
|
||||
@ -77,7 +72,6 @@ const SpreadsheetTable = ({
|
||||
|
||||
useEffect(() => {
|
||||
const initialNonEditableCells = new Set();
|
||||
|
||||
raw_data.forEach((row, rowIndex) => {
|
||||
headers.forEach((_, colIndex) => {
|
||||
const key = `${row.row_num}-${headers[colIndex]}`;
|
||||
@ -86,7 +80,6 @@ const SpreadsheetTable = ({
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
setNonEditableCells(initialNonEditableCells);
|
||||
}, [raw_data, headers, errorMap]);
|
||||
|
||||
@ -148,35 +141,7 @@ const SpreadsheetTable = ({
|
||||
'dewarname': 0,
|
||||
'puckname': 1,
|
||||
'pucktype': 2,
|
||||
'crystalname': 3,
|
||||
'positioninpuck': 4,
|
||||
'priority': 5,
|
||||
'comments': 6,
|
||||
'directory': 7,
|
||||
'proteinname': 8,
|
||||
'oscillation': 9,
|
||||
'aperture': 10,
|
||||
'exposure': 11,
|
||||
'totalrange': 12,
|
||||
'transmission': 13,
|
||||
'dose': 14,
|
||||
'targetresolution': 15,
|
||||
'datacollectiontype': 16,
|
||||
'processingpipeline': 17,
|
||||
'spacegroupnumber': 18,
|
||||
'cellparameters': 19,
|
||||
'rescutkey': 20,
|
||||
'rescutvalue': 21,
|
||||
'userresolution': 22,
|
||||
'pdbid': 23,
|
||||
'autoprocfull': 24,
|
||||
'procfull': 25,
|
||||
'adpenabled': 26,
|
||||
'noano': 27,
|
||||
'ffcscampaign': 28,
|
||||
'trustedhigh': 29,
|
||||
'autoprocextraparams': 30,
|
||||
'chiphiangles': 31,
|
||||
// Add other fields as needed
|
||||
};
|
||||
|
||||
const createDewarsFromSheet = async (data, contactPerson, returnAddress) => {
|
||||
@ -185,7 +150,13 @@ const SpreadsheetTable = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const dewars = new Map(); // Use a Map to prevent duplicates
|
||||
const dewars = new Map();
|
||||
|
||||
const dewarNameIdx = fieldToCol['dewarname'];
|
||||
const puckNameIdx = fieldToCol['puckname'];
|
||||
const puckTypeIdx = fieldToCol['pucktype'];
|
||||
|
||||
let puckPositionInDewar = 1;
|
||||
|
||||
for (const row of data) {
|
||||
if (!row.data) {
|
||||
@ -193,46 +164,74 @@ const SpreadsheetTable = ({
|
||||
continue;
|
||||
}
|
||||
|
||||
const dewarNameIdx = fieldToCol['dewarname'];
|
||||
const dewarName = row.data[dewarNameIdx]?.trim();
|
||||
const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null;
|
||||
const puckName = typeof row.data[puckNameIdx] === 'string' ? row.data[puckNameIdx].trim() : null;
|
||||
const puckType = typeof row.data[puckTypeIdx] === 'string' ? row.data[puckTypeIdx] : 'Unipuck';
|
||||
|
||||
if (dewarName && !dewars.has(dewarName)) {
|
||||
const newDewarToPost = {
|
||||
console.log(`Processing Dewar: ${dewarName}, Puck: ${puckName}, Type: ${puckType}`);
|
||||
|
||||
if (dewarName) {
|
||||
let dewar;
|
||||
if (!dewars.has(dewarName)) {
|
||||
dewar = {
|
||||
...initialNewDewarState,
|
||||
dewar_name: dewarName,
|
||||
tracking_number: row.data[fieldToCol['trackingnumber']] || "UNKNOWN",
|
||||
status: row.data[fieldToCol['status']] || "In preparation",
|
||||
contact_person_id: contactPerson.id,
|
||||
return_address_id: returnAddress.id,
|
||||
pucks: []
|
||||
};
|
||||
dewars.set(dewarName, dewar);
|
||||
puckPositionInDewar = 1;
|
||||
console.log(`Created new dewar: ${dewarName}`);
|
||||
} else {
|
||||
dewar = dewars.get(dewarName);
|
||||
puckPositionInDewar++;
|
||||
console.log(`Found existing dewar: ${dewarName}`);
|
||||
}
|
||||
|
||||
const puck = {
|
||||
puck_name: puckName || 'test', // Fixed puck name
|
||||
puck_type: puckType || 'Unipuck', // Fixed puck type
|
||||
puck_position_in_dewar: puckPositionInDewar
|
||||
};
|
||||
dewar.pucks.push(puck);
|
||||
|
||||
console.log(`Added puck: ${JSON.stringify(puck)}`);
|
||||
} else {
|
||||
console.error('Dewar name is missing in the row');
|
||||
}
|
||||
}
|
||||
|
||||
const dewarsArray = Array.from(dewars.values());
|
||||
for (const dewar of dewarsArray) {
|
||||
try {
|
||||
const createdDewar = await DewarsService.createDewarDewarsPost(newDewarToPost);
|
||||
// Call to create the dewar
|
||||
const createdDewar = await DewarsService.createDewarDewarsPost(dewar);
|
||||
console.log(`Created dewar: ${createdDewar.id}`);
|
||||
|
||||
// Add dewar to the shipment if created successfully
|
||||
if (createdDewar && selectedShipment) {
|
||||
const updatedShipment = await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(
|
||||
await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(
|
||||
selectedShipment.id,
|
||||
createdDewar.id
|
||||
);
|
||||
dewars.set(dewarName, updatedShipment); // Track the added dewar
|
||||
console.log(`Added dewar to shipment: ${createdDewar.id}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error adding dewar for row: ${row.row_num}`, error);
|
||||
console.error(`Error adding dewar`, error);
|
||||
if (error instanceof ApiError && error.body) {
|
||||
console.error('Validation errors:', error.body.detail);
|
||||
} else {
|
||||
console.error('Unexpected error:', error);
|
||||
}
|
||||
}
|
||||
} else if (!dewarName) {
|
||||
console.error('Dewar name is missing in the row');
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(dewars.values());
|
||||
return dewarsArray;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting) return; // Prevent multiple submissions
|
||||
if (isSubmitting) return;
|
||||
if (!headers || headers.length === 0) {
|
||||
console.error('Cannot submit, headers are not defined or empty');
|
||||
return;
|
||||
|
@ -1,28 +1,29 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import ShipmentPanel from '../components/ShipmentPanel';
|
||||
import ShipmentDetails from '../components/ShipmentDetails';
|
||||
import ShipmentForm from '../components/ShipmentForm';
|
||||
import { Dewar, OpenAPI, ContactPerson, ShipmentsService } from '../../openapi';
|
||||
import { Dewar, OpenAPI, Shipment } from '../../openapi';
|
||||
import useShipments from '../hooks/useShipments';
|
||||
import { Grid, Container } from '@mui/material';
|
||||
|
||||
// Define props for Shipments View
|
||||
type ShipmentViewProps = React.PropsWithChildren<Record<string, never>>;
|
||||
|
||||
const API_BASE_URL = 'http://127.0.0.1:8000';
|
||||
OpenAPI.BASE = API_BASE_URL; // Setting API base URL
|
||||
OpenAPI.BASE = API_BASE_URL;
|
||||
|
||||
const ShipmentView: React.FC<ShipmentViewProps> = () => {
|
||||
const { shipments, error, defaultContactPerson, fetchAndSetShipments } = useShipments();
|
||||
const [selectedShipment, setSelectedShipment] = useState<ShipmentsService | null>(null);
|
||||
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||
|
||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
||||
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
|
||||
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Updated shipments:', shipments);
|
||||
}, [shipments]);
|
||||
|
||||
// Handlers for selecting shipment and canceling form
|
||||
const handleSelectShipment = (shipment: ShipmentsService | null) => {
|
||||
const handleSelectShipment = (shipment: Shipment | null) => {
|
||||
setSelectedShipment(shipment);
|
||||
setIsCreatingShipment(false);
|
||||
};
|
||||
@ -59,8 +60,9 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
|
||||
return <div>No shipment details available.</div>;
|
||||
};
|
||||
|
||||
// Render the main layout
|
||||
// Render the main layout using Grid for layout
|
||||
return (
|
||||
<Container maxWidth={false} disableGutters sx={{ display: 'flex', height: '100vh' }}>
|
||||
<Grid container spacing={2} sx={{ height: '100vh' }}>
|
||||
<Grid
|
||||
item
|
||||
@ -84,7 +86,7 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
md={8}
|
||||
md={9}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@ -94,6 +96,7 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
|
||||
{renderShipmentContent()}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user