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:
@ -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",
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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"]
|
||||||
|
)
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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"])
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
}))
|
}))
|
||||||
|
@ -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) => {
|
||||||
|
@ -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();
|
||||||
|
@ -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.");
|
||||||
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
@ -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 }}
|
||||||
|
Reference in New Issue
Block a user