Connected frontend new contact, new address and shipments to backend

This commit is contained in:
GotthardG
2024-10-28 08:40:11 +01:00
parent e5073eacb8
commit aaf519f13f
5 changed files with 309 additions and 262 deletions

View File

@ -4,6 +4,8 @@ from fastapi import HTTPException, status
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Optional from typing import List, Optional
import logging import logging
import uuid
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -53,7 +55,7 @@ class Dewar(BaseModel):
class Shipment(BaseModel): class Shipment(BaseModel):
shipment_id: str shipment_id: Optional[str] = None
shipment_name: str shipment_name: str
shipment_date: str shipment_date: str
shipment_status: str shipment_status: str
@ -233,16 +235,14 @@ async def get_shipment_contact_persons():
return [{"shipment_id": shipment.shipment_id, "contact_person": shipment.get_shipment_contact_persons()} for shipment in return [{"shipment_id": shipment.shipment_id, "contact_person": shipment.get_shipment_contact_persons()} for shipment in
shipments] shipments]
# Creation of a new shipment
@app.post("/shipments", response_model=Shipment, status_code=status.HTTP_201_CREATED) @app.post("/shipments", response_model=Shipment, status_code=status.HTTP_201_CREATED)
async def create_shipment(shipment: Shipment): async def create_shipment(shipment: Shipment):
# Check for duplicate shipment_id # Automatically generate a shipment ID
if any(s.shipment_id == shipment.shipment_id for s in shipments): shipment_id = f'SHIP-{uuid.uuid4().hex[:8].upper()}' # Generates a unique shipment ID
raise HTTPException( shipment.shipment_id = shipment_id # Set the generated ID
status_code=status.HTTP_400_BAD_REQUEST,
detail="Shipment with this ID already exists."
)
# Append the shipment to the list
shipments.append(shipment) shipments.append(shipment)
return shipment return shipment

View File

@ -2,43 +2,32 @@ import * as React from 'react';
import { Box, Button, TextField, Typography, Select, MenuItem, Stack, FormControl, InputLabel } from '@mui/material'; import { Box, Button, TextField, Typography, Select, MenuItem, Stack, FormControl, InputLabel } from '@mui/material';
import { SelectChangeEvent } from '@mui/material'; import { SelectChangeEvent } from '@mui/material';
import { SxProps } from '@mui/material'; import { SxProps } from '@mui/material';
import { useEffect } from "react"; import {Dispatch, SetStateAction, useEffect, useState} from "react";
import { ContactPerson, Address, Proposal, DefaultService, OpenAPI } from "../../openapi"; import {ContactPerson, Address, Proposal, DefaultService, OpenAPI, Shipment_Input, type Dewar} from "../../openapi";
// Define the Shipment interface (example; adjust according to your types)
interface Shipment {
shipment_name: string;
contact_person: ContactPerson[];
proposal_number: string;
return_address: Address[];
comments: string;
}
// Define the ShipmentFormProps interface
interface ShipmentFormProps { interface ShipmentFormProps {
newShipment: Shipment; newShipment: Shipment;
setNewShipment: React.Dispatch<React.SetStateAction<Shipment>>; setNewShipment: Dispatch<SetStateAction<Shipment>>;
handleSaveShipment: () => void; onShipmentCreated: () => void;
proposals: Proposal[]; // Define proposals type
returnAddresses: Address[];
sx?: SxProps; sx?: SxProps;
} }
const ShipmentForm: React.FC<ShipmentFormProps> = ({ const ShipmentForm: React.FC<ShipmentFormProps> = ({
newShipment, newShipment,
setNewShipment, setNewShipment,
handleSaveShipment, onShipmentCreated,
sx = {}, sx = {},
}) => { }) => {
const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]); // State to hold contact persons const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]);
const [returnAddresses, setReturnAddresses] = React.useState<Address[]>([]); // Renamed for consistency const [returnAddresses, setReturnAddresses] = React.useState<Address[]>([]);
const [proposals, setProposals] = React.useState<Proposal[]>([]); const [proposals, setProposals] = React.useState<Proposal[]>([]);
const [shipments, setShipments] = useState([]);
const [isCreatingContactPerson, setIsCreatingContactPerson] = React.useState(false); const [isCreatingContactPerson, setIsCreatingContactPerson] = React.useState(false);
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = React.useState(false); const [isCreatingReturnAddress, setIsCreatingReturnAddress] = React.useState(false);
const [newContactPerson, setNewContactPerson] = React.useState({ const [newContactPerson, setNewContactPerson] = React.useState({
firstName: '', firstName: '',
lastName: '', lastName: '',
phone: '', phone_number: '',
email: '', email: '',
}); });
const [newReturnAddress, setNewReturnAddress] = React.useState<Address>({ const [newReturnAddress, setNewReturnAddress] = React.useState<Address>({
@ -50,12 +39,11 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
const [errorMessage, setErrorMessage] = React.useState<string | null>(null); // For error handling const [errorMessage, setErrorMessage] = React.useState<string | null>(null); // For error handling
useEffect(() => { useEffect(() => {
OpenAPI.BASE = 'http://127.0.0.1:8000'; // Set the base URL for OpenAPI OpenAPI.BASE = 'http://127.0.0.1:8000';
const getContacts = async () => { const getContacts = async () => {
console.log('Trying to fetch some contacts');
try { try {
const c: ContactPerson[] = await DefaultService.getContactsContactsGet(); // Fetch contacts const c: ContactPerson[] = await DefaultService.getContactsContactsGet();
setContactPersons(c); setContactPersons(c);
} catch (err) { } catch (err) {
console.error('Failed to fetch contact persons:', err); console.error('Failed to fetch contact persons:', err);
@ -64,9 +52,8 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
}; };
const getAddresses = async () => { const getAddresses = async () => {
console.log('Trying to fetch some return addresses');
try { try {
const a: Address[] = await DefaultService.getReturnAddressesReturnAddressesGet(); // Fetch addresses const a: Address[] = await DefaultService.getReturnAddressesReturnAddressesGet();
setReturnAddresses(a); setReturnAddresses(a);
} catch (err) { } catch (err) {
console.error('Failed to fetch return addresses:', err); console.error('Failed to fetch return addresses:', err);
@ -77,10 +64,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
const getProposals = async () => { const getProposals = async () => {
try { try {
const p: Proposal[] = await DefaultService.getProposalsProposalsGet(); const p: Proposal[] = await DefaultService.getProposalsProposalsGet();
console.log('Fetched Proposals:', p); // Debug log to check if proposals are being fetched
setProposals(p); setProposals(p);
} catch (err) { } catch (err) {
console.error('Error fetching proposals:', err); // Log the error console.error('Error fetching proposals:', err);
setErrorMessage('Failed to load proposals. Please try again later.'); setErrorMessage('Failed to load proposals. Please try again later.');
} }
}; };
@ -90,16 +76,78 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
getProposals(); getProposals();
}, []); }, []);
const handleSaveShipment = async () => {
// Set the base URL for the OpenAPI requests
OpenAPI.BASE = 'http://127.0.0.1:8000';
// Create the payload for the shipment
const payload: Shipment_Input = {
shipment_name: newShipment.shipment_name,
shipment_date: new Date().toISOString().split('T')[0], // YYYY-MM-DD
shipment_status: 'In preparation',
contact_person: newShipment.contact_person.map(person => ({
firstname: person.firstname,
lastname: person.lastname,
phone_number: person.phone_number,
email: person.email
})),
proposal_number: [
{
id: 1, // Use the correct ID from your context
number: newShipment.proposal_number || "Default Proposal Number" // Make sure it's a valid string
}
],
return_address: newShipment.return_address.map(address => ({
street: address.street,
city: address.city,
zipcode: address.zipcode,
country: address.country
})),
comments: newShipment.comments || '', // Set to an empty string if null
dewars: [] // Assuming you want this to be an empty array
};
// Print the payload to the console for debugging
console.log('Request Payload:', JSON.stringify(payload, null, 2));
try {
// Create a shipment using the API
const s: Shipment_Input[] = await DefaultService.createShipmentShipmentsPost(payload);
setShipments(s);
if (onShipmentCreated) {
onShipmentCreated(s[0]); // Call the function if it is defined
} else {
console.error('onShipmentCreated is not defined');
}
// Pass the created shipment to the callback
console.log('Shipment created successfully:', s);
// Optionally reset the form or handle the response
setNewShipment({
shipment_name: '',
contact_person: [], // Reset to an empty array
proposal_number: '',
return_address: [], // Reset to an empty array
comments: '',
dewars: [],
});
setErrorMessage(null); // Clear any previous error message
} catch (err) {
console.error('Failed to create shipment:', err.response?.data || err.message);
setErrorMessage('Failed to create shipment. Please try again later.');
}
};
const handleContactPersonChange = (event: SelectChangeEvent) => { 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: [] }); // Reset for new person setNewShipment({ ...newShipment, contact_person: [] });
} else { } else {
setIsCreatingContactPerson(false); setIsCreatingContactPerson(false);
const selectedPerson = contactPersons.find((person) => person.lastname === value) || null; const selectedPerson = contactPersons.find((person) => person.lastname === value) || null;
if (selectedPerson) { if (selectedPerson) {
setNewShipment({ ...newShipment, contact_person: [selectedPerson] }); // Set selected person setNewShipment({ ...newShipment, contact_person: [selectedPerson] });
} }
} }
}; };
@ -108,43 +156,48 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
const value = event.target.value; const value = event.target.value;
if (value === 'new') { if (value === 'new') {
setIsCreatingReturnAddress(true); setIsCreatingReturnAddress(true);
setNewShipment({ ...newShipment, return_address: [] }); // Reset for new address setNewShipment({ ...newShipment, return_address: [] });
} else { } else {
setIsCreatingReturnAddress(false); setIsCreatingReturnAddress(false);
const selectedAddress = returnAddresses.find((address) => address.city === value); // Match by a unique ID const selectedAddress = returnAddresses.find((address) => address.city === value);
if (selectedAddress) { if (selectedAddress) {
setNewShipment({ ...newShipment, return_address: [selectedAddress] }); // Set selected address setNewShipment({ ...newShipment, return_address: [selectedAddress] });
} }
} }
}; };
const handleProposalChange = (event: SelectChangeEvent) => { const handleProposalChange = (event: SelectChangeEvent) => {
const selectedProposal = event.target.value; const selectedProposal = event.target.value;
console.log('Selected Proposal:', selectedProposal); // Corrected log statement
setNewShipment({ ...newShipment, proposal_number: selectedProposal }); setNewShipment({ ...newShipment, proposal_number: selectedProposal });
}; };
const handleNewReturnAddressChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setNewReturnAddress((prev) => ({
...prev,
[name]: value,
}));
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target; const { name, value } = e.target;
setNewShipment((prev) => ({ ...prev, [name]: value })); setNewShipment((prev) => ({ ...prev, [name]: value }));
}; };
const handleSaveNewReturnAddress = async () => { const handleSaveNewContactPerson = async () => {
OpenAPI.BASE = 'http://127.0.0.1:8000'; const payload = {
firstname: newContactPerson.firstName,
lastname: newContactPerson.lastName,
phone_number: newContactPerson.phone_number,
email: newContactPerson.email,
};
if (!newReturnAddress.street || !newReturnAddress.city || !newReturnAddress.zipcode || !newReturnAddress.country) { try {
setErrorMessage('All fields are required.'); const c: ContactPerson = await DefaultService.createContactContactsPost(payload);
return; setContactPersons([...contactPersons, c]);
setErrorMessage(null);
} catch (err) {
console.error('Failed to create a new contact person:', err);
setErrorMessage('Failed to create a new contact person. Please try again later.');
} }
setNewContactPerson({ firstName: '', lastName: '', phone_number: '', email: '' });
setIsCreatingContactPerson(false);
};
const handleSaveNewReturnAddress = async () => {
const payload = { const payload = {
street: newReturnAddress.street.trim(), street: newReturnAddress.street.trim(),
city: newReturnAddress.city.trim(), city: newReturnAddress.city.trim(),
@ -152,6 +205,11 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
country: newReturnAddress.country.trim(), country: newReturnAddress.country.trim(),
}; };
if (!payload.street || !payload.city || !payload.zipcode || !payload.country) {
setErrorMessage('All fields are required.');
return;
}
try { try {
const a: Address = await DefaultService.createReturnAddressReturnAddressesPost(payload); const a: Address = await DefaultService.createReturnAddressReturnAddressesPost(payload);
setReturnAddresses([...returnAddresses, a]); setReturnAddresses([...returnAddresses, a]);
@ -205,6 +263,41 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
</MenuItem> </MenuItem>
</Select> </Select>
</FormControl> </FormControl>
{isCreatingContactPerson && (
<>
<TextField
label="First Name"
name="firstName"
value={newContactPerson.firstName}
onChange={(e) => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
fullWidth
/>
<TextField
label="Last Name"
name="lastName"
value={newContactPerson.lastName}
onChange={(e) => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
fullWidth
/>
<TextField
label="Phone"
name="phone"
value={newContactPerson.phone_number}
onChange={(e) => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
fullWidth
/>
<TextField
label="Email"
name="email"
value={newContactPerson.email}
onChange={(e) => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
fullWidth
/>
<Button variant="contained" color="primary" onClick={handleSaveNewContactPerson}>
Save New Contact Person
</Button>
</>
)}
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel>Proposal Number</InputLabel> <InputLabel>Proposal Number</InputLabel>
<Select <Select
@ -242,28 +335,28 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
label="Street" label="Street"
name="street" name="street"
value={newReturnAddress.street} value={newReturnAddress.street}
onChange={handleNewReturnAddressChange} onChange={(e) => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
fullWidth fullWidth
/> />
<TextField <TextField
label="City" label="City"
name="city" name="city"
value={newReturnAddress.city} value={newReturnAddress.city}
onChange={handleNewReturnAddressChange} onChange={(e) => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
fullWidth fullWidth
/> />
<TextField <TextField
label="Zip Code" label="Zip Code"
name="zipcode" name="zipcode"
value={newReturnAddress.zipcode} value={newReturnAddress.zipcode}
onChange={handleNewReturnAddressChange} onChange={(e) => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
fullWidth fullWidth
/> />
<TextField <TextField
label="Country" label="Country"
name="country" name="country"
value={newReturnAddress.country} value={newReturnAddress.country}
onChange={handleNewReturnAddressChange} onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
fullWidth fullWidth
/> />
<Button variant="contained" color="primary" onClick={handleSaveNewReturnAddress}> <Button variant="contained" color="primary" onClick={handleSaveNewReturnAddress}>

View File

@ -2,49 +2,49 @@ import * as React from 'react';
import { useEffect, useState, Dispatch, SetStateAction } from 'react'; import { useEffect, useState, Dispatch, SetStateAction } from 'react';
import { Button, Box, Typography, IconButton } from '@mui/material'; import { Button, Box, Typography, IconButton } from '@mui/material';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import DeleteIcon from '@mui/icons-material/Delete'; // Import delete icon import DeleteIcon from '@mui/icons-material/Delete';
import UploadFileIcon from '@mui/icons-material/UploadFile'; // Import the upload icon import UploadFileIcon from '@mui/icons-material/UploadFile';
import UploadDialog from './UploadDialog.tsx'; // Import the UploadDialog component import UploadDialog from './UploadDialog'; // Ensure the file extension is correct
//import {Shipment} from '../types.ts'; // Ensure Shipment type is correctly imported
import { SxProps } from '@mui/material'; import { SxProps } from '@mui/material';
import bottleGrey from '../assets/icons/bottle-svgrepo-com-grey.svg'; 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"; import { Shipment_Input, DefaultService, OpenAPI } from "../../openapi";
//interface ShipmentPanelProps { interface ShipmentPanelProps {
// selectedPage: string; selectedPage?: string;
// setIsCreatingShipment: Dispatch<SetStateAction<boolean>>; setIsCreatingShipment: Dispatch<SetStateAction<boolean>>;
// newShipment: Shipment; // Ensure this aligns with the Shipment type selectShipment: (shipment: Shipment_Input | null) => void;
// setNewShipment: Dispatch<SetStateAction<Shipment>>; sx?: SxProps;
// selectShipment: (shipment: Shipment | null) => void; // Allow null for deselection }
// sx?: SxProps; // Optional sx prop for styling
//}
const ShipmentPanel: React.FC<ShipmentPanelProps> = ({ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
setIsCreatingShipment, setIsCreatingShipment,
//newShipment,
//setNewShipment,
//selectedPage,
selectShipment, selectShipment,
sx, sx,
}) => { }) => {
const [shipments, setShipments] = useState<Shipment[]>([]); const [shipments, setShipments] = useState<Shipment_Input[]>([]);
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null); const [selectedShipment, setSelectedShipment] = useState<Shipment_Input | 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 fetchShipments = async () => {
setUploadDialogOpen(true); console.log('trying to fetch some shipments');
try {
const s: Shipment_Input[] = await DefaultService.getShipmentsShipmentsGet();
setShipments(s);
} catch (error) {
console.error('Failed to fetch shipments:', error);
setError("Failed to fetch shipments. Please try again later.");
}
}; };
const handleCloseUploadDialog = () => { useEffect(() => {
setUploadDialogOpen(false); OpenAPI.BASE = 'http://127.0.0.1:8000';
}; fetchShipments();
}, []);
// Status icon mapping
const statusIconMap: Record<string, string> = { const statusIconMap: Record<string, string> = {
"In Transit": bottleYellow, "In Transit": bottleYellow,
"Delivered": bottleGreen, "Delivered": bottleGreen,
@ -52,26 +52,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
"Unknown": bottleRed, "Unknown": bottleRed,
}; };
useEffect(() => { const handleShipmentSelection = (shipment: Shipment_Input) => {
OpenAPI.BASE = 'http://127.0.0.1:8000';
const fetchShipments = async () => {
console.log('trying to fetch some shipments');
try {
// Await the response from the service call
const s: Shipment_Input[] = await DefaultService.getShipmentsShipmentsGet();
// Set the shipments state with the fetched data
setShipments(s);
} catch (error) {
// If an error occurs, set the error message
console.error('Failed to fetch shipments:', error);
setError("Failed to fetch shipments. Please try again later.");
}
};
fetchShipments();
}, []);
const handleShipmentSelection = (shipment: Shipment) => {
const isCurrentlySelected = selectedShipment?.shipment_id === shipment.shipment_id; const isCurrentlySelected = selectedShipment?.shipment_id === shipment.shipment_id;
setSelectedShipment(isCurrentlySelected ? null : shipment); setSelectedShipment(isCurrentlySelected ? null : shipment);
selectShipment(isCurrentlySelected ? null : shipment); selectShipment(isCurrentlySelected ? null : shipment);
@ -83,11 +64,19 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
if (confirmed) { if (confirmed) {
const updatedShipments = shipments.filter(shipment => shipment.shipment_id !== selectedShipment.shipment_id); const updatedShipments = shipments.filter(shipment => shipment.shipment_id !== selectedShipment.shipment_id);
setShipments(updatedShipments); setShipments(updatedShipments);
setSelectedShipment(null); // Optionally clear the selected shipment setSelectedShipment(null);
} }
} }
}; };
const handleOpenUploadDialog = () => {
setUploadDialogOpen(true);
};
const handleCloseUploadDialog = () => {
setUploadDialogOpen(false);
};
return ( return (
<Box sx={{ width: '90%', borderRight: '1px solid #ccc', padding: 2, ...sx }}> <Box sx={{ width: '90%', borderRight: '1px solid #ccc', padding: 2, ...sx }}>
{error && <Typography color="error">{error}</Typography>} {error && <Typography color="error">{error}</Typography>}
@ -98,21 +87,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
variant="contained" variant="contained"
color="primary" color="primary"
startIcon={<AddIcon />} startIcon={<AddIcon />}
onClick={() => { onClick={() => setIsCreatingShipment(true)}
// setNewShipment({
// shipment_id: '', // Ensure this matches the Shipment type
// shipment_name: '',
// shipment_status: '',
// number_of_dewars: 0,
// shipment_date: '',
// contact_person: null, // Keep this as null to match Shipment type
// dewars: [],
// return_address: [], // Make sure return_address is initialized as an array
// proposal_number: undefined, // Optional property
// comments: '', // Optional property
// });
setIsCreatingShipment(true);
}}
sx={{ marginBottom: 2, padding: '10px 16px' }} sx={{ marginBottom: 2, padding: '10px 16px' }}
> >
Create Shipment Create Shipment
@ -144,8 +119,8 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
}, },
}} }}
> >
<div style={{display: 'flex', alignItems: 'center'}}> <div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{position: 'relative', marginRight: '8px'}}> <div style={{ position: 'relative', marginRight: '8px' }}>
<img <img
src={statusIconMap[shipment.shipment_status] || bottleGrey} src={statusIconMap[shipment.shipment_status] || bottleGrey}
alt={`Status: ${shipment.shipment_status}`} alt={`Status: ${shipment.shipment_status}`}
@ -168,36 +143,34 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
</div> </div>
<div> <div>
<div>{shipment.shipment_name}</div> <div>{shipment.shipment_name}</div>
<div style={{fontSize: '0.6rem', color: '#ccc'}}>{shipment.shipment_date}</div> <div style={{ fontSize: '0.6rem', color: '#ccc' }}>{shipment.shipment_date}</div>
<div style={{fontSize: '0.6rem', color: '#ccc'}}> <div style={{ fontSize: '0.6rem', color: '#ccc' }}>
Total Total Pucks: {shipment.dewars.reduce((total, dewar) => total + dewar.number_of_pucks, 0)}
Pucks: {shipment.dewars.reduce((total, dewar) => total + dewar.number_of_pucks, 0)}
</div> </div>
</div> </div>
</div> </div>
<div style={{display: 'flex', alignItems: 'center'}}> <div style={{ display: 'flex', alignItems: 'center' }}>
<IconButton <IconButton
onClick={handleOpenUploadDialog} onClick={handleOpenUploadDialog}
color="primary" color="primary"
title="Upload Sample Data Sheet" title="Upload Sample Data Sheet"
sx={{marginLeft: 1}} sx={{ marginLeft: 1 }}
> >
<UploadFileIcon/> <UploadFileIcon />
</IconButton> </IconButton>
{selectedShipment?.shipment_id === shipment.shipment_id && ( {selectedShipment?.shipment_id === shipment.shipment_id && (
<IconButton <IconButton
onClick={handleDeleteShipment} onClick={handleDeleteShipment}
color="error" color="error"
title="Delete Shipment" title="Delete Shipment"
sx={{marginLeft: 1}} sx={{ marginLeft: 1 }}
> >
<DeleteIcon/> <DeleteIcon />
</IconButton> </IconButton>
)} )}
</div> </div>
</Button> </Button>
))} ))}
{/* UploadDialog component */}
<UploadDialog <UploadDialog
open={uploadDialogOpen} open={uploadDialogOpen}
onClose={handleCloseUploadDialog} onClose={handleCloseUploadDialog}

View File

@ -1,87 +0,0 @@
import React from 'react';
import { Grid } from '@mui/material';
import ShipmentPanel from './ShipmentPanel.tsx';
import ShipmentDetails from './ShipmentDetails.tsx';
import ShipmentForm from './ShipmentForm.tsx';
import ParentComponent from "./ParentComponent.tsx";
import { Shipment, Dewar } from '../types.ts';
import { ContactPerson, Address, Proposal } from '../types.ts';
const ShipmentView: React.FC<ParentComponent> = ({
newShipment,
setNewShipment,
isCreatingShipment,
setIsCreatingShipment,
selectedShipment,
selectShipment,
selectedDewar,
setSelectedDewar,
handleSaveShipment,
contactPersons,
returnAddresses,
proposals,
}) => {
return (
<Grid container spacing={2} sx={{ height: '100vh' }}>
{/* Left column: ShipmentPanel */}
<Grid
item
xs={12}
md={3}
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 0,
}}
>
<ShipmentPanel
selectedPage="Shipments"
setIsCreatingShipment={setIsCreatingShipment}
newShipment={newShipment}
setNewShipment={setNewShipment}
selectShipment={selectShipment}
/>
</Grid>
{/* Right column: ShipmentForm or ShipmentDetails */}
<Grid
item
xs={12}
md={9}
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
}}
>
{isCreatingShipment ? (
<ShipmentForm
newShipment={newShipment}
setNewShipment={setNewShipment}
handleSaveShipment={handleSaveShipment}
contactPersons={contactPersons}
proposals={proposals}
returnAddresses={returnAddresses}
sx={{ flexGrow: 1 }} // Allow form to grow and take available space
/>
) : (
<ShipmentDetails
selectedShipment={selectedShipment}
setSelectedDewar={setSelectedDewar}
isCreatingShipment={isCreatingShipment}
newShipment={newShipment}
setNewShipment={setNewShipment}
handleSaveShipment={handleSaveShipment}
contactPersons={contactPersons}
proposals={proposals}
returnAddresses={returnAddresses}
sx={{ flexGrow: 1 }} // Allow details to grow and take available space
/>
)}
</Grid>
</Grid>
);
};
export default ShipmentView;

View File

@ -1,70 +1,138 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import ShipmentPanel from '../components/ShipmentPanel'; import { Grid } from '@mui/material';
import ShipmentForm from '../components/ShipmentForm'; // Import the ShipmentForm component import ShipmentPanel from '../components/ShipmentPanel.tsx';
import { Shipment, ContactPerson, Proposal, Address } from '../types'; // Ensure these types are defined // import ShipmentDetails from '../components/ShipmentDetails.tsx'; // Commented out for now
import ShipmentForm from '../components/ShipmentForm.tsx';
import { ContactPerson, Address, Proposal, Dewar, DefaultService, Shipment_Input } from '../../openapi'; // Import your API service
const ShipmentView: React.FC = () => { const ShipmentView: React.FC = () => {
const [isCreatingShipment, setIsCreatingShipment] = useState(false); // State to track if the form is open // State for managing shipments
const [newShipment, setNewShipment] = useState<Shipment>({ const [shipments, setShipments] = useState<Shipment_Input[]>([]);
shipment_id: '', const [newShipment, setNewShipment] = useState<Shipment_Input>({
shipment_name: '', shipment_name: '',
shipment_status: '', contact_person: [],
number_of_dewars: 0, proposal_number: '',
shipment_date: '',
contact_person: null,
dewars: [],
return_address: [], return_address: [],
proposal_number: undefined,
comments: '', comments: '',
dewars: [],
}); });
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
const [selectedShipment, setSelectedShipment] = useState<Shipment_Input | null>(null);
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
const [contactPersons, setContactPersons] = useState<ContactPerson[]>([]);
const [returnAddresses, setReturnAddresses] = useState<Address[]>([]);
const [proposals, setProposals] = useState<Proposal[]>([]);
// Dummy data for contact persons, proposals, and addresses // Fetch initial shipments data
const contactPersons: ContactPerson[] = [ const fetchShipments = async () => {
// Populate with contact person objects try {
]; const fetchedShipments = await DefaultService.getShipmentsShipmentsGet();
const proposals: Proposal[] = [ setShipments(fetchedShipments);
// Populate with proposal objects } catch (error) {
]; console.error("Failed to fetch shipments:", error);
const returnAddresses: Address[] = [ }
// Populate with return address objects
];
const selectShipment = (shipment: Shipment | null) => {
console.log('Selected Shipment:', shipment);
// Additional logic for selected shipment
}; };
const handleSaveShipment = () => { useEffect(() => {
console.log('Saving shipment:', newShipment); fetchShipments();
setIsCreatingShipment(false); // Close the form after saving }, []);
// Add logic to save the shipment
// Handle saving a new shipment
const handleSaveShipment = async (shipment: Shipment_Input) => {
try {
const savedShipment = await DefaultService.createShipmentShipmentsPost(shipment);
// Update the shipments list with the newly created shipment
setShipments(prev => [...prev, savedShipment]); // Add the new shipment to the state
onShipmentCreated(savedShipment); // Trigger the callback after saving
} catch (error) {
console.error("Failed to save shipment:", error);
}
};
// Shipment creation callback
const onShipmentCreated = async (newShipment: Shipment_Input) => {
setIsCreatingShipment(false); // Close the form
console.log("Shipment created:", newShipment);
// Reset the form
setNewShipment({
shipment_name: '',
contact_person: [],
proposal_number: [],
return_address: [],
comments: '',
dewars: [],
});
await fetchShipments(); // Re-fetch the shipments after creation
};
// Function to select a shipment
const selectShipment = (shipment: Shipment_Input | null) => {
setSelectedShipment(shipment);
}; };
return ( return (
<div style={{ display: 'flex', padding: '16px' }}> <Grid container spacing={2} sx={{ height: '100vh' }}>
{/* Shipment Panel on the left side */} {/* Left column: ShipmentPanel */}
<ShipmentPanel <Grid
selectedPage="shipments" item
setIsCreatingShipment={setIsCreatingShipment} // Pass the state setter xs={12}
selectShipment={selectShipment} md={3}
sx={{ width: '300px' }} // Adjust width as needed sx={{
/> display: 'flex',
{/* Conditional rendering of the ShipmentForm */} flexDirection: 'column',
<div style={{ marginLeft: '16px', flexGrow: 1 }}> flexGrow: 0,
}}
>
<ShipmentPanel
selectedPage="Shipments"
setIsCreatingShipment={setIsCreatingShipment}
fetchShipments={fetchShipments}
shipments={shipments} // Pass down the updated shipments
selectShipment={selectShipment}
/>
</Grid>
{/* Right column: ShipmentForm or ShipmentDetails */}
<Grid
item
xs={12}
md={9}
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
}}
>
{isCreatingShipment ? ( {isCreatingShipment ? (
<ShipmentForm <ShipmentForm
newShipment={newShipment} newShipment={newShipment}
setNewShipment={setNewShipment} setNewShipment={setNewShipment}
handleSaveShipment={handleSaveShipment} handleSaveShipment={handleSaveShipment}
onShipmentCreated={onShipmentCreated} // Pass this prop
contactPersons={contactPersons} contactPersons={contactPersons}
proposals={proposals} proposals={proposals}
returnAddresses={returnAddresses} returnAddresses={returnAddresses}
sx={{ flexGrow: 1 }} // Allow form to grow and take available space
/> />
) : ( ) : (
<div> {/* Content when form is not open */} </div> // Commented out ShipmentDetails for now
// <ShipmentDetails
// selectedShipment={selectedShipment}
// setSelectedDewar={setSelectedDewar}
// isCreatingShipment={isCreatingShipment}
// newShipment={newShipment}
// setNewShipment={setNewShipment}
// handleSaveShipment={handleSaveShipment}
// contactPersons={contactPersons}
// proposals={proposals}
// returnAddresses={returnAddresses}
// sx={{ flexGrow: 1 }} // Allow details to grow and take available space
// />
<div>No shipment details available.</div> // Placeholder while ShipmentDetails is commented out
)} )}
</div> </Grid>
</div> </Grid>
); );
}; };