From 0a67d9ac1be24387e5d21289ceae159b3b7cb0d0 Mon Sep 17 00:00:00 2001 From: Lee Calcote Date: Tue, 7 Apr 2026 00:07:23 -0500 Subject: [PATCH 1/2] chore: upgrade @meshery/schemas to v1.0.5 and use View types Replace Pattern stand-in type with proper MesheryViewWithLocation from the dedicated view package in workspace View components. Signed-off-by: Lee Calcote --- package.json | 2 +- .../Workspaces/hooks/useViewsAssignment.tsx | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index bb04bf589..f6612eda9 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@eslint/compat": "^2.0.3", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "^9.0.0", - "@meshery/schemas": "^1.0.4", + "@meshery/schemas": "^1.0.5", "@mui/icons-material": "^7.3.9", "@reduxjs/toolkit": "^2.11.2", "@testing-library/dom": "^10.4.1", diff --git a/src/custom/Workspaces/hooks/useViewsAssignment.tsx b/src/custom/Workspaces/hooks/useViewsAssignment.tsx index 9f274bcf0..f37aaaf8c 100644 --- a/src/custom/Workspaces/hooks/useViewsAssignment.tsx +++ b/src/custom/Workspaces/hooks/useViewsAssignment.tsx @@ -1,9 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; -import { Pattern } from '../../CustomCatalog/CustomCard'; +import type { components } from '@meshery/schemas/constructs/v1beta1/view/View'; import { withDefaultPageArgs } from '../../PerformersSection/PerformersSection'; import { AssignmentHookResult } from '../types'; +type MesheryViewWithLocation = components['schemas']['MesheryViewWithLocation']; + interface AddedAndRemovedViews { addedviewsIds: string[]; removedviewsIds: string[]; @@ -23,16 +25,16 @@ const useViewAssignment = ({ useAssignViewToWorkspaceMutation, useUnassignViewFromWorkspaceMutation, isViewsVisible -}: useViewAssignmentProps): AssignmentHookResult => { +}: useViewAssignmentProps): AssignmentHookResult => { const [viewsPage, setviewsPage] = useState(0); - const [viewsData, setviewsData] = useState([]); + const [viewsData, setviewsData] = useState([]); const viewsPageSize = 25; const [viewsOfWorkspacePage, setviewsOfWorkspacePage] = useState(0); - const [workspaceviewsData, setWorkspaceviewsData] = useState([]); + const [workspaceviewsData, setWorkspaceviewsData] = useState([]); const [assignviewModal, setAssignviewModal] = useState(false); const [skipviews, setSkipviews] = useState(true); const [disableTransferButton, setDisableTransferButton] = useState(true); - const [assignedviews, setAssignedviews] = useState([]); + const [assignedviews, setAssignedviews] = useState([]); const { data: views } = useGetViewsOfWorkspaceQuery( withDefaultPageArgs({ @@ -96,9 +98,9 @@ const useViewAssignment = ({ } }; - const getAddedAndRemovedviews = (allAssignedviews: Pattern[]): AddedAndRemovedViews => { - const originalviewsIds = workspaceviewsData.map((view) => view.id); - const updatedviewsIds = allAssignedviews.map((view) => view.id); + const getAddedAndRemovedviews = (allAssignedviews: MesheryViewWithLocation[]): AddedAndRemovedViews => { + const originalviewsIds = workspaceviewsData.map((view) => view.id).filter((id): id is string => !!id); + const updatedviewsIds = allAssignedviews.map((view) => view.id).filter((id): id is string => !!id); const addedviewsIds = updatedviewsIds.filter((id) => !originalviewsIds.includes(id)); const removedviewsIds = originalviewsIds.filter((id) => !updatedviewsIds.includes(id)); @@ -106,7 +108,7 @@ const useViewAssignment = ({ return { addedviewsIds, removedviewsIds }; }; - const isViewsActivityOccurred = (allViews: Pattern[]): boolean => { + const isViewsActivityOccurred = (allViews: MesheryViewWithLocation[]): boolean => { const { addedviewsIds, removedviewsIds } = getAddedAndRemovedviews(allViews); return addedviewsIds.length > 0 || removedviewsIds.length > 0; }; @@ -135,7 +137,7 @@ const useViewAssignment = ({ handleAssignviewModalClose(); }; - const handleAssignviewsData = (updatedAssignedData: Pattern[]): void => { + const handleAssignviewsData = (updatedAssignedData: MesheryViewWithLocation[]): void => { const { addedviewsIds, removedviewsIds } = getAddedAndRemovedviews(updatedAssignedData); setDisableTransferButton(!(addedviewsIds.length > 0 || removedviewsIds.length > 0)); setAssignedviews(updatedAssignedData); From c508e2006099c5da41ef4965da25a65a5c8c3efc Mon Sep 17 00:00:00 2001 From: Lee Calcote Date: Tue, 7 Apr 2026 00:53:11 -0500 Subject: [PATCH 2/2] fix: filter views without id and use explicit typeof checks in useViewsAssignment Filter out views with undefined id when setting state to prevent unstable React keys downstream. Replace !!id with typeof id === 'string' for explicit type narrowing that correctly handles edge cases. Signed-off-by: Lee Calcote --- src/custom/Workspaces/hooks/useViewsAssignment.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/custom/Workspaces/hooks/useViewsAssignment.tsx b/src/custom/Workspaces/hooks/useViewsAssignment.tsx index f37aaaf8c..249d9edc0 100644 --- a/src/custom/Workspaces/hooks/useViewsAssignment.tsx +++ b/src/custom/Workspaces/hooks/useViewsAssignment.tsx @@ -63,13 +63,15 @@ const useViewAssignment = ({ const [unassignviewFromWorkspace] = useUnassignViewFromWorkspaceMutation(); useEffect(() => { - const viewsDataRtk = views?.views ? views.views : []; - setviewsData((prevData) => [...prevData, ...viewsDataRtk]); + const viewsDataRtk: MesheryViewWithLocation[] = views?.views ? views.views : []; + const viewsWithId = viewsDataRtk.filter((view) => typeof view.id === 'string'); + setviewsData((prevData) => [...prevData, ...viewsWithId]); }, [views]); useEffect(() => { - const viewsOfWorkspaceDataRtk = viewsOfWorkspace?.views ? viewsOfWorkspace.views : []; - setWorkspaceviewsData((prevData) => [...prevData, ...viewsOfWorkspaceDataRtk]); + const viewsOfWorkspaceDataRtk: MesheryViewWithLocation[] = viewsOfWorkspace?.views ? viewsOfWorkspace.views : []; + const viewsWithId = viewsOfWorkspaceDataRtk.filter((view) => typeof view.id === 'string'); + setWorkspaceviewsData((prevData) => [...prevData, ...viewsWithId]); }, [viewsOfWorkspace]); const handleAssignviewModal = (e?: React.MouseEvent): void => { @@ -99,8 +101,8 @@ const useViewAssignment = ({ }; const getAddedAndRemovedviews = (allAssignedviews: MesheryViewWithLocation[]): AddedAndRemovedViews => { - const originalviewsIds = workspaceviewsData.map((view) => view.id).filter((id): id is string => !!id); - const updatedviewsIds = allAssignedviews.map((view) => view.id).filter((id): id is string => !!id); + const originalviewsIds = workspaceviewsData.map((view) => view.id).filter((id): id is string => typeof id === 'string'); + const updatedviewsIds = allAssignedviews.map((view) => view.id).filter((id): id is string => typeof id === 'string'); const addedviewsIds = updatedviewsIds.filter((id) => !originalviewsIds.includes(id)); const removedviewsIds = originalviewsIds.filter((id) => !updatedviewsIds.includes(id));