Integrate pgroups for shipment data security

Added `pgroups` to secure and associate data with specific permission groups. Updated backend routers, database models, and API endpoints to handle authorization based on `pgroups`. Adjusted frontend components and hooks to support `pgroups` in data fetching and management workflows.
This commit is contained in:
GotthardG
2025-01-22 22:53:37 +01:00
parent 4a1852882a
commit 173e192fc4
14 changed files with 123 additions and 92 deletions

View File

@ -284,6 +284,7 @@ specific_dewars3 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids3
shipments = [ shipments = [
Shipment( Shipment(
id=1, id=1,
pgroups="p20001, p20003",
shipment_date=datetime.strptime("2024-10-10", "%Y-%m-%d"), shipment_date=datetime.strptime("2024-10-10", "%Y-%m-%d"),
shipment_name="Shipment from Mordor", shipment_name="Shipment from Mordor",
shipment_status="Delivered", shipment_status="Delivered",
@ -295,6 +296,7 @@ shipments = [
), ),
Shipment( Shipment(
id=2, id=2,
pgroups="p20001, p20002",
shipment_date=datetime.strptime("2024-10-24", "%Y-%m-%d"), shipment_date=datetime.strptime("2024-10-24", "%Y-%m-%d"),
shipment_name="Shipment from Mordor", shipment_name="Shipment from Mordor",
shipment_status="In Transit", shipment_status="In Transit",
@ -306,6 +308,7 @@ shipments = [
), ),
Shipment( Shipment(
id=3, id=3,
pgroups="p20004",
shipment_date=datetime.strptime("2024-10-28", "%Y-%m-%d"), shipment_date=datetime.strptime("2024-10-28", "%Y-%m-%d"),
shipment_name="Shipment from Mordor", shipment_name="Shipment from Mordor",
shipment_status="In Transit", shipment_status="In Transit",

View File

@ -17,12 +17,13 @@ class Shipment(Base):
__tablename__ = "shipments" __tablename__ = "shipments"
id = Column(Integer, primary_key=True, index=True, autoincrement=True) id = Column(Integer, primary_key=True, index=True, autoincrement=True)
pgroups = Column(String(255), nullable=False)
shipment_name = Column(String(255), index=True) shipment_name = Column(String(255), index=True)
shipment_date = Column(Date) shipment_date = Column(Date, nullable=True)
shipment_status = Column(String(255)) shipment_status = Column(String(255), nullable=True)
comments = Column(String(200), nullable=True) comments = Column(String(200), nullable=True)
contact_id = Column(Integer, ForeignKey("contacts.id")) contact_id = Column(Integer, ForeignKey("contacts.id"), nullable=False)
return_address_id = Column(Integer, ForeignKey("addresses.id")) return_address_id = Column(Integer, ForeignKey("addresses.id"), nullable=False)
proposal_id = Column(Integer, ForeignKey("proposals.id"), nullable=True) proposal_id = Column(Integer, ForeignKey("proposals.id"), nullable=True)
contact = relationship("Contact", back_populates="shipments") contact = relationship("Contact", back_populates="shipments")

View File

@ -2,7 +2,7 @@ from .address import address_router
from .contact import contact_router from .contact import contact_router
from .proposal import router as proposal_router from .proposal import router as proposal_router
from .dewar import router as dewar_router from .dewar import router as dewar_router
from .shipment import router as shipment_router from .shipment import shipment_router
from .auth import router as auth_router from .auth import router as auth_router
from .protected_router import protected_router as protected_router from .protected_router import protected_router as protected_router

View File

@ -3,6 +3,7 @@ from fastapi import APIRouter, Depends
from app.routers.auth import get_current_user from app.routers.auth import get_current_user
from app.routers.address import address_router from app.routers.address import address_router
from app.routers.contact import contact_router from app.routers.contact import contact_router
from app.routers.shipment import shipment_router
protected_router = APIRouter( protected_router = APIRouter(
dependencies=[Depends(get_current_user)] # Applies to all routes dependencies=[Depends(get_current_user)] # Applies to all routes
@ -10,3 +11,6 @@ protected_router = APIRouter(
protected_router.include_router(address_router, prefix="/addresses", tags=["addresses"]) protected_router.include_router(address_router, prefix="/addresses", tags=["addresses"])
protected_router.include_router(contact_router, prefix="/contacts", tags=["contacts"]) protected_router.include_router(contact_router, prefix="/contacts", tags=["contacts"])
protected_router.include_router(
shipment_router, prefix="/shipments", tags=["shipments"]
)

View File

@ -1,7 +1,6 @@
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
import logging
from datetime import date from datetime import date
import json import json
@ -22,11 +21,13 @@ from app.schemas import (
Contact as ContactSchema, Contact as ContactSchema,
Sample as SampleSchema, Sample as SampleSchema,
DewarSchema, DewarSchema,
loginData,
) )
from app.database import get_db from app.database import get_db
from app.crud import get_shipments, get_shipment_by_id from app.crud import get_shipment_by_id
from app.routers.auth import get_current_user
router = APIRouter() shipment_router = APIRouter()
def default_serializer(obj): def default_serializer(obj):
@ -35,28 +36,30 @@ def default_serializer(obj):
raise TypeError(f"Type {type(obj)} not serializable") raise TypeError(f"Type {type(obj)} not serializable")
@router.get("", response_model=List[ShipmentSchema]) @shipment_router.get("", response_model=List[ShipmentSchema])
async def fetch_shipments( async def fetch_shipments(
id: Optional[int] = Query(None), db: Session = Depends(get_db) active_pgroup: str = Query(...),
db: Session = Depends(get_db),
current_user: loginData = Depends(get_current_user),
): ):
if id: # Validate that the active_pgroup belongs to the user
shipment = get_shipment_by_id(db, id) if active_pgroup not in current_user.pgroups:
if not shipment: raise HTTPException(
logging.error(f"Shipment with ID {id} not found") status_code=status.HTTP_400_BAD_REQUEST,
raise HTTPException(status_code=404, detail="Shipment not found") detail="Invalid pgroup provided.",
logging.info(f"Shipment found: {shipment}")
return [shipment]
shipments = get_shipments(db)
logging.info(f"Total shipments fetched: {len(shipments)}")
for shipment in shipments:
logging.info(
f"Shipment ID: {shipment.id}, Shipment Name: {shipment.shipment_name}"
) )
# Query shipments matching the active_pgroup
shipments = (
db.query(ShipmentModel)
.filter(ShipmentModel.pgroups.like(f"%{active_pgroup}%"))
.all()
)
return shipments return shipments
@router.get("/{shipment_id}/dewars", response_model=List[DewarSchema]) @shipment_router.get("/{shipment_id}/dewars", response_model=List[DewarSchema])
async def get_dewars_by_shipment_id(shipment_id: int, db: Session = Depends(get_db)): async def get_dewars_by_shipment_id(shipment_id: int, 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:
@ -69,7 +72,9 @@ async def get_dewars_by_shipment_id(shipment_id: int, db: Session = Depends(get_
return dewars return dewars
@router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED) @shipment_router.post(
"", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED
)
async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)): async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)):
contact = ( contact = (
db.query(ContactModel).filter(ContactModel.id == shipment.contact_id).first() db.query(ContactModel).filter(ContactModel.id == shipment.contact_id).first()
@ -94,6 +99,7 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
contact_id=contact.id, contact_id=contact.id,
return_address_id=return_address.id, return_address_id=return_address.id,
proposal_id=proposal.id, proposal_id=proposal.id,
pgroups=shipment.pgroups,
) )
# Handling dewars association # Handling dewars association
@ -111,7 +117,7 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
return db_shipment return db_shipment
@router.delete("/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT) @shipment_router.delete("/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_shipment(shipment_id: int, db: Session = Depends(get_db)): async def delete_shipment(shipment_id: int, db: Session = Depends(get_db)):
# Fetch the shipment # Fetch the shipment
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
@ -172,7 +178,7 @@ async def delete_shipment(shipment_id: int, db: Session = Depends(get_db)):
return return
@router.put("/{shipment_id}", response_model=ShipmentSchema) @shipment_router.put("/{shipment_id}", response_model=ShipmentSchema)
async def update_shipment( async def update_shipment(
shipment_id: int, updated_shipment: ShipmentCreate, db: Session = Depends(get_db) shipment_id: int, updated_shipment: ShipmentCreate, db: Session = Depends(get_db)
): ):
@ -251,7 +257,7 @@ async def update_shipment(
return shipment return shipment
@router.post("/{shipment_id}/add_dewar", response_model=ShipmentSchema) @shipment_router.post("/{shipment_id}/add_dewar", response_model=ShipmentSchema)
async def add_dewar_to_shipment( async def add_dewar_to_shipment(
shipment_id: int, dewar_id: int, db: Session = Depends(get_db) shipment_id: int, dewar_id: int, db: Session = Depends(get_db)
): ):
@ -269,7 +275,9 @@ async def add_dewar_to_shipment(
return shipment return shipment
@router.delete("/{shipment_id}/remove_dewar/{dewar_id}", response_model=ShipmentSchema) @shipment_router.delete(
"/{shipment_id}/remove_dewar/{dewar_id}", response_model=ShipmentSchema
)
async def remove_dewar_from_shipment( async def remove_dewar_from_shipment(
shipment_id: int, dewar_id: int, db: Session = Depends(get_db) shipment_id: int, dewar_id: int, db: Session = Depends(get_db)
): ):
@ -338,13 +346,13 @@ async def remove_dewar_from_shipment(
return shipment return shipment
@router.get("/contact_persons", response_model=List[ContactSchema]) @shipment_router.get("/contact_persons", response_model=List[ContactSchema])
async def get_shipment_contact_persons(db: Session = Depends(get_db)): async def get_shipment_contact_persons(db: Session = Depends(get_db)):
contact_persons = db.query(ContactModel).all() contact_persons = db.query(ContactModel).all()
return contact_persons return contact_persons
@router.get("/{shipment_id}/samples", response_model=List[SampleSchema]) @shipment_router.get("/{shipment_id}/samples", response_model=List[SampleSchema])
async def get_samples_in_shipment(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 == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
if shipment is None: if shipment is None:
@ -358,7 +366,7 @@ async def get_samples_in_shipment(shipment_id: int, db: Session = Depends(get_db
return samples return samples
@router.get( @shipment_router.get(
"/shipments/{shipment_id}/dewars/{dewar_id}/samples", "/shipments/{shipment_id}/dewars/{dewar_id}/samples",
response_model=List[SampleSchema], response_model=List[SampleSchema],
) )
@ -381,7 +389,7 @@ async def get_samples_in_dewar(
return samples return samples
@router.put("/{shipment_id}/comments", response_model=ShipmentSchema) @shipment_router.put("/{shipment_id}/comments", response_model=ShipmentSchema)
async def update_shipment_comments( async def update_shipment_comments(
shipment_id: int, shipment_id: int,
comments_data: UpdateShipmentComments, comments_data: UpdateShipmentComments,

View File

@ -569,6 +569,7 @@ class Proposal(BaseModel):
class Shipment(BaseModel): class Shipment(BaseModel):
id: int id: int
pgroups: str
shipment_name: str shipment_name: str
shipment_date: date shipment_date: date
shipment_status: str shipment_status: str
@ -583,6 +584,7 @@ class Shipment(BaseModel):
class ShipmentCreate(BaseModel): class ShipmentCreate(BaseModel):
pgroups: str
shipment_name: str shipment_name: str
shipment_date: date shipment_date: date
shipment_status: str shipment_status: str
@ -597,6 +599,7 @@ class ShipmentCreate(BaseModel):
class UpdateShipmentComments(BaseModel): class UpdateShipmentComments(BaseModel):
pgroups: str
comments: str comments: str

View File

@ -8,7 +8,6 @@ from app import ssl_heidi
from app.routers import ( from app.routers import (
proposal, proposal,
dewar, dewar,
shipment,
puck, puck,
spreadsheet, spreadsheet,
logistics, logistics,
@ -157,7 +156,6 @@ app.include_router(protected_router, prefix="/protected", tags=["protected"])
app.include_router(auth.router, prefix="/auth", tags=["auth"]) app.include_router(auth.router, prefix="/auth", tags=["auth"])
app.include_router(proposal.router, prefix="/proposals", tags=["proposals"]) app.include_router(proposal.router, prefix="/proposals", tags=["proposals"])
app.include_router(dewar.router, prefix="/dewars", tags=["dewars"]) app.include_router(dewar.router, prefix="/dewars", tags=["dewars"])
app.include_router(shipment.router, prefix="/shipments", tags=["shipments"])
app.include_router(puck.router, prefix="/pucks", tags=["pucks"]) app.include_router(puck.router, prefix="/pucks", tags=["pucks"])
app.include_router(spreadsheet.router, tags=["spreadsheet"]) app.include_router(spreadsheet.router, tags=["spreadsheet"])
app.include_router(logistics.router, prefix="/logistics", tags=["logistics"]) app.include_router(logistics.router, prefix="/logistics", tags=["logistics"])

View File

@ -82,7 +82,7 @@ const App: React.FC = () => {
<Routes> <Routes>
<Route path="/login" element={<LoginView />} /> <Route path="/login" element={<LoginView />} />
<Route path="/" element={<ProtectedRoute element={<HomePage />} />} /> <Route path="/" element={<ProtectedRoute element={<HomePage />} />} />
<Route path="/shipments" element={<ProtectedRoute element={<ShipmentView activePgroup={activePgroup} />} />} /> <Route path="/shipments" element={<ProtectedRoute element={<ShipmentView pgroups={pgroups} activePgroup={activePgroup} />} />} />
<Route path="/planning" element={<ProtectedRoute element={<PlanningView />} />} /> <Route path="/planning" element={<ProtectedRoute element={<PlanningView />} />} />
<Route path="/results" element={<ProtectedRoute element={<ResultsView />} />} /> <Route path="/results" element={<ProtectedRoute element={<ResultsView />} />} />
</Routes> </Routes>

View File

@ -35,6 +35,8 @@ import DownloadIcon from '@mui/icons-material/Download';
interface DewarDetailsProps { interface DewarDetailsProps {
dewar: Dewar; dewar: Dewar;
pgroups: string;
activePgroup: string;
trackingNumber: string; trackingNumber: string;
setTrackingNumber: (trackingNumber: string) => void; setTrackingNumber: (trackingNumber: string) => void;
initialContacts?: Contact[]; initialContacts?: Contact[];
@ -45,7 +47,7 @@ interface DewarDetailsProps {
} }
interface NewContact { interface NewContact {
id: number; pgroups: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
phone_number: string; phone_number: string;
@ -53,7 +55,7 @@ interface NewContact {
} }
interface NewReturnAddress { interface NewReturnAddress {
id: number; pgroups: string;
street: string; street: string;
city: string; city: string;
zipcode: string; zipcode: string;
@ -61,6 +63,8 @@ interface NewReturnAddress {
} }
const DewarDetails: React.FC<DewarDetailsProps> = ({ const DewarDetails: React.FC<DewarDetailsProps> = ({
pgroups,
activePgroup,
dewar, dewar,
trackingNumber, trackingNumber,
setTrackingNumber, setTrackingNumber,
@ -80,6 +84,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]); const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
const [newContact, setNewContact] = useState<NewContact>({ const [newContact, setNewContact] = useState<NewContact>({
id: 0, id: 0,
pgroups: activePgroup,
firstName: '', firstName: '',
lastName: '', lastName: '',
phone_number: '', phone_number: '',
@ -87,6 +92,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
}); });
const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({ const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({
id: 0, id: 0,
pgroups: activePgroup,
street: '', street: '',
city: '', city: '',
zipcode: '', zipcode: '',
@ -166,8 +172,8 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
useEffect(() => { useEffect(() => {
const getContacts = async () => { const getContacts = async () => {
try { try {
const c = await ContactsService.getContactsContactsGet(); const c = await ContactsService.getContactsProtectedContactsGet(activePgroup);
setContactPersons(c); setContacts(c);
} catch { } catch {
setFeedbackMessage('Failed to load contact persons. Please try again later.'); setFeedbackMessage('Failed to load contact persons. Please try again later.');
setOpenSnackbar(true); setOpenSnackbar(true);
@ -176,7 +182,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
const getReturnAddresses = async () => { const getReturnAddresses = async () => {
try { try {
const a = await AddressesService.getReturnAddressesAddressesGet(); const a = await AddressesService.getReturnAddressesProtectedAddressesGet(activePgroup);
setReturnAddresses(a); setReturnAddresses(a);
} catch { } catch {
setFeedbackMessage('Failed to load return addresses. Please try again later.'); setFeedbackMessage('Failed to load return addresses. Please try again later.');
@ -192,7 +198,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
const fetchSamples = async () => { const fetchSamples = async () => {
if (dewar.id) { if (dewar.id) {
try { try {
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet( const fetchedSamples = await ShipmentsService.getSamplesInDewarProtectedShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(
shipmentId, shipmentId,
dewar.id dewar.id
); );
@ -276,31 +282,32 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
}; };
const handleAddContact = async () => { const handleAddContact = async () => {
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) || !newContactPerson.firstName || !newContactPerson.lastName) { if (!validateEmail(newContact.email) || !validatePhoneNumber(newContact.phone_number) || !newContact.firstName || !newContact.lastName) {
setFeedbackMessage('Please fill in all new contact person fields correctly.'); setFeedbackMessage('Please fill in all new contact person fields correctly.');
setOpenSnackbar(true); setOpenSnackbar(true);
return; return;
} }
const payload = { const payload = {
firstname: newContactPerson.firstName, pgroups: activePgroup,
lastname: newContactPerson.lastName, firstname: newContact.firstName,
phone_number: newContactPerson.phone_number, lastname: newContact.lastName,
email: newContactPerson.email, phone_number: newContact.phone_number,
email: newContact.email,
}; };
try { try {
const c = await ContactsService.createContactContactsPost(payload); const c = await ContactsService.createContactProtectedContactsPost(payload);
setContactPersons([...contactPersons, c]); setContacts([...contacts, c]);
setFeedbackMessage('Contact person added successfully.'); setFeedbackMessage('Contact person added successfully.');
setNewContactPerson({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' }); setNewContact({ pgroups: activePgroup, firstName: '', lastName: '', phone_number: '', email: '' });
setSelectedContactPerson(c.id?.toString() || ''); setSelectedContact(c.id?.toString() || '');
} catch { } catch {
setFeedbackMessage('Failed to create a new contact person. Please try again later.'); setFeedbackMessage('Failed to create a new contact person. Please try again later.');
} }
setOpenSnackbar(true); setOpenSnackbar(true);
setIsCreatingContactPerson(false); setIsCreatingContact(false);
setChangesMade(true); setChangesMade(true);
}; };
@ -312,6 +319,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
} }
const payload = { const payload = {
pgroups: activePgroup,
street: newReturnAddress.street.trim(), street: newReturnAddress.street.trim(),
city: newReturnAddress.city.trim(), city: newReturnAddress.city.trim(),
zipcode: newReturnAddress.zipcode.trim(), zipcode: newReturnAddress.zipcode.trim(),
@ -319,11 +327,11 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
}; };
try { try {
const a = await AddressesService.createReturnAddressAddressesPost(payload); const a = await AddressesService.createReturnAddressProtectedAddressesPost(payload);
setReturnAddresses([...returnAddresses, a]); setReturnAddresses([...returnAddresses, a]);
setFeedbackMessage('Return address added successfully.'); setFeedbackMessage('Return address added successfully.');
setNewReturnAddress({ setNewReturnAddress({
id: 0, pgroups: activePgroup,
street: '', street: '',
city: '', city: '',
zipcode: '', zipcode: '',
@ -347,7 +355,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
return date.toISOString().split('T')[0]; return date.toISOString().split('T')[0];
}; };
if (!selectedContactPerson || !selectedReturnAddress) { if (!selectedContact || !selectedReturnAddress) {
setFeedbackMessage('Please ensure all required fields are filled.'); setFeedbackMessage('Please ensure all required fields are filled.');
setOpenSnackbar(true); setOpenSnackbar(true);
return; return;
@ -375,7 +383,7 @@ 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,
return_address_id: parseInt(selectedReturnAddress ?? '', 10), return_address_id: parseInt(selectedReturnAddress ?? '', 10),
contact_id: parseInt(selectedContactPerson ?? '', 10), contact_id: parseInt(selectedContact ?? '', 10),
}; };
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload); await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
@ -554,30 +562,30 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
<Typography variant="body1">Current Contact Person:</Typography> <Typography variant="body1">Current Contact Person:</Typography>
<Select <Select
value={selectedContactPerson} value={selectedContact}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
setSelectedContactPerson(value); setSelectedContact(value);
setIsCreatingContactPerson(value === 'add'); setIsCreatingContact(value === 'add');
setChangesMade(true); setChangesMade(true);
}} }}
displayEmpty displayEmpty
sx={{ width: '300px', marginBottom: 2 }} sx={{ width: '300px', marginBottom: 2 }}
> >
{contactPersons.map((person) => ( {contacts.map((person) => (
<MenuItem key={person.id} value={person.id?.toString()}> <MenuItem key={person.id} value={person.id?.toString()}>
{person.firstname} {person.lastname} {person.firstname} {person.lastname}
</MenuItem> </MenuItem>
))} ))}
<MenuItem value="add">Add New Contact Person</MenuItem> <MenuItem value="add">Add New Contact Person</MenuItem>
</Select> </Select>
{isCreatingContactPerson && ( {isCreatingContact && (
<Box sx={{ marginBottom: 2 }}> <Box sx={{ marginBottom: 2 }}>
<TextField <TextField
label="First Name" label="First Name"
value={newContactPerson.firstName} value={newContact.firstName}
onChange={(e) => onChange={(e) =>
setNewContactPerson((prev) => ({ setNewContact((prev) => ({
...prev, ...prev,
firstName: e.target.value, firstName: e.target.value,
})) }))
@ -587,9 +595,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
/> />
<TextField <TextField
label="Last Name" label="Last Name"
value={newContactPerson.lastName} value={newContact.lastName}
onChange={(e) => onChange={(e) =>
setNewContactPerson((prev) => ({ setNewContact((prev) => ({
...prev, ...prev,
lastName: e.target.value, lastName: e.target.value,
})) }))
@ -599,9 +607,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
/> />
<TextField <TextField
label="Phone Number" label="Phone Number"
value={newContactPerson.phone_number} value={newContact.phone_number}
onChange={(e) => onChange={(e) =>
setNewContactPerson((prev) => ({ setNewContact((prev) => ({
...prev, ...prev,
phone_number: e.target.value, phone_number: e.target.value,
})) }))
@ -611,9 +619,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
/> />
<TextField <TextField
label="Email" label="Email"
value={newContactPerson.email} value={newContact.email}
onChange={(e) => onChange={(e) =>
setNewContactPerson((prev) => ({ setNewContact((prev) => ({
...prev, ...prev,
email: e.target.value, email: e.target.value,
})) }))

View File

@ -13,6 +13,7 @@ const MAX_COMMENTS_LENGTH = 200;
interface ShipmentDetailsProps { interface ShipmentDetailsProps {
activePgroup: string; activePgroup: string;
pgroups: string;
isCreatingShipment: boolean; isCreatingShipment: boolean;
sx?: SxProps; sx?: SxProps;
selectedShipment: Shipment | null; selectedShipment: Shipment | null;
@ -24,6 +25,7 @@ interface ShipmentDetailsProps {
} }
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
pgroups,
activePgroup, activePgroup,
sx, sx,
selectedShipment, selectedShipment,
@ -82,7 +84,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
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 {
const updatedShipment = await ShipmentsService.removeDewarFromShipmentShipmentsShipmentIdRemoveDewarDewarIdDelete( const updatedShipment = await ShipmentsService.removeDewarFromShipmentProtectedShipmentsShipmentIdRemoveDewarDewarIdDelete(
selectedShipment.id, selectedShipment.id,
dewarId dewarId
); );
@ -133,7 +135,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
const createdDewar = await DewarsService.createOrUpdateDewarDewarsPost(selectedShipment.id, newDewarToPost); const createdDewar = await DewarsService.createOrUpdateDewarDewarsPost(selectedShipment.id, newDewarToPost);
if (createdDewar && selectedShipment) { if (createdDewar && selectedShipment) {
const updatedShipment = await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(selectedShipment.id, createdDewar.id); const updatedShipment = await ShipmentsService.addDewarToShipmentProtectedShipmentsShipmentIdAddDewarPost(selectedShipment.id, createdDewar.id);
setSelectedShipment(updatedShipment); setSelectedShipment(updatedShipment);
setIsAddingDewar(false); setIsAddingDewar(false);
setNewDewar(initialNewDewarState); setNewDewar(initialNewDewarState);
@ -159,7 +161,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
const payload = { comments }; const payload = { comments };
// Assuming `updateShipmentCommentsShipmentsShipmentIdCommentsPut` only needs the shipment ID // Assuming `updateShipmentCommentsShipmentsShipmentIdCommentsPut` only needs the shipment ID
const updatedShipment = await ShipmentsService.updateShipmentCommentsShipmentsShipmentIdCommentsPut(selectedShipment.id, payload); const updatedShipment = await ShipmentsService.updateShipmentCommentsProtectedShipmentsShipmentIdCommentsPut(selectedShipment.id, payload);
setSelectedShipment({ ...selectedShipment, comments: updatedShipment.comments }); setSelectedShipment({ ...selectedShipment, comments: updatedShipment.comments });
setInitialComments(comments); setInitialComments(comments);
@ -350,6 +352,8 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
{localSelectedDewar?.id === dewar.id && ( {localSelectedDewar?.id === dewar.id && (
<DewarDetails <DewarDetails
pgroups={pgroups}
activePgroup={activePgroup}
dewar={localSelectedDewar} dewar={localSelectedDewar}
trackingNumber={localSelectedDewar?.tracking_number || ''} trackingNumber={localSelectedDewar?.tracking_number || ''}
setTrackingNumber={(value) => { setTrackingNumber={(value) => {

View File

@ -11,8 +11,6 @@ import {
} from '../../openapi'; } from '../../openapi';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { CountryList } from './CountryList'; // Import the list of countries import { CountryList } from './CountryList'; // Import the list of countries
import { jwtDecode } from 'jwt-decode';
const MAX_COMMENTS_LENGTH = 200; const MAX_COMMENTS_LENGTH = 200;
@ -206,13 +204,13 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
return_address_id: selectedReturnAddressId!, return_address_id: selectedReturnAddressId!,
proposal_id: selectedProposalId!, proposal_id: selectedProposalId!,
dewars: newShipment.dewars || [], dewars: newShipment.dewars || [],
//pgroup: activePgroup, pgroups: activePgroup,
}; };
console.log('Shipment Payload being sent:', payload); console.log('Shipment Payload being sent:', payload);
try { try {
await ShipmentsService.createShipmentShipmentsPost(payload); await ShipmentsService.createShipmentProtectedShipmentsPost(payload);
setErrorMessage(null); setErrorMessage(null);
refreshShipments(); refreshShipments();
onCancel(); onCancel();

View File

@ -58,7 +58,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
if (!shipmentId) return; if (!shipmentId) return;
try { try {
await ShipmentsService.deleteShipmentShipmentsShipmentIdDelete(shipmentId); await ShipmentsService.deleteShipmentProtectedShipmentsShipmentIdDelete(shipmentId);
refreshShipments(); refreshShipments();
selectShipment(null); selectShipment(null);
alert("Shipment deleted successfully."); alert("Shipment deleted successfully.");

View File

@ -1,14 +1,14 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { ShipmentsService, Shipment, Contact } from '../../openapi'; import { ShipmentsService, Shipment, Contact } from '../../openapi';
const useShipments = () => { const useShipments = (activePgroup: string) => {
const [shipments, setShipments] = useState<Shipment[]>([]); const [shipments, setShipments] = useState<Shipment[]>([]);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [defaultContact, setDefaultContact] = useState<Contact | undefined>(); const [defaultContact, setDefaultContact] = useState<Contact | undefined>();
const fetchAndSetShipments = async () => { const fetchAndSetShipments = async () => {
try { try {
const shipmentsData = await ShipmentsService.fetchShipmentsShipmentsGet(); const shipmentsData = await ShipmentsService.fetchShipmentsProtectedShipmentsGet(activePgroup);
setShipments(shipmentsData); setShipments(shipmentsData);
} catch (error) { } catch (error) {
console.error('Failed to fetch shipments:', error); console.error('Failed to fetch shipments:', error);
@ -18,7 +18,7 @@ const useShipments = () => {
const fetchDefaultContact = async () => { const fetchDefaultContact = async () => {
try { try {
const contacts = await ShipmentsService.getShipmentContactPersonsShipmentsContactPersonsGet(); const contacts = await ShipmentsService.getShipmentContactPersonsProtectedShipmentsContactPersonsGet();
setDefaultContact(contacts[0]); setDefaultContact(contacts[0]);
} catch (error) { } catch (error) {
console.error('Failed to fetch contact persons:', error); console.error('Failed to fetch contact persons:', error);
@ -27,9 +27,11 @@ const useShipments = () => {
}; };
useEffect(() => { useEffect(() => {
fetchAndSetShipments(); if (activePgroup) {
fetchDefaultContact(); fetchAndSetShipments();
}, []); fetchDefaultContact();
}
}, [activePgroup]); // Refetch shipments when activePgroup changes
return { shipments, error, defaultContact, fetchAndSetShipments }; return { shipments, error, defaultContact, fetchAndSetShipments };
}; };

View File

@ -1,17 +1,18 @@
import React, { useState, useEffect } from 'react'; import React, {useState, useEffect} from 'react';
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, Shipment } from '../../openapi'; import {Dewar, OpenAPI, Shipment} from '../../openapi';
import useShipments from '../hooks/useShipments'; import useShipments from '../hooks/useShipments';
import { Grid, Container } from '@mui/material'; import {Grid, Container} from '@mui/material';
type ShipmentViewProps = { type ShipmentViewProps = {
activePgroup: string; activePgroup: string,
pgroups: string,
}; };
const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => { const ShipmentView: React.FC<ShipmentViewProps> = ({activePgroup, pgroups}) => {
const { shipments, error, defaultContact, fetchAndSetShipments } = useShipments(); const { shipments, error, defaultContact, fetchAndSetShipments } = useShipments(activePgroup);
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null); const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null); const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
const [isCreatingShipment, setIsCreatingShipment] = useState(false); const [isCreatingShipment, setIsCreatingShipment] = useState(false);
@ -29,8 +30,8 @@ const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
mode === 'test' mode === 'test'
? import.meta.env.VITE_OPENAPI_BASE_TEST ? import.meta.env.VITE_OPENAPI_BASE_TEST
: mode === 'prod' : mode === 'prod'
? import.meta.env.VITE_OPENAPI_BASE_PROD ? import.meta.env.VITE_OPENAPI_BASE_PROD
: import.meta.env.VITE_OPENAPI_BASE_DEV; : import.meta.env.VITE_OPENAPI_BASE_DEV;
// Log warning if `OpenAPI.BASE` is unresolved // Log warning if `OpenAPI.BASE` is unresolved
if (!OpenAPI.BASE) { if (!OpenAPI.BASE) {
@ -68,6 +69,7 @@ const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
if (selectedShipment) { if (selectedShipment) {
return ( return (
<ShipmentDetails <ShipmentDetails
pgroups={pgroups}
activePgroup={activePgroup} activePgroup={activePgroup}
isCreatingShipment={isCreatingShipment} isCreatingShipment={isCreatingShipment}
sx={{ flexGrow: 1 }} sx={{ flexGrow: 1 }}