Add more functionality to TransferBox

This commit is contained in:
Matt DiMeglio 2025-06-04 02:29:10 -04:00
parent fe13b651ed
commit 892134d63b
3 changed files with 213 additions and 89 deletions

View file

@ -5,6 +5,70 @@ import DoNotDisturbIcon from '@mui/icons-material/DoNotDisturb';
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
const OuterShell = styled('div')`
display: flex;
flex-direction: row;
width: 100%;
justify-content: space-around;
`;
const TransferShell = styled('div')`
display: flex;
flex-direction: column;
width: 100%;
align-items: center;
padding: 5px;
`;
const TransferButtonShell = styled('div')`
display: flex;
flex-direction: column;
width: 25%;
align-items: center;
justify-content: center;
`;
const TransferHeader = styled('div')`
padding-bottom: 10px;
`;
const TransferOuterBox = styled('div')`
display: flex;
width: 200px;
height: 250px;
border: 1px solid black;
border-radius: 5px;
background-color: #E8E8E8;
`;
const TransferInnerBox = styled('div')`
display: flex;
flex-direction: column;
padding: 5px;
`;
const TransferBoxItem = styled('div')`
display: flex;
align-items: center;
width: 188px;
padding: 2px 4px;
background-color: ${({ isSelected }) => (isSelected ? '#D3D3D3' : 'transparent')};
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
color: ${({ disabled }) => (disabled ? '#999' : 'inherit')};
border-radius: 4px;
overflow: hidden;
user-select: ${({ disabled }) => (disabled ? 'none' : 'auto')};
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
`;
const TransferBoxLabel = styled('span')`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
`;
const CenterButton = styled('div')`
display: flex;
justify-content: center;
@ -26,7 +90,21 @@ const CenterButton = styled('div')`
`}
`;
export const TransferBox = ({ fields, leftGroup, rightGroup }) => {
const sortByLastThenFirst = (arr) => {
return [...arr].sort((a, b) => {
const [aLast, aFirst] = a.value.split(',').map(s => s.trim());
const [bLast, bFirst] = b.value.split(',').map(s => s.trim());
if (aLast < bLast) return -1;
if (aLast > bLast) return 1;
if (aFirst < bFirst) return -1;
if (aFirst > bFirst) return 1;
return 0;
});
};
export const TransferBox = ({ fields, leftGroup, rightGroup, onSave, user={} }) => {
const {
id,
exclusionList,
@ -35,104 +113,141 @@ export const TransferBox = ({ fields, leftGroup, rightGroup }) => {
} = fields;
const [itemSelected, setItemSelected] = useState({});
const [containsSelection, setContainsSelection] = useState([]);
const [leftItems, setLeftItems] = useState(sortByLastThenFirst(leftGroup || []));
const [rightItems, setRightItems] = useState(sortByLastThenFirst(rightGroup || []));
const handleItemClick = (item, side) => {
setItemSelected({ ...item, from: side });
};
const arraysEqual = (a, b) => {
if (a.length !== b.length) return false;
const aIds = a.map(i => i.id).sort();
const bIds = b.map(i => i.id).sort();
return JSON.stringify(aIds) === JSON.stringify(bIds);
};
const isLeftArrowEnabled = itemSelected?.from === 'right';
const isRightArrowEnabled = itemSelected?.from === 'left';
const hasChanges = !arraysEqual(leftItems, leftGroup) || !arraysEqual(rightItems, rightGroup);
return (
<div style={{ display: 'flex', flexDirection: 'row', width: '100%', justifyContent: 'space-around' }}>
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', alignItems: 'center', padding: '5px' }}>
<div style={{ paddingBottom: '10px' }}>
<OuterShell>
<TransferShell>
<TransferHeader>
<h2>{oldListLabel}</h2>
</div>
<div style={{ display: 'flex', width: '200px', height: '250px', border: '1px solid black', borderRadius: '5px', backgroundColor: '#E8E8E8' }}>
<div
style={{
display: 'flex',
flexDirection: 'column',
padding: '5px',
}}
>
{leftGroup?.map((j, index) => {
</TransferHeader>
<TransferOuterBox>
<TransferInnerBox>
{leftItems?.filter(leftItem => !rightItems.some(rightItem => rightItem.id === leftItem.id))?.map((j) => {
const isUser = user?.id === j?.id;
const isSelected = itemSelected?.id === j?.id && itemSelected?.from === 'left' && !isUser;
return (
<div
key={index}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
width: '80%',
padding: '2px',
<TransferBoxItem
key={j?.id}
isSelected={isSelected}
disabled={isUser}
onClick={() => {
if (isUser) return;
handleItemClick(j, 'left');
}}
title={j?.value}
>
<span>{j?.value}</span>
</div>
<TransferBoxLabel>
{j?.value}
</TransferBoxLabel>
</TransferBoxItem>
)
})}
</div>
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', width: '25%', alignItems: 'center', justifyContent: 'center' }}>
</TransferInnerBox>
</TransferOuterBox>
</TransferShell>
<TransferButtonShell>
<CenterButton
onClick={() => { console.log('clicked Right') }}
onClick={() => {
if (!itemSelected) return;
if (itemSelected.from === 'left') {
setLeftItems(prev => prev.filter(item => item.id !== itemSelected.id));
setRightItems(prev => sortByLastThenFirst([
...rightItems.filter(item => item.id !== itemSelected.id),
itemSelected
]));
}
setItemSelected({});
}}
color='#A8A8A8'
buttonEnabled={itemSelected?.id}
buttonEnabled={isRightArrowEnabled}
>
<KeyboardArrowRightIcon />
</CenterButton>
<CenterButton
onClick={() => { console.log('clicked Clear') }}
onClick={() => {
setLeftItems(sortByLastThenFirst(leftGroup));
setRightItems(sortByLastThenFirst(rightGroup));
setItemSelected({});
}}
color='#FF6666'
buttonEnabled={containsSelection?.length > 0}
buttonEnabled={hasChanges}
>
<DoNotDisturbIcon />
</CenterButton>
<CenterButton
onClick={() => { console.log('clicked Save') }}
onClick={() => {
return onSave(rightItems?.map((item) => { return item?.id }))
}}
color='#00B33C'
buttonEnabled={containsSelection?.length > 0}
buttonEnabled={hasChanges}
>
<CheckIcon />
</CenterButton>
<CenterButton
onClick={() => { console.log('clicked Left') }}
onClick={() => {
if (!itemSelected) return;
if (itemSelected.from === 'right') {
setRightItems(prev => prev.filter(item => item.id !== itemSelected.id));
setLeftItems(prev => sortByLastThenFirst([
...leftItems.filter(item => item.id !== itemSelected.id),
itemSelected
]));
}
setItemSelected({});
}}
color='#A8A8A8'
buttonEnabled={itemSelected?.id}
buttonEnabled={isLeftArrowEnabled}
>
<KeyboardArrowLeftIcon />
</CenterButton>
</div>
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', alignItems: 'center', padding: '5px' }}>
<div style={{ paddingBottom: '10px' }}>
</TransferButtonShell>
<TransferShell>
<TransferHeader>
<h2>{newListLabel}</h2>
</div>
<div style={{ display: 'flex', width: '200px', height: '250px', border: '1px solid black', borderRadius: '5px', backgroundColor: '#E8E8E8' }}>
<div
style={{
display: 'flex',
flexDirection: 'column',
padding: '5px',
}}
>
{rightGroup?.map((j, index) => {
</TransferHeader>
<TransferOuterBox>
<TransferInnerBox>
{rightItems?.map((j) => {
const isUser = user?.id === j?.id;
const isSelected = itemSelected?.id === j?.id && itemSelected?.from === 'right' && !isUser;
return (
<div
key={index}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
width: '80%',
padding: '2px',
<TransferBoxItem
key={j?.id}
isSelected={isSelected}
disabled={isUser}
onClick={() => {
if (isUser) return;
handleItemClick(j, 'right');
}}
title={j?.value}
>
<span>{j?.value}</span>
</div>
<TransferBoxLabel>
{j?.value}
</TransferBoxLabel>
</TransferBoxItem>
)
})}
</div>
</div>
</div>
</div>
</TransferInnerBox>
</TransferOuterBox>
</TransferShell>
</OuterShell>
);
}

View file

@ -285,8 +285,22 @@ export const Settings = () => {
}, [pageValue?.id, windowWidth, window.innerHeight]);
useEffect(() => {
setPageValue(settingsFields?.filter((field) => field?.tab === tabValue?.value)[0]);
}, [tabValue]);
const filteredFields = settingsFields?.filter((field) => {
const hasAccess =
(field?.accessRequired === 'administrator' && isAdministrator) ||
(field?.accessRequired === 'manager' && isManager) ||
(field?.accessRequired === 'scheduler' && isScheduler) ||
(!field?.accessRequired || field?.accessRequired === 'user');
return field?.tab === tabValue?.value && hasAccess;
});
if (filteredFields.length > 0) {
setPageValue(filteredFields[0]);
} else {
setPageValue(null);
}
}, [tabValue, isAdministrator, isManager, isScheduler]);
return (
<div>
@ -433,9 +447,11 @@ export const Settings = () => {
} else if (field?.type === 'transfer') {
fieldType = <TransferBox
style={{ display: 'flex', flexDirection: 'row' }}
user={user}
fields={field}
leftGroup={department[field?.origList]}
rightGroup={department[field?.id]}
onSave={(t) => console.log(t)}
/>
}
return field?.type !== 'header' && (

View file

@ -85,22 +85,22 @@ export const settingsFields = [
title: 'Roles',
tab: 'department',
cards: [
{
id: 'companyAdmins',
label: 'Company Administrators',
fields: [
{
id: 'administrators',
type: 'transfer',
exclusionList: [],
origList: 'users',
oldListLabel: 'Users',
newListLabel: 'Administrators'
},
],
accessRequired: 'administrator',
removeEdit: true
},
// Need to decide if Admins should be able to Add other Admins
// {
// id: 'companyAdmins',
// label: 'Company Administrators',
// fields: [
// {
// id: 'administrators',
// type: 'transfer',
// origList: 'users',
// oldListLabel: 'Users',
// newListLabel: 'Administrators'
// },
// ],
// accessRequired: 'administrator',
// removeEdit: true
// },
{
id: 'companyMgrs',
label: 'Company Managers',
@ -108,9 +108,6 @@ export const settingsFields = [
{
id: 'managers',
type: 'transfer',
exclusionList: [
'administrators'
],
origList: 'users',
oldListLabel: 'Users',
newListLabel: 'Managers'
@ -126,10 +123,6 @@ export const settingsFields = [
{
id: 'schedulers',
type: 'transfer',
exclusionList: [
'administrators',
'managers'
],
origList: 'users',
oldListLabel: 'Users',
newListLabel: 'Schedulers'