Enhance handling of associated pgroups in Contacts and Address views.

Ensure `associatedPgroups` defaults to an empty array to avoid undefined behavior. Add non-editable pgroups input fields for new items and improve conditional rendering of pgroup chips in both views. Minor structural updates for consistency and clarity.
This commit is contained in:
GotthardG 2025-01-22 16:44:03 +01:00
parent 382b1eaba8
commit 4a1852882a
2 changed files with 62 additions and 49 deletions

View File

@ -49,6 +49,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
const [countrySuggestions, setCountrySuggestions] = React.useState<string[]>([]); const [countrySuggestions, setCountrySuggestions] = React.useState<string[]>([]);
const [addresses, setAddresses] = React.useState<Address[]>([]); const [addresses, setAddresses] = React.useState<Address[]>([]);
const [newAddress, setNewAddress] = React.useState<Partial<Address>>({ const [newAddress, setNewAddress] = React.useState<Partial<Address>>({
pgroups: activePgroup,
house_number: '', house_number: '',
street: '', street: '',
city: '', city: '',
@ -80,13 +81,13 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
try { try {
const response = await AddressesService.getAllAddressesProtectedAddressesAllGet(); const response = await AddressesService.getAllAddressesProtectedAddressesAllGet();
// Preprocess: Add associated and unassociated pgroups // Preprocess: Add associated pgroups with a default value if not available
const transformedAddresses = response.map((address) => { const transformedAddresses = response.map((address) => {
const addressPgroups = address.pgroups?.split(',').map((p) => p.trim()) || []; const addressPgroups = address.pgroups?.split(',').map((p) => p.trim()) || [];
const associatedPgroups = pgroups.filter((pgroup) => addressPgroups.includes(pgroup)); const associatedPgroups = pgroups.filter((pgroup) => addressPgroups.includes(pgroup));
return { return {
...address, ...address,
associatedPgroups, // pgroups linked to the address associatedPgroups: associatedPgroups || [], // Ensure it's always an array
}; };
}); });
@ -240,7 +241,7 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
<TextField <TextField
label="pgroup" label="pgroup"
name="pgroup" name="pgroup"
value={newAddress.activePgroup || ''} value={newAddress.pgroups || ''}
disabled disabled
sx={{ width: '120px' }} // Small fixed-size for non-editable field sx={{ width: '120px' }} // Small fixed-size for non-editable field
/> />
@ -343,7 +344,9 @@ const AddressManager: React.FC<AddressManagerProps> = ({ pgroups, activePgroup }
primary={`${address.house_number}, ${address.street}, ${address.city}`} primary={`${address.house_number}, ${address.street}, ${address.city}`}
secondary={ secondary={
<Box display="flex" flexWrap="wrap"> <Box display="flex" flexWrap="wrap">
{renderPgroupChips(address)} {address.associatedPgroups
? renderPgroupChips(address) // Render chips if associatedPgroups is available
: null}
</Box> </Box>
} }
/> />

View File

@ -37,6 +37,7 @@ interface ContactWithPgroups extends Contact {
const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup }) => { const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup }) => {
const [contacts, setContacts] = React.useState<ContactWithPgroups[]>([]); const [contacts, setContacts] = React.useState<ContactWithPgroups[]>([]);
const [newContact, setNewContact] = React.useState<Partial<Contact>>({ const [newContact, setNewContact] = React.useState<Partial<Contact>>({
pgroups: activePgroup,
firstname: '', firstname: '',
lastname: '', lastname: '',
phone_number: '', phone_number: '',
@ -58,7 +59,7 @@ const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup
const associatedPgroups = pgroups.filter((pgroup) => contactPgroups.includes(pgroup)); const associatedPgroups = pgroups.filter((pgroup) => contactPgroups.includes(pgroup));
return { return {
...contact, ...contact,
associatedPgroups, // pgroups linked to the contact associatedPgroups: associatedPgroups || [], // Ensure associatedPgroups is always an array
}; };
}); });
@ -168,30 +169,30 @@ const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup
const renderPgroupChips = (contact: ContactWithPgroups) => { const renderPgroupChips = (contact: ContactWithPgroups) => {
return pgroups.map((pgroup) => { return pgroups.map((pgroup) => {
const isAssociated = contact.associatedPgroups.includes(pgroup); const isAssociated = (contact.associatedPgroups ?? []).includes(pgroup); // Ensure default empty array
return ( return (
<Chip <Chip
key={pgroup} key={pgroup}
label={pgroup} label={pgroup}
onClick={ onClick={
!isAssociated !isAssociated
? () => togglePgroupAssociation(contact.id, pgroup) ? () => togglePgroupAssociation(contact.id, pgroup)
: undefined : undefined
} }
sx={{ sx={{
backgroundColor: isAssociated ? '#19d238' : '#b0b0b0', backgroundColor: isAssociated ? '#19d238' : '#b0b0b0',
color: 'white', color: 'white',
borderRadius: '8px', borderRadius: '8px',
fontWeight: 'bold', fontWeight: 'bold',
height: '20px', height: '20px',
fontSize: '12px', fontSize: '12px',
boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)',
cursor: isAssociated ? 'default' : 'pointer', // Disable pointer for associated chips cursor: isAssociated ? 'default' : 'pointer', // Disable pointer for associated chips
'&:hover': { opacity: isAssociated ? 1 : 0.8 }, // Disable hover effect for associated chips '&:hover': { opacity: isAssociated ? 1 : 0.8 }, // Disable hover effect for associated chips
mr: 1, mr: 1,
mb: 1, mb: 1,
}} }}
/> />
); );
}); });
}; };
@ -202,6 +203,13 @@ const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup
Contacts Management Contacts Management
</Typography> </Typography>
<Box mb={3} display="flex" justifyContent="center" alignItems="center"> <Box mb={3} display="flex" justifyContent="center" alignItems="center">
<TextField
label="pgroup"
name="pgroup"
value={newContact.pgroups || ''}
disabled
sx={{ width: '120px' }} // Small fixed-size for non-editable field
/>
<TextField <TextField
label="First Name" label="First Name"
name="firstname" name="firstname"
@ -233,28 +241,30 @@ const ContactsManager: React.FC<ContactsManagerProps> = ({ pgroups, activePgroup
{errorMessage && <Typography color="error">{errorMessage}</Typography>} {errorMessage && <Typography color="error">{errorMessage}</Typography>}
<List> <List>
{contacts.length > 0 ? ( {contacts.length > 0 ? (
contacts.map((contact) => ( contacts.map((contact) => (
<ListItem key={contact.id} button> <ListItem key={contact.id} button>
<ListItemText <ListItemText
primary={`${contact.firstname} ${contact.lastname}`} primary={`${contact.firstname} ${contact.lastname}`}
secondary={ secondary={
<Box display="flex" flexWrap="wrap"> <Box display="flex" flexWrap="wrap">
{renderPgroupChips(contact)} {contact.associatedPgroups
</Box> ? renderPgroupChips(contact) // Render chips only if associatedPgroups exists
} : null}
/> </Box>
<ListItemSecondaryAction> }
<IconButton edge="end" color="primary" onClick={() => handleEditContact(contact)}> />
<EditIcon /> <ListItemSecondaryAction>
</IconButton> <IconButton edge="end" color="primary" onClick={() => handleEditContact(contact)}>
<IconButton edge="end" color="secondary" onClick={() => openDialog(contact)}> <EditIcon />
<DeleteIcon /> </IconButton>
</IconButton> <IconButton edge="end" color="secondary" onClick={() => openDialog(contact)}>
</ListItemSecondaryAction> <DeleteIcon />
</ListItem> </IconButton>
)) </ListItemSecondaryAction>
</ListItem>
))
) : ( ) : (
<Typography>No contacts found</Typography> <Typography>No contacts found</Typography>
)} )}
</List> </List>
<Dialog <Dialog