Przeglądaj źródła

Add toggle for task groups

tags/Baseline_30082024_FRONTEND_UAT
Wayne 1 rok temu
rodzic
commit
0686c529b1
3 zmienionych plików z 85 dodań i 36 usunięć
  1. +0
    -27
      src/app/api/projects/index.ts
  2. +43
    -8
      src/components/TransferList/MultiSelectList.tsx
  3. +42
    -1
      src/components/TransferList/TransferList.tsx

+ 0
- 27
src/app/api/projects/index.ts Wyświetl plik

@@ -65,30 +65,3 @@ export const fetchProjectCategories = cache(async () => {
}, },
); );
}); });

const mockProjects: ProjectResult[] = [
{
id: 1,
code: "M1001",
name: "Consultancy Project A",
category: "Confirmed Project",
team: "TW",
client: "Client A",
},
{
id: 2,
code: "M1002",
name: "Consultancy Project B",
category: "Project to be bidded",
team: "WY",
client: "Client B",
},
{
id: 3,
code: "S1001",
name: "Consultancy Project C",
category: "Confirmed Project",
team: "WY",
client: "Client C",
},
];

+ 43
- 8
src/components/TransferList/MultiSelectList.tsx Wyświetl plik

@@ -20,6 +20,9 @@ import { LabelGroup, LabelWithId, TransferListProps } from "./TransferList";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import uniqBy from "lodash/uniqBy"; import uniqBy from "lodash/uniqBy";
import groupBy from "lodash/groupBy"; import groupBy from "lodash/groupBy";
import intersectionBy from "lodash/intersectionBy";
import union from "lodash/union";
import differenceBy from "lodash/differenceBy";


export const MultiSelectList: React.FC<TransferListProps> = ({ export const MultiSelectList: React.FC<TransferListProps> = ({
allItems, allItems,
@@ -53,15 +56,28 @@ export const MultiSelectList: React.FC<TransferListProps> = ({
[allItems, onChange], [allItems, onChange],
); );


const handleToggleAll = useCallback(
() => () => {
if (selectedItems.length === allItems.length) {
onChange([]);
const handleToggleAll = useCallback(() => {
if (selectedItems.length === allItems.length) {
onChange([]);
} else {
onChange(allItems);
}
}, [allItems, onChange, selectedItems.length]);

const handleToggleAllInGroup = useCallback(
(groupItems: LabelWithId[]) => () => {
const selectedGroupItems = intersectionBy(
selectedItems,
groupItems,
"id",
);
if (selectedGroupItems.length !== groupItems.length) {
onChange(union(selectedItems, groupItems));
} else { } else {
onChange(allItems);
onChange(differenceBy(selectedItems, groupItems, "id"));
} }
}, },
[allItems, onChange, selectedItems.length],
[onChange, selectedItems],
); );


const { t } = useTranslation(); const { t } = useTranslation();
@@ -117,7 +133,7 @@ export const MultiSelectList: React.FC<TransferListProps> = ({
direction="row" direction="row"
paddingY={1} paddingY={1}
paddingX={3} paddingX={3}
onClick={handleToggleAll()}
onClick={handleToggleAll}
> >
<ListItemIcon> <ListItemIcon>
<Checkbox <Checkbox
@@ -141,11 +157,30 @@ export const MultiSelectList: React.FC<TransferListProps> = ({
</ListSubheader> </ListSubheader>
{groups.flatMap((group) => { {groups.flatMap((group) => {
const groupItems = groupedItems[group.id]; const groupItems = groupedItems[group.id];
const selectedGroupItems = intersectionBy(
selectedItems,
groupItems,
"id",
);
if (!groupItems) return null; if (!groupItems) return null;


return [ return [
<ListSubheader disableSticky key={`${group.id}-${group.name}`}> <ListSubheader disableSticky key={`${group.id}-${group.name}`}>
{group.name}
<Stack
onClick={handleToggleAllInGroup(groupItems)}
direction="row"
paddingX={1}
>
<Checkbox
disableRipple
checked={selectedGroupItems.length === groupItems.length}
indeterminate={
selectedGroupItems.length !== groupItems.length &&
selectedGroupItems.length !== 0
}
/>
{group.name}
</Stack>
</ListSubheader>, </ListSubheader>,
...groupItems.map((item) => { ...groupItems.map((item) => {
return ( return (


+ 42
- 1
src/components/TransferList/TransferList.tsx Wyświetl plik

@@ -19,6 +19,8 @@ import ListSubheader from "@mui/material/ListSubheader";
import groupBy from "lodash/groupBy"; import groupBy from "lodash/groupBy";
import uniqBy from "lodash/uniqBy"; import uniqBy from "lodash/uniqBy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import union from "lodash/union";
import intersectionBy from "lodash/intersectionBy";


export interface LabelGroup { export interface LabelGroup {
id: number; id: number;
@@ -47,6 +49,10 @@ interface ItemListProps {
items: LabelWithId[], items: LabelWithId[],
checkedItems: LabelWithId[], checkedItems: LabelWithId[],
) => React.MouseEventHandler; ) => React.MouseEventHandler;
handleToggleAllInGroup: (
groupItems: LabelWithId[],
checkedItems: LabelWithId[],
) => React.MouseEventHandler;
handleToggle: (item: LabelWithId) => React.MouseEventHandler; handleToggle: (item: LabelWithId) => React.MouseEventHandler;
} }


@@ -56,6 +62,7 @@ const ItemList: React.FC<ItemListProps> = ({
label, label,
handleToggle, handleToggle,
handleToggleAll, handleToggleAll,
handleToggleAllInGroup,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const groups: LabelGroup[] = uniqBy( const groups: LabelGroup[] = uniqBy(
@@ -111,14 +118,34 @@ const ItemList: React.FC<ItemListProps> = ({
> >
{groups.map((group) => { {groups.map((group) => {
const groupItems = groupedItems[group.id]; const groupItems = groupedItems[group.id];
const selectedGroupItems = intersectionBy(
checkedItems,
groupItems,
"id",
);
if (!groupItems) return null; if (!groupItems) return null;


return ( return (
<React.Fragment key={group.id}> <React.Fragment key={group.id}>
<ListSubheader <ListSubheader
disableSticky disableSticky
sx={{ paddingBlock: 2, lineHeight: 1.8 }}
onClick={handleToggleAllInGroup(groupItems, checkedItems)}
sx={{
paddingBlock: 2,
lineHeight: 1.8,
display: "flex",
alignItems: "center",
}}
> >
<ListItemIcon>
<Checkbox
checked={selectedGroupItems.length === groupItems.length}
indeterminate={
selectedGroupItems.length !== groupItems.length &&
selectedGroupItems.length !== 0
}
/>
</ListItemIcon>
{group.name} {group.name}
</ListSubheader> </ListSubheader>
{groupItems.map((item) => { {groupItems.map((item) => {
@@ -210,6 +237,18 @@ const TransferList: React.FC<TransferListProps> = ({
setCheckedList(differenceBy(checkedList, rightListChecked, "id")); setCheckedList(differenceBy(checkedList, rightListChecked, "id"));
}; };


const handleToggleAllInGroup = React.useCallback(
(groupItems: LabelWithId[], checkedItems: LabelWithId[]) => () => {
const selectedGroupItems = intersectionBy(checkedItems, groupItems, "id");
if (selectedGroupItems.length !== groupItems.length) {
setCheckedList(union(checkedList, groupItems));
} else {
setCheckedList(differenceBy(checkedList, groupItems, "id"));
}
},
[checkedList],
);

return ( return (
<Stack spacing={2} direction="row" alignItems="center" position="relative"> <Stack spacing={2} direction="row" alignItems="center" position="relative">
<ItemList <ItemList
@@ -218,6 +257,7 @@ const TransferList: React.FC<TransferListProps> = ({
label={allItemsLabel} label={allItemsLabel}
handleToggleAll={handleToggleAll} handleToggleAll={handleToggleAll}
handleToggle={handleToggle} handleToggle={handleToggle}
handleToggleAllInGroup={handleToggleAllInGroup}
/> />
<ItemList <ItemList
items={rightList} items={rightList}
@@ -225,6 +265,7 @@ const TransferList: React.FC<TransferListProps> = ({
label={selectedItemsLabel} label={selectedItemsLabel}
handleToggleAll={handleToggleAll} handleToggleAll={handleToggleAll}
handleToggle={handleToggle} handleToggle={handleToggle}
handleToggleAllInGroup={handleToggleAllInGroup}
/> />
<Stack <Stack
spacing={1} spacing={1}


Ładowanie…
Anuluj
Zapisz