Skip to content

Commit

Permalink
feat: Update results column, fix applications table (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
callmevladik authored and SergK committed Jan 21, 2025
1 parent 57c1da2 commit b3aaaa1
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 113 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM epamedp/headlamp:0.22.46
FROM epamedp/headlamp:0.22.47

COPY --chown=100:101 assets/ /headlamp/frontend
COPY --chown=100:101 dist/main.js /headlamp/plugins/edp/main.js
Expand Down
70 changes: 46 additions & 24 deletions src/pages/stage-details/components/Applications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useFormContext } from 'react-hook-form';
import { ButtonWithPermission } from '../../../../components/ButtonWithPermission';
import { ConditionalWrapper } from '../../../../components/ConditionalWrapper';
import { Table } from '../../../../components/Table';
import { TableProps } from '../../../../components/Table/types';
import { TabSection } from '../../../../components/TabSection';
import { ICONS } from '../../../../icons/iconify-icons-mapping';
import { useArgoApplicationCRUD } from '../../../../k8s/groups/ArgoCD/Application/hooks/useArgoApplicationCRUD';
Expand Down Expand Up @@ -55,10 +54,17 @@ export const Applications = ({
const [mode, setMode] = React.useState<ApplicationsTableMode>(APPLICATIONS_TABLE_MODE.PREVIEW);
const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);

const toggleMode = () =>
const [dataSnapshot, setDataSnapshot] = React.useState<EnrichedApplicationWithArgoApplication[]>(
[]
);

const toggleMode = React.useCallback(() => {
mode === APPLICATIONS_TABLE_MODE.PREVIEW
? setMode(APPLICATIONS_TABLE_MODE.CONFIGURATION)
: setMode(APPLICATIONS_TABLE_MODE.PREVIEW);
setDataSnapshot(enrichedApplicationsWithArgoApplications);
}, [mode, enrichedApplicationsWithArgoApplications]);

const {
watch,
formState: { dirtyFields },
Expand Down Expand Up @@ -109,32 +115,39 @@ export const Applications = ({
values,
});

const _TableProps: TableProps<EnrichedApplicationWithArgoApplication> = React.useMemo(() => {
return {
data: enrichedApplicationsWithArgoApplications,
isLoading: enrichedApplicationsWithArgoApplications === null,
columns,
upperColumns,
handleSelectRowClick: mode === APPLICATIONS_TABLE_MODE.PREVIEW ? handleClickSelectRow : null,
handleSelectAllClick: mode === APPLICATIONS_TABLE_MODE.PREVIEW ? handleClickSelectAll : null,
selected,
isSelected: (row) => selected.indexOf(row.application.metadata.name) !== -1,
};
}, [
columns,
enrichedApplicationsWithArgoApplications,
handleClickSelectAll,
handleClickSelectRow,
mode,
selected,
upperColumns,
]);

const timer = React.useRef<number | null>(null);
const [deployBtnDisabled, setDeployBtnDisabled] = React.useState(false);
const { reset } = useFormContext();
const theme = useTheme();

const isSelected = React.useCallback(
(row) => selected.indexOf(row.application.metadata.name) !== -1,
[selected]
);

const tableData = React.useMemo(
() =>
mode === APPLICATIONS_TABLE_MODE.PREVIEW
? enrichedApplicationsWithArgoApplications
: dataSnapshot,
[mode, enrichedApplicationsWithArgoApplications, dataSnapshot]
);

const handleClickSelectRowMemo = React.useMemo(
() => (mode === APPLICATIONS_TABLE_MODE.PREVIEW ? handleClickSelectRow : null),
[handleClickSelectRow, mode]
);

const handleClickSelectAllMemo = React.useMemo(
() => (mode === APPLICATIONS_TABLE_MODE.PREVIEW ? handleClickSelectAll : null),
[handleClickSelectAll, mode]
);

const isTableDataLoading = React.useMemo(
() => enrichedApplicationsWithArgoApplications === null,
[enrichedApplicationsWithArgoApplications]
);

return (
<TabSection
title={
Expand Down Expand Up @@ -249,7 +262,16 @@ export const Applications = ({
</Stack>
}
>
<Table<EnrichedApplicationWithArgoApplication> {..._TableProps} />
<Table<EnrichedApplicationWithArgoApplication>
data={tableData}
columns={columns}
isSelected={isSelected}
isLoading={isTableDataLoading}
selected={selected}
upperColumns={upperColumns}
handleSelectRowClick={handleClickSelectRowMemo}
handleSelectAllClick={handleClickSelectAllMemo}
/>
<ApplicationsMultiDeletion
applications={allArgoApplications}
selected={selected}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react';
import { ApplicationKubeObjectInterface } from '../../../k8s/groups/ArgoCD/Application/types';
import { CDPipelineKubeObjectInterface } from '../../../k8s/groups/EDP/CDPipeline/types';
import { EnrichedApplicationWithItsImageStreams } from '../../../k8s/groups/EDP/Codebase/hooks/useEnrichedApplicationsWithImageStreamsQuery';
import { CodebaseImageStreamKubeObjectInterface } from '../../../k8s/groups/EDP/CodebaseImageStream/types';
import { StageKubeObjectInterface } from '../../../k8s/groups/EDP/Stage/types';
import { useDataContext } from '../providers/Data/hooks';
import { useDynamicDataContext } from '../providers/DynamicData/hooks';
import { DataProviderValue } from '../../../types/pages';

const findPreviousStage = (
stages: StageKubeObjectInterface[],
Expand All @@ -11,21 +13,25 @@ const findPreviousStage = (
return stages.find(({ spec: { order: stageOrder } }) => stageOrder === currentStageOrder - 1);
};

export const useEnrichedApplicationsWithArgoApplications = () => {
const {
CDPipeline: { data: CDPipeline },
stages: { data: stages },
enrichedApplications: { data: enrichedApplicationsWithItsImageStreams },
} = useDataContext();
const {
stage: { data: stage, isLoading: isStageLoading },
argoApplications: { data: argoApplications, isLoading: argoApplicationsIsLoading },
} = useDynamicDataContext();

const inputDockerStreams = CDPipeline?.spec.inputDockerStreams;
const appsToPromote = CDPipeline?.spec.applicationsToPromote;
const CDPipelineName = CDPipeline?.metadata.name;
const stageOrder = stage?.spec.order;
export const useEnrichedApplicationsWithArgoApplications = ({
CDPipeline,
stages,
enrichedApplicationsWithItsImageStreams,
stage,
argoApplications,
}: {
CDPipeline: DataProviderValue<CDPipelineKubeObjectInterface>;
stages: DataProviderValue<StageKubeObjectInterface[]>;
enrichedApplicationsWithItsImageStreams: DataProviderValue<
EnrichedApplicationWithItsImageStreams[]
>;
stage: DataProviderValue<StageKubeObjectInterface>;
argoApplications: DataProviderValue<ApplicationKubeObjectInterface[]>;
}) => {
const inputDockerStreams = CDPipeline.data?.spec.inputDockerStreams;
const appsToPromote = CDPipeline.data?.spec.applicationsToPromote;
const CDPipelineName = CDPipeline.data?.metadata.name;
const stageOrder = stage.data?.spec.order;

const normalizedInputDockerStreamNames = inputDockerStreams?.map((el) => el.replaceAll('.', '-'));
const normalizedAppsToPromoteNames = appsToPromote?.map((el) => el.replaceAll('.', '-'));
Expand All @@ -52,7 +58,7 @@ export const useEnrichedApplicationsWithArgoApplications = () => {
);
}

const previousStage = findPreviousStage(stages, order);
const previousStage = findPreviousStage(stages.data, order);

return (
imageStreams &&
Expand All @@ -78,25 +84,25 @@ export const useEnrichedApplicationsWithArgoApplications = () => {
);

return React.useMemo(() => {
if (isStageLoading || argoApplicationsIsLoading) {
if (stage.isLoading || argoApplications.isLoading) {
return null;
}

return (
enrichedApplicationsWithItsImageStreams &&
enrichedApplicationsWithItsImageStreams.length &&
enrichedApplicationsWithItsImageStreams.map((enrichedApplicationWithItsImageStreams) => {
enrichedApplicationsWithItsImageStreams.data &&
enrichedApplicationsWithItsImageStreams.data.length &&
enrichedApplicationsWithItsImageStreams.data.map((enrichedApplicationWithItsImageStreams) => {
const appName = enrichedApplicationWithItsImageStreams.application.metadata.name;
const isPromote = CDPipelineAppsToPromoteSet.has(appName);

const applicationVerifiedImageStream =
enrichedApplicationWithItsImageStreams.applicationImageStreams &&
enrichedApplicationWithItsImageStreams.applicationImageStreams.find(
({ spec: { codebase }, metadata: { name } }) =>
name === `${CDPipelineName}-${stage?.spec.name}-${codebase}-verified`
name === `${CDPipelineName}-${stage.data?.spec.name}-${codebase}-verified`
);

const argoApplicationByCodebaseName = argoApplications.find(
const argoApplicationByCodebaseName = argoApplications.data.find(
(argoApplication) =>
argoApplication.metadata.labels['app.edp.epam.com/app-name'] === appName
);
Expand All @@ -119,15 +125,15 @@ export const useEnrichedApplicationsWithArgoApplications = () => {
})
);
}, [
isStageLoading,
argoApplicationsIsLoading,
enrichedApplicationsWithItsImageStreams,
stage.isLoading,
stage.data?.spec.name,
argoApplications.isLoading,
argoApplications.data,
enrichedApplicationsWithItsImageStreams.data,
CDPipelineAppsToPromoteSet,
argoApplications,
getImageStreamByStageOrder,
stageOrder,
getImageStreamByToPromoteFlag,
CDPipelineName,
stage?.spec.name,
]);
};
63 changes: 44 additions & 19 deletions src/pages/stage-details/hooks/usePageTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ import { LoadingWrapper } from '../../../components/LoadingWrapper';
import { TabSection } from '../../../components/TabSection';
import { PIPELINE_TYPES } from '../../../constants/pipelineTypes';
import { EDP_USER_GUIDE } from '../../../constants/urls';
import { ApplicationKubeObjectInterface } from '../../../k8s/groups/ArgoCD/Application/types';
import { ConfigMapKubeObjectInterface } from '../../../k8s/groups/default/ConfigMap/types';
import { CDPipelineKubeObjectInterface } from '../../../k8s/groups/EDP/CDPipeline/types';
import { EnrichedApplicationWithItsImageStreams } from '../../../k8s/groups/EDP/Codebase/hooks/useEnrichedApplicationsWithImageStreamsQuery';
import { SYSTEM_QUICK_LINKS } from '../../../k8s/groups/EDP/QuickLink/constants';
import { useQuickLinksQuery } from '../../../k8s/groups/EDP/QuickLink/hooks/useQuickLinksQuery';
import { useQuickLinksURLsQuery } from '../../../k8s/groups/EDP/QuickLink/hooks/useQuickLinksURLQuery';
import { QUICK_LINK_LABEL_SELECTOR_TYPE } from '../../../k8s/groups/EDP/QuickLink/labels';
import { StageKubeObjectInterface } from '../../../k8s/groups/EDP/Stage/types';
import { PipelineRunKubeObject } from '../../../k8s/groups/Tekton/PipelineRun';
import { PIPELINE_RUN_REASON } from '../../../k8s/groups/Tekton/PipelineRun/constants';
import { PipelineRunKubeObjectInterface } from '../../../k8s/groups/Tekton/PipelineRun/types';
import { FilterContextProvider } from '../../../providers/Filter/provider';
import { Tab } from '../../../providers/Tabs/components/Tabs/types';
import { DataProviderValue } from '../../../types/pages';
import { getDefaultNamespace } from '../../../utils/getDefaultNamespace';
import { PipelineRunList } from '../../../widgets/PipelineRunList';
import {
Expand All @@ -26,14 +33,38 @@ import {
import { ApplicationsWrapper } from '../components/ApplicationsWrapper';
import { Monitoring } from '../components/Monitoring';
import { Variables } from '../components/Variables';
import { useDataContext } from '../providers/Data/hooks';
import { useDynamicDataContext } from '../providers/DynamicData/hooks';
import { EDPStageDetailsRouteParams } from '../types';
import { useEnrichedApplicationsWithArgoApplications } from './useEnrichedApplicationsWithArgoApplication';
import { useInfoColumns } from './useInfoColumns';
import { useTypedPermissions } from './useTypedPermissions';

export const usePageTabs = (): Tab[] => {
export const usePageTabs = ({
CDPipeline,
stage,
stages,
pipelineRuns,
deployPipelineRuns,
cleanPipelineRuns,
argoApplications,
newPipelineRunAdded,
setNewPipelineRunAdded,
variablesConfigMap,
enrichedApplicationsWithItsImageStreams,
}: {
CDPipeline: DataProviderValue<CDPipelineKubeObjectInterface>;
stage: DataProviderValue<StageKubeObjectInterface>;
stages: DataProviderValue<StageKubeObjectInterface[]>;
pipelineRuns: DataProviderValue<PipelineRunKubeObjectInterface[]>;
deployPipelineRuns: DataProviderValue<PipelineRunKubeObjectInterface[]>;
cleanPipelineRuns: DataProviderValue<PipelineRunKubeObjectInterface[]>;
argoApplications: DataProviderValue<ApplicationKubeObjectInterface[]>;
newPipelineRunAdded: boolean;
setNewPipelineRunAdded: (value: boolean) => void;
variablesConfigMap: DataProviderValue<ConfigMapKubeObjectInterface>;
enrichedApplicationsWithItsImageStreams: DataProviderValue<
EnrichedApplicationWithItsImageStreams[]
>;
}): Tab[] => {
const { namespace, stageName } = useParams<EDPStageDetailsRouteParams>();
const { data: QuickLinksURLS } = useQuickLinksURLsQuery(namespace);
const { data: QuickLinks } = useQuickLinksQuery({
Expand All @@ -46,24 +77,11 @@ export const usePageTabs = (): Tab[] => {
QuickLinks &&
QuickLinks?.items?.find((el) => el.metadata.name === SYSTEM_QUICK_LINKS.MONITORING);

const {
stage,
pipelineRuns,
deployPipelineRuns,
cleanPipelineRuns,
argoApplications,
newPipelineRunAdded,
setNewPipelineRunAdded,
variablesConfigMap,
} = useDynamicDataContext();

const isLoading = React.useMemo(
() => stage.isLoading || deployPipelineRuns.isLoading || argoApplications.isLoading,
[argoApplications.isLoading, deployPipelineRuns.isLoading, stage.isLoading]
);

const { enrichedApplications } = useDataContext();

const latestDeployPipelineRunIsRunning = React.useMemo(() => {
const latestNewDeployPipelineRun = deployPipelineRuns.data?.[0];

Expand Down Expand Up @@ -92,13 +110,20 @@ export const usePageTabs = (): Tab[] => {
);
}, [cleanPipelineRuns]);

const enrichedApplicationsWithArgoApplications = useEnrichedApplicationsWithArgoApplications();
const enrichedApplicationsWithArgoApplications = useEnrichedApplicationsWithArgoApplications({
CDPipeline,
stages,
enrichedApplicationsWithItsImageStreams,
stage,
argoApplications,
});

const permissions = useTypedPermissions();
const infoColumns = useInfoColumns();

return React.useMemo(() => {
const _isLoading = isLoading || enrichedApplications.isLoading || argoApplications.isLoading;
const _isLoading =
isLoading || enrichedApplicationsWithItsImageStreams.isLoading || argoApplications.isLoading;

return [
{
Expand Down Expand Up @@ -207,8 +232,8 @@ export const usePageTabs = (): Tab[] => {
}, [
QuickLinksURLS?.monitoring,
argoApplications.isLoading,
enrichedApplications.isLoading,
enrichedApplicationsWithArgoApplications,
enrichedApplicationsWithItsImageStreams.isLoading,
infoColumns,
isLoading,
latestCleanPipelineRunIsRunning,
Expand Down
21 changes: 2 additions & 19 deletions src/pages/stage-details/providers/DynamicData/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,6 @@ const filterByLabels = (items: KubeObjectInterface[], labels: Record<string, str
);
};

const checkApplicationsForStatusChange = (
oldData: ApplicationKubeObjectInterface[],
newData: ApplicationKubeObjectInterface[]
) => {
return oldData.some((oldApp, index) => {
const newApp = newData[index];
if (!newApp || !oldApp) return false;
const oldStatus = oldApp.status?.health?.status;
const newStatus = newApp.status?.health?.status;
const oldSync = oldApp.status?.sync?.status;
const newSync = newApp.status?.sync?.status;
return oldStatus !== newStatus || oldSync !== newSync;
});
};

const sortFn = (data: PipelineRunKubeObjectInterface[]) =>
data.sort(sortKubeObjectByCreationTimestamp);

Expand Down Expand Up @@ -139,17 +124,15 @@ export const DynamicDataContextProvider: React.FC = ({ children }) => {
stageSpecName,
CDPipelineMetadataName: CDPipelineName,
dataHandler: (newData) => {
if (!applicationList || checkApplicationsForStatusChange(applicationList, newData)) {
setApplicationList(newData);
}
setApplicationList(newData);
},
errorHandler: (error) => console.error(error),
});

return () => {
cancelStream();
};
}, [CDPipelineName, applicationList, namespace, stageSpecName]);
}, [CDPipelineName, namespace, stageSpecName]);

const [stagePods, setStagePods] = React.useState<PodKubeObjectInterface[]>(null);

Expand Down
Loading

0 comments on commit b3aaaa1

Please sign in to comment.