diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx
new file mode 100644
index 0000000000..c3958db7ac
--- /dev/null
+++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx
@@ -0,0 +1,21 @@
+import { memo } from 'react'
+import { Button } from 'react-bootstrap'
+import { useTranslation } from 'react-i18next'
+import { useProjectListContext } from '../../../../context/project-list-context'
+import { unarchiveProject } from '../../../../util/api'
+
+function UnarchiveProjectsButton() {
+ const { selectedProjects, updateProjectViewData } = useProjectListContext()
+ const { t } = useTranslation()
+
+ const handleUnarchiveProjects = async () => {
+ for (const project of selectedProjects) {
+ await unarchiveProject(project.id)
+ updateProjectViewData({ ...project, archived: false, selected: false })
+ }
+ }
+
+ return
+}
+
+export default memo(UnarchiveProjectsButton)
diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx
index c6d96144b7..497a786fc5 100644
--- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx
+++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx
@@ -1,4 +1,4 @@
-import { memo, useCallback } from 'react'
+import { memo } from 'react'
import { Button } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { useProjectListContext } from '../../../../context/project-list-context'
@@ -8,12 +8,12 @@ function UntrashProjectsButton() {
const { selectedProjects, updateProjectViewData } = useProjectListContext()
const { t } = useTranslation()
- const handleUntrashProjects = useCallback(async () => {
- for (const [, project] of Object.entries(selectedProjects)) {
+ const handleUntrashProjects = async () => {
+ for (const project of selectedProjects) {
await untrashProject(project.id)
updateProjectViewData({ ...project, trashed: false, selected: false })
}
- }, [selectedProjects, updateProjectViewData])
+ }
return
}
diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx
index 838722a13b..5dcbaef57a 100644
--- a/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx
+++ b/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx
@@ -4,6 +4,7 @@ import { useProjectListContext } from '../../../context/project-list-context'
import ArchiveProjectsButton from './buttons/archive-projects-button'
import DownloadProjectsButton from './buttons/download-projects-button'
import TrashProjectsButton from './buttons/trash-projects-button'
+import UnarchiveProjectsButton from './buttons/unarchive-projects-button'
import UntrashProjectsButton from './buttons/untrash-projects-button'
function ProjectTools() {
@@ -18,6 +19,7 @@ function ProjectTools() {
{filter === 'trashed' && }
+ {filter === 'archived' && }
)
diff --git a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
index 1d66a33e43..208d5a0cef 100644
--- a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
@@ -4,7 +4,11 @@ import fetchMock from 'fetch-mock'
import sinon from 'sinon'
import ProjectListRoot from '../../../../../frontend/js/features/project-list/components/project-list-root'
import { renderWithProjectListContext } from '../helpers/render-with-context'
-import { owner, makeLongProjectList } from '../fixtures/projects-data'
+import {
+ owner,
+ archivedProjects,
+ makeLongProjectList,
+} from '../fixtures/projects-data'
const { fullList, currentList, trashedList } = makeLongProjectList(40)
const userId = owner.id
@@ -191,10 +195,43 @@ describe('', function () {
// first one is the select all checkbox
fireEvent.click(allCheckboxes[1])
project1Id = allCheckboxes[1].getAttribute('data-project-id')
+
+ actionsToolbar = screen.getAllByRole('toolbar')[0]
})
+
it('does not show the archive button in toolbar when archive view selected', function () {
expect(screen.queryByLabelText('Archive')).to.be.null
})
+
+ it('restores all projects when selected', async function () {
+ fetchMock.delete(`express:/project/:id/archive`, {
+ status: 200,
+ })
+
+ const unarchiveButton =
+ within(actionsToolbar).getByText('Restore')
+ fireEvent.click(unarchiveButton)
+
+ await fetchMock.flush(true)
+ expect(fetchMock.done()).to.be.true
+
+ screen.getByText('No projects')
+ })
+
+ it('only unarchive the selected projects', async function () {
+ // beforeEach selected all, so uncheck the 1st project
+ fireEvent.click(allCheckboxes[1])
+
+ const allCheckboxesChecked = allCheckboxes.filter(c => c.checked)
+ expect(allCheckboxesChecked.length).to.equal(
+ archivedProjects.length - 1
+ )
+
+ await fetchMock.flush(true)
+ expect(fetchMock.done()).to.be.true
+
+ expect(screen.queryByText('No projects')).to.be.null
+ })
})
describe('trashed projects', function () {
diff --git a/services/web/test/frontend/features/project-list/fixtures/projects-data.ts b/services/web/test/frontend/features/project-list/fixtures/projects-data.ts
index e04aa1e148..9f603d6c54 100644
--- a/services/web/test/frontend/features/project-list/fixtures/projects-data.ts
+++ b/services/web/test/frontend/features/project-list/fixtures/projects-data.ts
@@ -132,13 +132,13 @@ export const projectsData: Array = [
trashedAndNotOwnedProject,
]
-export const currentProjects: Array = projectsData.filter(
+export const archivedProjects = projectsData.filter(({ archived }) => archived)
+
+export const currentProjects = projectsData.filter(
({ archived, trashed }) => !archived && !trashed
)
-export const trashedProjects: Array = projectsData.filter(
- ({ trashed }) => trashed
-)
+export const trashedProjects = projectsData.filter(({ trashed }) => trashed)
export const makeLongProjectList = (listLength: number) => {
const longList = [...projectsData]