now creating dewars from spreadsheet
This commit is contained in:
parent
5e6eb40033
commit
86883133a7
@ -25,7 +25,7 @@ class Shipment(Base):
|
|||||||
class ContactPerson(Base):
|
class ContactPerson(Base):
|
||||||
__tablename__ = "contact_persons"
|
__tablename__ = "contact_persons"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
firstname = Column(String)
|
firstname = Column(String)
|
||||||
lastname = Column(String)
|
lastname = Column(String)
|
||||||
phone_number = Column(String)
|
phone_number = Column(String)
|
||||||
@ -37,7 +37,7 @@ class ContactPerson(Base):
|
|||||||
class Address(Base):
|
class Address(Base):
|
||||||
__tablename__ = "addresses"
|
__tablename__ = "addresses"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
street = Column(String)
|
street = Column(String)
|
||||||
city = Column(String)
|
city = Column(String)
|
||||||
zipcode = Column(String)
|
zipcode = Column(String)
|
||||||
@ -81,7 +81,7 @@ class Dewar(Base):
|
|||||||
class Proposal(Base):
|
class Proposal(Base):
|
||||||
__tablename__ = "proposals"
|
__tablename__ = "proposals"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
number = Column(String)
|
number = Column(String)
|
||||||
|
|
||||||
shipments = relationship("Shipment", back_populates="proposal")
|
shipments = relationship("Shipment", back_populates="proposal")
|
||||||
@ -90,21 +90,21 @@ class Proposal(Base):
|
|||||||
class Puck(Base):
|
class Puck(Base):
|
||||||
__tablename__ = 'pucks'
|
__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_name = Column(String, index=True)
|
||||||
puck_type = Column(String)
|
puck_type = Column(String)
|
||||||
puck_location_in_dewar = Column(Integer)
|
puck_location_in_dewar = Column(Integer)
|
||||||
|
|
||||||
# Foreign keys and relationships
|
# Foreign keys and relationships
|
||||||
dewar_id = Column(Integer, ForeignKey('dewars.id'))
|
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")
|
samples = relationship("Sample", back_populates="puck")
|
||||||
|
|
||||||
|
|
||||||
class Sample(Base):
|
class Sample(Base):
|
||||||
__tablename__ = 'samples'
|
__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
|
sample_name = Column(String, index=True) # Matches `sample_name` in data creation
|
||||||
position = Column(Integer) # Matches `position` in data creation script
|
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 sqlalchemy.orm import Session, joinedload
|
||||||
from typing import List
|
from typing import List
|
||||||
import uuid
|
import uuid
|
||||||
from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate, Sample as SampleSchema, Puck as PuckSchema
|
from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate
|
||||||
from app.models import Dewar as DewarModel, Puck as PuckModel, Sample as SampleModel
|
from app.models import Dewar as DewarModel, Puck as PuckModel
|
||||||
from app.dependencies import get_db
|
from app.dependencies import get_db
|
||||||
|
|
||||||
router = APIRouter()
|
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)
|
@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(
|
dewar = db.query(DewarModel).options(
|
||||||
joinedload(DewarModel.pucks).joinedload(PuckModel.positions)
|
joinedload(DewarModel.pucks).joinedload(PuckModel.positions)
|
||||||
).filter(DewarModel.id == dewar_id).first()
|
).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)
|
@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()
|
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||||
|
|
||||||
if not dewar:
|
if not dewar:
|
||||||
@ -70,3 +70,14 @@ async def update_dewar(dewar_id: str, dewar_update: DewarUpdate, db: Session = D
|
|||||||
db.refresh(dewar)
|
db.refresh(dewar)
|
||||||
|
|
||||||
return 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 fastapi import APIRouter, HTTPException, status, Query, Depends
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
import logging
|
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, \
|
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, \
|
from app.schemas import ShipmentCreate, UpdateShipmentComments, Shipment as ShipmentSchema, DewarUpdate, \
|
||||||
ContactPerson as ContactPersonSchema, Sample as SampleSchema, DewarCreate, PuckCreate, SampleCreate
|
ContactPerson as ContactPersonSchema, Sample as SampleSchema, DewarCreate, PuckCreate, SampleCreate
|
||||||
from app.database import get_db
|
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)
|
@router.delete("/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_shipment(id: int, db: Session = Depends(get_db)):
|
async def delete_shipment(shipment_id: int, db: Session = Depends(get_db)):
|
||||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||||
if not shipment:
|
if not shipment:
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||||
db.delete(shipment)
|
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)
|
@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))
|
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:
|
if not shipment:
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
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)
|
@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)):
|
async def add_dewar_to_shipment(shipment_id: int, dewar_id: int, db: Session = Depends(get_db)):
|
||||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||||
if not shipment:
|
if not shipment:
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||||
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
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])
|
@router.get("/{shipment_id}/samples", response_model=List[SampleSchema])
|
||||||
def get_samples_in_shipment(id: int, db: Session = Depends(get_db)):
|
async def get_samples_in_shipment(shipment_id: int, db: Session = Depends(get_db)):
|
||||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||||
if shipment is None:
|
if shipment is None:
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
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])
|
@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)
|
shipment = get_shipment_by_id(db, shipment_id)
|
||||||
if not shipment:
|
if not shipment:
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
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)
|
@router.put("/{shipment_id}/comments", response_model=ShipmentSchema)
|
||||||
async def update_shipment_comments(id: int, comments_data: UpdateShipmentComments, db: Session = Depends(get_db)):
|
async def update_shipment_comments(shipment_id: int, comments_data: UpdateShipmentComments,
|
||||||
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == id).first()
|
db: Session = Depends(get_db)):
|
||||||
|
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||||
if not shipment:
|
if not shipment:
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
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)
|
@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()
|
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
|
||||||
if not shipment:
|
if not shipment:
|
||||||
|
logging.error(f"Shipment not found with ID: {shipment_id}")
|
||||||
raise HTTPException(status_code=404, detail="Shipment not found")
|
raise HTTPException(status_code=404, detail="Shipment not found")
|
||||||
|
|
||||||
for dewar_data in payload.dewars:
|
try:
|
||||||
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:
|
|
||||||
dewar = DewarModel(
|
dewar = DewarModel(
|
||||||
shipment_id=shipment_id,
|
shipment_id=shipment_id,
|
||||||
dewar_name=dewar_data.dewar_name, # Ensure this field aligns with the frontend
|
dewar_name=payload.dewar_name,
|
||||||
tracking_number=dewar_data.tracking_number,
|
tracking_number=payload.tracking_number,
|
||||||
status=dewar_data.status,
|
status=payload.status,
|
||||||
contact_person_id=dewar_data.contact_person_id,
|
contact_person_id=payload.contact_person_id,
|
||||||
return_address_id=dewar_data.return_address_id,
|
return_address_id=payload.return_address_id,
|
||||||
)
|
)
|
||||||
db.add(dewar)
|
db.add(dewar)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(dewar)
|
db.refresh(dewar)
|
||||||
|
|
||||||
for puck_data in dewar_data.pucks:
|
for puck_data in payload.pucks:
|
||||||
puck = PuckModel(
|
puck = PuckModel(
|
||||||
dewar_id=dewar.id,
|
dewar_id=dewar.id,
|
||||||
puck_name=puck_data.puck_name,
|
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:
|
for sample_data in puck_data.samples:
|
||||||
sample = SampleModel(
|
sample = SampleModel(
|
||||||
puck_id=puck.id,
|
puck_id=puck.id,
|
||||||
sample_name=sample_data.crystalname,
|
sample_name=sample_data.sample_name,
|
||||||
position=sample_data.positioninpuck,
|
position=sample_data.position,
|
||||||
data_collection_parameters=sample_data.data_collection_parameters
|
data_collection_parameters=sample_data.data_collection_parameters,
|
||||||
)
|
)
|
||||||
db.add(sample)
|
db.add(sample)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(sample)
|
db.refresh(sample)
|
||||||
|
|
||||||
db.refresh(shipment)
|
db.refresh(shipment)
|
||||||
return 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}`);
|
console.log(`👀 Watching for changes in ${backendDirectory}`);
|
||||||
const watcher = chokidar.watch(backendDirectory, { persistent: true, ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY] });
|
const watcher = chokidar.watch(backendDirectory, { persistent: true, ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY] });
|
||||||
|
|
||||||
|
1594
frontend/package-lock.json
generated
1594
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/react": "^6.1.15",
|
||||||
"@fullcalendar/timegrid": "^6.1.15",
|
"@fullcalendar/timegrid": "^6.1.15",
|
||||||
"@mui/icons-material": "^6.1.5",
|
"@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",
|
"axios": "^1.7.7",
|
||||||
"chokidar": "^4.0.1",
|
"chokidar": "^4.0.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
@ -33,7 +34,7 @@
|
|||||||
"react-big-calendar": "^1.15.0",
|
"react-big-calendar": "^1.15.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-qr-code": "^2.0.15",
|
"react-qr-code": "^2.0.15",
|
||||||
"react-router-dom": "^6.27.0",
|
"react-router-dom": "^6.28.0",
|
||||||
"react-scheduler": "^0.1.0",
|
"react-scheduler": "^0.1.0",
|
||||||
"rimraf": "^5.0.10"
|
"rimraf": "^5.0.10"
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material';
|
import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import { ContactPerson, Address, Dewar, ContactsService, AddressesService, DewarsService, ShipmentsService } from '../../openapi';
|
import { ContactPerson, Address, Dewar, ContactsService, AddressesService, DewarsService, ShipmentsService } from '../../openapi'; // Adjust path if necessary
|
||||||
import Unipuck from '../components/Unipuck';
|
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 {
|
interface DewarDetailsProps {
|
||||||
dewar: Dewar;
|
dewar: Dewar;
|
||||||
@ -12,9 +13,24 @@ interface DewarDetailsProps {
|
|||||||
initialReturnAddresses?: Address[];
|
initialReturnAddresses?: Address[];
|
||||||
defaultContactPerson?: ContactPerson;
|
defaultContactPerson?: ContactPerson;
|
||||||
defaultReturnAddress?: Address;
|
defaultReturnAddress?: Address;
|
||||||
shipmentId: string;
|
shipmentId: number;
|
||||||
refreshShipments: () => void;
|
selectedShipment?: Shipment;
|
||||||
selectedShipment?: any;
|
}
|
||||||
|
|
||||||
|
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> = ({
|
const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||||
@ -26,24 +42,20 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
defaultContactPerson,
|
defaultContactPerson,
|
||||||
defaultReturnAddress,
|
defaultReturnAddress,
|
||||||
shipmentId,
|
shipmentId,
|
||||||
refreshShipments,
|
|
||||||
selectedShipment,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [localTrackingNumber, setLocalTrackingNumber] = useState(trackingNumber);
|
const [localTrackingNumber, setLocalTrackingNumber] = useState(trackingNumber);
|
||||||
const [contactPersons, setContactPersons] = useState(initialContactPersons);
|
const [contactPersons, setContactPersons] = useState(initialContactPersons);
|
||||||
const [returnAddresses, setReturnAddresses] = useState(initialReturnAddresses);
|
const [returnAddresses, setReturnAddresses] = useState(initialReturnAddresses);
|
||||||
const [selectedContactPerson, setSelectedContactPerson] = useState('');
|
const [selectedContactPerson, setSelectedContactPerson] = useState<string>('');
|
||||||
const [selectedReturnAddress, setSelectedReturnAddress] = useState('');
|
const [selectedReturnAddress, setSelectedReturnAddress] = useState<string>('');
|
||||||
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
|
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
|
||||||
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
|
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
|
||||||
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
|
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
|
||||||
const [newContactPerson, setNewContactPerson] = useState({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
|
const [newContactPerson, setNewContactPerson] = useState<NewContactPerson>({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
|
||||||
const [newReturnAddress, setNewReturnAddress] = useState({ id: 0, street: '', city: '', zipcode: '', country: '' });
|
const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({ id: 0, street: '', city: '', zipcode: '', country: '' });
|
||||||
const [changesMade, setChangesMade] = useState(false);
|
const [changesMade, setChangesMade] = useState(false);
|
||||||
const [feedbackMessage, setFeedbackMessage] = useState('');
|
const [feedbackMessage, setFeedbackMessage] = useState('');
|
||||||
const [openSnackbar, setOpenSnackbar] = useState(false);
|
const [openSnackbar, setOpenSnackbar] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState('');
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const setInitialContactPerson = () => {
|
const setInitialContactPerson = () => {
|
||||||
@ -99,17 +111,14 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(shipmentId, dewar.id);
|
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(shipmentId, dewar.id);
|
||||||
console.log("Fetched Samples: ", fetchedSamples);
|
console.log("Fetched Samples: ", fetchedSamples);
|
||||||
|
|
||||||
const updatedPuckStatuses = dewar.pucks.map(puck => {
|
const updatedPuckStatuses = (dewar.pucks ?? []).map(puck => {
|
||||||
// Filter samples for the current puck
|
|
||||||
const puckSamples = fetchedSamples.filter(sample => sample.puck_id === puck.id);
|
const puckSamples = fetchedSamples.filter(sample => sample.puck_id === puck.id);
|
||||||
|
|
||||||
// Initialize positions as 'empty'
|
|
||||||
const statusArray = Array(16).fill('empty');
|
const statusArray = Array(16).fill('empty');
|
||||||
|
|
||||||
// Update positions based on puckSamples' positions
|
|
||||||
puckSamples.forEach(sample => {
|
puckSamples.forEach(sample => {
|
||||||
if (sample.position >= 1 && sample.position <= 16) {
|
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);
|
setPuckStatuses(updatedPuckStatuses);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('Failed to load samples. Please try again later.');
|
console.error("Error fetching samples:", error);
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -194,7 +201,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveChanges = async () => {
|
const handleSaveChanges = async () => {
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string | null) => {
|
||||||
if (!dateString) return null;
|
if (!dateString) return null;
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
if (isNaN(date.getTime())) return null;
|
if (isNaN(date.getTime())) return null;
|
||||||
@ -228,14 +235,13 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
arrival_date: dewar.arrival_date,
|
arrival_date: dewar.arrival_date,
|
||||||
returning_date: dewar.returning_date,
|
returning_date: dewar.returning_date,
|
||||||
qrcode: dewar.qrcode,
|
qrcode: dewar.qrcode,
|
||||||
return_address_id: selectedReturnAddress,
|
return_address_id: parseInt(selectedReturnAddress ?? '', 10),
|
||||||
contact_person_id: selectedContactPerson,
|
contact_person_id: parseInt(selectedContactPerson ?? '', 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
||||||
setFeedbackMessage("Changes saved successfully.");
|
setFeedbackMessage("Changes saved successfully.");
|
||||||
setChangesMade(false);
|
setChangesMade(false);
|
||||||
refreshShipments();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFeedbackMessage("Failed to save changes. Please try again later.");
|
setFeedbackMessage("Failed to save changes. Please try again later.");
|
||||||
setOpenSnackbar(true);
|
setOpenSnackbar(true);
|
||||||
@ -270,7 +276,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ marginTop: 2 }}>
|
<Box sx={{ marginTop: 2 }}>
|
||||||
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
|
<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>
|
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="body1">Current Contact Person:</Typography>
|
<Typography variant="body1">Current Contact Person:</Typography>
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import React, { useState } from 'react';
|
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 AirplanemodeActiveIcon from '@mui/icons-material/AirplanemodeActive';
|
||||||
import StoreIcon from '@mui/icons-material/Store';
|
import StoreIcon from '@mui/icons-material/Store';
|
||||||
import RecycleIcon from '@mui/icons-material/Restore';
|
import RecycleIcon from '@mui/icons-material/Restore';
|
||||||
import { Dewar, DewarsService } from "../../openapi";
|
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 };
|
const ICON_STYLE = { width: 24, height: 24 };
|
||||||
|
|
||||||
// Inline SVG Component
|
// Custom SVG Icon Component
|
||||||
const BottleIcon: React.FC<{ fill: string }> = ({ fill }) => (
|
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"/>
|
<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>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Define types for icons mapping.
|
// Icons Mapping
|
||||||
const ICONS: { [key: number]: React.ReactElement } = {
|
const ICONS: { [key: number]: React.ReactElement } = {
|
||||||
0: <BottleIcon fill="grey" />,
|
0: <BottleIcon fill="grey" />,
|
||||||
1: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'blue' }} />,
|
1: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'blue' }} />,
|
||||||
@ -25,24 +25,38 @@ const ICONS: { [key: number]: React.ReactElement } = {
|
|||||||
5: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'orange' }} />,
|
5: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'orange' }} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface StepIconContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
// StepIconContainer Component
|
||||||
color: string;
|
interface StepIconContainerProps {
|
||||||
|
completed?: boolean;
|
||||||
|
active?: boolean;
|
||||||
|
error?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepIconContainer: React.FC<StepIconContainerProps> = ({ color, children, ...rest }) => (
|
const StepIconContainer: React.FC<StepIconContainerProps> = ({ completed, active, error, children }) => {
|
||||||
<div style={{ color }} {...rest}>
|
const className = [
|
||||||
{children}
|
completed ? 'completed' : '',
|
||||||
</div>
|
active ? 'active' : '',
|
||||||
);
|
error ? 'error' : '',
|
||||||
|
].join(' ').trim();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// StepIconComponent Props
|
||||||
type StepIconComponentProps = {
|
type StepIconComponentProps = {
|
||||||
icon: number;
|
icon: number;
|
||||||
dewar: Dewar;
|
dewar: Dewar;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
refreshShipments: () => void;
|
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 [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
|
||||||
const handleIconEnter = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleIconEnter = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
@ -74,9 +88,9 @@ const StepIconComponent = ({ icon, dewar, isSelected, refreshShipments, ...props
|
|||||||
contact_person_id: dewar.contact_person_id,
|
contact_person_id: dewar.contact_person_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id || '', payload);
|
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id, payload);
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
refreshShipments(); // Refresh shipments after status update
|
refreshShipments();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update dewar status:', error);
|
console.error('Failed to update dewar status:', error);
|
||||||
alert('Failed to update dewar status. Please try again.');
|
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 { iconIndex, color } = getIconProperties(icon, dewar);
|
||||||
const IconComponent = ICONS[iconIndex];
|
const IconComponent = ICONS[iconIndex];
|
||||||
const iconProps = iconIndex === 0 ? { fill: color } : {};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onMouseEnter={handleIconEnter}
|
onMouseEnter={handleIconEnter}
|
||||||
onMouseLeave={handleIconLeave}
|
onMouseLeave={handleIconLeave}
|
||||||
style={{ position: 'relative' }}
|
style={{ position: 'relative' }}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<StepIconContainer color={color} {...props}>
|
<StepIconContainer
|
||||||
|
completed={rest['aria-activedescendant'] ? true : undefined}
|
||||||
|
active={Boolean(rest['aria-activedescendant'])}
|
||||||
|
error={rest.role === 'error'}
|
||||||
|
>
|
||||||
{IconComponent
|
{IconComponent
|
||||||
? React.cloneElement(IconComponent, iconProps)
|
? React.cloneElement(IconComponent, iconIndex === 0 ? { fill: color } : {})
|
||||||
: <Typography variant="body2" color="error">Invalid icon</Typography>
|
: <Typography variant="body2" color="error">Invalid icon</Typography>
|
||||||
}
|
}
|
||||||
</StepIconContainer>
|
</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 getIconProperties = (icon: number, dewar: Dewar) => {
|
||||||
const status = dewar.status as DewarStatus;
|
const status = dewar.status as DewarStatus;
|
||||||
const iconIndex = status === 'Delayed' && icon === 1 ? 5 : icon;
|
const iconIndex = status === 'Delayed' && icon === 1 ? 5 : icon;
|
||||||
const color = determineIconColor(icon, status);
|
const color = determineIconColor(icon, status);
|
||||||
const fill = status === 'In Preparation' ? color : undefined;
|
return { iconIndex, color };
|
||||||
return { iconIndex, color, fill };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Steps of the stepper
|
||||||
const steps = ['In-House', 'Transit', 'At SLS', 'Returned'];
|
const steps = ['In-House', 'Transit', 'At SLS', 'Returned'];
|
||||||
|
|
||||||
|
// Props for the CustomStepper
|
||||||
type CustomStepperProps = {
|
type CustomStepperProps = {
|
||||||
dewar: Dewar;
|
dewar: Dewar;
|
||||||
selectedDewarId: string | null;
|
selectedDewarId: number | null;
|
||||||
refreshShipments: () => void; // Add refreshShipments prop
|
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 activeStep = getStatusStepIndex(dewar.status as DewarStatus);
|
||||||
const isSelected = dewar.id === selectedDewarId;
|
const isSelected = dewar.id === selectedDewarId;
|
||||||
|
|
||||||
|
@ -163,11 +163,13 @@ const ResponsiveAppBar: React.FC<ResponsiveAppBarProps> = ({ onOpenAddressManage
|
|||||||
{item.name}
|
{item.name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem key={item.name} onClick={handleCloseUserMenu}>
|
item.path && (
|
||||||
<Link to={item.path} style={{ textDecoration: 'none', color: 'inherit' }}>
|
<MenuItem key={item.name} onClick={handleCloseUserMenu}>
|
||||||
{item.name}
|
<Link to={item.path} style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||||
</Link>
|
{item.name}
|
||||||
</MenuItem>
|
</Link>
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
@ -4,7 +4,7 @@ import QRCode from 'react-qr-code';
|
|||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import CheckIcon from '@mui/icons-material/Check';
|
import CheckIcon from '@mui/icons-material/Check';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
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 { SxProps } from "@mui/system";
|
||||||
import CustomStepper from "./DewarStepper";
|
import CustomStepper from "./DewarStepper";
|
||||||
import DewarDetails from './DewarDetails';
|
import DewarDetails from './DewarDetails';
|
||||||
@ -14,10 +14,10 @@ const MAX_COMMENTS_LENGTH = 200;
|
|||||||
interface ShipmentDetailsProps {
|
interface ShipmentDetailsProps {
|
||||||
isCreatingShipment: boolean;
|
isCreatingShipment: boolean;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
selectedShipment: ShipmentsService | null;
|
selectedShipment: Shipment | null;
|
||||||
selectedDewar: Dewar | null;
|
selectedDewar: Dewar | null;
|
||||||
setSelectedDewar: React.Dispatch<React.SetStateAction<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;
|
refreshShipments: () => void;
|
||||||
defaultContactPerson?: ContactPerson;
|
defaultContactPerson?: ContactPerson;
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
setSelectedDewar(newSelection);
|
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?');
|
const confirmed = window.confirm('Are you sure you want to delete this dewar?');
|
||||||
if (confirmed && selectedShipment) {
|
if (confirmed && selectedShipment) {
|
||||||
try {
|
try {
|
||||||
@ -311,10 +311,9 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between'
|
||||||
}}>
|
}}>
|
||||||
<CustomStepper dewar={dewar} selectedDewarId={localSelectedDewar?.id} refreshShipments={refreshShipments} />
|
<CustomStepper dewar={dewar} selectedDewarId={localSelectedDewar?.id ?? null} refreshShipments={refreshShipments} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
{localSelectedDewar?.id === dewar.id && (
|
{localSelectedDewar?.id === dewar.id && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -333,21 +332,19 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{localSelectedDewar?.id === dewar.id && (
|
{localSelectedDewar?.id === dewar.id && (
|
||||||
<Box sx={{ padding: 2, border: '1px solid #ccc', borderRadius: '4px' }}>
|
<DewarDetails
|
||||||
<DewarDetails
|
dewar={localSelectedDewar}
|
||||||
dewar={localSelectedDewar}
|
trackingNumber={localSelectedDewar?.tracking_number || ''}
|
||||||
trackingNumber={localSelectedDewar.tracking_number || ''}
|
setTrackingNumber={(value) => {
|
||||||
setTrackingNumber={(value) => {
|
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
|
||||||
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
|
}}
|
||||||
}}
|
initialContactPersons={localSelectedDewar?.contact_person ? [localSelectedDewar.contact_person] : []} // Focus on dewar contact person
|
||||||
initialContactPersons={localSelectedDewar?.contact_person ? [localSelectedDewar.contact_person] : []} // Focus on dewar contact person
|
initialReturnAddresses={localSelectedDewar?.return_address ? [localSelectedDewar.return_address] : []} // Focus on dewar return address
|
||||||
initialReturnAddresses={localSelectedDewar?.return_address ? [localSelectedDewar.return_address] : []} // Focus on dewar return address
|
defaultContactPerson={localSelectedDewar?.contact_person ?? undefined} // Use `?? undefined`
|
||||||
defaultContactPerson={localSelectedDewar?.contact_person}
|
defaultReturnAddress={localSelectedDewar?.return_address ?? undefined} // Use `?? undefined`
|
||||||
defaultReturnAddress={localSelectedDewar?.return_address}
|
shipmentId={selectedShipment?.id ?? null}
|
||||||
shipmentId={selectedShipment?.id || ''}
|
refreshShipments={refreshShipments}
|
||||||
refreshShipments={refreshShipments}
|
/>
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
import { SelectChangeEvent } from '@mui/material';
|
import { SelectChangeEvent } from '@mui/material';
|
||||||
import { SxProps } from '@mui/system';
|
import { SxProps } from '@mui/system';
|
||||||
import {
|
import {
|
||||||
ContactPersonCreate, ContactPerson, Address, Proposal, ContactsService, AddressesService, ProposalsService,
|
ContactPersonCreate, ContactPerson, Address, Proposal, ContactsService, AddressesService, AddressCreate, ProposalsService,
|
||||||
OpenAPI, ShipmentCreate, ShipmentsService
|
OpenAPI, ShipmentCreate, ShipmentsService
|
||||||
} from '../../openapi';
|
} from '../../openapi';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
@ -98,7 +98,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshS
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
const { shipment_name, shipment_status } = newShipment;
|
const { shipment_name } = newShipment;
|
||||||
|
|
||||||
if (!shipment_name) return false;
|
if (!shipment_name) return false;
|
||||||
if (!selectedContactPersonId || !selectedReturnAddressId || !selectedProposalId) return false;
|
if (!selectedContactPersonId || !selectedReturnAddressId || !selectedProposalId) return false;
|
||||||
@ -431,7 +431,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshS
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleSaveShipment}
|
onClick={handleSaveShipment}
|
||||||
disabled={newShipment.comments?.length > MAX_COMMENTS_LENGTH}
|
disabled={(newShipment.comments?.length ?? 0) > MAX_COMMENTS_LENGTH}
|
||||||
>
|
>
|
||||||
Save Shipment
|
Save Shipment
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import { Button, Box, Typography, IconButton } from '@mui/material';
|
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 { Add as AddIcon, Delete as DeleteIcon, UploadFile as UploadFileIcon, Refresh as RefreshIcon } from '@mui/icons-material';
|
||||||
import UploadDialog from './UploadDialog';
|
import UploadDialog from './UploadDialog';
|
||||||
import { Dewar, ShipmentsService } from '../../openapi';
|
import { Dewar, Shipment, ShipmentsService } from '../../openapi';
|
||||||
import { SxProps } from '@mui/material';
|
import { SxProps } from '@mui/material';
|
||||||
import bottleGrey from '/src/assets/icons/bottle-svgrepo-com-grey.svg';
|
import bottleGrey from '/src/assets/icons/bottle-svgrepo-com-grey.svg';
|
||||||
import bottleYellow from '/src/assets/icons/bottle-svgrepo-com-yellow.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 {
|
interface ShipmentPanelProps {
|
||||||
setCreatingShipment: (value: boolean) => void;
|
setCreatingShipment: (value: boolean) => void;
|
||||||
selectShipment: (shipment: ShipmentsService | null) => void;
|
selectShipment: (shipment: Shipment | null) => void;
|
||||||
selectedShipment: ShipmentsService | null;
|
selectedShipment: Shipment | null;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
shipments: ShipmentsService[];
|
shipments: Shipment[];
|
||||||
refreshShipments: () => void;
|
refreshShipments: () => void;
|
||||||
error: string | null;
|
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;
|
if (!shipmentId) return;
|
||||||
try {
|
try {
|
||||||
await ShipmentsService.deleteShipmentShipmentsShipmentIdDelete(shipmentId);
|
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 isSelected = selectedShipment?.id === shipment.id;
|
||||||
const updatedShipment = isSelected ? null : shipment;
|
selectShipment(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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const openUploadDialog = (event: React.MouseEvent) => {
|
const openUploadDialog = (event: React.MouseEvent) => {
|
||||||
event.stopPropagation(); // Prevent event bubbling
|
event.stopPropagation();
|
||||||
setUploadDialogOpen(true);
|
setUploadDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeUploadDialog = () => setUploadDialogOpen(false);
|
const closeUploadDialog = () => setUploadDialogOpen(false);
|
||||||
|
|
||||||
const getNumberOfDewars = (shipment: ShipmentsService): number => shipment.dewars?.length || 0;
|
const getNumberOfDewars = (shipment: Shipment): number => shipment.dewars?.length || 0;
|
||||||
|
|
||||||
console.log("Current selected shipment:", selectedShipment); // debug log
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ width: '90%', borderRight: '1px solid #ccc', padding: 2, ...sx }}>
|
<Box sx={{ width: '90%', borderRight: '1px solid #ccc', padding: 2, ...sx }}>
|
||||||
@ -104,70 +96,67 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
<RefreshIcon />
|
<RefreshIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
{shipments.map((shipment) => (
|
{shipments.map((shipment) => {
|
||||||
<Button
|
const isSelected = selectedShipment?.id === shipment.id;
|
||||||
key={shipment.id}
|
return (
|
||||||
onClick={() => handleShipmentSelection(shipment)}
|
<Box
|
||||||
sx={{
|
key={shipment.id}
|
||||||
width: '100%',
|
sx={{
|
||||||
textAlign: 'left',
|
marginBottom: 1,
|
||||||
marginBottom: 1,
|
padding: '10px 16px',
|
||||||
color: 'white',
|
backgroundColor: isSelected ? '#52893e' : '#424242',
|
||||||
whiteSpace: 'nowrap',
|
display: 'flex',
|
||||||
overflow: 'hidden',
|
justifyContent: 'space-between',
|
||||||
textOverflow: 'ellipsis',
|
alignItems: 'center',
|
||||||
padding: '10px 16px',
|
color: 'white',
|
||||||
fontSize: '0.7rem',
|
borderRadius: 1,
|
||||||
display: 'flex',
|
'&:hover': {
|
||||||
alignItems: 'center',
|
backgroundColor: isSelected ? '#9aca8c' : '#616161'
|
||||||
justifyContent: 'space-between',
|
},
|
||||||
backgroundColor: selectedShipment?.id === shipment.id ? '#52893e' : '#424242',
|
cursor: 'pointer'
|
||||||
'&:hover': {
|
}}
|
||||||
backgroundColor: selectedShipment?.id === shipment.id ? '#9aca8c' : '#616161',
|
onClick={() => handleShipmentSelection(shipment)}
|
||||||
},
|
>
|
||||||
'&:active': {
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
backgroundColor: selectedShipment?.id === shipment.id ? '#915151' : '#212121',
|
<Box sx={{ position: 'relative', marginRight: 2 }}>
|
||||||
},
|
<img
|
||||||
}}
|
src={statusIconMap[shipment.shipment_status] || bottleGrey}
|
||||||
>
|
alt={`Status: ${shipment.shipment_status}`}
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
width="24"
|
||||||
<Box sx={{ position: 'relative', marginRight: '8px' }}>
|
/>
|
||||||
<img
|
<Typography
|
||||||
src={statusIconMap[shipment.shipment_status] || bottleGrey}
|
component="span"
|
||||||
alt={`Status: ${shipment.shipment_status}`}
|
sx={{
|
||||||
width="24"
|
position: 'absolute',
|
||||||
/>
|
top: '0%',
|
||||||
<Typography
|
right: '0%',
|
||||||
component="span"
|
transform: 'translate(50%, -50%)',
|
||||||
sx={{
|
color: 'white',
|
||||||
position: 'absolute',
|
fontWeight: 'bold',
|
||||||
top: '0%',
|
fontSize: '0.6rem',
|
||||||
right: '0%',
|
backgroundColor: 'transparent',
|
||||||
transform: 'translate(50%, -50%)',
|
borderRadius: '50%',
|
||||||
color: 'white',
|
padding: '0 2px',
|
||||||
fontWeight: 'bold',
|
}}
|
||||||
fontSize: '0.6rem',
|
>
|
||||||
backgroundColor: 'transparent',
|
{getNumberOfDewars(shipment)}
|
||||||
borderRadius: '50%',
|
</Typography>
|
||||||
padding: '0 2px',
|
</Box>
|
||||||
}}
|
<Box>
|
||||||
>
|
<Typography>{shipment.shipment_name}</Typography>
|
||||||
{getNumberOfDewars(shipment)}
|
<Typography sx={{ fontSize: '0.6rem', color: '#ccc' }}>{shipment.shipment_date}</Typography>
|
||||||
</Typography>
|
<Typography sx={{ fontSize: '0.6rem', color: '#ccc' }}>
|
||||||
|
Total Pucks: {shipment.dewars?.reduce((total, dewar: Dewar) => total + (dewar.number_of_pucks || 0), 0) ?? 0}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
{isSelected && (
|
||||||
<Typography>{shipment.shipment_name}</Typography>
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<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)}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
{selectedShipment?.id === shipment.id && (
|
|
||||||
<>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={openUploadDialog}
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
openUploadDialog(event);
|
||||||
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
title="Upload Sample Data Sheet"
|
title="Upload Sample Data Sheet"
|
||||||
sx={{ marginLeft: 1 }}
|
sx={{ marginLeft: 1 }}
|
||||||
@ -177,7 +166,6 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
console.log('Delete button clicked'); // debug log
|
|
||||||
handleDeleteShipment();
|
handleDeleteShipment();
|
||||||
}}
|
}}
|
||||||
color="error"
|
color="error"
|
||||||
@ -186,15 +174,15 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Button>
|
);
|
||||||
))}
|
})}
|
||||||
<UploadDialog
|
<UploadDialog
|
||||||
open={uploadDialogOpen}
|
open={uploadDialogOpen}
|
||||||
onClose={closeUploadDialog}
|
onClose={closeUploadDialog}
|
||||||
selectedShipment={selectedShipment} // <-- Pass selectedShipment here
|
selectedShipment={selectedShipment}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -17,12 +17,6 @@ import { SpreadsheetService, ShipmentsService, DewarsService, ApiError } from '.
|
|||||||
import * as ExcelJS from 'exceljs';
|
import * as ExcelJS from 'exceljs';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
const HeaderMapping = {
|
|
||||||
'dewarname': 'dewar_name',
|
|
||||||
'trackingnumber': 'tracking_number',
|
|
||||||
'status': 'status'
|
|
||||||
};
|
|
||||||
|
|
||||||
const SpreadsheetTable = ({
|
const SpreadsheetTable = ({
|
||||||
raw_data,
|
raw_data,
|
||||||
errors,
|
errors,
|
||||||
@ -50,6 +44,7 @@ const SpreadsheetTable = ({
|
|||||||
dewar_name: '',
|
dewar_name: '',
|
||||||
tracking_number: 'UNKNOWN',
|
tracking_number: 'UNKNOWN',
|
||||||
status: 'In preparation',
|
status: 'In preparation',
|
||||||
|
pucks: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const [newDewar, setNewDewar] = useState(initialNewDewarState);
|
const [newDewar, setNewDewar] = useState(initialNewDewarState);
|
||||||
@ -77,7 +72,6 @@ const SpreadsheetTable = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialNonEditableCells = new Set();
|
const initialNonEditableCells = new Set();
|
||||||
|
|
||||||
raw_data.forEach((row, rowIndex) => {
|
raw_data.forEach((row, rowIndex) => {
|
||||||
headers.forEach((_, colIndex) => {
|
headers.forEach((_, colIndex) => {
|
||||||
const key = `${row.row_num}-${headers[colIndex]}`;
|
const key = `${row.row_num}-${headers[colIndex]}`;
|
||||||
@ -86,7 +80,6 @@ const SpreadsheetTable = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
setNonEditableCells(initialNonEditableCells);
|
setNonEditableCells(initialNonEditableCells);
|
||||||
}, [raw_data, headers, errorMap]);
|
}, [raw_data, headers, errorMap]);
|
||||||
|
|
||||||
@ -148,35 +141,7 @@ const SpreadsheetTable = ({
|
|||||||
'dewarname': 0,
|
'dewarname': 0,
|
||||||
'puckname': 1,
|
'puckname': 1,
|
||||||
'pucktype': 2,
|
'pucktype': 2,
|
||||||
'crystalname': 3,
|
// Add other fields as needed
|
||||||
'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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createDewarsFromSheet = async (data, contactPerson, returnAddress) => {
|
const createDewarsFromSheet = async (data, contactPerson, returnAddress) => {
|
||||||
@ -185,7 +150,13 @@ const SpreadsheetTable = ({
|
|||||||
return null;
|
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) {
|
for (const row of data) {
|
||||||
if (!row.data) {
|
if (!row.data) {
|
||||||
@ -193,46 +164,74 @@ const SpreadsheetTable = ({
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dewarNameIdx = fieldToCol['dewarname'];
|
const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null;
|
||||||
const dewarName = row.data[dewarNameIdx]?.trim();
|
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)) {
|
console.log(`Processing Dewar: ${dewarName}, Puck: ${puckName}, Type: ${puckType}`);
|
||||||
const newDewarToPost = {
|
|
||||||
...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,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
if (dewarName) {
|
||||||
const createdDewar = await DewarsService.createDewarDewarsPost(newDewarToPost);
|
let dewar;
|
||||||
if (createdDewar && selectedShipment) {
|
if (!dewars.has(dewarName)) {
|
||||||
const updatedShipment = await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(
|
dewar = {
|
||||||
selectedShipment.id,
|
...initialNewDewarState,
|
||||||
createdDewar.id
|
dewar_name: dewarName,
|
||||||
);
|
contact_person_id: contactPerson.id,
|
||||||
dewars.set(dewarName, updatedShipment); // Track the added dewar
|
return_address_id: returnAddress.id,
|
||||||
}
|
pucks: []
|
||||||
} catch (error) {
|
};
|
||||||
console.error(`Error adding dewar for row: ${row.row_num}`, error);
|
dewars.set(dewarName, dewar);
|
||||||
if (error instanceof ApiError && error.body) {
|
puckPositionInDewar = 1;
|
||||||
console.error('Validation errors:', error.body.detail);
|
console.log(`Created new dewar: ${dewarName}`);
|
||||||
} else {
|
} else {
|
||||||
console.error('Unexpected error:', error);
|
dewar = dewars.get(dewarName);
|
||||||
}
|
puckPositionInDewar++;
|
||||||
|
console.log(`Found existing dewar: ${dewarName}`);
|
||||||
}
|
}
|
||||||
} else if (!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');
|
console.error('Dewar name is missing in the row');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(dewars.values());
|
const dewarsArray = Array.from(dewars.values());
|
||||||
|
for (const dewar of dewarsArray) {
|
||||||
|
try {
|
||||||
|
// 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) {
|
||||||
|
await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(
|
||||||
|
selectedShipment.id,
|
||||||
|
createdDewar.id
|
||||||
|
);
|
||||||
|
console.log(`Added dewar to shipment: ${createdDewar.id}`);
|
||||||
|
}
|
||||||
|
} catch (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dewarsArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (isSubmitting) return; // Prevent multiple submissions
|
if (isSubmitting) return;
|
||||||
if (!headers || headers.length === 0) {
|
if (!headers || headers.length === 0) {
|
||||||
console.error('Cannot submit, headers are not defined or empty');
|
console.error('Cannot submit, headers are not defined or empty');
|
||||||
return;
|
return;
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Grid from '@mui/material/Grid';
|
|
||||||
import ShipmentPanel from '../components/ShipmentPanel';
|
import ShipmentPanel from '../components/ShipmentPanel';
|
||||||
import ShipmentDetails from '../components/ShipmentDetails';
|
import ShipmentDetails from '../components/ShipmentDetails';
|
||||||
import ShipmentForm from '../components/ShipmentForm';
|
import ShipmentForm from '../components/ShipmentForm';
|
||||||
import { Dewar, OpenAPI, ContactPerson, ShipmentsService } from '../../openapi';
|
import { Dewar, OpenAPI, Shipment } from '../../openapi';
|
||||||
import useShipments from '../hooks/useShipments';
|
import useShipments from '../hooks/useShipments';
|
||||||
|
import { Grid, Container } from '@mui/material';
|
||||||
|
|
||||||
|
// Define props for Shipments View
|
||||||
type ShipmentViewProps = React.PropsWithChildren<Record<string, never>>;
|
type ShipmentViewProps = React.PropsWithChildren<Record<string, never>>;
|
||||||
|
|
||||||
const API_BASE_URL = 'http://127.0.0.1:8000';
|
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 ShipmentView: React.FC<ShipmentViewProps> = () => {
|
||||||
const { shipments, error, defaultContactPerson, fetchAndSetShipments } = useShipments();
|
const { shipments, error, defaultContactPerson, fetchAndSetShipments } = useShipments();
|
||||||
const [selectedShipment, setSelectedShipment] = useState<ShipmentsService | null>(null);
|
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
||||||
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
|
||||||
|
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Updated shipments:', shipments);
|
console.log('Updated shipments:', shipments);
|
||||||
}, [shipments]);
|
}, [shipments]);
|
||||||
|
|
||||||
// Handlers for selecting shipment and canceling form
|
// Handlers for selecting shipment and canceling form
|
||||||
const handleSelectShipment = (shipment: ShipmentsService | null) => {
|
const handleSelectShipment = (shipment: Shipment | null) => {
|
||||||
setSelectedShipment(shipment);
|
setSelectedShipment(shipment);
|
||||||
setIsCreatingShipment(false);
|
setIsCreatingShipment(false);
|
||||||
};
|
};
|
||||||
@ -59,41 +60,43 @@ const ShipmentView: React.FC<ShipmentViewProps> = () => {
|
|||||||
return <div>No shipment details available.</div>;
|
return <div>No shipment details available.</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render the main layout
|
// Render the main layout using Grid for layout
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={2} sx={{ height: '100vh' }}>
|
<Container maxWidth={false} disableGutters sx={{ display: 'flex', height: '100vh' }}>
|
||||||
<Grid
|
<Grid container spacing={2} sx={{ height: '100vh' }}>
|
||||||
item
|
<Grid
|
||||||
xs={12}
|
item
|
||||||
md={3}
|
xs={12}
|
||||||
sx={{
|
md={3}
|
||||||
display: 'flex',
|
sx={{
|
||||||
flexDirection: 'column',
|
display: 'flex',
|
||||||
flexGrow: 0,
|
flexDirection: 'column',
|
||||||
}}
|
flexGrow: 0,
|
||||||
>
|
}}
|
||||||
<ShipmentPanel
|
>
|
||||||
setCreatingShipment={setIsCreatingShipment}
|
<ShipmentPanel
|
||||||
selectShipment={handleSelectShipment}
|
setCreatingShipment={setIsCreatingShipment}
|
||||||
shipments={shipments}
|
selectShipment={handleSelectShipment}
|
||||||
selectedShipment={selectedShipment}
|
shipments={shipments}
|
||||||
refreshShipments={fetchAndSetShipments}
|
selectedShipment={selectedShipment}
|
||||||
error={error}
|
refreshShipments={fetchAndSetShipments}
|
||||||
/>
|
error={error}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
md={9}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{renderShipmentContent()}
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
</Container>
|
||||||
item
|
|
||||||
xs={12}
|
|
||||||
md={8}
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
flexGrow: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{renderShipmentContent()}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user