diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx index 936fb20993..e2957a6733 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx @@ -35,10 +35,7 @@ function ArchiveProjectButton({ const handleArchiveProject = useCallback(async () => { await archiveProject(project.id) - - // update view - project.archived = true - updateProjectViewData(project) + updateProjectViewData({ ...project, archived: true, selected: false }) }, [project, updateProjectViewData]) if (project.archived) return null diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx index dd5bc5f129..604a29a497 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx @@ -14,7 +14,8 @@ type CopyButtonProps = { } function CopyProjectButton({ project, children }: CopyButtonProps) { - const { addClonedProjectToViewData } = useProjectListContext() + const { addClonedProjectToViewData, updateProjectViewData } = + useProjectListContext() const { t } = useTranslation() const text = t('copy') const [showModal, setShowModal] = useState(false) @@ -31,16 +32,17 @@ function CopyProjectButton({ project, children }: CopyButtonProps) { }, [isMounted]) const handleAfterCloned = useCallback( - project => { + clonedProject => { eventTracking.send( 'project-list-page-interaction', 'project action', 'Clone' ) - addClonedProjectToViewData(project) + addClonedProjectToViewData(clonedProject) + updateProjectViewData({ ...project, selected: false }) setShowModal(false) }, - [addClonedProjectToViewData] + [addClonedProjectToViewData, project, updateProjectViewData] ) if (project.archived || project.trashed) return null diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx index 69bd99c183..cbe0c4f81d 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx @@ -32,11 +32,12 @@ function TrashProjectButton({ project, children }: TrashProjectButtonProps) { const handleTrashProject = useCallback(async () => { await trashProject(project.id) - - // update view - project.trashed = true - project.archived = false - updateProjectViewData(project) + updateProjectViewData({ + ...project, + trashed: true, + archived: false, + selected: false, + }) }, [project, updateProjectViewData]) if (project.trashed) return null diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx index ea0c258524..f66ac9e4e8 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx @@ -25,9 +25,7 @@ function UnarchiveProjectButton({ const handleUnarchiveProject = useCallback(async () => { await unarchiveProject(project.id) - // update view - project.archived = false - updateProjectViewData(project) + updateProjectViewData({ ...project, archived: false, selected: false }) }, [project, updateProjectViewData]) if (!project.archived) return null diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx index 9caf4949a1..85fd85f7c2 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx @@ -24,9 +24,7 @@ function UntrashProjectButton({ const handleUntrashProject = useCallback(async () => { await untrashProject(project.id) - // update view - project.trashed = false - updateProjectViewData(project) + updateProjectViewData({ ...project, trashed: false, selected: false }) }, [project, updateProjectViewData]) if (!project.trashed) return null diff --git a/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx b/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx index 74e5e1ecc4..bf0c6b6f79 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx @@ -21,8 +21,7 @@ export default function ProjectListTableRow({ const handleCheckboxChange = useCallback( (event: React.ChangeEvent) => { - project.selected = event.target.checked - updateProjectViewData(project) + updateProjectViewData({ ...project, selected: event.target.checked }) }, [project, updateProjectViewData] ) diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx index 12add72f6a..c033a96bf3 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx @@ -28,17 +28,14 @@ function ArchiveProjectsButton() { const handleArchiveProjects = useCallback(async () => { for (const project of selectedProjects) { await archiveProject(project.id) - // update view - project.archived = true - project.selected = false - updateProjectViewData(project) + updateProjectViewData({ ...project, archived: true, selected: false }) } }, [selectedProjects, updateProjectViewData]) return ( <> diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx index 88352c9df9..8e0e29a82d 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx @@ -28,18 +28,19 @@ function TrashProjectsButton() { const handleTrashProjects = useCallback(async () => { for (const project of selectedProjects) { await trashProject(project.id) - // update view - project.trashed = true - project.archived = false - project.selected = false - updateProjectViewData(project) + updateProjectViewData({ + ...project, + trashed: true, + archived: false, + selected: false, + }) } }, [selectedProjects, updateProjectViewData]) return ( <> 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 f53ffd727a..c6d96144b7 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 @@ -11,10 +11,7 @@ function UntrashProjectsButton() { const handleUntrashProjects = useCallback(async () => { for (const [, project] of Object.entries(selectedProjects)) { await untrashProject(project.id) - // update view - project.trashed = false - project.selected = false - updateProjectViewData(project) + updateProjectViewData({ ...project, trashed: false, selected: false }) } }, [selectedProjects, updateProjectViewData]) diff --git a/services/web/frontend/js/features/project-list/context/project-list-context.tsx b/services/web/frontend/js/features/project-list/context/project-list-context.tsx index 6c1186c2b9..e5f2f7d570 100644 --- a/services/web/frontend/js/features/project-list/context/project-list-context.tsx +++ b/services/web/frontend/js/features/project-list/context/project-list-context.tsx @@ -81,7 +81,7 @@ type ProjectListContextValue = { addTag: (tag: Tag) => void renameTag: (tagId: string, newTagName: string) => void deleteTag: (tagId: string) => void - updateProjectViewData: (project: Project) => void + updateProjectViewData: (newProjectData: Project) => void removeProjectFromView: (project: Project) => void removeProjectFromTagInView: (tagId: string, projectId: string) => void searchText: string @@ -245,11 +245,16 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { const selectOrUnselectAllProjects = useCallback( checked => { - const projects = visibleProjects.map(project => { - project.selected = checked - return project - }) - setVisibleProjects(projects) + const visibleProjectIds = visibleProjects.map(p => p.id) + setLoadedProjects(loadedProjects => + loadedProjects.map(p => { + if (visibleProjectIds.includes(p.id)) { + return { ...p, selected: checked } + } else { + return p + } + }) + ) }, [visibleProjects] ) @@ -321,39 +326,38 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { const addClonedProjectToViewData = useCallback( project => { // clone API not using camelCase and does not return all data - project.id = project.project_id + const owner = { id: project.owner?._id, email: project.owner?.email, firstName: project.owner?.first_name, lastName: project.owner?.last_name, } - project.owner = owner - project.lastUpdatedBy = project.owner - project.source = 'owner' - project.trashed = false - project.archived = false - loadedProjects.push(project) - const loadedProjectsSorted = sortProjects(loadedProjects, sort) - const visibleProjectsSorted = sortProjects(visibleProjects, sort) - setVisibleProjects(visibleProjectsSorted) - setLoadedProjects(loadedProjectsSorted) + + const clonedProject = { + ...project, + id: project.project_id, + owner, + lastUpdatedBy: owner, + source: 'owner', + trashed: false, + archived: false, + } + + setLoadedProjects(loadedProjects => { + return sortProjects([...loadedProjects, clonedProject], sort) + }) }, - [loadedProjects, visibleProjects, sort] + [sort] ) - const updateProjectViewData = useCallback( - (project: Project) => { - const projects = loadedProjects.map((p: Project) => { - if (p.id === project.id) { - p = project - } - return p - }) - setLoadedProjects(projects) - }, - [loadedProjects] - ) + const updateProjectViewData = useCallback((newProjectData: Project) => { + setLoadedProjects(loadedProjects => { + return loadedProjects.map((p: Project) => + p.id === newProjectData.id ? { ...newProjectData } : p + ) + }) + }, []) const removeProjectFromView = useCallback( (project: Project) => {