Refactor contact handling across backend and frontend
Replaced usage of "ContactPerson" with "Contact" for consistency across the codebase. Updated related component props, state variables, API calls, and database queries to align with the new model. Also enhanced backend functionality with stricter validations and added support for handling active pgroups in contact management.
This commit is contained in:
@ -90,7 +90,7 @@ const App: React.FC = () => {
|
||||
<AddressManager pgroups={pgroups} activePgroup={activePgroup} />
|
||||
</Modal>
|
||||
<Modal open={openContactsManager} onClose={handleCloseContactsManager} title="Contacts Management">
|
||||
<ContactsManager />
|
||||
<ContactsManager pgroups={pgroups} activePgroup={activePgroup} />
|
||||
</Modal>
|
||||
</Router>
|
||||
);
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
Dewar,
|
||||
DewarType,
|
||||
DewarSerialNumber,
|
||||
ContactPerson,
|
||||
Contact,
|
||||
Address,
|
||||
ContactsService,
|
||||
AddressesService,
|
||||
@ -37,14 +37,14 @@ interface DewarDetailsProps {
|
||||
dewar: Dewar;
|
||||
trackingNumber: string;
|
||||
setTrackingNumber: (trackingNumber: string) => void;
|
||||
initialContactPersons?: ContactPerson[];
|
||||
initialContacts?: Contact[];
|
||||
initialReturnAddresses?: Address[];
|
||||
defaultContactPerson?: ContactPerson;
|
||||
defaultContact?: Contact;
|
||||
defaultReturnAddress?: Address;
|
||||
shipmentId: number;
|
||||
}
|
||||
|
||||
interface NewContactPerson {
|
||||
interface NewContact {
|
||||
id: number;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
@ -64,21 +64,21 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
dewar,
|
||||
trackingNumber,
|
||||
setTrackingNumber,
|
||||
initialContactPersons = [],
|
||||
initialContacts = [],
|
||||
initialReturnAddresses = [],
|
||||
defaultContactPerson,
|
||||
defaultContact,
|
||||
defaultReturnAddress,
|
||||
shipmentId,
|
||||
}) => {
|
||||
const [localTrackingNumber, setLocalTrackingNumber] = useState(trackingNumber);
|
||||
const [contactPersons, setContactPersons] = useState(initialContactPersons);
|
||||
const [contacts, setContacts] = useState(initialContacts);
|
||||
const [returnAddresses, setReturnAddresses] = useState(initialReturnAddresses);
|
||||
const [selectedContactPerson, setSelectedContactPerson] = useState<string>('');
|
||||
const [selectedContact, setSelectedContact] = useState<string>('');
|
||||
const [selectedReturnAddress, setSelectedReturnAddress] = useState<string>('');
|
||||
const [isCreatingContactPerson, setIsCreatingContactPerson] = useState(false);
|
||||
const [isCreatingContact, setIsCreatingContact] = useState(false);
|
||||
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = useState(false);
|
||||
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
|
||||
const [newContactPerson, setNewContactPerson] = useState<NewContactPerson>({
|
||||
const [newContact, setNewContact] = useState<NewContact>({
|
||||
id: 0,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
@ -140,9 +140,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
useEffect(() => {
|
||||
setLocalTrackingNumber(dewar.tracking_number || '');
|
||||
|
||||
const setInitialContactPerson = () => {
|
||||
setSelectedContactPerson(
|
||||
dewar.contact_person?.id?.toString() || defaultContactPerson?.id?.toString() || ''
|
||||
const setInitialContact = () => {
|
||||
setSelectedContact(
|
||||
dewar.contact?.id?.toString() || defaultContact?.id?.toString() || ''
|
||||
);
|
||||
};
|
||||
|
||||
@ -152,7 +152,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
setInitialContactPerson();
|
||||
setInitialContact();
|
||||
setInitialReturnAddress();
|
||||
|
||||
if (dewar.dewar_type_id) {
|
||||
@ -161,7 +161,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
if (dewar.dewar_serial_number_id) {
|
||||
setSelectedSerialNumber(dewar.dewar_serial_number_id.toString());
|
||||
}
|
||||
}, [dewar, defaultContactPerson, defaultReturnAddress]);
|
||||
}, [dewar, defaultContact, defaultReturnAddress]);
|
||||
|
||||
useEffect(() => {
|
||||
const getContacts = async () => {
|
||||
@ -375,7 +375,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
arrival_date: dewar.arrival_date,
|
||||
returning_date: dewar.returning_date,
|
||||
return_address_id: parseInt(selectedReturnAddress ?? '', 10),
|
||||
contact_person_id: parseInt(selectedContactPerson ?? '', 10),
|
||||
contact_id: parseInt(selectedContactPerson ?? '', 10),
|
||||
};
|
||||
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
||||
|
@ -85,7 +85,7 @@ const StepIconComponent: React.FC<StepIconComponentProps> = ({ icon, dewar, isSe
|
||||
returning_date: dewar.returning_date,
|
||||
qrcode: dewar.qrcode,
|
||||
return_address_id: dewar.return_address_id,
|
||||
contact_person_id: dewar.contact_person_id,
|
||||
contact_id: dewar.contact_id,
|
||||
};
|
||||
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id, payload);
|
||||
|
@ -4,7 +4,7 @@ import QRCode from 'react-qr-code';
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { Dewar, DewarsService, Shipment, ContactPerson, ApiError, ShipmentsService } from "../../openapi";
|
||||
import { Dewar, DewarsService, Shipment, Contact, ApiError, ShipmentsService } from "../../openapi";
|
||||
import { SxProps } from "@mui/system";
|
||||
import CustomStepper from "./DewarStepper";
|
||||
import DewarDetails from './DewarDetails';
|
||||
@ -20,7 +20,7 @@ interface ShipmentDetailsProps {
|
||||
setSelectedDewar: React.Dispatch<React.SetStateAction<Dewar | null>>;
|
||||
setSelectedShipment: React.Dispatch<React.SetStateAction<Shipment | null>>;
|
||||
refreshShipments: () => void;
|
||||
defaultContactPerson?: ContactPerson;
|
||||
defaultContact?: Contact;
|
||||
}
|
||||
|
||||
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
@ -48,7 +48,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
shipping_date: null,
|
||||
arrival_date: null,
|
||||
returning_date: null,
|
||||
contact_person_id: selectedShipment?.contact_person?.id,
|
||||
contact_id: selectedShipment?.contact?.id,
|
||||
return_address_id: selectedShipment?.return_address?.id,
|
||||
};
|
||||
|
||||
@ -59,7 +59,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
// Ensure to update the default contact person and return address when the shipment changes
|
||||
setNewDewar((prev) => ({
|
||||
...prev,
|
||||
contact_person_id: selectedShipment?.contact_person?.id,
|
||||
contact_id: selectedShipment?.contact?.id,
|
||||
return_address_id: selectedShipment?.return_address?.id
|
||||
}));
|
||||
}, [selectedShipment]);
|
||||
@ -122,7 +122,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
...initialNewDewarState,
|
||||
...newDewar,
|
||||
dewar_name: newDewar.dewar_name.trim(),
|
||||
contact_person_id: selectedShipment?.contact_person?.id,
|
||||
contact_id: selectedShipment?.contact?.id,
|
||||
return_address_id: selectedShipment?.return_address?.id
|
||||
} as Dewar;
|
||||
|
||||
@ -179,7 +179,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
};
|
||||
|
||||
const isCommentsEdited = comments !== initialComments;
|
||||
const contactPerson = selectedShipment?.contact_person;
|
||||
const contact = selectedShipment?.contact;
|
||||
|
||||
return (
|
||||
<Box sx={{ ...sx, padding: 2, textAlign: 'left' }}>
|
||||
@ -228,7 +228,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
<Box sx={{ marginTop: 2, marginBottom: 2 }}>
|
||||
<Typography variant="h5">{selectedShipment.shipment_name}</Typography>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
Main contact person: {contactPerson ? `${contactPerson.firstname} ${contactPerson.lastname}` : 'N/A'}
|
||||
Main contact person: {contact ? `${contact.firstname} ${contact.lastname}` : 'N/A'}
|
||||
</Typography>
|
||||
<Typography variant="body1">Number of Pucks: {totalPucks}</Typography>
|
||||
<Typography variant="body1">Number of Samples: {totalSamples}</Typography>
|
||||
@ -318,7 +318,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
<Typography variant="body2">Number of Samples: {dewar.number_of_samples || 0}</Typography>
|
||||
<Typography variant="body2">Tracking Number: {dewar.tracking_number}</Typography>
|
||||
<Typography variant="body2">
|
||||
Contact Person: {dewar.contact_person?.firstname ? `${dewar.contact_person.firstname} ${dewar.contact_person.lastname}` : 'N/A'}
|
||||
Contact Person: {dewar.contact?.firstname ? `${dewar.contact.firstname} ${dewar.contact.lastname}` : 'N/A'}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
@ -355,9 +355,9 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
setTrackingNumber={(value) => {
|
||||
setLocalSelectedDewar((prev) => (prev ? { ...prev, tracking_number: value as string } : prev));
|
||||
}}
|
||||
initialContactPersons={localSelectedDewar?.contact_person ? [localSelectedDewar.contact_person] : []}
|
||||
initialContacts={localSelectedDewar?.contact ? [localSelectedDewar.contact] : []}
|
||||
initialReturnAddresses={localSelectedDewar?.return_address ? [localSelectedDewar.return_address] : []}
|
||||
defaultContactPerson={localSelectedDewar?.contact_person ?? undefined}
|
||||
defaultContact={localSelectedDewar?.contact ?? undefined}
|
||||
defaultReturnAddress={localSelectedDewar?.return_address ?? undefined}
|
||||
shipmentId={selectedShipment?.id ?? null}
|
||||
refreshShipments={refreshShipments}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
import { SelectChangeEvent } from '@mui/material';
|
||||
import { SxProps } from '@mui/system';
|
||||
import {
|
||||
ContactPersonCreate, ContactPerson, Address, AddressCreate, Proposal, ContactsService, AddressesService, ProposalsService,
|
||||
ContactCreate, Contact, Address, AddressCreate, Proposal, ContactsService, AddressesService, ProposalsService,
|
||||
OpenAPI, ShipmentCreate, ShipmentsService
|
||||
} from '../../openapi';
|
||||
import { useEffect } from 'react';
|
||||
@ -41,21 +41,21 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
onCancel,
|
||||
refreshShipments }) => {
|
||||
const [countrySuggestions, setCountrySuggestions] = React.useState<string[]>([]);
|
||||
const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]);
|
||||
const [contacts, setContacts] = React.useState<Contact[]>([]);
|
||||
const [returnAddresses, setReturnAddresses] = React.useState<Address[]>([]);
|
||||
const [proposals, setProposals] = React.useState<Proposal[]>([]);
|
||||
const [isCreatingContactPerson, setIsCreatingContactPerson] = React.useState(false);
|
||||
const [isCreatingContact, setIsCreatingContact] = React.useState(false);
|
||||
const [isCreatingReturnAddress, setIsCreatingReturnAddress] = React.useState(false);
|
||||
const [newContactPerson, setNewContactPerson] = React.useState<ContactPersonCreate>({
|
||||
firstname: '', lastname: '', phone_number: '', email: ''
|
||||
const [newContact, setNewContact] = React.useState<ContactCreate>({
|
||||
pgroups:'', firstname: '', lastname: '', phone_number: '', email: ''
|
||||
});
|
||||
const [newReturnAddress, setNewReturnAddress] = React.useState<Omit<Address, 'id'>>({
|
||||
pgroup:'', house_number: '', street: '', city: '', state: '', zipcode: '', country: ''
|
||||
pgroups:'', house_number: '', street: '', city: '', state: '', zipcode: '', country: ''
|
||||
});
|
||||
const [newShipment, setNewShipment] = React.useState<Partial<ShipmentCreate>>({
|
||||
shipment_name: '', shipment_status: 'In preparation', comments: ''
|
||||
});
|
||||
const [selectedContactPersonId, setSelectedContactPersonId] = React.useState<number | null>(null);
|
||||
const [selectedContactId, setSelectedContactId] = React.useState<number | null>(null);
|
||||
const [selectedReturnAddressId, setSelectedReturnAddressId] = React.useState<number | null>(null);
|
||||
const [selectedProposalId, setSelectedProposalId] = React.useState<number | null>(null);
|
||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||
@ -83,12 +83,17 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
|
||||
// Fetch necessary data
|
||||
const getContacts = async () => {
|
||||
if (!activePgroup) {
|
||||
console.error("Active pgroup is missing.");
|
||||
setErrorMessage("Active pgroup is missing. Unable to load contacts.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const fetchedContacts: ContactPerson[] =
|
||||
await ContactsService.getContactsContactsGet();
|
||||
setContactPersons(fetchedContacts);
|
||||
const fetchedContacts: Contact[] =
|
||||
await ContactsService.getContactsProtectedContactsGet(activePgroup);
|
||||
setContacts(fetchedContacts);
|
||||
} catch {
|
||||
setErrorMessage('Failed to load contact persons.');
|
||||
setErrorMessage('Failed to load contact s.');
|
||||
}
|
||||
};
|
||||
|
||||
@ -102,7 +107,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
try {
|
||||
// Pass activePgroup directly as a string (not as an object)
|
||||
const fetchedAddresses: Address[] =
|
||||
await AddressesService.getReturnAddressesAddressesGet(activePgroup);
|
||||
await AddressesService.getReturnAddressesProtectedAddressesGet(activePgroup);
|
||||
|
||||
setReturnAddresses(fetchedAddresses);
|
||||
} catch (error) {
|
||||
@ -150,9 +155,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
};
|
||||
|
||||
const isContactFormValid = () => {
|
||||
const { firstname, lastname, phone_number, email } = newContactPerson;
|
||||
const { firstname, lastname, phone_number, email } = newContact;
|
||||
|
||||
if (isCreatingContactPerson) {
|
||||
if (isCreatingContact) {
|
||||
if (!firstname || !lastname || !validateEmail(email) || !validatePhoneNumber(phone_number)) return false;
|
||||
}
|
||||
|
||||
@ -173,9 +178,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
const { shipment_name } = newShipment;
|
||||
|
||||
if (!shipment_name) return false;
|
||||
if (!selectedContactPersonId || !selectedReturnAddressId || !selectedProposalId) return false;
|
||||
if (!selectedContactId || !selectedReturnAddressId || !selectedProposalId) return false;
|
||||
|
||||
if (isCreatingContactPerson && !isContactFormValid()) return false;
|
||||
if (isCreatingContact && !isContactFormValid()) return false;
|
||||
if (isCreatingReturnAddress && !isAddressFormValid()) return false;
|
||||
|
||||
return true;
|
||||
@ -197,7 +202,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
shipment_date: new Date().toISOString().split('T')[0], // Remove if date is not required
|
||||
shipment_status: newShipment.shipment_status || 'In preparation',
|
||||
comments: newShipment.comments || '',
|
||||
contact_person_id: selectedContactPersonId!,
|
||||
contact_id: selectedContactId!,
|
||||
return_address_id: selectedReturnAddressId!,
|
||||
proposal_id: selectedProposalId!,
|
||||
dewars: newShipment.dewars || [],
|
||||
@ -217,14 +222,14 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleContactPersonChange = (event: SelectChangeEvent) => {
|
||||
const handleContactChange = (event: SelectChangeEvent) => {
|
||||
const value = event.target.value;
|
||||
if (value === 'new') {
|
||||
setIsCreatingContactPerson(true);
|
||||
setSelectedContactPersonId(null);
|
||||
setIsCreatingContact(true);
|
||||
setSelectedContactId(null);
|
||||
} else {
|
||||
setIsCreatingContactPerson(false);
|
||||
setSelectedContactPersonId(parseInt(value));
|
||||
setIsCreatingContact(false);
|
||||
setSelectedContactId(parseInt(value));
|
||||
}
|
||||
};
|
||||
|
||||
@ -244,33 +249,52 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
setSelectedProposalId(parseInt(value));
|
||||
};
|
||||
|
||||
const handleSaveNewContactPerson = async () => {
|
||||
if (!isContactFormValid()) {
|
||||
const handleSaveNewContact = async () => {
|
||||
// Validate contact form fields
|
||||
if (!isContactFormValid(newContact)) {
|
||||
setErrorMessage('Please fill in all new contact person fields correctly.');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: ContactPersonCreate = {
|
||||
firstname: newContactPerson.firstname,
|
||||
lastname: newContactPerson.lastname,
|
||||
phone_number: newContactPerson.phone_number,
|
||||
email: newContactPerson.email,
|
||||
};
|
||||
|
||||
console.log('Contact Person Payload being sent:', payload);
|
||||
|
||||
try {
|
||||
const newPerson: ContactPerson = await ContactsService.createContactContactsPost(payload);
|
||||
setContactPersons([...contactPersons, newPerson]);
|
||||
setErrorMessage(null);
|
||||
setSelectedContactPersonId(newPerson.id);
|
||||
} catch (error) {
|
||||
console.error('Failed to create a new contact person:', error);
|
||||
setErrorMessage('Failed to create a new contact person. Please try again later.');
|
||||
// Ensure activePgroup is available
|
||||
if (!activePgroup) {
|
||||
setErrorMessage('Active pgroup is missing. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
setNewContactPerson({ firstname: '', lastname: '', phone_number: '', email: '' });
|
||||
setIsCreatingContactPerson(false);
|
||||
// Construct the payload
|
||||
const payload: ContactCreate = {
|
||||
pgroups: activePgroup, // Ensure this value is available
|
||||
firstname: newContact.firstname.trim(),
|
||||
lastname: newContact.lastname.trim(),
|
||||
phone_number: newContact.phone_number.trim(),
|
||||
email: newContact.email.trim(),
|
||||
};
|
||||
|
||||
console.log('Payload being sent:', JSON.stringify(payload, null, 2));
|
||||
|
||||
try {
|
||||
// Call the API with the correctly constructed payload
|
||||
const newPerson: Contact = await ContactsService.createContactProtectedContactsPost(payload);
|
||||
|
||||
// Update state on success
|
||||
setContacts([...contacts, newPerson]); // Add new contact to the list
|
||||
setErrorMessage(null); // Clear error messages
|
||||
setSelectedContactId(newPerson.id); // Optionally select the contact
|
||||
|
||||
// Reset form inputs
|
||||
setNewContact({ pgroups: '', firstname: '', lastname: '', phone_number: '', email: '' });
|
||||
setIsCreatingContact(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to create a new contact person:', error);
|
||||
|
||||
// Handle detailed backend error messages if available
|
||||
if (error.response?.data?.detail) {
|
||||
setErrorMessage(`Error: ${error.response.data.detail}`);
|
||||
} else {
|
||||
setErrorMessage('Failed to create a new contact person. Please try again later.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveNewReturnAddress = async () => {
|
||||
@ -301,7 +325,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
|
||||
// Call the API with the completed payload
|
||||
try {
|
||||
const response: Address = await AddressesService.createReturnAddressAddressesPost(payload);
|
||||
const response: Address = await AddressesService.createReturnAddressProtectedAddressesPost(payload);
|
||||
setReturnAddresses([...returnAddresses, response]); // Update the address state
|
||||
setErrorMessage(null);
|
||||
setSelectedReturnAddressId(response.id); // Set the newly created address ID to the form
|
||||
@ -311,7 +335,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
}
|
||||
|
||||
// Reset form inputs and close the "Create New Address" form
|
||||
setNewReturnAddress({ pgroup: '', house_number: '', street: '', city: '', state: '', zipcode: '', country: '' });
|
||||
setNewReturnAddress({ pgroups: '', house_number: '', street: '', city: '', state: '', zipcode: '', country: '' });
|
||||
setIsCreatingReturnAddress(false);
|
||||
};
|
||||
|
||||
@ -342,11 +366,11 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Contact Person</InputLabel>
|
||||
<Select
|
||||
value={selectedContactPersonId ? selectedContactPersonId.toString() : ''}
|
||||
onChange={handleContactPersonChange}
|
||||
value={selectedContactId ? selectedContactId.toString() : ''}
|
||||
onChange={handleContactChange}
|
||||
displayEmpty
|
||||
>
|
||||
{contactPersons.map((person) => (
|
||||
{contacts.map((person) => (
|
||||
<MenuItem key={person.id} value={person.id.toString()}>
|
||||
{`${person.lastname}, ${person.firstname}`}
|
||||
</MenuItem>
|
||||
@ -356,21 +380,21 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
{isCreatingContactPerson && (
|
||||
{isCreatingContact && (
|
||||
<>
|
||||
<TextField
|
||||
label="First Name"
|
||||
name="firstname"
|
||||
value={newContactPerson.firstname}
|
||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, firstname: e.target.value })}
|
||||
value={newContact.firstname}
|
||||
onChange={(e) => setNewContact({ ...newContact, firstname: e.target.value })}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
label="Last Name"
|
||||
name="lastname"
|
||||
value={newContactPerson.lastname}
|
||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, lastname: e.target.value })}
|
||||
value={newContact.lastname}
|
||||
onChange={(e) => setNewContact({ ...newContact, lastname: e.target.value })}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
@ -378,28 +402,28 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
label="Phone"
|
||||
name="phone_number"
|
||||
type="tel"
|
||||
value={newContactPerson.phone_number}
|
||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, phone_number: e.target.value })}
|
||||
value={newContact.phone_number}
|
||||
onChange={(e) => setNewContact({ ...newContact, phone_number: e.target.value })}
|
||||
fullWidth
|
||||
required
|
||||
error={!validatePhoneNumber(newContactPerson.phone_number)}
|
||||
helperText={!validatePhoneNumber(newContactPerson.phone_number) ? 'Invalid phone number' : ''}
|
||||
error={!validatePhoneNumber(newContact.phone_number)}
|
||||
helperText={!validatePhoneNumber(newContact.phone_number) ? 'Invalid phone number' : ''}
|
||||
/>
|
||||
<TextField
|
||||
label="Email"
|
||||
name="email"
|
||||
type="email"
|
||||
value={newContactPerson.email}
|
||||
onChange={(e) => setNewContactPerson({ ...newContactPerson, email: e.target.value })}
|
||||
value={newContact.email}
|
||||
onChange={(e) => setNewContact({ ...newContact, email: e.target.value })}
|
||||
fullWidth
|
||||
required
|
||||
error={!validateEmail(newContactPerson.email)}
|
||||
helperText={!validateEmail(newContactPerson.email) ? 'Invalid email' : ''}
|
||||
error={!validateEmail(newContact.email)}
|
||||
helperText={!validateEmail(newContact.email) ? 'Invalid email' : ''}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleSaveNewContactPerson}
|
||||
onClick={handleSaveNewContact}
|
||||
disabled={!isContactFormValid()}
|
||||
>
|
||||
Save New Contact Person
|
||||
|
@ -65,7 +65,7 @@ const SpreadsheetTable = ({
|
||||
shipping_date: null,
|
||||
arrival_date: null,
|
||||
returning_date: null,
|
||||
contact_person_id: selectedShipment?.contact_person?.id,
|
||||
contact_id: selectedShipment?.contact?.id,
|
||||
return_address_id: selectedShipment?.return_address?.id,
|
||||
dewar_name: '',
|
||||
tracking_number: 'UNKNOWN',
|
||||
@ -78,7 +78,7 @@ const SpreadsheetTable = ({
|
||||
useEffect(() => {
|
||||
setNewDewar((prev) => ({
|
||||
...prev,
|
||||
contact_person_id: selectedShipment?.contact_person?.id,
|
||||
contact_id: selectedShipment?.contact?.id,
|
||||
return_address_id: selectedShipment?.return_address?.id
|
||||
}));
|
||||
}, [selectedShipment]);
|
||||
@ -259,8 +259,8 @@ const SpreadsheetTable = ({
|
||||
};
|
||||
|
||||
const createOrUpdateDewarsFromSheet = async (data, contactPerson, returnAddress) => {
|
||||
if (!contactPerson?.id || !returnAddress?.id) {
|
||||
console.error('contact_person_id or return_address_id is missing');
|
||||
if (!contact?.id || !returnAddress?.id) {
|
||||
console.error('contact_id or return_address_id is missing');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ const SpreadsheetTable = ({
|
||||
dewar = {
|
||||
...initialNewDewarState,
|
||||
dewar_name: dewarName,
|
||||
contact_person_id: contactPerson.id,
|
||||
contact_id: contactPerson.id,
|
||||
return_address_id: returnAddress.id,
|
||||
pucks: [],
|
||||
};
|
||||
@ -498,7 +498,7 @@ const SpreadsheetTable = ({
|
||||
|
||||
await createOrUpdateDewarsFromSheet(
|
||||
raw_data,
|
||||
selectedShipment?.contact_person,
|
||||
selectedShipment?.contact,
|
||||
selectedShipment?.return_address
|
||||
);
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ShipmentsService, Shipment, ContactPerson } from '../../openapi';
|
||||
import { ShipmentsService, Shipment, Contact } from '../../openapi';
|
||||
|
||||
const useShipments = () => {
|
||||
const [shipments, setShipments] = useState<Shipment[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [defaultContactPerson, setDefaultContactPerson] = useState<ContactPerson | undefined>();
|
||||
const [defaultContact, setDefaultContact] = useState<Contact | undefined>();
|
||||
|
||||
const fetchAndSetShipments = async () => {
|
||||
try {
|
||||
@ -16,10 +16,10 @@ const useShipments = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDefaultContactPerson = async () => {
|
||||
const fetchDefaultContact = async () => {
|
||||
try {
|
||||
const contacts = await ShipmentsService.getShipmentContactPersonsShipmentsContactPersonsGet();
|
||||
setDefaultContactPerson(contacts[0]);
|
||||
setDefaultContact(contacts[0]);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch contact persons:', error);
|
||||
setError('Failed to load contact persons. Please try again later.');
|
||||
@ -28,10 +28,10 @@ const useShipments = () => {
|
||||
|
||||
useEffect(() => {
|
||||
fetchAndSetShipments();
|
||||
fetchDefaultContactPerson();
|
||||
fetchDefaultContact();
|
||||
}, []);
|
||||
|
||||
return { shipments, error, defaultContactPerson, fetchAndSetShipments };
|
||||
return { shipments, error, defaultContact, fetchAndSetShipments };
|
||||
};
|
||||
|
||||
export default useShipments;
|
@ -78,7 +78,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
|
||||
React.useEffect(() => {
|
||||
const fetchAllData = async () => {
|
||||
try {
|
||||
const response = await AddressesService.getAllAddressesAddressesAllGet();
|
||||
const response = await AddressesService.getAllAddressesProtectedAddressesAllGet();
|
||||
|
||||
// Preprocess: Add associated and unassociated pgroups
|
||||
const transformedAddresses = response.map((address) => {
|
||||
@ -108,7 +108,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
|
||||
try {
|
||||
if (editAddressId !== null) {
|
||||
// Update address (mark old one obsolete, create a new one)
|
||||
const updatedAddress = await AddressesService.updateReturnAddressAddressesAddressIdPut(
|
||||
const updatedAddress = await AddressesService.updateReturnAddressProtectedAddressesAddressIdPut(
|
||||
editAddressId,
|
||||
newAddress as AddressUpdate
|
||||
);
|
||||
@ -120,7 +120,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
|
||||
setEditAddressId(null);
|
||||
} else {
|
||||
// Add new address
|
||||
const response = await AddressesService.createReturnAddressAddressesPost(newAddress as AddressCreate);
|
||||
const response = await AddressesService.createReturnAddressProtectedAddressesPost(newAddress as AddressCreate);
|
||||
setAddresses([...addresses, response]);
|
||||
}
|
||||
setNewAddress({ house_number:'', street: '', city: '', state: '', zipcode: '', country: '' });
|
||||
@ -134,7 +134,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
|
||||
const handleDeleteAddress = async (id: number) => {
|
||||
try {
|
||||
// Delete (inactivate) the address
|
||||
await AddressesService.deleteReturnAddressAddressesAddressIdDelete(id);
|
||||
await AddressesService.deleteReturnAddressProtectedAddressesAddressIdDelete(id);
|
||||
|
||||
// Remove the obsolete address from the active list in the UI
|
||||
setAddresses(addresses.filter(address => address.id !== id && address.status === "active"));
|
||||
@ -182,7 +182,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
|
||||
const updatedPgroups = [...address.associatedPgroups, pgroup]; // Add the pgroup
|
||||
|
||||
// Update the backend
|
||||
await AddressesService.updateReturnAddressAddressesAddressIdPut(addressId, {
|
||||
await AddressesService.updateReturnAddressProtectedAddressesAddressIdPut(addressId, {
|
||||
...address,
|
||||
pgroups: updatedPgroups.join(','), // Sync updated pgroups
|
||||
});
|
||||
|
@ -1,158 +1,285 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Container, Typography, List, ListItem, IconButton, TextField, Box, ListItemText, ListItemSecondaryAction, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Button
|
||||
Container,
|
||||
Typography,
|
||||
List,
|
||||
ListItem,
|
||||
IconButton,
|
||||
TextField,
|
||||
Box,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Button,
|
||||
Chip
|
||||
} from '@mui/material';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import { ContactsService } from '../../openapi';
|
||||
import type { ContactPerson, ContactPersonCreate, ContactPersonUpdate } from '../models/ContactPerson';
|
||||
import {Contact, ContactCreate, ContactsService, ContactUpdate} from '../../openapi';
|
||||
|
||||
const ContactsManager: React.FC = () => {
|
||||
const [contacts, setContacts] = React.useState<ContactPerson[]>([]);
|
||||
const [newContact, setNewContact] = React.useState<Partial<ContactPerson>>({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
phone_number: '',
|
||||
email: '',
|
||||
|
||||
interface ContactsManagerProps {
|
||||
pgroups: string[];
|
||||
activePgroup: string;
|
||||
}
|
||||
|
||||
// Extend the generated Contact type
|
||||
interface ContactWithPgroups extends Contact {
|
||||
associatedPgroups: string[]; // Dynamically added pgroups
|
||||
}
|
||||
|
||||
const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup }) => {
|
||||
const [contacts, setContacts] = React.useState<ContactWithPgroups[]>([]);
|
||||
const [newContact, setNewContact] = React.useState<Partial<Contact>>({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
phone_number: '',
|
||||
email: '',
|
||||
});
|
||||
const [editContactId, setEditContactId] = React.useState<number | null>(null);
|
||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||
const [dialogOpen, setDialogOpen] = React.useState(false);
|
||||
const [selectedContact, setSelectedContact] = React.useState<ContactWithPgroups | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchContacts = async () => {
|
||||
try {
|
||||
const response = await ContactsService.getAllContactsProtectedContactsAllGet();
|
||||
|
||||
// Preprocess: Add associated and unassociated pgroups
|
||||
const transformedContacts = response.map((contact) => {
|
||||
const contactPgroups = contact.pgroups?.split(',').map((p) => p.trim()) || [];
|
||||
const associatedPgroups = pgroups.filter((pgroup) => contactPgroups.includes(pgroup));
|
||||
return {
|
||||
...contact,
|
||||
associatedPgroups, // pgroups linked to the contact
|
||||
};
|
||||
});
|
||||
|
||||
setContacts(transformedContacts);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch contacts', error);
|
||||
setErrorMessage('Failed to load contacts. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
fetchContacts();
|
||||
}, [pgroups]);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = event.target;
|
||||
setNewContact({ ...newContact, [name]: value });
|
||||
};
|
||||
|
||||
const handleAddOrUpdateContact = async () => {
|
||||
try {
|
||||
if (editContactId !== null) {
|
||||
// Update contact
|
||||
await ContactsService.updateContactProtectedContactsContactIdPut(editContactId, newContact as ContactUpdate);
|
||||
setContacts(
|
||||
contacts.map((contact) =>
|
||||
contact.id === editContactId ? { ...contact, ...newContact } : contact
|
||||
)
|
||||
);
|
||||
setEditContactId(null);
|
||||
} else {
|
||||
// Add new contact
|
||||
const response = await ContactsService.createContactProtectedContactsPost(newContact as ContactCreate);
|
||||
setContacts([...contacts, response]);
|
||||
}
|
||||
setNewContact({ firstname: '', lastname: '', phone_number: '', email: '' });
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to add/update contact', error);
|
||||
setErrorMessage('Failed to add/update contact. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteContact = async (id: number) => {
|
||||
try {
|
||||
await ContactsService.deleteContactProtectedContactsContactIdDelete(id);
|
||||
setContacts(contacts.filter((contact) => contact.id !== id));
|
||||
} catch (error) {
|
||||
console.error('Failed to delete contact', error);
|
||||
setErrorMessage('Failed to delete contact. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditContact = (contact: Contact) => {
|
||||
setEditContactId(contact.id);
|
||||
setNewContact(contact);
|
||||
};
|
||||
|
||||
const openDialog = (contact: ContactWithPgroups) => {
|
||||
setSelectedContact(contact);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
setDialogOpen(false);
|
||||
setSelectedContact(null);
|
||||
};
|
||||
|
||||
const confirmDelete = async () => {
|
||||
if (selectedContact) {
|
||||
await handleDeleteContact(selectedContact.id);
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
|
||||
const togglePgroupAssociation = async (contactId: number, pgroup: string) => {
|
||||
try {
|
||||
const contact = contacts.find((c) => c.id === contactId);
|
||||
if (!contact) return;
|
||||
|
||||
const isAssociated = contact.associatedPgroups.includes(pgroup);
|
||||
|
||||
// Only allow adding a pgroup
|
||||
if (isAssociated) {
|
||||
console.warn('Removing a pgroup is not allowed.');
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedPgroups = [...contact.associatedPgroups, pgroup]; // Add the pgroup
|
||||
|
||||
// Update the backend
|
||||
await ContactsService.updateContactProtectedContactsContactIdPut(contactId, {
|
||||
...contact,
|
||||
pgroups: updatedPgroups.join(','), // Sync updated pgroups
|
||||
});
|
||||
|
||||
// Update contact in local state
|
||||
setContacts((prevContacts) =>
|
||||
prevContacts.map((c) =>
|
||||
c.id === contactId ? { ...c, associatedPgroups: updatedPgroups } : c
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to add pgroup association', error);
|
||||
setErrorMessage('Failed to add pgroup association. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
const renderPgroupChips = (contact: ContactWithPgroups) => {
|
||||
return pgroups.map((pgroup) => {
|
||||
const isAssociated = contact.associatedPgroups.includes(pgroup);
|
||||
return (
|
||||
<Chip
|
||||
key={pgroup}
|
||||
label={pgroup}
|
||||
onClick={
|
||||
!isAssociated
|
||||
? () => togglePgroupAssociation(contact.id, pgroup)
|
||||
: undefined
|
||||
}
|
||||
sx={{
|
||||
backgroundColor: isAssociated ? '#19d238' : '#b0b0b0',
|
||||
color: 'white',
|
||||
borderRadius: '8px',
|
||||
fontWeight: 'bold',
|
||||
height: '20px',
|
||||
fontSize: '12px',
|
||||
boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)',
|
||||
cursor: isAssociated ? 'default' : 'pointer', // Disable pointer for associated chips
|
||||
'&:hover': { opacity: isAssociated ? 1 : 0.8 }, // Disable hover effect for associated chips
|
||||
mr: 1,
|
||||
mb: 1,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
const [editContactId, setEditContactId] = React.useState<number | null>(null);
|
||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||
const [dialogOpen, setDialogOpen] = React.useState(false);
|
||||
const [selectedContact, setSelectedContact] = React.useState<ContactPerson | null>(null);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchContacts = async () => {
|
||||
try {
|
||||
const response = await ContactsService.getContactsContactsGet();
|
||||
setContacts(response);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch contacts', error);
|
||||
setErrorMessage('Failed to load contacts. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
fetchContacts();
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = event.target;
|
||||
setNewContact({ ...newContact, [name]: value });
|
||||
};
|
||||
|
||||
const handleAddOrUpdateContact = async () => {
|
||||
try {
|
||||
if (editContactId !== null) {
|
||||
// Update contact
|
||||
await ContactsService.updateContactContactsContactIdPut(editContactId, newContact as ContactPersonUpdate);
|
||||
setContacts(contacts.map(contact => contact.id === editContactId ? { ...contact, ...newContact } : contact));
|
||||
setEditContactId(null);
|
||||
} else {
|
||||
// Add new contact
|
||||
const response = await ContactsService.createContactContactsPost(newContact as ContactPersonCreate);
|
||||
setContacts([...contacts, response]);
|
||||
}
|
||||
setNewContact({ firstname: '', lastname: '', phone_number: '', email: '' });
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to add/update contact', error);
|
||||
setErrorMessage('Failed to add/update contact. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteContact = async (id: number) => {
|
||||
try {
|
||||
await ContactsService.deleteContactContactsContactIdDelete(id);
|
||||
setContacts(contacts.filter(contact => contact.id !== id));
|
||||
} catch (error) {
|
||||
console.error('Failed to delete contact', error);
|
||||
setErrorMessage('Failed to delete contact. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditContact = (contact: ContactPerson) => {
|
||||
setEditContactId(contact.id);
|
||||
setNewContact(contact);
|
||||
};
|
||||
|
||||
const openDialog = (contact: ContactPerson) => {
|
||||
setSelectedContact(contact);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
setDialogOpen(false);
|
||||
setSelectedContact(null);
|
||||
};
|
||||
|
||||
const confirmDelete = async () => {
|
||||
if (selectedContact) {
|
||||
await handleDeleteContact(selectedContact.id);
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Contacts Management
|
||||
</Typography>
|
||||
<Box mb={3} display="flex" justifyContent="center" alignItems="center">
|
||||
<TextField label="First Name" name="firstname" value={newContact.firstname || ''} onChange={handleInputChange} />
|
||||
<TextField label="Last Name" name="lastname" value={newContact.lastname || ''} onChange={handleInputChange} />
|
||||
<TextField label="Phone Number" name="phone_number" value={newContact.phone_number || ''} onChange={handleInputChange} />
|
||||
<TextField label="Email" name="email" value={newContact.email || ''} onChange={handleInputChange} />
|
||||
<IconButton color="primary" onClick={handleAddOrUpdateContact}>
|
||||
{editContactId !== null ? <SaveIcon /> : <AddIcon />}
|
||||
return (
|
||||
<Container>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Contacts Management
|
||||
</Typography>
|
||||
<Box mb={3} display="flex" justifyContent="center" alignItems="center">
|
||||
<TextField
|
||||
label="First Name"
|
||||
name="firstname"
|
||||
value={newContact.firstname || ''}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<TextField
|
||||
label="Last Name"
|
||||
name="lastname"
|
||||
value={newContact.lastname || ''}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<TextField
|
||||
label="Phone Number"
|
||||
name="phone_number"
|
||||
value={newContact.phone_number || ''}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<TextField
|
||||
label="Email"
|
||||
name="email"
|
||||
value={newContact.email || ''}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<IconButton color="primary" onClick={handleAddOrUpdateContact}>
|
||||
{editContactId !== null ? <SaveIcon /> : <AddIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
{errorMessage && <Typography color="error">{errorMessage}</Typography>}
|
||||
<List>
|
||||
{contacts.length > 0 ? (
|
||||
contacts.map((contact) => (
|
||||
<ListItem key={contact.id} button>
|
||||
<ListItemText
|
||||
primary={`${contact.firstname} ${contact.lastname}`}
|
||||
secondary={
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
{renderPgroupChips(contact)}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" color="primary" onClick={() => handleEditContact(contact)}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
{errorMessage && <Typography color="error">{errorMessage}</Typography>}
|
||||
<List>
|
||||
{contacts.length > 0 ? (
|
||||
contacts.map((contact) => (
|
||||
<ListItem key={contact.id} button>
|
||||
<ListItemText
|
||||
primary={`${contact.firstname} ${contact.lastname}`}
|
||||
secondary={`${contact.phone_number} - ${contact.email}`}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" color="primary" onClick={() => handleEditContact(contact)}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton edge="end" color="secondary" onClick={() => openDialog(contact)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
))
|
||||
) : (
|
||||
<Typography>No contacts found</Typography>
|
||||
)}
|
||||
</List>
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
onClose={closeDialog}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Confirm Delete"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete this contact?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={closeDialog} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={confirmDelete} color="secondary" autoFocus>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Container>
|
||||
);
|
||||
<IconButton edge="end" color="secondary" onClick={() => openDialog(contact)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
))
|
||||
) : (
|
||||
<Typography>No contacts found</Typography>
|
||||
)}
|
||||
</List>
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
onClose={closeDialog}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Confirm Delete"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete this contact?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={closeDialog} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={confirmDelete} color="secondary" autoFocus>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactsManager;
|
@ -11,7 +11,7 @@ type ShipmentViewProps = {
|
||||
};
|
||||
|
||||
const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
|
||||
const { shipments, error, defaultContactPerson, fetchAndSetShipments } = useShipments();
|
||||
const { shipments, error, defaultContact, fetchAndSetShipments } = useShipments();
|
||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
||||
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
|
||||
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||
@ -76,7 +76,7 @@ const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
|
||||
setSelectedDewar={setSelectedDewar}
|
||||
setSelectedShipment={setSelectedShipment}
|
||||
refreshShipments={fetchAndSetShipments}
|
||||
defaultContactPerson={defaultContactPerson}
|
||||
defaultContact={defaultContact}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export interface ContactPerson {
|
||||
export interface Contact {
|
||||
id: string;
|
||||
lastname: string;
|
||||
firstname: string;
|
||||
@ -29,7 +29,7 @@ export interface Dewar {
|
||||
number_of_pucks: number;
|
||||
number_of_samples: number;
|
||||
return_address: ReturnAddress[];
|
||||
contact_person: ContactPerson[];
|
||||
contact_: Contact[];
|
||||
status: string;
|
||||
ready_date?: string; // Make sure this is included
|
||||
shipping_date?: string; // Make sure this is included
|
||||
@ -45,7 +45,7 @@ export interface Shipment {
|
||||
shipment_date: string;
|
||||
number_of_dewars: number;
|
||||
shipment_status: string;
|
||||
contact_person: ContactPerson[] | null; // Change to an array to accommodate multiple contacts
|
||||
contact_: Contact[] | null; // Change to an array to accommodate multiple contacts
|
||||
proposal_number?: string;
|
||||
return_address: Address[]; // Change to an array of Address
|
||||
comments?: string;
|
||||
|
Reference in New Issue
Block a user