Skip to content

Commit

Permalink
client: Create edit notification channel forms
Browse files Browse the repository at this point in the history
  • Loading branch information
XxRoloxX committed Nov 19, 2024
1 parent d91f47b commit 692e7e0
Show file tree
Hide file tree
Showing 32 changed files with 1,141 additions and 799 deletions.
4 changes: 1 addition & 3 deletions client/src/assets/notification-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
227 changes: 114 additions & 113 deletions client/src/components/EntriesSelector/EntriesSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,133 +1,134 @@
import React, { useState, useEffect } from 'react';
import Table, { TableColumn, TableRow } from 'components/Table/Table';
import Checkbox from 'components/Checkbox/Checkbox';
import ActionButton, { ActionButtonColor } from 'components/ActionButton/ActionButton';
import ActionButton, {
ActionButtonColor,
} from 'components/ActionButton/ActionButton';
import './EntriesSelector.scss';

interface EntriesSelectorProps<T extends TableRow> {
selectedItems: T[];
setSelectedItems: React.Dispatch<React.SetStateAction<T[]>>;
itemsToExclude: T[];
onAdd: () => void;
onClose: () => void;
fetchData: () => Promise<T[]>;
columns: TableColumn<T>[];
getKey: (item: T) => string;
entityLabel: string;
noEntriesMessage?: React.ReactNode;
title?: string;
selectedItems: T[];
setSelectedItems: React.Dispatch<React.SetStateAction<T[]>>;
itemsToExclude: T[];
onAdd: () => void;
onClose: () => void;
fetchData: () => Promise<T[]>;
columns: TableColumn<T>[];
getKey: (item: T) => string;
entityLabel: string;
noEntriesMessage?: React.ReactNode;
title?: string;
}

const EntriesSelector = <T extends TableRow>({
selectedItems,
setSelectedItems,
itemsToExclude,
onAdd,
onClose,
fetchData,
columns,
getKey,
entityLabel,
noEntriesMessage,
title,
}: EntriesSelectorProps<T>): React.ReactNode => {
const [items, setItems] = useState<T[]>([]);
const [selectAll, setSelectAll] = useState(false);
selectedItems,
setSelectedItems,
itemsToExclude,
onAdd,
onClose,
fetchData,
columns,
getKey,
entityLabel,
noEntriesMessage,
title,
}: EntriesSelectorProps<T>): React.ReactNode => {
const [items, setItems] = useState<T[]>([]);
const [selectAll, setSelectAll] = useState(false);

useEffect(() => {
const fetchItems = async () => {
try {
const data = await fetchData();
setItems(data);
} catch (error) {
console.error(`Failed to fetch ${entityLabel}s:`, error);
}
};
fetchItems();
}, [fetchData, entityLabel]);
useEffect(() => {
const fetchItems = async () => {
try {
const data = await fetchData();
setItems(data);
} catch (error) {
console.error(`Failed to fetch ${entityLabel}s:`, error);
}
};
fetchItems();
}, [fetchData, entityLabel]);

const availableItems = items.filter(
(item) => !itemsToExclude.some((excluded) => getKey(excluded) === getKey(item))
);
const availableItems = items.filter(
(item) =>
!itemsToExclude.some((excluded) => getKey(excluded) === getKey(item)),
);

useEffect(() => {
setSelectAll(
availableItems.length > 0 && selectedItems.length === availableItems.length
);
}, [selectedItems, availableItems]);
useEffect(() => {
setSelectAll(
availableItems.length > 0 &&
selectedItems.length === availableItems.length,
);
}, [selectedItems, availableItems]);

const handleSelectAllChange = () => {
setSelectedItems(selectAll ? [] : availableItems);
setSelectAll(!selectAll);
};
const handleSelectAllChange = () => {
setSelectedItems(selectAll ? [] : availableItems);
setSelectAll(!selectAll);
};

const handleCheckboxChange = (item: T) => {
setSelectedItems((prevSelected) => {
const isSelected = prevSelected.some(
(selectedItem) => getKey(selectedItem) === getKey(item)
);
return isSelected
// eslint-disable-next-line max-len
? prevSelected.filter((selectedItem) => getKey(selectedItem) !== getKey(item))
: [...prevSelected, item];
});
};
const handleCheckboxChange = (item: T) => {
setSelectedItems((prevSelected) => {
const isSelected = prevSelected.some(
(selectedItem) => getKey(selectedItem) === getKey(item),
);
return isSelected
? // eslint-disable-next-line max-len
prevSelected.filter(
(selectedItem) => getKey(selectedItem) !== getKey(item),
)
: [...prevSelected, item];
});
};

const updatedColumns: TableColumn<T>[] = [
{
header: (
<Checkbox
checked={selectAll}
onChange={handleSelectAllChange}
/>
),
columnKey: 'checkbox',
customComponent: (row: T) => (
<Checkbox
checked={selectedItems.some(
(selectedItem) => getKey(selectedItem) === getKey(row)
)}
onChange={() => handleCheckboxChange(row)}
/>
),
},
...columns,
];
const updatedColumns: TableColumn<T>[] = [
{
header: <Checkbox checked={selectAll} onChange={handleSelectAllChange} />,
columnKey: 'checkbox',
customComponent: (row: T) => (
<Checkbox
checked={selectedItems.some(
(selectedItem) => getKey(selectedItem) === getKey(row),
)}
onChange={() => handleCheckboxChange(row)}
/>
),
},
...columns,
];

return (
<div className="entries-selector">
{title && <h3 className="entries-selector__title">{title}</h3>}
{availableItems.length === 0 ? (
<div className="entries-selector--no-entries-message">
{noEntriesMessage || <p>No {entityLabel} to add.</p>}
</div>
) : (
<Table
columns={updatedColumns}
rows={availableItems.map((item) => ({
...item,
key: getKey(item),
}))}
maxHeight="65svh"
alignLeft={true}
/>
)}
<div className="entries-selector__buttons">
{availableItems.length > 0 && (
<ActionButton
onClick={onAdd}
description="Add"
color={ActionButtonColor.GREEN}
/>
)}
<ActionButton
onClick={onClose}
description="Close"
color={ActionButtonColor.RED}
/>
</div>
return (
<div className="entries-selector">
{title && <h3 className="entries-selector__title">{title}</h3>}
{availableItems.length === 0 ? (
<div className="entries-selector--no-entries-message">
{noEntriesMessage || <p>No {entityLabel} to add.</p>}
</div>
);
) : (
<Table
columns={updatedColumns}
rows={availableItems.map((item) => ({
...item,
key: getKey(item),
}))}
maxHeight="65svh"
alignLeft={true}
/>
)}
<div className="entries-selector__buttons">
{availableItems.length > 0 && (
<ActionButton
onClick={onAdd}
description="Add"
color={ActionButtonColor.GREEN}
/>
)}
<ActionButton
onClick={onClose}
description="Close"
color={ActionButtonColor.RED}
/>
</div>
</div>
);
};

export default EntriesSelector;
Loading

0 comments on commit 692e7e0

Please sign in to comment.