Chore(ui): AutoPilot status banner behaviour improvements (#20758)

* Modify the AutoPilot status banner visibility banner to hide once closed until and unless the status changes again

* Add test case for asyncDeleteProvider

* Fix the sonarcloud issues

* Fix the failing playwright tests

* Fix the playwright for delete service
This commit is contained in:
Aniket Katkar 2025-04-11 10:41:11 +05:30 committed by GitHub
parent ccee49f24b
commit fc58893239
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 206 additions and 54 deletions

View File

@ -122,8 +122,22 @@ export const deleteService = async (
// Closing the toast notification // Closing the toast notification
await toastNotification(page, /deleted successfully!/, 5 * 60 * 1000); // Wait for up to 5 minutes for the toast notification to appear await toastNotification(page, /deleted successfully!/, 5 * 60 * 1000); // Wait for up to 5 minutes for the toast notification to appear
await page.reload();
await page.waitForLoadState('networkidle');
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
const serviceSearchResponse = page.waitForResponse(
`/api/v1/search/query?q=*${encodeURIComponent(
escapeESReservedCharacters(serviceName)
)}*`
);
await page.fill('[data-testid="searchbar"]', serviceName);
await serviceSearchResponse;
await page.waitForSelector(`[data-testid="service-name-${serviceName}"]`, { await page.waitForSelector(`[data-testid="service-name-${serviceName}"]`, {
state: 'hidden', state: 'detached',
}); });
}; };

View File

@ -172,8 +172,7 @@ const APIEndpointDetails: React.FC<APIEndpointDetailsProps> = ({
); );
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? onToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -102,7 +102,7 @@ const DashboardDetails = ({
dashboardDetails.id dashboardDetails.id
); );
setDashboardPermissions(entityPermission); setDashboardPermissions(entityPermission);
} catch (error) { } catch {
showErrorToast( showErrorToast(
t('server.fetch-entity-permissions-error', { t('server.fetch-entity-permissions-error', {
entity: t('label.dashboard'), entity: t('label.dashboard'),
@ -203,8 +203,7 @@ const DashboardDetails = ({
}; };
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -151,8 +151,7 @@ const DataModelDetails = ({
}; };
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -155,8 +155,7 @@ const MetricDetails: React.FC<MetricDetailsProps> = ({
getFeedCounts(EntityType.METRIC, decodedMetricFqn, handleFeedCount); getFeedCounts(EntityType.METRIC, decodedMetricFqn, handleFeedCount);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push(ROUTES.METRICS),
isSoftDelete ? onToggleDelete(version) : history.push(ROUTES.METRICS),
[] []
); );

View File

@ -97,7 +97,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
mlModelDetail.id mlModelDetail.id
); );
setMlModelPermissions(entityPermission); setMlModelPermissions(entityPermission);
} catch (error) { } catch {
showErrorToast( showErrorToast(
t('server.fetch-entity-permissions-error', { t('server.fetch-entity-permissions-error', {
entity: t('label.ml-model'), entity: t('label.ml-model'),
@ -294,8 +294,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
}, [mlModelDetail, mlModelStoreColumn]); }, [mlModelDetail, mlModelStoreColumn]);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -115,7 +115,7 @@ const PipelineDetails = ({
pipelineDetails.id pipelineDetails.id
); );
setPipelinePermissions(entityPermission); setPipelinePermissions(entityPermission);
} catch (error) { } catch {
showErrorToast( showErrorToast(
t('server.fetch-entity-permissions-error', { t('server.fetch-entity-permissions-error', {
entity: t('label.asset-lowercase'), entity: t('label.asset-lowercase'),
@ -254,8 +254,7 @@ const PipelineDetails = ({
}; };
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -17,7 +17,7 @@ import { AxiosError } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { isUndefined } from 'lodash'; import { isUndefined } from 'lodash';
import { ServiceTypes } from 'Models'; import { ServiceTypes } from 'Models';
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { PLATFORM_INSIGHTS_CHART } from '../../constants/ServiceInsightsTab.constants'; import { PLATFORM_INSIGHTS_CHART } from '../../constants/ServiceInsightsTab.constants';
import { SystemChartType } from '../../enums/DataInsight.enum'; import { SystemChartType } from '../../enums/DataInsight.enum';
@ -27,7 +27,9 @@ import {
getCurrentDayStartGMTinMillis, getCurrentDayStartGMTinMillis,
getDayAgoStartGMTinMillis, getDayAgoStartGMTinMillis,
} from '../../utils/date-time/DateTimeUtils'; } from '../../utils/date-time/DateTimeUtils';
import { updateAutoPilotStatus } from '../../utils/LocalStorageUtils';
import { import {
checkIfAutoPilotStatusIsDismissed,
filterDistributionChartItem, filterDistributionChartItem,
getPlatformInsightsChartDataFormattingMethod, getPlatformInsightsChartDataFormattingMethod,
getStatusIconFromStatusType, getStatusIconFromStatusType,
@ -144,9 +146,40 @@ const ServiceInsightsTab = ({
workflowStatesData?.mainInstanceState?.status workflowStatesData?.mainInstanceState?.status
); );
const showAutoPilotStatus = useMemo(() => {
const isDataPresent =
!isWorkflowStatusLoading && !isUndefined(workflowStatesData);
const isStatusDismissed = checkIfAutoPilotStatusIsDismissed(
serviceDetails.fullyQualifiedName,
workflowStatesData?.mainInstanceState?.status
);
return isDataPresent && !isStatusDismissed;
}, [
isWorkflowStatusLoading,
workflowStatesData,
serviceDetails.fullyQualifiedName,
workflowStatesData?.mainInstanceState?.status,
]);
const onStatusBannerClose = useCallback(() => {
if (
serviceDetails.fullyQualifiedName &&
workflowStatesData?.mainInstanceState?.status
) {
updateAutoPilotStatus({
serviceFQN: serviceDetails.fullyQualifiedName,
status: workflowStatesData?.mainInstanceState?.status,
});
}
}, [
serviceDetails.fullyQualifiedName,
workflowStatesData?.mainInstanceState?.status,
]);
return ( return (
<Row className="service-insights-tab" gutter={[16, 16]}> <Row className="service-insights-tab" gutter={[16, 16]}>
{!isWorkflowStatusLoading && !isUndefined(workflowStatesData) && ( {showAutoPilotStatus && (
<Alert <Alert
closable closable
showIcon showIcon
@ -163,6 +196,7 @@ const ServiceInsightsTab = ({
</div> </div>
} }
message={message} message={message}
onClose={onStatusBannerClose}
/> />
)} )}
{arrayOfWidgets.map( {arrayOfWidgets.map(

View File

@ -241,8 +241,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
getFeedCounts(EntityType.TOPIC, decodedTopicFQN, handleFeedCount); getFeedCounts(EntityType.TOPIC, decodedTopicFQN, handleFeedCount);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -218,6 +218,7 @@ const DeleteWidgetModal = ({
deleteType: values.deleteType, deleteType: values.deleteType,
prepareType, prepareType,
isRecursiveDelete: isRecursiveDelete ?? false, isRecursiveDelete: isRecursiveDelete ?? false,
afterDeleteAction,
}); });
setIsLoading(false); setIsLoading(false);
handleOnEntityDeleteCancel(); handleOnEntityDeleteCancel();
@ -234,6 +235,7 @@ const DeleteWidgetModal = ({
isRecursiveDelete, isRecursiveDelete,
handleOnEntityDeleteConfirm, handleOnEntityDeleteConfirm,
handleOnEntityDeleteCancel, handleOnEntityDeleteCancel,
afterDeleteAction,
] ]
); );

View File

@ -0,0 +1,14 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const LOCAL_STORAGE_AUTO_PILOT_STATUS =
'serviceAutoPilotDismissedStatuses';

View File

@ -24,6 +24,7 @@ export interface DeleteWidgetAsyncFormFields {
deleteType: DeleteType; deleteType: DeleteType;
prepareType: boolean; prepareType: boolean;
isRecursiveDelete: boolean; isRecursiveDelete: boolean;
afterDeleteAction?: (isSoftDelete?: boolean, version?: number) => void;
} }
export interface AsyncDeleteContextType { export interface AsyncDeleteContextType {

View File

@ -30,6 +30,8 @@ jest.mock('../../rest/miscAPI', () => ({
deleteAsyncEntity: jest.fn().mockImplementation(() => Promise.resolve()), deleteAsyncEntity: jest.fn().mockImplementation(() => Promise.resolve()),
})); }));
const mockAfterDeleteAction = jest.fn();
describe('AsyncDeleteProvider', () => { describe('AsyncDeleteProvider', () => {
const mockResponse = { const mockResponse = {
entityName: 'DELETE', entityName: 'DELETE',
@ -95,6 +97,7 @@ describe('AsyncDeleteProvider', () => {
mockError, mockError,
'server.delete-entity-error' 'server.delete-entity-error'
); );
expect(mockAfterDeleteAction).not.toHaveBeenCalled();
}); });
it('should handle websocket response', async () => { it('should handle websocket response', async () => {
@ -158,4 +161,18 @@ describe('AsyncDeleteProvider', () => {
false false
); );
}); });
it('should execute afterDeleteAction if present', async () => {
(deleteAsyncEntity as jest.Mock).mockResolvedValueOnce(mockResponse);
const { result } = renderHook(() => useAsyncDeleteProvider(), { wrapper });
await act(async () => {
await result.current.handleOnAsyncEntityDeleteConfirm({
...mockDeleteParams,
afterDeleteAction: mockAfterDeleteAction,
});
});
expect(mockAfterDeleteAction).toHaveBeenCalledWith(true);
});
}); });

View File

@ -46,6 +46,7 @@ const AsyncDeleteProvider = ({ children }: AsyncDeleteProviderProps) => {
deleteType, deleteType,
prepareType, prepareType,
isRecursiveDelete, isRecursiveDelete,
afterDeleteAction,
}: DeleteWidgetAsyncFormFields) => { }: DeleteWidgetAsyncFormFields) => {
try { try {
const response = await deleteAsyncEntity( const response = await deleteAsyncEntity(
@ -73,6 +74,9 @@ const AsyncDeleteProvider = ({ children }: AsyncDeleteProviderProps) => {
setAsyncDeleteJob(response); setAsyncDeleteJob(response);
asyncDeleteJobRef.current = response; asyncDeleteJobRef.current = response;
showSuccessToast(response.message); showSuccessToast(response.message);
if (afterDeleteAction) {
afterDeleteAction(deleteType === DeleteType.SOFT_DELETE);
}
} catch (error) { } catch (error) {
showErrorToast( showErrorToast(
error as AxiosError, error as AxiosError,

View File

@ -329,8 +329,7 @@ const APICollectionPage: FunctionComponent = () => {
}, [currentVersion, decodedAPICollectionFQN]); }, [currentVersion, decodedAPICollectionFQN]);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -160,7 +160,7 @@ const ContainerPage = () => {
await fetchContainerDetail(containerFQN); await fetchContainerDetail(containerFQN);
getEntityFeedCount(); getEntityFeedCount();
} }
} catch (error) { } catch {
showErrorToast( showErrorToast(
t('server.fetch-entity-permissions-error', { t('server.fetch-entity-permissions-error', {
entity: t('label.asset-lowercase'), entity: t('label.asset-lowercase'),
@ -364,8 +364,7 @@ const ContainerPage = () => {
}; };
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -139,7 +139,7 @@ const DatabaseDetails: FunctionComponent = () => {
decodedDatabaseFQN decodedDatabaseFQN
); );
setDatabasePermission(response); setDatabasePermission(response);
} catch (error) { } catch {
// Error // Error
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@ -368,8 +368,7 @@ const DatabaseDetails: FunctionComponent = () => {
); );
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -332,8 +332,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
}, [currentVersion, decodedDatabaseSchemaFQN]); }, [currentVersion, decodedDatabaseSchemaFQN]);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -108,7 +108,7 @@ function SearchIndexDetailsPage() {
timestamp: 0, timestamp: 0,
id: details.id, id: details.id,
}); });
} catch (error) { } catch {
// Error here // Error here
} finally { } finally {
setLoading(false); setLoading(false);
@ -482,8 +482,7 @@ function SearchIndexDetailsPage() {
}, [version]); }, [version]);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -106,6 +106,7 @@ import {
} from '../../utils/date-time/DateTimeUtils'; } from '../../utils/date-time/DateTimeUtils';
import entityUtilClassBase from '../../utils/EntityUtilClassBase'; import entityUtilClassBase from '../../utils/EntityUtilClassBase';
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils'; import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
import { removeAutoPilotStatus } from '../../utils/LocalStorageUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { import {
getEditConnectionPath, getEditConnectionPath,
@ -961,16 +962,18 @@ const ServiceDetailsPage: FunctionComponent = () => {
}, []); }, []);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => {
isSoftDelete if (!isSoftDelete) {
? handleToggleDelete(version) removeAutoPilotStatus(serviceDetails.fullyQualifiedName ?? '');
: history.push( history.push(
getSettingPath( getSettingPath(
GlobalSettingsMenuCategory.SERVICES, GlobalSettingsMenuCategory.SERVICES,
getServiceRouteFromServiceType(serviceCategory) getServiceRouteFromServiceType(serviceCategory)
) )
), );
[handleToggleDelete, serviceCategory] }
},
[serviceCategory, serviceDetails.fullyQualifiedName]
); );
const handleRestoreService = useCallback(async () => { const handleRestoreService = useCallback(async () => {
@ -1342,6 +1345,11 @@ const ServiceDetailsPage: FunctionComponent = () => {
isWorkflowStatusLoading, isWorkflowStatusLoading,
]); ]);
const afterAutoPilotAppTrigger = useCallback(() => {
removeAutoPilotStatus(serviceDetails.fullyQualifiedName ?? '');
fetchWorkflowInstanceStates();
}, [serviceDetails.fullyQualifiedName, fetchWorkflowInstanceStates]);
if (isLoading) { if (isLoading) {
return <Loader />; return <Loader />;
} }
@ -1367,7 +1375,7 @@ const ServiceDetailsPage: FunctionComponent = () => {
isRecursiveDelete isRecursiveDelete
afterDeleteAction={afterDeleteAction} afterDeleteAction={afterDeleteAction}
afterDomainUpdateAction={afterDomainUpdateAction} afterDomainUpdateAction={afterDomainUpdateAction}
afterTriggerAction={fetchWorkflowInstanceStates} afterTriggerAction={afterAutoPilotAppTrigger}
dataAsset={serviceDetails} dataAsset={serviceDetails}
disableRunAgentsButton={disableRunAgentsButton} disableRunAgentsButton={disableRunAgentsButton}
entityType={entityType} entityType={entityType}

View File

@ -130,7 +130,7 @@ const StoredProcedurePage = () => {
); );
setStoredProcedurePermissions(permission); setStoredProcedurePermissions(permission);
} catch (error) { } catch {
showErrorToast( showErrorToast(
t('server.fetch-entity-permissions-error', { t('server.fetch-entity-permissions-error', {
entity: t('label.resource-permission-lowercase'), entity: t('label.resource-permission-lowercase'),
@ -358,8 +358,7 @@ const StoredProcedurePage = () => {
); );
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );
@ -445,7 +444,7 @@ const StoredProcedurePage = () => {
const tabLabelMap = getTabLabelMapFromTabs(customizedPage?.tabs); const tabLabelMap = getTabLabelMapFromTabs(customizedPage?.tabs);
const tabs = getStoredProcedureDetailsPageTabs({ const tabs = getStoredProcedureDetailsPageTabs({
activeTab: activeTab as EntityTabs, activeTab,
feedCount, feedCount,
decodedStoredProcedureFQN, decodedStoredProcedureFQN,
entityName, entityName,

View File

@ -228,7 +228,7 @@ const TableDetailsPageV1: React.FC = () => {
data.nodes?.filter((node) => node?.fullyQualifiedName !== tableFqn) ?? data.nodes?.filter((node) => node?.fullyQualifiedName !== tableFqn) ??
[]; [];
setDqFailureCount(updatedNodes.length); setDqFailureCount(updatedNodes.length);
} catch (error) { } catch {
setDqFailureCount(0); setDqFailureCount(0);
} }
}; };
@ -259,7 +259,7 @@ const TableDetailsPageV1: React.FC = () => {
} else { } else {
setDqFailureCount(failureCount); setDqFailureCount(failureCount);
} }
} catch (error) { } catch {
setTestCaseSummary(undefined); setTestCaseSummary(undefined);
} }
}; };
@ -274,7 +274,7 @@ const TableDetailsPageV1: React.FC = () => {
entityId: tableDetails.id, entityId: tableDetails.id,
}); });
setQueryCount(response.paging.total); setQueryCount(response.paging.total);
} catch (error) { } catch {
setQueryCount(0); setQueryCount(0);
} }
}; };
@ -324,7 +324,7 @@ const TableDetailsPageV1: React.FC = () => {
); );
setTablePermissions(tablePermission); setTablePermissions(tablePermission);
} catch (error) { } catch {
showErrorToast( showErrorToast(
t('server.fetch-entity-permissions-error', { t('server.fetch-entity-permissions-error', {
entity: t('label.resource-permission-lowercase'), entity: t('label.resource-permission-lowercase'),
@ -664,8 +664,7 @@ const TableDetailsPageV1: React.FC = () => {
}, [version, tableFqn]); }, [version, tableFqn]);
const afterDeleteAction = useCallback( const afterDeleteAction = useCallback(
(isSoftDelete?: boolean, version?: number) => (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'),
isSoftDelete ? handleToggleDelete(version) : history.push('/'),
[] []
); );

View File

@ -0,0 +1,18 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { WorkflowStatus } from '../generated/governance/workflows/workflowInstance';
export interface AutoPilotStatus {
serviceFQN: string;
status: WorkflowStatus;
}

View File

@ -10,7 +10,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { LOCAL_STORAGE_AUTO_PILOT_STATUS } from '../constants/LocalStorage.constants';
import { OM_SESSION_KEY } from '../hooks/useApplicationStore'; import { OM_SESSION_KEY } from '../hooks/useApplicationStore';
import { AutoPilotStatus } from './LocalStorageUtils.interface';
export const getOidcToken = (): string => { export const getOidcToken = (): string => {
return ( return (
@ -38,3 +40,38 @@ export const setRefreshToken = (token: string) => {
session.refreshTokenKey = token; session.refreshTokenKey = token;
localStorage.setItem(OM_SESSION_KEY, JSON.stringify(session)); localStorage.setItem(OM_SESSION_KEY, JSON.stringify(session));
}; };
export const getAutoPilotStatuses = (): Array<AutoPilotStatus> => {
return JSON.parse(
localStorage.getItem(LOCAL_STORAGE_AUTO_PILOT_STATUS) ?? '[]'
);
};
export const updateAutoPilotStatus = (workflowStatus: AutoPilotStatus) => {
const currentStatuses = getAutoPilotStatuses();
// Remove the status if it already exists for the serviceFQN
const filteredStatuses = currentStatuses.filter(
(status) => status.serviceFQN !== workflowStatus.serviceFQN
);
// Add the new status
const updatedStatuses: Array<AutoPilotStatus> = [
...filteredStatuses,
workflowStatus,
];
localStorage.setItem(
LOCAL_STORAGE_AUTO_PILOT_STATUS,
JSON.stringify(updatedStatuses)
);
};
export const removeAutoPilotStatus = (serviceFQN: string) => {
const currentStatuses = getAutoPilotStatuses();
const filteredStatuses = currentStatuses.filter(
(status) => status.serviceFQN !== serviceFQN
);
localStorage.setItem(
LOCAL_STORAGE_AUTO_PILOT_STATUS,
JSON.stringify(filteredStatuses)
);
};

View File

@ -36,6 +36,7 @@ import i18n from '../utils/i18next/LocalUtil';
import { Transi18next } from './CommonUtils'; import { Transi18next } from './CommonUtils';
import documentationLinksClassBase from './DocumentationLinksClassBase'; import documentationLinksClassBase from './DocumentationLinksClassBase';
import Fqn from './Fqn'; import Fqn from './Fqn';
import { getAutoPilotStatuses } from './LocalStorageUtils';
const { t } = i18n; const { t } = i18n;
@ -296,3 +297,19 @@ export const filterDistributionChartItem = (item: {
return toLower(tag_name) === toLower(item.group); return toLower(tag_name) === toLower(item.group);
}; };
export const checkIfAutoPilotStatusIsDismissed = (
serviceFQN?: string,
workflowStatus?: WorkflowStatus
) => {
if (!serviceFQN || !workflowStatus) {
return false;
}
const autoPilotStatuses = getAutoPilotStatuses();
return autoPilotStatuses.some(
(status) =>
status.serviceFQN === serviceFQN && status.status === workflowStatus
);
};