added contacts and addresses manager
This commit is contained in:
@ -5,6 +5,8 @@ import ShipmentView from './pages/ShipmentView';
|
||||
import HomePage from './pages/HomeView'; // Assuming this is a default export
|
||||
import ResultsView from './pages/ResultsView';
|
||||
import PlanningView from './pages/PlanningView';
|
||||
import ContactsManager from './pages/ContactsManagerView';
|
||||
import AddressManager from './pages/AddressManagerView';
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
@ -15,9 +17,11 @@ const App: React.FC = () => {
|
||||
<Route path="/shipments" element={<ShipmentView />} />
|
||||
<Route path="/planning" element={<PlanningView />} />
|
||||
<Route path="/results" element={<ResultsView />} />
|
||||
<Route path="/contacts_manager" element={<ContactsManager />} />
|
||||
<Route path="/addresses_manager" element={<AddressManager />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default App;
|
@ -23,6 +23,14 @@ const pages = [
|
||||
{ name: 'Results', path: '/results' }
|
||||
];
|
||||
|
||||
// User menu items
|
||||
const userMenuItems = [
|
||||
{ name: 'My Contacts', path: '/contacts_manager' },
|
||||
{ name: 'My Addresses', path: '/addresses_manager' },
|
||||
{ name: 'DUO', path: '/duo' },
|
||||
{ name: 'Logout', path: '/logout' }
|
||||
];
|
||||
|
||||
const ResponsiveAppBar: React.FC = () => {
|
||||
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
|
||||
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
|
||||
@ -141,8 +149,13 @@ const ResponsiveAppBar: React.FC = () => {
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
<MenuItem onClick={handleCloseUserMenu}>DUO</MenuItem>
|
||||
<MenuItem onClick={handleCloseUserMenu}>Logout</MenuItem>
|
||||
{userMenuItems.map((item) => (
|
||||
<MenuItem key={item.name} onClick={handleCloseUserMenu}>
|
||||
<Link to={item.path} style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
{item.name}
|
||||
</Link>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
@ -152,4 +165,4 @@ const ResponsiveAppBar: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default ResponsiveAppBar;
|
||||
export default ResponsiveAppBar;
|
138
frontend/src/pages/AddressManagerView.tsx
Normal file
138
frontend/src/pages/AddressManagerView.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
import * as React from 'react';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
Container, Typography, List, ListItem, IconButton, TextField, Box, ListItemText, ListItemSecondaryAction
|
||||
} 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';
|
||||
|
||||
interface Address {
|
||||
id: number;
|
||||
street: string;
|
||||
city: string;
|
||||
zipcode: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
const AddressManager: React.FC = () => {
|
||||
const [addresses, setAddresses] = React.useState<Address[]>([]);
|
||||
const [newAddress, setNewAddress] = React.useState<Partial<Address>>({
|
||||
id: 0,
|
||||
street: '',
|
||||
city: '',
|
||||
zipcode: '',
|
||||
country: '',
|
||||
});
|
||||
const [editAddressId, setEditAddressId] = React.useState<number | null>(null);
|
||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchAddresses = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://127.0.0.1:8000/addresses');
|
||||
if (Array.isArray(response.data)) {
|
||||
setAddresses(response.data);
|
||||
} else {
|
||||
setErrorMessage('Failed to load addresses. Expected an array of addresses.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch addresses', error);
|
||||
setErrorMessage('Failed to load addresses. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
fetchAddresses();
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = event.target;
|
||||
setNewAddress({ ...newAddress, [name]: value });
|
||||
};
|
||||
|
||||
const handleAddOrUpdateAddress = async () => {
|
||||
if (editAddressId !== null) {
|
||||
// Update address
|
||||
try {
|
||||
await axios.put(`http://127.0.0.1:8000/addresses/${editAddressId}`, newAddress);
|
||||
setAddresses(addresses.map(address => address.id === editAddressId ? { ...address, ...newAddress } : address));
|
||||
setEditAddressId(null);
|
||||
setNewAddress({ street: '', city: '', zipcode: '', country: '' });
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to update address', error);
|
||||
setErrorMessage('Failed to update address. Please try again later.');
|
||||
}
|
||||
} else {
|
||||
// Add new address
|
||||
try {
|
||||
const response = await axios.post('http://127.0.0.1:8000/addresses', newAddress);
|
||||
setAddresses([...addresses, response.data]);
|
||||
setNewAddress({ street: '', city: '', zipcode: '', country: '' });
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to add address', error);
|
||||
setErrorMessage('Failed to add address. Please try again later.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteAddress = async (id: number) => {
|
||||
try {
|
||||
await axios.delete(`http://127.0.0.1:8000/addresses/${id}`);
|
||||
setAddresses(addresses.filter(address => address.id !== id));
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete address', error);
|
||||
setErrorMessage('Failed to delete address. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditAddress = (address: Address) => {
|
||||
setEditAddressId(address.id);
|
||||
setNewAddress(address);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Addresses Management
|
||||
</Typography>
|
||||
<Box mb={3} display="flex" justifyContent="center" alignItems="center">
|
||||
<TextField label="Street" name="street" value={newAddress.street || ''} onChange={handleInputChange} />
|
||||
<TextField label="City" name="city" value={newAddress.city || ''} onChange={handleInputChange} />
|
||||
<TextField label="Zipcode" name="zipcode" value={newAddress.zipcode || ''} onChange={handleInputChange} />
|
||||
<TextField label="Country" name="country" value={newAddress.country || ''} onChange={handleInputChange} />
|
||||
<IconButton color="primary" onClick={handleAddOrUpdateAddress}>
|
||||
{editAddressId !== null ? <SaveIcon /> : <AddIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
{errorMessage && <Typography color="error">{errorMessage}</Typography>}
|
||||
<List>
|
||||
{addresses.length > 0 ? (
|
||||
addresses.map((address) => (
|
||||
<ListItem key={address.id} button>
|
||||
<ListItemText
|
||||
primary={`${address.street}, ${address.city}`}
|
||||
secondary={`${address.zipcode} - ${address.country}`}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" color="primary" onClick={() => handleEditAddress(address)}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton edge="end" color="secondary" onClick={() => handleDeleteAddress(address.id)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
))
|
||||
) : (
|
||||
<Typography>No addresses found</Typography>
|
||||
)}
|
||||
</List>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddressManager;
|
138
frontend/src/pages/ContactsManagerView.tsx
Normal file
138
frontend/src/pages/ContactsManagerView.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
import * as React from 'react';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
Container, Typography, List, ListItem, IconButton, TextField, Box, ListItemText, ListItemSecondaryAction
|
||||
} 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';
|
||||
|
||||
interface Contact {
|
||||
id: number;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
phone_number: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
const ContactsManager: React.FC = () => {
|
||||
const [contacts, setContacts] = React.useState<Contact[]>([]);
|
||||
const [newContact, setNewContact] = React.useState<Partial<Contact>>({
|
||||
id: 0,
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
phone_number: '',
|
||||
email: '',
|
||||
});
|
||||
const [editContactId, setEditContactId] = React.useState<number | null>(null);
|
||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchContacts = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://127.0.0.1:8000/contacts');
|
||||
if (Array.isArray(response.data)) {
|
||||
setContacts(response.data);
|
||||
} else {
|
||||
setErrorMessage('Failed to load contacts. Expected an array of contacts.');
|
||||
}
|
||||
} 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 () => {
|
||||
if (editContactId !== null) {
|
||||
// Update contact
|
||||
try {
|
||||
await axios.put(`http://127.0.0.1:8000/contacts/${editContactId}`, newContact);
|
||||
setContacts(contacts.map(contact => contact.id === editContactId ? { ...contact, ...newContact } : contact));
|
||||
setEditContactId(null);
|
||||
setNewContact({ firstname: '', lastname: '', phone_number: '', email: '' });
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to update contact', error);
|
||||
setErrorMessage('Failed to update contact. Please try again later.');
|
||||
}
|
||||
} else {
|
||||
// Add new contact
|
||||
try {
|
||||
const response = await axios.post('http://127.0.0.1:8000/contacts', newContact);
|
||||
setContacts([...contacts, response.data]);
|
||||
setNewContact({ firstname: '', lastname: '', phone_number: '', email: '' });
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to add contact', error);
|
||||
setErrorMessage('Failed to add contact. Please try again later.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteContact = async (id: number) => {
|
||||
try {
|
||||
await axios.delete(`http://127.0.0.1:8000/contacts/${id}`);
|
||||
setContacts(contacts.filter(contact => contact.id !== id));
|
||||
setErrorMessage(null);
|
||||
} 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);
|
||||
};
|
||||
|
||||
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={`${contact.phone_number} - ${contact.email}`}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" color="primary" onClick={() => handleEditContact(contact)}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton edge="end" color="secondary" onClick={() => handleDeleteContact(contact.id)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
))
|
||||
) : (
|
||||
<Typography>No contacts found</Typography>
|
||||
)}
|
||||
</List>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactsManager;
|
Reference in New Issue
Block a user