Connected frontend shipments to backend
This commit is contained in:
parent
b6611fdac0
commit
2a4f2d1d85
206
backend/main.py
Normal file
206
backend/main.py
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContactPerson(BaseModel):
|
||||||
|
firstname: str
|
||||||
|
lastname: str
|
||||||
|
phone_number: str
|
||||||
|
email: str
|
||||||
|
|
||||||
|
|
||||||
|
class Address(BaseModel):
|
||||||
|
street: str
|
||||||
|
city: str
|
||||||
|
zipcode: str
|
||||||
|
country: str
|
||||||
|
|
||||||
|
|
||||||
|
class Dewar(BaseModel):
|
||||||
|
id: str
|
||||||
|
dewar_name: str
|
||||||
|
tracking_number: str
|
||||||
|
number_of_pucks: int
|
||||||
|
number_of_samples: int
|
||||||
|
return_address: List[Address]
|
||||||
|
contact_person: List[ContactPerson]
|
||||||
|
status: str
|
||||||
|
ready_date: Optional[str] = None
|
||||||
|
shipping_date: Optional[str] = None
|
||||||
|
arrival_date: Optional[str] = None
|
||||||
|
shippingStatus: str
|
||||||
|
arrivalStatus: str
|
||||||
|
qrcode: str
|
||||||
|
|
||||||
|
|
||||||
|
class Shipment(BaseModel):
|
||||||
|
shipment_id: str
|
||||||
|
shipment_name: str
|
||||||
|
shipment_date: str
|
||||||
|
shipment_status: str
|
||||||
|
contact_person: List[ContactPerson]
|
||||||
|
proposal_number: Optional[str] = None
|
||||||
|
return_address: List[Address]
|
||||||
|
comments: Optional[str] = None
|
||||||
|
dewars: List[Dewar]
|
||||||
|
|
||||||
|
def get_number_of_dewars(self) -> int:
|
||||||
|
return len(self.dewars)
|
||||||
|
|
||||||
|
def get_shipment_contact_persons(self) -> str:
|
||||||
|
return self.contact_person
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
# Example data for contacts
|
||||||
|
contacts = [
|
||||||
|
ContactPerson(firstname="Frodo", lastname="Baggins", phone_number="123-456-7890", email="frodo.baggins@lotr.com"),
|
||||||
|
ContactPerson(firstname="Samwise", lastname="Gamgee", phone_number="987-654-3210", email="samwise.gamgee@lotr.com"),
|
||||||
|
ContactPerson(firstname="Aragorn", lastname="Elessar", phone_number="123-333-4444",
|
||||||
|
email="aragorn.elessar@lotr.com"),
|
||||||
|
ContactPerson(firstname="Legolas", lastname="Greenleaf", phone_number="555-666-7777",
|
||||||
|
email="legolas.greenleaf@lotr.com"),
|
||||||
|
ContactPerson(firstname="Gimli", lastname="Son of Gloin", phone_number="888-999-0000",
|
||||||
|
email="gimli.sonofgloin@lotr.com"),
|
||||||
|
ContactPerson(firstname="Gandalf", lastname="The Grey", phone_number="222-333-4444",
|
||||||
|
email="gandalf.thegrey@lotr.com"),
|
||||||
|
ContactPerson(firstname="Boromir", lastname="Son of Denethor", phone_number="111-222-3333",
|
||||||
|
email="boromir.sonofdenethor@lotr.com"),
|
||||||
|
ContactPerson(firstname="Galadriel", lastname="Lady of Lothlórien", phone_number="444-555-6666",
|
||||||
|
email="galadriel.lothlorien@lotr.com"),
|
||||||
|
ContactPerson(firstname="Elrond", lastname="Half-elven", phone_number="777-888-9999",
|
||||||
|
email="elrond.halfelven@lotr.com"),
|
||||||
|
ContactPerson(firstname="Eowyn", lastname="Shieldmaiden of Rohan", phone_number="000-111-2222",
|
||||||
|
email="eowyn.rohan@lotr.com")
|
||||||
|
]
|
||||||
|
|
||||||
|
# Example data for return addresses
|
||||||
|
return_addresses = [
|
||||||
|
Address(street='123 Hobbiton St', city='Shire', zipcode='12345', country='Middle Earth'),
|
||||||
|
Address(street='456 Rohan Rd', city='Edoras', zipcode='67890', country='Middle Earth')
|
||||||
|
]
|
||||||
|
|
||||||
|
# Example data for dewars
|
||||||
|
dewars = [
|
||||||
|
Dewar(
|
||||||
|
id='DEWAR001',
|
||||||
|
dewar_name='Dewar One',
|
||||||
|
tracking_number='TRACK123',
|
||||||
|
number_of_pucks=7,
|
||||||
|
number_of_samples=70,
|
||||||
|
return_address=[return_addresses[0]],
|
||||||
|
contact_person=[contacts[0]],
|
||||||
|
status='Ready',
|
||||||
|
ready_date='2023-09-30',
|
||||||
|
shipping_date='2023-10-01',
|
||||||
|
arrival_date='2023-10-02',
|
||||||
|
shippingStatus='Shipped',
|
||||||
|
arrivalStatus='Arrived',
|
||||||
|
qrcode='QR123DEWAR001'
|
||||||
|
),
|
||||||
|
Dewar(
|
||||||
|
id='DEWAR002',
|
||||||
|
dewar_name='Dewar Two',
|
||||||
|
tracking_number='TRACK124',
|
||||||
|
number_of_pucks=3,
|
||||||
|
number_of_samples=33,
|
||||||
|
return_address=[return_addresses[1]],
|
||||||
|
contact_person=[contacts[1]],
|
||||||
|
status='In Transit',
|
||||||
|
ready_date='2023-10-01',
|
||||||
|
shipping_date='2023-10-02',
|
||||||
|
arrival_date='2023-10-04',
|
||||||
|
shippingStatus='In Transit',
|
||||||
|
arrivalStatus='Pending',
|
||||||
|
qrcode='QR123DEWAR002'
|
||||||
|
),
|
||||||
|
Dewar(
|
||||||
|
id='DEWAR003',
|
||||||
|
dewar_name='Dewar Three',
|
||||||
|
tracking_number='TRACK125',
|
||||||
|
number_of_pucks=4,
|
||||||
|
number_of_samples=47,
|
||||||
|
return_address=[return_addresses[0]],
|
||||||
|
contact_person=[contacts[2]],
|
||||||
|
status='Pending',
|
||||||
|
shippingStatus='Ready for Shipping',
|
||||||
|
arrivalStatus='Pending',
|
||||||
|
qrcode='QR123DEWAR003'
|
||||||
|
),
|
||||||
|
]
|
||||||
|
# Example: Attach a specific Dewar by its id to a shipment
|
||||||
|
specific_dewar_id = 'DEWAR003' # The ID of the Dewar you want to attach
|
||||||
|
|
||||||
|
# Find the Dewar with the matching id
|
||||||
|
specific_dewar = next((dewar for dewar in dewars if dewar.id == specific_dewar_id), None)
|
||||||
|
|
||||||
|
# Since shipments need dewars, define them afterward
|
||||||
|
shipments = [
|
||||||
|
Shipment(
|
||||||
|
shipment_id='SHIPMORDOR',
|
||||||
|
shipment_date='2024-10-10',
|
||||||
|
shipment_name='Shipment example test',
|
||||||
|
shipment_status='Delivered',
|
||||||
|
contact_person=[contacts[1]],
|
||||||
|
proposal_number='PROJ001',
|
||||||
|
return_address=[return_addresses[0]],
|
||||||
|
comments='Handle with care',
|
||||||
|
dewars=[specific_dewar] # Taking all dewars as an example
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/contacts", response_model=List[ContactPerson])
|
||||||
|
async def get_contacts():
|
||||||
|
return contacts
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/shipments", response_model=List[Shipment])
|
||||||
|
async def get_shipments():
|
||||||
|
return shipments
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/dewars", response_model=List[Dewar])
|
||||||
|
async def get_dewars():
|
||||||
|
return dewars
|
||||||
|
|
||||||
|
|
||||||
|
# Endpoint to get the number of dewars in each shipment
|
||||||
|
@app.get("/shipment_dewars")
|
||||||
|
async def get_shipment_dewars():
|
||||||
|
return [{"shipment_id": shipment.shipment_id, "number_of_dewars": shipment.get_number_of_dewars()} for shipment in
|
||||||
|
shipments]
|
||||||
|
|
||||||
|
@app.get("/shipment_contact_persons")
|
||||||
|
async def get_shipment_contact_persons():
|
||||||
|
return [{"shipment_id": shipment.shipment_id, "contact_person": shipment.get_shipment_contact_persons()} for shipment in
|
||||||
|
shipments]
|
||||||
|
|
||||||
|
# Creation of a new shipment
|
||||||
|
@app.post("/shipments", response_model=Shipment, status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_shipment(shipment: Shipment):
|
||||||
|
# Check for duplicate shipment_id
|
||||||
|
if any(s.shipment_id == shipment.shipment_id for s in shipments):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Shipment with this ID already exists."
|
||||||
|
)
|
||||||
|
|
||||||
|
shipments.append(shipment)
|
||||||
|
return shipment
|
207
frontend/package-lock.json
generated
207
frontend/package-lock.json
generated
@ -21,10 +21,12 @@
|
|||||||
"@mui/icons-material": "^6.1.5",
|
"@mui/icons-material": "^6.1.5",
|
||||||
"@mui/material": "^6.1.5",
|
"@mui/material": "^6.1.5",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"openapi-typescript-codegen": "^0.29.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-big-calendar": "^1.15.0",
|
"react-big-calendar": "^1.15.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-qr-code": "^2.0.15",
|
"react-qr-code": "^2.0.15",
|
||||||
|
"react-router-dom": "^6.27.0",
|
||||||
"react-scheduler": "^0.1.0"
|
"react-scheduler": "^0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -71,6 +73,22 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@apidevtools/json-schema-ref-parser": {
|
||||||
|
"version": "11.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz",
|
||||||
|
"integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@jsdevtools/ono": "^7.1.3",
|
||||||
|
"@types/json-schema": "^7.0.15",
|
||||||
|
"js-yaml": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/philsturgeon"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.25.9",
|
"version": "7.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.9.tgz",
|
||||||
@ -1222,6 +1240,11 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jsdevtools/ono": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="
|
||||||
|
},
|
||||||
"node_modules/@mui/core-downloads-tracker": {
|
"node_modules/@mui/core-downloads-tracker": {
|
||||||
"version": "6.1.5",
|
"version": "6.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.5.tgz",
|
||||||
@ -1574,6 +1597,14 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@remix-run/router": {
|
||||||
|
"version": "1.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
|
||||||
|
"integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@restart/hooks": {
|
"node_modules/@restart/hooks": {
|
||||||
"version": "0.4.16",
|
"version": "0.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
|
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
|
||||||
@ -1843,8 +1874,7 @@
|
|||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.15",
|
"version": "7.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
@ -2187,8 +2217,7 @@
|
|||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/babel-plugin-macros": {
|
"node_modules/babel-plugin-macros": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@ -2286,6 +2315,17 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/camelize": {
|
"node_modules/camelize": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
||||||
@ -2355,6 +2395,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "12.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||||
|
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@ -2904,6 +2952,19 @@
|
|||||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "11.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||||
|
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@ -2963,12 +3024,45 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||||
|
},
|
||||||
"node_modules/graphemer": {
|
"node_modules/graphemer": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/handlebars": {
|
||||||
|
"version": "4.7.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||||
|
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.5",
|
||||||
|
"neo-async": "^2.6.2",
|
||||||
|
"source-map": "^0.6.1",
|
||||||
|
"wordwrap": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"handlebars": "bin/handlebars"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.7"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"uglify-js": "^3.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/handlebars/node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
@ -3111,7 +3205,6 @@
|
|||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
},
|
},
|
||||||
@ -3164,6 +3257,17 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@ -3293,6 +3397,14 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/moment": {
|
"node_modules/moment": {
|
||||||
"version": "2.30.1",
|
"version": "2.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
@ -3341,6 +3453,11 @@
|
|||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/neo-async": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||||
@ -3354,6 +3471,21 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/openapi-typescript-codegen": {
|
||||||
|
"version": "0.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.29.0.tgz",
|
||||||
|
"integrity": "sha512-/wC42PkD0LGjDTEULa/XiWQbv4E9NwLjwLjsaJ/62yOsoYhwvmBR31kPttn1DzQ2OlGe5stACcF/EIkZk43M6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@apidevtools/json-schema-ref-parser": "^11.5.4",
|
||||||
|
"camelcase": "^6.3.0",
|
||||||
|
"commander": "^12.0.0",
|
||||||
|
"fs-extra": "^11.2.0",
|
||||||
|
"handlebars": "^4.7.8"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"openapi": "bin/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@ -3702,6 +3834,36 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "6.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
|
||||||
|
"integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.20.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "6.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
|
||||||
|
"integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.20.0",
|
||||||
|
"react-router": "6.27.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-scheduler": {
|
"node_modules/react-scheduler": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-scheduler/-/react-scheduler-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-scheduler/-/react-scheduler-0.1.0.tgz",
|
||||||
@ -3796,16 +3958,6 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rrule": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rrule/-/rrule-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/run-parallel": {
|
"node_modules/run-parallel": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
@ -4052,6 +4204,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uglify-js": {
|
||||||
|
"version": "3.19.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
|
||||||
|
"integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"uglifyjs": "bin/uglifyjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/uncontrollable": {
|
"node_modules/uncontrollable": {
|
||||||
"version": "7.2.1",
|
"version": "7.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
|
||||||
@ -4066,6 +4230,14 @@
|
|||||||
"react": ">=15.0.0"
|
"react": ">=15.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
|
||||||
@ -4203,6 +4375,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wordwrap": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
@ -23,10 +23,12 @@
|
|||||||
"@mui/icons-material": "^6.1.5",
|
"@mui/icons-material": "^6.1.5",
|
||||||
"@mui/material": "^6.1.5",
|
"@mui/material": "^6.1.5",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"openapi-typescript-codegen": "^0.29.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-big-calendar": "^1.15.0",
|
"react-big-calendar": "^1.15.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-qr-code": "^2.0.15",
|
"react-qr-code": "^2.0.15",
|
||||||
|
"react-router-dom": "^6.27.0",
|
||||||
"react-scheduler": "^0.1.0"
|
"react-scheduler": "^0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
"shipping_date": "",
|
"shipping_date": "",
|
||||||
"arrival_date": "",
|
"arrival_date": "",
|
||||||
"shippingStatus": "shipped",
|
"shippingStatus": "shipped",
|
||||||
"arrivalStatus": "arrived",
|
"arrivalStatus": "not arrived",
|
||||||
"returned": "",
|
"returned": "",
|
||||||
"qrcode": ""
|
"qrcode": ""
|
||||||
},
|
},
|
||||||
@ -102,7 +102,7 @@
|
|||||||
"shipping_date": "2024-02-21",
|
"shipping_date": "2024-02-21",
|
||||||
"arrival_date": "",
|
"arrival_date": "",
|
||||||
"shippingStatus": "shipped",
|
"shippingStatus": "shipped",
|
||||||
"arrivalStatus": "arrived",
|
"arrivalStatus": "not arrived",
|
||||||
"returned": "",
|
"returned": "",
|
||||||
"qrcode": "https://example.com/qrcode/dewar_004"
|
"qrcode": "https://example.com/qrcode/dewar_004"
|
||||||
},
|
},
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ResponsiveAppBar from './components/ResponsiveAppBar.tsx';
|
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
||||||
|
import ResponsiveAppBar from './components/ResponsiveAppBar';
|
||||||
|
import ShipmentView from './pages/ShipmentView';
|
||||||
|
import HomePage from './pages/HomeView'; // Assuming this is a default export
|
||||||
|
import ResultsView from './pages/ResultsView';
|
||||||
|
import PlanningView from './pages/PlanningView';
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Router>
|
||||||
<ResponsiveAppBar />
|
<ResponsiveAppBar />
|
||||||
</div>
|
<Routes>
|
||||||
|
<Route path="/" element={<HomePage />} />
|
||||||
|
<Route path="/shipments" element={<ShipmentView />} />
|
||||||
|
<Route path="/planning" element={<PlanningView />} />
|
||||||
|
<Route path="/results" element={<ResultsView />} />
|
||||||
|
</Routes>
|
||||||
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ const Calendar: React.FC = () => {
|
|||||||
eventContent={eventContent}
|
eventContent={eventContent}
|
||||||
height={700}
|
height={700}
|
||||||
headerToolbar={{
|
headerToolbar={{
|
||||||
left: 'prev,next today',
|
left: 'prev,next',
|
||||||
center: 'title',
|
center: 'title',
|
||||||
right: 'dayGridMonth',
|
right: 'dayGridMonth',
|
||||||
}}
|
}}
|
||||||
|
@ -11,36 +11,21 @@ import Container from '@mui/material/Container';
|
|||||||
import Avatar from '@mui/material/Avatar';
|
import Avatar from '@mui/material/Avatar';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
|
import { Link } from 'react-router-dom'; // Import Link for navigation
|
||||||
import logo from '../assets/icons/psi_01_sn.svg';
|
import logo from '../assets/icons/psi_01_sn.svg';
|
||||||
import '../App.css';
|
import '../App.css';
|
||||||
import { Shipment, Dewar, ContactPerson, Address, Proposal } from '../types.ts'; // Import types from a single statement
|
|
||||||
import ShipmentView from './ShipmentView.tsx';
|
|
||||||
import PlanningView from './PlanningView.tsx';
|
|
||||||
|
|
||||||
const pages = ['Validator', 'Shipments', 'Samples', 'Planning', 'Experiments', 'Results', 'Docs'];
|
// Page definitions for navigation
|
||||||
|
const pages = [
|
||||||
|
{ name: 'Home', path: '/' },
|
||||||
|
{ name: 'Shipments', path: '/shipments' },
|
||||||
|
{ name: 'Planning', path: '/planning' },
|
||||||
|
{ name: 'Results', path: '/results' }
|
||||||
|
];
|
||||||
|
|
||||||
const ResponsiveAppBar: React.FC = () => {
|
const ResponsiveAppBar: React.FC = () => {
|
||||||
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
|
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
|
||||||
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
|
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
|
||||||
const [selectedPage, setSelectedPage] = useState<string>('Shipments');
|
|
||||||
const [newShipment, setNewShipment] = useState<Shipment>({
|
|
||||||
shipment_id: '',
|
|
||||||
shipment_name: '',
|
|
||||||
shipment_status: '',
|
|
||||||
number_of_dewars: 0,
|
|
||||||
shipment_date: '',
|
|
||||||
return_address: [], // Correctly initialize return_address
|
|
||||||
contact_person: null, // Use null
|
|
||||||
dewars: [],
|
|
||||||
});
|
|
||||||
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
|
||||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
|
||||||
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
|
|
||||||
|
|
||||||
// Define missing state variables for contacts, addresses, and proposals
|
|
||||||
const [contactPersons, setContactPersons] = useState<ContactPerson[]>([]);
|
|
||||||
const [returnAddresses, setReturnAddresses] = useState<Address[]>([]);
|
|
||||||
const [proposals, setProposals] = useState<Proposal[]>([]);
|
|
||||||
|
|
||||||
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorElNav(event.currentTarget);
|
setAnchorElNav(event.currentTarget);
|
||||||
@ -50,11 +35,6 @@ const ResponsiveAppBar: React.FC = () => {
|
|||||||
setAnchorElNav(null);
|
setAnchorElNav(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageClick = (page: string) => {
|
|
||||||
setSelectedPage(page);
|
|
||||||
handleCloseNavMenu();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorElUser(event.currentTarget);
|
setAnchorElUser(event.currentTarget);
|
||||||
};
|
};
|
||||||
@ -63,36 +43,15 @@ const ResponsiveAppBar: React.FC = () => {
|
|||||||
setAnchorElUser(null);
|
setAnchorElUser(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updated selectShipment to accept Shipment | null
|
|
||||||
const selectShipment = (shipment: Shipment | null) => {
|
|
||||||
setSelectedShipment(shipment);
|
|
||||||
setIsCreatingShipment(false);
|
|
||||||
setSelectedDewar(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveShipment = () => {
|
|
||||||
console.log('Saving shipment:', newShipment);
|
|
||||||
setIsCreatingShipment(false);
|
|
||||||
setNewShipment({
|
|
||||||
shipment_id: '',
|
|
||||||
shipment_name: '',
|
|
||||||
shipment_status: '',
|
|
||||||
number_of_dewars: 0,
|
|
||||||
shipment_date: '',
|
|
||||||
return_address: [], // Add return_address to the reset state
|
|
||||||
contact_person: null, // Use null
|
|
||||||
dewars: [],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AppBar position="static" sx={{ backgroundColor: '#2F4858' }}>
|
<AppBar position="static" sx={{ backgroundColor: '#2F4858' }}>
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
<a className="nav" href="">
|
{/* Logo and Title */}
|
||||||
|
<Link to="/" style={{ textDecoration: 'none' }}>
|
||||||
<img src={logo} height="50px" alt="PSI logo" />
|
<img src={logo} height="50px" alt="PSI logo" />
|
||||||
</a>
|
</Link>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
noWrap
|
noWrap
|
||||||
@ -109,6 +68,8 @@ const ResponsiveAppBar: React.FC = () => {
|
|||||||
>
|
>
|
||||||
Heidi v2
|
Heidi v2
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
|
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="large"
|
size="large"
|
||||||
@ -134,23 +95,30 @@ const ResponsiveAppBar: React.FC = () => {
|
|||||||
onClose={handleCloseNavMenu}
|
onClose={handleCloseNavMenu}
|
||||||
>
|
>
|
||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
<MenuItem key={page} onClick={() => handlePageClick(page)}>
|
<MenuItem key={page.name} onClick={handleCloseNavMenu}>
|
||||||
{page}
|
<Link to={page.path} style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||||
|
{page.name}
|
||||||
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Desktop Menu */}
|
||||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
<Button
|
<Button
|
||||||
key={page}
|
key={page.name}
|
||||||
onClick={() => handlePageClick(page)}
|
component={Link} // Make button a Link
|
||||||
|
to={page.path}
|
||||||
sx={{ my: 2, color: 'white', display: 'block', fontSize: '1rem', padding: '12px 24px' }}
|
sx={{ my: 2, color: 'white', display: 'block', fontSize: '1rem', padding: '12px 24px' }}
|
||||||
>
|
>
|
||||||
{page}
|
{page.name}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* User Settings Menu */}
|
||||||
<Box sx={{ flexGrow: 0 }}>
|
<Box sx={{ flexGrow: 0 }}>
|
||||||
<Tooltip title="Open settings">
|
<Tooltip title="Open settings">
|
||||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||||
@ -180,25 +148,6 @@ const ResponsiveAppBar: React.FC = () => {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</Container>
|
</Container>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Box sx={{ width: '100%', borderRight: '1px solid #ccc', padding: 2 }}>
|
|
||||||
{selectedPage === 'Shipments' && (
|
|
||||||
<ShipmentView
|
|
||||||
newShipment={newShipment}
|
|
||||||
setNewShipment={setNewShipment}
|
|
||||||
isCreatingShipment={isCreatingShipment}
|
|
||||||
setIsCreatingShipment={setIsCreatingShipment}
|
|
||||||
selectedShipment={selectedShipment}
|
|
||||||
selectShipment={selectShipment} // Now accepts Shipment | null
|
|
||||||
selectedDewar={selectedDewar}
|
|
||||||
setSelectedDewar={setSelectedDewar}
|
|
||||||
handleSaveShipment={handleSaveShipment}
|
|
||||||
contactPersons={contactPersons}
|
|
||||||
returnAddresses={returnAddresses}
|
|
||||||
proposals={proposals}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{selectedPage === 'Planning' && <PlanningView />}
|
|
||||||
</Box>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,7 @@ import bottleGrey from '../assets/icons/bottle-svgrepo-com-grey.svg';
|
|||||||
import bottleYellow from '../assets/icons/bottle-svgrepo-com-yellow.svg';
|
import bottleYellow from '../assets/icons/bottle-svgrepo-com-yellow.svg';
|
||||||
import bottleGreen from '../assets/icons/bottle-svgrepo-com-green.svg';
|
import bottleGreen from '../assets/icons/bottle-svgrepo-com-green.svg';
|
||||||
import bottleRed from '../assets/icons/bottle-svgrepo-com-red.svg';
|
import bottleRed from '../assets/icons/bottle-svgrepo-com-red.svg';
|
||||||
|
import {Shipment_Input, DefaultService, OpenAPI} from "../../openapi";
|
||||||
|
|
||||||
interface ShipmentPanelProps {
|
interface ShipmentPanelProps {
|
||||||
selectedPage: string;
|
selectedPage: string;
|
||||||
@ -22,17 +23,16 @@ interface ShipmentPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||||
setIsCreatingShipment,
|
//setIsCreatingShipment,
|
||||||
newShipment,
|
//newShipment,
|
||||||
setNewShipment,
|
//setNewShipment,
|
||||||
selectedPage,
|
//selectedPage,
|
||||||
selectShipment,
|
selectShipment,
|
||||||
sx,
|
sx,
|
||||||
}) => {
|
}) => {
|
||||||
const [shipments, setShipments] = useState<Shipment[]>([]);
|
const [shipments, setShipments] = useState<Shipment[]>([]);
|
||||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
|
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
|
||||||
|
|
||||||
const handleOpenUploadDialog = () => {
|
const handleOpenUploadDialog = () => {
|
||||||
@ -43,6 +43,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
setUploadDialogOpen(false);
|
setUploadDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Status icon mapping
|
// Status icon mapping
|
||||||
const statusIconMap: Record<string, string> = {
|
const statusIconMap: Record<string, string> = {
|
||||||
"In Transit": bottleYellow,
|
"In Transit": bottleYellow,
|
||||||
@ -52,9 +53,13 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
OpenAPI.BASE='http://127.0.0.1:8000'
|
||||||
const fetchShipments = async () => {
|
const fetchShipments = async () => {
|
||||||
|
console.log('trying to fetch some shipments');
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/shipmentsdb.json');
|
DefaultService.getShipmentsShipmentsGet().then((s : Shipment_Input[]) => {
|
||||||
|
setShipments(s);
|
||||||
|
});
|
||||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@ -95,21 +100,21 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
onClick={() => {
|
//onClick={() => {
|
||||||
setNewShipment({
|
// setNewShipment({
|
||||||
shipment_id: '', // Ensure this matches the Shipment type
|
// shipment_id: '', // Ensure this matches the Shipment type
|
||||||
shipment_name: '',
|
// shipment_name: '',
|
||||||
shipment_status: '',
|
// shipment_status: '',
|
||||||
number_of_dewars: 0,
|
// number_of_dewars: 0,
|
||||||
shipment_date: '',
|
// shipment_date: '',
|
||||||
contact_person: null, // Keep this as null to match Shipment type
|
// contact_person: null, // Keep this as null to match Shipment type
|
||||||
dewars: [],
|
// dewars: [],
|
||||||
return_address: [], // Make sure return_address is initialized as an array
|
// return_address: [], // Make sure return_address is initialized as an array
|
||||||
proposal_number: undefined, // Optional property
|
// proposal_number: undefined, // Optional property
|
||||||
comments: '', // Optional property
|
// comments: '', // Optional property
|
||||||
});
|
// });
|
||||||
setIsCreatingShipment(true);
|
// setIsCreatingShipment(true);
|
||||||
}}
|
//}}
|
||||||
sx={{ marginBottom: 2, padding: '10px 16px' }}
|
sx={{ marginBottom: 2, padding: '10px 16px' }}
|
||||||
>
|
>
|
||||||
Create Shipment
|
Create Shipment
|
||||||
|
@ -130,6 +130,9 @@ const UploadDialog: React.FC<UploadDialogProps> = ({ open, onClose }) => {
|
|||||||
)}
|
)}
|
||||||
{fileSummary && (
|
{fileSummary && (
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
|
<Typography color="success.main">
|
||||||
|
File uploaded successfully!
|
||||||
|
</Typography>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
<strong>File Summary:</strong>
|
<strong>File Summary:</strong>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import DewarDetails from './DewarDetails.tsx';
|
import DewarDetails from './DewarDetails.tsx';
|
||||||
import { Shipment, ContactPerson, Address, Dewar } from '../types.ts';
|
import { Shipment, ContactPerson, Address, Dewar } from '../types.ts';
|
||||||
import shipmentData from '../../public/shipmentsdb.json'; // Adjust the path to where your JSON file is stored
|
import shipmentData from '../../public/shipmentsdb.json';
|
||||||
|
import {Contact, DefaultService, OpenAPI} from "../../openapi";
|
||||||
|
|
||||||
const ParentComponent = () => {
|
const ParentComponent = () => {
|
||||||
const [dewars, setDewars] = useState<Dewar[]>([]);
|
const [dewars, setDewars] = useState<Dewar[]>([]);
|
||||||
const [contactPersons, setContactPersons] = useState<ContactPerson[]>([]);
|
const [contactPersons, setContactPersons] = useState<Contact[]>([]);
|
||||||
const [returnAddresses, setReturnAddresses] = useState<Address[]>([]);
|
const [returnAddresses, setReturnAddresses] = useState<Address[]>([]);
|
||||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
OpenAPI.BASE='http://127.0.0.1:8000'
|
||||||
|
|
||||||
const firstShipment = shipmentData.shipments[0] as Shipment; // Ensure proper typing
|
const firstShipment = shipmentData.shipments[0] as Shipment; // Ensure proper typing
|
||||||
setSelectedShipment(firstShipment);
|
setSelectedShipment(firstShipment);
|
||||||
setContactPersons(firstShipment.contact_person || []); // Set to array directly
|
console.log('this is meeee');
|
||||||
|
DefaultService.getContactsContactsGet().then((c : Contact[]) => {
|
||||||
|
setContactPersons(c);
|
||||||
|
});
|
||||||
|
|
||||||
if (firstShipment.return_address) {
|
if (firstShipment.return_address) {
|
||||||
setReturnAddresses(firstShipment.return_address); // Set to array directly
|
setReturnAddresses(firstShipment.return_address); // Set to array directly
|
||||||
}
|
}
|
157
frontend/src/keep/ResponsiveAppBar.tsx
Normal file
157
frontend/src/keep/ResponsiveAppBar.tsx
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import AppBar from '@mui/material/AppBar';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
import { Button } from '@mui/material';
|
||||||
|
import logo from '../assets/icons/psi_01_sn.svg';
|
||||||
|
import '../App.css';
|
||||||
|
|
||||||
|
// Page definitions for navigation
|
||||||
|
const pages = [
|
||||||
|
{ name: 'Home', path: '/' },
|
||||||
|
{ name: 'Shipments', path: '/shipments' },
|
||||||
|
{ name: 'Planning', path: '/planning' },
|
||||||
|
{ name: 'Results', path: '/results' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const ResponsiveAppBar: React.FC = () => {
|
||||||
|
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
|
||||||
|
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
|
||||||
|
const [selectedPage, setSelectedPage] = useState<string>('Home'); // Default to 'Home' page
|
||||||
|
|
||||||
|
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorElNav(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseNavMenu = () => {
|
||||||
|
setAnchorElNav(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageClick = (page: string) => {
|
||||||
|
setSelectedPage(page);
|
||||||
|
handleCloseNavMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorElUser(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseUserMenu = () => {
|
||||||
|
setAnchorElUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AppBar position="static" sx={{ backgroundColor: '#2F4858' }}>
|
||||||
|
<Container maxWidth="xl">
|
||||||
|
<Toolbar disableGutters>
|
||||||
|
{/* Logo and Title */}
|
||||||
|
<a className="nav" href="">
|
||||||
|
<img src={logo} height="50px" alt="PSI logo" />
|
||||||
|
</a>
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
noWrap
|
||||||
|
component="a"
|
||||||
|
href="#app-bar-with-responsive-menu"
|
||||||
|
sx={{
|
||||||
|
mr: 2,
|
||||||
|
display: { xs: 'none', md: 'flex' },
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontWeight: 300,
|
||||||
|
color: 'inherit',
|
||||||
|
textDecoration: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Heidi v2
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
|
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
|
||||||
|
<IconButton
|
||||||
|
size="large"
|
||||||
|
aria-label="menu"
|
||||||
|
onClick={handleOpenNavMenu}
|
||||||
|
color="inherit"
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Menu
|
||||||
|
id="menu-appbar"
|
||||||
|
anchorEl={anchorElNav}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'bottom',
|
||||||
|
horizontal: 'left',
|
||||||
|
}}
|
||||||
|
keepMounted
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'left',
|
||||||
|
}}
|
||||||
|
open={Boolean(anchorElNav)}
|
||||||
|
onClose={handleCloseNavMenu}
|
||||||
|
>
|
||||||
|
{pages.map((page) => (
|
||||||
|
<MenuItem key={page.name} onClick={() => handlePageClick(page.name)}>
|
||||||
|
{page.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Desktop Menu */}
|
||||||
|
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||||
|
{pages.map((page) => (
|
||||||
|
<Button
|
||||||
|
key={page.name}
|
||||||
|
onClick={() => handlePageClick(page.name)}
|
||||||
|
sx={{ my: 2, color: 'white', display: 'block', fontSize: '1rem', padding: '12px 24px' }}
|
||||||
|
>
|
||||||
|
{page.name}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* User Settings Menu */}
|
||||||
|
<Box sx={{ flexGrow: 0 }}>
|
||||||
|
<Tooltip title="Open settings">
|
||||||
|
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||||
|
<Avatar />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Menu
|
||||||
|
sx={{ mt: '45px' }}
|
||||||
|
id="menu-appbar"
|
||||||
|
anchorEl={anchorElUser}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'right',
|
||||||
|
}}
|
||||||
|
keepMounted
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'right',
|
||||||
|
}}
|
||||||
|
open={Boolean(anchorElUser)}
|
||||||
|
onClose={handleCloseUserMenu}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={handleCloseUserMenu}>DUO</MenuItem>
|
||||||
|
<MenuItem onClick={handleCloseUserMenu}>Logout</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
</Container>
|
||||||
|
</AppBar>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResponsiveAppBar;
|
@ -1,17 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Typography, Button, Stack, TextField } from '@mui/material';
|
import {
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
Button,
|
||||||
|
Stack,
|
||||||
|
TextField,
|
||||||
|
Stepper,
|
||||||
|
Step,
|
||||||
|
StepLabel,
|
||||||
|
} from '@mui/material';
|
||||||
import ShipmentForm from './ShipmentForm.tsx';
|
import ShipmentForm from './ShipmentForm.tsx';
|
||||||
import DewarDetails from './DewarDetails.tsx';
|
import DewarDetails from './DewarDetails.tsx';
|
||||||
import { Shipment, Dewar, ContactPerson, Proposal, Address } from '../types.ts';
|
import { Shipment, Dewar, ContactPerson, Proposal, Address } from '../types.ts';
|
||||||
import { SxProps } from '@mui/system';
|
import { SxProps } from '@mui/system';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import bottleGrey from '../assets/icons/bottle-svgrepo-com-grey.svg';
|
import bottleIcon from '../assets/icons/bottle-svgrepo-com-grey.svg';
|
||||||
import bottleYellow from '../assets/icons/bottle-svgrepo-com-yellow.svg';
|
|
||||||
import bottleGreen from '../assets/icons/bottle-svgrepo-com-green.svg';
|
|
||||||
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
|
||||||
import AirplanemodeActiveIcon from "@mui/icons-material/AirplanemodeActive";
|
import AirplanemodeActiveIcon from "@mui/icons-material/AirplanemodeActive";
|
||||||
import StoreIcon from "@mui/icons-material/Store";
|
import StoreIcon from "@mui/icons-material/Store";
|
||||||
import DeleteIcon from "@mui/icons-material/Delete"; // Import delete icon
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
import {Contact} from "../../openapi"; // Import delete icon
|
||||||
|
|
||||||
interface ShipmentDetailsProps {
|
interface ShipmentDetailsProps {
|
||||||
selectedShipment: Shipment | null;
|
selectedShipment: Shipment | null;
|
||||||
@ -20,7 +27,7 @@ interface ShipmentDetailsProps {
|
|||||||
newShipment: Shipment;
|
newShipment: Shipment;
|
||||||
setNewShipment: React.Dispatch<React.SetStateAction<Shipment>>;
|
setNewShipment: React.Dispatch<React.SetStateAction<Shipment>>;
|
||||||
handleSaveShipment: () => void;
|
handleSaveShipment: () => void;
|
||||||
contactPersons: ContactPerson[];
|
contactPersons: Contact[];
|
||||||
proposals: Proposal[];
|
proposals: Proposal[];
|
||||||
returnAddresses: Address[];
|
returnAddresses: Address[];
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
@ -46,16 +53,8 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
tracking_number: '',
|
tracking_number: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const shippingStatusMap: { [key: string]: string } = {
|
// Step titles based on your status
|
||||||
"not shipped": "grey",
|
const steps = ['Ready for Shipping', 'Shipped', 'Arrived'];
|
||||||
"shipped": "yellow",
|
|
||||||
"arrived": "green",
|
|
||||||
};
|
|
||||||
|
|
||||||
const arrivalStatusMap: { [key: string]: string } = {
|
|
||||||
"not arrived": "grey",
|
|
||||||
"arrived": "green",
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (localSelectedDewar) {
|
if (localSelectedDewar) {
|
||||||
@ -143,6 +142,17 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
console.log('Add new return address:', address);
|
console.log('Add new return address:', address);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Function to determine the color of the step icon
|
||||||
|
const getStepIconColor = (dewar: Dewar) => {
|
||||||
|
const { status, shippingStatus, arrivalStatus } = dewar;
|
||||||
|
if (status === 'Ready for Shipping') return 'green'; // Bottle Icon
|
||||||
|
if (shippingStatus === 'shipped') return 'green'; // Plane Icon
|
||||||
|
if (shippingStatus === 'not shipped') return 'yellow'; // Plane Icon
|
||||||
|
if (arrivalStatus === 'arrived') return 'green'; // Store Icon
|
||||||
|
if (arrivalStatus === 'not arrived') return 'yellow'; // Store Icon
|
||||||
|
return 'grey'; // Default color
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ ...sx, padding: 2, textAlign: 'left' }}>
|
<Box sx={{ ...sx, padding: 2, textAlign: 'left' }}>
|
||||||
{/* Add Dewar Button - only visible if no dewar is selected */}
|
{/* Add Dewar Button - only visible if no dewar is selected */}
|
||||||
@ -177,7 +187,6 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
|
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
|
||||||
<Typography variant="body1">Number of Pucks: {totalPucks}</Typography>
|
<Typography variant="body1">Number of Pucks: {totalPucks}</Typography>
|
||||||
<Typography variant="body1">Number of Samples: {totalSamples}</Typography>
|
<Typography variant="body1">Number of Samples: {totalSamples}</Typography>
|
||||||
@ -195,28 +204,26 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
returnAddresses={returnAddresses} // Pass return addresses
|
returnAddresses={returnAddresses} // Pass return addresses
|
||||||
addNewContactPerson={addNewContactPerson} // Pass function to add a new contact person
|
addNewContactPerson={addNewContactPerson} // Pass function to add a new contact person
|
||||||
addNewReturnAddress={addNewReturnAddress} // Pass function to add a new return address
|
addNewReturnAddress={addNewReturnAddress} // Pass function to add a new return address
|
||||||
shipping_date={localSelectedDewar?.shipping_date} // Ensure these are passed
|
|
||||||
arrival_date={localSelectedDewar?.arrival_date}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedShipment.dewars.map((dewar: Dewar) => (
|
|
||||||
|
{selectedShipment.dewars.map((dewar) => (
|
||||||
<Button
|
<Button
|
||||||
key={dewar.tracking_number}
|
key={dewar.tracking_number}
|
||||||
onClick={() => handleDewarSelection(dewar)}
|
onClick={() => handleDewarSelection(dewar)}
|
||||||
|
variant="outlined"
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
textAlign: 'left',
|
|
||||||
backgroundColor: localSelectedDewar?.tracking_number === dewar.tracking_number ? '#d0f0c0' : '#f0f0f0', // Highlight if selected
|
|
||||||
padding: 2,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
padding: 1,
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
borderRadius: 1,
|
||||||
|
backgroundColor: localSelectedDewar?.tracking_number === dewar.tracking_number ? '#f0f0f0' : '#fff', // Color when selected
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 80,
|
|
||||||
height: 80,
|
|
||||||
backgroundColor: '#e0e0e0',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@ -226,7 +233,20 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
{dewar.qrcode ? (
|
{dewar.qrcode ? (
|
||||||
<QRCode value={dewar.qrcode} size={70} />
|
<QRCode value={dewar.qrcode} size={70} />
|
||||||
) : (
|
) : (
|
||||||
<Typography variant="body2" color="textSecondary">No QR code available</Typography>
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
border: '1px dashed #ccc', // Dashed border for placeholder
|
||||||
|
borderRadius: 1,
|
||||||
|
color: 'text.secondary'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2">No QR Code</Typography>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -237,48 +257,37 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
<Typography variant="body2">Tracking Number: {dewar.tracking_number}</Typography>
|
<Typography variant="body2">Tracking Number: {dewar.tracking_number}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'center', flexDirection: 'row', justifyContent: 'space-evenly' }}>
|
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'center', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
{/* Status icons and date information */}
|
{/* Horizontal Stepper for status */}
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
|
<Stepper alternativeLabel activeStep={steps.indexOf(dewar.status) !== -1 ? steps.indexOf(dewar.status) : 0}>
|
||||||
<img
|
{steps.map((label, index) => (
|
||||||
src={dewar.status === "in preparation" ? bottleYellow : (dewar.status === "ready for shipping" ? bottleGreen : bottleGrey)}
|
<Step key={label}>
|
||||||
alt={`Status: ${dewar.status}`}
|
<StepLabel StepIconComponent={({ active, completed }) => {
|
||||||
style={{ width: '40px', height: '40px', marginBottom: '4px' }}
|
const color = getStepIconColor(dewar); // Use status for color
|
||||||
|
return (
|
||||||
|
<span style={{ color }}>
|
||||||
|
{index === 0 ? (
|
||||||
|
<img src={bottleIcon} alt="Bottle Icon" style={{ width: 24, height: 24 }} />
|
||||||
|
) : index === 1 ? (
|
||||||
|
<AirplanemodeActiveIcon style={{ color }} />
|
||||||
|
) : index === 2 ? (
|
||||||
|
<StoreIcon style={{ color , width: 24, height: 24}} />
|
||||||
|
) : (
|
||||||
|
<StoreIcon style={{ color }} // Use store icon for arrival status
|
||||||
/>
|
/>
|
||||||
<Typography variant="caption" sx={{ fontSize: '12px' }} color="textSecondary">
|
)}
|
||||||
{dewar.ready_date ? `Ready: ${new Date(dewar.ready_date).toLocaleDateString()}` : 'N/A'}
|
</span>
|
||||||
|
);
|
||||||
|
}}>{label}</StepLabel>
|
||||||
|
{/* Display associated date */}
|
||||||
|
<Typography variant="body2">
|
||||||
|
{index === 0 ? dewar.ready_date :
|
||||||
|
index === 1 ? dewar.shipping_date :
|
||||||
|
index === 2 ? dewar.arrival_date : ''}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Step>
|
||||||
|
))}
|
||||||
<ArrowForwardIcon sx={{ margin: '0 8px', fontSize: '40px', alignSelf: 'center' }} />
|
</Stepper>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
|
|
||||||
<AirplanemodeActiveIcon
|
|
||||||
sx={{
|
|
||||||
color: shippingStatusMap[dewar.shippingStatus || ""] || "grey",
|
|
||||||
fontSize: '40px',
|
|
||||||
marginBottom: '4px'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography variant="caption" sx={{ fontSize: '12px' }} color="textSecondary">
|
|
||||||
{dewar.shipping_date ? `Shipped: ${new Date(dewar.shipping_date).toLocaleDateString()}` : 'N/A'}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<ArrowForwardIcon sx={{ margin: '0 8px', fontSize: '40px', alignSelf: 'center' }} />
|
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
|
|
||||||
<StoreIcon
|
|
||||||
sx={{
|
|
||||||
color: arrivalStatusMap[dewar.arrivalStatus || ""] || "grey",
|
|
||||||
fontSize: '40px',
|
|
||||||
marginBottom: '4px'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography variant="caption" sx={{ fontSize: '12px' }} color="textSecondary">
|
|
||||||
{dewar.arrival_date ? `Arrived: ${new Date(dewar.arrival_date).toLocaleDateString()}` : 'N/A'}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Delete button if the dewar is selected */}
|
{/* Delete button if the dewar is selected */}
|
||||||
{localSelectedDewar?.tracking_number === dewar.tracking_number && (
|
{localSelectedDewar?.tracking_number === dewar.tracking_number && (
|
||||||
@ -290,9 +299,8 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
height: '40px',
|
height: '40px',
|
||||||
marginLeft: 2,
|
marginLeft: 2,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
alignSelf: 'center'
|
alignSelf: 'center',
|
||||||
}}
|
}}
|
||||||
title="Delete Dewar"
|
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</Button>
|
</Button>
|
@ -3,6 +3,8 @@ import { Box, Button, TextField, Typography, Select, MenuItem, Stack, FormContro
|
|||||||
import { SelectChangeEvent } from '@mui/material';
|
import { SelectChangeEvent } from '@mui/material';
|
||||||
import { Shipment, ContactPerson, Proposal, Address } from '../types.ts'; // Adjust import paths as necessary
|
import { Shipment, ContactPerson, Proposal, Address } from '../types.ts'; // Adjust import paths as necessary
|
||||||
import { SxProps } from '@mui/material';
|
import { SxProps } from '@mui/material';
|
||||||
|
import {useEffect} from "react";
|
||||||
|
import {DefaultService} from "../../openapi";
|
||||||
|
|
||||||
interface ShipmentFormProps {
|
interface ShipmentFormProps {
|
||||||
newShipment: Shipment;
|
newShipment: Shipment;
|
||||||
@ -33,21 +35,21 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
});
|
});
|
||||||
const [newReturnAddress, setNewReturnAddress] = React.useState('');
|
const [newReturnAddress, setNewReturnAddress] = React.useState('');
|
||||||
|
|
||||||
const handleContactPersonChange = (event: SelectChangeEvent<string>) => {
|
const handleContactPersonChange = (event: SelectChangeEvent) => {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
if (value === 'new') {
|
if (value === 'new') {
|
||||||
setIsCreatingContactPerson(true);
|
setIsCreatingContactPerson(true);
|
||||||
setNewShipment({ ...newShipment, contact_person: [] }); // Set to empty array for new person
|
setNewShipment({ ...newShipment, contact_person: [] }); // Set to empty array for new person
|
||||||
} else {
|
} else {
|
||||||
setIsCreatingContactPerson(false);
|
setIsCreatingContactPerson(false);
|
||||||
const selectedPerson = contactPersons.find((person) => person.name === value) || null;
|
const selectedPerson = contactPersons.find((person) => person.lastname === value) || null;
|
||||||
if (selectedPerson) {
|
if (selectedPerson) {
|
||||||
setNewShipment({ ...newShipment, contact_person: [selectedPerson] }); // Wrap in array
|
setNewShipment({ ...newShipment, contact_person: [selectedPerson] }); // Wrap in array
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReturnAddressChange = (event: SelectChangeEvent<string>) => {
|
const handleReturnAddressChange = (event: SelectChangeEvent) => {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
if (value === 'new') {
|
if (value === 'new') {
|
||||||
setIsCreatingReturnAddress(true);
|
setIsCreatingReturnAddress(true);
|
||||||
@ -105,7 +107,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<InputLabel>Contact Person</InputLabel>
|
<InputLabel>Contact Person</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={newShipment.contact_person?.[0]?.name || ''} // Access first contact person
|
value={newShipment.contact_person?.[0]?.lastname || ''}
|
||||||
onChange={handleContactPersonChange}
|
onChange={handleContactPersonChange}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
>
|
>
|
||||||
@ -113,8 +115,8 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
<em>Select a Contact Person</em>
|
<em>Select a Contact Person</em>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{contactPersons.map((person) => (
|
{contactPersons.map((person) => (
|
||||||
<MenuItem key={person.id} value={person.name}>
|
<MenuItem key={person.id} value={person.lastname}>
|
||||||
{person.name}
|
{person.lastname}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
<MenuItem value="new">
|
<MenuItem value="new">
|
@ -3,25 +3,12 @@ import { Grid } from '@mui/material';
|
|||||||
import ShipmentPanel from './ShipmentPanel.tsx';
|
import ShipmentPanel from './ShipmentPanel.tsx';
|
||||||
import ShipmentDetails from './ShipmentDetails.tsx';
|
import ShipmentDetails from './ShipmentDetails.tsx';
|
||||||
import ShipmentForm from './ShipmentForm.tsx';
|
import ShipmentForm from './ShipmentForm.tsx';
|
||||||
|
import ParentComponent from "./ParentComponent.tsx";
|
||||||
import { Shipment, Dewar } from '../types.ts';
|
import { Shipment, Dewar } from '../types.ts';
|
||||||
import { ContactPerson, Address, Proposal } from '../types.ts';
|
import { ContactPerson, Address, Proposal } from '../types.ts';
|
||||||
|
|
||||||
interface ShipmentProps {
|
|
||||||
newShipment: Shipment;
|
|
||||||
setNewShipment: React.Dispatch<React.SetStateAction<Shipment>>;
|
|
||||||
isCreatingShipment: boolean;
|
|
||||||
setIsCreatingShipment: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
selectedShipment: Shipment | null;
|
|
||||||
selectShipment: (shipment: Shipment | null) => void; // Allow null for deselection
|
|
||||||
selectedDewar: Dewar | null;
|
|
||||||
setSelectedDewar: React.Dispatch<React.SetStateAction<Dewar | null>>;
|
|
||||||
handleSaveShipment: () => void;
|
|
||||||
contactPersons: ContactPerson[];
|
|
||||||
returnAddresses: Address[];
|
|
||||||
proposals: Proposal[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const ShipmentView: React.FC<ShipmentProps> = ({
|
const ShipmentView: React.FC<ParentComponent> = ({
|
||||||
newShipment,
|
newShipment,
|
||||||
setNewShipment,
|
setNewShipment,
|
||||||
isCreatingShipment,
|
isCreatingShipment,
|
||||||
@ -41,11 +28,11 @@ const ShipmentView: React.FC<ShipmentProps> = ({
|
|||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
xs={12}
|
xs={12}
|
||||||
md={3} // Adjust width for left column
|
md={3}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: 0, // Do not allow the panel to grow
|
flexGrow: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ShipmentPanel
|
<ShipmentPanel
|
||||||
@ -53,7 +40,7 @@ const ShipmentView: React.FC<ShipmentProps> = ({
|
|||||||
setIsCreatingShipment={setIsCreatingShipment}
|
setIsCreatingShipment={setIsCreatingShipment}
|
||||||
newShipment={newShipment}
|
newShipment={newShipment}
|
||||||
setNewShipment={setNewShipment}
|
setNewShipment={setNewShipment}
|
||||||
selectShipment={selectShipment} // This now accepts Shipment | null
|
selectShipment={selectShipment}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
8
frontend/src/pages/HomeView.tsx
Normal file
8
frontend/src/pages/HomeView.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// components/HomePage.tsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const HomeView: React.FC = () => {
|
||||||
|
return <div>Welcome to the Home Page</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HomeView;
|
@ -1,9 +1,10 @@
|
|||||||
// Planning.tsx
|
// Planning.tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CustomCalendar from './Calendar.tsx';
|
import CustomCalendar from '../components/Calendar.tsx';
|
||||||
|
|
||||||
const PlanningView: React.FC = () => {
|
const PlanningView: React.FC = () => {
|
||||||
return <CustomCalendar />;
|
return <CustomCalendar />;
|
||||||
|
//return <div>Welcome to the Planning Page</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlanningView;
|
export default PlanningView;
|
8
frontend/src/pages/ResultsView.tsx
Normal file
8
frontend/src/pages/ResultsView.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// components/ResultView.tsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ResultsView: React.FC = () => {
|
||||||
|
return <div>Results page</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResultsView;
|
44
frontend/src/pages/ShipmentView.tsx
Normal file
44
frontend/src/pages/ShipmentView.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import ShipmentPanel from '../components/ShipmentPanel';
|
||||||
|
import { Shipment } from '../types';
|
||||||
|
|
||||||
|
const ShipmentView: React.FC = () => {
|
||||||
|
//const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||||
|
//const [newShipment, setNewShipment] = useState<Shipment>({
|
||||||
|
// shipment_id: '',
|
||||||
|
// shipment_name: '',
|
||||||
|
// shipment_status: '',
|
||||||
|
// number_of_dewars: 0,
|
||||||
|
// shipment_date: '',
|
||||||
|
// contact_person: null,
|
||||||
|
// dewars: [],
|
||||||
|
// return_address: [],
|
||||||
|
// proposal_number: undefined,
|
||||||
|
// comments: '',
|
||||||
|
//});
|
||||||
|
|
||||||
|
const selectShipment = (shipment: Shipment | null) => {
|
||||||
|
console.log('Selected Shipment:', shipment);
|
||||||
|
// Additional logic for selected shipment
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', padding: '16px' }}>
|
||||||
|
{/* Shipment Panel on the left side */}
|
||||||
|
<ShipmentPanel
|
||||||
|
selectedPage="shipments"
|
||||||
|
//setIsCreatingShipment={setIsCreatingShipment}
|
||||||
|
//newShipment={newShipment}
|
||||||
|
//setNewShipment={setNewShipment}
|
||||||
|
selectShipment={selectShipment}
|
||||||
|
sx={{ width: '300px' }} // Adjust width as needed
|
||||||
|
/>
|
||||||
|
{/* Additional content can go here */}
|
||||||
|
<div style={{ marginLeft: '16px', flexGrow: 1 }}>
|
||||||
|
{/* Other components or information related to selected shipment can go here */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ShipmentView;
|
@ -1,6 +1,9 @@
|
|||||||
export interface ContactPerson {
|
export interface ContactPerson {
|
||||||
id: string; // ID is a string
|
id: string;
|
||||||
name: string; // Name of the contact person
|
lastname: string;
|
||||||
|
firstname: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Address {
|
export interface Address {
|
||||||
@ -19,14 +22,6 @@ export interface Proposal {
|
|||||||
number: string; // Proposal number
|
number: string; // Proposal number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NewShipment {
|
|
||||||
shipment_name: string;
|
|
||||||
contact_person: string; // Could also be a ContactPerson type if you want to relate it
|
|
||||||
proposal_number: string;
|
|
||||||
return_address: string; // Could be changed to Address
|
|
||||||
comments: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Dewar {
|
export interface Dewar {
|
||||||
id: string; // Add this line
|
id: string; // Add this line
|
||||||
dewar_name: string;
|
dewar_name: string;
|
||||||
@ -46,8 +41,8 @@ export interface Dewar {
|
|||||||
|
|
||||||
export interface Shipment {
|
export interface Shipment {
|
||||||
shipment_id: string;
|
shipment_id: string;
|
||||||
shipment_date: string;
|
|
||||||
shipment_name: string;
|
shipment_name: string;
|
||||||
|
shipment_date: string;
|
||||||
number_of_dewars: number;
|
number_of_dewars: number;
|
||||||
shipment_status: string;
|
shipment_status: string;
|
||||||
contact_person: ContactPerson[] | null; // Change to an array to accommodate multiple contacts
|
contact_person: ContactPerson[] | null; // Change to an array to accommodate multiple contacts
|
||||||
@ -56,24 +51,3 @@ export interface Shipment {
|
|||||||
comments?: string;
|
comments?: string;
|
||||||
dewars: Dewar[];
|
dewars: Dewar[];
|
||||||
}
|
}
|
||||||
|
|
||||||
import { DateLocalizer, Event as BigCalendarEvent } from 'react-big-calendar'; // Adjust according to your import structure
|
|
||||||
|
|
||||||
export interface CustomEvent extends BigCalendarEvent {
|
|
||||||
id: number; // Custom property
|
|
||||||
title: string; // Ensure title is included
|
|
||||||
start: Date;
|
|
||||||
end: Date;
|
|
||||||
beamline?: string; // Optional property for beamline
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Shift {
|
|
||||||
beamline: string;
|
|
||||||
local_contact: string;
|
|
||||||
beamtime_shift: 'morning' | 'evening' | 'night';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Beamtime {
|
|
||||||
date: string;
|
|
||||||
shifts: Shift[];
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user