added dewar type, serial number, generate unique id, qr code and generate label
This commit is contained in:
parent
ca11a359f9
commit
6083c72a1d
@ -1,8 +0,0 @@
|
|||||||
def calculate_number_of_pucks(dewar):
|
|
||||||
return len(dewar.pucks) if dewar.pucks else 0
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_number_of_samples(dewar):
|
|
||||||
if not dewar.pucks:
|
|
||||||
return 0
|
|
||||||
return sum(len(puck.positions) for puck in dewar.pucks)
|
|
@ -1 +1 @@
|
|||||||
from .data import contacts, return_addresses, dewars, proposals, shipments, pucks, samples
|
from .data import contacts, return_addresses, dewars, proposals, shipments, pucks, samples, dewar_types, serial_numbers
|
||||||
|
@ -1,30 +1,40 @@
|
|||||||
from app.models import ContactPerson, Address, Dewar, Proposal, Shipment, Puck, Sample
|
from app.models import ContactPerson, Address, Dewar, Proposal, Shipment, Puck, Sample, DewarType, DewarSerialNumber
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import random
|
import random
|
||||||
|
import uuid
|
||||||
|
|
||||||
contacts = [
|
|
||||||
ContactPerson(id=1, firstname="Frodo", lastname="Baggins", phone_number="123-456-7890",
|
dewar_types = [
|
||||||
email="frodo.baggins@lotr.com"),
|
DewarType(id=1, dewar_type="Type A"),
|
||||||
ContactPerson(id=2, firstname="Samwise", lastname="Gamgee", phone_number="987-654-3210",
|
DewarType(id=2, dewar_type="Type B"),
|
||||||
email="samwise.gamgee@lotr.com"),
|
DewarType(id=3, dewar_type="Type C"),
|
||||||
ContactPerson(id=3, firstname="Aragorn", lastname="Elessar", phone_number="123-333-4444",
|
|
||||||
email="aragorn.elessar@lotr.com"),
|
|
||||||
ContactPerson(id=4, firstname="Legolas", lastname="Greenleaf", phone_number="555-666-7777",
|
|
||||||
email="legolas.greenleaf@lotr.com"),
|
|
||||||
ContactPerson(id=5, firstname="Gimli", lastname="Son of Gloin", phone_number="888-999-0000",
|
|
||||||
email="gimli.sonofgloin@lotr.com"),
|
|
||||||
ContactPerson(id=6, firstname="Gandalf", lastname="The Grey", phone_number="222-333-4444",
|
|
||||||
email="gandalf.thegrey@lotr.com"),
|
|
||||||
ContactPerson(id=7, firstname="Boromir", lastname="Son of Denethor", phone_number="111-222-3333",
|
|
||||||
email="boromir.sonofdenethor@lotr.com"),
|
|
||||||
ContactPerson(id=8, firstname="Galadriel", lastname="Lady of Lothlórien", phone_number="444-555-6666",
|
|
||||||
email="galadriel.lothlorien@lotr.com"),
|
|
||||||
ContactPerson(id=9, firstname="Elrond", lastname="Half-elven", phone_number="777-888-9999",
|
|
||||||
email="elrond.halfelven@lotr.com"),
|
|
||||||
ContactPerson(id=10, firstname="Eowyn", lastname="Shieldmaiden of Rohan", phone_number="000-111-2222",
|
|
||||||
email="eowyn.rohan@lotr.com"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Define Dewar serial numbers
|
||||||
|
serial_numbers = [
|
||||||
|
DewarSerialNumber(id=1, serial_number="SN00001", dewar_type_id=1),
|
||||||
|
DewarSerialNumber(id=2, serial_number="SN00002", dewar_type_id=1),
|
||||||
|
DewarSerialNumber(id=3, serial_number="SN00003", dewar_type_id=2),
|
||||||
|
DewarSerialNumber(id=4, serial_number="SN00004", dewar_type_id=2),
|
||||||
|
DewarSerialNumber(id=5, serial_number="SN00005", dewar_type_id=3),
|
||||||
|
DewarSerialNumber(id=6, serial_number="SN00006", dewar_type_id=3),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Define contact persons
|
||||||
|
contacts = [
|
||||||
|
ContactPerson(id=1, firstname="Frodo", lastname="Baggins", phone_number="123-456-7890", email="frodo.baggins@lotr.com"),
|
||||||
|
ContactPerson(id=2, firstname="Samwise", lastname="Gamgee", phone_number="987-654-3210", email="samwise.gamgee@lotr.com"),
|
||||||
|
ContactPerson(id=3, firstname="Aragorn", lastname="Elessar", phone_number="123-333-4444", email="aragorn.elessar@lotr.com"),
|
||||||
|
ContactPerson(id=4, firstname="Legolas", lastname="Greenleaf", phone_number="555-666-7777", email="legolas.greenleaf@lotr.com"),
|
||||||
|
ContactPerson(id=5, firstname="Gimli", lastname="Son of Gloin", phone_number="888-999-0000", email="gimli.sonofgloin@lotr.com"),
|
||||||
|
ContactPerson(id=6, firstname="Gandalf", lastname="The Grey", phone_number="222-333-4444", email="gandalf.thegrey@lotr.com"),
|
||||||
|
ContactPerson(id=7, firstname="Boromir", lastname="Son of Denethor", phone_number="111-222-3333", email="boromir.sonofdenethor@lotr.com"),
|
||||||
|
ContactPerson(id=8, firstname="Galadriel", lastname="Lady of Lothlórien", phone_number="444-555-6666", email="galadriel.lothlorien@lotr.com"),
|
||||||
|
ContactPerson(id=9, firstname="Elrond", lastname="Half-elven", phone_number="777-888-9999", email="elrond.halfelven@lotr.com"),
|
||||||
|
ContactPerson(id=10, firstname="Eowyn", lastname="Shieldmaiden of Rohan", phone_number="000-111-2222", email="eowyn.rohan@lotr.com"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Define return addresses
|
||||||
return_addresses = [
|
return_addresses = [
|
||||||
Address(id=1, street='123 Hobbiton St', city='Shire', zipcode='12345', country='Middle Earth'),
|
Address(id=1, street='123 Hobbiton St', city='Shire', zipcode='12345', country='Middle Earth'),
|
||||||
Address(id=2, street='456 Rohan Rd', city='Edoras', zipcode='67890', country='Middle Earth'),
|
Address(id=2, street='456 Rohan Rd', city='Edoras', zipcode='67890', country='Middle Earth'),
|
||||||
@ -33,40 +43,51 @@ return_addresses = [
|
|||||||
Address(id=5, street='654 Falgorn Pass', city='Rivendell', zipcode='11223', country='Middle Earth'),
|
Address(id=5, street='654 Falgorn Pass', city='Rivendell', zipcode='11223', country='Middle Earth'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Utilize a function to generate unique IDs
|
||||||
|
def generate_unique_id():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Define dewars with unique IDs
|
||||||
dewars = [
|
dewars = [
|
||||||
Dewar(
|
Dewar(
|
||||||
id=1, dewar_name='Dewar One', tracking_number='TRACK123',
|
id=1, dewar_name='Dewar One', dewar_type_id=1,
|
||||||
|
dewar_serial_number_id=2, tracking_number='TRACK123',
|
||||||
return_address_id=1, contact_person_id=1, status='Ready for Shipping',
|
return_address_id=1, contact_person_id=1, status='Ready for Shipping',
|
||||||
ready_date=datetime.strptime('2023-09-30', '%Y-%m-%d'), shipping_date=None, arrival_date=None,
|
ready_date=datetime.strptime('2023-09-30', '%Y-%m-%d'), shipping_date=None, arrival_date=None,
|
||||||
returning_date=None, qrcode='QR123DEWAR001',
|
returning_date=None, qrcode=generate_unique_id()
|
||||||
),
|
),
|
||||||
Dewar(
|
Dewar(
|
||||||
id=2, dewar_name='Dewar Two', tracking_number='TRACK124',
|
id=2, dewar_name='Dewar Two', dewar_type_id=3,
|
||||||
|
dewar_serial_number_id=1, tracking_number='TRACK124',
|
||||||
return_address_id=2, contact_person_id=2, status='In Preparation',
|
return_address_id=2, contact_person_id=2, status='In Preparation',
|
||||||
ready_date=None, shipping_date=None, arrival_date=None, returning_date=None, qrcode='QR123DEWAR002',
|
ready_date=None, shipping_date=None, arrival_date=None, returning_date=None, qrcode=generate_unique_id()
|
||||||
),
|
),
|
||||||
Dewar(
|
Dewar(
|
||||||
id=3, dewar_name='Dewar Three', tracking_number='TRACK125',
|
id=3, dewar_name='Dewar Three', dewar_type_id=2,
|
||||||
|
dewar_serial_number_id=3, tracking_number='TRACK125',
|
||||||
return_address_id=1, contact_person_id=3, status='Not Shipped',
|
return_address_id=1, contact_person_id=3, status='Not Shipped',
|
||||||
ready_date=datetime.strptime('2024-01-01', '%Y-%m-%d'), shipping_date=None, arrival_date=None,
|
ready_date=datetime.strptime('2024-01-01', '%Y-%m-%d'), shipping_date=None, arrival_date=None,
|
||||||
returning_date=None, qrcode='QR123DEWAR003',
|
returning_date=None, qrcode=''
|
||||||
),
|
),
|
||||||
Dewar(
|
Dewar(
|
||||||
id=4, dewar_name='Dewar Four', tracking_number='',
|
id=4, dewar_name='Dewar Four', dewar_type_id=2,
|
||||||
|
dewar_serial_number_id=4, tracking_number='',
|
||||||
return_address_id=1, contact_person_id=3, status='Delayed',
|
return_address_id=1, contact_person_id=3, status='Delayed',
|
||||||
ready_date=datetime.strptime('2024-01-01', '%Y-%m-%d'),
|
ready_date=datetime.strptime('2024-01-01', '%Y-%m-%d'),
|
||||||
shipping_date=datetime.strptime('2024-01-02', '%Y-%m-%d'),
|
shipping_date=datetime.strptime('2024-01-02', '%Y-%m-%d'),
|
||||||
arrival_date=None, returning_date=None, qrcode='QR123DEWAR004',
|
arrival_date=None, returning_date=None, qrcode=''
|
||||||
),
|
),
|
||||||
Dewar(
|
Dewar(
|
||||||
id=5, dewar_name='Dewar Five', tracking_number='',
|
id=5, dewar_name='Dewar Five', dewar_type_id=1,
|
||||||
|
dewar_serial_number_id=1, tracking_number='',
|
||||||
return_address_id=1, contact_person_id=3, status='Returned',
|
return_address_id=1, contact_person_id=3, status='Returned',
|
||||||
arrival_date=datetime.strptime('2024-01-03', '%Y-%m-%d'),
|
arrival_date=datetime.strptime('2024-01-03', '%Y-%m-%d'),
|
||||||
returning_date=datetime.strptime('2024-01-07', '%Y-%m-%d'),
|
returning_date=datetime.strptime('2024-01-07', '%Y-%m-%d'),
|
||||||
qrcode='QR123DEWAR005',
|
qrcode=''
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Define proposals
|
||||||
proposals = [
|
proposals = [
|
||||||
Proposal(id=1, number="PROPOSAL-FRODO-001"),
|
Proposal(id=1, number="PROPOSAL-FRODO-001"),
|
||||||
Proposal(id=2, number="PROPOSAL-GANDALF-002"),
|
Proposal(id=2, number="PROPOSAL-GANDALF-002"),
|
||||||
@ -75,6 +96,7 @@ proposals = [
|
|||||||
Proposal(id=5, number="PROPOSAL-MORDOR-005"),
|
Proposal(id=5, number="PROPOSAL-MORDOR-005"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Define shipment specific dewars
|
||||||
specific_dewar_ids1 = [5]
|
specific_dewar_ids1 = [5]
|
||||||
specific_dewar_ids2 = [1, 2]
|
specific_dewar_ids2 = [1, 2]
|
||||||
specific_dewar_ids3 = [3, 4]
|
specific_dewar_ids3 = [3, 4]
|
||||||
@ -83,6 +105,7 @@ specific_dewars1 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids1
|
|||||||
specific_dewars2 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids2]
|
specific_dewars2 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids2]
|
||||||
specific_dewars3 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids3]
|
specific_dewars3 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids3]
|
||||||
|
|
||||||
|
# Define shipments
|
||||||
shipments = [
|
shipments = [
|
||||||
Shipment(
|
Shipment(
|
||||||
id=1, shipment_date=datetime.strptime('2024-10-10', '%Y-%m-%d'),
|
id=1, shipment_date=datetime.strptime('2024-10-10', '%Y-%m-%d'),
|
||||||
@ -101,6 +124,7 @@ shipments = [
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Define pucks
|
||||||
pucks = [
|
pucks = [
|
||||||
Puck(id=1, puck_name="PUCK001", puck_type="Unipuck", puck_location_in_dewar=1, dewar_id=1),
|
Puck(id=1, puck_name="PUCK001", puck_type="Unipuck", puck_location_in_dewar=1, dewar_id=1),
|
||||||
Puck(id=2, puck_name="PUCK002", puck_type="Unipuck", puck_location_in_dewar=2, dewar_id=1),
|
Puck(id=2, puck_name="PUCK002", puck_type="Unipuck", puck_location_in_dewar=2, dewar_id=1),
|
||||||
@ -134,7 +158,7 @@ pucks = [
|
|||||||
Puck(id=30, puck_name="PKK007", puck_type="Unipuck", puck_location_in_dewar=7, dewar_id=5)
|
Puck(id=30, puck_name="PKK007", puck_type="Unipuck", puck_location_in_dewar=7, dewar_id=5)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Define samples
|
||||||
samples = []
|
samples = []
|
||||||
sample_id_counter = 1
|
sample_id_counter = 1
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ def init_db():
|
|||||||
|
|
||||||
def load_sample_data(session: Session):
|
def load_sample_data(session: Session):
|
||||||
# Import models inside function to avoid circular dependency
|
# Import models inside function to avoid circular dependency
|
||||||
from app.data import contacts, return_addresses, dewars, proposals, shipments, pucks, samples
|
from app.data import contacts, return_addresses, dewars, proposals, shipments, pucks, samples, dewar_types, serial_numbers
|
||||||
|
|
||||||
from app import models
|
from app import models
|
||||||
|
|
||||||
@ -36,5 +36,5 @@ def load_sample_data(session: Session):
|
|||||||
if session.query(models.ContactPerson).first():
|
if session.query(models.ContactPerson).first():
|
||||||
return
|
return
|
||||||
|
|
||||||
session.add_all(contacts + return_addresses + dewars + proposals + shipments + pucks + samples)
|
session.add_all(contacts + return_addresses + dewars + proposals + shipments + pucks + samples + dewar_types + serial_numbers)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from sqlalchemy import Column, Integer, String, Date, ForeignKey, JSON
|
from sqlalchemy import Column, Integer, String, Date, ForeignKey, JSON
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from app.database import Base
|
from app.database import Base
|
||||||
from app.calculations import calculate_number_of_pucks, calculate_number_of_samples
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
class Shipment(Base):
|
class Shipment(Base):
|
||||||
@ -45,19 +45,35 @@ class Address(Base):
|
|||||||
|
|
||||||
shipments = relationship("Shipment", back_populates="return_address")
|
shipments = relationship("Shipment", back_populates="return_address")
|
||||||
|
|
||||||
|
class DewarType(Base):
|
||||||
|
__tablename__ = "dewar_types"
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
dewar_type = Column(String, unique=True, index=True)
|
||||||
|
serial_numbers = relationship("DewarSerialNumber", back_populates="dewar_type")
|
||||||
|
|
||||||
|
class DewarSerialNumber(Base):
|
||||||
|
__tablename__ = "dewar_serial_numbers"
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
serial_number = Column(String, index=True)
|
||||||
|
dewar_type_id = Column(Integer, ForeignKey('dewar_types.id'))
|
||||||
|
dewar_type = relationship("DewarType", back_populates="serial_numbers")
|
||||||
|
|
||||||
|
|
||||||
class Dewar(Base):
|
class Dewar(Base):
|
||||||
__tablename__ = "dewars"
|
__tablename__ = "dewars"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
dewar_name = Column(String)
|
dewar_name = Column(String)
|
||||||
|
dewar_type_id = Column(Integer, ForeignKey("dewar_types.id"), nullable=True)
|
||||||
|
dewar_serial_number_id = Column(Integer, ForeignKey("dewar_serial_numbers.id"), nullable=True)
|
||||||
tracking_number = Column(String)
|
tracking_number = Column(String)
|
||||||
status = Column(String)
|
status = Column(String)
|
||||||
ready_date = Column(Date, nullable=True)
|
ready_date = Column(Date, nullable=True)
|
||||||
shipping_date = Column(Date, nullable=True)
|
shipping_date = Column(Date, nullable=True)
|
||||||
arrival_date = Column(Date, nullable=True)
|
arrival_date = Column(Date, nullable=True)
|
||||||
returning_date = Column(Date, nullable=True)
|
returning_date = Column(Date, nullable=True)
|
||||||
qrcode = Column(String)
|
unique_id = Column(String(36), default=lambda: str(uuid.uuid4()), unique=True, index=True, nullable=True)
|
||||||
|
qrcode = Column(String, nullable=True)
|
||||||
shipment_id = Column(Integer, ForeignKey("shipments.id"))
|
shipment_id = Column(Integer, ForeignKey("shipments.id"))
|
||||||
return_address_id = Column(Integer, ForeignKey("addresses.id"))
|
return_address_id = Column(Integer, ForeignKey("addresses.id"))
|
||||||
contact_person_id = Column(Integer, ForeignKey("contact_persons.id"))
|
contact_person_id = Column(Integer, ForeignKey("contact_persons.id"))
|
||||||
@ -67,6 +83,9 @@ class Dewar(Base):
|
|||||||
contact_person = relationship("ContactPerson")
|
contact_person = relationship("ContactPerson")
|
||||||
pucks = relationship("Puck", back_populates="dewar")
|
pucks = relationship("Puck", back_populates="dewar")
|
||||||
|
|
||||||
|
dewar_type = relationship("DewarType")
|
||||||
|
dewar_serial_number = relationship("DewarSerialNumber")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def number_of_pucks(self) -> int:
|
def number_of_pucks(self) -> int:
|
||||||
return len(self.pucks) if self.pucks else 0
|
return len(self.pucks) if self.pucks else 0
|
||||||
@ -77,7 +96,6 @@ class Dewar(Base):
|
|||||||
return 0
|
return 0
|
||||||
return sum(len(puck.samples) for puck in self.pucks)
|
return sum(len(puck.samples) for puck in self.pucks)
|
||||||
|
|
||||||
|
|
||||||
class Proposal(Base):
|
class Proposal(Base):
|
||||||
__tablename__ = "proposals"
|
__tablename__ = "proposals"
|
||||||
|
|
||||||
|
@ -1,26 +1,50 @@
|
|||||||
from fastapi import APIRouter, HTTPException, status, Depends
|
from fastapi import APIRouter, HTTPException, status, Depends, Response
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from typing import List
|
from typing import List
|
||||||
import logging
|
import logging
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate
|
from app.schemas import (
|
||||||
from app.models import Dewar as DewarModel, Puck as PuckModel, \
|
Dewar as DewarSchema,
|
||||||
Sample as SampleModel # Assuming SampleModel is defined in models
|
DewarCreate,
|
||||||
|
DewarUpdate,
|
||||||
|
DewarType as DewarTypeSchema,
|
||||||
|
DewarTypeCreate,
|
||||||
|
DewarSerialNumber as DewarSerialNumberSchema,
|
||||||
|
DewarSerialNumberCreate
|
||||||
|
)
|
||||||
|
from app.models import (
|
||||||
|
Dewar as DewarModel,
|
||||||
|
Puck as PuckModel,
|
||||||
|
Sample as SampleModel,
|
||||||
|
DewarType as DewarTypeModel,
|
||||||
|
DewarSerialNumber as DewarSerialNumberModel
|
||||||
|
)
|
||||||
from app.dependencies import get_db
|
from app.dependencies import get_db
|
||||||
|
import uuid
|
||||||
|
import qrcode
|
||||||
|
import io
|
||||||
|
from io import BytesIO
|
||||||
|
from PIL import Image
|
||||||
|
from reportlab.lib.pagesizes import A5
|
||||||
|
from reportlab.lib.units import cm
|
||||||
|
from reportlab.pdfgen import canvas
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", response_model=List[DewarSchema])
|
def generate_unique_id(db: Session) -> str:
|
||||||
async def get_dewars(db: Session = Depends(get_db)):
|
while True:
|
||||||
dewars = db.query(DewarModel).options(joinedload(DewarModel.pucks)).all()
|
unique_id = str(uuid.uuid4())
|
||||||
return dewars
|
existing_dewar = db.query(DewarModel).filter(DewarModel.unique_id == unique_id).first()
|
||||||
|
if not existing_dewar:
|
||||||
|
break
|
||||||
|
return unique_id
|
||||||
|
|
||||||
@router.post("/", response_model=DewarSchema, status_code=status.HTTP_201_CREATED)
|
@router.post("/", response_model=DewarSchema, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> DewarSchema:
|
async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> DewarSchema:
|
||||||
try:
|
try:
|
||||||
|
unique_id = generate_unique_id(db)
|
||||||
db_dewar = DewarModel(
|
db_dewar = DewarModel(
|
||||||
dewar_name=dewar.dewar_name,
|
dewar_name=dewar.dewar_name,
|
||||||
tracking_number=dewar.tracking_number,
|
tracking_number=dewar.tracking_number,
|
||||||
@ -29,11 +53,10 @@ async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> Dew
|
|||||||
shipping_date=dewar.shipping_date,
|
shipping_date=dewar.shipping_date,
|
||||||
arrival_date=dewar.arrival_date,
|
arrival_date=dewar.arrival_date,
|
||||||
returning_date=dewar.returning_date,
|
returning_date=dewar.returning_date,
|
||||||
qrcode=dewar.qrcode,
|
|
||||||
contact_person_id=dewar.contact_person_id,
|
contact_person_id=dewar.contact_person_id,
|
||||||
return_address_id=dewar.return_address_id
|
return_address_id=dewar.return_address_id,
|
||||||
|
unique_id=unique_id
|
||||||
)
|
)
|
||||||
|
|
||||||
db.add(db_dewar)
|
db.add(db_dewar)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_dewar)
|
db.refresh(db_dewar)
|
||||||
@ -54,7 +77,6 @@ async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> Dew
|
|||||||
puck_id=puck.id,
|
puck_id=puck.id,
|
||||||
sample_name=sample_data.sample_name,
|
sample_name=sample_data.sample_name,
|
||||||
position=sample_data.position,
|
position=sample_data.position,
|
||||||
# Ensure only valid attributes are set
|
|
||||||
data_collection_parameters=sample_data.data_collection_parameters,
|
data_collection_parameters=sample_data.data_collection_parameters,
|
||||||
)
|
)
|
||||||
db.add(sample)
|
db.add(sample)
|
||||||
@ -70,24 +92,142 @@ async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> Dew
|
|||||||
logging.error(f"Validation error occurred: {e}")
|
logging.error(f"Validation error occurred: {e}")
|
||||||
raise HTTPException(status_code=400, detail="Validation error")
|
raise HTTPException(status_code=400, detail="Validation error")
|
||||||
|
|
||||||
|
@router.post("/{dewar_id}/generate-qrcode")
|
||||||
|
async def generate_dewar_qrcode(dewar_id: int, db: Session = Depends(get_db)):
|
||||||
|
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||||
|
if not dewar:
|
||||||
|
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||||
|
|
||||||
|
if not dewar.unique_id:
|
||||||
|
dewar.unique_id = generate_unique_id(db)
|
||||||
|
|
||||||
|
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||||
|
qr.add_data(dewar.unique_id)
|
||||||
|
qr.make(fit=True)
|
||||||
|
img = qr.make_image(fill='black', back_color='white')
|
||||||
|
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.save(buf)
|
||||||
|
buf.seek(0)
|
||||||
|
dewar.qrcode = dewar.unique_id
|
||||||
|
dewar.qrcode_image = buf.getvalue()
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {"message": "QR Code generated", "qrcode": dewar.unique_id}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_label(dewar):
|
||||||
|
buffer = BytesIO()
|
||||||
|
c = canvas.Canvas(buffer, pagesize=A5)
|
||||||
|
|
||||||
|
# Draw header
|
||||||
|
c.setFont("Helvetica-Bold", 16)
|
||||||
|
c.drawCentredString(10.5 * cm, 14 * cm, "COMPANY LOGO / TITLE")
|
||||||
|
|
||||||
|
# Draw details section
|
||||||
|
c.setFont("Helvetica", 12)
|
||||||
|
c.drawString(2 * cm, 12.5 * cm, f"Dewar Name: {dewar.dewar_name}")
|
||||||
|
c.drawString(2 * cm, 11.5 * cm, f"Unique ID: {dewar.unique_id}")
|
||||||
|
if dewar.dewar_type:
|
||||||
|
c.drawString(2 * cm, 10.5 * cm, f"Dewar Type: {dewar.dewar_type.dewar_type}")
|
||||||
|
else:
|
||||||
|
c.drawString(2 * cm, 10.5 * cm, "Dewar Type: Unknown")
|
||||||
|
c.drawString(2 * cm, 9.5 * cm, "Beamtime Information: Placeholder")
|
||||||
|
|
||||||
|
# Generate QR code
|
||||||
|
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||||
|
qr.add_data(dewar.unique_id)
|
||||||
|
qr.make(fit=True)
|
||||||
|
img = qr.make_image(fill='black', back_color='white')
|
||||||
|
qr_io = BytesIO()
|
||||||
|
img.save(qr_io, format='PNG')
|
||||||
|
qr_io.seek(0)
|
||||||
|
qr_image = Image.open(qr_io)
|
||||||
|
|
||||||
|
# Add QR code to PDF
|
||||||
|
c.drawInlineImage(qr_image, 8 * cm, 5 * cm, width=4 * cm, height=4 * cm)
|
||||||
|
|
||||||
|
# Add footer text
|
||||||
|
c.setFont("Helvetica", 10)
|
||||||
|
c.drawCentredString(10.5 * cm, 4 * cm, "Scan for more information")
|
||||||
|
|
||||||
|
# Draw border
|
||||||
|
c.rect(1 * cm, 3 * cm, 18 * cm, 12 * cm)
|
||||||
|
|
||||||
|
# Finalize the canvas
|
||||||
|
c.showPage()
|
||||||
|
c.save()
|
||||||
|
|
||||||
|
buffer.seek(0)
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
@router.get("/{dewar_id}/download-label", response_class=Response)
|
||||||
|
async def download_dewar_label(dewar_id: int, db: Session = Depends(get_db)):
|
||||||
|
dewar = db.query(DewarModel).options(joinedload(DewarModel.dewar_type)).filter(DewarModel.id == dewar_id).first()
|
||||||
|
if not dewar:
|
||||||
|
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||||
|
if not dewar.unique_id:
|
||||||
|
raise HTTPException(status_code=404, detail="QR Code not generated for this dewar")
|
||||||
|
|
||||||
|
buffer = generate_label(dewar)
|
||||||
|
|
||||||
|
return Response(buffer.getvalue(), media_type="application/pdf", headers={
|
||||||
|
"Content-Disposition": f"attachment; filename=dewar_label_{dewar.id}.pdf"
|
||||||
|
})
|
||||||
|
|
||||||
|
@router.get("/", response_model=List[DewarSchema])
|
||||||
|
async def get_dewars(db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
dewars = db.query(DewarModel).options(joinedload(DewarModel.pucks)).all()
|
||||||
|
return dewars
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
logging.error(f"Database error occurred: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
@router.get("/dewar-types", response_model=List[DewarTypeSchema])
|
||||||
|
def get_dewar_types(db: Session = Depends(get_db)):
|
||||||
|
return db.query(DewarTypeModel).all()
|
||||||
|
|
||||||
|
@router.get("/dewar-types/{type_id}/serial-numbers", response_model=List[DewarSerialNumberSchema])
|
||||||
|
def get_serial_numbers(type_id: int, db: Session = Depends(get_db)):
|
||||||
|
return db.query(DewarSerialNumberModel).filter(DewarSerialNumberModel.dewar_type_id == type_id).all()
|
||||||
|
|
||||||
|
@router.post("/dewar-types", response_model=DewarTypeSchema)
|
||||||
|
def create_dewar_type(dewar_type: DewarTypeCreate, db: Session = Depends(get_db)):
|
||||||
|
db_type = DewarTypeModel(**dewar_type.dict())
|
||||||
|
db.add(db_type)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_type)
|
||||||
|
return db_type
|
||||||
|
|
||||||
|
@router.post("/dewar-serial-numbers", response_model=DewarSerialNumberSchema)
|
||||||
|
def create_dewar_serial_number(serial_number: DewarSerialNumberCreate, db: Session = Depends(get_db)):
|
||||||
|
db_serial = DewarSerialNumberModel(**serial_number.dict())
|
||||||
|
db.add(db_serial)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_serial)
|
||||||
|
return db_serial
|
||||||
|
|
||||||
|
@router.get("/dewar-serial-numbers", response_model=List[DewarSerialNumberSchema])
|
||||||
|
def get_all_serial_numbers(db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
serial_numbers = db.query(DewarSerialNumberModel).all()
|
||||||
|
return serial_numbers
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
logging.error(f"Database error occurred: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
@router.get("/{dewar_id}", response_model=DewarSchema)
|
@router.get("/{dewar_id}", response_model=DewarSchema)
|
||||||
async def get_dewar(dewar_id: int, db: Session = Depends(get_db)):
|
async def get_dewar(dewar_id: int, db: Session = Depends(get_db)):
|
||||||
dewar = db.query(DewarModel).options(
|
dewar = db.query(DewarModel).options(
|
||||||
joinedload(DewarModel.pucks).joinedload(PuckModel.positions)
|
joinedload(DewarModel.pucks).joinedload(PuckModel.samples)
|
||||||
).filter(DewarModel.id == dewar_id).first()
|
).filter(DewarModel.id == dewar_id).first()
|
||||||
|
|
||||||
if not dewar:
|
if not dewar:
|
||||||
raise HTTPException(status_code=404, detail="Dewar not found")
|
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||||
|
|
||||||
# Ensure dewar.pucks is an empty list if there are no pucks
|
|
||||||
dewar_dict = dewar.__dict__
|
|
||||||
if dewar_dict.get("pucks") is None:
|
|
||||||
dewar_dict["pucks"] = []
|
|
||||||
|
|
||||||
return DewarSchema.from_orm(dewar)
|
return DewarSchema.from_orm(dewar)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{dewar_id}", response_model=DewarSchema)
|
@router.put("/{dewar_id}", response_model=DewarSchema)
|
||||||
async def update_dewar(dewar_id: int, dewar_update: DewarUpdate, db: Session = Depends(get_db)) -> DewarSchema:
|
async def update_dewar(dewar_id: int, dewar_update: DewarUpdate, db: Session = Depends(get_db)) -> DewarSchema:
|
||||||
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||||
@ -96,22 +236,20 @@ async def update_dewar(dewar_id: int, dewar_update: DewarUpdate, db: Session = D
|
|||||||
raise HTTPException(status_code=404, detail="Dewar not found")
|
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||||
|
|
||||||
for key, value in dewar_update.dict(exclude_unset=True).items():
|
for key, value in dewar_update.dict(exclude_unset=True).items():
|
||||||
# Ensure we're only setting directly settable attributes
|
|
||||||
if hasattr(dewar, key):
|
if hasattr(dewar, key):
|
||||||
setattr(dewar, key, value)
|
setattr(dewar, key, value)
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(dewar)
|
db.refresh(dewar)
|
||||||
|
|
||||||
return dewar
|
return dewar
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{dewar_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/{dewar_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_dewar(dewar_id: int, db: Session = Depends(get_db)):
|
async def delete_dewar(dewar_id: int, db: Session = Depends(get_db)):
|
||||||
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first()
|
||||||
|
|
||||||
if not dewar:
|
if not dewar:
|
||||||
raise HTTPException(status_code=404, detail="Dewar not found")
|
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||||
|
|
||||||
db.delete(dewar)
|
db.delete(dewar)
|
||||||
db.commit()
|
db.commit()
|
||||||
return
|
return
|
@ -3,6 +3,38 @@ from pydantic import BaseModel, EmailStr, constr, Field
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
|
|
||||||
|
class DewarTypeBase(BaseModel):
|
||||||
|
dewar_type: str
|
||||||
|
|
||||||
|
|
||||||
|
class DewarTypeCreate(DewarTypeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DewarType(DewarTypeBase):
|
||||||
|
id: int
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class DewarSerialNumberBase(BaseModel):
|
||||||
|
serial_number: str
|
||||||
|
dewar_type_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class DewarSerialNumberCreate(DewarSerialNumberBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DewarSerialNumber(DewarSerialNumberBase):
|
||||||
|
id: int
|
||||||
|
dewar_type: DewarType
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
class DataCollectionParameters(BaseModel):
|
class DataCollectionParameters(BaseModel):
|
||||||
priority: Optional[int] = None
|
priority: Optional[int] = None
|
||||||
comments: Optional[str] = None
|
comments: Optional[str] = None
|
||||||
@ -41,7 +73,6 @@ class Results(BaseModel):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Contact Person schemas
|
|
||||||
class ContactPersonBase(BaseModel):
|
class ContactPersonBase(BaseModel):
|
||||||
firstname: str
|
firstname: str
|
||||||
lastname: str
|
lastname: str
|
||||||
@ -61,13 +92,12 @@ class ContactPerson(ContactPersonBase):
|
|||||||
|
|
||||||
|
|
||||||
class ContactPersonUpdate(BaseModel):
|
class ContactPersonUpdate(BaseModel):
|
||||||
firstname: str | None = None
|
firstname: Optional[str] = None
|
||||||
lastname: str | None = None
|
lastname: Optional[str] = None
|
||||||
phone_number: str | None = None
|
phone_number: Optional[str] = None
|
||||||
email: EmailStr | None = None
|
email: Optional[EmailStr] = None
|
||||||
|
|
||||||
|
|
||||||
# Address schemas
|
|
||||||
class AddressCreate(BaseModel):
|
class AddressCreate(BaseModel):
|
||||||
street: str
|
street: str
|
||||||
city: str
|
city: str
|
||||||
@ -83,10 +113,10 @@ class Address(AddressCreate):
|
|||||||
|
|
||||||
|
|
||||||
class AddressUpdate(BaseModel):
|
class AddressUpdate(BaseModel):
|
||||||
street: str | None = None
|
street: Optional[str] = None
|
||||||
city: str | None = None
|
city: Optional[str] = None
|
||||||
zipcode: str | None = None
|
zipcode: Optional[str] = None
|
||||||
country: str | None = None
|
country: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class Sample(BaseModel):
|
class Sample(BaseModel):
|
||||||
@ -108,8 +138,6 @@ class SampleCreate(BaseModel):
|
|||||||
populate_by_name = True
|
populate_by_name = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Puck schemas
|
|
||||||
class PuckBase(BaseModel):
|
class PuckBase(BaseModel):
|
||||||
puck_name: str
|
puck_name: str
|
||||||
puck_type: str
|
puck_type: str
|
||||||
@ -142,9 +170,11 @@ class Puck(BaseModel):
|
|||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
# Dewar schemas
|
|
||||||
class DewarBase(BaseModel):
|
class DewarBase(BaseModel):
|
||||||
dewar_name: str
|
dewar_name: str
|
||||||
|
dewar_type_id: Optional[int] = None
|
||||||
|
dewar_serial_number_id: Optional[int] = None
|
||||||
|
unique_id: Optional[str] = None
|
||||||
tracking_number: str
|
tracking_number: str
|
||||||
number_of_pucks: int
|
number_of_pucks: int
|
||||||
number_of_samples: int
|
number_of_samples: int
|
||||||
@ -176,6 +206,9 @@ class Dewar(DewarBase):
|
|||||||
|
|
||||||
class DewarUpdate(BaseModel):
|
class DewarUpdate(BaseModel):
|
||||||
dewar_name: Optional[str] = None
|
dewar_name: Optional[str] = None
|
||||||
|
dewar_type_id: Optional[int] = None
|
||||||
|
dewar_serial_number_id: Optional[int] = None
|
||||||
|
unique_id: Optional[str] = None
|
||||||
tracking_number: Optional[str] = None
|
tracking_number: Optional[str] = None
|
||||||
status: Optional[str] = None
|
status: Optional[str] = None
|
||||||
ready_date: Optional[date] = None
|
ready_date: Optional[date] = None
|
||||||
@ -186,6 +219,7 @@ class DewarUpdate(BaseModel):
|
|||||||
contact_person_id: Optional[int] = None
|
contact_person_id: Optional[int] = None
|
||||||
address_id: Optional[int] = None
|
address_id: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
class DewarSchema(BaseModel):
|
class DewarSchema(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
dewar_name: str
|
dewar_name: str
|
||||||
@ -198,7 +232,6 @@ class DewarSchema(BaseModel):
|
|||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
# Proposal schemas
|
|
||||||
class Proposal(BaseModel):
|
class Proposal(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
number: str
|
number: str
|
||||||
@ -207,7 +240,6 @@ class Proposal(BaseModel):
|
|||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
# Shipment schemas
|
|
||||||
class Shipment(BaseModel):
|
class Shipment(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
shipment_name: str
|
shipment_name: str
|
||||||
@ -238,4 +270,4 @@ class ShipmentCreate(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class UpdateShipmentComments(BaseModel):
|
class UpdateShipmentComments(BaseModel):
|
||||||
comments: str
|
comments: str
|
@ -1,9 +1,34 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material';
|
import {
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
TextField,
|
||||||
|
Button,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
Snackbar,
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
Alert,
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import { ContactPerson, Address, Dewar, ContactsService, AddressesService, DewarsService, ShipmentsService } from '../../openapi'; // Adjust path if necessary
|
import {
|
||||||
import Unipuck from '../components/Unipuck'; // This path should be checked and corrected if necessary
|
Dewar,
|
||||||
import { Shipment } from "../types.ts"; // Correct or adjust as needed
|
DewarType,
|
||||||
|
DewarSerialNumber,
|
||||||
|
ContactPerson,
|
||||||
|
Address,
|
||||||
|
ContactsService,
|
||||||
|
AddressesService,
|
||||||
|
DewarsService,
|
||||||
|
ShipmentsService,
|
||||||
|
} from '../../openapi';
|
||||||
|
import Unipuck from '../components/Unipuck';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import DownloadIcon from '@mui/icons-material/Download';
|
||||||
|
|
||||||
interface DewarDetailsProps {
|
interface DewarDetailsProps {
|
||||||
dewar: Dewar;
|
dewar: Dewar;
|
||||||
@ -14,7 +39,6 @@ interface DewarDetailsProps {
|
|||||||
defaultContactPerson?: ContactPerson;
|
defaultContactPerson?: ContactPerson;
|
||||||
defaultReturnAddress?: Address;
|
defaultReturnAddress?: Address;
|
||||||
shipmentId: number;
|
shipmentId: number;
|
||||||
selectedShipment?: Shipment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NewContactPerson {
|
interface NewContactPerson {
|
||||||
@ -51,32 +75,86 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
|
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
|
||||||
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
|
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
|
||||||
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
|
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
|
||||||
const [newContactPerson, setNewContactPerson] = useState<NewContactPerson>({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
|
const [newContactPerson, setNewContactPerson] = useState<NewContactPerson>({
|
||||||
const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({ id: 0, street: '', city: '', zipcode: '', country: '' });
|
id: 0,
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
phone_number: '',
|
||||||
|
email: '',
|
||||||
|
});
|
||||||
|
const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({
|
||||||
|
id: 0,
|
||||||
|
street: '',
|
||||||
|
city: '',
|
||||||
|
zipcode: '',
|
||||||
|
country: '',
|
||||||
|
});
|
||||||
const [changesMade, setChangesMade] = useState(false);
|
const [changesMade, setChangesMade] = useState(false);
|
||||||
const [feedbackMessage, setFeedbackMessage] = useState('');
|
const [feedbackMessage, setFeedbackMessage] = useState<string>('');
|
||||||
const [openSnackbar, setOpenSnackbar] = useState(false);
|
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
|
||||||
|
const [newDewarType, setNewDewarType] = useState<string>('');
|
||||||
|
const [newDewarSerialNumber, setNewDewarSerialNumber] = useState<string>('');
|
||||||
|
const [selectedDewarType, setSelectedDewarType] = useState<string>(dewar.dewar_type_id?.toString() || '');
|
||||||
|
const [knownDewarTypes, setKnownDewarTypes] = useState<DewarType[]>([]);
|
||||||
|
const [knownSerialNumbers, setKnownSerialNumbers] = useState<DewarSerialNumber[]>([]);
|
||||||
|
const [selectedSerialNumber, setSelectedSerialNumber] = useState<string>('');
|
||||||
|
const [isQRCodeGenerated, setIsQRCodeGenerated] = useState(false);
|
||||||
|
const [qrCodeValue, setQrCodeValue] = useState(dewar.qrcode || '');
|
||||||
|
const qrCodeRef = useRef<HTMLCanvasElement>(null); //
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const fetchDewarTypes = async () => {
|
||||||
|
try {
|
||||||
|
const response = await DewarsService.getDewarTypesDewarsDewarTypesGet();
|
||||||
|
setKnownDewarTypes(response ?? []);
|
||||||
|
} catch (error) {
|
||||||
|
setFeedbackMessage('Failed to fetch dewar types.');
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
console.error('Error fetching dewar types:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchDewarTypes();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Fetch known serial numbers
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSerialNumbers = async () => {
|
||||||
|
try {
|
||||||
|
const response = await DewarsService.getAllSerialNumbersDewarsDewarSerialNumbersGet();
|
||||||
|
setKnownSerialNumbers(response ?? []);
|
||||||
|
} catch (error) {
|
||||||
|
setFeedbackMessage('Failed to fetch serial numbers.');
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
console.error('Error fetching serial numbers:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchSerialNumbers();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalTrackingNumber(dewar.tracking_number || '');
|
||||||
|
|
||||||
const setInitialContactPerson = () => {
|
const setInitialContactPerson = () => {
|
||||||
setSelectedContactPerson(
|
setSelectedContactPerson(
|
||||||
dewar.contact_person?.id?.toString() ||
|
dewar.contact_person?.id?.toString() || defaultContactPerson?.id?.toString() || ''
|
||||||
defaultContactPerson?.id?.toString() ||
|
|
||||||
''
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setInitialReturnAddress = () => {
|
const setInitialReturnAddress = () => {
|
||||||
setSelectedReturnAddress(
|
setSelectedReturnAddress(
|
||||||
dewar.return_address?.id?.toString() ||
|
dewar.return_address?.id?.toString() || defaultReturnAddress?.id?.toString() || ''
|
||||||
defaultReturnAddress?.id?.toString() ||
|
|
||||||
''
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
setLocalTrackingNumber(dewar.tracking_number || '');
|
|
||||||
setInitialContactPerson();
|
setInitialContactPerson();
|
||||||
setInitialReturnAddress();
|
setInitialReturnAddress();
|
||||||
|
|
||||||
|
if (dewar.dewar_type_id) {
|
||||||
|
setSelectedDewarType(dewar.dewar_type_id.toString());
|
||||||
|
}
|
||||||
|
if (dewar.dewar_serial_number_id) {
|
||||||
|
setSelectedSerialNumber(dewar.dewar_serial_number_id.toString());
|
||||||
|
}
|
||||||
}, [dewar, defaultContactPerson, defaultReturnAddress]);
|
}, [dewar, defaultContactPerson, defaultReturnAddress]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -108,17 +186,19 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
const fetchSamples = async () => {
|
const fetchSamples = async () => {
|
||||||
if (dewar.id) {
|
if (dewar.id) {
|
||||||
try {
|
try {
|
||||||
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(shipmentId, dewar.id);
|
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(
|
||||||
console.log("Fetched Samples: ", fetchedSamples);
|
shipmentId,
|
||||||
|
dewar.id
|
||||||
|
);
|
||||||
|
|
||||||
const updatedPuckStatuses = (dewar.pucks ?? []).map(puck => {
|
const updatedPuckStatuses = (dewar.pucks ?? []).map((puck) => {
|
||||||
const puckSamples = fetchedSamples.filter(sample => sample.puck_id === puck.id);
|
const puckSamples = fetchedSamples.filter((sample) => sample.puck_id === puck.id);
|
||||||
|
|
||||||
const statusArray = Array(16).fill('empty');
|
const statusArray = Array(16).fill('empty');
|
||||||
|
|
||||||
puckSamples.forEach(sample => {
|
puckSamples.forEach((sample) => {
|
||||||
if (sample.position >= 1 && sample.position <= 16) {
|
if (sample.position >= 1 && sample.position <= 16) {
|
||||||
statusArray[sample.position - 1] = 'filled'; // Corrected line
|
statusArray[sample.position - 1] = 'filled';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -127,7 +207,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
|
|
||||||
setPuckStatuses(updatedPuckStatuses);
|
setPuckStatuses(updatedPuckStatuses);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching samples:", error);
|
console.error('Error fetching samples:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -135,15 +215,62 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
fetchSamples();
|
fetchSamples();
|
||||||
}, [dewar, shipmentId]);
|
}, [dewar, shipmentId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedDewarType(
|
||||||
|
knownDewarTypes.find((type) => type.id === dewar.dewar_type_id)?.id.toString() || ''
|
||||||
|
);
|
||||||
|
}, [knownDewarTypes, dewar.dewar_type_id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedSerialNumber(
|
||||||
|
knownSerialNumbers.find((sn) => sn.id === dewar.dewar_serial_number_id)?.id.toString() || ''
|
||||||
|
);
|
||||||
|
}, [knownSerialNumbers, dewar.dewar_serial_number_id]);
|
||||||
|
|
||||||
const validateEmail = (email: string) => /\S+@\S+\.\S+/.test(email);
|
const validateEmail = (email: string) => /\S+@\S+\.\S+/.test(email);
|
||||||
const validatePhoneNumber = (phone: string) => /^\+?[1-9]\d{1,14}$/.test(phone);
|
const validatePhoneNumber = (phone: string) => /^\+?[1-9]\d{1,14}$/.test(phone);
|
||||||
const validateZipCode = (zipcode: string) => /^\d{5}(?:[-\s]\d{4})?$/.test(zipcode);
|
const validateZipCode = (zipcode: string) => /^\d{5}(?:[-\s]\d{4})?$/.test(zipcode);
|
||||||
|
|
||||||
if (!dewar) return <Typography>No dewar selected.</Typography>;
|
if (!dewar) return <Typography>No dewar selected.</Typography>;
|
||||||
|
|
||||||
|
const handleSaveNewDewarTypeAndSerialNumber = async () => {
|
||||||
|
if (newDewarType) {
|
||||||
|
try {
|
||||||
|
const typeResponse = await DewarsService.createDewarTypeDewarsDewarTypesPost({ dewar_type: newDewarType });
|
||||||
|
const serialResponse = await DewarsService.createDewarSerialNumberDewarsDewarSerialNumbersPost({
|
||||||
|
serial_number: newDewarSerialNumber,
|
||||||
|
dewar_type_id: typeResponse.id,
|
||||||
|
});
|
||||||
|
setKnownDewarTypes([...knownDewarTypes, typeResponse]);
|
||||||
|
setKnownSerialNumbers([...knownSerialNumbers, serialResponse]);
|
||||||
|
setSelectedDewarType(typeResponse.id.toString());
|
||||||
|
setSelectedSerialNumber(serialResponse.serial_number);
|
||||||
|
setNewDewarType('');
|
||||||
|
setNewDewarSerialNumber('');
|
||||||
|
setChangesMade(true);
|
||||||
|
} catch (error) {
|
||||||
|
setFeedbackMessage('Failed to save new dewar type and serial number.');
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
}
|
||||||
|
} else if (newDewarSerialNumber && selectedDewarType) {
|
||||||
|
try {
|
||||||
|
const response = await DewarsService.createDewarSerialNumberDewarsDewarSerialNumbersPost({
|
||||||
|
serial_number: newDewarSerialNumber,
|
||||||
|
dewar_type_id: parseInt(selectedDewarType, 10),
|
||||||
|
});
|
||||||
|
setKnownSerialNumbers([...knownSerialNumbers, response]);
|
||||||
|
setSelectedSerialNumber(response.serial_number);
|
||||||
|
setNewDewarSerialNumber('');
|
||||||
|
setChangesMade(true);
|
||||||
|
} catch (error) {
|
||||||
|
setFeedbackMessage('Failed to save new serial number.');
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleAddContact = async () => {
|
const handleAddContact = async () => {
|
||||||
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) ||
|
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) || !newContactPerson.firstName || !newContactPerson.lastName) {
|
||||||
!newContactPerson.firstName || !newContactPerson.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;
|
||||||
@ -189,7 +316,13 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
const a = await AddressesService.createReturnAddressAddressesPost(payload);
|
const a = await AddressesService.createReturnAddressAddressesPost(payload);
|
||||||
setReturnAddresses([...returnAddresses, a]);
|
setReturnAddresses([...returnAddresses, a]);
|
||||||
setFeedbackMessage('Return address added successfully.');
|
setFeedbackMessage('Return address added successfully.');
|
||||||
setNewReturnAddress({ id: 0, street: '', city: '', zipcode: '', country: '' });
|
setNewReturnAddress({
|
||||||
|
id: 0,
|
||||||
|
street: '',
|
||||||
|
city: '',
|
||||||
|
zipcode: '',
|
||||||
|
country: '',
|
||||||
|
});
|
||||||
setSelectedReturnAddress(a.id?.toString() || '');
|
setSelectedReturnAddress(a.id?.toString() || '');
|
||||||
} catch {
|
} catch {
|
||||||
setFeedbackMessage('Failed to create a new return address. Please try again later.');
|
setFeedbackMessage('Failed to create a new return address. Please try again later.');
|
||||||
@ -209,7 +342,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!selectedContactPerson || !selectedReturnAddress) {
|
if (!selectedContactPerson || !selectedReturnAddress) {
|
||||||
setFeedbackMessage("Please ensure all required fields are filled.");
|
setFeedbackMessage('Please ensure all required fields are filled.');
|
||||||
setOpenSnackbar(true);
|
setOpenSnackbar(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -217,15 +350,16 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
const dewarId = dewar.id;
|
const dewarId = dewar.id;
|
||||||
|
|
||||||
if (!dewarId) {
|
if (!dewarId) {
|
||||||
setFeedbackMessage("Invalid Dewar ID. Please ensure Dewar ID is provided.");
|
setFeedbackMessage('Invalid Dewar ID. Please ensure Dewar ID is provided.');
|
||||||
setOpenSnackbar(true);
|
setOpenSnackbar(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
dewar_id: dewarId,
|
|
||||||
dewar_name: dewar.dewar_name,
|
dewar_name: dewar.dewar_name,
|
||||||
|
dewar_type_id: parseInt(selectedDewarType, 10),
|
||||||
|
dewar_serial_number_id: parseInt(selectedSerialNumber, 10),
|
||||||
tracking_number: localTrackingNumber,
|
tracking_number: localTrackingNumber,
|
||||||
number_of_pucks: dewar.number_of_pucks,
|
number_of_pucks: dewar.number_of_pucks,
|
||||||
number_of_samples: dewar.number_of_samples,
|
number_of_samples: dewar.number_of_samples,
|
||||||
@ -240,10 +374,60 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
||||||
setFeedbackMessage("Changes saved successfully.");
|
setFeedbackMessage('Changes saved successfully.');
|
||||||
setChangesMade(false);
|
setChangesMade(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFeedbackMessage("Failed to save changes. Please try again later.");
|
setFeedbackMessage('Failed to save changes. Please try again later.');
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSerialNumberChange = (value: string) => {
|
||||||
|
setSelectedSerialNumber(value);
|
||||||
|
const serialNumber = knownSerialNumbers.find((sn) => sn.id.toString() === value);
|
||||||
|
if (serialNumber) {
|
||||||
|
setSelectedDewarType(serialNumber.dewar_type_id.toString());
|
||||||
|
}
|
||||||
|
setChangesMade(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerateQRCode = async () => {
|
||||||
|
if (!dewar) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await DewarsService.generateDewarQrcodeDewarsDewarIdGenerateQrcodePost(dewar.id);
|
||||||
|
setQrCodeValue(response.qrcode); // assuming the backend returns the QR code value
|
||||||
|
setIsQRCodeGenerated(true); // to track the state if the QR code is generated
|
||||||
|
setFeedbackMessage("QR Code generated successfully");
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to generate QR code:", error);
|
||||||
|
setFeedbackMessage("QR Code generation failed");
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownloadLabel = async () => {
|
||||||
|
if (!dewar) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await DewarsService.downloadDewarLabelDewarsDewarIdDownloadLabelGet(dewar.id);
|
||||||
|
|
||||||
|
// The response object might need parsing
|
||||||
|
const blob = new Blob([response as any], { type: 'application/pdf' });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute('download', `dewar_label_${dewar.id}.pdf`);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
setFeedbackMessage("Label downloaded successfully");
|
||||||
|
setOpenSnackbar(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to download label:", error);
|
||||||
|
setFeedbackMessage("Label download failed");
|
||||||
setOpenSnackbar(true);
|
setOpenSnackbar(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -254,7 +438,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Tracking Number"
|
label="Tracking Number"
|
||||||
value={localTrackingNumber}
|
value={localTrackingNumber}
|
||||||
onChange={e => {
|
onChange={(e) => {
|
||||||
setLocalTrackingNumber(e.target.value);
|
setLocalTrackingNumber(e.target.value);
|
||||||
setTrackingNumber(e.target.value);
|
setTrackingNumber(e.target.value);
|
||||||
setChangesMade(true);
|
setChangesMade(true);
|
||||||
@ -262,41 +446,121 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{ width: '300px', marginBottom: 2 }}
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
/>
|
/>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
|
<FormControl variant="outlined" sx={{ width: '300px', marginBottom: 2 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' }}>
|
<InputLabel id="dewar-serial-number-label">Dewar Serial Number</InputLabel>
|
||||||
{dewar.qrcode ? (
|
<Select
|
||||||
<QRCode value={dewar.qrcode} size={70} />
|
labelId="dewar-serial-number-label"
|
||||||
|
label="Dewar Serial Number"
|
||||||
|
value={selectedSerialNumber}
|
||||||
|
onChange={(e) => handleSerialNumberChange(e.target.value as string)}
|
||||||
|
displayEmpty
|
||||||
|
>
|
||||||
|
{knownSerialNumbers.map((sn) => (
|
||||||
|
<MenuItem key={sn.id} value={sn.id.toString()}>
|
||||||
|
{sn.serial_number}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
<MenuItem value="add">Add New Serial Number</MenuItem>
|
||||||
|
</Select>
|
||||||
|
{selectedSerialNumber === 'add' && (
|
||||||
|
<Box>
|
||||||
|
<FormControl variant="outlined" sx={{ width: '300px', marginBottom: 2 }}>
|
||||||
|
<InputLabel id="dewar-type-label">Dewar Type</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="dewar-type-label"
|
||||||
|
label="Dewar Type"
|
||||||
|
value={selectedDewarType}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value as string;
|
||||||
|
setSelectedDewarType(value);
|
||||||
|
setChangesMade(true);
|
||||||
|
}}
|
||||||
|
displayEmpty
|
||||||
|
>
|
||||||
|
{knownDewarTypes.map((type) => (
|
||||||
|
<MenuItem key={type.id} value={type.id.toString()}>
|
||||||
|
{type.dewar_type}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
<MenuItem value="add">Add New Dewar Type</MenuItem>
|
||||||
|
</Select>
|
||||||
|
{selectedDewarType === 'add' && (
|
||||||
|
<Box>
|
||||||
|
<TextField
|
||||||
|
label="Add New Dewar Type"
|
||||||
|
value={newDewarType}
|
||||||
|
onChange={(e) => setNewDewarType(e.target.value)}
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
<TextField
|
||||||
|
label="Add New Serial Number"
|
||||||
|
value={newDewarSerialNumber}
|
||||||
|
onChange={(e) => setNewDewarSerialNumber(e.target.value)}
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
|
/>
|
||||||
|
<Button onClick={handleSaveNewDewarTypeAndSerialNumber} variant="contained">
|
||||||
|
Save Dewar Type and Serial Number
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<Box sx={{ marginTop: 2 }}>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 2 }}>
|
||||||
|
{qrCodeValue ? (
|
||||||
|
<Box sx={{ textAlign: 'center', marginBottom: 2 }}>
|
||||||
|
<QRCode id="qrCodeCanvas" value={qrCodeValue} size={150} />
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', marginTop: 1 }}>
|
||||||
|
<Tooltip title="Download Label">
|
||||||
|
<IconButton onClick={handleDownloadLabel} sx={{ transform: 'scale(1.5)', margin: 1 }}>
|
||||||
|
<DownloadIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Typography variant="body2">Label is ready for download</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Typography>No QR code available</Typography>
|
<Typography>No QR code available</Typography>
|
||||||
)}
|
)}
|
||||||
<Button variant="contained" sx={{ marginTop: 1 }} onClick={() => { /* Add logic to generate QR Code */ }}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{ marginTop: 1 }}
|
||||||
|
onClick={handleGenerateQRCode}
|
||||||
|
>
|
||||||
Generate QR Code
|
Generate QR Code
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ marginTop: 2 }}>
|
<Box sx={{ marginTop: 2 }}>
|
||||||
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
|
<Typography variant="body1">Number of Pucks: {dewar.number_of_pucks}</Typography>
|
||||||
{(dewar.pucks ?? []).length > 0
|
{(dewar.pucks ?? []).length > 0 ? (
|
||||||
? <Unipuck pucks={(dewar.pucks ?? []).length} samples={puckStatuses} />
|
<Unipuck pucks={(dewar.pucks ?? []).length} samples={puckStatuses} />
|
||||||
: <Typography>No pucks attached to the dewar.</Typography>}
|
) : (
|
||||||
|
<Typography>No pucks attached to the dewar.</Typography>
|
||||||
|
)}
|
||||||
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
|
<Typography variant="body1">Number of Samples: {dewar.number_of_samples}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography variant="body1">Current Contact Person:</Typography>
|
<Typography variant="body1">Current Contact Person:</Typography>
|
||||||
<Select
|
<Select
|
||||||
value={selectedContactPerson}
|
value={selectedContactPerson}
|
||||||
onChange={e => {
|
onChange={(e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setSelectedContactPerson(value);
|
setSelectedContactPerson(value);
|
||||||
setIsCreatingContactPerson(value === 'add');
|
setIsCreatingContactPerson(value === 'add');
|
||||||
setChangesMade(true);
|
setChangesMade(true);
|
||||||
}}
|
}}
|
||||||
fullWidth
|
|
||||||
sx={{ marginBottom: 2 }}
|
|
||||||
variant="outlined"
|
|
||||||
displayEmpty
|
displayEmpty
|
||||||
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
>
|
>
|
||||||
{contactPersons.map(person => (
|
{contactPersons.map((person) => (
|
||||||
<MenuItem key={person.id?.toString()} value={person.id?.toString() || ''}>
|
<MenuItem key={person.id} value={person.id?.toString()}>
|
||||||
{person.firstname} {person.lastname}
|
{person.firstname} {person.lastname}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
@ -307,61 +571,72 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
<TextField
|
<TextField
|
||||||
label="First Name"
|
label="First Name"
|
||||||
value={newContactPerson.firstName}
|
value={newContactPerson.firstName}
|
||||||
onChange={e => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewContactPerson((prev) => ({
|
||||||
|
...prev,
|
||||||
|
firstName: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Last Name"
|
label="Last Name"
|
||||||
value={newContactPerson.lastName}
|
value={newContactPerson.lastName}
|
||||||
onChange={e => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewContactPerson((prev) => ({
|
||||||
|
...prev,
|
||||||
|
lastName: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Phone"
|
label="Phone Number"
|
||||||
value={newContactPerson.phone_number}
|
value={newContactPerson.phone_number}
|
||||||
onChange={e => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewContactPerson((prev) => ({
|
||||||
|
...prev,
|
||||||
|
phone_number: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
error={!validatePhoneNumber(newContactPerson.phone_number)}
|
|
||||||
helperText={!validatePhoneNumber(newContactPerson.phone_number) ? "Invalid phone number" : ""}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Email"
|
label="Email"
|
||||||
value={newContactPerson.email}
|
value={newContactPerson.email}
|
||||||
onChange={e => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewContactPerson((prev) => ({
|
||||||
|
...prev,
|
||||||
|
email: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
error={!validateEmail(newContactPerson.email)}
|
|
||||||
helperText={!validateEmail(newContactPerson.email) ? "Invalid email" : ""}
|
|
||||||
/>
|
/>
|
||||||
<Button variant="contained" onClick={handleAddContact}>
|
<Button onClick={handleAddContact} variant="contained">
|
||||||
Save Contact Person
|
Save New Contact Person
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Typography variant="body1">Current Return Address:</Typography>
|
<Typography variant="body1">Current Return Address:</Typography>
|
||||||
<Select
|
<Select
|
||||||
value={selectedReturnAddress}
|
value={selectedReturnAddress}
|
||||||
onChange={e => {
|
onChange={(e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setSelectedReturnAddress(value);
|
setSelectedReturnAddress(value);
|
||||||
setIsCreatingReturnAddress(value === 'add');
|
setIsCreatingReturnAddress(value === 'add');
|
||||||
setChangesMade(true);
|
setChangesMade(true);
|
||||||
}}
|
}}
|
||||||
fullWidth
|
|
||||||
sx={{ marginBottom: 2 }}
|
|
||||||
variant="outlined"
|
|
||||||
displayEmpty
|
displayEmpty
|
||||||
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
>
|
>
|
||||||
{returnAddresses.map(address => (
|
{returnAddresses.map((address) => (
|
||||||
<MenuItem key={address.id?.toString()} value={address.id?.toString() || ''}>
|
<MenuItem key={address.id} value={address.id?.toString()}>
|
||||||
{address.street}, {address.city}
|
{address.street}, {address.city}, {address.zipcode}, {address.country}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
<MenuItem value="add">Add New Return Address</MenuItem>
|
<MenuItem value="add">Add New Return Address</MenuItem>
|
||||||
@ -371,53 +646,77 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Street"
|
label="Street"
|
||||||
value={newReturnAddress.street}
|
value={newReturnAddress.street}
|
||||||
onChange={e => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewReturnAddress((prev) => ({
|
||||||
|
...prev,
|
||||||
|
street: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="City"
|
label="City"
|
||||||
value={newReturnAddress.city}
|
value={newReturnAddress.city}
|
||||||
onChange={e => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewReturnAddress((prev) => ({
|
||||||
|
...prev,
|
||||||
|
city: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Zip Code"
|
label="Zip Code"
|
||||||
value={newReturnAddress.zipcode}
|
value={newReturnAddress.zipcode}
|
||||||
onChange={e => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewReturnAddress((prev) => ({
|
||||||
|
...prev,
|
||||||
|
zipcode: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
error={!validateZipCode(newReturnAddress.zipcode)}
|
|
||||||
helperText={!validateZipCode(newReturnAddress.zipcode) ? "Invalid zip code" : ""}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Country"
|
label="Country"
|
||||||
value={newReturnAddress.country}
|
value={newReturnAddress.country}
|
||||||
onChange={e => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setNewReturnAddress((prev) => ({
|
||||||
|
...prev,
|
||||||
|
country: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
sx={{ width: '300px', marginBottom: 2 }}
|
||||||
sx={{ marginBottom: 1 }}
|
|
||||||
/>
|
/>
|
||||||
<Button variant="contained" onClick={handleAddAddress}>
|
<Button onClick={handleAddAddress} variant="contained">
|
||||||
Save Return Address
|
Save New Return Address
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{changesMade && (
|
|
||||||
<Button variant="contained" color="primary" onClick={handleSaveChanges} sx={{ marginTop: 2 }}>
|
<Box sx={{ marginTop: 2 }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSaveChanges}
|
||||||
|
disabled={!changesMade}
|
||||||
|
>
|
||||||
Save Changes
|
Save Changes
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</Box>
|
||||||
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={openSnackbar}
|
open={openSnackbar}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
onClose={() => setOpenSnackbar(false)}
|
onClose={() => setOpenSnackbar(false)}
|
||||||
message={feedbackMessage}
|
>
|
||||||
/>
|
<Alert onClose={() => setOpenSnackbar(false)} severity="info" sx={{ width: '100%' }}>
|
||||||
|
{feedbackMessage}
|
||||||
|
</Alert>
|
||||||
|
</Snackbar>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user