390 lines
16 KiB
TypeScript
390 lines
16 KiB
TypeScript
import * as React from 'react';
|
|
import { Box, Button, TextField, Typography, Select, MenuItem, Stack, FormControl, InputLabel } from '@mui/material';
|
|
import { SelectChangeEvent } from '@mui/material';
|
|
import { SxProps } from '@mui/material';
|
|
import {Dispatch, SetStateAction, useEffect, useState} from "react";
|
|
import {ContactPerson, Address, Proposal, DefaultService, OpenAPI, Shipment_Input, type Dewar} from "../../openapi";
|
|
|
|
interface ShipmentFormProps {
|
|
newShipment: Shipment;
|
|
setNewShipment: Dispatch<SetStateAction<Shipment>>;
|
|
onShipmentCreated: () => void;
|
|
sx?: SxProps;
|
|
}
|
|
|
|
const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
|
newShipment,
|
|
setNewShipment,
|
|
onShipmentCreated,
|
|
sx = {},
|
|
}) => {
|
|
const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]);
|
|
const [returnAddresses, setReturnAddresses] = React.useState<Address[]>([]);
|
|
const [proposals, setProposals] = React.useState<Proposal[]>([]);
|
|
const [shipments, setShipments] = useState([]);
|
|
const [isCreatingContactPerson, setIsCreatingContactPerson] = React.useState(false);
|
|
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = React.useState(false);
|
|
const [newContactPerson, setNewContactPerson] = React.useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
phone_number: '',
|
|
email: '',
|
|
});
|
|
const [newReturnAddress, setNewReturnAddress] = React.useState<Address>({
|
|
street: '',
|
|
city: '',
|
|
zipcode: '',
|
|
country: '',
|
|
});
|
|
const [errorMessage, setErrorMessage] = React.useState<string | null>(null); // For error handling
|
|
|
|
useEffect(() => {
|
|
OpenAPI.BASE = 'http://127.0.0.1:8000';
|
|
|
|
const getContacts = async () => {
|
|
try {
|
|
const c: ContactPerson[] = await DefaultService.getContactsContactsGet();
|
|
setContactPersons(c);
|
|
} catch (err) {
|
|
console.error('Failed to fetch contact persons:', err);
|
|
setErrorMessage('Failed to load contact persons. Please try again later.');
|
|
}
|
|
};
|
|
|
|
const getAddresses = async () => {
|
|
try {
|
|
const a: Address[] = await DefaultService.getReturnAddressesReturnAddressesGet();
|
|
setReturnAddresses(a);
|
|
} catch (err) {
|
|
console.error('Failed to fetch return addresses:', err);
|
|
setErrorMessage('Failed to load return addresses. Please try again later.');
|
|
}
|
|
};
|
|
|
|
const getProposals = async () => {
|
|
try {
|
|
const p: Proposal[] = await DefaultService.getProposalsProposalsGet();
|
|
setProposals(p);
|
|
} catch (err) {
|
|
console.error('Error fetching proposals:', err);
|
|
setErrorMessage('Failed to load proposals. Please try again later.');
|
|
}
|
|
};
|
|
|
|
getContacts();
|
|
getAddresses();
|
|
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 value = event.target.value;
|
|
if (value === 'new') {
|
|
setIsCreatingContactPerson(true);
|
|
setNewShipment({ ...newShipment, contact_person: [] });
|
|
} else {
|
|
setIsCreatingContactPerson(false);
|
|
const selectedPerson = contactPersons.find((person) => person.lastname === value) || null;
|
|
if (selectedPerson) {
|
|
setNewShipment({ ...newShipment, contact_person: [selectedPerson] });
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleReturnAddressChange = (event: SelectChangeEvent) => {
|
|
const value = event.target.value;
|
|
if (value === 'new') {
|
|
setIsCreatingReturnAddress(true);
|
|
setNewShipment({ ...newShipment, return_address: [] });
|
|
} else {
|
|
setIsCreatingReturnAddress(false);
|
|
const selectedAddress = returnAddresses.find((address) => address.city === value);
|
|
if (selectedAddress) {
|
|
setNewShipment({ ...newShipment, return_address: [selectedAddress] });
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleProposalChange = (event: SelectChangeEvent) => {
|
|
const selectedProposal = event.target.value;
|
|
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 payload = {
|
|
firstname: newContactPerson.firstName,
|
|
lastname: newContactPerson.lastName,
|
|
phone_number: newContactPerson.phone_number,
|
|
email: newContactPerson.email,
|
|
};
|
|
|
|
try {
|
|
const c: ContactPerson = await DefaultService.createContactContactsPost(payload);
|
|
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 = {
|
|
street: newReturnAddress.street.trim(),
|
|
city: newReturnAddress.city.trim(),
|
|
zipcode: newReturnAddress.zipcode.trim(),
|
|
country: newReturnAddress.country.trim(),
|
|
};
|
|
|
|
if (!payload.street || !payload.city || !payload.zipcode || !payload.country) {
|
|
setErrorMessage('All fields are required.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const a: Address = await DefaultService.createReturnAddressReturnAddressesPost(payload);
|
|
setReturnAddresses([...returnAddresses, a]);
|
|
setErrorMessage(null);
|
|
} catch (err) {
|
|
console.error('Failed to create a new return address:', err);
|
|
setErrorMessage('Failed to create a new return address. Please try again later.');
|
|
}
|
|
|
|
setNewReturnAddress({ street: '', city: '', zipcode: '', country: '' });
|
|
setIsCreatingReturnAddress(false);
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
padding: 4,
|
|
border: '1px solid #ccc',
|
|
borderRadius: '4px',
|
|
marginBottom: 2,
|
|
maxWidth: '600px',
|
|
...sx,
|
|
}}
|
|
>
|
|
<Typography variant="h6" sx={{ marginBottom: 2 }}>
|
|
Create Shipment
|
|
</Typography>
|
|
{errorMessage && <Typography color="error">{errorMessage}</Typography>}
|
|
<Stack spacing={2}>
|
|
<TextField
|
|
label="Shipment Name"
|
|
name="shipment_name"
|
|
value={newShipment.shipment_name || ''}
|
|
onChange={handleChange}
|
|
fullWidth
|
|
/>
|
|
<FormControl fullWidth>
|
|
<InputLabel>Contact Person</InputLabel>
|
|
<Select
|
|
value={newShipment.contact_person?.[0]?.lastname || ''}
|
|
onChange={handleContactPersonChange}
|
|
displayEmpty
|
|
>
|
|
{contactPersons.map((person) => (
|
|
<MenuItem key={person.lastname + person.firstname} value={person.lastname}>
|
|
{`${person.lastname}, ${person.firstname}`}
|
|
</MenuItem>
|
|
))}
|
|
<MenuItem value="new">
|
|
<em>Create New Contact Person</em>
|
|
</MenuItem>
|
|
</Select>
|
|
</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>
|
|
<InputLabel>Proposal Number</InputLabel>
|
|
<Select
|
|
value={newShipment.proposal_number || ''}
|
|
onChange={handleProposalChange}
|
|
displayEmpty
|
|
>
|
|
{proposals.map((proposal) => (
|
|
<MenuItem key={proposal.id} value={proposal.number}>
|
|
{proposal.number}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
<FormControl fullWidth>
|
|
<InputLabel>Return Address</InputLabel>
|
|
<Select
|
|
value={newShipment.return_address?.[0]?.city || ''}
|
|
onChange={handleReturnAddressChange}
|
|
displayEmpty
|
|
>
|
|
{returnAddresses.map((address) => (
|
|
<MenuItem key={address.city} value={address.city}>
|
|
{`${address.street}, ${address.city}, ${address.zipcode}, ${address.country}`}
|
|
</MenuItem>
|
|
))}
|
|
<MenuItem value="new">
|
|
<em>Create New Return Address</em>
|
|
</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
{isCreatingReturnAddress && (
|
|
<>
|
|
<TextField
|
|
label="Street"
|
|
name="street"
|
|
value={newReturnAddress.street}
|
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, street: e.target.value })}
|
|
fullWidth
|
|
/>
|
|
<TextField
|
|
label="City"
|
|
name="city"
|
|
value={newReturnAddress.city}
|
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, city: e.target.value })}
|
|
fullWidth
|
|
/>
|
|
<TextField
|
|
label="Zip Code"
|
|
name="zipcode"
|
|
value={newReturnAddress.zipcode}
|
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, zipcode: e.target.value })}
|
|
fullWidth
|
|
/>
|
|
<TextField
|
|
label="Country"
|
|
name="country"
|
|
value={newReturnAddress.country}
|
|
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
|
|
fullWidth
|
|
/>
|
|
<Button variant="contained" color="primary" onClick={handleSaveNewReturnAddress}>
|
|
Save New Return Address
|
|
</Button>
|
|
</>
|
|
)}
|
|
<TextField
|
|
label="Comments"
|
|
name="comments"
|
|
fullWidth
|
|
multiline
|
|
rows={4}
|
|
value={newShipment.comments || ''}
|
|
onChange={handleChange}
|
|
/>
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={handleSaveShipment}
|
|
sx={{ alignSelf: 'flex-end' }}
|
|
>
|
|
Save Shipment
|
|
</Button>
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default ShipmentForm;
|