Connected frontend new contact, new address and shipments to backend
This commit is contained in:
parent
aaf519f13f
commit
7f46006435
@ -38,9 +38,9 @@ class Proposal(BaseModel):
|
|||||||
number: str
|
number: str
|
||||||
|
|
||||||
class Dewar(BaseModel):
|
class Dewar(BaseModel):
|
||||||
id: str
|
id: Optional[str] = None
|
||||||
dewar_name: str
|
dewar_name: str
|
||||||
tracking_number: str
|
tracking_number: Optional[str] = None
|
||||||
number_of_pucks: int
|
number_of_pucks: int
|
||||||
number_of_samples: int
|
number_of_samples: int
|
||||||
return_address: List[Address]
|
return_address: List[Address]
|
||||||
@ -218,11 +218,25 @@ async def get_proposals():
|
|||||||
async def get_shipments():
|
async def get_shipments():
|
||||||
return shipments
|
return shipments
|
||||||
|
|
||||||
|
@app.delete("/shipments/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
async def delete_shipment(shipment_id: str):
|
||||||
|
global shipments # Use global variable to access the shipments list
|
||||||
|
shipments = [shipment for shipment in shipments if shipment.shipment_id != shipment_id]
|
||||||
|
|
||||||
|
|
||||||
@app.get("/dewars", response_model=List[Dewar])
|
@app.get("/dewars", response_model=List[Dewar])
|
||||||
async def get_dewars():
|
async def get_dewars():
|
||||||
return dewars
|
return dewars
|
||||||
|
|
||||||
|
@app.post("/dewars", response_model=List[Dewar], status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_dewar(shipment: Dewar):
|
||||||
|
dewar_id = f'SHIP-{uuid.uuid4().hex[:8].upper()}' # Generates a unique shipment ID
|
||||||
|
shipment.id = dewar_id # Set the generated ID on the shipment object
|
||||||
|
|
||||||
|
dewars.append(shipment) # Add the modified shipment object to the list
|
||||||
|
|
||||||
|
return dewars # Return the list of all dewars
|
||||||
|
|
||||||
|
|
||||||
# Endpoint to get the number of dewars in each shipment
|
# Endpoint to get the number of dewars in each shipment
|
||||||
@app.get("/shipment_dewars")
|
@app.get("/shipment_dewars")
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material';
|
import { Box, Typography, TextField, Button, Select, MenuItem, Snackbar } from '@mui/material';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import { Dewar, ContactPerson, Address } from '../types.ts';
|
import {ContactPerson, Address, Dewar} from "../../openapi";
|
||||||
|
|
||||||
|
|
||||||
interface DewarDetailsProps {
|
interface DewarDetailsProps {
|
||||||
dewar: Dewar | null;
|
dewar: Dewar | null;
|
||||||
@ -13,8 +14,8 @@ interface DewarDetailsProps {
|
|||||||
addNewContactPerson: (name: string) => void;
|
addNewContactPerson: (name: string) => void;
|
||||||
addNewReturnAddress: (address: string) => void;
|
addNewReturnAddress: (address: string) => void;
|
||||||
ready_date?: string;
|
ready_date?: string;
|
||||||
shipping_date?: string; // Make this optional
|
shipping_date?: string;
|
||||||
arrival_date?: string; // Make this optional
|
arrival_date?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DewarDetails: React.FC<DewarDetailsProps> = ({
|
const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||||
@ -36,10 +37,10 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (contactPersons.length > 0) {
|
if (contactPersons.length > 0) {
|
||||||
setSelectedContactPerson(contactPersons[0].name); // Default to the first contact person
|
setSelectedContactPerson(contactPersons[0].firstname); // Default to the first contact person
|
||||||
}
|
}
|
||||||
if (returnAddresses.length > 0) {
|
if (returnAddresses.length > 0) {
|
||||||
setSelectedReturnAddress(returnAddresses[0].address); // Default to the first return address
|
setSelectedReturnAddress(returnAddresses[0].return_address); // Default to the first return address
|
||||||
}
|
}
|
||||||
}, [contactPersons, returnAddresses]);
|
}, [contactPersons, returnAddresses]);
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
|||||||
>
|
>
|
||||||
<MenuItem value="" disabled>Select Contact Person</MenuItem>
|
<MenuItem value="" disabled>Select Contact Person</MenuItem>
|
||||||
{contactPersons.map((person) => (
|
{contactPersons.map((person) => (
|
||||||
<MenuItem key={person.id} value={person.name}>{person.name}</MenuItem>
|
<MenuItem key={person.id} value={person.firstname}>{person.lastname}</MenuItem>
|
||||||
))}
|
))}
|
||||||
<MenuItem value="add">Add New Contact Person</MenuItem>
|
<MenuItem value="add">Add New Contact Person</MenuItem>
|
||||||
</Select>
|
</Select>
|
@ -1,33 +1,22 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {Box, Typography, Button, Stack, TextField, Stepper, Step, StepLabel} from '@mui/material';
|
||||||
Box,
|
import DewarDetails from '../components/DewarDetails.tsx';
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
Stack,
|
|
||||||
TextField,
|
|
||||||
Stepper,
|
|
||||||
Step,
|
|
||||||
StepLabel,
|
|
||||||
} from '@mui/material';
|
|
||||||
import ShipmentForm from './ShipmentForm.tsx';
|
|
||||||
import DewarDetails from './DewarDetails.tsx';
|
|
||||||
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 bottleIcon from '../assets/icons/bottle-svgrepo-com-grey.svg';
|
import bottleIcon from '../assets/icons/bottle-svgrepo-com-grey.svg';
|
||||||
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 DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import {Contact} from "../../openapi"; // Import delete icon
|
import {ContactPerson, Dewar, Proposal, Address, Shipment_Input, DefaultService} from "../../openapi"; // Import delete icon
|
||||||
|
|
||||||
interface ShipmentDetailsProps {
|
interface ShipmentDetailsProps {
|
||||||
selectedShipment: Shipment | null;
|
selectedShipment: Shipment_Input | null;
|
||||||
setSelectedDewar: React.Dispatch<React.SetStateAction<Dewar | null>>;
|
setSelectedDewar: React.Dispatch<React.SetStateAction<Dewar | null>>;
|
||||||
isCreatingShipment: boolean;
|
isCreatingShipment: boolean;
|
||||||
newShipment: Shipment;
|
newShipment: Shipment_Input;
|
||||||
setNewShipment: React.Dispatch<React.SetStateAction<Shipment>>;
|
setNewShipment: React.Dispatch<React.SetStateAction<Shipment_Input>>;
|
||||||
handleSaveShipment: () => void;
|
handleSaveShipment: () => void;
|
||||||
contactPersons: Contact[];
|
contactPersons: ContactPerson[];
|
||||||
proposals: Proposal[];
|
proposals: Proposal[];
|
||||||
returnAddresses: Address[];
|
returnAddresses: Address[];
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
@ -36,12 +25,8 @@ interface ShipmentDetailsProps {
|
|||||||
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||||
selectedShipment,
|
selectedShipment,
|
||||||
setSelectedDewar,
|
setSelectedDewar,
|
||||||
isCreatingShipment,
|
|
||||||
newShipment,
|
|
||||||
setNewShipment,
|
setNewShipment,
|
||||||
handleSaveShipment,
|
|
||||||
contactPersons,
|
contactPersons,
|
||||||
proposals,
|
|
||||||
returnAddresses,
|
returnAddresses,
|
||||||
sx = {},
|
sx = {},
|
||||||
}) => {
|
}) => {
|
||||||
@ -55,29 +40,6 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
|
|
||||||
// Step titles based on your status
|
// Step titles based on your status
|
||||||
const steps = ['Ready for Shipping', 'Shipped', 'Arrived'];
|
const steps = ['Ready for Shipping', 'Shipped', 'Arrived'];
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (localSelectedDewar) {
|
|
||||||
setTrackingNumber(localSelectedDewar.tracking_number);
|
|
||||||
}
|
|
||||||
}, [localSelectedDewar]);
|
|
||||||
|
|
||||||
if (!selectedShipment) {
|
|
||||||
return isCreatingShipment ? (
|
|
||||||
<ShipmentForm
|
|
||||||
newShipment={newShipment}
|
|
||||||
setNewShipment={setNewShipment}
|
|
||||||
handleSaveShipment={handleSaveShipment}
|
|
||||||
contactPersons={contactPersons}
|
|
||||||
proposals={proposals}
|
|
||||||
returnAddresses={returnAddresses}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Typography>No shipment selected.</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total pucks and samples
|
|
||||||
const totalPucks = selectedShipment.dewars.reduce((acc, dewar) => acc + (dewar.number_of_pucks || 0), 0);
|
const totalPucks = selectedShipment.dewars.reduce((acc, dewar) => acc + (dewar.number_of_pucks || 0), 0);
|
||||||
const totalSamples = selectedShipment.dewars.reduce((acc, dewar) => acc + (dewar.number_of_samples || 0), 0);
|
const totalSamples = selectedShipment.dewars.reduce((acc, dewar) => acc + (dewar.number_of_samples || 0), 0);
|
||||||
|
|
||||||
@ -108,19 +70,59 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createDewar = async (newDewar: Partial<Dewar>, shipmentId: string) => {
|
||||||
|
console.log("Payload being sent to the API:", newDewar);
|
||||||
|
try {
|
||||||
|
const response = await DefaultService.createDewarDewarsPost(shipmentId, newDewar);
|
||||||
|
console.log("Response from API:", response);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error creating dewar:", error);
|
||||||
|
if (error.response) {
|
||||||
|
console.error("Validation error details:", error.response.data);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Handle adding a new dewar
|
// Handle adding a new dewar
|
||||||
const handleAddDewar = () => {
|
const handleAddDewar = async () => {
|
||||||
if (selectedShipment && newDewar.dewar_name) {
|
if (selectedShipment && newDewar.dewar_name) {
|
||||||
const updatedDewars = [
|
try {
|
||||||
...selectedShipment.dewars,
|
const newDewarToPost: Dewar = {
|
||||||
{ ...newDewar, tracking_number: newDewar.tracking_number || `TN-${Date.now()}` } as Dewar,
|
//id: `DEWAR${Date.now()}`,
|
||||||
];
|
dewar_name: newDewar.dewar_name.trim() || 'Unnamed Dewar',
|
||||||
setNewShipment({
|
number_of_pucks: newDewar.number_of_pucks ?? 0,
|
||||||
...selectedShipment,
|
number_of_samples: newDewar.number_of_samples ?? 0,
|
||||||
dewars: updatedDewars,
|
return_address: selectedShipment.return_address,
|
||||||
});
|
contact_person: selectedShipment.contact_person,
|
||||||
setIsAddingDewar(false);
|
status: 'In preparation',
|
||||||
setNewDewar({ dewar_name: '', number_of_pucks: 0, number_of_samples: 0, tracking_number: '' });
|
shippingStatus: 'not shipped',
|
||||||
|
arrivalStatus: 'not arrived',
|
||||||
|
qrcode: newDewar.qrcode || 'N/A',
|
||||||
|
//tracking_number: newDewar.tracking_number?.trim() || `TN-${Date.now()}`,
|
||||||
|
//ready_date: newDewar.ready_date || 'N/A',
|
||||||
|
//shipping_date: newDewar.shipping_date || 'N/A',
|
||||||
|
//arrival_date: newDewar.arrival_date || 'N/A',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Post to backend
|
||||||
|
const createdDewar = await createDewar(newDewarToPost, selectedShipment.id);
|
||||||
|
|
||||||
|
// Update state with the response from backend
|
||||||
|
setNewShipment(prev => ({
|
||||||
|
...prev,
|
||||||
|
dewars: [...prev.dewars, createdDewar],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Reset form fields
|
||||||
|
setIsAddingDewar(false);
|
||||||
|
//setNewDewar({ dewar_name: '', number_of_pucks: 0, number_of_samples: 0, tracking_number: '' });
|
||||||
|
} catch (error) {
|
||||||
|
alert("Failed to add dewar. Please try again.");
|
||||||
|
console.error("Error adding dewar:", error);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
alert('Please fill in the Dewar Name');
|
alert('Please fill in the Dewar Name');
|
||||||
}
|
}
|
||||||
@ -188,6 +190,18 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
|
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
|
||||||
|
|
||||||
|
{/* Iterate over contact persons if it's an array */}
|
||||||
|
{selectedShipment.contact_person && selectedShipment.contact_person.length > 0 ? (
|
||||||
|
selectedShipment.contact_person.map((person, index) => (
|
||||||
|
<Typography key={index} variant="body1">
|
||||||
|
Contact Person: {person.firstname} {person.lastname}
|
||||||
|
</Typography>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Typography variant="body1">No contact person assigned.</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>
|
||||||
<Typography variant="body1">Shipment Date: {selectedShipment.shipment_date}</Typography>
|
<Typography variant="body1">Shipment Date: {selectedShipment.shipment_date}</Typography>
|
@ -1,52 +1,61 @@
|
|||||||
import * as React from 'react';
|
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/system';
|
||||||
import {Dispatch, SetStateAction, useEffect, useState} from "react";
|
import { ContactPerson, Address, Proposal, DefaultService, OpenAPI, Shipment_Input } from "../../openapi";
|
||||||
import {ContactPerson, Address, Proposal, DefaultService, OpenAPI, Shipment_Input, type Dewar} from "../../openapi";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
interface ShipmentFormProps {
|
interface ShipmentFormProps {
|
||||||
newShipment: Shipment;
|
|
||||||
setNewShipment: Dispatch<SetStateAction<Shipment>>;
|
|
||||||
onShipmentCreated: () => void;
|
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||||
newShipment,
|
|
||||||
setNewShipment,
|
|
||||||
onShipmentCreated,
|
|
||||||
sx = {},
|
sx = {},
|
||||||
|
onCancel,
|
||||||
}) => {
|
}) => {
|
||||||
const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]);
|
const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]);
|
||||||
const [returnAddresses, setReturnAddresses] = React.useState<Address[]>([]);
|
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_number: '',
|
phone_number: '',
|
||||||
email: '',
|
email: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [newReturnAddress, setNewReturnAddress] = React.useState<Address>({
|
const [newReturnAddress, setNewReturnAddress] = React.useState<Address>({
|
||||||
street: '',
|
street: '',
|
||||||
city: '',
|
city: '',
|
||||||
zipcode: '',
|
zipcode: '',
|
||||||
country: '',
|
country: '',
|
||||||
});
|
});
|
||||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null); // For error handling
|
|
||||||
|
const [newShipment, setNewShipment] = React.useState<Shipment_Input>({
|
||||||
|
comments: undefined,
|
||||||
|
contact_person: [],
|
||||||
|
dewars: [],
|
||||||
|
proposal_number: [],
|
||||||
|
return_address: [],
|
||||||
|
shipment_date: "",
|
||||||
|
shipment_id: undefined,
|
||||||
|
shipment_name: "",
|
||||||
|
shipment_status: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
OpenAPI.BASE = 'http://127.0.0.1:8000';
|
OpenAPI.BASE = 'http://127.0.0.1:8000'; // Define Base URL
|
||||||
|
|
||||||
const getContacts = async () => {
|
const getContacts = async () => {
|
||||||
try {
|
try {
|
||||||
const c: ContactPerson[] = await DefaultService.getContactsContactsGet();
|
const c: ContactPerson[] = await DefaultService.getContactsContactsGet();
|
||||||
setContactPersons(c);
|
setContactPersons(c);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch contact persons:', err);
|
|
||||||
setErrorMessage('Failed to load contact persons. Please try again later.');
|
setErrorMessage('Failed to load contact persons. Please try again later.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -56,7 +65,6 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
const a: Address[] = await DefaultService.getReturnAddressesReturnAddressesGet();
|
const a: Address[] = await DefaultService.getReturnAddressesReturnAddressesGet();
|
||||||
setReturnAddresses(a);
|
setReturnAddresses(a);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch return addresses:', err);
|
|
||||||
setErrorMessage('Failed to load return addresses. Please try again later.');
|
setErrorMessage('Failed to load return addresses. Please try again later.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -66,7 +74,6 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
const p: Proposal[] = await DefaultService.getProposalsProposalsGet();
|
const p: Proposal[] = await DefaultService.getProposalsProposalsGet();
|
||||||
setProposals(p);
|
setProposals(p);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching proposals:', err);
|
|
||||||
setErrorMessage('Failed to load proposals. Please try again later.');
|
setErrorMessage('Failed to load proposals. Please try again later.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -76,65 +83,95 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
getProposals();
|
getProposals();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const validateEmail = (email: string) => /\S+@\S+\.\S+/.test(email);
|
||||||
|
const validatePhoneNumber = (phone: string) => /^\+?[1-9]\d{1,14}$/.test(phone);
|
||||||
|
const validateZipCode = (zipcode: string) => /^\d{5}(?:[-\s]\d{4})?$/.test(zipcode);
|
||||||
|
|
||||||
|
const isContactFormValid = () => {
|
||||||
|
const { firstName, lastName, phone_number, email } = newContactPerson;
|
||||||
|
|
||||||
|
if (isCreatingContactPerson) {
|
||||||
|
if (!firstName || !lastName || !validateEmail(email) || !validatePhoneNumber(phone_number)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAddressFormValid = () => {
|
||||||
|
const { street, city, zipcode, country } = newReturnAddress;
|
||||||
|
|
||||||
|
if (isCreatingReturnAddress) {
|
||||||
|
if (!street || !city || !validateZipCode(zipcode) || !country) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFormValid = () => {
|
||||||
|
const { shipment_name, proposal_number, contact_person, return_address } = newShipment;
|
||||||
|
|
||||||
|
if (!shipment_name || !proposal_number.length) return false;
|
||||||
|
if (!contact_person.length || !return_address.length) return false;
|
||||||
|
|
||||||
|
if (isCreatingContactPerson && !isContactFormValid()) return false;
|
||||||
|
if (isCreatingReturnAddress && !isAddressFormValid()) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setNewShipment((prev) => ({ ...prev, [name]: value }));
|
||||||
|
if (name === 'email') {
|
||||||
|
setNewContactPerson((prev) => ({ ...prev, email: value }));
|
||||||
|
}
|
||||||
|
if (name === 'phone_number') {
|
||||||
|
setNewContactPerson((prev) => ({ ...prev, phone_number: value }));
|
||||||
|
}
|
||||||
|
if (name === 'zipcode') {
|
||||||
|
setNewReturnAddress((prev) => ({ ...prev, zipcode: value }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSaveShipment = async () => {
|
const handleSaveShipment = async () => {
|
||||||
// Set the base URL for the OpenAPI requests
|
if (!isFormValid()) {
|
||||||
|
setErrorMessage('Please fill in all mandatory fields correctly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OpenAPI.BASE = 'http://127.0.0.1:8000';
|
OpenAPI.BASE = 'http://127.0.0.1:8000';
|
||||||
|
|
||||||
// Create the payload for the shipment
|
|
||||||
const payload: Shipment_Input = {
|
const payload: Shipment_Input = {
|
||||||
shipment_name: newShipment.shipment_name,
|
shipment_name: newShipment.shipment_name || '',
|
||||||
shipment_date: new Date().toISOString().split('T')[0], // YYYY-MM-DD
|
shipment_date: new Date().toISOString().split('T')[0],
|
||||||
shipment_status: 'In preparation',
|
shipment_status: 'In preparation',
|
||||||
contact_person: newShipment.contact_person.map(person => ({
|
contact_person: newShipment.contact_person ? newShipment.contact_person.map(person => ({
|
||||||
firstname: person.firstname,
|
firstname: person.firstname,
|
||||||
lastname: person.lastname,
|
lastname: person.lastname,
|
||||||
phone_number: person.phone_number,
|
phone_number: person.phone_number,
|
||||||
email: person.email
|
email: person.email
|
||||||
})),
|
})) : [],
|
||||||
proposal_number: [
|
proposal_number: newShipment.proposal_number ? [{
|
||||||
{
|
id: 1,
|
||||||
id: 1, // Use the correct ID from your context
|
number: newShipment.proposal_number
|
||||||
number: newShipment.proposal_number || "Default Proposal Number" // Make sure it's a valid string
|
}] : [],
|
||||||
}
|
return_address: newShipment.return_address ? newShipment.return_address.map(address => ({
|
||||||
],
|
|
||||||
return_address: newShipment.return_address.map(address => ({
|
|
||||||
street: address.street,
|
street: address.street,
|
||||||
city: address.city,
|
city: address.city,
|
||||||
zipcode: address.zipcode,
|
zipcode: address.zipcode,
|
||||||
country: address.country
|
country: address.country
|
||||||
})),
|
})) : [],
|
||||||
comments: newShipment.comments || '', // Set to an empty string if null
|
comments: newShipment.comments || '',
|
||||||
dewars: [] // Assuming you want this to be an empty array
|
dewars: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// Print the payload to the console for debugging
|
|
||||||
console.log('Request Payload:', JSON.stringify(payload, null, 2));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create a shipment using the API
|
await DefaultService.createShipmentShipmentsPost(payload);
|
||||||
const s: Shipment_Input[] = await DefaultService.createShipmentShipmentsPost(payload);
|
setErrorMessage(null);
|
||||||
setShipments(s);
|
// Handle successful save action
|
||||||
|
onCancel(); // close the form after saving
|
||||||
if (onShipmentCreated) {
|
} catch (error) {
|
||||||
onShipmentCreated(s[0]); // Call the function if it is defined
|
setErrorMessage('Failed to save shipment. Please try again.');
|
||||||
} 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.');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -145,9 +182,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
setNewShipment({ ...newShipment, contact_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);
|
||||||
if (selectedPerson) {
|
if (selectedPerson) {
|
||||||
setNewShipment({ ...newShipment, contact_person: [selectedPerson] });
|
setNewShipment({ ...newShipment, contact_person: [{ ...selectedPerson }] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -161,7 +198,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
setIsCreatingReturnAddress(false);
|
setIsCreatingReturnAddress(false);
|
||||||
const selectedAddress = returnAddresses.find((address) => address.city === value);
|
const selectedAddress = returnAddresses.find((address) => address.city === value);
|
||||||
if (selectedAddress) {
|
if (selectedAddress) {
|
||||||
setNewShipment({ ...newShipment, return_address: [selectedAddress] });
|
setNewShipment({ ...newShipment, return_address: [{ ...selectedAddress }] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -171,12 +208,13 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
setNewShipment({ ...newShipment, proposal_number: selectedProposal });
|
setNewShipment({ ...newShipment, proposal_number: selectedProposal });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const { name, value } = e.target;
|
|
||||||
setNewShipment((prev) => ({ ...prev, [name]: value }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveNewContactPerson = async () => {
|
const handleSaveNewContactPerson = async () => {
|
||||||
|
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) ||
|
||||||
|
!newContactPerson.firstName || !newContactPerson.lastName) {
|
||||||
|
setErrorMessage('Please fill in all new contact person fields correctly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
firstname: newContactPerson.firstName,
|
firstname: newContactPerson.firstName,
|
||||||
lastname: newContactPerson.lastName,
|
lastname: newContactPerson.lastName,
|
||||||
@ -189,7 +227,6 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
setContactPersons([...contactPersons, c]);
|
setContactPersons([...contactPersons, c]);
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to create a new contact person:', err);
|
|
||||||
setErrorMessage('Failed to create a new contact person. Please try again later.');
|
setErrorMessage('Failed to create a new contact person. Please try again later.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +235,12 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveNewReturnAddress = async () => {
|
const handleSaveNewReturnAddress = async () => {
|
||||||
|
if (!validateZipCode(newReturnAddress.zipcode) || !newReturnAddress.street || !newReturnAddress.city ||
|
||||||
|
!newReturnAddress.country) {
|
||||||
|
setErrorMessage('Please fill in all new return address fields correctly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
street: newReturnAddress.street.trim(),
|
street: newReturnAddress.street.trim(),
|
||||||
city: newReturnAddress.city.trim(),
|
city: newReturnAddress.city.trim(),
|
||||||
@ -205,17 +248,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]);
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to create a new return address:', err);
|
|
||||||
setErrorMessage('Failed to create a new return address. Please try again later.');
|
setErrorMessage('Failed to create a new return address. Please try again later.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,8 +282,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newShipment.shipment_name || ''}
|
value={newShipment.shipment_name || ''}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Contact Person</InputLabel>
|
<InputLabel>Contact Person</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={newShipment.contact_person?.[0]?.lastname || ''}
|
value={newShipment.contact_person?.[0]?.lastname || ''}
|
||||||
@ -271,6 +309,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newContactPerson.firstName}
|
value={newContactPerson.firstName}
|
||||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
|
onChange={(e) => setNewContactPerson({ ...newContactPerson, firstName: e.target.value })}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Last Name"
|
label="Last Name"
|
||||||
@ -278,27 +317,45 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newContactPerson.lastName}
|
value={newContactPerson.lastName}
|
||||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
|
onChange={(e) => setNewContactPerson({ ...newContactPerson, lastName: e.target.value })}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Phone"
|
label="Phone"
|
||||||
name="phone"
|
name="phone_number"
|
||||||
|
type="tel"
|
||||||
value={newContactPerson.phone_number}
|
value={newContactPerson.phone_number}
|
||||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
|
onChange={(e) => {
|
||||||
|
setNewContactPerson({ ...newContactPerson, phone_number: e.target.value });
|
||||||
|
handleChange(e);
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
|
error={!validatePhoneNumber(newContactPerson.phone_number)}
|
||||||
|
helperText={!validatePhoneNumber(newContactPerson.phone_number) ? "Invalid phone number" : ""}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Email"
|
label="Email"
|
||||||
name="email"
|
name="email"
|
||||||
|
type="email"
|
||||||
value={newContactPerson.email}
|
value={newContactPerson.email}
|
||||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
|
onChange={(e) => {
|
||||||
|
setNewContactPerson({ ...newContactPerson, email: e.target.value });
|
||||||
|
handleChange(e);
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
|
error={!validateEmail(newContactPerson.email)}
|
||||||
|
helperText={!validateEmail(newContactPerson.email) ? "Invalid email" : ""}
|
||||||
/>
|
/>
|
||||||
<Button variant="contained" color="primary" onClick={handleSaveNewContactPerson}>
|
<Button variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSaveNewContactPerson}
|
||||||
|
disabled={!isContactFormValid()}>
|
||||||
Save New Contact Person
|
Save New Contact Person
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Proposal Number</InputLabel>
|
<InputLabel>Proposal Number</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={newShipment.proposal_number || ''}
|
value={newShipment.proposal_number || ''}
|
||||||
@ -312,7 +369,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Return Address</InputLabel>
|
<InputLabel>Return Address</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={newShipment.return_address?.[0]?.city || ''}
|
value={newShipment.return_address?.[0]?.city || ''}
|
||||||
@ -337,6 +394,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newReturnAddress.street}
|
value={newReturnAddress.street}
|
||||||
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="City"
|
label="City"
|
||||||
@ -344,13 +402,20 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newReturnAddress.city}
|
value={newReturnAddress.city}
|
||||||
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Zip Code"
|
label="Zip Code"
|
||||||
name="zipcode"
|
name="zipcode"
|
||||||
value={newReturnAddress.zipcode}
|
value={newReturnAddress.zipcode}
|
||||||
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
|
onChange={(e) => {
|
||||||
|
setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value });
|
||||||
|
handleChange(e);
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
|
error={!validateZipCode(newReturnAddress.zipcode)}
|
||||||
|
helperText={!validateZipCode(newReturnAddress.zipcode) ? "Invalid zip code" : ""}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Country"
|
label="Country"
|
||||||
@ -358,8 +423,12 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newReturnAddress.country}
|
value={newReturnAddress.country}
|
||||||
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<Button variant="contained" color="primary" onClick={handleSaveNewReturnAddress}>
|
<Button variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSaveNewReturnAddress}
|
||||||
|
disabled={!isAddressFormValid()}>
|
||||||
Save New Return Address
|
Save New Return Address
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
@ -373,14 +442,17 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|||||||
value={newShipment.comments || ''}
|
value={newShipment.comments || ''}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Stack direction="row" spacing={2} justifyContent="flex-end">
|
||||||
variant="contained"
|
<Button variant="outlined" color="error" onClick={onCancel}>Cancel</Button>
|
||||||
color="primary"
|
<Button
|
||||||
onClick={handleSaveShipment}
|
variant="contained"
|
||||||
sx={{ alignSelf: 'flex-end' }}
|
color="primary"
|
||||||
>
|
onClick={handleSaveShipment}
|
||||||
Save Shipment
|
disabled={!isFormValid()}
|
||||||
</Button>
|
>
|
||||||
|
Save Shipment
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -1,98 +1,111 @@
|
|||||||
|
// ShipmentPanel.tsx
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useEffect, useState, Dispatch, SetStateAction } from 'react';
|
import { useEffect, useState } 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 { Add as AddIcon, Delete as DeleteIcon, UploadFile as UploadFileIcon, Refresh as RefreshIcon } from '@mui/icons-material';
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
|
||||||
import UploadFileIcon from '@mui/icons-material/UploadFile';
|
|
||||||
import UploadDialog from './UploadDialog'; // Ensure the file extension is correct
|
import UploadDialog from './UploadDialog'; // Ensure the file extension is correct
|
||||||
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 {
|
||||||
|
setCreatingShipment: (value: boolean) => void;
|
||||||
selectedPage?: string;
|
selectedPage?: string;
|
||||||
setIsCreatingShipment: Dispatch<SetStateAction<boolean>>;
|
|
||||||
selectShipment: (shipment: Shipment_Input | null) => void;
|
selectShipment: (shipment: Shipment_Input | null) => void;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
const API_BASE_URL = 'http://127.0.0.1:8000';
|
||||||
setIsCreatingShipment,
|
OpenAPI.BASE = API_BASE_URL;
|
||||||
selectShipment,
|
|
||||||
sx,
|
const statusIconMap: Record<string, string> = {
|
||||||
}) => {
|
'In Transit': bottleYellow,
|
||||||
|
'Delivered': bottleGreen,
|
||||||
|
'Pending': bottleGrey,
|
||||||
|
'Unknown': bottleRed,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShipmentPanel: React.FC<ShipmentPanelProps> = ({ setCreatingShipment, selectShipment, sx }) => {
|
||||||
const [shipments, setShipments] = useState<Shipment_Input[]>([]);
|
const [shipments, setShipments] = useState<Shipment_Input[]>([]);
|
||||||
const [selectedShipment, setSelectedShipment] = useState<Shipment_Input | 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 fetchShipments = async () => {
|
useEffect(() => {
|
||||||
console.log('trying to fetch some shipments');
|
fetchAndSetShipments();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchAndSetShipments = async () => {
|
||||||
try {
|
try {
|
||||||
const s: Shipment_Input[] = await DefaultService.getShipmentsShipmentsGet();
|
const shipmentsData: Shipment_Input[] = await DefaultService.getShipmentsShipmentsGet();
|
||||||
setShipments(s);
|
shipmentsData.sort((a, b) => new Date(b.shipment_date).getTime() - new Date(a.shipment_date).getTime());
|
||||||
|
setShipments(shipmentsData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch shipments:', error);
|
setError('Failed to fetch shipments. Please try again later.');
|
||||||
setError("Failed to fetch shipments. Please try again later.");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const handleDeleteShipment = async () => {
|
||||||
OpenAPI.BASE = 'http://127.0.0.1:8000';
|
|
||||||
fetchShipments();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const statusIconMap: Record<string, string> = {
|
|
||||||
"In Transit": bottleYellow,
|
|
||||||
"Delivered": bottleGreen,
|
|
||||||
"Pending": bottleGrey,
|
|
||||||
"Unknown": bottleRed,
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleShipmentSelection = (shipment: Shipment_Input) => {
|
|
||||||
const isCurrentlySelected = selectedShipment?.shipment_id === shipment.shipment_id;
|
|
||||||
setSelectedShipment(isCurrentlySelected ? null : shipment);
|
|
||||||
selectShipment(isCurrentlySelected ? null : shipment);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteShipment = () => {
|
|
||||||
if (selectedShipment) {
|
if (selectedShipment) {
|
||||||
const confirmed = window.confirm(`Are you sure you want to delete the shipment: ${selectedShipment.shipment_name}?`);
|
const confirmed = window.confirm(`Are you sure you want to delete the shipment: ${selectedShipment.shipment_name}?`);
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
const updatedShipments = shipments.filter(shipment => shipment.shipment_id !== selectedShipment.shipment_id);
|
await deleteShipment(selectedShipment.shipment_id);
|
||||||
setShipments(updatedShipments);
|
|
||||||
setSelectedShipment(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenUploadDialog = () => {
|
const deleteShipment = async (shipmentId: string | undefined) => {
|
||||||
setUploadDialogOpen(true);
|
if (!shipmentId) return;
|
||||||
|
try {
|
||||||
|
await DefaultService.deleteShipmentShipmentsShipmentIdDelete(shipmentId);
|
||||||
|
setShipments(shipments.filter(shipment => shipment.shipment_id !== shipmentId));
|
||||||
|
setSelectedShipment(null);
|
||||||
|
setError(null);
|
||||||
|
} catch (error) {
|
||||||
|
setError('Failed to delete shipment. Please try again later.');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseUploadDialog = () => {
|
const handleShipmentSelection = (shipment: Shipment_Input) => {
|
||||||
setUploadDialogOpen(false);
|
const isSelected = selectedShipment?.shipment_id === shipment.shipment_id;
|
||||||
|
const updatedShipment = isSelected ? null : shipment;
|
||||||
|
setSelectedShipment(updatedShipment);
|
||||||
|
selectShipment(updatedShipment);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openUploadDialog = () => setUploadDialogOpen(true);
|
||||||
|
const closeUploadDialog = () => setUploadDialogOpen(false);
|
||||||
|
const refreshShipments = () => fetchAndSetShipments();
|
||||||
|
|
||||||
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>}
|
||||||
<Typography variant="h6" sx={{ marginBottom: 2, fontWeight: 'bold' }}>
|
<Typography variant="h6" sx={{ marginBottom: 2, fontWeight: 'bold' }}>
|
||||||
Shipments
|
Shipments
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button
|
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: 2 }}>
|
||||||
variant="contained"
|
<Button
|
||||||
color="primary"
|
variant="contained"
|
||||||
startIcon={<AddIcon />}
|
color="primary"
|
||||||
onClick={() => setIsCreatingShipment(true)}
|
startIcon={<AddIcon />}
|
||||||
sx={{ marginBottom: 2, padding: '10px 16px' }}
|
onClick={() => setCreatingShipment(true)}
|
||||||
>
|
sx={{ padding: '10px 16px' }}
|
||||||
Create Shipment
|
>
|
||||||
</Button>
|
Create Shipment
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
onClick={refreshShipments}
|
||||||
|
color="primary"
|
||||||
|
title="Refresh Shipments"
|
||||||
|
sx={{ marginLeft: 1 }}
|
||||||
|
>
|
||||||
|
<RefreshIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
{shipments.map((shipment) => (
|
{shipments.map((shipment) => (
|
||||||
<Button
|
<Button
|
||||||
key={shipment.shipment_id}
|
key={shipment.shipment_id}
|
||||||
@ -151,7 +164,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleOpenUploadDialog}
|
onClick={openUploadDialog}
|
||||||
color="primary"
|
color="primary"
|
||||||
title="Upload Sample Data Sheet"
|
title="Upload Sample Data Sheet"
|
||||||
sx={{ marginLeft: 1 }}
|
sx={{ marginLeft: 1 }}
|
||||||
@ -173,7 +186,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
|||||||
))}
|
))}
|
||||||
<UploadDialog
|
<UploadDialog
|
||||||
open={uploadDialogOpen}
|
open={uploadDialogOpen}
|
||||||
onClose={handleCloseUploadDialog}
|
onClose={closeUploadDialog}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import DewarDetails from './DewarDetails.tsx';
|
|
||||||
import { Shipment, ContactPerson, Address, Dewar } from '../types.ts';
|
|
||||||
import shipmentData from '../../public/shipmentsdb.json';
|
|
||||||
import {Contact, DefaultService, OpenAPI} from "../../openapi";
|
|
||||||
|
|
||||||
const ParentComponent = () => {
|
|
||||||
const [dewars, setDewars] = useState<Dewar[]>([]);
|
|
||||||
const [contactPersons, setContactPersons] = useState<Contact[]>([]);
|
|
||||||
const [returnAddresses, setReturnAddresses] = useState<Address[]>([]);
|
|
||||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
OpenAPI.BASE='http://127.0.0.1:8000'
|
|
||||||
|
|
||||||
const firstShipment = shipmentData.shipments[0] as Shipment; // Ensure proper typing
|
|
||||||
setSelectedShipment(firstShipment);
|
|
||||||
console.log('this is meeee');
|
|
||||||
DefaultService.getContactsContactsGet().then((c : Contact[]) => {
|
|
||||||
setContactPersons(c);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (firstShipment.return_address) {
|
|
||||||
setReturnAddresses(firstShipment.return_address); // Set to array directly
|
|
||||||
}
|
|
||||||
|
|
||||||
const dewarsWithId = firstShipment.dewars.map((dewar, index) => ({
|
|
||||||
...dewar,
|
|
||||||
id: `${firstShipment.shipment_id}-Dewar-${index + 1}`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setDewars(dewarsWithId);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const addNewContactPerson = (name: string) => {
|
|
||||||
const newContact: ContactPerson = { id: `${contactPersons.length + 1}`, name };
|
|
||||||
setContactPersons((prev) => [...prev, newContact]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addNewReturnAddress = (address: string) => {
|
|
||||||
const newAddress: Address = { id: `${returnAddresses.length + 1}`, address };
|
|
||||||
setReturnAddresses((prev) => [...prev, newAddress]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectedDewar = dewars[0]; // Just picking the first dewar for demonstration
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DewarDetails
|
|
||||||
dewar={selectedDewar}
|
|
||||||
trackingNumber={''}
|
|
||||||
setTrackingNumber={() => {}}
|
|
||||||
onGenerateQRCode={() => {}}
|
|
||||||
contactPersons={contactPersons}
|
|
||||||
returnAddresses={returnAddresses}
|
|
||||||
addNewContactPerson={addNewContactPerson}
|
|
||||||
addNewReturnAddress={addNewReturnAddress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ParentComponent;
|
|
@ -1,157 +0,0 @@
|
|||||||
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,83 +1,41 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Grid } from '@mui/material';
|
import Grid2 from '@mui/material/Grid2';
|
||||||
import ShipmentPanel from '../components/ShipmentPanel.tsx';
|
import ShipmentPanel from '../components/ShipmentPanel';
|
||||||
// import ShipmentDetails from '../components/ShipmentDetails.tsx'; // Commented out for now
|
import ShipmentDetails from '../components/ShipmentDetails';
|
||||||
import ShipmentForm from '../components/ShipmentForm.tsx';
|
import ShipmentForm from '../components/ShipmentForm';
|
||||||
import { ContactPerson, Address, Proposal, Dewar, DefaultService, Shipment_Input } from '../../openapi'; // Import your API service
|
import { Shipment_Input } from '../../openapi';
|
||||||
|
|
||||||
const ShipmentView: React.FC = () => {
|
type ShipmentViewProps = {};
|
||||||
// State for managing shipments
|
|
||||||
const [shipments, setShipments] = useState<Shipment_Input[]>([]);
|
const ShipmentView: React.FC<ShipmentViewProps> = () => {
|
||||||
const [newShipment, setNewShipment] = useState<Shipment_Input>({
|
|
||||||
shipment_name: '',
|
|
||||||
contact_person: [],
|
|
||||||
proposal_number: '',
|
|
||||||
return_address: [],
|
|
||||||
comments: '',
|
|
||||||
dewars: [],
|
|
||||||
});
|
|
||||||
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||||
const [selectedShipment, setSelectedShipment] = useState<Shipment_Input | null>(null);
|
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[]>([]);
|
|
||||||
|
|
||||||
// Fetch initial shipments data
|
const handleSelectShipment = (shipment: Shipment_Input | null) => {
|
||||||
const fetchShipments = async () => {
|
|
||||||
try {
|
|
||||||
const fetchedShipments = await DefaultService.getShipmentsShipmentsGet();
|
|
||||||
setShipments(fetchedShipments);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to fetch shipments:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchShipments();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 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);
|
setSelectedShipment(shipment);
|
||||||
|
setIsCreatingShipment(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelShipmentForm = () => {
|
||||||
|
setIsCreatingShipment(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderShipmentContent = () => {
|
||||||
|
if (isCreatingShipment) {
|
||||||
|
return <ShipmentForm sx={{ flexGrow: 1 }} onCancel={handleCancelShipmentForm} />;
|
||||||
|
}
|
||||||
|
if (selectedShipment) {
|
||||||
|
return <ShipmentDetails isCreatingShipment={isCreatingShipment} sx={{ flexGrow: 1 }} selectedShipment={selectedShipment} />;
|
||||||
|
}
|
||||||
|
return <div>No shipment details available.</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={2} sx={{ height: '100vh' }}>
|
<Grid2 container spacing={2} sx={{ height: '100vh' }}>
|
||||||
{/* Left column: ShipmentPanel */}
|
<Grid2
|
||||||
<Grid
|
|
||||||
item
|
item
|
||||||
xs={12}
|
xs={12}
|
||||||
md={3}
|
md={4}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
@ -86,53 +44,23 @@ const ShipmentView: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<ShipmentPanel
|
<ShipmentPanel
|
||||||
selectedPage="Shipments"
|
selectedPage="Shipments"
|
||||||
setIsCreatingShipment={setIsCreatingShipment}
|
setCreatingShipment={setIsCreatingShipment}
|
||||||
fetchShipments={fetchShipments}
|
selectShipment={handleSelectShipment}
|
||||||
shipments={shipments} // Pass down the updated shipments
|
|
||||||
selectShipment={selectShipment}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid2>
|
||||||
|
<Grid2
|
||||||
{/* Right column: ShipmentForm or ShipmentDetails */}
|
|
||||||
<Grid
|
|
||||||
item
|
item
|
||||||
xs={12}
|
xs={12}
|
||||||
md={9}
|
md={8}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isCreatingShipment ? (
|
{renderShipmentContent()}
|
||||||
<ShipmentForm
|
</Grid2>
|
||||||
newShipment={newShipment}
|
</Grid2>
|
||||||
setNewShipment={setNewShipment}
|
|
||||||
handleSaveShipment={handleSaveShipment}
|
|
||||||
onShipmentCreated={onShipmentCreated} // Pass this prop
|
|
||||||
contactPersons={contactPersons}
|
|
||||||
proposals={proposals}
|
|
||||||
returnAddresses={returnAddresses}
|
|
||||||
sx={{ flexGrow: 1 }} // Allow form to grow and take available space
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
// 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
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user