Integrate pgroups
for shipment data security
Added `pgroups` to secure and associate data with specific permission groups. Updated backend routers, database models, and API endpoints to handle authorization based on `pgroups`. Adjusted frontend components and hooks to support `pgroups` in data fetching and management workflows.
This commit is contained in:
@ -82,7 +82,7 @@ const App: React.FC = () => {
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginView />} />
|
||||
<Route path="/" element={<ProtectedRoute element={<HomePage />} />} />
|
||||
<Route path="/shipments" element={<ProtectedRoute element={<ShipmentView activePgroup={activePgroup} />} />} />
|
||||
<Route path="/shipments" element={<ProtectedRoute element={<ShipmentView pgroups={pgroups} activePgroup={activePgroup} />} />} />
|
||||
<Route path="/planning" element={<ProtectedRoute element={<PlanningView />} />} />
|
||||
<Route path="/results" element={<ProtectedRoute element={<ResultsView />} />} />
|
||||
</Routes>
|
||||
|
@ -35,6 +35,8 @@ import DownloadIcon from '@mui/icons-material/Download';
|
||||
|
||||
interface DewarDetailsProps {
|
||||
dewar: Dewar;
|
||||
pgroups: string;
|
||||
activePgroup: string;
|
||||
trackingNumber: string;
|
||||
setTrackingNumber: (trackingNumber: string) => void;
|
||||
initialContacts?: Contact[];
|
||||
@ -45,7 +47,7 @@ interface DewarDetailsProps {
|
||||
}
|
||||
|
||||
interface NewContact {
|
||||
id: number;
|
||||
pgroups: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
phone_number: string;
|
||||
@ -53,7 +55,7 @@ interface NewContact {
|
||||
}
|
||||
|
||||
interface NewReturnAddress {
|
||||
id: number;
|
||||
pgroups: string;
|
||||
street: string;
|
||||
city: string;
|
||||
zipcode: string;
|
||||
@ -61,6 +63,8 @@ interface NewReturnAddress {
|
||||
}
|
||||
|
||||
const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
pgroups,
|
||||
activePgroup,
|
||||
dewar,
|
||||
trackingNumber,
|
||||
setTrackingNumber,
|
||||
@ -80,6 +84,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
const [puckStatuses, setPuckStatuses] = useState<string[][]>([]);
|
||||
const [newContact, setNewContact] = useState<NewContact>({
|
||||
id: 0,
|
||||
pgroups: activePgroup,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
phone_number: '',
|
||||
@ -87,6 +92,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
});
|
||||
const [newReturnAddress, setNewReturnAddress] = useState<NewReturnAddress>({
|
||||
id: 0,
|
||||
pgroups: activePgroup,
|
||||
street: '',
|
||||
city: '',
|
||||
zipcode: '',
|
||||
@ -166,8 +172,8 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
useEffect(() => {
|
||||
const getContacts = async () => {
|
||||
try {
|
||||
const c = await ContactsService.getContactsContactsGet();
|
||||
setContactPersons(c);
|
||||
const c = await ContactsService.getContactsProtectedContactsGet(activePgroup);
|
||||
setContacts(c);
|
||||
} catch {
|
||||
setFeedbackMessage('Failed to load contact persons. Please try again later.');
|
||||
setOpenSnackbar(true);
|
||||
@ -176,7 +182,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
|
||||
const getReturnAddresses = async () => {
|
||||
try {
|
||||
const a = await AddressesService.getReturnAddressesAddressesGet();
|
||||
const a = await AddressesService.getReturnAddressesProtectedAddressesGet(activePgroup);
|
||||
setReturnAddresses(a);
|
||||
} catch {
|
||||
setFeedbackMessage('Failed to load return addresses. Please try again later.');
|
||||
@ -192,7 +198,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
const fetchSamples = async () => {
|
||||
if (dewar.id) {
|
||||
try {
|
||||
const fetchedSamples = await ShipmentsService.getSamplesInDewarShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(
|
||||
const fetchedSamples = await ShipmentsService.getSamplesInDewarProtectedShipmentsShipmentsShipmentIdDewarsDewarIdSamplesGet(
|
||||
shipmentId,
|
||||
dewar.id
|
||||
);
|
||||
@ -276,31 +282,32 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
};
|
||||
|
||||
const handleAddContact = async () => {
|
||||
if (!validateEmail(newContactPerson.email) || !validatePhoneNumber(newContactPerson.phone_number) || !newContactPerson.firstName || !newContactPerson.lastName) {
|
||||
if (!validateEmail(newContact.email) || !validatePhoneNumber(newContact.phone_number) || !newContact.firstName || !newContact.lastName) {
|
||||
setFeedbackMessage('Please fill in all new contact person fields correctly.');
|
||||
setOpenSnackbar(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
firstname: newContactPerson.firstName,
|
||||
lastname: newContactPerson.lastName,
|
||||
phone_number: newContactPerson.phone_number,
|
||||
email: newContactPerson.email,
|
||||
pgroups: activePgroup,
|
||||
firstname: newContact.firstName,
|
||||
lastname: newContact.lastName,
|
||||
phone_number: newContact.phone_number,
|
||||
email: newContact.email,
|
||||
};
|
||||
|
||||
try {
|
||||
const c = await ContactsService.createContactContactsPost(payload);
|
||||
setContactPersons([...contactPersons, c]);
|
||||
const c = await ContactsService.createContactProtectedContactsPost(payload);
|
||||
setContacts([...contacts, c]);
|
||||
setFeedbackMessage('Contact person added successfully.');
|
||||
setNewContactPerson({ id: 0, firstName: '', lastName: '', phone_number: '', email: '' });
|
||||
setSelectedContactPerson(c.id?.toString() || '');
|
||||
setNewContact({ pgroups: activePgroup, firstName: '', lastName: '', phone_number: '', email: '' });
|
||||
setSelectedContact(c.id?.toString() || '');
|
||||
} catch {
|
||||
setFeedbackMessage('Failed to create a new contact person. Please try again later.');
|
||||
}
|
||||
|
||||
setOpenSnackbar(true);
|
||||
setIsCreatingContactPerson(false);
|
||||
setIsCreatingContact(false);
|
||||
setChangesMade(true);
|
||||
};
|
||||
|
||||
@ -312,6 +319,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
}
|
||||
|
||||
const payload = {
|
||||
pgroups: activePgroup,
|
||||
street: newReturnAddress.street.trim(),
|
||||
city: newReturnAddress.city.trim(),
|
||||
zipcode: newReturnAddress.zipcode.trim(),
|
||||
@ -319,11 +327,11 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
};
|
||||
|
||||
try {
|
||||
const a = await AddressesService.createReturnAddressAddressesPost(payload);
|
||||
const a = await AddressesService.createReturnAddressProtectedAddressesPost(payload);
|
||||
setReturnAddresses([...returnAddresses, a]);
|
||||
setFeedbackMessage('Return address added successfully.');
|
||||
setNewReturnAddress({
|
||||
id: 0,
|
||||
pgroups: activePgroup,
|
||||
street: '',
|
||||
city: '',
|
||||
zipcode: '',
|
||||
@ -347,7 +355,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
return date.toISOString().split('T')[0];
|
||||
};
|
||||
|
||||
if (!selectedContactPerson || !selectedReturnAddress) {
|
||||
if (!selectedContact || !selectedReturnAddress) {
|
||||
setFeedbackMessage('Please ensure all required fields are filled.');
|
||||
setOpenSnackbar(true);
|
||||
return;
|
||||
@ -375,7 +383,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
arrival_date: dewar.arrival_date,
|
||||
returning_date: dewar.returning_date,
|
||||
return_address_id: parseInt(selectedReturnAddress ?? '', 10),
|
||||
contact_id: parseInt(selectedContactPerson ?? '', 10),
|
||||
contact_id: parseInt(selectedContact ?? '', 10),
|
||||
};
|
||||
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload);
|
||||
@ -554,30 +562,30 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
|
||||
<Typography variant="body1">Current Contact Person:</Typography>
|
||||
<Select
|
||||
value={selectedContactPerson}
|
||||
value={selectedContact}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setSelectedContactPerson(value);
|
||||
setIsCreatingContactPerson(value === 'add');
|
||||
setSelectedContact(value);
|
||||
setIsCreatingContact(value === 'add');
|
||||
setChangesMade(true);
|
||||
}}
|
||||
displayEmpty
|
||||
sx={{ width: '300px', marginBottom: 2 }}
|
||||
>
|
||||
{contactPersons.map((person) => (
|
||||
{contacts.map((person) => (
|
||||
<MenuItem key={person.id} value={person.id?.toString()}>
|
||||
{person.firstname} {person.lastname}
|
||||
</MenuItem>
|
||||
))}
|
||||
<MenuItem value="add">Add New Contact Person</MenuItem>
|
||||
</Select>
|
||||
{isCreatingContactPerson && (
|
||||
{isCreatingContact && (
|
||||
<Box sx={{ marginBottom: 2 }}>
|
||||
<TextField
|
||||
label="First Name"
|
||||
value={newContactPerson.firstName}
|
||||
value={newContact.firstName}
|
||||
onChange={(e) =>
|
||||
setNewContactPerson((prev) => ({
|
||||
setNewContact((prev) => ({
|
||||
...prev,
|
||||
firstName: e.target.value,
|
||||
}))
|
||||
@ -587,9 +595,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
/>
|
||||
<TextField
|
||||
label="Last Name"
|
||||
value={newContactPerson.lastName}
|
||||
value={newContact.lastName}
|
||||
onChange={(e) =>
|
||||
setNewContactPerson((prev) => ({
|
||||
setNewContact((prev) => ({
|
||||
...prev,
|
||||
lastName: e.target.value,
|
||||
}))
|
||||
@ -599,9 +607,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
/>
|
||||
<TextField
|
||||
label="Phone Number"
|
||||
value={newContactPerson.phone_number}
|
||||
value={newContact.phone_number}
|
||||
onChange={(e) =>
|
||||
setNewContactPerson((prev) => ({
|
||||
setNewContact((prev) => ({
|
||||
...prev,
|
||||
phone_number: e.target.value,
|
||||
}))
|
||||
@ -611,9 +619,9 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
|
||||
/>
|
||||
<TextField
|
||||
label="Email"
|
||||
value={newContactPerson.email}
|
||||
value={newContact.email}
|
||||
onChange={(e) =>
|
||||
setNewContactPerson((prev) => ({
|
||||
setNewContact((prev) => ({
|
||||
...prev,
|
||||
email: e.target.value,
|
||||
}))
|
||||
|
@ -13,6 +13,7 @@ const MAX_COMMENTS_LENGTH = 200;
|
||||
|
||||
interface ShipmentDetailsProps {
|
||||
activePgroup: string;
|
||||
pgroups: string;
|
||||
isCreatingShipment: boolean;
|
||||
sx?: SxProps;
|
||||
selectedShipment: Shipment | null;
|
||||
@ -24,6 +25,7 @@ interface ShipmentDetailsProps {
|
||||
}
|
||||
|
||||
const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
pgroups,
|
||||
activePgroup,
|
||||
sx,
|
||||
selectedShipment,
|
||||
@ -82,7 +84,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
const confirmed = window.confirm('Are you sure you want to delete this dewar?');
|
||||
if (confirmed && selectedShipment) {
|
||||
try {
|
||||
const updatedShipment = await ShipmentsService.removeDewarFromShipmentShipmentsShipmentIdRemoveDewarDewarIdDelete(
|
||||
const updatedShipment = await ShipmentsService.removeDewarFromShipmentProtectedShipmentsShipmentIdRemoveDewarDewarIdDelete(
|
||||
selectedShipment.id,
|
||||
dewarId
|
||||
);
|
||||
@ -133,7 +135,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
const createdDewar = await DewarsService.createOrUpdateDewarDewarsPost(selectedShipment.id, newDewarToPost);
|
||||
|
||||
if (createdDewar && selectedShipment) {
|
||||
const updatedShipment = await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(selectedShipment.id, createdDewar.id);
|
||||
const updatedShipment = await ShipmentsService.addDewarToShipmentProtectedShipmentsShipmentIdAddDewarPost(selectedShipment.id, createdDewar.id);
|
||||
setSelectedShipment(updatedShipment);
|
||||
setIsAddingDewar(false);
|
||||
setNewDewar(initialNewDewarState);
|
||||
@ -159,7 +161,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
const payload = { comments };
|
||||
|
||||
// Assuming `updateShipmentCommentsShipmentsShipmentIdCommentsPut` only needs the shipment ID
|
||||
const updatedShipment = await ShipmentsService.updateShipmentCommentsShipmentsShipmentIdCommentsPut(selectedShipment.id, payload);
|
||||
const updatedShipment = await ShipmentsService.updateShipmentCommentsProtectedShipmentsShipmentIdCommentsPut(selectedShipment.id, payload);
|
||||
|
||||
setSelectedShipment({ ...selectedShipment, comments: updatedShipment.comments });
|
||||
setInitialComments(comments);
|
||||
@ -350,6 +352,8 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
|
||||
{localSelectedDewar?.id === dewar.id && (
|
||||
<DewarDetails
|
||||
pgroups={pgroups}
|
||||
activePgroup={activePgroup}
|
||||
dewar={localSelectedDewar}
|
||||
trackingNumber={localSelectedDewar?.tracking_number || ''}
|
||||
setTrackingNumber={(value) => {
|
||||
|
@ -11,8 +11,6 @@ import {
|
||||
} from '../../openapi';
|
||||
import { useEffect } from 'react';
|
||||
import { CountryList } from './CountryList'; // Import the list of countries
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
|
||||
|
||||
const MAX_COMMENTS_LENGTH = 200;
|
||||
|
||||
@ -206,13 +204,13 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
|
||||
return_address_id: selectedReturnAddressId!,
|
||||
proposal_id: selectedProposalId!,
|
||||
dewars: newShipment.dewars || [],
|
||||
//pgroup: activePgroup,
|
||||
pgroups: activePgroup,
|
||||
};
|
||||
|
||||
console.log('Shipment Payload being sent:', payload);
|
||||
|
||||
try {
|
||||
await ShipmentsService.createShipmentShipmentsPost(payload);
|
||||
await ShipmentsService.createShipmentProtectedShipmentsPost(payload);
|
||||
setErrorMessage(null);
|
||||
refreshShipments();
|
||||
onCancel();
|
||||
|
@ -58,7 +58,7 @@ const ShipmentPanel: React.FC<ShipmentPanelProps> = ({
|
||||
if (!shipmentId) return;
|
||||
|
||||
try {
|
||||
await ShipmentsService.deleteShipmentShipmentsShipmentIdDelete(shipmentId);
|
||||
await ShipmentsService.deleteShipmentProtectedShipmentsShipmentIdDelete(shipmentId);
|
||||
refreshShipments();
|
||||
selectShipment(null);
|
||||
alert("Shipment deleted successfully.");
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ShipmentsService, Shipment, Contact } from '../../openapi';
|
||||
|
||||
const useShipments = () => {
|
||||
const useShipments = (activePgroup: string) => {
|
||||
const [shipments, setShipments] = useState<Shipment[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [defaultContact, setDefaultContact] = useState<Contact | undefined>();
|
||||
|
||||
const fetchAndSetShipments = async () => {
|
||||
try {
|
||||
const shipmentsData = await ShipmentsService.fetchShipmentsShipmentsGet();
|
||||
const shipmentsData = await ShipmentsService.fetchShipmentsProtectedShipmentsGet(activePgroup);
|
||||
setShipments(shipmentsData);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch shipments:', error);
|
||||
@ -18,7 +18,7 @@ const useShipments = () => {
|
||||
|
||||
const fetchDefaultContact = async () => {
|
||||
try {
|
||||
const contacts = await ShipmentsService.getShipmentContactPersonsShipmentsContactPersonsGet();
|
||||
const contacts = await ShipmentsService.getShipmentContactPersonsProtectedShipmentsContactPersonsGet();
|
||||
setDefaultContact(contacts[0]);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch contact persons:', error);
|
||||
@ -27,9 +27,11 @@ const useShipments = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchAndSetShipments();
|
||||
fetchDefaultContact();
|
||||
}, []);
|
||||
if (activePgroup) {
|
||||
fetchAndSetShipments();
|
||||
fetchDefaultContact();
|
||||
}
|
||||
}, [activePgroup]); // Refetch shipments when activePgroup changes
|
||||
|
||||
return { shipments, error, defaultContact, fetchAndSetShipments };
|
||||
};
|
||||
|
@ -1,17 +1,18 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import ShipmentPanel from '../components/ShipmentPanel';
|
||||
import ShipmentDetails from '../components/ShipmentDetails';
|
||||
import ShipmentForm from '../components/ShipmentForm';
|
||||
import { Dewar, OpenAPI, Shipment } from '../../openapi';
|
||||
import {Dewar, OpenAPI, Shipment} from '../../openapi';
|
||||
import useShipments from '../hooks/useShipments';
|
||||
import { Grid, Container } from '@mui/material';
|
||||
import {Grid, Container} from '@mui/material';
|
||||
|
||||
type ShipmentViewProps = {
|
||||
activePgroup: string;
|
||||
activePgroup: string,
|
||||
pgroups: string,
|
||||
};
|
||||
|
||||
const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
|
||||
const { shipments, error, defaultContact, fetchAndSetShipments } = useShipments();
|
||||
const ShipmentView: React.FC<ShipmentViewProps> = ({activePgroup, pgroups}) => {
|
||||
const { shipments, error, defaultContact, fetchAndSetShipments } = useShipments(activePgroup);
|
||||
const [selectedShipment, setSelectedShipment] = useState<Shipment | null>(null);
|
||||
const [selectedDewar, setSelectedDewar] = useState<Dewar | null>(null);
|
||||
const [isCreatingShipment, setIsCreatingShipment] = useState(false);
|
||||
@ -29,8 +30,8 @@ const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
|
||||
mode === 'test'
|
||||
? import.meta.env.VITE_OPENAPI_BASE_TEST
|
||||
: mode === 'prod'
|
||||
? import.meta.env.VITE_OPENAPI_BASE_PROD
|
||||
: import.meta.env.VITE_OPENAPI_BASE_DEV;
|
||||
? import.meta.env.VITE_OPENAPI_BASE_PROD
|
||||
: import.meta.env.VITE_OPENAPI_BASE_DEV;
|
||||
|
||||
// Log warning if `OpenAPI.BASE` is unresolved
|
||||
if (!OpenAPI.BASE) {
|
||||
@ -68,6 +69,7 @@ const ShipmentView: React.FC<ShipmentViewProps> = ( { activePgroup }) => {
|
||||
if (selectedShipment) {
|
||||
return (
|
||||
<ShipmentDetails
|
||||
pgroups={pgroups}
|
||||
activePgroup={activePgroup}
|
||||
isCreatingShipment={isCreatingShipment}
|
||||
sx={{ flexGrow: 1 }}
|
||||
|
Reference in New Issue
Block a user