diff --git a/backend/app/crud.py b/backend/app/crud.py
index 44af03a..2002b76 100644
--- a/backend/app/crud.py
+++ b/backend/app/crud.py
@@ -8,7 +8,7 @@ def get_shipments(db: Session):
shipments = (
db.query(Shipment)
.options(
- joinedload(Shipment.contact_person),
+ joinedload(Shipment.contact),
joinedload(Shipment.return_address),
joinedload(Shipment.proposal),
joinedload(Shipment.dewars),
@@ -30,7 +30,7 @@ def get_shipment_by_id(db: Session, id: int):
shipment = (
db.query(Shipment)
.options(
- joinedload(Shipment.contact_person),
+ joinedload(Shipment.contact),
joinedload(Shipment.return_address),
joinedload(Shipment.proposal),
joinedload(Shipment.dewars),
diff --git a/backend/app/data/data.py b/backend/app/data/data.py
index 55887e8..d5d494a 100644
--- a/backend/app/data/data.py
+++ b/backend/app/data/data.py
@@ -1,5 +1,5 @@
from app.models import (
- ContactPerson,
+ Contact,
Address,
Dewar,
Proposal,
@@ -34,71 +34,81 @@ serial_numbers = [
# Define contact persons
contacts = [
- ContactPerson(
+ Contact(
id=1,
+ pgroups="p20000, p20001",
firstname="Frodo",
lastname="Baggins",
phone_number="123-456-7890",
email="frodo.baggins@lotr.com",
),
- ContactPerson(
+ Contact(
id=2,
+ pgroups="p20000, p20002",
firstname="Samwise",
lastname="Gamgee",
phone_number="987-654-3210",
email="samwise.gamgee@lotr.com",
),
- ContactPerson(
+ Contact(
id=3,
+ pgroups="p20001, p20002",
firstname="Aragorn",
lastname="Elessar",
phone_number="123-333-4444",
email="aragorn.elessar@lotr.com",
),
- ContactPerson(
+ Contact(
id=4,
+ pgroups="p20003, p20004",
firstname="Legolas",
lastname="Greenleaf",
phone_number="555-666-7777",
email="legolas.greenleaf@lotr.com",
),
- ContactPerson(
+ Contact(
id=5,
+ pgroups="p20002, p20003",
firstname="Gimli",
lastname="Son of Gloin",
phone_number="888-999-0000",
email="gimli.sonofgloin@lotr.com",
),
- ContactPerson(
+ Contact(
id=6,
+ pgroups="p20001, p20002",
firstname="Gandalf",
lastname="The Grey",
phone_number="222-333-4444",
email="gandalf.thegrey@lotr.com",
),
- ContactPerson(
+ Contact(
id=7,
+ pgroups="p20000, p20004",
firstname="Boromir",
lastname="Son of Denethor",
phone_number="111-222-3333",
email="boromir.sonofdenethor@lotr.com",
),
- ContactPerson(
+ Contact(
id=8,
+ pgroups="p20001, p20002",
firstname="Galadriel",
lastname="Lady of Lothlórien",
phone_number="444-555-6666",
email="galadriel.lothlorien@lotr.com",
),
- ContactPerson(
+ Contact(
id=9,
+ pgroups="p20001, p20004",
firstname="Elrond",
lastname="Half-elven",
phone_number="777-888-9999",
email="elrond.halfelven@lotr.com",
),
- ContactPerson(
+ Contact(
id=10,
+ pgroups="p20004, p20006",
firstname="Eowyn",
lastname="Shieldmaiden of Rohan",
phone_number="000-111-2222",
@@ -184,7 +194,7 @@ dewars = [
dewar_serial_number_id=2,
tracking_number="TRACK123",
return_address_id=1,
- contact_person_id=1,
+ contact_id=1,
status="Ready for Shipping",
ready_date=datetime.strptime("2023-09-30", "%Y-%m-%d"),
shipping_date=None,
@@ -199,7 +209,7 @@ dewars = [
dewar_serial_number_id=1,
tracking_number="TRACK124",
return_address_id=2,
- contact_person_id=2,
+ contact_id=2,
status="In Preparation",
ready_date=None,
shipping_date=None,
@@ -214,7 +224,7 @@ dewars = [
dewar_serial_number_id=3,
tracking_number="TRACK125",
return_address_id=1,
- contact_person_id=3,
+ contact_id=3,
status="Not Shipped",
ready_date=datetime.strptime("2024-01-01", "%Y-%m-%d"),
shipping_date=None,
@@ -229,7 +239,7 @@ dewars = [
dewar_serial_number_id=4,
tracking_number="",
return_address_id=1,
- contact_person_id=3,
+ contact_id=3,
status="Delayed",
ready_date=datetime.strptime("2024-01-01", "%Y-%m-%d"),
shipping_date=datetime.strptime("2024-01-02", "%Y-%m-%d"),
@@ -244,7 +254,7 @@ dewars = [
dewar_serial_number_id=1,
tracking_number="",
return_address_id=1,
- contact_person_id=3,
+ contact_id=3,
status="Returned",
arrival_date=datetime.strptime("2024-01-03", "%Y-%m-%d"),
returning_date=datetime.strptime("2024-01-07", "%Y-%m-%d"),
@@ -277,7 +287,7 @@ shipments = [
shipment_date=datetime.strptime("2024-10-10", "%Y-%m-%d"),
shipment_name="Shipment from Mordor",
shipment_status="Delivered",
- contact_person_id=2,
+ contact_id=2,
proposal_id=3,
return_address_id=1,
comments="Handle with care",
@@ -288,7 +298,7 @@ shipments = [
shipment_date=datetime.strptime("2024-10-24", "%Y-%m-%d"),
shipment_name="Shipment from Mordor",
shipment_status="In Transit",
- contact_person_id=4,
+ contact_id=4,
proposal_id=4,
return_address_id=2,
comments="Contains the one ring",
@@ -299,7 +309,7 @@ shipments = [
shipment_date=datetime.strptime("2024-10-28", "%Y-%m-%d"),
shipment_name="Shipment from Mordor",
shipment_status="In Transit",
- contact_person_id=5,
+ contact_id=5,
proposal_id=5,
return_address_id=1,
comments="Contains the one ring",
diff --git a/backend/app/database.py b/backend/app/database.py
index bdd244a..f3dd8ed 100644
--- a/backend/app/database.py
+++ b/backend/app/database.py
@@ -80,7 +80,7 @@ def load_sample_data(session: Session):
)
# If any data exists, don't reseed
- if session.query(models.ContactPerson).first():
+ if session.query(models.Contact).first():
return
session.add_all(
diff --git a/backend/app/models.py b/backend/app/models.py
index 015a3ed..925be64 100644
--- a/backend/app/models.py
+++ b/backend/app/models.py
@@ -21,25 +21,27 @@ class Shipment(Base):
shipment_date = Column(Date)
shipment_status = Column(String(255))
comments = Column(String(200), nullable=True)
- contact_person_id = Column(Integer, ForeignKey("contact_persons.id"))
+ contact_id = Column(Integer, ForeignKey("contacts.id"))
return_address_id = Column(Integer, ForeignKey("addresses.id"))
proposal_id = Column(Integer, ForeignKey("proposals.id"), nullable=True)
- contact_person = relationship("ContactPerson", back_populates="shipments")
+ contact = relationship("Contact", back_populates="shipments")
return_address = relationship("Address", back_populates="shipments")
proposal = relationship("Proposal", back_populates="shipments")
dewars = relationship("Dewar", back_populates="shipment")
-class ContactPerson(Base):
- __tablename__ = "contact_persons"
+class Contact(Base):
+ __tablename__ = "contacts"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
- firstname = Column(String(255))
+ status = Column(String(255), default="active")
+ pgroups = Column(String(255), nullable=False)
+ firstname = Column(String(255), nullable=False)
lastname = Column(String(255))
phone_number = Column(String(255))
email = Column(String(255))
- shipments = relationship("Shipment", back_populates="contact_person")
+ shipments = relationship("Shipment", back_populates="contact")
class Address(Base):
@@ -91,11 +93,11 @@ class Dewar(Base):
unique_id = Column(String(255), unique=True, index=True, nullable=True)
shipment_id = Column(Integer, ForeignKey("shipments.id"))
return_address_id = Column(Integer, ForeignKey("addresses.id"))
- contact_person_id = Column(Integer, ForeignKey("contact_persons.id"))
+ contact_id = Column(Integer, ForeignKey("contacts.id"))
shipment = relationship("Shipment", back_populates="dewars")
return_address = relationship("Address")
- contact_person = relationship("ContactPerson")
+ contact = relationship("Contact")
pucks = relationship("Puck", back_populates="dewar")
dewar_type = relationship("DewarType")
diff --git a/backend/app/routers/__init__.py b/backend/app/routers/__init__.py
index 06abfac..8db84e8 100644
--- a/backend/app/routers/__init__.py
+++ b/backend/app/routers/__init__.py
@@ -1,5 +1,5 @@
-from .address import protected_router as address_router
-from .contact import router as contact_router
+from .address import address_router
+from .contact import contact_router
from .proposal import router as proposal_router
from .dewar import router as dewar_router
from .shipment import router as shipment_router
diff --git a/backend/app/routers/address.py b/backend/app/routers/address.py
index 303902f..f79ff25 100644
--- a/backend/app/routers/address.py
+++ b/backend/app/routers/address.py
@@ -1,4 +1,4 @@
-from fastapi import Depends, HTTPException, status, Query
+from fastapi import Depends, HTTPException, status, Query, APIRouter
from sqlalchemy.orm import Session
from sqlalchemy import or_
from typing import List
@@ -12,10 +12,11 @@ from app.schemas import (
)
from app.models import Address as AddressModel
from app.dependencies import get_db
-from app.routers.protected_router import protected_router
+
+address_router = APIRouter()
-@protected_router.get("/", response_model=List[AddressSchema])
+@address_router.get("/", response_model=List[AddressSchema])
async def get_return_addresses(
active_pgroup: str = Query(...),
db: Session = Depends(get_db),
@@ -36,7 +37,7 @@ async def get_return_addresses(
return user_addresses
-@protected_router.get("/all", response_model=List[AddressSchema])
+@address_router.get("/all", response_model=List[AddressSchema])
async def get_all_addresses(
db: Session = Depends(get_db),
current_user: loginData = Depends(get_current_user),
@@ -52,7 +53,7 @@ async def get_all_addresses(
return user_addresses
-@protected_router.post(
+@address_router.post(
"/", response_model=AddressSchema, status_code=status.HTTP_201_CREATED
)
async def create_return_address(address: AddressCreate, db: Session = Depends(get_db)):
@@ -81,7 +82,7 @@ async def create_return_address(address: AddressCreate, db: Session = Depends(ge
return db_address
-@protected_router.put("/{address_id}", response_model=AddressSchema)
+@address_router.put("/{address_id}", response_model=AddressSchema)
async def update_return_address(
address_id: int, address: AddressUpdate, db: Session = Depends(get_db)
):
@@ -140,7 +141,7 @@ async def update_return_address(
return new_address
-@protected_router.delete("/{address_id}", status_code=status.HTTP_204_NO_CONTENT)
+@address_router.delete("/{address_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_return_address(address_id: int, db: Session = Depends(get_db)):
db_address = db.query(AddressModel).filter(AddressModel.id == address_id).first()
if not db_address:
diff --git a/backend/app/routers/contact.py b/backend/app/routers/contact.py
index d64d117..fe0b059 100644
--- a/backend/app/routers/contact.py
+++ b/backend/app/routers/contact.py
@@ -1,36 +1,83 @@
-from fastapi import APIRouter, HTTPException, status, Depends
+from fastapi import APIRouter, HTTPException, status, Depends, Query
from sqlalchemy.orm import Session
+from sqlalchemy import or_
from typing import List
-from app.schemas import ContactPerson, ContactPersonCreate, ContactPersonUpdate
-from app.models import ContactPerson as ContactPersonModel
+
+from app.schemas import Contact, ContactCreate, ContactUpdate, loginData
+from app.models import Contact as ContactModel
from app.dependencies import get_db
+from app.routers.auth import get_current_user
-router = APIRouter()
+contact_router = APIRouter()
-# Existing routes
-@router.get("/", response_model=List[ContactPerson])
-async def get_contacts(db: Session = Depends(get_db)):
- return db.query(ContactPersonModel).all()
+# GET /contacts: Retrieve active contacts from the active_pgroup
+@contact_router.get("/", response_model=List[Contact])
+async def get_contacts(
+ active_pgroup: str = Query(...),
+ db: Session = Depends(get_db),
+ current_user: loginData = Depends(get_current_user),
+):
+ # Validate that the active_pgroup belongs to the user
+ if active_pgroup not in current_user.pgroups:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Invalid pgroup provided.",
+ )
+
+ # Query for active contacts in the active_pgroup
+ contacts = (
+ db.query(ContactModel)
+ .filter(
+ ContactModel.pgroups.like(f"%{active_pgroup}%"),
+ ContactModel.status == "active",
+ )
+ .all()
+ )
+ return contacts
-@router.post("/", response_model=ContactPerson, status_code=status.HTTP_201_CREATED)
-async def create_contact(contact: ContactPersonCreate, db: Session = Depends(get_db)):
+# GET /contacts/all: Retrieve all contacts from the user's pgroups
+@contact_router.get("/all", response_model=List[Contact])
+async def get_all_contacts(
+ db: Session = Depends(get_db),
+ current_user: loginData = Depends(get_current_user),
+):
+ # Query for all contacts belonging to any of the user's pgroups
+ user_pgroups = current_user.pgroups
+ filters = [ContactModel.pgroups.like(f"%{pgroup}%") for pgroup in user_pgroups]
+ contacts = db.query(ContactModel).filter(or_(*filters)).all()
+ return contacts
+
+
+@contact_router.post("/", response_model=Contact, status_code=status.HTTP_201_CREATED)
+async def create_contact(
+ contact: ContactCreate, # Body parameter ONLY
+ db: Session = Depends(get_db), # Secondary dependency for database access
+):
+ # Check if a contact with the same email already exists in this pgroup
if (
- db.query(ContactPersonModel)
- .filter(ContactPersonModel.email == contact.email)
+ db.query(ContactModel)
+ .filter(
+ ContactModel.email == contact.email,
+ ContactModel.pgroups.like(f"%{contact.pgroups}%"),
+ ContactModel.status == "active",
+ )
.first()
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
- detail="This contact already exists.",
+ detail="This contact already exists in the provided pgroup.",
)
- db_contact = ContactPersonModel(
+ # Create a new contact
+ db_contact = ContactModel(
firstname=contact.firstname,
lastname=contact.lastname,
phone_number=contact.phone_number,
email=contact.email,
+ pgroups=contact.pgroups, # Use the pgroups from the body
+ status="active", # Newly created contacts will be active
)
db.add(db_contact)
db.commit()
@@ -38,34 +85,78 @@ async def create_contact(contact: ContactPersonCreate, db: Session = Depends(get
return db_contact
-# New routes
-@router.put("/{contact_id}", response_model=ContactPerson)
+# PUT /contacts/{contact_id}: Update a contact
+@contact_router.put("/{contact_id}", response_model=Contact)
async def update_contact(
- contact_id: int, contact: ContactPersonUpdate, db: Session = Depends(get_db)
+ contact_id: int,
+ contact: ContactUpdate,
+ db: Session = Depends(get_db),
):
- db_contact = (
- db.query(ContactPersonModel).filter(ContactPersonModel.id == contact_id).first()
- )
+ # Retrieve the existing contact
+ db_contact = db.query(ContactModel).filter(ContactModel.id == contact_id).first()
if not db_contact:
raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found."
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Contact not found.",
)
- for key, value in contact.dict(exclude_unset=True).items():
- setattr(db_contact, key, value)
+ # Normalize existing and new pgroups (remove whitespace, handle case
+ # sensitivity if needed)
+ existing_pgroups = (
+ set(p.strip() for p in db_contact.pgroups.split(",") if p.strip())
+ if db_contact.pgroups
+ else set()
+ )
+ new_pgroups = (
+ set(p.strip() for p in contact.pgroups.split(",") if p.strip())
+ if contact.pgroups
+ else set()
+ )
+
+ # Check if any old pgroups are being removed (strict validation against removal)
+ if not new_pgroups.issuperset(existing_pgroups):
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Modifying pgroups to remove existing ones is not allowed.",
+ )
+
+ combined_pgroups = existing_pgroups.union(new_pgroups)
+
+ # Mark the old contact as inactive
+ db_contact.status = "inactive"
db.commit()
db.refresh(db_contact)
- return db_contact
-
-@router.delete("/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
-async def delete_contact(contact_id: int, db: Session = Depends(get_db)):
- db_contact = (
- db.query(ContactPersonModel).filter(ContactPersonModel.id == contact_id).first()
+ # Create a new contact with the updated data
+ new_contact = ContactModel(
+ firstname=contact.firstname or db_contact.firstname,
+ lastname=contact.lastname or db_contact.lastname,
+ phone_number=contact.phone_number or db_contact.phone_number,
+ email=contact.email or db_contact.email,
+ pgroups=",".join(combined_pgroups), # Use the active_pgroup
+ status="active", # Newly created contacts will be active
)
+ db.add(new_contact)
+ db.commit()
+ db.refresh(new_contact)
+
+ return new_contact
+
+
+# DELETE /contacts/{contact_id}: Mark a contact as inactive
+@contact_router.delete("/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
+async def delete_contact(
+ contact_id: int,
+ db: Session = Depends(get_db),
+):
+ # Retrieve the existing contact
+ db_contact = db.query(ContactModel).filter(ContactModel.id == contact_id).first()
if not db_contact:
raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found."
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Contact not found.",
)
- db.delete(db_contact)
+
+ # Mark the contact as inactive
+ db_contact.status = "inactive"
db.commit()
return
diff --git a/backend/app/routers/protected_router.py b/backend/app/routers/protected_router.py
index 62b0313..bb6a387 100644
--- a/backend/app/routers/protected_router.py
+++ b/backend/app/routers/protected_router.py
@@ -1,7 +1,12 @@
from fastapi import APIRouter, Depends
from app.routers.auth import get_current_user
+from app.routers.address import address_router
+from app.routers.contact import contact_router
protected_router = APIRouter(
dependencies=[Depends(get_current_user)] # Applies to all routes
)
+
+protected_router.include_router(address_router, prefix="/addresses", tags=["addresses"])
+protected_router.include_router(contact_router, prefix="/contacts", tags=["contacts"])
diff --git a/backend/app/routers/shipment.py b/backend/app/routers/shipment.py
index 1cb3cd0..f0e07e8 100644
--- a/backend/app/routers/shipment.py
+++ b/backend/app/routers/shipment.py
@@ -7,7 +7,7 @@ import json
from app.models import (
Shipment as ShipmentModel,
- ContactPerson as ContactPersonModel,
+ Contact as ContactModel,
Address as AddressModel,
Proposal as ProposalModel,
Dewar as DewarModel,
@@ -19,7 +19,7 @@ from app.schemas import (
ShipmentCreate,
UpdateShipmentComments,
Shipment as ShipmentSchema,
- ContactPerson as ContactPersonSchema,
+ Contact as ContactSchema,
Sample as SampleSchema,
DewarSchema,
)
@@ -71,10 +71,8 @@ async def get_dewars_by_shipment_id(shipment_id: int, db: Session = Depends(get_
@router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)):
- contact_person = (
- db.query(ContactPersonModel)
- .filter(ContactPersonModel.id == shipment.contact_person_id)
- .first()
+ contact = (
+ db.query(ContactModel).filter(ContactModel.id == shipment.contact_id).first()
)
return_address = (
db.query(AddressModel)
@@ -85,7 +83,7 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
db.query(ProposalModel).filter(ProposalModel.id == shipment.proposal_id).first()
)
- if not (contact_person or return_address or proposal):
+ if not (contact or return_address or proposal):
raise HTTPException(status_code=404, detail="Associated entity not found")
db_shipment = ShipmentModel(
@@ -93,7 +91,7 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
shipment_date=shipment.shipment_date,
shipment_status=shipment.shipment_status,
comments=shipment.comments,
- contact_person_id=contact_person.id,
+ contact_id=contact.id,
return_address_id=return_address.id,
proposal_id=proposal.id,
)
@@ -189,8 +187,8 @@ async def update_shipment(
# Validate relationships by IDs
contact_person = (
- db.query(ContactPersonModel)
- .filter(ContactPersonModel.id == updated_shipment.contact_person_id)
+ db.query(ContactModel)
+ .filter(ContactModel.id == updated_shipment.contact_person_id)
.first()
)
return_address = (
@@ -225,9 +223,7 @@ async def update_shipment(
for key, value in update_fields.items():
if key == "contact_person_id":
contact_person = (
- db.query(ContactPersonModel)
- .filter(ContactPersonModel.id == value)
- .first()
+ db.query(ContactModel).filter(ContactModel.id == value).first()
)
if not contact_person:
raise HTTPException(
@@ -342,9 +338,9 @@ async def remove_dewar_from_shipment(
return shipment
-@router.get("/contact_persons", response_model=List[ContactPersonSchema])
+@router.get("/contact_persons", response_model=List[ContactSchema])
async def get_shipment_contact_persons(db: Session = Depends(get_db)):
- contact_persons = db.query(ContactPersonModel).all()
+ contact_persons = db.query(ContactModel).all()
return contact_persons
diff --git a/backend/app/schemas.py b/backend/app/schemas.py
index b8e4a53..411fa3d 100644
--- a/backend/app/schemas.py
+++ b/backend/app/schemas.py
@@ -366,25 +366,24 @@ class Results(BaseModel):
pass
-class ContactPersonBase(BaseModel):
+class ContactCreate(BaseModel):
+ pgroups: str
firstname: str
lastname: str
phone_number: str
email: EmailStr
-class ContactPersonCreate(ContactPersonBase):
- pass
-
-
-class ContactPerson(ContactPersonBase):
+class Contact(ContactCreate):
id: int
+ status: str = "active"
class Config:
from_attributes = True
-class ContactPersonUpdate(BaseModel):
+class ContactUpdate(BaseModel):
+ pgroups: str
firstname: Optional[str] = None
lastname: Optional[str] = None
phone_number: Optional[str] = None
@@ -510,7 +509,7 @@ class DewarBase(BaseModel):
shipping_date: Optional[date]
arrival_date: Optional[date]
returning_date: Optional[date]
- contact_person_id: Optional[int]
+ contact_id: Optional[int]
return_address_id: Optional[int]
pucks: List[PuckCreate] = []
@@ -525,7 +524,7 @@ class DewarCreate(DewarBase):
class Dewar(DewarBase):
id: int
shipment_id: Optional[int]
- contact_person: Optional[ContactPerson]
+ contact: Optional[Contact]
return_address: Optional[Address]
pucks: List[Puck] = [] # List of pucks within this dewar
@@ -544,7 +543,7 @@ class DewarUpdate(BaseModel):
shipping_date: Optional[date] = None
arrival_date: Optional[date] = None
returning_date: Optional[date] = None
- contact_person_id: Optional[int] = None
+ contact_id: Optional[int] = None
address_id: Optional[int] = None
@@ -553,7 +552,7 @@ class DewarSchema(BaseModel):
dewar_name: str
tracking_number: str
status: str
- contact_person_id: int
+ contact_id: int
return_address_id: int
class Config:
@@ -574,7 +573,7 @@ class Shipment(BaseModel):
shipment_date: date
shipment_status: str
comments: Optional[str]
- contact_person: Optional[ContactPerson]
+ contact: Optional[Contact]
return_address: Optional[Address]
proposal: Optional[Proposal]
dewars: List[Dewar] = []
@@ -588,7 +587,7 @@ class ShipmentCreate(BaseModel):
shipment_date: date
shipment_status: str
comments: Optional[constr(max_length=200)]
- contact_person_id: int
+ contact_id: int
return_address_id: int
proposal_id: int
dewars: List[DewarCreate] = []
@@ -621,7 +620,7 @@ class SlotSchema(BaseModel):
retrievedTimestamp: Optional[str]
beamlineLocation: Optional[str]
shipment_name: Optional[str]
- contact_person: Optional[str]
+ contact: Optional[str]
local_contact: Optional[str]
class Config:
diff --git a/backend/main.py b/backend/main.py
index b6c62a3..3d3b3c2 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -6,8 +6,6 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app import ssl_heidi
from app.routers import (
- address,
- contact,
proposal,
dewar,
shipment,
@@ -157,8 +155,6 @@ def on_startup():
# Include routers with correct configuration
app.include_router(protected_router, prefix="/protected", tags=["protected"])
app.include_router(auth.router, prefix="/auth", tags=["auth"])
-app.include_router(contact.router, prefix="/contacts", tags=["contacts"])
-app.include_router(address.protected_router, prefix="/addresses", tags=["addresses"])
app.include_router(proposal.router, prefix="/proposals", tags=["proposals"])
app.include_router(dewar.router, prefix="/dewars", tags=["dewars"])
app.include_router(shipment.router, prefix="/shipments", tags=["shipments"])
diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py
index e0b0d5a..e939409 100644
--- a/backend/tests/test_auth.py
+++ b/backend/tests/test_auth.py
@@ -33,4 +33,7 @@ def test_protected_route():
headers = {"Authorization": f"Bearer {token}"}
response = client.get("/auth/protected-route", headers=headers)
assert response.status_code == 200
- assert response.json() == {"username": "testuser", "pgroups": [20000, 20001, 20003]}
+ assert response.json() == {
+ "username": "testuser",
+ "pgroups": [20000, 20001, 20002, 20003],
+ }
diff --git a/frontend/public/shipmentsdb.json b/frontend/public/shipmentsdb.json
index 1c0914c..fd8a6f8 100644
--- a/frontend/public/shipmentsdb.json
+++ b/frontend/public/shipmentsdb.json
@@ -6,7 +6,7 @@
"number_of_dewars": 2,
"shipment_status": "In Transit",
"shipment_date": "2024-01-15",
- "contact_person": [
+ "contact": [
{ "name": "Alice Johnson", "id": "alice" }
],
"dewars": [
@@ -19,7 +19,7 @@
"return_address": [
{ "address": "123 Main St, Anytown, USA", "id": "address1" }
],
- "contact_person": [
+ "contact": [
{ "name": "Alice Johnson", "id": "alice" }
],
"status": "in preparation",
@@ -40,7 +40,7 @@
"return_address": [
{ "address": "123 Main St, Anytown, USA", "id": "address1" }
],
- "contact_person": [
+ "contact": [
{ "name": "Alice Johnson", "id": "alice" }
],
"status": "in preparation",
@@ -60,7 +60,7 @@
"number_of_dewars": 3,
"shipment_status": "In Transit",
"shipment_date": "2024-02-20",
- "contact_person": [
+ "contact": [
{ "name": "Bob Smith", "id": "bob" }
],
"dewars": [
@@ -70,7 +70,7 @@
"tracking_number": "TRACK987654",
"number_of_pucks": 5,
"number_of_samples": 30,
- "contact_person": [
+ "contact": [
{ "name": "Bob Smith", "id": "bob" }
],
"return_address": [
@@ -91,7 +91,7 @@
"tracking_number": "TRACK876543",
"number_of_pucks": 6,
"number_of_samples": 36,
- "contact_person": [
+ "contact": [
{ "name": "Bob Smith", "id": "bob" }
],
"return_address": [
@@ -112,7 +112,7 @@
"tracking_number": "TRACK765432",
"number_of_pucks": 4,
"number_of_samples": 24,
- "contact_person": [
+ "contact": [
{ "name": "Bob Smith", "id": "bob" }
],
"return_address": [
@@ -135,7 +135,7 @@
"number_of_dewars": 5,
"shipment_status": "Pending",
"shipment_date": "2024-03-10",
- "contact_person": [
+ "contact": [
{ "name": "Charlie Brown", "id": "charlie" }
],
"dewars": [
@@ -145,7 +145,7 @@
"tracking_number": "TRACK112233",
"number_of_pucks": 7,
"number_of_samples": 42,
- "contact_person": [
+ "contact": [
{ "name": "Charlie Brown", "id": "charlie" }
],
"return_address": [
@@ -166,7 +166,7 @@
"tracking_number": "TRACK223344",
"number_of_pucks": 5,
"number_of_samples": 30,
- "contact_person": [
+ "contact": [
{ "name": "Charlie Brown", "id": "charlie" }
],
"return_address": [
@@ -187,7 +187,7 @@
"tracking_number": "TRACK334455",
"number_of_pucks": 8,
"number_of_samples": 48,
- "contact_person": [
+ "contact": [
{ "name": "Charlie Brown", "id": "charlie" }
],
"return_address": [
@@ -208,7 +208,7 @@
"tracking_number": "TRACK445566",
"number_of_pucks": 6,
"number_of_samples": 36,
- "contact_person": [
+ "contact": [
{ "name": "Charlie Brown", "id": "charlie" }
],
"return_address": [
@@ -229,7 +229,7 @@
"tracking_number": "TRACK556677",
"number_of_pucks": 4,
"number_of_samples": 24,
- "contact_person": [
+ "contact": [
{ "name": "Charlie Brown", "id": "charlie" }
],
"return_address": [
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 04170ca..23faa19 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -90,7 +90,7 @@ const App: React.FC = () => {
-
+
);
diff --git a/frontend/src/components/DewarDetails.tsx b/frontend/src/components/DewarDetails.tsx
index 2ed3359..903ef89 100644
--- a/frontend/src/components/DewarDetails.tsx
+++ b/frontend/src/components/DewarDetails.tsx
@@ -22,7 +22,7 @@ import {
Dewar,
DewarType,
DewarSerialNumber,
- ContactPerson,
+ Contact,
Address,
ContactsService,
AddressesService,
@@ -37,14 +37,14 @@ interface DewarDetailsProps {
dewar: Dewar;
trackingNumber: string;
setTrackingNumber: (trackingNumber: string) => void;
- initialContactPersons?: ContactPerson[];
+ initialContacts?: Contact[];
initialReturnAddresses?: Address[];
- defaultContactPerson?: ContactPerson;
+ defaultContact?: Contact;
defaultReturnAddress?: Address;
shipmentId: number;
}
-interface NewContactPerson {
+interface NewContact {
id: number;
firstName: string;
lastName: string;
@@ -64,21 +64,21 @@ const DewarDetails: React.FC = ({
dewar,
trackingNumber,
setTrackingNumber,
- initialContactPersons = [],
+ initialContacts = [],
initialReturnAddresses = [],
- defaultContactPerson,
+ defaultContact,
defaultReturnAddress,
shipmentId,
}) => {
const [localTrackingNumber, setLocalTrackingNumber] = useState(trackingNumber);
- const [contactPersons, setContactPersons] = useState(initialContactPersons);
+ const [contacts, setContacts] = useState(initialContacts);
const [returnAddresses, setReturnAddresses] = useState(initialReturnAddresses);
- const [selectedContactPerson, setSelectedContactPerson] = useState('');
+ const [selectedContact, setSelectedContact] = useState('');
const [selectedReturnAddress, setSelectedReturnAddress] = useState('');
- const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
+ const [isCreatingContact, setIsCreatingContact] = useState(false);
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
const [puckStatuses, setPuckStatuses] = useState([]);
- const [newContactPerson, setNewContactPerson] = useState({
+ const [newContact, setNewContact] = useState({
id: 0,
firstName: '',
lastName: '',
@@ -140,9 +140,9 @@ const DewarDetails: React.FC = ({
useEffect(() => {
setLocalTrackingNumber(dewar.tracking_number || '');
- const setInitialContactPerson = () => {
- setSelectedContactPerson(
- dewar.contact_person?.id?.toString() || defaultContactPerson?.id?.toString() || ''
+ const setInitialContact = () => {
+ setSelectedContact(
+ dewar.contact?.id?.toString() || defaultContact?.id?.toString() || ''
);
};
@@ -152,7 +152,7 @@ const DewarDetails: React.FC = ({
);
};
- setInitialContactPerson();
+ setInitialContact();
setInitialReturnAddress();
if (dewar.dewar_type_id) {
@@ -161,7 +161,7 @@ const DewarDetails: React.FC = ({
if (dewar.dewar_serial_number_id) {
setSelectedSerialNumber(dewar.dewar_serial_number_id.toString());
}
- }, [dewar, defaultContactPerson, defaultReturnAddress]);
+ }, [dewar, defaultContact, defaultReturnAddress]);
useEffect(() => {
const getContacts = async () => {
@@ -375,7 +375,7 @@ const DewarDetails: React.FC = ({
arrival_date: dewar.arrival_date,
returning_date: dewar.returning_date,
return_address_id: parseInt(selectedReturnAddress ?? '', 10),
- contact_person_id: parseInt(selectedContactPerson ?? '', 10),
+ contact_id: parseInt(selectedContactPerson ?? '', 10),
};
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
diff --git a/frontend/src/components/DewarStepper.tsx b/frontend/src/components/DewarStepper.tsx
index 3932cb4..e717257 100644
--- a/frontend/src/components/DewarStepper.tsx
+++ b/frontend/src/components/DewarStepper.tsx
@@ -85,7 +85,7 @@ const StepIconComponent: React.FC = ({ icon, dewar, isSe
returning_date: dewar.returning_date,
qrcode: dewar.qrcode,
return_address_id: dewar.return_address_id,
- contact_person_id: dewar.contact_person_id,
+ contact_id: dewar.contact_id,
};
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id, payload);
diff --git a/frontend/src/components/ShipmentDetails.tsx b/frontend/src/components/ShipmentDetails.tsx
index 0e232ba..40de626 100644
--- a/frontend/src/components/ShipmentDetails.tsx
+++ b/frontend/src/components/ShipmentDetails.tsx
@@ -4,7 +4,7 @@ import QRCode from 'react-qr-code';
import DeleteIcon from "@mui/icons-material/Delete";
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
-import { Dewar, DewarsService, Shipment, ContactPerson, ApiError, ShipmentsService } from "../../openapi";
+import { Dewar, DewarsService, Shipment, Contact, ApiError, ShipmentsService } from "../../openapi";
import { SxProps } from "@mui/system";
import CustomStepper from "./DewarStepper";
import DewarDetails from './DewarDetails';
@@ -20,7 +20,7 @@ interface ShipmentDetailsProps {
setSelectedDewar: React.Dispatch>;
setSelectedShipment: React.Dispatch>;
refreshShipments: () => void;
- defaultContactPerson?: ContactPerson;
+ defaultContact?: Contact;
}
const ShipmentDetails: React.FC = ({
@@ -48,7 +48,7 @@ const ShipmentDetails: React.FC = ({
shipping_date: null,
arrival_date: null,
returning_date: null,
- contact_person_id: selectedShipment?.contact_person?.id,
+ contact_id: selectedShipment?.contact?.id,
return_address_id: selectedShipment?.return_address?.id,
};
@@ -59,7 +59,7 @@ const ShipmentDetails: React.FC = ({
// Ensure to update the default contact person and return address when the shipment changes
setNewDewar((prev) => ({
...prev,
- contact_person_id: selectedShipment?.contact_person?.id,
+ contact_id: selectedShipment?.contact?.id,
return_address_id: selectedShipment?.return_address?.id
}));
}, [selectedShipment]);
@@ -122,7 +122,7 @@ const ShipmentDetails: React.FC = ({
...initialNewDewarState,
...newDewar,
dewar_name: newDewar.dewar_name.trim(),
- contact_person_id: selectedShipment?.contact_person?.id,
+ contact_id: selectedShipment?.contact?.id,
return_address_id: selectedShipment?.return_address?.id
} as Dewar;
@@ -179,7 +179,7 @@ const ShipmentDetails: React.FC = ({
};
const isCommentsEdited = comments !== initialComments;
- const contactPerson = selectedShipment?.contact_person;
+ const contact = selectedShipment?.contact;
return (
@@ -228,7 +228,7 @@ const ShipmentDetails: React.FC = ({
{selectedShipment.shipment_name}
- Main contact person: {contactPerson ? `${contactPerson.firstname} ${contactPerson.lastname}` : 'N/A'}
+ Main contact person: {contact ? `${contact.firstname} ${contact.lastname}` : 'N/A'}
Number of Pucks: {totalPucks}
Number of Samples: {totalSamples}
@@ -318,7 +318,7 @@ const ShipmentDetails: React.FC = ({
Number of Samples: {dewar.number_of_samples || 0}
Tracking Number: {dewar.tracking_number}
- Contact Person: {dewar.contact_person?.firstname ? `${dewar.contact_person.firstname} ${dewar.contact_person.lastname}` : 'N/A'}
+ Contact Person: {dewar.contact?.firstname ? `${dewar.contact.firstname} ${dewar.contact.lastname}` : 'N/A'}
= ({
setTrackingNumber={(value) => {
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
}}
- initialContactPersons={localSelectedDewar?.contact_person ? [localSelectedDewar.contact_person] : []}
+ initialContacts={localSelectedDewar?.contact ? [localSelectedDewar.contact] : []}
initialReturnAddresses={localSelectedDewar?.return_address ? [localSelectedDewar.return_address] : []}
- defaultContactPerson={localSelectedDewar?.contact_person ?? undefined}
+ defaultContact={localSelectedDewar?.contact ?? undefined}
defaultReturnAddress={localSelectedDewar?.return_address ?? undefined}
shipmentId={selectedShipment?.id ?? null}
refreshShipments={refreshShipments}
diff --git a/frontend/src/components/ShipmentForm.tsx b/frontend/src/components/ShipmentForm.tsx
index 4379237..f973f03 100644
--- a/frontend/src/components/ShipmentForm.tsx
+++ b/frontend/src/components/ShipmentForm.tsx
@@ -6,7 +6,7 @@ import {
import { SelectChangeEvent } from '@mui/material';
import { SxProps } from '@mui/system';
import {
- ContactPersonCreate, ContactPerson, Address, AddressCreate, Proposal, ContactsService, AddressesService, ProposalsService,
+ ContactCreate, Contact, Address, AddressCreate, Proposal, ContactsService, AddressesService, ProposalsService,
OpenAPI, ShipmentCreate, ShipmentsService
} from '../../openapi';
import { useEffect } from 'react';
@@ -41,21 +41,21 @@ const ShipmentForm: React.FC = ({
onCancel,
refreshShipments }) => {
const [countrySuggestions, setCountrySuggestions] = React.useState([]);
- const [contactPersons, setContactPersons] = React.useState([]);
+ const [contacts, setContacts] = React.useState([]);
const [returnAddresses, setReturnAddresses] = React.useState([]);
const [proposals, setProposals] = React.useState([]);
- const [isCreatingContactPerson, setIsCreatingContactPerson] = React.useState(false);
+ const [isCreatingContact, setIsCreatingContact] = React.useState(false);
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = React.useState(false);
- const [newContactPerson, setNewContactPerson] = React.useState({
- firstname: '', lastname: '', phone_number: '', email: ''
+ const [newContact, setNewContact] = React.useState({
+ pgroups:'', firstname: '', lastname: '', phone_number: '', email: ''
});
const [newReturnAddress, setNewReturnAddress] = React.useState>({
- pgroup:'', house_number: '', street: '', city: '', state: '', zipcode: '', country: ''
+ pgroups:'', house_number: '', street: '', city: '', state: '', zipcode: '', country: ''
});
const [newShipment, setNewShipment] = React.useState>({
shipment_name: '', shipment_status: 'In preparation', comments: ''
});
- const [selectedContactPersonId, setSelectedContactPersonId] = React.useState(null);
+ const [selectedContactId, setSelectedContactId] = React.useState(null);
const [selectedReturnAddressId, setSelectedReturnAddressId] = React.useState(null);
const [selectedProposalId, setSelectedProposalId] = React.useState(null);
const [errorMessage, setErrorMessage] = React.useState(null);
@@ -83,12 +83,17 @@ const ShipmentForm: React.FC = ({
// Fetch necessary data
const getContacts = async () => {
+ if (!activePgroup) {
+ console.error("Active pgroup is missing.");
+ setErrorMessage("Active pgroup is missing. Unable to load contacts.");
+ return;
+ }
try {
- const fetchedContacts: ContactPerson[] =
- await ContactsService.getContactsContactsGet();
- setContactPersons(fetchedContacts);
+ const fetchedContacts: Contact[] =
+ await ContactsService.getContactsProtectedContactsGet(activePgroup);
+ setContacts(fetchedContacts);
} catch {
- setErrorMessage('Failed to load contact persons.');
+ setErrorMessage('Failed to load contact s.');
}
};
@@ -102,7 +107,7 @@ const ShipmentForm: React.FC = ({
try {
// Pass activePgroup directly as a string (not as an object)
const fetchedAddresses: Address[] =
- await AddressesService.getReturnAddressesAddressesGet(activePgroup);
+ await AddressesService.getReturnAddressesProtectedAddressesGet(activePgroup);
setReturnAddresses(fetchedAddresses);
} catch (error) {
@@ -150,9 +155,9 @@ const ShipmentForm: React.FC = ({
};
const isContactFormValid = () => {
- const { firstname, lastname, phone_number, email } = newContactPerson;
+ const { firstname, lastname, phone_number, email } = newContact;
- if (isCreatingContactPerson) {
+ if (isCreatingContact) {
if (!firstname || !lastname || !validateEmail(email) || !validatePhoneNumber(phone_number)) return false;
}
@@ -173,9 +178,9 @@ const ShipmentForm: React.FC = ({
const { shipment_name } = newShipment;
if (!shipment_name) return false;
- if (!selectedContactPersonId || !selectedReturnAddressId || !selectedProposalId) return false;
+ if (!selectedContactId || !selectedReturnAddressId || !selectedProposalId) return false;
- if (isCreatingContactPerson && !isContactFormValid()) return false;
+ if (isCreatingContact && !isContactFormValid()) return false;
if (isCreatingReturnAddress && !isAddressFormValid()) return false;
return true;
@@ -197,7 +202,7 @@ const ShipmentForm: React.FC = ({
shipment_date: new Date().toISOString().split('T')[0], // Remove if date is not required
shipment_status: newShipment.shipment_status || 'In preparation',
comments: newShipment.comments || '',
- contact_person_id: selectedContactPersonId!,
+ contact_id: selectedContactId!,
return_address_id: selectedReturnAddressId!,
proposal_id: selectedProposalId!,
dewars: newShipment.dewars || [],
@@ -217,14 +222,14 @@ const ShipmentForm: React.FC = ({
}
};
- const handleContactPersonChange = (event: SelectChangeEvent) => {
+ const handleContactChange = (event: SelectChangeEvent) => {
const value = event.target.value;
if (value === 'new') {
- setIsCreatingContactPerson(true);
- setSelectedContactPersonId(null);
+ setIsCreatingContact(true);
+ setSelectedContactId(null);
} else {
- setIsCreatingContactPerson(false);
- setSelectedContactPersonId(parseInt(value));
+ setIsCreatingContact(false);
+ setSelectedContactId(parseInt(value));
}
};
@@ -244,33 +249,52 @@ const ShipmentForm: React.FC = ({
setSelectedProposalId(parseInt(value));
};
- const handleSaveNewContactPerson = async () => {
- if (!isContactFormValid()) {
+ const handleSaveNewContact = async () => {
+ // Validate contact form fields
+ if (!isContactFormValid(newContact)) {
setErrorMessage('Please fill in all new contact person fields correctly.');
return;
}
- const payload: ContactPersonCreate = {
- firstname: newContactPerson.firstname,
- lastname: newContactPerson.lastname,
- phone_number: newContactPerson.phone_number,
- email: newContactPerson.email,
- };
-
- console.log('Contact Person Payload being sent:', payload);
-
- try {
- const newPerson: ContactPerson = await ContactsService.createContactContactsPost(payload);
- setContactPersons([...contactPersons, newPerson]);
- setErrorMessage(null);
- setSelectedContactPersonId(newPerson.id);
- } catch (error) {
- console.error('Failed to create a new contact person:', error);
- setErrorMessage('Failed to create a new contact person. Please try again later.');
+ // Ensure activePgroup is available
+ if (!activePgroup) {
+ setErrorMessage('Active pgroup is missing. Please try again.');
+ return;
}
- setNewContactPerson({ firstname: '', lastname: '', phone_number: '', email: '' });
- setIsCreatingContactPerson(false);
+ // Construct the payload
+ const payload: ContactCreate = {
+ pgroups: activePgroup, // Ensure this value is available
+ firstname: newContact.firstname.trim(),
+ lastname: newContact.lastname.trim(),
+ phone_number: newContact.phone_number.trim(),
+ email: newContact.email.trim(),
+ };
+
+ console.log('Payload being sent:', JSON.stringify(payload, null, 2));
+
+ try {
+ // Call the API with the correctly constructed payload
+ const newPerson: Contact = await ContactsService.createContactProtectedContactsPost(payload);
+
+ // Update state on success
+ setContacts([...contacts, newPerson]); // Add new contact to the list
+ setErrorMessage(null); // Clear error messages
+ setSelectedContactId(newPerson.id); // Optionally select the contact
+
+ // Reset form inputs
+ setNewContact({ pgroups: '', firstname: '', lastname: '', phone_number: '', email: '' });
+ setIsCreatingContact(false);
+ } catch (error) {
+ console.error('Failed to create a new contact person:', error);
+
+ // Handle detailed backend error messages if available
+ if (error.response?.data?.detail) {
+ setErrorMessage(`Error: ${error.response.data.detail}`);
+ } else {
+ setErrorMessage('Failed to create a new contact person. Please try again later.');
+ }
+ }
};
const handleSaveNewReturnAddress = async () => {
@@ -301,7 +325,7 @@ const ShipmentForm: React.FC = ({
// Call the API with the completed payload
try {
- const response: Address = await AddressesService.createReturnAddressAddressesPost(payload);
+ const response: Address = await AddressesService.createReturnAddressProtectedAddressesPost(payload);
setReturnAddresses([...returnAddresses, response]); // Update the address state
setErrorMessage(null);
setSelectedReturnAddressId(response.id); // Set the newly created address ID to the form
@@ -311,7 +335,7 @@ const ShipmentForm: React.FC = ({
}
// Reset form inputs and close the "Create New Address" form
- setNewReturnAddress({ pgroup: '', house_number: '', street: '', city: '', state: '', zipcode: '', country: '' });
+ setNewReturnAddress({ pgroups: '', house_number: '', street: '', city: '', state: '', zipcode: '', country: '' });
setIsCreatingReturnAddress(false);
};
@@ -342,11 +366,11 @@ const ShipmentForm: React.FC = ({
Contact Person
- {isCreatingContactPerson && (
+ {isCreatingContact && (
<>
setNewContactPerson({ ...newContactPerson, firstname: e.target.value })}
+ value={newContact.firstname}
+ onChange={(e) => setNewContact({ ...newContact, firstname: e.target.value })}
fullWidth
required
/>
setNewContactPerson({ ...newContactPerson, lastname: e.target.value })}
+ value={newContact.lastname}
+ onChange={(e) => setNewContact({ ...newContact, lastname: e.target.value })}
fullWidth
required
/>
@@ -378,28 +402,28 @@ const ShipmentForm: React.FC = ({
label="Phone"
name="phone_number"
type="tel"
- value={newContactPerson.phone_number}
- onChange={(e) => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
+ value={newContact.phone_number}
+ onChange={(e) => setNewContact({ ...newContact, phone_number: e.target.value })}
fullWidth
required
- error={!validatePhoneNumber(newContactPerson.phone_number)}
- helperText={!validatePhoneNumber(newContactPerson.phone_number) ? 'Invalid phone number' : ''}
+ error={!validatePhoneNumber(newContact.phone_number)}
+ helperText={!validatePhoneNumber(newContact.phone_number) ? 'Invalid phone number' : ''}
/>
setNewContactPerson({ ...newContactPerson, email: e.target.value })}
+ value={newContact.email}
+ onChange={(e) => setNewContact({ ...newContact, email: e.target.value })}
fullWidth
required
- error={!validateEmail(newContactPerson.email)}
- helperText={!validateEmail(newContactPerson.email) ? 'Invalid email' : ''}
+ error={!validateEmail(newContact.email)}
+ helperText={!validateEmail(newContact.email) ? 'Invalid email' : ''}
/>
- {errorMessage && {errorMessage}}
-
- {contacts.length > 0 ? (
- contacts.map((contact) => (
-
-
-
- handleEditContact(contact)}>
-
-
- openDialog(contact)}>
-
-
-
-
- ))
- ) : (
- No contacts found
- )}
-
-
-
- );
+ openDialog(contact)}>
+
+
+
+
+ ))
+ ) : (
+ No contacts found
+ )}
+
+
+
+ );
};
export default ContactsManager;
\ No newline at end of file
diff --git a/frontend/src/pages/ShipmentView.tsx b/frontend/src/pages/ShipmentView.tsx
index 02127b3..1cc2c9a 100644
--- a/frontend/src/pages/ShipmentView.tsx
+++ b/frontend/src/pages/ShipmentView.tsx
@@ -11,7 +11,7 @@ type ShipmentViewProps = {
};
const ShipmentView: React.FC = ( { activePgroup }) => {
- const { shipments, error, defaultContactPerson, fetchAndSetShipments } = useShipments();
+ const { shipments, error, defaultContact, fetchAndSetShipments } = useShipments();
const [selectedShipment, setSelectedShipment] = useState(null);
const [selectedDewar, setSelectedDewar] = useState(null);
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
@@ -76,7 +76,7 @@ const ShipmentView: React.FC = ( { activePgroup }) => {
setSelectedDewar={setSelectedDewar}
setSelectedShipment={setSelectedShipment}
refreshShipments={fetchAndSetShipments}
- defaultContactPerson={defaultContactPerson}
+ defaultContact={defaultContact}
/>
);
}
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index e25b310..dc67538 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -1,4 +1,4 @@
-export interface ContactPerson {
+export interface Contact {
id: string;
lastname: string;
firstname: string;
@@ -29,7 +29,7 @@ export interface Dewar {
number_of_pucks: number;
number_of_samples: number;
return_address: ReturnAddress[];
- contact_person: ContactPerson[];
+ contact_: Contact[];
status: string;
ready_date?: string; // Make sure this is included
shipping_date?: string; // Make sure this is included
@@ -45,7 +45,7 @@ export interface Shipment {
shipment_date: string;
number_of_dewars: number;
shipment_status: string;
- contact_person: ContactPerson[] | null; // Change to an array to accommodate multiple contacts
+ contact_: Contact[] | null; // Change to an array to accommodate multiple contacts
proposal_number?: string;
return_address: Address[]; // Change to an array of Address
comments?: string;