From 2e94c6112f75ac4033458383776f4b13da395067 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Fri, 31 Jan 2025 15:44:44 -0500 Subject: [PATCH] [SiteSettings] Redesign project management --- public/locales/en/translation.json | 3 + .../SiteSettings/ProjectManagement/index.tsx | 131 ++++++++++++++---- .../ProjectManagement/tests/index.test.tsx | 35 ++--- 3 files changed, 118 insertions(+), 51 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 2c9c4a90d7..604c53fc56 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -114,6 +114,9 @@ }, "siteSettings": { "projectList": "Projects", + "activeProjects": "Active Projects: {{ val }}", + "archivedProjects": "Archived Projects: {{ val }}", + "manageProject": "Manage this project", "projectRoles": "Project roles", "addProjectUsers": "Add project users", "archiveProjectText": "This project will not be accessible to any users.", diff --git a/src/components/SiteSettings/ProjectManagement/index.tsx b/src/components/SiteSettings/ProjectManagement/index.tsx index 6c84d22077..99b1095058 100644 --- a/src/components/SiteSettings/ProjectManagement/index.tsx +++ b/src/components/SiteSettings/ProjectManagement/index.tsx @@ -1,8 +1,19 @@ -import { List, ListItem, Typography } from "@mui/material"; -import { ReactElement, useEffect, useState } from "react"; +import { Settings } from "@mui/icons-material"; +import { + Divider, + List, + ListItem, + ListItemText, + ListSubheader, + Stack, + Typography, +} from "@mui/material"; +import { ReactElement, ReactNode, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Project } from "api/models"; import { getAllProjects } from "backend"; +import { IconButtonWithTooltip } from "components/Buttons"; import ExportButton from "components/ProjectExport/ExportButton"; import ProjectArchive from "components/ProjectSettings/ProjectArchive"; import ProjectUsersButtonWithConfirmation from "components/SiteSettings/ProjectManagement/ProjectUsersButtonWithConfirmation"; @@ -36,43 +47,111 @@ export default function ProjectManagement(): ReactElement { ); }, [allProjects]); - return ProjectList(activeProjects, archivedProjects, updateProjectList); + return ( + + ); +} + +interface ProjectListProps { + activeProjects: Project[]; + archivedProjects: Project[]; + updateProjects: () => Promise; } // Extract and export for unit testing. -export function ProjectList( - activeProjects: Project[], - archivedProjects: Project[], - updateProjects: () => Promise -): ReactElement { - function getListItems(projects: Project[]): ReactElement[] { - return projects.map((project) => ( - - - {project.name} - - {/* Export Lift file */} +export function ProjectList(props: ProjectListProps): ReactElement { + const [manageProjectId, setManageProjectId] = useState(""); + + const { t } = useTranslation(); + + const toggleManageProjectId = (projId: string): void => { + setManageProjectId(projId === manageProjectId ? "" : projId); + }; + + function getProjectManagement(project: Project): ReactNode { + if (project.id !== manageProjectId) { + return; + } + + return ( + + {/* Export LIFT file */} - {/* Manage project users. */} + + {/* Manage project users */} - {/* Archive active project or restore archived project. */} + + {/* Archive active project or restore archived project */} - - )); + + ); + } + + function getListItems(projects: Project[]): ReactElement[] { + const items: ReactElement[] = []; + for (const project of projects) { + items.push(); + items.push( + + + + + {/* Project name */} + + {project.name} + + + {/* Button to open project management options */} + } + onClick={() => toggleManageProjectId(project.id)} + textId="siteSettings.manageProject" + /> + + + {/* Project management options */} + {getProjectManagement(project)} + + + + ); + } + return items; } return ( - {getListItems(activeProjects)} - {getListItems(archivedProjects)} + {/* Active projects */} + + + {t("siteSettings.activeProjects", { + val: props.activeProjects.length, + })} + + + {getListItems(props.activeProjects)} + + {/* Archived projects */} + + + {t("siteSettings.archivedProjects", { + val: props.archivedProjects.length, + })} + + + {getListItems(props.archivedProjects)} ); } diff --git a/src/components/SiteSettings/ProjectManagement/tests/index.test.tsx b/src/components/SiteSettings/ProjectManagement/tests/index.test.tsx index 4359dfc301..1d675d221a 100644 --- a/src/components/SiteSettings/ProjectManagement/tests/index.test.tsx +++ b/src/components/SiteSettings/ProjectManagement/tests/index.test.tsx @@ -1,21 +1,23 @@ import { ListItem } from "@mui/material"; +import { act } from "react"; import renderer from "react-test-renderer"; -import ExportButton from "components/ProjectExport/ExportButton"; -import ProjectArchive from "components/ProjectSettings/ProjectArchive"; import { ProjectList } from "components/SiteSettings/ProjectManagement"; -import ProjectUsersButtonWithConfirmation from "components/SiteSettings/ProjectManagement/ProjectUsersButtonWithConfirmation"; import { randomProject } from "types/project"; const mockProjects = [randomProject(), randomProject(), randomProject()]; -jest.mock("components/ProjectExport/ExportButton", () => "div"); - let testRenderer: renderer.ReactTestRenderer; -beforeAll(() => { - renderer.act(() => { - testRenderer = renderer.create(ProjectList(mockProjects, [], jest.fn())); +beforeAll(async () => { + await act(async () => { + testRenderer = renderer.create( + + ); }); }); @@ -24,21 +26,4 @@ describe("ProjectList", () => { const projectList = testRenderer.root.findAllByType(ListItem); expect(projectList.length).toEqual(mockProjects.length); }); - - it("Has the right number of export buttons", () => { - const exportButtons = testRenderer.root.findAllByType(ExportButton); - expect(exportButtons.length).toEqual(mockProjects.length); - }); - - it("Has the right number of archive/restore buttons", () => { - const projectButtons = testRenderer.root.findAllByType(ProjectArchive); - expect(projectButtons.length).toEqual(mockProjects.length); - }); - - it("Has the right number of project roles buttons", () => { - const projectButtons = testRenderer.root.findAllByType( - ProjectUsersButtonWithConfirmation - ); - expect(projectButtons.length).toEqual(mockProjects.length); - }); });