Skip to content

Commit

Permalink
Fix build tree and add search bar
Browse files Browse the repository at this point in the history
  • Loading branch information
anagperal committed Oct 10, 2024
1 parent f0f21dc commit 8f40b43
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 57 deletions.
29 changes: 21 additions & 8 deletions src/webapp/components/im-team-hierarchy/IMTeamHierarchyItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import { Maybe } from "../../../utils/ts-utils";
import { Checkbox } from "../checkbox/Checkbox";
import { TeamMember } from "../../../domain/entities/incident-management-team/TeamMember";
import { TeamMemberProfile } from "./TeamMemberProfile";
import { IMTeamHierarchyOption } from "./IMTeamHierarchyView";
import { Id } from "../../../domain/entities/Ref";

type IMTeamHierarchyItemProps = {
nodeId: string;
teamRole: string;
member: Maybe<TeamMember>;
selected: boolean;
disabled?: boolean;
onSelectedChange: (nodeId: string, selected: boolean) => void;
children?: React.ReactNode;
subChildren: IMTeamHierarchyOption[];
diseaseOutbreakEventName: string;
selectedItemId: Id;
};

export const IMTeamHierarchyItem: React.FC<IMTeamHierarchyItemProps> = React.memo(props => {
Expand All @@ -26,9 +28,9 @@ export const IMTeamHierarchyItem: React.FC<IMTeamHierarchyItemProps> = React.mem
member,
disabled = false,
onSelectedChange,
selected,
children,
subChildren,
diseaseOutbreakEventName,
selectedItemId,
} = props;

const [openMemberProfile, setOpenMemberProfile] = React.useState(false);
Expand All @@ -43,9 +45,9 @@ export const IMTeamHierarchyItem: React.FC<IMTeamHierarchyItemProps> = React.mem
const onTeamRoleClick = React.useCallback(
(event: React.MouseEvent<Element, MouseEvent>) => {
event.preventDefault();
!disabled && onSelectedChange(nodeId, !selected);
!disabled && onSelectedChange(nodeId, !(selectedItemId === nodeId));
},
[disabled, nodeId, onSelectedChange, selected]
[disabled, nodeId, onSelectedChange, selectedItemId]
);

const onMemberClick = React.useCallback(
Expand All @@ -67,7 +69,7 @@ export const IMTeamHierarchyItem: React.FC<IMTeamHierarchyItemProps> = React.mem
<LabelWrapper>
<Checkbox
id={`team-role-hierarchy-checkbox-${nodeId}`}
checked={selected}
checked={selectedItemId === nodeId}
onChange={onCheckboxChange}
disabled={disabled}
/>
Expand All @@ -84,7 +86,18 @@ export const IMTeamHierarchyItem: React.FC<IMTeamHierarchyItemProps> = React.mem
</LabelWrapper>
}
>
{children}
{subChildren.map(child => (
<IMTeamHierarchyItem
key={child.id}
nodeId={child.id}
teamRole={child.teamRole}
member={child.member}
selectedItemId={selectedItemId}
onSelectedChange={onSelectedChange}
diseaseOutbreakEventName={diseaseOutbreakEventName}
subChildren={child.children}
/>
))}
</StyledIMTeamHierarchyItem>

{member && (
Expand Down
33 changes: 16 additions & 17 deletions src/webapp/components/im-team-hierarchy/IMTeamHierarchyView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import { Maybe } from "../../../utils/ts-utils";
import { TeamMember } from "../../../domain/entities/incident-management-team/TeamMember";
import { IMTeamHierarchyItem } from "./IMTeamHierarchyItem";
import { Id } from "../../../domain/entities/Ref";
import { SearchInput } from "../search-input/SearchInput";

export type IMTeamHierarchyOption = {
id: Id;
teamRole: string;
teamRoleId: Id;
member: Maybe<TeamMember>;
parents: { id: Id; name: string }[];
parent: Maybe<string>;
children: IMTeamHierarchyOption[];
};

Expand All @@ -22,13 +23,23 @@ type IMTeamHierarchyViewProps = {
selectedItemId: Id;
onSelectedItemChange: (nodeId: Id, selected: boolean) => void;
diseaseOutbreakEventName: string;
onSearchChange: (term: string) => void;
searchTerm: string;
};

export const IMTeamHierarchyView: React.FC<IMTeamHierarchyViewProps> = React.memo(props => {
const { onSelectedItemChange, items, selectedItemId, diseaseOutbreakEventName } = props;
const {
onSelectedItemChange,
items,
selectedItemId,
diseaseOutbreakEventName,
searchTerm,
onSearchChange,
} = props;

return (
<IMTeamHierarchyViewContainer>
<SearchInput placeholder="Search" value={searchTerm} onChange={onSearchChange} />
<StyledIMTeamHierarchyView
disableSelection
defaultCollapseIcon={<ArrowDropDown />}
Expand All @@ -40,23 +51,11 @@ export const IMTeamHierarchyView: React.FC<IMTeamHierarchyViewProps> = React.mem
nodeId={item.id}
teamRole={item.teamRole}
member={item.member}
selected={selectedItemId === item.id}
selectedItemId={selectedItemId}
onSelectedChange={onSelectedItemChange}
diseaseOutbreakEventName={diseaseOutbreakEventName}
>
{item.children &&
item.children.map(child => (
<IMTeamHierarchyItem
key={child.id}
nodeId={child.id}
teamRole={child.teamRole}
member={child.member}
selected={selectedItemId === child.id}
onSelectedChange={onSelectedItemChange}
diseaseOutbreakEventName={diseaseOutbreakEventName}
/>
))}
</IMTeamHierarchyItem>
subChildren={item.children}
/>
))}
</StyledIMTeamHierarchyView>
</IMTeamHierarchyViewContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const IMTeamBuilderPage: React.FC = React.memo(() => {
selectedHierarchyItemId,
openDeleteModalData,
disableDeletion,
searchTerm,
onSearchChange,
onSelectHierarchyItem,
goToIncidentManagementTeamRole,
onDeleteIncidentManagementTeamMember,
Expand Down Expand Up @@ -88,6 +90,8 @@ export const IMTeamBuilderPage: React.FC = React.memo(() => {
selectedItemId={selectedHierarchyItemId}
onSelectedItemChange={onSelectHierarchyItem}
diseaseOutbreakEventName={getCurrentEventTracker()?.name || ""}
onSearchChange={onSearchChange}
searchTerm={searchTerm}
/>

<SimpleModal
Expand Down
113 changes: 81 additions & 32 deletions src/webapp/pages/incident-management-team-builder/useIMTeamBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RouteName, useRoutes } from "../../hooks/useRoutes";
import { IncidentManagementTeam } from "../../../domain/entities/incident-management-team/IncidentManagementTeam";
import { TeamMember, TeamRole } from "../../../domain/entities/incident-management-team/TeamMember";
import { RTSL_ZEBRA_INCIDENT_MANAGEMENT_TEAM_BUILDER_ROLE_IDS } from "../../../data/repositories/consts/IncidentManagementTeamBuilderConstants";
import _c from "../../../domain/entities/generic/Collection";

type GlobalMessage = {
text: string;
Expand All @@ -34,6 +35,8 @@ type State = {
openDeleteModalData: ProfileModalData | undefined;
onOpenDeleteModalData: (selectedHierarchyItemId: Id | undefined) => void;
disableDeletion: boolean;
onSearchChange: (term: string) => void;
searchTerm: string;
};

export function useIMTeamBuilder(id: Id): State {
Expand All @@ -51,14 +54,15 @@ export function useIMTeamBuilder(id: Id): State {
const [openDeleteModalData, setOpenDeleteModalData] = useState<ProfileModalData | undefined>(
undefined
);
const [searchTerm, setSearchTerm] = useState<string>("");

const getIncidentManagementTeam = useCallback(() => {
compositionRoot.incidentManagementTeam.get.execute(id).run(
incidentManagementTeam => {
setIncidentManagementTeam(incidentManagementTeam);
setIncidentManagementTeamHierarchyItems(
mapIncidentManagementTeamToIncidentManagementTeamHierarchyItems(
incidentManagementTeam
incidentManagementTeam?.teamHierarchy
)
);
},
Expand Down Expand Up @@ -198,6 +202,28 @@ export function useIMTeamBuilder(id: Id): State {
}
}, [incidentManagementTeam?.teamHierarchy]);

const onSearchChange = useCallback(
(term: string) => {
setSearchTerm(term);

if (incidentManagementTeamHierarchyItems) {
const filteredIncidentManagementTeamHierarchyItems = term
? filterIncidentManagementTeamHierarchy(
incidentManagementTeamHierarchyItems,
term
)
: mapIncidentManagementTeamToIncidentManagementTeamHierarchyItems(
incidentManagementTeam?.teamHierarchy
);

setIncidentManagementTeamHierarchyItems(
filteredIncidentManagementTeamHierarchyItems
);
}
},
[incidentManagementTeam?.teamHierarchy, incidentManagementTeamHierarchyItems]
);

const lastUpdated = getDateAsLocaleDateTimeString(new Date()); //TO DO : Fetch sync time from datastore once implemented

return {
Expand All @@ -212,13 +238,15 @@ export function useIMTeamBuilder(id: Id): State {
openDeleteModalData,
onOpenDeleteModalData,
disableDeletion,
searchTerm,
onSearchChange,
};
}

function mapIncidentManagementTeamToIncidentManagementTeamHierarchyItems(
incidentManagementTeam: Maybe<IncidentManagementTeam>
incidentManagementTeamHierarchy: Maybe<TeamMember[]>
): IMTeamHierarchyOption[] {
if (incidentManagementTeam?.teamHierarchy) {
if (incidentManagementTeamHierarchy) {
const createHierarchyItem = (
item: TeamMember,
teamRole: TeamRole
Expand All @@ -237,11 +265,11 @@ function mapIncidentManagementTeamToIncidentManagementTeamHierarchyItems(
teamRoles: item.teamRoles,
workPosition: item.workPosition,
}),
parents: [],
parent: teamRole.reportsToUsername,
children: [],
});

const teamMap = incidentManagementTeam?.teamHierarchy.reduce<
const teamMap = incidentManagementTeamHierarchy.reduce<
Record<string, IMTeamHierarchyOption>
>((map, item) => {
const hierarchyItems = item.teamRoles?.map(teamRole =>
Expand All @@ -259,34 +287,55 @@ function mapIncidentManagementTeamToIncidentManagementTeamHierarchyItems(
);
}, {});

return incidentManagementTeam.teamHierarchy.reduce<IMTeamHierarchyOption[]>((acc, item) => {
return item.teamRoles
? item.teamRoles.reduce((innerAcc, teamRole) => {
const hierarchyItem = teamMap[teamRole.id];
if (!hierarchyItem) return innerAcc;

const reportsToUsername = teamRole.reportsToUsername;
if (reportsToUsername) {
const parentItem = Object.values(teamMap).find(
teamItem => teamItem.member?.username === reportsToUsername
);

if (parentItem) {
parentItem.children = [...(parentItem.children || []), hierarchyItem];
hierarchyItem.parents = [
...hierarchyItem.parents,
{ id: parentItem.id, name: parentItem.teamRole },
];
}
}

return hierarchyItem.parents.length === 0
? [...innerAcc, hierarchyItem]
: innerAcc;
}, acc)
: acc;
}, []);
return buildTree(teamMap);
} else {
return [];
}
}

function buildTree(teamMap: Record<string, IMTeamHierarchyOption>): IMTeamHierarchyOption[] {
const findChildren = (parentUsername: string): IMTeamHierarchyOption[] =>
Object.values(teamMap)
.filter(item => item.parent === parentUsername)
.reduce<IMTeamHierarchyOption[]>((acc, item) => {
const children = findChildren(item.member?.username || "");
return [...acc, { ...item, children: [...item.children, ...children] }];
}, []);

return Object.values(teamMap).reduce<IMTeamHierarchyOption[]>((acc, item) => {
const isRoot = !item.parent;
if (isRoot) {
const children = findChildren(item.member?.username || "");
return [...acc, { ...item, children: [...item.children, ...children] }];
}

return acc;
}, []);
}

function filterIncidentManagementTeamHierarchy(
items: IMTeamHierarchyOption[],
searchTerm: string
): IMTeamHierarchyOption[] {
return _c(
items.map(item => {
const filteredChildren = filterIncidentManagementTeamHierarchy(
item.children,
searchTerm
);

const isMatch = item.teamRole.toLowerCase().includes(searchTerm.toLowerCase());

if (isMatch || filteredChildren.length > 0) {
return {
...item,
children: filteredChildren,
};
}

return null;
})
)
.compact()
.toArray();
}

0 comments on commit 8f40b43

Please sign in to comment.