-
+
+
{DEPLOYMENT_TEMPLATE_LABELS_KEYS.codeEditor.warning}
)}
-
- {renderContent()}
-
+
+
{renderContent()}
+
{!state.error && (
)}
>
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplateOptionsHeader.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplateOptionsHeader.tsx
new file mode 100644
index 0000000000..dc3e886f8e
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplateOptionsHeader.tsx
@@ -0,0 +1,77 @@
+import { ConfigurationType, InvalidYAMLTippyWrapper, Toggle } from '@devtron-labs/devtron-fe-common-lib'
+import DTChartSelector from './DTChartSelector'
+import { DeploymentTemplateOptionsHeaderProps } from './types'
+
+const DeploymentTemplateOptionsHeader = ({
+ disableVersionSelect,
+ editMode,
+ showReadMe,
+ isUnSet,
+ handleChangeToGUIMode,
+ handleChangeToYAMLMode,
+ parsingError,
+ restoreLastSavedTemplate,
+ handleChartChange,
+ chartDetails,
+ selectedChart,
+ isCompareView,
+ isGuiSupported,
+ areChartsLoading,
+ showDeleteOverrideDraftEmptyState,
+}: DeploymentTemplateOptionsHeaderProps) => {
+ if (isCompareView || showReadMe || showDeleteOverrideDraftEmptyState) {
+ return null
+ }
+
+ const handleToggleEditMode = () => {
+ if (editMode === ConfigurationType.YAML) {
+ handleChangeToGUIMode()
+ return
+ }
+
+ handleChangeToYAMLMode()
+ }
+
+ return (
+
+ {isGuiSupported && (
+ <>
+
+
+
+ >
+ )}
+
+
+
+ )
+}
+
+export default DeploymentTemplateOptionsHeader
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/constants.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/constants.ts
new file mode 100644
index 0000000000..37f58bea07
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/constants.ts
@@ -0,0 +1,51 @@
+import { DOCUMENTATION } from '@Config/constants'
+
+export const BASE_DEPLOYMENT_TEMPLATE_ENV_ID = -1
+export const PROTECT_BASE_DEPLOYMENT_TEMPLATE_IDENTIFIER_DTO = 'BaseDeploymentTemplate' as const
+
+export const CHART_TYPE_TAB_KEYS = { DEVTRON_CHART: 'devtronChart', CUSTOM_CHARTS: 'customCharts' }
+export const CHART_TYPE_TAB = { devtronChart: 'Charts by Devtron', customCharts: 'Custom charts' }
+
+export const CHART_DOCUMENTATION_LINK = {
+ 'Job & CronJob': DOCUMENTATION.JOB_CRONJOB,
+ 'Rollout Deployment': DOCUMENTATION.ROLLOUT,
+ Deployment: DOCUMENTATION.DEPLOYMENT,
+}
+
+export const GUI_VIEW_TEXTS = {
+ SWITCH_TO_ADVANCE_BUTTON_TEXT: 'Switch to advance',
+}
+
+export const DEPLOYMENT_TEMPLATE_LABELS_KEYS = {
+ applicationMetrics: {
+ label: 'Show application metrics',
+ learnMore: 'Learn more',
+ supported:
+ 'Capture and show key application metrics over time. (E.g. Status codes 2xx, 3xx, 5xx; throughput and latency).',
+ notSupported: (selectedChartName: string): string =>
+ `Application metrics is not supported for ${selectedChartName} version.`,
+ },
+ baseTemplate: {
+ key: 'base',
+ label: 'Base deployment template',
+ allowOverrideText:
+ 'Base configurations are being inherited for this environment. Allow override to fork and edit.',
+ },
+ codeEditor: {
+ warning: 'Chart type cannot be changed once saved.',
+ },
+ otherEnv: {
+ key: 'env',
+ label: 'Values on other environments',
+ publishedLabel: 'Published on environments',
+ noOptions: { label: 'No options', value: 0, kind: 'env' },
+ },
+ otherVersion: {
+ key: 'chartVersion',
+ label: 'Default values',
+ version: 'version',
+ noOptions: { label: 'No options', value: 0, kind: 'chartVersion' },
+ },
+}
+
+export const NO_SCOPED_VARIABLES_MESSAGE = 'No valid variable found on this page'
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/deploymentTemplateReducer.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/deploymentTemplateReducer.ts
new file mode 100644
index 0000000000..64f7d95da6
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/deploymentTemplateReducer.ts
@@ -0,0 +1,763 @@
+import YAML from 'yaml'
+import {
+ BaseURLParams,
+ CompareFromApprovalOptionsValuesType,
+ ConfigHeaderTabType,
+ ConfigToolbarPopupNodeType,
+ ConfigurationType,
+ DEFAULT_LOCKED_KEYS_CONFIG,
+ DeploymentChartVersionType,
+ DeploymentTemplateConfigState,
+ DryRunEditorMode,
+ ProtectConfigTabsType,
+ ServerErrors,
+ ToastManager,
+ ToastVariantType,
+ YAMLStringify,
+} from '@devtron-labs/devtron-fe-common-lib'
+import { DeploymentTemplateEditorDataStateType, DeploymentTemplateProps, DeploymentTemplateStateType } from './types'
+import {
+ applyCompareDiffOfTempFormDataOnOriginalData,
+ getCurrentTemplateWithLockedKeys,
+ getEditorTemplateAndLockedKeys,
+} from './utils'
+
+interface InitializeStateBasePayloadType
+ extends Pick<
+ DeploymentTemplateStateType,
+ 'baseDeploymentTemplateData' | 'publishedTemplateData' | 'chartDetails' | 'lockedConfigKeysWithLockType'
+ > {}
+
+interface GetDeploymentTemplateInitialStateParamsType {
+ isSuperAdmin: boolean
+}
+
+export enum DeploymentTemplateActionType {
+ RESET_ALL = 'RESET_ALL',
+ INITIATE_INITIAL_DATA_LOAD = 'INITIATE_INITIAL_DATA_LOAD',
+ INITIAL_DATA_ERROR = 'INITIAL_DATA_ERROR',
+ INITIALIZE_TEMPLATES_WITHOUT_DRAFT = 'INITIALIZE_TEMPLATES_WITHOUT_DRAFT',
+ INITIALIZE_TEMPLATES_WITH_DRAFT = 'INITIALIZE_TEMPLATES_WITH_DRAFT',
+ INITIATE_CHART_CHANGE = 'INITIATE_CHART_CHANGE',
+ CHART_CHANGE_SUCCESS = 'CHART_CHANGE_SUCCESS',
+ CHART_CHANGE_ERROR = 'CHART_CHANGE_ERROR',
+ INITIATE_RESOLVE_SCOPED_VARIABLES = 'INITIATE_RESOLVE_SCOPED_VARIABLES',
+ RESOLVE_SCOPED_VARIABLES = 'RESOLVE_SCOPED_VARIABLES',
+ UN_RESOLVE_SCOPED_VARIABLES = 'UN_RESOLVE_SCOPED_VARIABLES',
+ TOGGLE_DRAFT_COMMENTS = 'TOGGLE_DRAFT_COMMENTS',
+ UPDATE_README_MODE = 'UPDATE_README_MODE',
+ RESTORE_LAST_SAVED_TEMPLATE = 'RESTORE_LAST_SAVED_TEMPLATE',
+ CURRENT_EDITOR_VALUE_CHANGE = 'CURRENT_EDITOR_VALUE_CHANGE',
+ UPDATE_HIDE_LOCKED_KEYS = 'UPDATE_HIDE_LOCKED_KEYS',
+ CHANGE_TO_GUI_MODE = 'CHANGE_TO_GUI_MODE',
+ CHANGE_TO_YAML_MODE = 'CHANGE_TO_YAML_MODE',
+ UPDATE_CONFIG_HEADER_TAB = 'UPDATE_CONFIG_HEADER_TAB',
+ TOGGLE_SHOW_COMPARISON_WITH_MERGED_PATCHES = 'TOGGLE_SHOW_COMPARISON_WITH_MERGED_PATCHES',
+ UPDATE_PROTECTION_VIEW_TAB = 'UPDATE_PROTECTION_VIEW_TAB',
+ UPDATE_DRY_RUN_EDITOR_MODE = 'UPDATE_DRY_RUN_EDITOR_MODE',
+ INITIATE_SAVE = 'INITIATE_SAVE',
+ SAVE_ERROR = 'SAVE_ERROR',
+ FINISH_SAVE = 'FINISH_SAVE',
+ SHOW_EDIT_HISTORY = 'SHOW_EDIT_HISTORY',
+ SHOW_DISCARD_DRAFT_POPUP = 'SHOW_DISCARD_DRAFT_POPUP',
+ CLEAR_POPUP_NODE = 'CLEAR_POPUP_NODE',
+ CHANGE_COMPARE_FROM_SELECTED_OPTION = 'CHANGE_COMPARE_FROM_SELECTED_OPTION',
+ SHOW_LOCKED_DIFF_FOR_APPROVAL = 'SHOW_LOCKED_DIFF_FOR_APPROVAL',
+ TOGGLE_APP_METRICS = 'TOGGLE_APP_METRICS',
+ UPDATE_MERGE_STRATEGY = 'UPDATE_MERGE_STRATEGY',
+ SHOW_DELETE_OVERRIDE_DIALOG = 'SHOW_DELETE_OVERRIDE_DIALOG',
+ DELETE_LOCAL_OVERRIDE = 'DELETE_LOCAL_OVERRIDE',
+ OVERRIDE_TEMPLATE = 'OVERRIDE_TEMPLATE',
+ DELETE_OVERRIDE_CONCURRENT_PROTECTION_ERROR = 'DELETE_OVERRIDE_CONCURRENT_PROTECTION_ERROR',
+ CLOSE_DELETE_DRAFT_OVERRIDE_DIALOG = 'CLOSE_DELETE_DRAFT_OVERRIDE_DIALOG',
+ CLOSE_OVERRIDE_DIALOG = 'CLOSE_OVERRIDE_DIALOG',
+ LOCKED_CHANGES_DETECTED_ON_SAVE = 'LOCKED_CHANGES_DETECTED_ON_SAVE',
+ SHOW_PROTECTED_SAVE_MODAL = 'SHOW_PROTECTED_SAVE_MODAL',
+ CLOSE_SAVE_CHANGES_MODAL = 'CLOSE_SAVE_CHANGES_MODAL',
+ CLOSE_LOCKED_DIFF_MODAL = 'CLOSE_LOCKED_DIFF_MODAL',
+ UPDATE_ARE_COMMENTS_PRESENT = 'UPDATE_ARE_COMMENTS_PRESENT',
+}
+
+type DeploymentTemplateNoPayloadActions =
+ | DeploymentTemplateActionType.INITIATE_INITIAL_DATA_LOAD
+ | DeploymentTemplateActionType.INITIATE_CHART_CHANGE
+ | DeploymentTemplateActionType.CHART_CHANGE_ERROR
+ | DeploymentTemplateActionType.INITIATE_RESOLVE_SCOPED_VARIABLES
+ | DeploymentTemplateActionType.UN_RESOLVE_SCOPED_VARIABLES
+ | DeploymentTemplateActionType.TOGGLE_DRAFT_COMMENTS
+ | DeploymentTemplateActionType.RESTORE_LAST_SAVED_TEMPLATE
+ | DeploymentTemplateActionType.CHANGE_TO_GUI_MODE
+ | DeploymentTemplateActionType.CHANGE_TO_YAML_MODE
+ | DeploymentTemplateActionType.TOGGLE_SHOW_COMPARISON_WITH_MERGED_PATCHES
+ | DeploymentTemplateActionType.INITIATE_SAVE
+ | DeploymentTemplateActionType.SHOW_EDIT_HISTORY
+ | DeploymentTemplateActionType.SHOW_DISCARD_DRAFT_POPUP
+ | DeploymentTemplateActionType.CLEAR_POPUP_NODE
+ | DeploymentTemplateActionType.SHOW_LOCKED_DIFF_FOR_APPROVAL
+ | DeploymentTemplateActionType.TOGGLE_APP_METRICS
+ | DeploymentTemplateActionType.DELETE_LOCAL_OVERRIDE
+ | DeploymentTemplateActionType.OVERRIDE_TEMPLATE
+ | DeploymentTemplateActionType.DELETE_OVERRIDE_CONCURRENT_PROTECTION_ERROR
+ | DeploymentTemplateActionType.CLOSE_DELETE_DRAFT_OVERRIDE_DIALOG
+ | DeploymentTemplateActionType.CLOSE_OVERRIDE_DIALOG
+ | DeploymentTemplateActionType.LOCKED_CHANGES_DETECTED_ON_SAVE
+ | DeploymentTemplateActionType.SHOW_PROTECTED_SAVE_MODAL
+ | DeploymentTemplateActionType.CLOSE_SAVE_CHANGES_MODAL
+ | DeploymentTemplateActionType.CLOSE_LOCKED_DIFF_MODAL
+
+export type DeploymentTemplateActionState =
+ | {
+ type: DeploymentTemplateActionType.RESET_ALL
+ payload: GetDeploymentTemplateInitialStateParamsType
+ }
+ | {
+ type: DeploymentTemplateNoPayloadActions
+ payload?: never
+ }
+ | {
+ type: DeploymentTemplateActionType.INITIAL_DATA_ERROR
+ payload: {
+ error: ServerErrors
+ }
+ }
+ | {
+ type: DeploymentTemplateActionType.INITIALIZE_TEMPLATES_WITHOUT_DRAFT
+ payload: InitializeStateBasePayloadType &
+ Pick
&
+ Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.INITIALIZE_TEMPLATES_WITH_DRAFT
+ payload: InitializeStateBasePayloadType &
+ Pick<
+ DeploymentTemplateStateType,
+ 'currentEditorTemplateData' | 'draftTemplateData' | 'selectedProtectionViewTab'
+ >
+ }
+ | {
+ type: DeploymentTemplateActionType.CHART_CHANGE_SUCCESS
+ payload: {
+ selectedChart: DeploymentChartVersionType
+ selectedChartTemplateDetails: DeploymentTemplateConfigState
+ isEnvView: boolean
+ }
+ }
+ | {
+ type: DeploymentTemplateActionType.RESOLVE_SCOPED_VARIABLES
+ payload: Pick<
+ DeploymentTemplateStateType,
+ 'resolvedEditorTemplate' | 'resolvedOriginalTemplate' | 'resolvedPublishedTemplate'
+ >
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_README_MODE
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.CURRENT_EDITOR_VALUE_CHANGE
+ payload: {
+ template: string
+ }
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_HIDE_LOCKED_KEYS
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_CONFIG_HEADER_TAB
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_PROTECTION_VIEW_TAB
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_DRY_RUN_EDITOR_MODE
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.CHANGE_COMPARE_FROM_SELECTED_OPTION
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_MERGE_STRATEGY
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.SHOW_DELETE_OVERRIDE_DIALOG
+ payload: Pick
+ }
+ | {
+ type: DeploymentTemplateActionType.SAVE_ERROR
+ payload: {
+ isProtectionError: boolean
+ }
+ }
+ | {
+ type: DeploymentTemplateActionType.FINISH_SAVE
+ payload: {
+ isLockConfigError: boolean
+ }
+ }
+ | {
+ type: DeploymentTemplateActionType.UPDATE_ARE_COMMENTS_PRESENT
+ payload: Pick
+ }
+
+export const getDeploymentTemplateInitialState = ({
+ isSuperAdmin,
+}: GetDeploymentTemplateInitialStateParamsType): DeploymentTemplateStateType => ({
+ isLoadingInitialData: true,
+ initialLoadError: null,
+ chartDetails: {
+ charts: [],
+ chartsMetadata: {},
+ globalChartDetails: null,
+ latestAppChartRef: null,
+ },
+ publishedTemplateData: null,
+ draftTemplateData: null,
+ baseDeploymentTemplateData: null,
+ currentEditorTemplateData: null,
+ resolveScopedVariables: false,
+ isResolvingVariables: false,
+ resolvedEditorTemplate: {
+ originalTemplateString: '',
+ templateWithoutLockedKeys: '',
+ },
+ resolvedOriginalTemplate: {
+ originalTemplateString: '',
+ templateWithoutLockedKeys: '',
+ },
+ resolvedPublishedTemplate: {
+ originalTemplateString: '',
+ templateWithoutLockedKeys: '',
+ },
+ wasGuiOrHideLockedKeysEdited: false,
+ showDraftComments: false,
+ hideLockedKeys: false,
+ lockedConfigKeysWithLockType: structuredClone(DEFAULT_LOCKED_KEYS_CONFIG),
+ isSaving: false,
+ lockedDiffModalState: {
+ showLockedDiffForApproval: false,
+ showLockedTemplateDiffModal: false,
+ },
+ showSaveChangesModal: false,
+ popupNodeType: null,
+ compareFromSelectedOptionValue: CompareFromApprovalOptionsValuesType.APPROVAL_PENDING,
+ dryRunEditorMode: DryRunEditorMode.VALUES_FROM_DRAFT,
+ showDeleteOverrideDialog: false,
+ showDeleteDraftOverrideDialog: false,
+ showReadMe: false,
+ editMode: isSuperAdmin ? ConfigurationType.YAML : ConfigurationType.GUI,
+ configHeaderTab: ConfigHeaderTabType.VALUES,
+ shouldMergeTemplateWithPatches: false,
+ selectedProtectionViewTab: ProtectConfigTabsType.EDIT_DRAFT,
+ isLoadingChangedChartDetails: false,
+ areCommentsPresent: false,
+})
+
+const handleSwitchToYAMLMode = (state: DeploymentTemplateStateType): DeploymentTemplateStateType => {
+ if (state.editMode === ConfigurationType.GUI && state.wasGuiOrHideLockedKeysEdited) {
+ try {
+ const editorTemplate = YAMLStringify(
+ applyCompareDiffOfTempFormDataOnOriginalData(
+ YAMLStringify(state.currentEditorTemplateData.originalTemplate),
+ state.currentEditorTemplateData.editorTemplate,
+ ),
+ { simpleKeys: true },
+ )
+
+ return {
+ ...state,
+ editMode: ConfigurationType.YAML,
+ currentEditorTemplateData: {
+ ...state.currentEditorTemplateData,
+ editorTemplate,
+ },
+ }
+ } catch {
+ // Do nothing
+ }
+ }
+
+ return {
+ ...state,
+ editMode: ConfigurationType.YAML,
+ }
+}
+
+const handleUnResolveScopedVariables = (): Pick<
+ DeploymentTemplateStateType,
+ 'isResolvingVariables' | 'resolveScopedVariables'
+> => ({
+ isResolvingVariables: false,
+ resolveScopedVariables: false,
+})
+
+const handleRestoreLastSavedTemplate = (state: DeploymentTemplateStateType): DeploymentTemplateStateType => {
+ const originalTemplateData = state.currentEditorTemplateData.originalTemplateState
+ const stringifiedYAML = originalTemplateData.editorTemplate
+
+ // Since have'nt stored removed patches in global scope so had to re-calculate
+ const { editorTemplate, removedPatches } = getEditorTemplateAndLockedKeys(
+ stringifiedYAML,
+ state.lockedConfigKeysWithLockType.config,
+ )
+
+ const currentEditorTemplateData: typeof state.currentEditorTemplateData = {
+ ...originalTemplateData,
+ editorTemplate: state.hideLockedKeys ? editorTemplate : stringifiedYAML,
+ removedPatches: state.hideLockedKeys ? removedPatches : [],
+ parsingError: '',
+ originalTemplateState: originalTemplateData,
+ }
+
+ // When restoring would restore everything, including schema, readme, etc, that is why not using originalTemplate from currentEditorTemplate
+ return {
+ ...state,
+ ...handleUnResolveScopedVariables(),
+ wasGuiOrHideLockedKeysEdited: state.hideLockedKeys,
+ currentEditorTemplateData,
+ }
+}
+
+const handleReApplyLockedKeys = (state: DeploymentTemplateStateType): DeploymentTemplateStateType => {
+ try {
+ const updatedEditorValue = getCurrentTemplateWithLockedKeys({
+ currentEditorTemplateData: state.currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited: state.wasGuiOrHideLockedKeysEdited,
+ })
+
+ const currentEditorTemplateData: typeof state.currentEditorTemplateData = {
+ ...state.currentEditorTemplateData,
+ editorTemplate: updatedEditorValue,
+ removedPatches: [],
+ }
+
+ return {
+ ...state,
+ hideLockedKeys: false,
+ currentEditorTemplateData,
+ }
+ } catch {
+ ToastManager.showToast({
+ variant: ToastVariantType.error,
+ description: 'Something went wrong while re-applying locked keys',
+ })
+
+ return state
+ }
+}
+
+export const deploymentTemplateReducer = (
+ state: DeploymentTemplateStateType,
+ action: DeploymentTemplateActionState,
+): DeploymentTemplateStateType => {
+ switch (action.type) {
+ case DeploymentTemplateActionType.RESET_ALL:
+ return getDeploymentTemplateInitialState(action.payload)
+
+ case DeploymentTemplateActionType.INITIATE_INITIAL_DATA_LOAD:
+ return {
+ ...state,
+ isLoadingInitialData: true,
+ initialLoadError: null,
+ }
+
+ case DeploymentTemplateActionType.INITIAL_DATA_ERROR:
+ return {
+ ...state,
+ isLoadingInitialData: false,
+ initialLoadError: action.payload.error,
+ }
+
+ case DeploymentTemplateActionType.INITIALIZE_TEMPLATES_WITHOUT_DRAFT: {
+ const {
+ baseDeploymentTemplateData,
+ publishedTemplateData,
+ chartDetails,
+ lockedConfigKeysWithLockType,
+ currentEditorTemplateData,
+ envId,
+ } = action.payload
+
+ return {
+ ...state,
+ baseDeploymentTemplateData,
+ publishedTemplateData,
+ chartDetails,
+ lockedConfigKeysWithLockType,
+ currentEditorTemplateData,
+ configHeaderTab:
+ envId && !publishedTemplateData.isOverridden
+ ? ConfigHeaderTabType.INHERITED
+ : ConfigHeaderTabType.VALUES,
+ isLoadingInitialData: false,
+ initialLoadError: null,
+ }
+ }
+
+ case DeploymentTemplateActionType.INITIALIZE_TEMPLATES_WITH_DRAFT: {
+ const {
+ baseDeploymentTemplateData,
+ publishedTemplateData,
+ chartDetails,
+ lockedConfigKeysWithLockType,
+ draftTemplateData,
+ currentEditorTemplateData,
+ selectedProtectionViewTab,
+ } = action.payload
+
+ return {
+ ...state,
+ baseDeploymentTemplateData,
+ publishedTemplateData,
+ chartDetails,
+ lockedConfigKeysWithLockType,
+ draftTemplateData,
+ currentEditorTemplateData,
+ configHeaderTab: ConfigHeaderTabType.VALUES,
+ selectedProtectionViewTab,
+ areCommentsPresent: draftTemplateData?.latestDraft?.commentsCount > 0,
+ isLoadingInitialData: false,
+ initialLoadError: null,
+ }
+ }
+
+ case DeploymentTemplateActionType.INITIATE_CHART_CHANGE:
+ return {
+ ...state,
+ isLoadingChangedChartDetails: true,
+ }
+
+ case DeploymentTemplateActionType.CHART_CHANGE_SUCCESS: {
+ const { selectedChart, selectedChartTemplateDetails, isEnvView } = action.payload
+ const { id, name, isAppMetricsSupported } = selectedChart
+
+ // We will retain editor values in all cases except when chart type is changed
+ const isChartTypeChanged = state.currentEditorTemplateData.selectedChart.name !== name
+ const currentEditorTemplateData: typeof state.currentEditorTemplateData = {
+ ...state.currentEditorTemplateData,
+ isAppMetricsEnabled: isAppMetricsSupported
+ ? state.currentEditorTemplateData.isAppMetricsEnabled
+ : false,
+ selectedChart,
+ selectedChartRefId: +id,
+ schema: selectedChartTemplateDetails.schema,
+ readme: selectedChartTemplateDetails.readme,
+ guiSchema: selectedChartTemplateDetails.guiSchema,
+
+ ...(isEnvView
+ ? {
+ environmentConfig: selectedChartTemplateDetails.environmentConfig,
+ }
+ : {
+ chartConfig: selectedChartTemplateDetails.chartConfig,
+ }),
+
+ ...(isChartTypeChanged && {
+ editorTemplate: selectedChartTemplateDetails.editorTemplate,
+ parsingError: '',
+ // Not resetting originalTemplate since we are not changing it
+ }),
+ }
+
+ return {
+ ...state,
+ isLoadingChangedChartDetails: false,
+ currentEditorTemplateData,
+ hideLockedKeys: isChartTypeChanged ? false : state.hideLockedKeys,
+ isResolvingVariables: isChartTypeChanged ? false : state.isResolvingVariables,
+ resolveScopedVariables: isChartTypeChanged ? false : state.resolveScopedVariables,
+ }
+ }
+
+ case DeploymentTemplateActionType.CHART_CHANGE_ERROR:
+ return {
+ ...state,
+ isLoadingChangedChartDetails: false,
+ }
+
+ case DeploymentTemplateActionType.INITIATE_RESOLVE_SCOPED_VARIABLES:
+ return {
+ ...state,
+ isResolvingVariables: true,
+ resolveScopedVariables: true,
+ }
+
+ case DeploymentTemplateActionType.UN_RESOLVE_SCOPED_VARIABLES:
+ return {
+ ...state,
+ ...handleUnResolveScopedVariables(),
+ }
+
+ case DeploymentTemplateActionType.RESOLVE_SCOPED_VARIABLES: {
+ const { resolvedEditorTemplate, resolvedOriginalTemplate, resolvedPublishedTemplate } = action.payload
+
+ return {
+ ...state,
+ isResolvingVariables: false,
+ resolvedEditorTemplate,
+ resolvedOriginalTemplate,
+ resolvedPublishedTemplate,
+ }
+ }
+
+ case DeploymentTemplateActionType.TOGGLE_DRAFT_COMMENTS:
+ return {
+ ...state,
+ showDraftComments: !state.showDraftComments,
+ }
+
+ case DeploymentTemplateActionType.RESTORE_LAST_SAVED_TEMPLATE:
+ return handleRestoreLastSavedTemplate(state)
+
+ case DeploymentTemplateActionType.CURRENT_EDITOR_VALUE_CHANGE: {
+ const wasGuiOrHideLockedKeysEdited =
+ state.wasGuiOrHideLockedKeysEdited || state.editMode === ConfigurationType.GUI
+ const { template } = action.payload
+
+ const currentEditorTemplateData: typeof state.currentEditorTemplateData = {
+ ...state.currentEditorTemplateData,
+ editorTemplate: template,
+ parsingError: '',
+ }
+
+ try {
+ YAML.parse(template)
+ } catch (error) {
+ currentEditorTemplateData.parsingError = error.message || 'Unable to parse YAML'
+ }
+
+ return {
+ ...state,
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited,
+ }
+ }
+
+ case DeploymentTemplateActionType.UPDATE_HIDE_LOCKED_KEYS: {
+ const { hideLockedKeys } = action.payload
+
+ if (hideLockedKeys) {
+ const { editorTemplate, removedPatches } = getEditorTemplateAndLockedKeys(
+ state.currentEditorTemplateData.editorTemplate,
+ state.lockedConfigKeysWithLockType.config,
+ )
+
+ const currentEditorTemplateData: typeof state.currentEditorTemplateData = {
+ ...state.currentEditorTemplateData,
+ editorTemplate,
+ removedPatches,
+ }
+
+ return {
+ ...state,
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited: true,
+ hideLockedKeys,
+ }
+ }
+
+ return handleReApplyLockedKeys(state)
+ }
+
+ case DeploymentTemplateActionType.CHANGE_TO_GUI_MODE:
+ return {
+ ...state,
+ editMode: ConfigurationType.GUI,
+ }
+
+ case DeploymentTemplateActionType.CHANGE_TO_YAML_MODE:
+ return handleSwitchToYAMLMode(state)
+
+ case DeploymentTemplateActionType.UPDATE_CONFIG_HEADER_TAB:
+ return {
+ ...state,
+ ...handleUnResolveScopedVariables(),
+ configHeaderTab: action.payload.configHeaderTab,
+ }
+
+ case DeploymentTemplateActionType.TOGGLE_SHOW_COMPARISON_WITH_MERGED_PATCHES:
+ return {
+ ...state,
+ shouldMergeTemplateWithPatches: !state.shouldMergeTemplateWithPatches,
+ }
+
+ case DeploymentTemplateActionType.UPDATE_PROTECTION_VIEW_TAB:
+ return {
+ ...handleReApplyLockedKeys(state),
+ ...handleUnResolveScopedVariables(),
+ selectedProtectionViewTab: action.payload.selectedProtectionViewTab,
+ }
+
+ case DeploymentTemplateActionType.UPDATE_DRY_RUN_EDITOR_MODE:
+ return {
+ ...handleReApplyLockedKeys(state),
+ ...handleUnResolveScopedVariables(),
+ dryRunEditorMode: action.payload.dryRunEditorMode,
+ }
+
+ case DeploymentTemplateActionType.INITIATE_SAVE:
+ return {
+ ...state,
+ isSaving: true,
+ }
+
+ case DeploymentTemplateActionType.SHOW_EDIT_HISTORY:
+ return {
+ ...state,
+ popupNodeType: ConfigToolbarPopupNodeType.EDIT_HISTORY,
+ }
+
+ case DeploymentTemplateActionType.SHOW_DISCARD_DRAFT_POPUP:
+ return {
+ ...state,
+ popupNodeType: ConfigToolbarPopupNodeType.DISCARD_DRAFT,
+ }
+
+ case DeploymentTemplateActionType.CLEAR_POPUP_NODE:
+ return {
+ ...state,
+ popupNodeType: null,
+ }
+
+ case DeploymentTemplateActionType.UPDATE_README_MODE:
+ return {
+ ...handleSwitchToYAMLMode(state),
+ ...handleUnResolveScopedVariables(),
+ showReadMe: action.payload.showReadMe,
+ }
+
+ case DeploymentTemplateActionType.CHANGE_COMPARE_FROM_SELECTED_OPTION:
+ return {
+ ...state,
+ ...handleUnResolveScopedVariables(),
+ compareFromSelectedOptionValue: action.payload.compareFromSelectedOptionValue,
+ }
+
+ case DeploymentTemplateActionType.SHOW_LOCKED_DIFF_FOR_APPROVAL:
+ return {
+ ...state,
+ lockedDiffModalState: {
+ showLockedDiffForApproval: true,
+ showLockedTemplateDiffModal: true,
+ },
+ }
+
+ case DeploymentTemplateActionType.TOGGLE_APP_METRICS:
+ return {
+ ...state,
+ currentEditorTemplateData: {
+ ...state.currentEditorTemplateData,
+ isAppMetricsEnabled: !state.currentEditorTemplateData.isAppMetricsEnabled,
+ },
+ }
+
+ case DeploymentTemplateActionType.UPDATE_MERGE_STRATEGY:
+ return {
+ ...state,
+ currentEditorTemplateData: {
+ ...state.currentEditorTemplateData,
+ mergeStrategy: action.payload.mergeStrategy,
+ },
+ }
+
+ case DeploymentTemplateActionType.SHOW_DELETE_OVERRIDE_DIALOG:
+ return {
+ ...state,
+ showDeleteDraftOverrideDialog: action.payload.isProtected,
+ showDeleteOverrideDialog: !action.payload.isProtected,
+ }
+
+ case DeploymentTemplateActionType.DELETE_LOCAL_OVERRIDE:
+ return handleRestoreLastSavedTemplate(state)
+
+ case DeploymentTemplateActionType.OVERRIDE_TEMPLATE:
+ return {
+ ...state,
+ currentEditorTemplateData: {
+ ...state.currentEditorTemplateData,
+ isOverridden: true,
+ },
+ }
+
+ case DeploymentTemplateActionType.DELETE_OVERRIDE_CONCURRENT_PROTECTION_ERROR:
+ return {
+ ...state,
+ showDeleteDraftOverrideDialog: true,
+ showDeleteOverrideDialog: false,
+ }
+
+ case DeploymentTemplateActionType.CLOSE_DELETE_DRAFT_OVERRIDE_DIALOG:
+ return {
+ ...state,
+ showDeleteDraftOverrideDialog: false,
+ }
+
+ case DeploymentTemplateActionType.CLOSE_OVERRIDE_DIALOG:
+ return {
+ ...state,
+ showDeleteOverrideDialog: false,
+ }
+
+ case DeploymentTemplateActionType.SAVE_ERROR:
+ return {
+ ...state,
+ isSaving: false,
+ showSaveChangesModal: action.payload.isProtectionError ? true : state.showSaveChangesModal,
+ }
+
+ case DeploymentTemplateActionType.FINISH_SAVE:
+ return {
+ ...state,
+ isSaving: false,
+ lockedDiffModalState: {
+ showLockedTemplateDiffModal: action.payload.isLockConfigError,
+ showLockedDiffForApproval: false,
+ },
+ }
+
+ case DeploymentTemplateActionType.LOCKED_CHANGES_DETECTED_ON_SAVE: {
+ return {
+ ...state,
+ lockedDiffModalState: {
+ showLockedTemplateDiffModal: true,
+ showLockedDiffForApproval: false,
+ },
+ }
+ }
+
+ case DeploymentTemplateActionType.SHOW_PROTECTED_SAVE_MODAL:
+ return {
+ ...state,
+ showSaveChangesModal: true,
+ }
+
+ case DeploymentTemplateActionType.CLOSE_SAVE_CHANGES_MODAL:
+ return {
+ ...state,
+ showSaveChangesModal: false,
+ }
+
+ case DeploymentTemplateActionType.CLOSE_LOCKED_DIFF_MODAL:
+ return {
+ ...state,
+ showSaveChangesModal: false,
+ lockedDiffModalState: {
+ showLockedTemplateDiffModal: false,
+ showLockedDiffForApproval: false,
+ },
+ }
+
+ case DeploymentTemplateActionType.UPDATE_ARE_COMMENTS_PRESENT:
+ return {
+ ...state,
+ areCommentsPresent: action.payload.areCommentsPresent,
+ }
+
+ default:
+ return state
+ }
+}
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/index.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/index.ts
new file mode 100644
index 0000000000..391eccc68b
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/index.ts
@@ -0,0 +1 @@
+export { default as DeploymentTemplate } from './DeploymentTemplate'
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/service.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/service.ts
new file mode 100644
index 0000000000..6e29968d88
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/service.ts
@@ -0,0 +1,94 @@
+import { Routes } from '@Config/constants'
+import { BaseURLParams, get, post, put, ResponseType, trash } from '@devtron-labs/devtron-fe-common-lib'
+import { getChartReferencesForAppAndEnv } from '@Services/service'
+import {
+ DeploymentTemplateConfigDTO,
+ EnvironmentOverrideDeploymentTemplateDTO,
+ GetChartListReturnType,
+ UpdateBaseDTPayloadType,
+ UpdateEnvironmentDTPayloadType,
+} from './types'
+import { addGUISchemaIfAbsent } from './utils'
+
+export const updateBaseDeploymentTemplate = (request: UpdateBaseDTPayloadType, abortSignal: AbortSignal) => {
+ const URL = `${Routes.DEPLOYMENT_TEMPLATE_UPDATE}`
+ return post(URL, request, {
+ signal: abortSignal,
+ })
+}
+
+export const createBaseDeploymentTemplate = (request: UpdateBaseDTPayloadType, abortSignal: AbortSignal) => {
+ const URL = `${Routes.DEPLOYMENT_TEMPLATE}`
+ return post(URL, request, {
+ signal: abortSignal,
+ })
+}
+
+export function updateEnvDeploymentTemplate(payload: UpdateEnvironmentDTPayloadType, abortSignal: AbortSignal) {
+ return put('app/env', payload, {
+ signal: abortSignal,
+ })
+}
+
+export function createEnvDeploymentTemplate(
+ appId: number,
+ envId: number,
+ payload: UpdateEnvironmentDTPayloadType,
+ abortSignal: AbortSignal,
+) {
+ return post(`app/env/${appId}/${envId}`, payload, {
+ signal: abortSignal,
+ })
+}
+
+export const getEnvOverrideDeploymentTemplate = async (
+ appId: number,
+ envId: number,
+ chartId: number,
+ chartName: string,
+): Promise> => {
+ const data = await get(`app/env/${appId}/${envId}/${chartId}`)
+ return addGUISchemaIfAbsent(data, chartName)
+}
+
+export function deleteOverrideDeploymentTemplate(id: number, appId: number, envId: number) {
+ return trash(`app/env/reset/${appId}/${envId}/${id}`)
+}
+
+export async function getBaseDeploymentTemplate(
+ appId: number,
+ chartRefId: number,
+ abortSignal: AbortSignal,
+ chartName: string,
+): Promise> {
+ const response = await get(`${Routes.DEPLOYMENT_TEMPLATE}/${appId}/${chartRefId}`, {
+ signal: abortSignal,
+ })
+ return addGUISchemaIfAbsent(response, chartName)
+}
+
+export const getChartList = async ({
+ appId,
+ envId,
+}: Pick): Promise => {
+ const chartRefResp = await getChartReferencesForAppAndEnv(+appId, +envId)
+
+ const { chartRefs, latestAppChartRef, latestChartRef, latestEnvChartRef, chartMetadata } = chartRefResp.result
+ // Adding another layer of security
+ const envChartRef = envId ? latestEnvChartRef : null
+
+ const selectedChartId: number = envChartRef || latestAppChartRef || latestChartRef
+ const chart = chartRefs?.find((chartRef) => chartRef.id === selectedChartId) ?? chartRefs?.[0]
+
+ const globalChartRefId = latestAppChartRef || latestChartRef
+ const selectedGlobalChart = chartRefs?.find((chartRef) => chartRef.id === globalChartRefId) ?? chartRefs?.[0]
+
+ return {
+ charts: chartRefs || [],
+ chartsMetadata: chartMetadata || {},
+ selectedChartRefId: selectedChartId,
+ selectedChart: chart,
+ globalChartDetails: selectedGlobalChart,
+ latestAppChartRef,
+ }
+}
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/types.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/types.ts
new file mode 100644
index 0000000000..94c16c2ea9
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/types.ts
@@ -0,0 +1,438 @@
+import { SyntheticEvent } from 'react'
+import { Operation } from 'fast-json-patch'
+import {
+ ConfigKeysWithLockType,
+ DeploymentChartVersionType,
+ ChartMetadataType,
+ DeploymentTemplateConfigState,
+ SelectedChartDetailsType,
+ CompareFromApprovalOptionsValuesType,
+ ConfigurationType,
+ ServerErrors,
+ ConfigToolbarPopupNodeType,
+ DryRunEditorMode,
+ ConfigHeaderTabType,
+ ProtectConfigTabsType,
+ DraftMetadataDTO,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+type BaseDeploymentTemplateProps = {
+ /**
+ * If isUnSet is true would call this so that we re-direct users to next step
+ */
+ respondOnSuccess: (redirection: boolean) => void
+ /**
+ * Given in case we have'nt saved any deployment template
+ * If true, would show chart type selector.
+ */
+ isUnSet: boolean
+ /**
+ * Something related to git-ops
+ */
+ isCiPipeline: boolean
+
+ environmentName?: never
+ clusterId?: never
+}
+
+type EnvOverrideDeploymentTemplateProps = {
+ environmentName: string
+ clusterId?: string
+
+ respondOnSuccess?: never
+ isUnSet?: never
+ isCiPipeline?: never
+}
+
+export type DeploymentTemplateProps = {
+ isProtected: boolean
+ reloadEnvironments: () => void
+ fetchEnvConfig: (environmentId: number) => void
+} & (BaseDeploymentTemplateProps | EnvOverrideDeploymentTemplateProps)
+
+export interface DeploymentTemplateChartStateType {
+ charts: DeploymentChartVersionType[]
+ chartsMetadata: Record
+ globalChartDetails: DeploymentChartVersionType
+ latestAppChartRef: number
+}
+
+export interface DeploymentTemplateEditorDataStateType
+ extends Omit {
+ parsingError: string
+ removedPatches: Operation[]
+ originalTemplateState: DeploymentTemplateConfigState
+}
+
+export interface DeploymentTemplateStateType {
+ isLoadingInitialData: boolean
+ initialLoadError: ServerErrors
+ /**
+ * (Readonly)
+ */
+ chartDetails: DeploymentTemplateChartStateType
+
+ /**
+ * Template state that would be used in case actual deployment happens
+ * (Readonly)
+ */
+ publishedTemplateData: DeploymentTemplateConfigState
+ /**
+ * Last saved draft template data
+ * Only present in case of protected config
+ * (Readonly)
+ */
+ draftTemplateData: DeploymentTemplateConfigState
+ /**
+ * Template state of base configuration
+ * (Readonly)
+ */
+ baseDeploymentTemplateData: DeploymentTemplateConfigState
+ /**
+ * The state of current editor
+ */
+ currentEditorTemplateData: DeploymentTemplateEditorDataStateType
+
+ /**
+ * If true, would resolve scoped variables
+ */
+ resolveScopedVariables: boolean
+ isResolvingVariables: boolean
+
+ /**
+ * Contains resolved editor template for current editor - has two keys 1. Complete template 2. Template without locked keys
+ */
+ resolvedEditorTemplate: ResolvedEditorTemplateType
+ /**
+ * Contains resolved original template for current editor for us to feed to GUI View
+ */
+ resolvedOriginalTemplate: ResolvedEditorTemplateType
+
+ /**
+ * Used in case of approval view since we need to compare with published template
+ */
+ resolvedPublishedTemplate: ResolvedEditorTemplateType
+
+ /**
+ * Used to identify whether we are going to maintain sorting order of keys in editor
+ */
+ wasGuiOrHideLockedKeysEdited: boolean
+ showDraftComments: boolean
+ hideLockedKeys: boolean
+ lockedConfigKeysWithLockType: ConfigKeysWithLockType
+ lockedDiffModalState: {
+ showLockedTemplateDiffModal: boolean
+ /**
+ * State to show locked changes modal in case user is non super admin and is changing locked keys
+ * Would be showing an info bar in locked modal
+ */
+ showLockedDiffForApproval: boolean
+ }
+ isSaving: boolean
+ /**
+ * Would show modal to save in case of config protection to propose changes / save as draft
+ */
+ showSaveChangesModal: boolean
+ /**
+ * To replace the opened popup menu body in config toolbar
+ */
+ popupNodeType: ConfigToolbarPopupNodeType
+ /**
+ * In case of approval pending mode, we would be showing a select to compare from, this is its selected value
+ * If the action is to delete override then we would only be showing approval pending
+ */
+ compareFromSelectedOptionValue: CompareFromApprovalOptionsValuesType
+ /**
+ * There is a select in dry run mode in case isDraftPresent for us to toggle between draft/published/approval-pending
+ */
+ dryRunEditorMode: DryRunEditorMode
+ /**
+ * Triggered on changing chart/version
+ */
+ isLoadingChangedChartDetails: boolean
+ showDeleteOverrideDialog: boolean
+ showDeleteDraftOverrideDialog: boolean
+ /**
+ * This mode can only be activated when user is in edit mode
+ */
+ showReadMe: boolean
+ editMode: ConfigurationType
+ configHeaderTab: ConfigHeaderTabType
+ shouldMergeTemplateWithPatches: boolean
+ selectedProtectionViewTab: ProtectConfigTabsType
+ /**
+ * This state is present in case we have a draft available
+ * We will be initialize it with count coming from draft
+ * Will send handler in DraftComment which onchange would update this state
+ */
+ areCommentsPresent: boolean
+}
+
+export interface DeploymentTemplateOptionsHeaderProps
+ extends Pick,
+ Pick,
+ Pick {
+ disableVersionSelect: boolean
+ handleChangeToGUIMode: () => void
+ handleChangeToYAMLMode: () => void
+ restoreLastSavedTemplate: () => void
+ handleChartChange: (selectedChart: DeploymentChartVersionType) => void
+ isCompareView: boolean
+ isGuiSupported: boolean
+ areChartsLoading: boolean
+ showDeleteOverrideDraftEmptyState: boolean
+}
+
+// Can derive editMode from url as well, just wanted the typing to be more explicit
+export interface DeploymentTemplateFormProps
+ extends Pick,
+ Pick,
+ Pick,
+ Pick<
+ DeploymentTemplateStateType,
+ 'showReadMe' | 'lockedConfigKeysWithLockType' | 'hideLockedKeys' | 'editMode'
+ > {
+ editorOnChange: (value: string) => void
+ readOnly: boolean
+ editedDocument: string
+ uneditedDocument: string
+ readMe: string
+ handleChangeToYAMLMode: () => void
+ isGuiSupported: boolean
+}
+
+export interface DeploymentTemplateGUIViewProps
+ extends Pick<
+ DeploymentTemplateFormProps,
+ | 'editorOnChange'
+ | 'lockedConfigKeysWithLockType'
+ | 'hideLockedKeys'
+ | 'uneditedDocument'
+ | 'editedDocument'
+ | 'isUnSet'
+ >,
+ Pick {
+ value: string
+ readOnly: boolean
+ handleChangeToYAMLMode: () => void
+ rootClassName?: string
+}
+
+export interface ResolvedEditorTemplateType {
+ originalTemplateString: string
+ templateWithoutLockedKeys: string
+}
+
+export interface DeploymentTemplateCTAProps
+ extends Pick,
+ Pick {
+ isLoading: boolean
+ isDisabled: boolean
+ showApplicationMetrics: boolean
+ handleSave: (e: SyntheticEvent) => void
+ toggleAppMetrics: () => void
+ restoreLastSavedYAML: () => void
+ isDryRunView: boolean
+}
+
+export interface DeleteOverrideDialogProps {
+ environmentConfigId: number
+ handleReload: () => void
+ handleClose: () => void
+ handleProtectionError: () => void
+ reloadEnvironments: () => void
+}
+
+export interface DTChartSelectorProps
+ extends Pick,
+ Pick<
+ DeploymentTemplateOptionsHeaderProps,
+ | 'isUnSet'
+ | 'selectedChart'
+ | 'disableVersionSelect'
+ | 'areChartsLoading'
+ | 'parsingError'
+ | 'restoreLastSavedTemplate'
+ > {
+ selectChart: (selectedChart: DeploymentChartVersionType) => void
+ selectedChartRefId: number
+}
+
+export interface ChartSelectorDropdownProps
+ extends Pick,
+ Pick,
+ Pick,
+ Pick {
+ selectedChartRefId: number
+ selectChart: (
+ selectedChart: DeploymentChartVersionType,
+ ) => void | React.Dispatch>
+}
+
+interface EnvironmentConfigDTO {
+ IsOverride: boolean
+ active: boolean
+ chartRefId: number
+ clusterId: number
+ description: string
+ envOverrideValues: Record
+ environmentId: number
+ environmentName: string
+ id: number
+ isAppMetricsEnabled: boolean | null
+ isBasicViewLocked: boolean
+ latest: boolean
+ manualReviewed: boolean
+ namespace: string
+ saveEligibleChanges: boolean
+ status: number
+}
+
+export interface EnvironmentOverrideDeploymentTemplateDTO {
+ IsOverride: boolean
+ appMetrics: boolean
+ chartRefId: number
+ environmentConfig: EnvironmentConfigDTO
+ globalChartRefId: number
+ /**
+ * Base deployment template
+ */
+ globalConfig: Record
+ guiSchema: string
+ namespace: string
+ readme: string
+ schema: Record
+}
+
+interface DeploymentTemplateGlobalConfigDTO {
+ appId: number
+ chartRefId: number
+ /**
+ * FIXME: Not consumed at UI
+ */
+ chartRepositoryId: number
+ /**
+ * FIXME: Not consumed at UI
+ */
+ currentViewEditor: string
+ /**
+ * Base deployment template
+ */
+ defaultAppOverride: Record
+ id: number
+ isAppMetricsEnabled: boolean
+ isBasicViewLocked: boolean
+ latest: boolean
+ readme: string
+ refChartTemplate: string
+ refChartTemplateVersion: string
+ /**
+ * Might be irrelevant
+ */
+ saveEligibleChanges: boolean
+ /**
+ * Schema to feed into the Code editor
+ */
+ schema: Record
+}
+
+export interface DeploymentTemplateConfigDTO {
+ globalConfig: DeploymentTemplateGlobalConfigDTO
+ guiSchema: string
+}
+
+export interface GetPublishedAndBaseDeploymentTemplateReturnType {
+ publishedTemplateState: DeploymentTemplateConfigState
+ baseDeploymentTemplateState: DeploymentTemplateConfigState
+}
+
+export interface GetChartListReturnType
+ extends SelectedChartDetailsType,
+ Pick<
+ DeploymentTemplateChartStateType,
+ 'charts' | 'chartsMetadata' | 'globalChartDetails' | 'latestAppChartRef'
+ > {}
+
+export interface HandleInitializeTemplatesWithoutDraftParamsType {
+ baseDeploymentTemplateState: DeploymentTemplateStateType['baseDeploymentTemplateData']
+ publishedTemplateState: DeploymentTemplateStateType['publishedTemplateData']
+ chartDetailsState: DeploymentTemplateStateType['chartDetails']
+ lockedConfigKeysWithLockTypeState: DeploymentTemplateStateType['lockedConfigKeysWithLockType']
+}
+
+export interface GetCurrentEditorStateProps {
+ state: DeploymentTemplateStateType
+ isPublishedConfigPresent: boolean
+ isDryRunView: boolean
+ isDeleteOverrideDraft: boolean
+ isInheritedView: boolean
+ isPublishedValuesView: boolean
+ showApprovalPendingEditorInCompareView: boolean
+}
+
+export interface GetDryRunViewEditorStateProps
+ extends Pick {}
+
+export interface GetRawEditorValueForDryRunModeProps
+ extends Pick<
+ GetCurrentEditorStateProps,
+ 'isPublishedConfigPresent' | 'isDryRunView' | 'isDeleteOverrideDraft' | 'state'
+ > {}
+
+export interface GetCurrentEditorPayloadForScopedVariablesProps
+ extends Pick<
+ GetCurrentEditorStateProps,
+ 'isInheritedView' | 'isPublishedValuesView' | 'showApprovalPendingEditorInCompareView'
+ >,
+ GetRawEditorValueForDryRunModeProps {}
+
+export interface HandleInitializeDraftDataProps {
+ latestDraft: DraftMetadataDTO
+ guiSchema: string
+ chartRefsData: GetChartListReturnType
+ lockedConfigKeys: string[]
+ envId: string
+}
+
+interface UpdateDTCommonPayloadType {
+ chartRefId: DeploymentChartVersionType['id']
+ isAppMetricsEnabled: boolean
+ saveEligibleChanges: boolean
+ readme?: string
+ schema?: Record
+}
+
+export interface UpdateEnvironmentDTPayloadType
+ extends UpdateDTCommonPayloadType,
+ Partial> {
+ environmentId: number
+ envOverrideValues: Record
+ IsOverride: boolean
+ isDraftOverriden?: boolean
+ globalConfig?: Record
+}
+
+export interface UpdateBaseDTPayloadType
+ extends UpdateDTCommonPayloadType,
+ Partial> {
+ appId: number
+ defaultAppOverride: Record
+ id?: number
+ valuesOverride: Record
+}
+
+export interface GetCompareFromEditorConfigParams {
+ envId: string
+ isDeleteOverrideDraft: boolean
+ isPublishedConfigPresent: boolean
+ showApprovalPendingEditorInCompareView: boolean
+ state: DeploymentTemplateStateType
+}
+
+export type GetLockConfigEligibleAndIneligibleChangesType = (props: {
+ documents: Record<'edited' | 'unedited', object>
+ lockedConfigKeysWithLockType: ConfigKeysWithLockType
+}) => {
+ eligibleChanges: Record
+ ineligibleChanges: Record
+}
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/utils.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/utils.tsx
new file mode 100644
index 0000000000..c85c5045c4
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/utils.tsx
@@ -0,0 +1,661 @@
+import {
+ applyCompareDiffOnUneditedDocument,
+ DryRunEditorMode,
+ getGuiSchemaFromChartName,
+ ResponseType,
+ YAMLStringify,
+ logExceptionToSentry,
+ DeploymentTemplateConfigState,
+} from '@devtron-labs/devtron-fe-common-lib'
+import { importComponentFromFELibrary } from '@Components/common'
+import YAML from 'yaml'
+import {
+ DeploymentTemplateEditorDataStateType,
+ DeploymentTemplateStateType,
+ GetCompareFromEditorConfigParams,
+ GetCurrentEditorPayloadForScopedVariablesProps,
+ GetCurrentEditorStateProps,
+ GetDryRunViewEditorStateProps,
+ GetLockConfigEligibleAndIneligibleChangesType,
+ GetRawEditorValueForDryRunModeProps,
+ HandleInitializeDraftDataProps,
+ UpdateBaseDTPayloadType,
+ UpdateEnvironmentDTPayloadType,
+} from './types'
+import { PROTECT_BASE_DEPLOYMENT_TEMPLATE_IDENTIFIER_DTO } from './constants'
+import { DEFAULT_MERGE_STRATEGY } from '../constants'
+import { CompareConfigViewProps } from '../types'
+
+const removeLockedKeysFromYaml = importComponentFromFELibrary('removeLockedKeysFromYaml', null, 'function')
+const reapplyRemovedLockedKeysToYaml = importComponentFromFELibrary('reapplyRemovedLockedKeysToYaml', null, 'function')
+const getLockConfigEligibleAndIneligibleChanges: GetLockConfigEligibleAndIneligibleChangesType =
+ importComponentFromFELibrary('getLockConfigEligibleAndIneligibleChanges', null, 'function')
+
+export const makeObjectFromJsonPathArray = (index: number, paths: string[]) => {
+ if (index >= paths.length) {
+ return {
+ 'ui:widget': 'hidden',
+ }
+ }
+ if (paths[index] === '$') {
+ return makeObjectFromJsonPathArray(index + 1, paths)
+ }
+ const key = paths[index]
+ const isKeyNumber = !Number.isNaN(Number(key))
+ if (isKeyNumber) {
+ return { items: makeObjectFromJsonPathArray(index + 1, paths) }
+ }
+ return { [key]: makeObjectFromJsonPathArray(index + 1, paths) }
+}
+
+/**
+ * This method will compare and calculate the diffs between @unedited and @edited
+ * documents and apply these diffs onto the @unedited document and return this new document
+ * @param {string} unedited - The unedited document onto which we want to patch the changes from @edited
+ * @param {string} edited - The edited document whose changes we want to patch onto @unedited
+ */
+export const applyCompareDiffOfTempFormDataOnOriginalData = (
+ unedited: string,
+ edited: string,
+ updateTempFormData?: (data: string) => void,
+) => {
+ const updated = applyCompareDiffOnUneditedDocument(YAML.parse(unedited), YAML.parse(edited))
+ updateTempFormData?.(YAMLStringify(updated, { simpleKeys: true }))
+ return updated
+}
+
+export const addGUISchemaIfAbsent = (response: ResponseType, chartName: string) => {
+ if (response?.result && !response.result.guiSchema) {
+ return {
+ ...response,
+ result: {
+ ...response.result,
+ guiSchema: JSON.stringify(getGuiSchemaFromChartName(chartName)),
+ },
+ }
+ }
+ return response
+}
+
+export const getEditorTemplateAndLockedKeys = (
+ template: string,
+ lockedConfigKeys: string[],
+): Pick => {
+ const removedPatches: DeploymentTemplateEditorDataStateType['removedPatches'] = []
+
+ if (!removeLockedKeysFromYaml || !lockedConfigKeys.length) {
+ return { editorTemplate: template, removedPatches }
+ }
+
+ try {
+ const { document, addOperations } = removeLockedKeysFromYaml(template, lockedConfigKeys)
+ if (addOperations.length) {
+ removedPatches.push(...addOperations)
+ }
+
+ const updatedTemplate = YAMLStringify(document, {
+ simpleKeys: true,
+ })
+ return { editorTemplate: updatedTemplate, removedPatches }
+ } catch {
+ return { editorTemplate: template, removedPatches: [] }
+ }
+}
+
+export const getCurrentTemplateWithLockedKeys = ({
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited,
+}: Pick): string => {
+ const removedPatches = structuredClone(currentEditorTemplateData.removedPatches || [])
+
+ if (!removedPatches.length || !reapplyRemovedLockedKeysToYaml) {
+ return currentEditorTemplateData.editorTemplate
+ }
+
+ try {
+ const originalDocument = currentEditorTemplateData.originalTemplate
+ const parsedDocument = YAML.parse(currentEditorTemplateData.editorTemplate)
+
+ const updatedEditorObject = reapplyRemovedLockedKeysToYaml(parsedDocument, removedPatches)
+
+ if (wasGuiOrHideLockedKeysEdited) {
+ return YAMLStringify(applyCompareDiffOnUneditedDocument(originalDocument, updatedEditorObject), {
+ simpleKeys: true,
+ })
+ }
+ return YAMLStringify(updatedEditorObject, { simpleKeys: true })
+ } catch {
+ // Do nothing
+ }
+
+ return currentEditorTemplateData.editorTemplate
+}
+
+export const getLockedDiffModalDocuments = (isApprovalView: boolean, state: DeploymentTemplateStateType) => ({
+ unedited: state.publishedTemplateData.originalTemplate,
+ edited:
+ (isApprovalView
+ ? state.draftTemplateData.originalTemplate
+ : YAML.parse(
+ getCurrentTemplateWithLockedKeys({
+ currentEditorTemplateData: state.currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited: state.wasGuiOrHideLockedKeysEdited,
+ }),
+ )) ?? {},
+})
+
+export const getDeploymentTemplateResourceName = (environmentName: string): string => {
+ if (environmentName) {
+ return `${environmentName}-DeploymentTemplateOverride`
+ }
+
+ return PROTECT_BASE_DEPLOYMENT_TEMPLATE_IDENTIFIER_DTO
+}
+
+export const getAreTemplateChangesPresent = (state: DeploymentTemplateStateType): boolean => {
+ const { currentEditorTemplateData, wasGuiOrHideLockedKeysEdited } = state
+
+ if (!currentEditorTemplateData) {
+ return false
+ }
+
+ if (currentEditorTemplateData.parsingError) {
+ return true
+ }
+
+ const finalEditorValue = getCurrentTemplateWithLockedKeys({
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited,
+ })
+
+ const isEditorTemplateChanged = finalEditorValue !== currentEditorTemplateData.originalTemplateState.editorTemplate
+
+ const isChartRefIdChanged =
+ currentEditorTemplateData.selectedChartRefId !==
+ currentEditorTemplateData.originalTemplateState.selectedChartRefId
+
+ const areApplicationMetricsChanged =
+ currentEditorTemplateData.isAppMetricsEnabled !==
+ currentEditorTemplateData.originalTemplateState.isAppMetricsEnabled
+
+ const isOverriddenStatusChanged =
+ currentEditorTemplateData.isOverridden !== currentEditorTemplateData.originalTemplateState.isOverridden
+
+ if (isEditorTemplateChanged || isChartRefIdChanged || areApplicationMetricsChanged || isOverriddenStatusChanged) {
+ return true
+ }
+
+ return false
+}
+
+const getDryRunViewEditorState = ({
+ state,
+ isPublishedConfigPresent,
+ isDeleteOverrideDraft,
+}: GetDryRunViewEditorStateProps): DeploymentTemplateConfigState | DeploymentTemplateEditorDataStateType | null => {
+ const {
+ dryRunEditorMode,
+ draftTemplateData,
+ publishedTemplateData,
+ currentEditorTemplateData,
+ baseDeploymentTemplateData,
+ } = state
+
+ if (!draftTemplateData?.latestDraft) {
+ return currentEditorTemplateData
+ }
+
+ if (dryRunEditorMode === DryRunEditorMode.PUBLISHED_VALUES) {
+ return isPublishedConfigPresent ? publishedTemplateData : null
+ }
+
+ if (isDeleteOverrideDraft) {
+ return baseDeploymentTemplateData
+ }
+
+ if (dryRunEditorMode === DryRunEditorMode.APPROVAL_PENDING) {
+ return draftTemplateData
+ }
+
+ return currentEditorTemplateData
+}
+
+/**
+ * Returns template state based on the current view in case of single editor and RHS editor value in case of compare view
+ */
+export const getCurrentEditorState = ({
+ state,
+ isPublishedConfigPresent,
+ isDryRunView,
+ isDeleteOverrideDraft,
+ isInheritedView,
+ isPublishedValuesView,
+ showApprovalPendingEditorInCompareView,
+}: GetCurrentEditorStateProps): ReturnType => {
+ const { draftTemplateData, publishedTemplateData, currentEditorTemplateData, baseDeploymentTemplateData } = state
+
+ if (isDryRunView) {
+ return getDryRunViewEditorState({
+ state,
+ isPublishedConfigPresent,
+ isDeleteOverrideDraft,
+ })
+ }
+
+ if (isInheritedView) {
+ return baseDeploymentTemplateData
+ }
+
+ if (isPublishedValuesView) {
+ return isPublishedConfigPresent ? publishedTemplateData : null
+ }
+
+ if (showApprovalPendingEditorInCompareView) {
+ return isDeleteOverrideDraft ? baseDeploymentTemplateData : draftTemplateData
+ }
+
+ /*
+ In case of compare view if we have delete override we don't show select at all so if isDeleteOverrideDraft is true, we would be
+ showing the empty state
+ */
+ if (isDeleteOverrideDraft) {
+ return null
+ }
+
+ return currentEditorTemplateData
+}
+
+/**
+ * This method returns the editor value (un-resolved and with locked keys) in case of dry run mode
+ */
+export const getRawEditorValueForDryRunMode = ({
+ state,
+ isPublishedConfigPresent,
+ isDryRunView,
+ isDeleteOverrideDraft,
+}: GetRawEditorValueForDryRunModeProps): string => {
+ if (!isDryRunView) {
+ logExceptionToSentry(new Error('getRawEditorValueForDryRunMode called in non dry run mode'))
+ return ''
+ }
+
+ // We don't have to worry about hideLockedKeys since, we are always showing locked keys in dry run mode
+ return (
+ getCurrentEditorState({
+ state,
+ isPublishedConfigPresent,
+ isDeleteOverrideDraft,
+ isDryRunView: true,
+ isInheritedView: false,
+ isPublishedValuesView: false,
+ showApprovalPendingEditorInCompareView: false,
+ })?.editorTemplate || ''
+ )
+}
+
+/**
+ * This method returns the editor value (un-resolved and with locked keys) based on the current view (In case of single editor) and RHS editor value (In case of compare view)
+ */
+export const getCurrentEditorPayloadForScopedVariables = ({
+ state,
+ isPublishedConfigPresent,
+ isDryRunView,
+ isDeleteOverrideDraft,
+ isInheritedView,
+ isPublishedValuesView,
+ showApprovalPendingEditorInCompareView,
+}: GetCurrentEditorPayloadForScopedVariablesProps): string => {
+ const { hideLockedKeys, wasGuiOrHideLockedKeysEdited } = state
+
+ const currentEditorState = getCurrentEditorState({
+ state,
+ isPublishedConfigPresent,
+ isDryRunView,
+ isDeleteOverrideDraft,
+ isInheritedView,
+ isPublishedValuesView,
+ showApprovalPendingEditorInCompareView,
+ })
+
+ if (!currentEditorState) {
+ return ''
+ }
+
+ if (hideLockedKeys && !!(currentEditorState as DeploymentTemplateEditorDataStateType).removedPatches?.length) {
+ try {
+ const templateWithLockedKeys = getCurrentTemplateWithLockedKeys({
+ currentEditorTemplateData: currentEditorState as DeploymentTemplateEditorDataStateType,
+ wasGuiOrHideLockedKeysEdited,
+ })
+ return templateWithLockedKeys
+ } catch {
+ // Do nothing
+ }
+ }
+
+ return currentEditorState.editorTemplate
+}
+
+export const handleInitializeDraftData = ({
+ latestDraft,
+ guiSchema,
+ chartRefsData,
+ lockedConfigKeys,
+ envId,
+}: HandleInitializeDraftDataProps): DeploymentTemplateStateType['draftTemplateData'] => {
+ if (!envId) {
+ const {
+ valuesOverride,
+ id,
+ refChartTemplate,
+ refChartTemplateVersion,
+ isAppMetricsEnabled,
+ chartRefId,
+ readme,
+ schema,
+ } = JSON.parse(latestDraft.data)
+
+ const stringifiedTemplate = YAMLStringify(valuesOverride, { simpleKeys: true })
+ const { editorTemplate: editorTemplateWithoutLockedKeys } = getEditorTemplateAndLockedKeys(
+ stringifiedTemplate,
+ lockedConfigKeys,
+ )
+
+ const response: DeploymentTemplateStateType['draftTemplateData'] = {
+ originalTemplate: valuesOverride,
+ schema,
+ readme,
+ guiSchema,
+ isAppMetricsEnabled,
+ chartConfig: { id, refChartTemplate, refChartTemplateVersion, chartRefId, readme },
+ editorTemplate: stringifiedTemplate,
+ latestDraft,
+ selectedChartRefId: chartRefId,
+ selectedChart: chartRefsData.charts.find((chart) => chart.id === chartRefId),
+ editorTemplateWithoutLockedKeys,
+ }
+
+ return response
+ }
+
+ const {
+ envOverrideValues,
+ id,
+ isDraftOverriden,
+ isAppMetricsEnabled,
+ chartRefId,
+ status,
+ manualReviewed,
+ active,
+ namespace,
+ readme,
+ schema,
+ } = JSON.parse(latestDraft.data)
+ const stringifiedTemplate = YAMLStringify(envOverrideValues, { simpleKeys: true })
+ const { editorTemplate: editorTemplateWithoutLockedKeys } = getEditorTemplateAndLockedKeys(
+ stringifiedTemplate,
+ lockedConfigKeys,
+ )
+ const response: DeploymentTemplateStateType['draftTemplateData'] = {
+ originalTemplate: envOverrideValues,
+ schema,
+ readme,
+ guiSchema,
+ isAppMetricsEnabled,
+ editorTemplate: stringifiedTemplate,
+ latestDraft,
+ selectedChartRefId: chartRefId,
+ selectedChart: chartRefsData.charts.find((chart) => chart.id === chartRefId),
+ editorTemplateWithoutLockedKeys,
+ isOverridden: isDraftOverriden,
+ environmentConfig: {
+ id,
+ status,
+ manualReviewed,
+ active,
+ namespace,
+ },
+ mergeStrategy: DEFAULT_MERGE_STRATEGY,
+ }
+
+ return response
+}
+
+export const getUpdateBaseDeploymentTemplatePayload = (
+ state: DeploymentTemplateStateType,
+ appId: number,
+ skipReadmeAndSchema: boolean,
+): UpdateBaseDTPayloadType => {
+ const {
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited,
+ lockedDiffModalState: { showLockedTemplateDiffModal },
+ lockedConfigKeysWithLockType,
+ } = state
+
+ const editorTemplate = getCurrentTemplateWithLockedKeys({
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited,
+ })
+ const editorTemplateObject: Record = YAML.parse(editorTemplate)
+
+ const baseRequestData = {
+ ...(currentEditorTemplateData.chartConfig.chartRefId === currentEditorTemplateData.selectedChart.id
+ ? currentEditorTemplateData.chartConfig
+ : {}),
+
+ appId: +appId,
+ chartRefId: currentEditorTemplateData.selectedChart.id,
+ // NOTE: Ideally backend should not ask for this :/
+ defaultAppOverride: currentEditorTemplateData.originalTemplate,
+ isAppMetricsEnabled: currentEditorTemplateData.isAppMetricsEnabled,
+ saveEligibleChanges: showLockedTemplateDiffModal,
+
+ ...(!skipReadmeAndSchema
+ ? {
+ id: currentEditorTemplateData.chartConfig.id,
+ readme: currentEditorTemplateData.readme,
+ schema: currentEditorTemplateData.schema,
+ }
+ : {}),
+ }
+
+ if (showLockedTemplateDiffModal && getLockConfigEligibleAndIneligibleChanges) {
+ // Question: In case of draft edit should we do this or approval as unedited?
+ const { eligibleChanges } = getLockConfigEligibleAndIneligibleChanges({
+ documents: getLockedDiffModalDocuments(false, state),
+ lockedConfigKeysWithLockType,
+ })
+
+ return {
+ ...baseRequestData,
+ valuesOverride: eligibleChanges,
+ }
+ }
+
+ return {
+ ...baseRequestData,
+ valuesOverride: editorTemplateObject,
+ }
+}
+
+export const getDeleteProtectedOverridePayload = (
+ state: DeploymentTemplateStateType,
+ envId: number,
+ skipReadmeAndSchema: boolean,
+): UpdateEnvironmentDTPayloadType => {
+ const { baseDeploymentTemplateData, chartDetails, currentEditorTemplateData } = state
+
+ return {
+ environmentId: +envId,
+ envOverrideValues: baseDeploymentTemplateData.originalTemplate,
+ chartRefId: chartDetails.globalChartDetails.id,
+ IsOverride: false,
+ isAppMetricsEnabled: baseDeploymentTemplateData.isAppMetricsEnabled,
+ saveEligibleChanges: false,
+ ...(currentEditorTemplateData.environmentConfig.id > 0
+ ? {
+ id: currentEditorTemplateData.environmentConfig.id,
+ status: currentEditorTemplateData.environmentConfig.status,
+ manualReviewed: true,
+ active: currentEditorTemplateData.environmentConfig.active,
+ namespace: currentEditorTemplateData.environmentConfig.namespace,
+ }
+ : {}),
+ ...(!skipReadmeAndSchema
+ ? {
+ id: currentEditorTemplateData.environmentConfig.id,
+ globalConfig: baseDeploymentTemplateData.originalTemplate,
+ isDraftOverriden: false,
+ readme: baseDeploymentTemplateData.readme,
+ schema: baseDeploymentTemplateData.schema,
+ }
+ : {}),
+ }
+}
+
+export const getUpdateEnvironmentDTPayload = (
+ state: DeploymentTemplateStateType,
+ envId: number,
+ skipReadmeAndSchema: boolean,
+): UpdateEnvironmentDTPayloadType => {
+ const {
+ currentEditorTemplateData,
+ lockedDiffModalState: { showLockedTemplateDiffModal },
+ baseDeploymentTemplateData,
+ lockedConfigKeysWithLockType,
+ wasGuiOrHideLockedKeysEdited,
+ } = state
+
+ const editorTemplate = getCurrentTemplateWithLockedKeys({
+ currentEditorTemplateData,
+ wasGuiOrHideLockedKeysEdited,
+ })
+ const editorTemplateObject: Record = YAML.parse(editorTemplate)
+
+ const baseObject = {
+ environmentId: +envId,
+ chartRefId: currentEditorTemplateData.selectedChartRefId,
+ // Since this is for published here it will always be overridden
+ IsOverride: true,
+ isAppMetricsEnabled: currentEditorTemplateData.isAppMetricsEnabled,
+ saveEligibleChanges: showLockedTemplateDiffModal,
+
+ ...(currentEditorTemplateData.environmentConfig.id > 0
+ ? {
+ id: currentEditorTemplateData.environmentConfig.id,
+ status: currentEditorTemplateData.environmentConfig.status,
+ manualReviewed: true,
+ active: currentEditorTemplateData.environmentConfig.active,
+ namespace: currentEditorTemplateData.environmentConfig.namespace,
+ }
+ : {}),
+
+ // This is the data which we suppose to send for draft we are creating
+ ...(!skipReadmeAndSchema
+ ? {
+ id: currentEditorTemplateData.environmentConfig.id,
+ globalConfig: baseDeploymentTemplateData.originalTemplate,
+ isDraftOverriden: currentEditorTemplateData.isOverridden,
+ readme: currentEditorTemplateData.readme,
+ schema: currentEditorTemplateData.schema,
+ }
+ : {}),
+ }
+
+ if (showLockedTemplateDiffModal && getLockConfigEligibleAndIneligibleChanges) {
+ const { eligibleChanges } = getLockConfigEligibleAndIneligibleChanges({
+ documents: getLockedDiffModalDocuments(false, state),
+ lockedConfigKeysWithLockType,
+ })
+
+ return {
+ ...baseObject,
+ envOverrideValues: eligibleChanges,
+ }
+ }
+
+ return {
+ ...baseObject,
+ envOverrideValues: editorTemplateObject,
+ }
+}
+
+export const getCompareFromEditorConfig = ({
+ envId,
+ isDeleteOverrideDraft,
+ isPublishedConfigPresent,
+ showApprovalPendingEditorInCompareView,
+ state,
+}: GetCompareFromEditorConfigParams): Pick => {
+ const { draftTemplateData, currentEditorTemplateData, publishedTemplateData } = state
+
+ const templateState = showApprovalPendingEditorInCompareView ? draftTemplateData : currentEditorTemplateData
+
+ const currentEditorConfig: CompareConfigViewProps['currentEditorConfig'] = {
+ ...(envId &&
+ isDeleteOverrideDraft && {
+ isOverride: {
+ displayName: 'Configuration',
+ value: 'Inherit from base',
+ },
+ }),
+ chartName: {
+ displayName: 'Chart',
+ value: templateState?.selectedChart?.name,
+ },
+ chartVersion: {
+ displayName: 'Version',
+ value: templateState?.selectedChart?.version,
+ },
+ ...(!!envId && {
+ mergeStrategy: {
+ displayName: 'Merge strategy',
+ value: templateState?.mergeStrategy,
+ },
+ }),
+ ...(!!window._env_.APPLICATION_METRICS_ENABLED && {
+ applicationMetrics: {
+ displayName: 'Application metrics',
+ value: templateState?.isAppMetricsEnabled ? 'Enabled' : 'Disabled',
+ },
+ }),
+ }
+
+ const publishedEditorConfig: CompareConfigViewProps['publishedEditorConfig'] = isPublishedConfigPresent
+ ? {
+ ...(!!envId &&
+ isDeleteOverrideDraft && {
+ isOverride: {
+ displayName: 'Configuration',
+ value: 'Overridden',
+ },
+ }),
+ chartName: {
+ displayName: 'Chart',
+ value: publishedTemplateData?.selectedChart?.name,
+ },
+ chartVersion: {
+ displayName: 'Version',
+ value: publishedTemplateData?.selectedChart?.version,
+ },
+ ...(!!envId && {
+ mergeStrategy: {
+ displayName: 'Merge strategy',
+ value: publishedTemplateData?.mergeStrategy,
+ },
+ }),
+ ...(!!window._env_.APPLICATION_METRICS_ENABLED && {
+ applicationMetrics: {
+ displayName: 'Application metrics',
+ value: publishedTemplateData?.isAppMetricsEnabled ? 'Enabled' : 'Disabled',
+ },
+ }),
+ }
+ : {}
+
+ return {
+ currentEditorConfig,
+ publishedEditorConfig,
+ }
+}
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.scss b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.scss
new file mode 100644
index 0000000000..e99383fbe9
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.scss
@@ -0,0 +1,7 @@
+// FIXME: When issue for svg button height is fixed, remove this file
+.no-override-empty-state-container {
+ .empty-state svg {
+ height: 100%;
+ width: 100%;
+ }
+}
\ No newline at end of file
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx
new file mode 100644
index 0000000000..8928d2c3f4
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx
@@ -0,0 +1,71 @@
+import {
+ Button,
+ ButtonVariantType,
+ ComponentSizeType,
+ GenericEmptyState,
+ ImageType,
+} from '@devtron-labs/devtron-fe-common-lib'
+import cmCsEmptyState from '@Images/cm-cs-empty-state.png'
+import { ReactComponent as ICAdd } from '@Icons/ic-add.svg'
+import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types'
+import { NoOverrideEmptyStateProps } from './types'
+
+import './NoOverrideEmptyState.scss'
+
+const getNoOverrideEmptyStateTitle = ({
+ componentType,
+ environmentName,
+ configName,
+}: Pick): string => {
+ if (componentType === CMSecretComponentType.ConfigMap) {
+ return `ConfigMap '${configName}': Not overridden for '${environmentName}'`
+ }
+
+ if (componentType === CMSecretComponentType.Secret) {
+ return `Secret '${configName}': Not overridden for '${environmentName}'`
+ }
+
+ return `Deployment template: Not overridden for '${environmentName}'`
+}
+
+const renderCreateOverrideButton =
+ ({ handleCreateOverride }: Pick) =>
+ () => (
+ }
+ size={ComponentSizeType.large}
+ dataTestId="create-override-button"
+ text="Create Override"
+ />
+ )
+
+const NoOverrideEmptyState = ({
+ componentType,
+ configName,
+ environmentName,
+ hideOverrideButton,
+ handleCreateOverride,
+ handleViewInheritedConfig,
+}: NoOverrideEmptyStateProps) => (
+
+
+
+
+
+)
+
+export default NoOverrideEmptyState
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoPublishedVersionEmptyState.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoPublishedVersionEmptyState.tsx
new file mode 100644
index 0000000000..64bbb60385
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoPublishedVersionEmptyState.tsx
@@ -0,0 +1,14 @@
+import { GenericEmptyState } from '@devtron-labs/devtron-fe-common-lib'
+import noArtifact from '@Images/no-artifact@2x.png'
+
+import { NoPublishedVersionEmptyStateProps } from './types'
+
+const NoPublishedVersionEmptyState = ({ isOverride = true }: NoPublishedVersionEmptyStateProps) => (
+
+)
+
+export default NoPublishedVersionEmptyState
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/SelectMergeStrategy.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/SelectMergeStrategy.tsx
new file mode 100644
index 0000000000..a4e26005be
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/SelectMergeStrategy.tsx
@@ -0,0 +1,46 @@
+import {
+ InfoIconTippy,
+ OverrideMergeStrategyType,
+ OverrideStrategyTippyContent,
+ SelectPicker,
+ SelectPickerOptionType,
+ SelectPickerVariantType,
+ DOCUMENTATION_HOME_PAGE,
+ ComponentSizeType,
+} from '@devtron-labs/devtron-fe-common-lib'
+import { MERGE_STRATEGY_OPTIONS } from './constants'
+import { SelectMergeStrategyProps } from './types'
+
+const SelectMergeStrategy = ({ mergeStrategy, handleMergeStrategyChange, isDisabled }: SelectMergeStrategyProps) => {
+ const handleChange = (selectedOption: SelectPickerOptionType) => {
+ handleMergeStrategyChange(selectedOption.value as OverrideMergeStrategyType)
+ }
+
+ return (
+
+ }
+ documentationLink={DOCUMENTATION_HOME_PAGE}
+ />
+
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
+
+ Merge strategy
+
+
+ option.value === mergeStrategy)}
+ options={MERGE_STRATEGY_OPTIONS}
+ isDisabled={isDisabled}
+ variant={SelectPickerVariantType.BORDER_LESS}
+ isSearchable={false}
+ size={ComponentSizeType.small}
+ />
+
+ )
+}
+
+export default SelectMergeStrategy
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ToggleResolveScopedVariables.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ToggleResolveScopedVariables.tsx
new file mode 100644
index 0000000000..a15a58ac6b
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ToggleResolveScopedVariables.tsx
@@ -0,0 +1,29 @@
+import { Toggle, Tooltip } from '@devtron-labs/devtron-fe-common-lib'
+import { ReactComponent as ICViewVariableToggle } from '@Icons/ic-view-variable-toggle.svg'
+import { ToggleResolveScopedVariablesProps } from './types'
+
+const ToggleResolveScopedVariables = ({
+ resolveScopedVariables,
+ handleToggleScopedVariablesView,
+ isDisabled = false,
+ showTooltip = true,
+}: ToggleResolveScopedVariablesProps) => (
+
+
+
+
+
+)
+
+export default ToggleResolveScopedVariables
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants.ts
new file mode 100644
index 0000000000..43de4eadcd
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants.ts
@@ -0,0 +1,11 @@
+import { OverrideMergeStrategyType, SelectPickerOptionType } from '@devtron-labs/devtron-fe-common-lib'
+
+export const DEFAULT_MERGE_STRATEGY: OverrideMergeStrategyType = OverrideMergeStrategyType.REPLACE
+
+export const MERGE_STRATEGY_OPTIONS: SelectPickerOptionType[] = [
+ {
+ label: 'Replace',
+ description: 'Override complete configuration',
+ value: OverrideMergeStrategyType.REPLACE,
+ },
+]
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts
new file mode 100644
index 0000000000..cd1656256e
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts
@@ -0,0 +1,10 @@
+export * from './DeploymentTemplate'
+export { default as ConfigDryRun } from './ConfigDryRun'
+export { default as ConfigToolbar } from './ConfigToolbar'
+export { default as NoOverrideEmptyState } from './NoOverrideEmptyState'
+export { default as ConfigHeader } from './ConfigHeader'
+export { default as NoPublishedVersionEmptyState } from './NoPublishedVersionEmptyState'
+export { default as CompareConfigView } from './CompareConfigView'
+export * from './DeploymentConfigCompare'
+
+export type { ConfigToolbarProps, CompareConfigViewProps } from './types'
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts
new file mode 100644
index 0000000000..672e58da83
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts
@@ -0,0 +1,298 @@
+import {
+ CompareFromApprovalOptionsValuesType,
+ ConfigHeaderTabType,
+ ConfigToolbarPopupMenuConfigType,
+ ConfigToolbarPopupNodeType,
+ DeploymentHistorySingleValue,
+ DeploymentTemplateConfigState,
+ OverrideMergeStrategyType,
+ ProtectConfigTabsType,
+ SelectPickerOptionType,
+} from '@devtron-labs/devtron-fe-common-lib'
+import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types'
+import { FunctionComponent, MutableRefObject, ReactNode } from 'react'
+
+export interface ConfigHeaderProps {
+ configHeaderTab: ConfigHeaderTabType
+ handleTabChange: (tab: ConfigHeaderTabType) => void
+ areChangesPresent: boolean
+ isDisabled: boolean
+ /**
+ * If false, can not override config and will show values tab as Configuration
+ */
+ isOverridable: boolean
+ /**
+ * This means that we are showing no override empty state, i.e, currently we are inheriting base config
+ * and our editable state is also not overriding the base config.
+ */
+ showNoOverride: boolean
+ parsingError: string
+ restoreLastSavedYAML: () => void
+ /**
+ * This prop hides the dry run tab in the header
+ * This prop is meant to be removed after patch merge strategy is introduced
+ * @default - false
+ */
+ hideDryRunTab?: boolean
+}
+
+export interface ConfigHeaderTabProps
+ extends Pick<
+ ConfigHeaderProps,
+ 'handleTabChange' | 'isDisabled' | 'areChangesPresent' | 'isOverridable' | 'showNoOverride'
+ > {
+ tab: ConfigHeaderTabType
+ activeTabIndex: number
+ currentTabIndex: number
+ hasError: boolean
+}
+
+export interface ConfigHeaderTabConfigType {
+ text: string
+ icon?: FunctionComponent> | null
+}
+
+interface ConfigToolbarPopupConfigType {
+ menuConfig: Record
+ /**
+ * If true, would show popupMenuNode in body of the popup menu
+ */
+ popupNodeType?: ConfigToolbarPopupNodeType
+ /**
+ * If provided, will replace the popup menu view with the provided node given popupMenuConfig is not empty
+ */
+ popupMenuNode?: ReactNode
+}
+
+type ConfigToolbarReadMeProps =
+ | {
+ showEnableReadMeButton: boolean
+ handleEnableReadmeView: () => void
+ }
+ | {
+ showEnableReadMeButton?: never
+ handleEnableReadmeView?: never
+ }
+
+export type ConfigToolbarProps = {
+ configHeaderTab: ConfigHeaderTabType
+ handleToggleScopedVariablesView: () => void
+ resolveScopedVariables: boolean
+ /**
+ * Route for redirection to base configurations shown in case configHeaderTab is inherited
+ */
+ baseConfigurationURL: string
+ /**
+ * Will feed the selected tab in the protection tab group
+ */
+ selectedProtectionViewTab: ProtectConfigTabsType
+ handleProtectionViewTabChange: (tab: ProtectConfigTabsType) => void
+
+ handleToggleCommentsView: () => void
+ /**
+ * Would show red dot on comments icon if comments are present
+ */
+ areCommentsPresent: boolean
+
+ showMergePatchesButton: boolean
+ shouldMergeTemplateWithPatches: boolean
+ handleToggleShowTemplateMergedWithPatch: () => void
+
+ mergeStrategy: OverrideMergeStrategyType
+ handleMergeStrategyChange: (strategy: OverrideMergeStrategyType) => void
+
+ /**
+ * Used to place toggle editor view and chart selectors in deployment template
+ */
+ children?: ReactNode
+ /**
+ * If provided, will show popup menu on click three dots button
+ * If empty/null, will not show the button
+ */
+ popupConfig?: ConfigToolbarPopupConfigType
+ /**
+ * @default false
+ */
+ isProtected?: boolean
+ /**
+ * @default false
+ */
+ isApprovalPending?: boolean
+ isDraftPresent?: boolean
+ approvalUsers: string[]
+ /**
+ * @default - false
+ * If given would disable all the actions
+ */
+ disableAllActions?: boolean
+ parsingError: string
+ restoreLastSavedYAML: () => void
+ /**
+ * This key means if have saved a draft and have not proposed it yet, and we are creating a new entity like override.
+ * @default - true
+ * If false we will hide all the action in toolbar.
+ */
+ isPublishedConfigPresent?: boolean
+ headerMessage?: string
+ showDeleteOverrideDraftEmptyState: boolean
+} & ConfigToolbarReadMeProps
+
+interface ConfigToolbarPopupMenuLockedConfigDataType {
+ /**
+ * If false would not show hide/show locked keys button
+ */
+ areLockedKeysPresent: boolean
+ hideLockedKeys: boolean
+ handleSetHideLockedKeys: (value: boolean) => void
+}
+
+export interface GetConfigToolbarPopupConfigProps {
+ /**
+ * If not provided won't show locked config data
+ */
+ lockedConfigData?: ConfigToolbarPopupMenuLockedConfigDataType | null
+ /**
+ * @default false
+ */
+ showDeleteOverrideDraftEmptyState?: boolean
+ configHeaderTab: ConfigHeaderTabType
+ isOverridden: boolean
+ isPublishedValuesView: boolean
+ isPublishedConfigPresent: boolean
+ handleDeleteOverride: () => void
+ handleDelete?: () => void
+ handleDiscardDraft: () => void
+ unableToParseData: boolean
+ isLoading: boolean
+ isDraftAvailable: boolean
+ handleShowEditHistory: () => void
+ isProtected?: boolean
+ isDeletable?: boolean
+ isDeleteOverrideDraftPresent?: boolean
+}
+
+type ConfigDryRunManifestProps =
+ | {
+ showManifest: true
+ manifestAbortController: MutableRefObject
+ }
+ | {
+ showManifest?: never
+ manifestAbortController?: never
+ }
+
+export type ConfigDryRunProps = {
+ isLoading: boolean
+ handleToggleResolveScopedVariables: () => void
+ resolveScopedVariables: boolean
+ showManifest: boolean
+ chartRefId?: number
+ editorTemplate: string
+ editorSchema?: DeploymentTemplateConfigState['schema']
+ selectedChartVersion?: string
+ dryRunEditorMode: string
+ handleChangeDryRunEditorMode: (mode: string) => void
+ isDraftPresent: boolean
+ isApprovalPending: boolean
+ isPublishedConfigPresent: boolean
+} & ConfigDryRunManifestProps
+
+export interface ToggleResolveScopedVariablesProps {
+ resolveScopedVariables: boolean
+ handleToggleScopedVariablesView: () => void
+ isDisabled?: boolean
+ /**
+ * @default true
+ */
+ showTooltip?: boolean
+}
+
+export enum DeploymentTemplateComponentType {
+ DEPLOYMENT_TEMPLATE = '3',
+}
+
+type NoOverrideEmptyStateCMCSProps = {
+ componentType: CMSecretComponentType
+ configName: string
+}
+
+type NoOverrideEmptyStateDeploymentTemplateProps = {
+ componentType: DeploymentTemplateComponentType
+ configName?: never
+}
+
+export type NoOverrideEmptyStateProps = {
+ environmentName: string
+ handleCreateOverride: () => void
+ handleViewInheritedConfig: () => void
+ hideOverrideButton?: boolean
+} & (NoOverrideEmptyStateCMCSProps | NoOverrideEmptyStateDeploymentTemplateProps)
+
+type CMSecretDiffViewConfigType = {
+ configuration?: DeploymentHistorySingleValue
+ dataType: DeploymentHistorySingleValue
+ mountDataAs: DeploymentHistorySingleValue
+ volumeMountPath: DeploymentHistorySingleValue
+ setSubPath: DeploymentHistorySingleValue
+ externalSubpathValues: DeploymentHistorySingleValue
+ filePermission: DeploymentHistorySingleValue
+ roleARN: DeploymentHistorySingleValue
+}
+
+type DeploymentTemplateDiffViewConfigType =
+ | {
+ applicationMetrics?: DeploymentHistorySingleValue
+ chartName: DeploymentHistorySingleValue
+ chartVersion: DeploymentHistorySingleValue
+ mergeStrategy?: DeploymentHistorySingleValue
+ isOverride?: DeploymentHistorySingleValue
+ dataType?: never
+ mountDataAs?: never
+ volumeMountPath?: never
+ setSubPath?: never
+ externalSubpathValues?: never
+ filePermission?: never
+ roleARN?: never
+ }
+ | {
+ applicationMetrics?: never
+ chartName?: never
+ chartVersion?: never
+ mergeStrategy?: never
+ isOverride?: never
+ }
+
+export type CompareConfigViewEditorConfigType = DeploymentTemplateDiffViewConfigType | CMSecretDiffViewConfigType
+
+export interface CompareConfigViewProps {
+ compareFromSelectedOptionValue: CompareFromApprovalOptionsValuesType
+ handleCompareFromOptionSelection: (value: SelectPickerOptionType) => void
+ isApprovalView: boolean
+ isDeleteOverrideView: boolean
+
+ currentEditorTemplate: Record
+ publishedEditorTemplate: Record
+ currentEditorConfig: CompareConfigViewEditorConfigType
+ publishedEditorConfig: CompareConfigViewEditorConfigType
+ draftChartVersion?: string
+ selectedChartVersion?: string
+ /**
+ * @default ${compareFromSelectedOptionValue}-"draft-editor-key"
+ */
+ editorKey?: string
+ className?: string
+}
+
+export interface BaseConfigurationNavigationProps {
+ baseConfigurationURL: string
+}
+
+export interface NoPublishedVersionEmptyStateProps {
+ isOverride?: boolean
+}
+
+export interface SelectMergeStrategyProps {
+ mergeStrategy: OverrideMergeStrategyType
+ handleMergeStrategyChange: (value: OverrideMergeStrategyType) => void
+ isDisabled: boolean
+}
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/utils.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/utils.tsx
new file mode 100644
index 0000000000..a24144457a
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/utils.tsx
@@ -0,0 +1,194 @@
+import {
+ ConfigHeaderTabType,
+ ConfigToolbarPopupMenuConfigType,
+ DeploymentTemplateHistoryType,
+ Tooltip,
+} from '@devtron-labs/devtron-fe-common-lib'
+import { ReactComponent as ICFilePlay } from '@Icons/ic-file-play.svg'
+import { ReactComponent as ICFileCode } from '@Icons/ic-file-code.svg'
+import { ReactComponent as ICArrowSquareIn } from '@Icons/ic-arrow-square-in.svg'
+import { ReactComponent as ICDeleteInteractive } from '@Icons/ic-delete-interactive.svg'
+import { importComponentFromFELibrary } from '@Components/common'
+import {
+ CompareConfigViewEditorConfigType,
+ ConfigHeaderTabConfigType,
+ ConfigToolbarProps,
+ GetConfigToolbarPopupConfigProps,
+} from './types'
+
+const getToggleViewLockedKeysPopupButtonConfig = importComponentFromFELibrary(
+ 'getToggleViewLockedKeysPopupButtonConfig',
+ null,
+ 'function',
+)
+
+const getDeleteDraftPopupButtonConfig = importComponentFromFELibrary(
+ 'getDeleteDraftPopupButtonConfig',
+ null,
+ 'function',
+)
+
+const getEditHistoryPopupButtonConfig = importComponentFromFELibrary(
+ 'getEditHistoryPopupButtonConfig',
+ null,
+ 'function',
+)
+
+const getValuesViewTabText = (
+ isOverridable: Parameters[1],
+ showNoOverride: Parameters[2],
+) => {
+ if (!isOverridable) {
+ return 'Configuration'
+ }
+ if (showNoOverride) {
+ return 'No override'
+ }
+ return 'Override'
+}
+
+export const getConfigHeaderTabConfig = (
+ tab: ConfigHeaderTabType,
+ isOverridable: boolean,
+ showNoOverride: boolean,
+): ConfigHeaderTabConfigType => {
+ switch (tab) {
+ case ConfigHeaderTabType.DRY_RUN:
+ return {
+ text: 'Dry run',
+ icon: ICFilePlay,
+ }
+
+ case ConfigHeaderTabType.VALUES:
+ return {
+ text: getValuesViewTabText(isOverridable, showNoOverride),
+ icon: ICFileCode,
+ }
+
+ case ConfigHeaderTabType.INHERITED:
+ return {
+ text: 'Inherited',
+ icon: ICArrowSquareIn,
+ }
+ default:
+ return {
+ text: tab,
+ }
+ }
+}
+
+export const PopupMenuItem = ({
+ text,
+ onClick,
+ dataTestId,
+ disabled,
+ icon,
+ variant,
+}: ConfigToolbarPopupMenuConfigType) => (
+
+ {icon}
+
+ {text}
+
+
+)
+
+export const getConfigToolbarPopupConfig = ({
+ lockedConfigData,
+ showDeleteOverrideDraftEmptyState = false,
+ configHeaderTab,
+ isOverridden,
+ isPublishedValuesView,
+ isPublishedConfigPresent,
+ handleDeleteOverride,
+ handleDelete,
+ handleDiscardDraft,
+ unableToParseData,
+ isLoading,
+ isDraftAvailable,
+ handleShowEditHistory,
+ isProtected = false,
+ isDeletable = false,
+ isDeleteOverrideDraftPresent = false,
+}: GetConfigToolbarPopupConfigProps): ConfigToolbarProps['popupConfig']['menuConfig'] => {
+ if (isPublishedValuesView && !isPublishedConfigPresent) {
+ return null
+ }
+
+ const firstConfigSegment: ConfigToolbarPopupMenuConfigType[] = []
+ const secondConfigSegment: ConfigToolbarPopupMenuConfigType[] = []
+
+ if (getToggleViewLockedKeysPopupButtonConfig && lockedConfigData && !showDeleteOverrideDraftEmptyState) {
+ const lockedKeysConfig = getToggleViewLockedKeysPopupButtonConfig(
+ lockedConfigData.areLockedKeysPresent,
+ lockedConfigData.hideLockedKeys,
+ unableToParseData || isLoading,
+ lockedConfigData.handleSetHideLockedKeys,
+ )
+
+ if (lockedKeysConfig) {
+ firstConfigSegment.push(lockedKeysConfig)
+ }
+ }
+
+ if (getEditHistoryPopupButtonConfig && isDraftAvailable && configHeaderTab === ConfigHeaderTabType.VALUES) {
+ const activityHistoryConfig = getEditHistoryPopupButtonConfig(handleShowEditHistory, isLoading)
+ if (activityHistoryConfig) {
+ firstConfigSegment.push(activityHistoryConfig)
+ }
+ }
+
+ if (getDeleteDraftPopupButtonConfig && isDraftAvailable && configHeaderTab === ConfigHeaderTabType.VALUES) {
+ const deleteDraftConfig = getDeleteDraftPopupButtonConfig(handleDiscardDraft, isLoading)
+ if (deleteDraftConfig) {
+ secondConfigSegment.push(deleteDraftConfig)
+ }
+ }
+
+ if (isOverridden && configHeaderTab === ConfigHeaderTabType.VALUES && !isDeleteOverrideDraftPresent) {
+ secondConfigSegment.push({
+ text: 'Delete override',
+ onClick: handleDeleteOverride,
+ dataTestId: 'delete-override',
+ disabled: isLoading,
+ icon: ,
+ variant: 'negative',
+ })
+ }
+
+ if (isDeletable && configHeaderTab === ConfigHeaderTabType.VALUES) {
+ secondConfigSegment.push({
+ text: `Delete${isProtected ? '...' : ''}`,
+ onClick: handleDelete,
+ dataTestId: 'delete-config-map-secret',
+ disabled: isLoading,
+ icon: ,
+ variant: 'negative',
+ })
+ }
+
+ return {
+ ...(firstConfigSegment.length && { firstConfigSegment }),
+ ...(secondConfigSegment.length && { secondConfigSegment }),
+ }
+}
+
+export const getCompareViewHistoryDiffConfigProps = (
+ showDisplayName: boolean,
+ editorTemplate: Record,
+ editorConfig: CompareConfigViewEditorConfigType,
+):
+ | DeploymentTemplateHistoryType['baseTemplateConfiguration']
+ | DeploymentTemplateHistoryType['currentConfiguration'] => ({
+ codeEditorValue: {
+ displayName: showDisplayName ? 'Data' : '',
+ ...(editorTemplate && { value: JSON.stringify(editorTemplate) }),
+ },
+ values: editorConfig,
+})
diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppConfigurationCheckBox.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppConfigurationCheckBox.tsx
similarity index 100%
rename from src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppConfigurationCheckBox.tsx
rename to apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppConfigurationCheckBox.tsx
diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx
similarity index 88%
rename from src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx
rename to apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx
index 84b3224cc6..97ca0a6609 100644
--- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/AppNavigation.tsx
@@ -17,7 +17,16 @@
import { ReactNode } from 'react'
import { Route, Switch, useLocation, useRouteMatch } from 'react-router-dom'
-import { ConditionalWrap, EnvResourceType, TippyCustomized, TippyTheme } from '@devtron-labs/devtron-fe-common-lib'
+import {
+ Button,
+ ButtonStyleType,
+ ButtonVariantType,
+ ComponentSizeType,
+ ConditionalWrap,
+ EnvResourceType,
+ TippyCustomized,
+ TippyTheme,
+} from '@devtron-labs/devtron-fe-common-lib'
import { DEVTRON_APPS_STEPS, STAGE_NAME } from '../AppConfig.types'
import { URLS } from '../../../../../../config'
import AppConfigurationCheckBox from './AppConfigurationCheckBox'
@@ -118,7 +127,10 @@ export const AppNavigation = () => {
return (
{({ match }) => (
{
showBaseConfigurations
showDeploymentTemplate={!isJobView}
goBackURL={getValidBackURL()}
+ compareWithURL={`${path}/:envId(\\d+)?`}
showComparison={!isJobView && isUnlocked.workflowEditor}
isCMSecretLocked={!isUnlocked.workflowEditor}
/>
@@ -194,15 +207,16 @@ export const AppNavigation = () => {
return
})}
{isJobView &&
}
-
-
+
- Delete {isJobView ? 'Job' : 'Application'}
-
+ text={`Delete ${isJobView ? 'Job' : 'Application'}`}
+ fullWidth
+ />
>
diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx
similarity index 53%
rename from src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx
rename to apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx
index 4e70f8debe..8692f0aa58 100644
--- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav.tsx
@@ -1,21 +1,29 @@
-import { useEffect, useState } from 'react'
-import { useRouteMatch, useLocation, NavLink, useHistory } from 'react-router-dom'
+import { MouseEvent, useEffect, useState } from 'react'
+import { useRouteMatch, useLocation, NavLink, useHistory, generatePath } from 'react-router-dom'
import Tippy from '@tippyjs/react'
+import { GroupBase, OptionsOrGroups } from 'react-select'
import {
+ Button,
+ ButtonComponentType,
+ ButtonStyleType,
+ ButtonVariantType,
CollapsibleList,
CollapsibleListConfig,
+ ComponentSizeType,
EnvResourceType,
+ getSelectPickerOptionByValue,
SelectPicker,
SelectPickerOptionType,
SelectPickerVariantType,
} from '@devtron-labs/devtron-fe-common-lib'
import { ReactComponent as ICBack } from '@Icons/ic-caret-left-small.svg'
+import { ReactComponent as ICArrowsLeftRight } from '@Icons/ic-arrows-left-right.svg'
import { ReactComponent as ICAdd } from '@Icons/ic-add.svg'
import { ReactComponent as ICLocked } from '@Icons/ic-locked.svg'
+import { ReactComponent as ICFileCode } from '@Icons/ic-file-code.svg'
import { URLS } from '@Config/routes'
-import { importComponentFromFELibrary } from '@Components/common'
import { ReactComponent as ProtectedIcon } from '@Icons/ic-shield-protect-fill.svg'
import { ResourceConfigState } from '@Pages/Applications/DevtronApps/service.types'
@@ -23,8 +31,6 @@ import { BASE_CONFIGURATIONS } from '../AppConfig.constants'
import { EnvConfigRouteParams, EnvConfigurationsNavProps, EnvConfigObjectKey } from '../AppConfig.types'
import { getEnvConfiguration, getNavigationPath, resourceTypeBasedOnPath } from './Navigation.helper'
-const CompareWithButton = importComponentFromFELibrary('CompareWithButton', null, 'function')
-
// LOADING SHIMMER
const ShimmerText = ({ width }: { width: string }) => (
@@ -43,16 +49,19 @@ export const EnvConfigurationsNav = ({
paramToCheck = 'envId',
showComparison,
isCMSecretLocked,
+ hideEnvSelector,
+ compareWithURL,
}: EnvConfigurationsNavProps) => {
// HOOKS
const history = useHistory()
const { pathname } = useLocation()
const { path, params } = useRouteMatch
()
- const { envId, appId } = params
+ const { envId } = params
// STATES
const [expandedIds, setExpandedIds] =
useState, boolean>>()
+
const [updatedEnvConfig, setUpdatedEnvConfig] = useState>({
deploymentTemplate: null,
configmaps: [],
@@ -62,26 +71,15 @@ export const EnvConfigurationsNav = ({
// CONSTANTS
const { isLoading, config } = envConfig
/** Current Environment Data. */
- const _environmentData =
- environments && environments.find((environment) => environment.id === +params[paramToCheck])
- const selectedNavEnvironmentOptions = {
- ..._environmentData,
- label: _environmentData?.name || BASE_CONFIGURATIONS.name,
- value: _environmentData?.id || BASE_CONFIGURATIONS.id,
- isProtected: _environmentData?.isProtected,
- }
-
- const selectedEnvironmentWithBaseConfiguration = showBaseConfigurations
- ? {
- label: BASE_CONFIGURATIONS.name,
- value: BASE_CONFIGURATIONS.id,
- endIcon: isBaseConfigProtected && ,
- isProtected: isBaseConfigProtected,
- }
- : null
-
- const environmentData = selectedNavEnvironmentOptions || selectedEnvironmentWithBaseConfiguration
-
+ const environmentData =
+ environments.find((environment) => environment.id === +params[paramToCheck]) ||
+ (showBaseConfigurations
+ ? {
+ name: BASE_CONFIGURATIONS.name,
+ id: BASE_CONFIGURATIONS.id,
+ isProtected: isBaseConfigProtected,
+ }
+ : null)
const resourceType = resourceTypeBasedOnPath(pathname)
const isCreate = pathname.includes('/create')
@@ -89,13 +87,15 @@ export const EnvConfigurationsNav = ({
const envConfigKey =
resourceType === EnvResourceType.ConfigMap ? EnvConfigObjectKey.ConfigMap : EnvConfigObjectKey.Secret
+ setExpandedIds({ ...expandedIds, [resourceType]: true })
+
return {
..._updatedEnvConfig,
[envConfigKey]: [
..._updatedEnvConfig[envConfigKey],
{
title: 'Unnamed',
- href: getNavigationPath(path, params, environmentData.value, resourceType, 'create', paramToCheck),
+ href: getNavigationPath(path, params, resourceType, 'create'),
configState: ResourceConfigState.Unnamed,
subtitle: '',
},
@@ -103,6 +103,14 @@ export const EnvConfigurationsNav = ({
}
}
+ useEffect(() => {
+ if (environmentData.id === BASE_CONFIGURATIONS.id && envId) {
+ // Removing `/env-override/:envId` from pathname, resulting path will be base configuration path.
+ const [basePath, resourcePath] = pathname.split(`/${URLS.APP_ENV_OVERRIDE_CONFIG}/${envId}`)
+ history.push(`${basePath}${resourcePath}`)
+ }
+ }, [environmentData, envId])
+
useEffect(() => {
// Fetch the env configuration
fetchEnvConfig(+(envId || BASE_CONFIGURATIONS.id))
@@ -114,7 +122,7 @@ export const EnvConfigurationsNav = ({
useEffect(() => {
if (!isLoading && config) {
- const newEnvConfig = getEnvConfiguration(config, path, params, environmentData, paramToCheck)
+ const newEnvConfig = getEnvConfiguration(config, path, params, environmentData.isProtected)
setUpdatedEnvConfig(isCreate ? addUnnamedNavLink(newEnvConfig) : newEnvConfig)
}
}, [isLoading, config, pathname])
@@ -170,11 +178,11 @@ export const EnvConfigurationsNav = ({
return
}
setExpandedIds({ ...expandedIds, [_resourceType]: true })
- history.push(getNavigationPath(path, params, environmentData.value, _resourceType, 'create', paramToCheck))
+ history.push(getNavigationPath(path, params, _resourceType, 'create'))
}
/** Collapsible List Config. */
- const collapsibleListConfig: CollapsibleListConfig[] = [
+ const collapsibleListConfig: CollapsibleListConfig<'navLink'>[] = [
{
header: 'ConfigMaps',
id: EnvResourceType.ConfigMap,
@@ -221,40 +229,71 @@ export const EnvConfigurationsNav = ({
}
: {}),
},
- items: updatedEnvConfig.secrets,
+ items: updatedEnvConfig.secrets.map((secret) => {
+ const { title, subtitle, href, iconConfig } = secret
+ return { title, subtitle, href, iconConfig }
+ }),
noItemsText: 'No secrets',
isExpanded: expandedIds?.secrets,
},
]
// REACT SELECT PROPS
- const getSelectOptions = () =>
- [...environments].map((env) => ({
- label: env.name,
- value: env.id,
- endIcon: env.isProtected ? : null,
- }))
-
- const envOptions: SelectPickerOptionType[] = [
- ...(showBaseConfigurations
- ? [
- {
- label: BASE_CONFIGURATIONS.name,
- value: BASE_CONFIGURATIONS.id,
- endIcon: isBaseConfigProtected && ,
- },
- ]
- : []),
- ...getSelectOptions(),
+ const baseEnvOption = showBaseConfigurations
+ ? [
+ {
+ label: BASE_CONFIGURATIONS.name,
+ value: BASE_CONFIGURATIONS.id,
+ endIcon: isBaseConfigProtected ? : null,
+ },
+ ]
+ : []
+
+ const envOptions: OptionsOrGroups, GroupBase>> = [
+ ...baseEnvOption,
+ {
+ label: paramToCheck === 'envId' ? 'Environments' : 'Applications',
+ options: environments.map(({ name, id, isProtected }) => ({
+ label: name,
+ value: id,
+ endIcon: isProtected ? : null,
+ })),
+ },
]
- const onEnvSelect = (_selectedEnv) => {
- if (environmentData.value === _selectedEnv.value) {
+ const onEnvSelect = ({ value }: SelectPickerOptionType) => {
+ // Exit early if the selected environment is the current one
+ if (environmentData.id === value) {
return
}
- const name = pathname.split(`${resourceType}/`)[1]
- history.push(getNavigationPath(path, params, _selectedEnv.value, resourceType, name, paramToCheck))
+ // Extract the resource name from the current pathname based on resourceType
+ const resourceName = pathname.split(`${resourceType}/`)[1]
+
+ // Truncate the path to the base application configuration path
+ const truncatedPath = `${path.split(URLS.APP_CONFIG)[0]}${URLS.APP_CONFIG}`
+
+ // Build the new app path, conditionally adding the environment override config when switching to environment
+ const appPath = `${truncatedPath}${
+ value !== BASE_CONFIGURATIONS.id ? `/${URLS.APP_ENV_OVERRIDE_CONFIG}/:envId(\\d+)?` : ''
+ }/:resourceType(${Object.values(EnvResourceType).join('|')})` // Dynamically set valid resource types
+
+ // Generate the final path
+ // if application/job (paramToCheck = envId), use `appPath`
+ // otherwise applicationGroups (paramToCheck = 'appId'), use `path`
+ const generatedPath = `${generatePath(paramToCheck === 'envId' ? appPath : path, {
+ ...params,
+ [paramToCheck]: value,
+ })}${resourceName ? `/${resourceName}` : ''}`
+
+ // Navigate to the generated path
+ history.push(generatedPath)
+ }
+
+ const handleDeploymentTemplateNavLinkOnClick = (e: MouseEvent) => {
+ if (pathname === updatedEnvConfig.deploymentTemplate.href) {
+ e.preventDefault()
+ }
}
const renderEnvSelector = () => (
@@ -264,52 +303,61 @@ export const EnvConfigurationsNav = ({
- `${option.label}`}
- getOptionValue={(option) => `${option.value}`}
- onChange={onEnvSelect}
- placeholder="Select Environment"
- variant={SelectPickerVariantType.BORDER_LESS}
- />
+
+
+ inputId="env-config-selector"
+ classNamePrefix="env-config-selector"
+ variant={SelectPickerVariantType.BORDER_LESS}
+ isClearable={false}
+ value={getSelectPickerOptionByValue(envOptions, +params[paramToCheck], baseEnvOption[0])}
+ options={envOptions}
+ onChange={onEnvSelect}
+ placeholder="Select Environment"
+ showSelectedOptionIcon={false}
+ />
+
+ {environmentData?.isProtected && }
)
const renderCompareWithBtn = () => {
- const { label: compareTo } = environmentData
-
- // Determine base path based on pathname
- const isOverrideConfig = pathname.includes(URLS.APP_ENV_OVERRIDE_CONFIG)
- const basePath = isOverrideConfig
- ? pathname.split(URLS.APP_ENV_OVERRIDE_CONFIG)[0]
- : `${pathname.split(URLS.APP_CONFIG)[0]}${URLS.APP_CONFIG}`
-
- // Determine comparePath based on paramToCheck
- let comparePath = ''
- if (paramToCheck === 'envId') {
- comparePath = isOverrideConfig
- ? `${basePath}${envId}/${URLS.APP_ENV_CONFIG_COMPARE}/${compareTo}${pathname.split(`${URLS.APP_ENV_OVERRIDE_CONFIG}/${envId}`)[1]}`
- : `${basePath}/${URLS.APP_ENV_CONFIG_COMPARE}${pathname.split(URLS.APP_CONFIG)[1]}`
- } else if (paramToCheck === 'appId') {
- comparePath = `${basePath}/${appId}/${URLS.APP_ENV_CONFIG_COMPARE}/${compareTo}${pathname.split(`${URLS.APP_CONFIG}/${appId}`)[1]}`
- }
+ const { name: compareTo } = environmentData
+
+ // Extract the resource name from the current pathname based on resourceType
+ const resourceName = pathname.split(`/${resourceType}/`)[1]
+
+ // Construct the compare view path with dynamic route parameters for comparison
+ const compareViewPath = `${compareWithURL}/${URLS.APP_ENV_CONFIG_COMPARE}/:compareTo?/:resourceType(${Object.values(EnvResourceType).join('|')})/:resourceName?`
+
+ const compareWithHref = generatePath(compareViewPath, {
+ ...params,
+ // Only set compareTo if it's not the base configuration
+ compareTo: compareTo !== BASE_CONFIGURATIONS.name ? compareTo : null,
+ resourceType,
+ resourceName: resourceName ?? null,
+ })
return (
+
+ {!hideEnvSelector && renderEnvSelector()}
+ {showComparison && renderCompareWithBtn()}
+
{isLoading || !environmentData ? (
['90', '70', '50'].map((item) => )
) : (
@@ -317,17 +365,25 @@ export const EnvConfigurationsNav = ({
{showDeploymentTemplate && updatedEnvConfig.deploymentTemplate && (
- {updatedEnvConfig.deploymentTemplate.title}
+
+
+ {updatedEnvConfig.deploymentTemplate.title}
+
{renderDeploymentTemplateNavIcon()}
)}
-
+
>
)}
- >
+
)
}
diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvironmentOverrideRouter.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvironmentOverrideRouter.tsx
similarity index 100%
rename from src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvironmentOverrideRouter.tsx
rename to apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvironmentOverrideRouter.tsx
diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/Navigation.helper.tsx b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/Navigation.helper.tsx
similarity index 77%
rename from src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/Navigation.helper.tsx
rename to apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/Navigation.helper.tsx
index 10cf1d3bcc..9134abd00f 100644
--- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/Navigation.helper.tsx
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/Navigation.helper.tsx
@@ -6,7 +6,6 @@ import { ReactComponent as Lock } from '@Icons/ic-locked.svg'
import { ReactComponent as ProtectedIcon } from '@Icons/ic-shield-protect-fill.svg'
import { ReactComponent as ICStamp } from '@Icons/ic-stamp.svg'
import { ReactComponent as ICEditFile } from '@Icons/ic-edit-file.svg'
-import { URLS } from '@Config/routes'
import { ResourceConfigStage, ResourceConfigState } from '@Pages/Applications/DevtronApps/service.types'
import {
@@ -16,7 +15,6 @@ import {
ExtendedCollapsibleListItem,
EnvConfigObjectKey,
} from '../AppConfig.types'
-import { BASE_CONFIGURATIONS } from '../AppConfig.constants'
const renderNavItemIcon = (isLocked: boolean, isProtected: boolean, dataTestId: string) => {
if (isLocked) {
@@ -56,28 +54,14 @@ export const renderNavItem = (item: CustomNavItemsType, isBaseConfigProtected?:
* @param params - URL parameters
* @param resourceType - The type of resource.
* @param href - An optional href to append to the path.
- * @param paramToCheck - The parameter to check in the URL.
* @returns The generated URL path.
*/
export const getNavigationPath = (
basePath: string,
params: EnvConfigRouteParams,
- id: number,
resourceType: EnvResourceType,
href?: string,
- paramToCheck: 'appId' | 'envId' = 'envId',
-) => {
- const additionalPath = href ? `/${href}` : ''
- const isEnvIdChanged = paramToCheck === 'envId'
- const isBaseEnv = id === BASE_CONFIGURATIONS.id
- const _resourceType = isEnvIdChanged && !isBaseEnv ? URLS.APP_ENV_OVERRIDE_CONFIG : resourceType
-
- return `${generatePath(basePath, {
- ...params,
- resourceType: _resourceType,
- [paramToCheck]: !isBaseEnv ? id : null,
- })}${isEnvIdChanged && !isBaseEnv ? `/${resourceType}${additionalPath}` : `${additionalPath}`}`
-}
+) => `${generatePath(basePath, { ...params, resourceType })}${href ? `/${href}` : ''}`
/**
* Returns an object containing the appropriate icon, icon properties and tooltip properties based on the resource configuration state.
@@ -85,7 +69,10 @@ export const getNavigationPath = (
* @param configState - The state of the resource configuration.
* @returns An object containing the icon, props and tooltipProps if conditions are met, otherwise null.
*/
-const getIcon = (configState: ResourceConfigState, isProtected: boolean): CollapsibleListItem['iconConfig'] => {
+const getIcon = (
+ configState: ResourceConfigState,
+ isProtected: boolean,
+): CollapsibleListItem<'navLink'>['iconConfig'] => {
if (isProtected && configState !== ResourceConfigState.Published && configState !== ResourceConfigState.Unnamed) {
return {
Icon: configState === ResourceConfigState.ApprovalPending ? ICStamp : ICEditFile,
@@ -93,9 +80,10 @@ const getIcon = (configState: ResourceConfigState, isProtected: boolean): Collap
content: configState === ResourceConfigState.ApprovalPending ? 'Approval pending' : 'Draft',
placement: 'right',
arrow: false,
+ className: 'default-tt',
},
props: {
- className: configState === ResourceConfigState.Draft ? 'scn-6' : '',
+ className: `p-2 ${configState === ResourceConfigState.Draft ? 'scn-6' : ''}`,
},
}
}
@@ -114,8 +102,7 @@ export const getEnvConfiguration = (
envConfig: EnvConfigType,
basePath: string,
params: EnvConfigRouteParams,
- { value: id, isProtected },
- paramToCheck: 'appId' | 'envId' = 'envId',
+ isProtected: boolean,
): {
deploymentTemplate: ExtendedCollapsibleListItem
configmaps: ExtendedCollapsibleListItem[]
@@ -130,14 +117,7 @@ export const getEnvConfiguration = (
configState: envConfig[curr].configState,
title: 'Deployment Template',
subtitle: SUBTITLE[envConfig[curr].configStage],
- href: getNavigationPath(
- basePath,
- params,
- id,
- EnvResourceType.DeploymentTemplate,
- '',
- paramToCheck,
- ),
+ href: getNavigationPath(basePath, params, EnvResourceType.DeploymentTemplate),
iconConfig: getIcon(envConfig[curr].configState, isProtected),
}
: envConfig[curr].map(({ configState, name, configStage }) => ({
@@ -146,12 +126,10 @@ export const getEnvConfiguration = (
href: getNavigationPath(
basePath,
params,
- id,
curr === EnvConfigObjectKey.ConfigMap
? EnvResourceType.ConfigMap
: EnvResourceType.Secret,
name,
- paramToCheck,
),
iconConfig: getIcon(configState, isProtected),
subtitle: SUBTITLE[configStage],
@@ -171,9 +149,5 @@ export const resourceTypeBasedOnPath = (pathname: string) => {
if (pathname.includes(`/${EnvResourceType.Secret}`)) {
return EnvResourceType.Secret
}
- if (pathname.includes(`/${EnvResourceType.DeploymentTemplate}`)) {
- return EnvResourceType.DeploymentTemplate
- }
-
- return null
+ return EnvResourceType.DeploymentTemplate
}
diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/appConfig.scss b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/appConfig.scss
similarity index 98%
rename from src/Pages/Applications/DevtronApps/Details/AppConfigurations/appConfig.scss
rename to apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/appConfig.scss
index 28c0b59ee3..ace4b7516c 100644
--- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/appConfig.scss
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/appConfig.scss
@@ -30,7 +30,7 @@
}
.app-compose__nav {
- padding: 16px 12px 0;
+ padding: 16px 12px;
border-right: solid 1px #d6dbdf;
background: white;
height: 100%;
@@ -191,12 +191,6 @@
}
}
- .shebang {
- padding: 0 60px;
- color: #151515;
- opacity: 0.6;
- }
-
.yaml-container {
border: 1px solid var(--N200);
border-radius: 4px;
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/index.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/index.ts
new file mode 100644
index 0000000000..7c0259c50d
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/AppConfigurations/index.ts
@@ -0,0 +1 @@
+export * from './MainContent'
diff --git a/apps/web/src/Pages/Applications/DevtronApps/Details/index.ts b/apps/web/src/Pages/Applications/DevtronApps/Details/index.ts
new file mode 100644
index 0000000000..922ce5973d
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/Details/index.ts
@@ -0,0 +1 @@
+export * from './AppConfigurations'
diff --git a/apps/web/src/Pages/Applications/DevtronApps/index.ts b/apps/web/src/Pages/Applications/DevtronApps/index.ts
new file mode 100644
index 0000000000..fdbadef09a
--- /dev/null
+++ b/apps/web/src/Pages/Applications/DevtronApps/index.ts
@@ -0,0 +1 @@
+export * from './Details'
diff --git a/src/Pages/Applications/DevtronApps/service.ts b/apps/web/src/Pages/Applications/DevtronApps/service.ts
similarity index 100%
rename from src/Pages/Applications/DevtronApps/service.ts
rename to apps/web/src/Pages/Applications/DevtronApps/service.ts
diff --git a/src/Pages/Applications/DevtronApps/service.types.ts b/apps/web/src/Pages/Applications/DevtronApps/service.types.ts
similarity index 100%
rename from src/Pages/Applications/DevtronApps/service.types.ts
rename to apps/web/src/Pages/Applications/DevtronApps/service.types.ts
diff --git a/apps/web/src/Pages/Applications/index.ts b/apps/web/src/Pages/Applications/index.ts
new file mode 100644
index 0000000000..7fd4cacc27
--- /dev/null
+++ b/apps/web/src/Pages/Applications/index.ts
@@ -0,0 +1 @@
+export * from './DevtronApps'
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/APITokenList.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/APITokenList.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/APITokenList.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/APITokenList.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/CreateAPIToken.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/CreateAPIToken.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/CreateAPIToken.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/CreateAPIToken.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/DeleteAPITokenModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/DeleteAPITokenModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/DeleteAPITokenModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/DeleteAPITokenModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/EditAPIToken.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/EditAPIToken.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/EditAPIToken.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/EditAPIToken.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/ExpirationDate.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/ExpirationDate.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/ExpirationDate.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/ExpirationDate.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateActionButton.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateActionButton.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateActionButton.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateActionButton.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/GenerateModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/RegenerateModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/RegenerateModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/RegenerateModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/RegenerateModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/__mocks__/ApiTokens.mock.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/__mocks__/ApiTokens.mock.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/__mocks__/ApiTokens.mock.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/__mocks__/ApiTokens.mock.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/__tests__/ApiTokens.test.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/__tests__/ApiTokens.test.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/__tests__/ApiTokens.test.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/__tests__/ApiTokens.test.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.scss b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.scss
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.scss
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.scss
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.type.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.type.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.type.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.type.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.utils.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.utils.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.utils.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/apiToken.utils.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/service.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/service.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/service.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/service.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/validationRules.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/validationRules.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/APITokens/validationRules.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/APITokens/validationRules.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Authorization.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Authorization.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Authorization.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Authorization.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/AuthorizationProvider.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/AuthorizationProvider.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/AuthorizationProvider.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/AuthorizationProvider.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupAddEdit.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupAddEdit.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupAddEdit.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupAddEdit.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupForm.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupForm.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupForm.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/PermissionGroupForm.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/AddEdit/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/ExportPermissionGroupsToCsv.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/ExportPermissionGroupsToCsv.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/ExportPermissionGroupsToCsv.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/ExportPermissionGroupsToCsv.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/NoPermissionGroups.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/NoPermissionGroups.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/NoPermissionGroups.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/NoPermissionGroups.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupContainer.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupContainer.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupContainer.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupContainer.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupList.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupList.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupList.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupList.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupListHeader.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupListHeader.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupListHeader.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupListHeader.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupRow.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupRow.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupRow.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupRow.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupTable.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupTable.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupTable.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupTable.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/constants.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/constants.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/constants.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/constants.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/types.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/types.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/PermissionGroups.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/PermissionGroups.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/PermissionGroups.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/PermissionGroups.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/PermissionGroups/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/PermissionGroups/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/README.md b/apps/web/src/Pages/GlobalConfigurations/Authorization/README.md
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/README.md
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/README.md
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/SSOLogin.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/SSOLogin.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/SSOLogin.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/SSOLogin.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/constants.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/constants.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/constants.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/constants.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/sampleSSOConfig.json b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/sampleSSOConfig.json
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/sampleSSOConfig.json
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/sampleSSOConfig.json
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/service.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/service.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/service.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/service.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoConfig.types.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoConfig.types.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoConfig.types.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoConfig.types.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoLogin.scss b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoLogin.scss
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoLogin.scss
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/ssoLogin.scss
diff --git a/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/utils.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/utils.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/utils.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/SSOLoginServices/utils.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissionDetail.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissionDetail.tsx
similarity index 99%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissionDetail.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissionDetail.tsx
index b9d3ab54a9..35a3ac1182 100644
--- a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissionDetail.tsx
+++ b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissionDetail.tsx
@@ -41,7 +41,7 @@ const AppPermissionDetail = ({
Project
- Environment{accessType === ACCESS_TYPE_MAP.HELM_APPS ? 'or cluster/namespace' : ''}
+ Environment{accessType === ACCESS_TYPE_MAP.HELM_APPS ? ' or cluster/namespace' : ''}
{isAccessTypeJob ? 'Job Name' : 'Application'}
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissions.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissions.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissions.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/AppPermissions.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/DirectPermission.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/DirectPermission.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/DirectPermission.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/DirectPermission.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/common.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/common.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/common.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/common.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/constants.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/constants.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/constants.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/constants.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/types.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/types.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/utils.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/utils.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/utils.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/AppPermissions/utils.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkDeleteModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkDeleteModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkDeleteModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkDeleteModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionActionWidget.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionActionWidget.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionActionWidget.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionActionWidget.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionClearConfirmationModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionClearConfirmationModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionClearConfirmationModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionClearConfirmationModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/BulkSelectionModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/constants.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/constants.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/constants.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/constants.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/types.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/types.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/useAuthorizationBulkSelection.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/useAuthorizationBulkSelection.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/useAuthorizationBulkSelection.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/BulkSelection/useAuthorizationBulkSelection.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/ChartPermission.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/ChartPermission.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/ChartPermission.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/ChartPermission.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/constants.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/constants.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/constants.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/constants.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/types.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/ChartPermission/types.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionModal.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionModal.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionRow.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionRow.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionRow.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissionRow.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissions.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissions.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissions.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sPermissions.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/constants.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/constants.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/constants.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/constants.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/types.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/types.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/utils.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/utils.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/utils.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/utils.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationForm.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationForm.component.tsx
similarity index 82%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationForm.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationForm.component.tsx
index 220b99cea6..46d9482fba 100644
--- a/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationForm.component.tsx
+++ b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationForm.component.tsx
@@ -23,7 +23,10 @@ import { UserPermissionGroupsSelector } from '../UserPermissionGroupsSelector'
import { usePermissionConfiguration } from './PermissionConfigurationFormProvider'
import { PermissionConfigurationFormProps } from './types'
-const PermissionConfigurationForm = ({ showUserPermissionGroupSelector = false }: PermissionConfigurationFormProps) => {
+const PermissionConfigurationForm = ({
+ showUserPermissionGroupSelector = false,
+ hideDirectPermissions = false,
+}: PermissionConfigurationFormProps) => {
const { permissionType, setPermissionType } = usePermissionConfiguration()
const isSuperAdminPermission = getIsSuperAdminPermission(permissionType)
@@ -62,10 +65,19 @@ const PermissionConfigurationForm = ({ showUserPermissionGroupSelector = false }
{showUserPermissionGroupSelector && (
<>
-
+ {!hideDirectPermissions &&
}
>
)}
-
+
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationFormProvider.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationFormProvider.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationFormProvider.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/PermissionConfigurationFormProvider.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/types.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/types.ts
similarity index 93%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/types.ts
index efed162025..53bd0800d5 100644
--- a/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/types.ts
+++ b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/PermissionConfigurationForm/types.ts
@@ -26,6 +26,12 @@ import {
export type PermissionConfigurationFormProps = {
showUserPermissionGroupSelector: boolean
+ /**
+ * If true, direct permissions are hidden
+ *
+ * @default false
+ */
+ hideDirectPermissions?: boolean
}
export interface PermissionConfigurationFormContext {
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/SuperAdminInfoBar.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/SuperAdminInfoBar.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/SuperAdminInfoBar.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/SuperAdminInfoBar.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/SuperAdminInfoBar/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/UserPermissionGroupsSelector.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/UserPermissionGroupsSelector.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/UserPermissionGroupsSelector.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/UserPermissionGroupsSelector.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/Shared/components/UserPermissionGroupsSelector/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserAndGroupPermissions.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserAndGroupPermissions.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserAndGroupPermissions.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserAndGroupPermissions.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx
similarity index 97%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx
index 76c9f4c4b8..8a2347ddf7 100644
--- a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx
+++ b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx
@@ -61,7 +61,7 @@ const createOption = (label: string) => ({
})
const UserForm = ({ isAddMode }: { isAddMode: boolean }) => {
- const { serverMode } = useMainContext()
+ const { serverMode, isSuperAdmin } = useMainContext()
const { isAutoAssignFlowEnabled } = useAuthorizationContext()
@@ -373,7 +373,14 @@ const UserForm = ({ isAddMode }: { isAddMode: boolean }) => {
{!isAddMode && isAutoAssignFlowEnabled && (
)}
- {!isAutoAssignFlowEnabled &&
}
+ {!isAutoAssignFlowEnabled && (
+
+ )}
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserPermissionAddEdit.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserPermissionAddEdit.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserPermissionAddEdit.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserPermissionAddEdit.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/index.ts b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/index.ts
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/ExportUserPermissionsToCsv.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/ExportUserPermissionsToCsv.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/ExportUserPermissionsToCsv.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/ExportUserPermissionsToCsv.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/NoUsers.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/NoUsers.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/NoUsers.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/NoUsers.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionContainer.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionContainer.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionContainer.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionContainer.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionList.component.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionList.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionList.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionList.component.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionListHeader.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionListHeader.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionListHeader.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionListHeader.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionRow.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionRow.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionRow.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionRow.tsx
diff --git a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionTable.tsx b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionTable.tsx
similarity index 93%
rename from src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionTable.tsx
rename to apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionTable.tsx
index 087ce74c21..fc8218d0ec 100644
--- a/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionTable.tsx
+++ b/apps/web/src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionTable.tsx
@@ -78,16 +78,7 @@ const UserPermissionTable = ({
sortOrder={sortOrder}
disabled={isActionsDisabled}
/>
- {showStatus && (
-
- )}
+ {showStatus && }
value === 'true')}
+ {...register('isProduction', { sanitizeFn: (value) => value === 'true' })}
/>
Production
@@ -289,7 +289,7 @@ export const ClusterEnvironmentDrawer = ({
type="radio"
checked={!data.isProduction}
value="false"
- {...register('isProduction', (value) => value === 'true')}
+ {...register('isProduction', { sanitizeFn: (value) => value === 'true' })}
/>
Non - Production
diff --git a/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/index.ts b/apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/index.ts
diff --git a/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/schema.ts b/apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/schema.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/schema.ts
rename to apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/schema.ts
diff --git a/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/types.ts b/apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/types.ts
diff --git a/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/utils.ts b/apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/utils.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/utils.ts
rename to apps/web/src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer/utils.ts
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/DeploymentChartsRouter.component.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/DeploymentChartsRouter.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/DeploymentChartsRouter.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/DeploymentChartsRouter.component.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsList.component.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsList.component.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsList.component.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsList.component.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsListHeader.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsListHeader.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsListHeader.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/DeploymentChartsListHeader.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/DownloadChartButton.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/DownloadChartButton.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/DownloadChartButton.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/DownloadChartButton.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadButton.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadButton.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadButton.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadButton.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadChartModal.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadChartModal.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadChartModal.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadChartModal.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/index.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/index.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/index.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/index.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/service.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/service.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/service.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/service.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/List/styles.scss b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/styles.scss
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/List/styles.scss
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/List/styles.scss
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/index.tsx b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/index.tsx
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/index.tsx
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/index.tsx
diff --git a/src/Pages/GlobalConfigurations/DeploymentCharts/types.ts b/apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/types.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/DeploymentCharts/types.ts
rename to apps/web/src/Pages/GlobalConfigurations/DeploymentCharts/types.ts
diff --git a/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/OfflinePipelineModalAppView.tsx b/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/OfflinePipelineModalAppView.tsx
new file mode 100644
index 0000000000..defd299bd0
--- /dev/null
+++ b/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/OfflinePipelineModalAppView.tsx
@@ -0,0 +1,104 @@
+import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'
+import { APIResponseHandler, GenericEmptyState, ImageType, noop, useAsync } from '@devtron-labs/devtron-fe-common-lib'
+import noOffendingPipelineImg from '@Images/no-offending-pipeline.svg'
+import { WorkflowCreate } from '@Components/app/details/triggerView/config'
+import { getInitialWorkflows } from '@Components/app/details/triggerView/workflow.service'
+import { Workflow } from '@Components/workflowEditor/Workflow'
+import { OffendingPipelineModalAppViewProps } from './types'
+
+const OffendingPipelineModalAppView = ({
+ appId,
+ appName,
+ policyKind,
+ policyName,
+}: OffendingPipelineModalAppViewProps) => {
+ const history = useHistory()
+ const location = useLocation()
+ const match = useRouteMatch()
+
+ const [isWorkflowsLoading, workflowsResponse, workflowsError, refetchWorkflows] = useAsync(
+ () =>
+ getInitialWorkflows({
+ id: appId.toString(),
+ dimensions: WorkflowCreate,
+ workflowOffset: WorkflowCreate.workflow,
+ useAppWfViewAPI: true,
+ isJobView: false,
+ filteredEnvIds: null,
+ shouldCheckDeploymentWindow: false,
+ offending: `policy/${policyKind}|identifier|${policyName}`,
+ }),
+ [appId],
+ !!appId,
+ )
+
+ return (
+
+ {!isWorkflowsLoading && !workflowsError && workflowsResponse.workflows?.length > 0 ? (
+ workflowsResponse.workflows.map((workflow) => (
+
+ ))
+ ) : (
+
+ )}
+
+ )
+}
+
+export default OffendingPipelineModalAppView
diff --git a/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/index.ts b/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/index.ts
new file mode 100644
index 0000000000..ba06bd37f1
--- /dev/null
+++ b/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/index.ts
@@ -0,0 +1 @@
+export { default as OffendingPipelineModalAppView } from './OfflinePipelineModalAppView'
diff --git a/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/types.ts b/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/types.ts
new file mode 100644
index 0000000000..8a28694bc7
--- /dev/null
+++ b/apps/web/src/Pages/GlobalConfigurations/PluginPolicy/OffendingPipelineModal/types.ts
@@ -0,0 +1,8 @@
+import { PolicyKindType } from '@devtron-labs/devtron-fe-common-lib'
+
+export interface OffendingPipelineModalAppViewProps {
+ appId: number
+ appName: string
+ policyKind: PolicyKindType
+ policyName: string
+}
diff --git a/src/Pages/GlobalConfigurations/index.ts b/apps/web/src/Pages/GlobalConfigurations/index.ts
similarity index 100%
rename from src/Pages/GlobalConfigurations/index.ts
rename to apps/web/src/Pages/GlobalConfigurations/index.ts
diff --git a/apps/web/src/Pages/Releases/Detail/Configurations/Configurations.tsx b/apps/web/src/Pages/Releases/Detail/Configurations/Configurations.tsx
new file mode 100644
index 0000000000..5cc30439af
--- /dev/null
+++ b/apps/web/src/Pages/Releases/Detail/Configurations/Configurations.tsx
@@ -0,0 +1,179 @@
+import { useMemo } from 'react'
+import { generatePath, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'
+
+import {
+ DeploymentHistoryBaseParamsType,
+ EnvResourceType,
+ GenericEmptyState,
+ Progressing,
+ useAsync,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { URLS } from '@Config/routes'
+import { importComponentFromFELibrary } from '@Components/common'
+import { ConfigMapSecretWrapper, CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret'
+import { DeploymentConfigCompare, DeploymentTemplate } from '@Pages/Applications'
+import { getEnvConfig } from '@Pages/Applications/DevtronApps/service'
+import { EnvConfigurationsNav } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav'
+
+import { ReleaseConfigurationContextType } from './types'
+
+import './styles.scss'
+
+const useReleaseConfigurationContext = importComponentFromFELibrary('useReleaseConfigurationContext', null, 'function')
+const ConfigurationsAppEnvSelector = importComponentFromFELibrary('ConfigurationsAppEnvSelector', null, 'function')
+
+const renderNullState = () => (
+
+
+
+)
+
+export const Configurations = () => {
+ // HOOKS
+ const { path, params } = useRouteMatch>()
+ const { appId, envId } = params
+
+ // CONTEXTS
+ const {
+ environments,
+ applications,
+ reloadEnvironments,
+ isAppListLoading,
+ isEnvListLoading,
+ }: ReleaseConfigurationContextType = useReleaseConfigurationContext()
+
+ // ASYNC CALLS
+ const [envConfigResLoading, envConfigRes, , fetchEnvConfig] = useAsync(
+ () => getEnvConfig(+appId, +envId),
+ [appId, envId],
+ !!(appId && envId),
+ )
+
+ // CONSTANTS
+ const envConfig = {
+ config: envConfigRes,
+ isLoading: envConfigResLoading,
+ }
+ const selectedApp = useMemo(
+ () => (applications ? applications.find(({ value }) => +appId === value) : null),
+ [applications, appId, isAppListLoading],
+ )
+ const selectedEnv = useMemo(
+ () => (environments ? environments.find(({ id }) => +envId === id) : null),
+ [environments, envId, isEnvListLoading],
+ )
+ const showConfig = !!selectedApp && !!selectedEnv
+
+ // RENDERERS
+ const renderConfigSideNav = () => (
+
+
+
+
+
+ )
+
+ const renderConfig = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+ return (
+
+
+ {({ match, location }) => {
+ const basePath = generatePath(path, match.params)
+ // Set the resourceTypePath based on the resourceType from the URL parameters.
+ // If the resourceType is 'Manifest' or 'PipelineStrategy', use 'deployment-template' as the back URL.
+ // Otherwise, use the actual resourceType from the URL, which could be 'deployment-template', 'configmap', or 'secrets'.
+ const resourceTypePath = `/${match.params.resourceType === EnvResourceType.Manifest || match.params.resourceType === EnvResourceType.PipelineStrategy ? EnvResourceType.DeploymentTemplate : match.params.resourceType}`
+ const resourceNamePath = match.params.resourceName ? `/${match.params.resourceName}` : ''
+
+ const goBackURL = `${basePath}${resourceTypePath}${resourceNamePath}`
+
+ return showConfig ? (
+
+ `${generatePath(match.path, { ...match.params, resourceType, resourceName })}${location.search}`
+ }
+ />
+ ) : (
+
+ )
+ }}
+
+
+
+
+
+ {showConfig ? renderConfigSideNav() : null}
+
+ {showConfig ? renderConfig() : renderNullState()}
+
+
+
+ )
+}
diff --git a/apps/web/src/Pages/Releases/Detail/Configurations/index.ts b/apps/web/src/Pages/Releases/Detail/Configurations/index.ts
new file mode 100644
index 0000000000..d050a297bc
--- /dev/null
+++ b/apps/web/src/Pages/Releases/Detail/Configurations/index.ts
@@ -0,0 +1 @@
+export * from './Configurations'
diff --git a/apps/web/src/Pages/Releases/Detail/Configurations/styles.scss b/apps/web/src/Pages/Releases/Detail/Configurations/styles.scss
new file mode 100644
index 0000000000..4f66249fbf
--- /dev/null
+++ b/apps/web/src/Pages/Releases/Detail/Configurations/styles.scss
@@ -0,0 +1,3 @@
+.release-configurations {
+ grid-template-columns: 280px 1fr;
+}
diff --git a/apps/web/src/Pages/Releases/Detail/Configurations/types.ts b/apps/web/src/Pages/Releases/Detail/Configurations/types.ts
new file mode 100644
index 0000000000..a3a4ef5183
--- /dev/null
+++ b/apps/web/src/Pages/Releases/Detail/Configurations/types.ts
@@ -0,0 +1,15 @@
+import { SelectPickerOptionType } from '@devtron-labs/devtron-fe-common-lib'
+
+import { EnvironmentOptionType } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/AppConfig.types'
+
+interface AppOptionType extends Omit, 'label'> {
+ label: string
+}
+
+export interface ReleaseConfigurationContextType {
+ environments: EnvironmentOptionType[]
+ applications: AppOptionType[]
+ reloadEnvironments: () => void
+ isAppListLoading: boolean
+ isEnvListLoading: boolean
+}
diff --git a/apps/web/src/Pages/Releases/Detail/index.ts b/apps/web/src/Pages/Releases/Detail/index.ts
new file mode 100644
index 0000000000..d050a297bc
--- /dev/null
+++ b/apps/web/src/Pages/Releases/Detail/index.ts
@@ -0,0 +1 @@
+export * from './Configurations'
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts
new file mode 100644
index 0000000000..5dba77f41a
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2024. Devtron Inc.
+ *
+ * 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 {
+ get,
+ post,
+ trash,
+ ResponseType,
+ getResolvedDeploymentTemplate,
+ ValuesAndManifestFlagDTO,
+ GetResolvedDeploymentTemplateProps,
+ AppEnvDeploymentConfigType,
+ getAppEnvDeploymentConfig,
+ ConfigResourceType,
+ getIsRequestAborted,
+ DraftMetadataDTO,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { Routes } from '@Config/constants'
+
+import { importComponentFromFELibrary } from '@Components/common'
+import {
+ CMSecretDTO,
+ CMSecretComponentType,
+ GetConfigMapSecretConfigDataProps,
+ GetConfigMapSecretConfigDataReturnType,
+ UpdateConfigMapSecretProps,
+ DeleteConfigMapSecretProps,
+ DeleteEnvConfigMapSecretProps,
+ OverrideConfigMapSecretProps,
+ GetCMSecretProps,
+} from './types'
+
+const getDraftByResourceName = importComponentFromFELibrary('getDraftByResourceName', null, 'function')
+
+export const updateConfigMap = ({ id, appId, payload, signal }: UpdateConfigMapSecretProps) =>
+ post(
+ `${Routes.APP_CREATE_CONFIG_MAP}`,
+ {
+ ...(id && { id }),
+ appId,
+ configData: [payload],
+ },
+ { signal },
+ )
+
+export const deleteConfigMap = ({ id, appId, name }: DeleteConfigMapSecretProps) =>
+ trash(`${Routes.APP_CREATE_CONFIG_MAP}/${appId}/${id}?name=${name}`)
+
+export const deleteEnvConfigMap = ({ id, appId, envId, name }: DeleteEnvConfigMapSecretProps) =>
+ trash(`${Routes.APP_CREATE_ENV_CONFIG_MAP}/${appId}/${envId}/${id}?name=${name}`)
+
+export const overRideConfigMap = ({ appId, envId, payload, signal }: OverrideConfigMapSecretProps) =>
+ post(
+ `${Routes.APP_CREATE_ENV_CONFIG_MAP}`,
+ {
+ appId,
+ environmentId: envId,
+ configData: [payload],
+ },
+ { signal },
+ )
+
+export const updateSecret = ({ id, appId, payload, signal }: UpdateConfigMapSecretProps) =>
+ post(
+ `${Routes.APP_CREATE_SECRET}`,
+ {
+ ...(id && { id }),
+ appId,
+ configData: [payload],
+ },
+ { signal },
+ )
+
+export const deleteSecret = ({ id, appId, name }: DeleteConfigMapSecretProps) =>
+ trash(`${Routes.APP_CREATE_SECRET}/${appId}/${id}?name=${name}`)
+
+export const deleteEnvSecret = ({ id, appId, envId, name }: DeleteEnvConfigMapSecretProps) =>
+ trash(`${Routes.APP_CREATE_ENV_SECRET}/${appId}/${envId}/${id}?name=${name}`)
+
+export const overRideSecret = ({ appId, envId, payload, signal }: OverrideConfigMapSecretProps) =>
+ post(
+ `${Routes.APP_CREATE_ENV_SECRET}`,
+ {
+ appId,
+ environmentId: envId,
+ configData: [payload],
+ },
+ { signal },
+ )
+
+export const getCMSecret = ({
+ componentType,
+ id,
+ appId,
+ envId,
+ name,
+ signal,
+}: GetCMSecretProps): Promise> => {
+ let url = ''
+ if (envId !== null && envId !== undefined) {
+ url = `${
+ componentType === CMSecretComponentType.Secret
+ ? Routes.APP_CREATE_ENV_SECRET
+ : Routes.APP_CREATE_ENV_CONFIG_MAP
+ }/edit/${appId}/${envId}`
+ } else {
+ url = `${componentType === CMSecretComponentType.Secret ? Routes.APP_CREATE_SECRET : Routes.APP_CREATE_CONFIG_MAP}/edit/${appId}`
+ }
+ return get(`${url}/${id}?name=${name}`, { signal })
+}
+
+export const getConfigMapSecretConfigData = async ({
+ isJob,
+ appName,
+ envName,
+ componentType,
+ appId,
+ envId,
+ name,
+ resourceId,
+ abortControllerRef,
+}: GetConfigMapSecretConfigDataProps) => {
+ try {
+ const { result } = await (isJob
+ ? getCMSecret({
+ componentType,
+ id: resourceId,
+ appId,
+ name,
+ envId,
+ signal: abortControllerRef.current.signal,
+ })
+ : getAppEnvDeploymentConfig({
+ params: {
+ appName,
+ envName,
+ configType: AppEnvDeploymentConfigType.PUBLISHED_ONLY,
+ resourceId,
+ resourceName: name,
+ resourceType:
+ componentType === CMSecretComponentType.ConfigMap
+ ? ConfigResourceType.ConfigMap
+ : ConfigResourceType.Secret,
+ },
+ signal: abortControllerRef.current.signal,
+ }))
+
+ return result as GetConfigMapSecretConfigDataReturnType
+ } catch (error) {
+ if (!getIsRequestAborted(error)) {
+ throw error
+ }
+
+ return null
+ }
+}
+
+export const getConfigMapSecretConfigDraftData = async ({
+ appId,
+ envId,
+ name,
+ componentType,
+ abortControllerRef,
+}: Pick<
+ GetConfigMapSecretConfigDataProps,
+ 'abortControllerRef' | 'appId' | 'envId' | 'componentType' | 'name'
+>) => {
+ try {
+ const res = await (getDraftByResourceName
+ ? getDraftByResourceName(appId, envId ?? -1, componentType, name, abortControllerRef.current.signal)
+ : null)
+
+ return res ? (res.result as DraftMetadataDTO) : null
+ } catch (error) {
+ if (!getIsRequestAborted(error)) {
+ throw error
+ }
+
+ return null
+ }
+}
+
+export const getConfigMapSecretResolvedValues = (
+ params: Required>,
+ signal?: AbortSignal,
+) =>
+ getResolvedDeploymentTemplate(
+ {
+ ...params,
+ valuesAndManifestFlag: ValuesAndManifestFlagDTO.DEPLOYMENT_TEMPLATE,
+ chartRefId: null,
+ },
+ signal,
+ )
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx
new file mode 100644
index 0000000000..3c445f0c94
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx
@@ -0,0 +1,69 @@
+import { useEffect, useRef } from 'react'
+import { useParams } from 'react-router-dom'
+
+import {
+ abortPreviousRequests,
+ ErrorScreenManager,
+ getIsRequestAborted,
+ Progressing,
+ showError,
+ useAsync,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { getAppChartRefForAppAndEnv } from '@Services/service'
+import { ComponentStates } from '@Pages/Shared/EnvironmentOverride/EnvironmentOverrides.types'
+
+import { CM_SECRET_COMPONENT_NAME } from './constants'
+import { CMSecretComponentType, CMSecretWrapperProps } from './types'
+
+import { ConfigMapSecretContainer } from './ConfigMapSecretContainer'
+import { ConfigMapSecretFormProvider } from './ConfigMapSecretFormContext'
+
+export const ConfigMapSecretWrapper = (props: CMSecretWrapperProps) => {
+ // PROPS
+ const { componentType = CMSecretComponentType.ConfigMap, parentState, setParentState, onErrorRedirectURL } = props
+
+ // HOOKS
+ const { appId, envId, name } = useParams<{ appId: string; envId: string; name: string }>()
+
+ // REFS
+ const abortControllerRef = useRef(new AbortController())
+
+ // ASYNC CALLS
+ const [appChartRefLoading, appChartRefRes, appChartRefErr, reload] = useAsync(
+ () => abortPreviousRequests(() => getAppChartRefForAppAndEnv(+appId, +envId), abortControllerRef),
+ [componentType],
+ )
+
+ useEffect(() => {
+ if (appChartRefRes) {
+ setParentState?.(ComponentStates.loaded)
+ }
+ if (appChartRefErr && !getIsRequestAborted(appChartRefErr)) {
+ setParentState?.(ComponentStates.failed)
+ showError(appChartRefErr)
+ }
+
+ return () => {
+ setParentState?.(ComponentStates.loading)
+ }
+ }, [appChartRefRes, appChartRefErr])
+
+ if (parentState === ComponentStates.loading || appChartRefLoading || getIsRequestAborted(appChartRefErr)) {
+ return
+ }
+
+ if (appChartRefErr) {
+ return
+ }
+
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx
new file mode 100644
index 0000000000..aa2204a3e7
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx
@@ -0,0 +1,826 @@
+import { useEffect, useMemo, useRef, useState } from 'react'
+import { generatePath, Prompt, useHistory, useRouteMatch } from 'react-router-dom'
+import ReactGA from 'react-ga4'
+
+import {
+ abortPreviousRequests,
+ API_STATUS_CODES,
+ ConfigHeaderTabType,
+ ConfigToolbarPopupNodeType,
+ DraftAction,
+ DraftState,
+ ERROR_STATUS_CODE,
+ ErrorScreenManager,
+ noop,
+ OverrideMergeStrategyType,
+ Progressing,
+ ProtectConfigTabsType,
+ ServerErrors,
+ showError,
+ ToastManager,
+ ToastVariantType,
+ useAsync,
+ usePrompt,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { URLS } from '@Config/routes'
+import { UNSAVED_CHANGES_PROMPT_MESSAGE } from '@Config/constants'
+import { ConfigHeader, ConfigToolbar, ConfigToolbarProps, NoOverrideEmptyState } from '@Pages/Applications'
+import { getConfigToolbarPopupConfig } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/utils'
+import { FloatingVariablesSuggestions, importComponentFromFELibrary } from '@Components/common'
+import { EnvConfigObjectKey } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/AppConfig.types'
+
+import {
+ getConfigMapSecretConfigData,
+ getConfigMapSecretConfigDraftData,
+ getConfigMapSecretResolvedValues,
+ overRideConfigMap,
+ overRideSecret,
+ updateConfigMap,
+ updateSecret,
+} from './ConfigMapSecret.service'
+import {
+ getConfigMapSecretDraftAndPublishedData,
+ getConfigMapSecretError,
+ getConfigMapSecretInheritedData,
+ getConfigMapSecretPayload,
+ getConfigMapSecretResolvedData,
+ getConfigMapSecretResolvedDataPayload,
+ getConfigMapSecretStateLabel,
+ hasHashiOrAWS,
+} from './utils'
+import { CM_SECRET_COMPONENT_NAME, CONFIG_MAP_SECRET_NO_DATA_ERROR } from './constants'
+import {
+ CM_SECRET_STATE,
+ CMSecretComponentType,
+ CMSecretDeleteModalType,
+ CMSecretDraftPayloadType,
+ CMSecretPayloadType,
+ ConfigMapSecretContainerProps,
+ ConfigMapSecretFormProps,
+} from './types'
+
+import { ConfigMapSecretDeleteModal } from './ConfigMapSecretDeleteModal'
+import { ConfigMapSecretForm } from './ConfigMapSecretForm'
+import { ConfigMapSecretReadyOnly } from './ConfigMapSecretReadyOnly'
+import { ConfigMapSecretProtected } from './ConfigMapSecretProtected'
+import { ConfigMapSecretNullState } from './ConfigMapSecretNullState'
+import { useConfigMapSecretFormContext } from './ConfigMapSecretFormContext'
+
+import './styles.scss'
+
+const ProtectionViewToolbarPopupNode = importComponentFromFELibrary('ProtectionViewToolbarPopupNode', null, 'function')
+const DraftComments = importComponentFromFELibrary('DraftComments')
+const SaveChangesModal = importComponentFromFELibrary('SaveChangesModal')
+
+export const ConfigMapSecretContainer = ({
+ componentType = CMSecretComponentType.ConfigMap,
+ isJob = false,
+ clusterId,
+ envConfig,
+ isProtected,
+ fetchEnvConfig,
+ onErrorRedirectURL,
+ envName,
+ appName,
+ parentName,
+ appChartRef,
+ reloadEnvironments,
+}: ConfigMapSecretContainerProps) => {
+ // HOOKS
+ const { setFormState, isFormDirty, parsingError, formDataRef } = useConfigMapSecretFormContext()
+ const history = useHistory()
+ const { path, params } = useRouteMatch<{ appId: string; envId: string; name: string }>()
+ const { appId, envId, name } = params
+
+ // REFS
+ const abortControllerRef = useRef()
+
+ // STATES
+ const [configHeaderTab, setConfigHeaderTab] = useState(null)
+ const [mergeStrategy, setMergeStrategy] = useState(OverrideMergeStrategyType.REPLACE)
+ const [selectedProtectionViewTab, setSelectedProtectionViewTab] = useState(null)
+ const [popupNodeType, setPopupNodeType] = useState(null)
+ const [showComments, setShowComments] = useState(false)
+ const [isSubmitting, setIsSubmitting] = useState(false)
+ const [hideNoOverrideEmptyState, setHideNoOverrideEmptyState] = useState(false)
+ const [openDeleteModal, setOpenDeleteModal] = useState(null)
+ const [showDraftSaveModal, setShowDraftSaveModal] = useState(false)
+ const [draftPayload, setDraftPayload] = useState(null)
+ const [resolvedScopeVariables, setResolvedScopeVariables] = useState(false)
+ const [areCommentsPresent, setAreCommentsPresent] = useState(false)
+ const [restoreYAML, setRestoreYAML] = useState(false)
+
+ // CONSTANTS
+ const componentName = CM_SECRET_COMPONENT_NAME[componentType]
+ const isSecret = componentType === CMSecretComponentType.Secret
+
+ const { config, isLoading: isEnvConfigLoading } = envConfig
+ const envConfigData = config?.[isSecret ? EnvConfigObjectKey.Secret : EnvConfigObjectKey.ConfigMap] || []
+
+ const selectedCMSecret = useMemo(() => envConfigData.find((data) => data.name === name), [envConfig, name])
+ const cmSecretStateLabel = getConfigMapSecretStateLabel(selectedCMSecret?.configStage, !!envId)
+
+ const id = selectedCMSecret?.id
+ const isCreateState = name === 'create' && !id
+ const isEmptyState = !name && !envConfigData.length
+
+ // GA EVENT CATEGORY (BASED ON CM/SECRET)
+ const gaEventCategory = `devtronapp-configuration-${isSecret ? 'secret' : 'cm'}`
+
+ // COMPONENT PROP CONSTANTS
+ const baseConfigurationURL = `${isJob ? URLS.JOB : URLS.APP}/${appId}/${URLS.APP_CONFIG}/${isSecret ? URLS.APP_CS_CONFIG : URLS.APP_CM_CONFIG}/${name}`
+ const headerMessage =
+ cmSecretStateLabel === CM_SECRET_STATE.ENV ||
+ cmSecretStateLabel === CM_SECRET_STATE.UNPUBLISHED ||
+ cmSecretStateLabel === CM_SECRET_STATE.BASE
+ ? `${envId ? `This is an environment specific ${componentName}` : `Base ${componentName} is inherited by environments`}`
+ : null
+ /**
+ * * Show the prompt only when not in create mode, as unsaved changes are already handled in ConfigMapSecretForm.
+ * * During creation, route changes (/create -> /{configName}) would trigger an unnecessary prompt, so we skip it in that case.
+ */
+ const shouldPrompt = !isCreateState && isFormDirty
+
+ // PROMPT FOR UNSAVED CHANGES
+ usePrompt({ shouldPrompt })
+
+ // USE EFFECTS
+ useEffect(
+ // Reset the form state after unmounting
+ () => () => {
+ setFormState({ type: 'RESET' })
+ },
+ [],
+ )
+
+ useEffect(() => {
+ abortControllerRef.current = new AbortController()
+ return () => {
+ abortControllerRef.current.abort()
+ }
+ }, [envId, resolvedScopeVariables])
+
+ // ASYNC CALL - CONFIGMAP/SECRET DATA
+ const [configMapSecretResLoading, configMapSecretRes, , reloadConfigMapSecret] = useAsync(
+ () =>
+ abortPreviousRequests(
+ () =>
+ Promise.allSettled([
+ // Fetch Published Configuration
+ cmSecretStateLabel !== CM_SECRET_STATE.UNPUBLISHED
+ ? getConfigMapSecretConfigData({
+ appId: +appId,
+ appName,
+ envId: envId ? +envId : null,
+ envName,
+ componentType,
+ name,
+ resourceId: id,
+ isJob,
+ abortControllerRef,
+ })
+ : null,
+ // Fetch Base Configuration (Inherited Tab Data)
+ cmSecretStateLabel === CM_SECRET_STATE.INHERITED ||
+ cmSecretStateLabel === CM_SECRET_STATE.OVERRIDDEN
+ ? getConfigMapSecretConfigData({
+ appId: +appId,
+ appName,
+ envId: null,
+ envName: '',
+ componentType,
+ name,
+ resourceId: isJob ? id : null,
+ isJob,
+ abortControllerRef,
+ })
+ : null,
+ // Fetch Draft Configuration
+ isProtected
+ ? getConfigMapSecretConfigDraftData({
+ appId: +appId,
+ envId: envId ? +envId : -1,
+ componentType,
+ name,
+ abortControllerRef,
+ })
+ : null,
+ ]),
+ abortControllerRef,
+ ),
+ [],
+ !isEnvConfigLoading && !!selectedCMSecret && !isCreateState,
+ )
+
+ // CONFIGMAP/SECRET DATA
+ const { configMapSecretData, inheritedConfigMapSecretData, draftData } = useMemo(() => {
+ if (!configMapSecretResLoading && configMapSecretRes) {
+ const data = getConfigMapSecretDraftAndPublishedData({
+ cmSecretConfigDataRes: configMapSecretRes[0],
+ draftConfigDataRes: configMapSecretRes[2],
+ isSecret,
+ isJob,
+ })
+
+ return {
+ ...data,
+ inheritedConfigMapSecretData: getConfigMapSecretInheritedData({
+ cmSecretConfigDataRes: configMapSecretRes[1],
+ isJob,
+ isSecret,
+ }),
+ }
+ }
+
+ return { configMapSecretData: null, draftData: null, inheritedConfigMapSecretData: null }
+ }, [configMapSecretResLoading, configMapSecretRes])
+
+ // CONFIGMAP/SECRET DELETED
+ const configHasBeenDeleted = useMemo(
+ () =>
+ !configMapSecretResLoading && configMapSecretRes
+ ? !configMapSecretData && !inheritedConfigMapSecretData && !draftData
+ : null,
+ [configMapSecretResLoading, configMapSecretRes],
+ )
+
+ // CONFIGMAP/SECRET ERROR
+ const configMapSecretResErr = useMemo(
+ () =>
+ !configMapSecretResLoading && configMapSecretRes
+ ? getConfigMapSecretError(configMapSecretRes[0]) ||
+ getConfigMapSecretError(configMapSecretRes[1]) ||
+ getConfigMapSecretError(configMapSecretRes[2])
+ : null,
+ [configMapSecretResLoading, configMapSecretRes],
+ )
+
+ // ASYNC CALL - CONFIGMAP/SECRET RESOLVED DATA
+ const [resolvedScopeVariablesResLoading, resolvedScopeVariablesRes, reloadResolvedScopeVariablesResErr] = useAsync(
+ () =>
+ abortPreviousRequests(() => {
+ const values = getConfigMapSecretResolvedDataPayload({
+ formData: formDataRef.current,
+ inheritedConfigMapSecretData,
+ configMapSecretData,
+ draftData,
+ isSecret,
+ })
+
+ return getConfigMapSecretResolvedValues(
+ {
+ appId: +appId,
+ envId: envId ? +envId : null,
+ values,
+ },
+ abortControllerRef.current.signal,
+ )
+ }, abortControllerRef),
+ [resolvedScopeVariables],
+ resolvedScopeVariables,
+ )
+
+ // RESOLVED CONFIGMAP/SECRET DATA
+ const { resolvedFormData, resolvedInheritedConfigMapSecretData, resolvedConfigMapSecretData, resolvedDraftData } =
+ useMemo(() => {
+ if (resolvedScopeVariablesRes?.areVariablesPresent) {
+ return getConfigMapSecretResolvedData(resolvedScopeVariablesRes.resolvedData)
+ }
+
+ return {
+ resolvedFormData: null,
+ resolvedInheritedConfigMapSecretData: null,
+ resolvedConfigMapSecretData: null,
+ resolvedDraftData: null,
+ }
+ }, [resolvedScopeVariablesRes])
+
+ // DATA CONSTANTS
+ const isError = configHasBeenDeleted || configMapSecretResErr
+ const isLoading =
+ configMapSecretResLoading ||
+ isEnvConfigLoading ||
+ (id && !isError && !(configMapSecretData || inheritedConfigMapSecretData || draftData))
+ const isHashiOrAWS = configMapSecretData && hasHashiOrAWS(configMapSecretData.externalType)
+ const hideConfigToolbar =
+ cmSecretStateLabel === CM_SECRET_STATE.INHERITED &&
+ configHeaderTab === ConfigHeaderTabType.VALUES &&
+ !hideNoOverrideEmptyState &&
+ !draftData
+
+ // SET DRAFT DATA BASED STATES
+ useEffect(() => {
+ if (draftData) {
+ setAreCommentsPresent(draftData.commentsCount > 0)
+ setSelectedProtectionViewTab(
+ draftData.draftState === DraftState.AwaitApproval
+ ? ProtectConfigTabsType.COMPARE
+ : ProtectConfigTabsType.EDIT_DRAFT,
+ )
+ }
+ }, [draftData])
+
+ // ERROR HANDLING
+ useEffect(() => {
+ if (
+ (!isJob &&
+ (configMapSecretData?.unAuthorized ||
+ inheritedConfigMapSecretData?.unAuthorized ||
+ draftData?.unAuthorized)) ||
+ configMapSecretResErr?.code === ERROR_STATUS_CODE.PERMISSION_DENIED
+ ) {
+ ToastManager.showToast({
+ variant: ToastVariantType.warn,
+ title: 'View-only access',
+ description: "You won't be able to make any changes",
+ })
+ } else if (configMapSecretResErr) {
+ showError(configMapSecretResErr)
+ }
+
+ if (configHasBeenDeleted) {
+ ToastManager.showToast({
+ variant: ToastVariantType.error,
+ description: `The ${componentName} '${name}' has been deleted`,
+ })
+ }
+
+ if (reloadResolvedScopeVariablesResErr) {
+ setResolvedScopeVariables(false)
+ }
+ }, [
+ configMapSecretData,
+ inheritedConfigMapSecretData,
+ draftData,
+ configMapSecretResErr,
+ configHasBeenDeleted,
+ reloadResolvedScopeVariablesResErr,
+ ])
+
+ // NO SCOPE VARIABLES PRESENT HANDLING
+ useEffect(() => {
+ if (resolvedScopeVariablesRes && !resolvedScopeVariablesRes.areVariablesPresent) {
+ setResolvedScopeVariables(false)
+ ToastManager.showToast({
+ title: 'Error',
+ description: 'No valid variable found on this page',
+ variant: ToastVariantType.error,
+ })
+ }
+ }, [resolvedScopeVariablesRes])
+
+ // TAB HANDLING
+ useEffect(() => {
+ if (cmSecretStateLabel === CM_SECRET_STATE.INHERITED && !draftData) {
+ setConfigHeaderTab(ConfigHeaderTabType.INHERITED)
+ } else {
+ setConfigHeaderTab(ConfigHeaderTabType.VALUES)
+ }
+ }, [cmSecretStateLabel, draftData])
+
+ const redirectURLToValidPage = () => {
+ history.replace(
+ generatePath(path, {
+ ...params,
+ appId,
+ envId,
+ name: envConfigData.length ? envConfigData[envConfigData.length - 1].name : null,
+ }),
+ )
+ }
+
+ useEffect(() => {
+ if (!isLoading && !selectedCMSecret && !isCreateState && !isEmptyState) {
+ redirectURLToValidPage()
+ }
+ }, [selectedCMSecret, isLoading])
+
+ // METHODS
+ const updateCMSecret = (configName?: string) => {
+ setFormState({ type: 'RESET' })
+ setResolvedScopeVariables(false)
+ setHideNoOverrideEmptyState(false)
+ fetchEnvConfig(+envId || -1)
+
+ if (isCreateState) {
+ history.push(generatePath(path, { ...params, appId, envId, name: configName }))
+ }
+ }
+
+ const restoreLastSavedYAML = () => setRestoreYAML(true)
+
+ const toggleDraftComments = () => setShowComments(!showComments)
+
+ const handleDelete = () => setOpenDeleteModal(isProtected ? 'protectedDeleteModal' : 'deleteModal')
+
+ const handleDeleteOverride = () => {
+ handleDelete()
+ ReactGA.event({
+ category: gaEventCategory,
+ action: 'clicked-delete-override',
+ })
+ }
+
+ const closeDeleteModal = () => setOpenDeleteModal(null)
+
+ const handleOpenDiscardDraftPopup = () => setPopupNodeType(ConfigToolbarPopupNodeType.DISCARD_DRAFT)
+
+ const handleShowEditHistory = () => setPopupNodeType(ConfigToolbarPopupNodeType.EDIT_HISTORY)
+
+ const handleClearPopupNode = () => setPopupNodeType(null)
+
+ const handleViewInheritedConfig = () => setConfigHeaderTab(ConfigHeaderTabType.INHERITED)
+
+ const handleProtectionViewTabChange = (tab: ProtectConfigTabsType) => {
+ setSelectedProtectionViewTab(tab)
+ if (tab === ProtectConfigTabsType.COMPARE) {
+ ReactGA.event({
+ category: gaEventCategory,
+ action: 'clicked-compare',
+ })
+ }
+ }
+
+ const handleToggleScopedVariablesView = () => {
+ ReactGA.event({
+ category: gaEventCategory,
+ action: resolvedScopeVariables ? 'clicked-unresolve-scoped-variable' : 'clicked-resolve-scoped-variable',
+ })
+ setResolvedScopeVariables(!resolvedScopeVariables)
+ }
+
+ const handleCreateOverride = () => {
+ setResolvedScopeVariables(false)
+ setHideNoOverrideEmptyState(true)
+ ReactGA.event({
+ category: gaEventCategory,
+ action: 'clicked-create-override-button',
+ })
+ }
+
+ const handleNoOverrideFormCancel = () => {
+ setFormState({ type: 'RESET' })
+ setHideNoOverrideEmptyState(false)
+ }
+
+ const handleMergeStrategyChange = (strategy: OverrideMergeStrategyType) => {
+ setMergeStrategy(strategy)
+ ReactGA.event({
+ category: gaEventCategory,
+ action: 'clicked-merge-strategy-dropdown',
+ })
+ }
+
+ const toggleSaveChangesModal = () => setShowDraftSaveModal(false)
+
+ const reloadSaveChangesModal = () => {
+ setShowDraftSaveModal(false)
+ updateCMSecret(draftPayload.configData[0].name)
+ setDraftPayload(null)
+ }
+
+ const handleError = (actionType: DraftAction, err: any, payloadData?: CMSecretPayloadType) => {
+ if (err instanceof ServerErrors && Array.isArray(err.errors)) {
+ err.errors.forEach((error) => {
+ if (error.code === API_STATUS_CODES.LOCKED) {
+ if (actionType === DraftAction.Delete) {
+ setOpenDeleteModal('protectedDeleteModal')
+ } else {
+ const _draftPayload: CMSecretDraftPayloadType = {
+ id: id ?? 0,
+ appId: +appId,
+ configData: [payloadData],
+ environmentId: envId ? +envId : null,
+ }
+ setDraftPayload(_draftPayload)
+ setShowDraftSaveModal(true)
+ }
+ reloadEnvironments()
+ }
+ })
+ }
+ if (err.code === ERROR_STATUS_CODE.PERMISSION_DENIED) {
+ ToastManager.showToast({
+ variant: ToastVariantType.notAuthorized,
+ description: 'You cannot make any changes',
+ })
+ } else {
+ showError(err)
+ }
+ }
+
+ const onSubmit: ConfigMapSecretFormProps['onSubmit'] = async (formData) => {
+ const payloadData = getConfigMapSecretPayload(resolvedScopeVariables ? formDataRef.current : formData)
+
+ if (isProtected) {
+ setDraftPayload({
+ id: id ?? 0,
+ appId: +appId,
+ configData: [payloadData],
+ environmentId: envId ? +envId : null,
+ })
+ setShowDraftSaveModal(true)
+ return
+ }
+
+ try {
+ setIsSubmitting(true)
+ let toastTitle = ''
+
+ if (!envId) {
+ const updateConfigMapSecretParams = {
+ id,
+ appId: +appId,
+ payload: payloadData,
+ signal: abortControllerRef.current.signal,
+ }
+
+ await (isSecret ? updateSecret : updateConfigMap)(updateConfigMapSecretParams)
+ toastTitle = `${payloadData.name ? 'Updated' : 'Saved'}`
+ } else {
+ const overrideConfigMapSecretParams = {
+ appId: +appId,
+ envId: +envId,
+ payload: payloadData,
+ signal: abortControllerRef.current.signal,
+ }
+
+ await (isSecret ? overRideSecret : overRideConfigMap)(overrideConfigMapSecretParams)
+ toastTitle = 'Overridden'
+ }
+ ToastManager.showToast({
+ variant: ToastVariantType.success,
+ title: toastTitle,
+ description: 'Changes will be reflected after next deployment.',
+ })
+ setIsSubmitting(false)
+
+ if (!abortControllerRef.current.signal.aborted) {
+ updateCMSecret(payloadData.name)
+ }
+ } catch (err) {
+ setIsSubmitting(false)
+ if (!abortControllerRef.current.signal.aborted) {
+ handleError(DraftAction.Update, err, payloadData)
+ }
+ }
+ }
+
+ const onError: ConfigMapSecretFormProps['onError'] = (errors) => {
+ if (errors.currentData?.[0] === CONFIG_MAP_SECRET_NO_DATA_ERROR) {
+ ToastManager.showToast({
+ variant: ToastVariantType.error,
+ description: `Please add ${CM_SECRET_COMPONENT_NAME[componentType]} data before saving.`,
+ })
+ }
+
+ if (errors.hasCurrentDataErr?.[0]) {
+ ToastManager.showToast({
+ variant: ToastVariantType.error,
+ description: errors.hasCurrentDataErr[0],
+ })
+ }
+ }
+
+ // CONFIG TOOLBAR POPUP MENU
+ const toolbarPopupConfig: ConfigToolbarProps['popupConfig'] = {
+ menuConfig: getConfigToolbarPopupConfig({
+ configHeaderTab,
+ isOverridden: cmSecretStateLabel === CM_SECRET_STATE.OVERRIDDEN,
+ isProtected,
+ isPublishedValuesView: selectedProtectionViewTab === ProtectConfigTabsType.PUBLISHED,
+ isPublishedConfigPresent: !!configMapSecretData,
+ unableToParseData: !!parsingError,
+ isLoading: isLoading || isSubmitting,
+ isDraftAvailable: !!draftData,
+ handleDiscardDraft: handleOpenDiscardDraftPopup,
+ handleShowEditHistory,
+ handleDelete,
+ handleDeleteOverride,
+ isDeletable:
+ cmSecretStateLabel !== CM_SECRET_STATE.INHERITED &&
+ cmSecretStateLabel !== CM_SECRET_STATE.UNPUBLISHED &&
+ cmSecretStateLabel !== CM_SECRET_STATE.OVERRIDDEN &&
+ draftData?.action !== DraftAction.Delete &&
+ !isCreateState,
+ isDeleteOverrideDraftPresent: draftData?.action === DraftAction.Delete,
+ }),
+ popupNodeType,
+ popupMenuNode: ProtectionViewToolbarPopupNode ? (
+
+ ) : null,
+ }
+
+ // RENDERERS
+ const renderForm = ({ onCancel }: Pick) =>
+ isProtected && draftData ? (
+
+ ) : (
+
+ )
+
+ const renderNoOverrideForm = () =>
+ hideNoOverrideEmptyState ? (
+ renderForm({ onCancel: handleNoOverrideFormCancel })
+ ) : (
+
+ )
+
+ const renderConfigHeaderTabContent = () => {
+ switch (configHeaderTab) {
+ case ConfigHeaderTabType.VALUES:
+ return cmSecretStateLabel !== CM_SECRET_STATE.INHERITED || draftData
+ ? renderForm({ onCancel: redirectURLToValidPage })
+ : renderNoOverrideForm()
+ case ConfigHeaderTabType.INHERITED:
+ return (
+
+ )
+ default:
+ return null
+ }
+ }
+
+ const renderDeleteModal = (): JSX.Element => (
+
+ )
+
+ const renderContent = () => {
+ if (isEmptyState) {
+ return
+ }
+
+ if (isLoading) {
+ return
+ }
+
+ if (isError) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ {!hideConfigToolbar && (
+
+ )}
+ {renderConfigHeaderTabContent()}
+
+ )
+ }
+
+ return (
+ <>
+
+
+
{renderContent()}
+ {openDeleteModal && renderDeleteModal()}
+ {SaveChangesModal && showDraftSaveModal && (
+
draftPayload}
+ toggleModal={toggleSaveChangesModal}
+ latestDraft={draftData}
+ reload={reloadSaveChangesModal}
+ showAsModal
+ />
+ )}
+ {DraftComments && showComments && draftData && (
+
+ )}
+ {window._env_.ENABLE_SCOPED_VARIABLES && (
+
+
+
+ )}
+
+ >
+ )
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx
new file mode 100644
index 0000000000..cacab5e20b
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx
@@ -0,0 +1,392 @@
+import { ChangeEvent, useState } from 'react'
+
+import {
+ Button,
+ ButtonStyleType,
+ ButtonVariantType,
+ CMSecretExternalType,
+ CodeEditor,
+ ComponentSizeType,
+ KeyValueConfig,
+ KeyValueTable,
+ noop,
+ StyledRadioGroup,
+ ToastManager,
+ ToastVariantType,
+ YAMLStringify,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { ReactComponent as ICPencil } from '@Icons/ic-pencil.svg'
+import { ReactComponent as HideIcon } from '@Icons/ic-visibility-off.svg'
+import { ReactComponent as ICErrorExclamation } from '@Icons/ic-error-exclamation.svg'
+import { PATTERNS } from '@Config/constants'
+
+import {
+ CODE_EDITOR_RADIO_STATE,
+ CODE_EDITOR_RADIO_STATE_VALUE,
+ CONFIG_MAP_SECRET_NO_DATA_ERROR,
+ DATA_HEADER_MAP,
+ sampleJSONs,
+ VIEW_MODE,
+} from './constants'
+import { externalTypeSecretCodeEditorDataHeaders, renderYamlInfoText } from './helpers'
+import {
+ convertKeyValuePairToYAML,
+ convertYAMLToKeyValuePair,
+ getLockedYamlString,
+ getYAMLWithStringifiedNumbers,
+} from './utils'
+import { ConfigMapSecretDataProps } from './types'
+
+export const ConfigMapSecretData = ({
+ useFormProps,
+ isUnAuthorized,
+ isESO,
+ isHashiOrAWS,
+ readOnly,
+}: ConfigMapSecretDataProps) => {
+ // USE FORM PROPS
+ const { data, errors, setValue, register } = useFormProps
+
+ // STATES
+ const [secretMode, setSecretMode] = useState(false)
+ const [codeEditorRadio, setCodeEditorRadio] = useState(CODE_EDITOR_RADIO_STATE.DATA)
+
+ // CONSTANTS
+ const isLocked = data.isSecret && (secretMode || (data.externalType === '' && isUnAuthorized))
+
+ // METHODS & CONFIGURATIONS
+ const config: KeyValueConfig<'k' | 'v'> = {
+ headers: [
+ {
+ label: data.externalType === '' && data.selectedType === 'volume' ? 'File Name' : 'Key',
+ key: 'k',
+ },
+ {
+ label: data.externalType === '' && data.selectedType === 'volume' ? 'File Content' : 'Value',
+ key: 'v',
+ },
+ ],
+ rows: data.currentData.map(({ k, v, id }) => ({
+ data: {
+ k: {
+ value: k,
+ },
+ v: {
+ value: typeof v === 'object' ? YAMLStringify(v) : v.toString(),
+ },
+ },
+ id,
+ })),
+ }
+
+ const keyValueTableHandleChange =
+ (onChange: (value: unknown) => void) => (rowId: string | number, headerKey: string, value: string) => {
+ // - When data is changed from the YAML editor to the GUI, IDs are mapped to indices (numbers).
+ // - When data is added via the GUI, IDs are created internally by the GUI editor as strings.
+ const _currentData = data.currentData.reduce(
+ (acc, currentData) => {
+ if (currentData.id === rowId) {
+ // If the item is found, update it with the new value and reset errors.
+ acc.found = true
+ acc.updatedData.push({
+ ...currentData,
+ [headerKey]: value,
+ })
+ } else {
+ // If the item is not the one we're looking for, just add it as is.
+ acc.updatedData.push(currentData)
+ }
+ return acc
+ },
+ { updatedData: [], found: false },
+ )
+
+ // If the item is not found, it means it's a new entry added via the GUI editor.
+ // Create a new data object and add it to the current data state.
+ if (!_currentData.found) {
+ _currentData.updatedData.push({
+ k: '',
+ v: '',
+ [headerKey]: value,
+ id: rowId,
+ })
+ }
+
+ // call useForm register 'onChange' method to update the form data value
+ onChange(_currentData.updatedData)
+ // Convert the current key-value data to YAML format and set it in the 'yaml' field.
+ setValue('yaml', convertKeyValuePairToYAML(_currentData.updatedData), { shouldDirty: true })
+ }
+
+ const keyValueHandleDelete = (onChange: (e: unknown) => void) => (rowId: string | number) => {
+ // Create a new array by filtering out the item with the matching rowId.
+ const _currentData = data.currentData.filter(({ id }) => id !== rowId)
+ // call useForm register 'onChange' method to update the form data value
+ onChange(_currentData)
+ // Convert the current key-value data to YAML format and set it in the 'yaml' field.
+ setValue('yaml', convertKeyValuePairToYAML(_currentData), { shouldDirty: true })
+ }
+
+ const keyValueHandleError = (err: boolean) => {
+ // Sets the useForm 'hasCurrentDataErr' field, this is used while validation to block save action if GUI has errors.
+ setValue('hasCurrentDataErr', err)
+ }
+
+ /** Toggles between the secret mode (show/hide values) */
+ const toggleSecretMode = () => setSecretMode(!secretMode)
+
+ /**
+ * Toggles between YAML view mode and key-value pair mode in the editor.
+ *
+ * @param e - The change event triggered by switching modes (YAML or key-value pair).
+ * @returns The current mode if there are unresolved errors, or the selected mode.
+ */
+ const toggleYamlMode = (e: ChangeEvent) => {
+ // The selected mode from the event (either YAML view or key-value pair view).
+ const yamlMode = e.target.value
+ // Check if there are any errors in 'yaml' or 'currentData' and ensure they are not equal to the 'NO_DATA_ERROR' error.
+ const hasDataError =
+ (errors.yaml || errors.currentData) &&
+ (errors.yaml?.[0] || errors.currentData?.[0]) !== CONFIG_MAP_SECRET_NO_DATA_ERROR
+
+ // If there are validation errors, show a toast notification and return the current mode without switching.
+ if (hasDataError) {
+ ToastManager.showToast({
+ variant: ToastVariantType.error,
+ description: 'Please resolve the errors before switching editor mode.',
+ })
+
+ // Return to prevent switching due to errors.
+ return
+ }
+
+ /*
+ * We use setValue instead of the register method from useForm to avoid marking the form as dirty when switching modes. \
+ * Since the data does not change during mode switches, the dirty state is unnecessary.
+ * Additionally, we require the form state for data processing upon submission, so we're not using useState.
+ */
+ // Set 'yamlMode' true if the selected mode is YAML, otherwise return false.
+ setValue('yamlMode', yamlMode === VIEW_MODE.YAML)
+ }
+
+ const handleCodeEditorRadioChange = (e: ChangeEvent) =>
+ setCodeEditorRadio(e.target.value as CODE_EDITOR_RADIO_STATE)
+
+ /**
+ * Determines the key to be used for the code editor form based on the current configuration.
+ * @returns The key in the `data` object corresponding to the selected mode (ESO, HashiCorp/AWS, or YAML).
+ */
+ const getCodeEditorFormKey = (): keyof typeof data => {
+ // If ESO is enabled, return 'esoSecretYaml'.
+ if (isESO) {
+ return 'esoSecretYaml'
+ }
+ // If using HashiCorp or AWS, return 'secretDataYaml'.
+ if (isHashiOrAWS) {
+ return 'secretDataYaml'
+ }
+ // Otherwise, default to 'yaml'.
+ return 'yaml'
+ }
+
+ const getCodeEditorValue = () => {
+ if (codeEditorRadio === CODE_EDITOR_RADIO_STATE.SAMPLE) {
+ return YAMLStringify(sampleJSONs[data.externalType] || sampleJSONs[DATA_HEADER_MAP.DEFAULT])
+ }
+
+ const codeEditorValue = isLocked
+ ? getLockedYamlString(data[getCodeEditorFormKey()] as string)
+ : (data[getCodeEditorFormKey()] as string)
+
+ return readOnly ? getYAMLWithStringifiedNumbers(codeEditorValue) : codeEditorValue
+ }
+
+ // RENDERERS
+ const renderDataEditorSelector = () => {
+ if (
+ (data.isSecret && data.externalType === CMSecretExternalType.KubernetesSecret) ||
+ (!data.isSecret && data.external)
+ ) {
+ return null
+ }
+
+ return (
+
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
+ Data
+ {!isESO && !isHashiOrAWS && (
+
+ {Object.keys(VIEW_MODE).map((key) =>
+ VIEW_MODE[key] !== VIEW_MODE.MANIFEST ? (
+
+ {VIEW_MODE[key].toUpperCase()}
+
+ ) : null,
+ )}
+
+ )}
+
+ )
+ }
+
+ const renderSecretShowHide = (showDivider = true) => {
+ const isDisabled = !!errors.yaml
+
+ return (
+ data.isSecret &&
+ !data.external &&
+ !isUnAuthorized && (
+ <>
+ : }
+ />
+ {showDivider &&
}
+ >
+ )
+ )
+ }
+
+ const renderCodeEditor = ({ sheBangText }: { sheBangText: string }) => {
+ const codeEditorFormKey = getCodeEditorFormKey()
+ const { onChange, onFocus } = register(codeEditorFormKey, {
+ sanitizeFn: (value: string) => {
+ if (codeEditorFormKey === 'yaml') {
+ // Convert the YAML data to key-value pairs and set it in 'currentData'.
+ setValue('currentData', convertYAMLToKeyValuePair(value), { shouldDirty: true })
+ }
+ return value
+ },
+ isCustomComponent: true,
+ })
+
+ return (
+
+
+
+ {!isHashiOrAWS && data.external ? (
+
+ {Object.keys(CODE_EDITOR_RADIO_STATE).map((key) => (
+
+ {CODE_EDITOR_RADIO_STATE_VALUE[key]}
+
+ ))}
+
+ ) : null}
+
+ {renderSecretShowHide()}
+
+
+
+
+
+ {codeEditorRadio === CODE_EDITOR_RADIO_STATE.DATA && errors[codeEditorFormKey] && (
+
+
+
{errors[codeEditorFormKey]}
+
+ )}
+
+ {!data.external && data.yamlMode && renderYamlInfoText()}
+
+ )
+ }
+
+ const renderGUIEditor = () => {
+ const { onChange } = register('currentData', { isCustomComponent: true })
+
+ return (
+
+ {
+ if (key === 'k' && value) {
+ const isValid = new RegExp(PATTERNS.CONFIG_MAP_AND_SECRET_KEY).test(value)
+ return isValid
+ }
+ return true
+ }}
+ errorMessages={[
+ 'Can only contain alphanumeric chars and ( - ), ( _ ), ( . )',
+ 'Spaces not allowed',
+ ]}
+ onError={keyValueHandleError}
+ headerComponent={renderSecretShowHide(false)}
+ validateEmptyKeys
+ validateDuplicateKeys
+ />
+
+ )
+ }
+
+ const externalSecretEditor = () => {
+ if ((isHashiOrAWS || isESO) && data.yamlMode) {
+ return renderCodeEditor({
+ sheBangText:
+ codeEditorRadio === CODE_EDITOR_RADIO_STATE.DATA
+ ? '#Check sample for usage.'
+ : externalTypeSecretCodeEditorDataHeaders[data.externalType] ||
+ externalTypeSecretCodeEditorDataHeaders[DATA_HEADER_MAP.DEFAULT],
+ })
+ }
+
+ return null
+ }
+
+ return (
+
+ {renderDataEditorSelector()}
+ {!data.external &&
+ (data.yamlMode
+ ? renderCodeEditor({
+ sheBangText: '#key: value',
+ })
+ : renderGUIEditor())}
+ {externalSecretEditor()}
+
+ )
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx
new file mode 100644
index 0000000000..1bb59b9889
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx
@@ -0,0 +1,129 @@
+import { useState } from 'react'
+
+import {
+ DeleteDialog,
+ DraftAction,
+ showError,
+ ToastManager,
+ ToastVariantType,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { importComponentFromFELibrary } from '@Components/common'
+
+import { deleteEnvSecret, deleteEnvConfigMap, deleteSecret, deleteConfigMap } from './ConfigMapSecret.service'
+import { CM_SECRET_COMPONENT_NAME } from './constants'
+import { CM_SECRET_STATE, CMSecretComponentType, ConfigMapSecretDeleteModalProps } from './types'
+
+const DeleteModal = importComponentFromFELibrary('DeleteModal')
+const DeleteOverrideDraftModal = importComponentFromFELibrary('DeleteOverrideDraftModal')
+
+export const ConfigMapSecretDeleteModal = ({
+ appId,
+ envId,
+ componentType,
+ cmSecretStateLabel,
+ id,
+ configName,
+ openDeleteModal,
+ draftData,
+ closeDeleteModal,
+ updateCMSecret,
+ handleError,
+}: ConfigMapSecretDeleteModalProps) => {
+ // STATES
+ const [isDeleting, setIsDeleting] = useState(false)
+
+ // CONSTANTS
+ const isDeleteOverride = cmSecretStateLabel === CM_SECRET_STATE.OVERRIDDEN
+ const isSecret = componentType === CMSecretComponentType.Secret
+
+ // METHODS
+ const handleDelete = async () => {
+ setIsDeleting(true)
+ try {
+ if (envId) {
+ const deleteEnvConfigMapSecretParams = { id, appId, envId, name: configName }
+ await (isSecret ? deleteEnvSecret : deleteEnvConfigMap)(deleteEnvConfigMapSecretParams)
+
+ ToastManager.showToast({
+ variant: ToastVariantType.success,
+ description: isDeleteOverride ? 'Restored to global.' : 'Successfully Deleted',
+ })
+ } else {
+ const deleteConfigMapSecretParams = { id, appId, name: configName }
+ await (isSecret ? deleteSecret : deleteConfigMap)(deleteConfigMapSecretParams)
+
+ ToastManager.showToast({
+ variant: ToastVariantType.success,
+ description: 'Successfully deleted',
+ })
+ }
+
+ setIsDeleting(false)
+ updateCMSecret()
+ closeDeleteModal()
+ } catch (err) {
+ setIsDeleting(false)
+ handleError(DraftAction.Delete, err)
+ showError(err)
+ }
+ }
+
+ const prepareDataToDeleteOverrideDraft = () => ({ id })
+
+ // RENDERERS
+ const renderProtectedDeleteModal = () => {
+ if (isDeleteOverride) {
+ return DeleteOverrideDraftModal ? (
+
+ ) : null
+ }
+
+ return DeleteModal ? (
+
+ ) : null
+ }
+
+ const renderDeleteModal = () => (
+
+ )
+
+ if (!openDeleteModal) {
+ return null
+ }
+
+ return openDeleteModal === 'protectedDeleteModal' ? renderProtectedDeleteModal() : renderDeleteModal()
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx
new file mode 100644
index 0000000000..19c2f648bb
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx
@@ -0,0 +1,434 @@
+import { useEffect } from 'react'
+import { Prompt } from 'react-router-dom'
+
+import {
+ Button,
+ ButtonStyleType,
+ ButtonVariantType,
+ Checkbox,
+ CMSecretExternalType,
+ ComponentSizeType,
+ CustomInput,
+ getSelectPickerOptionByValue,
+ Progressing,
+ RadioGroup,
+ RadioGroupItem,
+ SelectPicker,
+ stopPropagation,
+ useForm,
+ usePrompt,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { ROLLOUT_DEPLOYMENT, UNSAVED_CHANGES_PROMPT_MESSAGE } from '@Config/constants'
+import { isChartRef3090OrBelow, isVersionLessThanOrEqualToTarget } from '@Components/common'
+
+import { useConfigMapSecretFormContext } from './ConfigMapSecretFormContext'
+import {
+ CM_SECRET_COMPONENT_NAME,
+ configMapDataTypeOptions,
+ configMapSecretMountDataMap,
+ getSecretDataTypeOptions,
+} from './constants'
+import { getConfigMapSecretFormInitialValues, hasESO, hasHashiOrAWS } from './utils'
+import { getConfigMapSecretFormValidations } from './validations'
+import {
+ renderChartVersionBelow3090NotSupportedText,
+ renderESOInfo,
+ renderExternalInfo,
+ renderHashiOrAwsDeprecatedInfo,
+} from './helpers'
+import {
+ ConfigMapSecretFormProps,
+ ConfigMapSecretDataTypeOptionType,
+ ConfigMapSecretUseFormProps,
+ CM_SECRET_STATE,
+} from './types'
+import { ConfigMapSecretData } from './ConfigMapSecretData'
+
+export const ConfigMapSecretForm = ({
+ id = null,
+ configMapSecretData,
+ cmSecretStateLabel,
+ isJob,
+ appChartRef,
+ isDraft,
+ componentType,
+ isSubmitting,
+ isProtected,
+ areScopeVariablesResolving,
+ resolvedFormData,
+ restoreYAML,
+ setRestoreYAML,
+ onSubmit,
+ onError,
+ onCancel,
+}: ConfigMapSecretFormProps) => {
+ // HOOKS
+ const { setFormState, formDataRef } = useConfigMapSecretFormContext()
+
+ // INITIAL FORM VALUES
+ const formInitialValues = getConfigMapSecretFormInitialValues({
+ configMapSecretData,
+ componentType,
+ cmSecretStateLabel,
+ })
+
+ // FORM INITIALIZATION
+ const useFormProps = useForm({
+ initialValues: formInitialValues,
+ validations: getConfigMapSecretFormValidations,
+ })
+ const { data, errors, formState, setValue, register, handleSubmit, reset } = useFormProps
+
+ // CONSTANTS
+ const isCreateView = id === null
+ const componentName = CM_SECRET_COMPONENT_NAME[componentType]
+ const isUnAuthorized = configMapSecretData?.unAuthorized
+ const isESO = data.isSecret && hasESO(data.externalType)
+ const isHashiOrAWS = data.isSecret && hasHashiOrAWS(data.externalType)
+ const isFormDisabled = isHashiOrAWS || data.isResolvedData || (data.isSecret && isUnAuthorized)
+ const isChartVersion309OrBelow =
+ appChartRef &&
+ appChartRef.name === ROLLOUT_DEPLOYMENT &&
+ isVersionLessThanOrEqualToTarget(appChartRef.version, [3, 9]) &&
+ isChartRef3090OrBelow(appChartRef.id)
+ /**
+ * * In create mode, show the prompt only if the form has unsaved changes (i.e., form is dirty).
+ * * This ensures the user is warned about losing data when navigating away during creation.
+ * * Non-create mode is being handled by the parent component.
+ */
+ const shouldPrompt = isCreateView && formState.isDirty
+
+ // USE EFFECTS
+ useEffect(() => {
+ /*
+ * When resolved form data is available, we reset the form with this resolved form data.
+ * isResolvedData check determines whether the current formState contains resolved data or not.
+ * @note resolvedFormData means show scope variables is true.
+ */
+ if (resolvedFormData) {
+ reset({ ...resolvedFormData, isResolvedData: true }, { keepDirty: true })
+ } else if (formDataRef.current) {
+ /*
+ * We use formDataRef (is present) to restore form values after mounting & when `resolvedFormData` is null.
+ * The form reset is triggered with the keepDirty option, which resets the form using the stored values while preserving the “dirty” state.
+ * During the reset, we also ensure that yamlMode is preserved.
+ */
+ reset({ ...formDataRef.current, yamlMode: data.yamlMode, isResolvedData: false }, { keepDirty: true })
+ }
+ }, [resolvedFormData])
+
+ useEffect(() => {
+ /*
+ * We update formDataRef whenever the form’s data state changes, \
+ * but only if resolvedFormData is null. \
+ * Since resolvedFormData represents a read-only view, it doesn’t require form updates. \
+ * Also, formDataRef is required to restore the form data state when `resolvedFormData` is null.
+ */
+ if (!resolvedFormData) {
+ setFormState({ type: 'SET_DATA', data, isDirty: formState.isDirty, errors })
+ }
+ }, [data, formState.isDirty, errors, resolvedFormData])
+
+ useEffect(() => {
+ /*
+ * When the 'Restore Last Saved YAML' button is clicked, it sets the restoreYAML state to true. \
+ * This triggers a reset of the YAML to its initial state (i.e., the latest valid state). \
+ * Depending on the type of ConfigMap or Secret, we set the appropriate YAML form field accordingly. \
+ * Once the above is done, we reset restoreYAML state back to false to denote YAML has been restored to initial state.
+ */
+ if (restoreYAML) {
+ const yamlFormKey = isESO ? 'esoSecretYaml' : 'yaml'
+ setValue(yamlFormKey, formInitialValues[yamlFormKey], { shouldDirty: true })
+ setRestoreYAML(false)
+ }
+ }, [restoreYAML])
+
+ // PROMPT FOR UNSAVED CHANGES
+ usePrompt({ shouldPrompt })
+
+ // METHODS
+ const handleDataTypeSelectorChange = (item: ConfigMapSecretDataTypeOptionType) => {
+ setValue('external', item.value !== '', { shouldTouch: true, shouldDirty: true })
+
+ if (data.isSecret) {
+ return item.value
+ }
+ // For ConfigMap, external type is always empty, it is controlled by external. (since, there are only 2 types)
+ return CMSecretExternalType.Internal
+ }
+
+ // RENDERERS
+ const renderDataTypeSelector = () => {
+ const dataTypeOptions = data.isSecret ? getSecretDataTypeOptions(isJob, isHashiOrAWS) : configMapDataTypeOptions
+ const dataTypePlaceholder = data.isSecret ? 'Select Secret Type' : 'Select ConfigMap Type'
+
+ return (
+
+ inputId="cm-cs-data-type-selector"
+ classNamePrefix="cm-cs-data-type"
+ label="Data Type"
+ placeholder={dataTypePlaceholder}
+ required
+ isDisabled={isFormDisabled}
+ options={dataTypeOptions}
+ size={ComponentSizeType.large}
+ {...register('externalType', {
+ sanitizeFn: handleDataTypeSelectorChange,
+ isCustomComponent: true,
+ })}
+ value={getSelectPickerOptionByValue(
+ dataTypeOptions,
+ (data.external &&
+ data.externalType === '' &&
+ (data.isSecret
+ ? CMSecretExternalType.KubernetesSecret
+ : CMSecretExternalType.KubernetesConfigMap)) ||
+ data.externalType,
+ )}
+ />
+ )
+ }
+
+ const renderName = () => (
+
+ )
+
+ const renderMountData = () => (
+
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
+ Mount data as
+
+ {Object.keys(configMapSecretMountDataMap).map((key) => (
+
+ {configMapSecretMountDataMap[key].title}
+
+ ))}
+
+
+ )
+
+ const renderSubPathCheckBoxContent = () => (
+
+
+ Set SubPath (same as
+
+ subPath
+
+
+ for volume mount)
+
+ {data.isSubPathChecked && (
+
+ {data.external
+ ? `Please provide keys of ${componentName} to be mounted`
+ : 'Keys will be used as filename for subpath'}
+
+ )}
+ {isChartVersion309OrBelow && renderChartVersionBelow3090NotSupportedText()}
+
+ )
+
+ const renderSubPath = () => (
+
+
!data.isSubPathChecked })}
+ value="CHECKED"
+ >
+ {renderSubPathCheckBoxContent()}
+
+ {(isESO ||
+ data.externalType === CMSecretExternalType.KubernetesSecret ||
+ (!data.isSecret && data.external)) &&
+ data.isSubPathChecked && (
+
+
+
+ )}
+
+ )
+
+ const renderFilePermission = () => (
+
+
!data.isFilePermissionChecked })}
+ >
+
+ Set File Permission (same as
+
+ defaultMode
+
+ for secrets in kubernetes)
+
+ {isChartVersion309OrBelow ? renderChartVersionBelow3090NotSupportedText() : null}
+
+
+ {data.isFilePermissionChecked && (
+
+
+
+ )}
+
+ )
+
+ const renderVolumeMountPath = () =>
+ data.selectedType === configMapSecretMountDataMap.volume.value && (
+ <>
+
+ {renderSubPath()}
+ {renderFilePermission()}
+ >
+ )
+
+ const renderRollARN = () =>
+ (isHashiOrAWS || isESO) && (
+
+
+
+ )
+
+ const renderFormButtons = () => (
+
+
+
+ {!isDraft && (isCreateView || cmSecretStateLabel === CM_SECRET_STATE.INHERITED) && (
+
+ )}
+
+
+ )
+
+ return (
+ <>
+
+
+ >
+ )
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretFormContext.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretFormContext.tsx
new file mode 100644
index 0000000000..33a0094c31
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretFormContext.tsx
@@ -0,0 +1,53 @@
+import { createContext, useContext, useMemo, useRef, useState } from 'react'
+
+import { CONFIG_MAP_SECRET_NO_DATA_ERROR } from './constants'
+import { ConfigMapSecretFormContextType, ConfigMapSecretFormProviderProps, ConfigMapSecretUseFormProps } from './types'
+
+// CONTEXT
+const ConfigMapSecretForm = createContext(null)
+
+// CONTEXT PROVIDER
+export const ConfigMapSecretFormProvider = ({ children }: ConfigMapSecretFormProviderProps) => {
+ // STATES
+ const [isFormDirty, setIsFormDirty] = useState(false)
+ const [parsingError, setParsingError] = useState('')
+
+ // REFS
+ const formDataRef = useRef(null)
+
+ // METHODS
+ const setFormState: ConfigMapSecretFormContextType['setFormState'] = ({ type, data, errors, isDirty }) => {
+ if (type === 'SET_DATA') {
+ formDataRef.current = data
+ setIsFormDirty(isDirty)
+ const yamlError = (formDataRef.current.external ? errors.esoSecretYaml : errors.yaml)?.[0]
+ setParsingError(yamlError && yamlError !== CONFIG_MAP_SECRET_NO_DATA_ERROR ? yamlError : '')
+ } else {
+ formDataRef.current = null
+ setIsFormDirty(false)
+ setParsingError('')
+ }
+ }
+
+ // CONTEXT VALUE
+ const contextValue = useMemo(
+ () => ({
+ setFormState,
+ formDataRef,
+ isFormDirty,
+ parsingError,
+ }),
+ [isFormDirty, parsingError],
+ )
+
+ return {children}
+}
+
+// CONTEXT HOOK
+export const useConfigMapSecretFormContext = () => {
+ const context = useContext(ConfigMapSecretForm)
+ if (!context) {
+ throw new Error(`ConfigMapSecretForm Context cannot be used outside configmap/secret scope`)
+ }
+ return context
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretNullState.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretNullState.tsx
new file mode 100644
index 0000000000..5d7ece4094
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretNullState.tsx
@@ -0,0 +1,42 @@
+import { generatePath, useRouteMatch } from 'react-router-dom'
+
+import { Button, ButtonComponentType, GenericEmptyState, ImageType } from '@devtron-labs/devtron-fe-common-lib'
+
+import EmptyFolder from '@Images/Empty-folder.png'
+import EmptyStateImg from '@Images/cm-cs-empty-state.png'
+import { ReactComponent as ICAdd } from '@Icons/ic-add.svg'
+
+import { CM_SECRET_EMPTY_STATE_TEXT, getCMSecretNullStateText } from './constants'
+import { ConfigMapSecretNullStateProps } from './types'
+
+export const ConfigMapSecretNullState = ({
+ componentType,
+ componentName,
+ nullStateType,
+}: ConfigMapSecretNullStateProps) => {
+ // HOOKS
+ const { path, params } = useRouteMatch()
+
+ const noCMSecretPresent = nullStateType === 'NO_CM_CS'
+
+ return (
+ (
+ }
+ text={CM_SECRET_EMPTY_STATE_TEXT[componentType].buttonText}
+ linkProps={{
+ to: generatePath(path, { ...params, name: 'create' }),
+ }}
+ />
+ )}
+ />
+ )
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx
new file mode 100644
index 0000000000..4afde02e24
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx
@@ -0,0 +1,287 @@
+import { useMemo, useState } from 'react'
+import YAML from 'yaml'
+
+import {
+ Button,
+ ButtonStyleType,
+ CompareFromApprovalOptionsValuesType,
+ ComponentSizeType,
+ DraftAction,
+ DraftState,
+ noop,
+ Progressing,
+ ProtectConfigTabsType,
+ SelectPickerOptionType,
+ useUserEmail,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { hasApproverAccess, importComponentFromFELibrary } from '@Components/common'
+import { CompareConfigView, CompareConfigViewProps, NoPublishedVersionEmptyState } from '@Pages/Applications'
+
+import { CM_SECRET_STATE, CMSecretComponentType, CMSecretConfigData, ConfigMapSecretProtectedProps } from './types'
+import { getConfigMapSecretPayload, getConfigMapSecretReadOnlyValues } from './utils'
+import { ConfigMapSecretForm } from './ConfigMapSecretForm'
+import { ConfigMapSecretReadyOnly } from './ConfigMapSecretReadyOnly'
+import { ConfigMapSecretNullState } from './ConfigMapSecretNullState'
+import { useConfigMapSecretFormContext } from './ConfigMapSecretFormContext'
+
+const ApproveRequestTippy = importComponentFromFELibrary('ApproveRequestTippy', null, 'function')
+
+export const ConfigMapSecretProtected = ({
+ id,
+ draftData,
+ componentType,
+ componentName,
+ publishedConfigMapSecretData,
+ cmSecretStateLabel,
+ isJob,
+ appChartRef,
+ selectedProtectionViewTab,
+ parentName,
+ inheritedConfigMapSecretData,
+ resolvedFormData,
+ areScopeVariablesResolving,
+ onError,
+ onSubmit,
+ updateCMSecret,
+ restoreYAML,
+ setRestoreYAML,
+}: ConfigMapSecretProtectedProps) => {
+ // HOOKS
+ const { email } = useUserEmail()
+ const { formDataRef } = useConfigMapSecretFormContext()
+
+ // STATES
+ const [compareFromSelectedOptionValue, setCompareFromSelectedOptionValue] =
+ useState(CompareFromApprovalOptionsValuesType.APPROVAL_PENDING)
+
+ // CONSTANTS
+ const isApprovalView =
+ selectedProtectionViewTab === ProtectConfigTabsType.COMPARE && draftData.draftState === DraftState.AwaitApproval
+ const isApprovalPendingOptionSelected =
+ isApprovalView && compareFromSelectedOptionValue === CompareFromApprovalOptionsValuesType.APPROVAL_PENDING
+
+ // DATA
+ const configMapSecretData = useMemo(
+ () => ({ ...JSON.parse(draftData.data).configData?.[0], unAuthorized: !draftData.isAppAdmin }),
+ [draftData],
+ )
+
+ const currentConfigMapSecretData = useMemo(
+ () =>
+ (componentType === CMSecretComponentType.ConfigMap || !configMapSecretData?.unAuthorized) &&
+ (resolvedFormData ?? formDataRef.current)
+ ? {
+ ...configMapSecretData,
+ ...getConfigMapSecretPayload(resolvedFormData ?? formDataRef.current),
+ }
+ : configMapSecretData,
+ [configMapSecretData, formDataRef.current, resolvedFormData],
+ )
+
+ // METHODS
+ const handleCompareFromOptionSelection = (option: SelectPickerOptionType) =>
+ setCompareFromSelectedOptionValue(option.value as CompareFromApprovalOptionsValuesType)
+
+ const getConfigMapSecretDiffViewData = () => {
+ if (draftData.action === DraftAction.Delete) {
+ return isApprovalPendingOptionSelected ? inheritedConfigMapSecretData : null
+ }
+
+ return isApprovalPendingOptionSelected ? configMapSecretData : currentConfigMapSecretData
+ }
+
+ const getCurrentEditorConfig = (): Pick<
+ CompareConfigViewProps,
+ 'currentEditorConfig' | 'currentEditorTemplate'
+ > => {
+ const { configData: editorConfigData, data } = getConfigMapSecretReadOnlyValues({
+ isJob,
+ componentType,
+ configMapSecretData: getConfigMapSecretDiffViewData(),
+ })
+
+ return {
+ currentEditorConfig: editorConfigData.reduce(
+ (acc, curr) => ({
+ ...acc,
+ [curr.displayName]: {
+ displayName: curr.displayName,
+ value: curr.value,
+ },
+ }),
+ (draftData.action === DraftAction.Delete && cmSecretStateLabel === CM_SECRET_STATE.OVERRIDDEN
+ ? {
+ configuration: {
+ displayName: 'Configuration',
+ value: 'Inherit from base',
+ },
+ }
+ : {}) as CompareConfigViewProps['currentEditorConfig'],
+ ),
+ currentEditorTemplate: data ? YAML.parse(data) : undefined,
+ }
+ }
+
+ const getPublishedEditorConfig = (): Pick<
+ CompareConfigViewProps,
+ 'publishedEditorConfig' | 'publishedEditorTemplate'
+ > => {
+ const { configData: editorConfigData, data } = getConfigMapSecretReadOnlyValues({
+ componentType,
+ configMapSecretData: publishedConfigMapSecretData,
+ isJob,
+ })
+
+ return {
+ publishedEditorConfig: editorConfigData.reduce(
+ (acc, curr) => ({
+ ...acc,
+ [curr.displayName]: {
+ displayName: curr.displayName,
+ value: curr.value,
+ },
+ }),
+ (draftData.action === DraftAction.Delete && cmSecretStateLabel === CM_SECRET_STATE.OVERRIDDEN
+ ? {
+ configuration: {
+ displayName: 'Configuration',
+ value: 'Override base',
+ },
+ }
+ : {}) as CompareConfigViewProps['publishedEditorConfig'],
+ ),
+ publishedEditorTemplate: data ? YAML.parse(data) : undefined,
+ }
+ }
+
+ // RENDERERS
+ const renderFormView = () => (
+
+ )
+
+ const renderApproveButton = () => {
+ if (draftData.draftState !== DraftState.AwaitApproval || !ApproveRequestTippy) {
+ return null
+ }
+
+ const hasAccess = hasApproverAccess(email, draftData.approvers)
+
+ return (
+
+ {draftData.canApprove && hasAccess ? (
+
+
+
+ ) : (
+
+ )}
+
+ )
+ }
+
+ const renderCompareView = () => (
+
+
+ {areScopeVariablesResolving ? (
+
+ ) : (
+
+ )}
+
+ {renderApproveButton()}
+
+ )
+
+ const renderContent = () => {
+ switch (selectedProtectionViewTab) {
+ case ProtectConfigTabsType.PUBLISHED:
+ if (cmSecretStateLabel === CM_SECRET_STATE.UNPUBLISHED || !publishedConfigMapSecretData) {
+ return
+ }
+
+ if (cmSecretStateLabel === CM_SECRET_STATE.INHERITED) {
+ return
+ }
+
+ return (
+
+ )
+ case ProtectConfigTabsType.EDIT_DRAFT:
+ if (draftData.action === DraftAction.Delete) {
+ return cmSecretStateLabel === CM_SECRET_STATE.OVERRIDDEN ? (
+
+ ) : (
+
+ )
+ }
+
+ return renderFormView()
+ case ProtectConfigTabsType.COMPARE:
+ return renderCompareView()
+ default:
+ return null
+ }
+ }
+
+ return renderContent()
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretReadyOnly.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretReadyOnly.tsx
new file mode 100644
index 0000000000..6fc23bb21e
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretReadyOnly.tsx
@@ -0,0 +1,47 @@
+import { ClipboardButton, CodeEditor, Progressing } from '@devtron-labs/devtron-fe-common-lib'
+
+import { getConfigMapSecretReadOnlyValues, hasHashiOrAWS } from './utils'
+import { ConfigMapSecretReadyOnlyProps } from './types'
+import { renderHashiOrAwsDeprecatedInfo } from './helpers'
+
+export const ConfigMapSecretReadyOnly = ({
+ componentType,
+ isJob,
+ configMapSecretData,
+ areScopeVariablesResolving,
+}: ConfigMapSecretReadyOnlyProps) => {
+ const displayValues = getConfigMapSecretReadOnlyValues({
+ configMapSecretData,
+ componentType,
+ isJob,
+ })
+
+ return areScopeVariablesResolving ? (
+
+ ) : (
+
+ {hasHashiOrAWS(configMapSecretData?.externalType) && renderHashiOrAwsDeprecatedInfo()}
+
+ {displayValues.configData.map(({ displayName, value }) =>
+ value ? (
+
+
{displayName}
+
{value}
+
+ ) : null,
+ )}
+
+ {displayValues.data && (
+
+ )}
+
+ )
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/constants.ts b/apps/web/src/Pages/Shared/ConfigMapSecret/constants.ts
new file mode 100644
index 0000000000..38b32ae40b
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/constants.ts
@@ -0,0 +1,283 @@
+import { GroupBase, OptionsOrGroups } from 'react-select'
+
+import { CMSecretExternalType } from '@devtron-labs/devtron-fe-common-lib'
+
+import {
+ CMSecretComponentType,
+ CMSecretYamlData,
+ ConfigMapSecretDataTypeOptionType,
+ ConfigMapSecretNullStateProps,
+} from './types'
+
+export const CM_SECRET_COMPONENT_NAME = {
+ [CMSecretComponentType.ConfigMap]: 'ConfigMap',
+ [CMSecretComponentType.Secret]: 'Secret',
+}
+
+export const EXTERNAL_INFO_TEXT = {
+ [CMSecretComponentType.Secret]: {
+ title: 'Mount Existing Kubernetes Secret',
+ infoText:
+ 'Secret will not be created by system. However, they will be used inside the pod. Please make sure that secret with the same name is present in the environment.',
+ },
+ [CMSecretComponentType.ConfigMap]: {
+ title: 'Using External Configmaps',
+ infoText:
+ 'Configmap will not be created by system. However, they will be used inside the pod. Please make sure that configmap with the same name is present in the environment',
+ },
+}
+
+export const CM_SECRET_EMPTY_STATE_TEXT = {
+ [CMSecretComponentType.ConfigMap]: {
+ title: 'ConfigMaps',
+ subtitle:
+ 'The ConfigMap API resource holds key-value pairs of the configuration data that can be consumed by pods or used to store configuration data for system components such as controllers.',
+ buttonText: 'Create ConfigMap',
+ },
+ [CMSecretComponentType.Secret]: {
+ title: 'Secrets',
+ subtitle:
+ 'Secret objects let you store and manage sensitive information, such as passwords, authentication tokens, and ssh keys.',
+ buttonText: 'Create Secret',
+ },
+}
+
+export const getCMSecretNullStateText = (
+ componentType: CMSecretComponentType = CMSecretComponentType.ConfigMap,
+ componentName: string = '',
+): Record => ({
+ DELETE_OVERRIDE: {
+ title: 'Delete override requested',
+ subTitle: 'This override will be deleted on approval',
+ },
+ DELETE: {
+ title: 'File deletion requested',
+ subTitle: `This ${componentName} will be deleted on approval`,
+ },
+ NOT_OVERRIDDEN: {
+ title: 'This file is not overridden',
+ subTitle: `Published override for this file will be available here`,
+ },
+ NO_CM_CS: {
+ title: CM_SECRET_EMPTY_STATE_TEXT[componentType].title,
+ subTitle: CM_SECRET_EMPTY_STATE_TEXT[componentType].subtitle,
+ },
+})
+
+export const configMapDataTypeOptions: ConfigMapSecretDataTypeOptionType[] = [
+ { value: '', label: 'Kubernetes ConfigMap' },
+ { value: CMSecretExternalType.KubernetesConfigMap, label: 'Kubernetes External ConfigMap' },
+]
+
+export const getSecretDataTypeOptions = (
+ isJob: boolean,
+ isHashiOrAWS: boolean,
+):
+ | ConfigMapSecretDataTypeOptionType[]
+ | OptionsOrGroups> => {
+ const kubernetesOptions: ConfigMapSecretDataTypeOptionType[] = [
+ { value: '', label: 'Kubernetes Secret' },
+ { value: CMSecretExternalType.KubernetesSecret, label: 'Mount Existing Kubernetes Secret' },
+ ]
+
+ const esoOptions: GroupBase[] = [
+ {
+ label: 'External Secret Operator (ESO)',
+ options: [
+ { value: CMSecretExternalType.ESO_GoogleSecretsManager, label: 'Google Secrets Manager' },
+ { value: CMSecretExternalType.ESO_AWSSecretsManager, label: 'AWS Secrets Manager' },
+ { value: CMSecretExternalType.ESO_AzureSecretsManager, label: 'Azure Secrets Manager' },
+ { value: CMSecretExternalType.ESO_HashiCorpVault, label: 'Hashi Corp Vault' },
+ ],
+ },
+ ]
+
+ const kesOptions: GroupBase[] = [
+ {
+ label: 'Kubernetes External Secret (KES)',
+ options: [
+ {
+ value: CMSecretExternalType.AWSSecretsManager,
+ label: 'AWS Secrets Manager',
+ description: 'Deprecated',
+ },
+ {
+ value: CMSecretExternalType.AWSSystemManager,
+ label: 'AWS System Manager',
+ description: 'Deprecated',
+ },
+ {
+ value: CMSecretExternalType.HashiCorpVault,
+ label: 'Hashi Corp Vault',
+ description: 'Deprecated',
+ },
+ ],
+ },
+ ]
+
+ return isJob ? kubernetesOptions : [...kubernetesOptions, ...esoOptions, ...(isHashiOrAWS ? kesOptions : [])]
+}
+
+export const configMapSecretMountDataMap = {
+ environment: { title: 'Environment Variable', value: 'environment' },
+ volume: { title: 'Data Volume', value: 'volume' },
+}
+
+export const CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA: CMSecretYamlData[] = [{ k: '', v: '', id: 0 }]
+
+export enum CODE_EDITOR_RADIO_STATE {
+ DATA = 'data',
+ SAMPLE = 'sample',
+}
+
+export const CODE_EDITOR_RADIO_STATE_VALUE = { DATA: 'Data', SAMPLE: 'Sample' }
+
+export const DATA_HEADER_MAP = { DEFAULT: 'default' }
+
+export const VIEW_MODE = {
+ GUI: 'gui',
+ YAML: 'yaml',
+ MANIFEST: 'manifest',
+}
+
+export const sampleJSONs = {
+ ESO_GoogleSecretsManager: {
+ secretStore: {
+ gcpsm: {
+ auth: {
+ secretRef: {
+ secretAccessKeySecretRef: {
+ name: 'gcpsm-secret',
+ key: 'secret-access-credentials',
+ },
+ },
+ },
+ projectID: 'myProject',
+ },
+ },
+ esoData: [
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ },
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ },
+ ],
+ },
+ ESO_AWSSecretsManager: {
+ secretStore: {
+ aws: {
+ service: 'SecretsManager',
+ region: 'us-east-1',
+ auth: {
+ secretRef: {
+ accessKeyIDSecretRef: {
+ name: 'awssm-secret',
+ key: 'access-key',
+ },
+ secretAccessKeySecretRef: {
+ name: 'awssm-secret',
+ key: 'secret-access-key',
+ },
+ },
+ },
+ },
+ },
+ esoData: [
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ property: 'prodPassword',
+ },
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ property: 'prodPassword',
+ },
+ ],
+ },
+ ESO_AzureSecretsManager: {
+ secretStore: {
+ azurekv: {
+ tenantId: 'd3bc2180-xxxx-xxxx-xxxx-154105743342',
+ vaultUrl: 'https://my-keyvault-name.vault.azure.net',
+ authSecretRef: {
+ clientId: {
+ name: 'azure-secret-sp',
+ key: 'ClientID',
+ },
+ clientSecret: {
+ name: 'azure-secret-sp',
+ key: 'ClientSecret',
+ },
+ },
+ },
+ },
+ esoData: [
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ },
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ },
+ ],
+ },
+ ESO_HashiCorpVault: {
+ secretStore: {
+ vault: {
+ server: 'http://my.vault.server:8200',
+ path: 'secret',
+ version: 'v2',
+ auth: {
+ tokenSecretRef: {
+ name: 'vault-token',
+ key: 'token',
+ },
+ },
+ },
+ },
+ esoData: [
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ property: 'prodPassword',
+ },
+ {
+ secretKey: 'prod-mysql-password',
+ key: 'secrets/prod-mysql-secrets',
+ property: 'prodPassword',
+ },
+ ],
+ },
+ default: [
+ {
+ key: 'service/credentials',
+ name: 'secret-key',
+ property: 'property-name',
+ isBinary: true,
+ },
+ {
+ key: 'service/credentials',
+ name: 'secret-key',
+ property: 'property-name',
+ isBinary: true,
+ },
+ ],
+}
+
+export const CONFIG_MAP_SECRET_NO_DATA_ERROR = 'This is a required field'
+
+export const CONFIG_MAP_SECRET_YAML_PARSE_ERROR = 'Could not parse to valid YAML'
+
+export const SECRET_TOAST_INFO = {
+ BOTH_STORE_AVAILABLE: 'Please use either secretStore or secretStoreRef',
+ CHECK_KEY_SECRET_KEY: 'Please check key and secretKey',
+ BOTH_STORE_UNAVAILABLE: 'Please provide secretStore or secretStoreRef',
+ CHECK_KEY_NAME: 'Please check key and name',
+ BOTH_ESO_DATA_AND_DATA_FROM_AVAILABLE: 'Please use either esoData or esoDataFrom',
+ BOTH_ESO_DATA_AND_DATA_FROM_UNAVAILABLE: 'Please provide esoData or esoDataFrom',
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/helpers.tsx b/apps/web/src/Pages/Shared/ConfigMapSecret/helpers.tsx
new file mode 100644
index 0000000000..4283726bac
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/helpers.tsx
@@ -0,0 +1,177 @@
+import { Link } from 'react-router-dom'
+
+import { CMSecretExternalType, InfoColourBar } from '@devtron-labs/devtron-fe-common-lib'
+
+import { ReactComponent as InfoIcon } from '@Icons/info-filled.svg'
+import { ReactComponent as InfoIconN7 } from '@Icons/info-filled-n7.svg'
+import { ReactComponent as ICWarningY5 } from '@Icons/ic-warning-y5.svg'
+import { URLS } from '@Config/routes'
+import { DOCUMENTATION } from '@Config/constants'
+
+import { EXTERNAL_INFO_TEXT } from './constants'
+import { CMSecretComponentType } from './types'
+
+export const renderESOInfo = (isESO: boolean) =>
+ isESO ? (
+
+
+ External Secrets Operator
+
+ should be installed in the target cluster.
+
+ Learn more
+
+
+ }
+ Icon={InfoIcon}
+ iconSize={20}
+ />
+ ) : null
+
+export const renderExternalInfo = (renderCondition: boolean, componentType: CMSecretComponentType) =>
+ renderCondition ? (
+
+ {EXTERNAL_INFO_TEXT[componentType].title}
+ {EXTERNAL_INFO_TEXT[componentType].infoText}
+
+ }
+ Icon={InfoIcon}
+ iconSize={20}
+ />
+ ) : null
+
+export const renderHashiOrAwsDeprecatedInfo = () => (
+
+
+ Kubernetes External Secret (KES) has been deprecated and will be removed in the next Devtron
+ version. You can delete this file and create a secret using
+
+
+
+ External Secret Operator (ESO).
+
+
+ }
+ Icon={ICWarningY5}
+ iconSize={20}
+ />
+)
+
+export const renderChartVersionBelow3090NotSupportedText = () => (
+
+ Supported for Chart Versions 3.10 and above.
+ Learn more about
+
+ Deployment Template > Chart Version
+
+
+)
+
+export const renderYamlInfoText = () => (
+
+
+
+ GUI Recommended for multi-line data. Boolean and numeric values must be wrapped in double quotes Eg.
+ "true", "123"
+
+
+)
+
+export const externalTypeSecretCodeEditorDataHeaders: Record<
+ | Extract<
+ CMSecretExternalType,
+ | CMSecretExternalType.ESO_GoogleSecretsManager
+ | CMSecretExternalType.ESO_AWSSecretsManager
+ | CMSecretExternalType.ESO_AzureSecretsManager
+ | CMSecretExternalType.ESO_HashiCorpVault
+ >
+ | 'default',
+ JSX.Element
+> = {
+ [CMSecretExternalType.ESO_GoogleSecretsManager]: (
+
+ #Sample Data
+
+ #secretKey: Name of the secret
+
+ #key: GCP secret name
+
+ #secretAccessKeySecretRef.name: The secret name which would be used for authentication
+
+ #secretAccessKeySecretRef.key: Key name containing SA key
+
+ #projectID: GCP Project ID where secret is created
+
+
+ ),
+ [CMSecretExternalType.ESO_AWSSecretsManager]: (
+
+ #Sample Data
+ #accessKeyIDSecretRef.name: Name of secret created that would be used for authentication
+ #region: The region where Secret is created
+ #secretKey: Name of the secret created.
+ #key: AWS Secrets Manager secret name
+ #property: AWS Secrets Manager secret key
+
+ ),
+ [CMSecretExternalType.ESO_AzureSecretsManager]: (
+
+ #Sample Data
+ #tenantId: azure tenant ID
+ #vaultUrl: URL of your vault instance
+ #authSecretRef.name: Name of secret created that would be used for authentication
+ #secretKey: Name of the secret
+ #key: Azure Key vault secret name
+
+ ),
+ [CMSecretExternalType.ESO_HashiCorpVault]: (
+
+ #Sample Data
+ #vault.server: Server URL where vault is running
+ #vault.path: Path where secret is stored
+ #tokenSecretRef.name: The secret name which would be used for authentication
+ #tokenSecretRef.key: Key name containing token
+ #secretKey: Name of the secret
+ #key: Vault secret name
+ #property: Vault secret key
+
+ ),
+ default: (
+
+ # Sample Data
+ # key: Secret key in backend
+ # name: Name for this key in the generated secret
+ # property: Property to extract if secret in backend is a JSON object(optional)
+ # isBinary: Set this to true if configuring an item for a binary file stored(optional)
+
+
+ ),
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/index.ts b/apps/web/src/Pages/Shared/ConfigMapSecret/index.ts
new file mode 100644
index 0000000000..b0d37367f1
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/index.ts
@@ -0,0 +1,4 @@
+export * from './ConfigMapSecret.wrapper'
+
+export type { CMSecretWrapperProps } from './types'
+export { CMSecretComponentType } from './types'
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/styles.scss b/apps/web/src/Pages/Shared/ConfigMapSecret/styles.scss
new file mode 100644
index 0000000000..b3914c4f03
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/styles.scss
@@ -0,0 +1,83 @@
+.configmap-secret-form {
+ &__name-container {
+ grid-template-columns: 300px 1fr;
+ }
+
+ &__mount-data {
+ width: fit-content;
+ border: none;
+ gap: 20px;
+
+ .radio__button {
+ margin: 0;
+ }
+
+ .form__radio-item {
+ border: none;
+
+ &-content {
+ padding: 0;
+ align-items: center;
+ gap: 8px;
+ }
+ }
+ }
+
+ &__checkbox {
+ .form__checkbox-container {
+ align-self: flex-start;
+ }
+ }
+
+ &__code-editor {
+ .form__error {
+ margin: 0;
+ }
+ }
+}
+
+.configmap-secret-container {
+ &.with-comment-drawer {
+ display: grid;
+ gap: 8px;
+ grid-template-columns: 1fr 300px;
+ background-color: var(--window-bg);
+ position: absolute;
+ left: 56px;
+ top: 77px;
+ right: 0;
+ height: calc(100% - 77px);
+ animation: comments-view-opening;
+ animation-duration: 200ms;
+ animation-timing-function: linear;
+ animation-fill-mode: forwards;
+ }
+
+ @keyframes comments-view-opening {
+ 0% {
+ grid-template-columns: 1fr 0;
+ }
+
+ 100% {
+ grid-template-columns: 1fr 300px;
+ }
+ }
+
+ &__approval-tippy {
+ .tippy-box {
+ top: 50px;
+ }
+ }
+
+ &__display-values-container {
+ grid-template-columns: 150px 1fr;
+ row-gap: 12px;
+ column-gap: 8px;
+ }
+}
+
+// TODO: remove when issue for svg button height is fixed
+.cm-cs-empty-state.empty-state svg {
+ height: 100%;
+ width: 100%;
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/types.ts b/apps/web/src/Pages/Shared/ConfigMapSecret/types.ts
new file mode 100644
index 0000000000..b0317e91ea
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/types.ts
@@ -0,0 +1,315 @@
+import { Dispatch, MutableRefObject, SetStateAction } from 'react'
+
+import {
+ ConfigDatum,
+ CMSecretExternalType,
+ DraftAction,
+ DraftMetadataDTO,
+ ProtectConfigTabsType,
+ SelectPickerOptionType,
+ useForm,
+ UseFormErrorHandler,
+ UseFormErrors,
+ UseFormSubmitHandler,
+ AppEnvDeploymentConfigDTO,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { ComponentStates, EnvironmentOverrideComponentProps } from '../EnvironmentOverride/EnvironmentOverrides.types'
+
+// ENUMS
+export enum CMSecretComponentType {
+ ConfigMap = 1,
+ Secret = 2,
+}
+
+export enum CM_SECRET_STATE {
+ BASE = '',
+ INHERITED = 'INHERITING',
+ OVERRIDDEN = 'OVERRIDDEN',
+ ENV = 'ENV',
+ UNPUBLISHED = 'UNPUBLISHED',
+}
+
+// PAYLOAD PROPS
+export type CMSecretPayloadType = Pick<
+ CMSecretConfigData,
+ | 'data'
+ | 'name'
+ | 'type'
+ | 'externalType'
+ | 'external'
+ | 'roleARN'
+ | 'mountPath'
+ | 'subPath'
+ | 'esoSecretData'
+ | 'filePermission'
+ | 'esoSubPath'
+>
+
+export interface ESOSecretData {
+ secretStore: Record
+ secretStoreRef: Record
+ refreshInterval: string
+ esoData: Record[]
+ esoDataFrom: Record[]
+ template: Record
+}
+
+export interface CMSecretDraftPayloadType {
+ id: number
+ appId: number
+ configData: [CMSecretPayloadType]
+ environmentId: number
+}
+
+export interface GetConfigMapSecretConfigDataProps
+ extends Pick {
+ envId: number
+ appId: number
+ name: string
+ isJob?: IsJob
+ resourceId: number
+ abortControllerRef: MutableRefObject
+}
+
+export type GetConfigMapSecretConfigDataReturnType = IsJob extends true
+ ? CMSecretDTO
+ : AppEnvDeploymentConfigDTO
+
+// SELECT PICKER OPTION TYPE
+export type ConfigMapSecretDataTypeOptionType = SelectPickerOptionType
+
+// USE FORM PROPS
+export interface CMSecretYamlData {
+ k: string
+ v: string
+ id: string | number
+}
+
+export interface ConfigMapSecretUseFormProps {
+ name: string
+ isSecret: boolean
+ external: boolean
+ externalType: CMSecretExternalType
+ selectedType: string
+ isFilePermissionChecked: boolean
+ isSubPathChecked: boolean
+ externalSubpathValues: string
+ filePermission: string
+ volumeMountPath: string
+ roleARN: string
+ yamlMode: boolean
+ yaml: string
+ currentData: CMSecretYamlData[]
+ secretDataYaml: string
+ esoSecretYaml: string
+ hasCurrentDataErr: boolean
+ isResolvedData: boolean
+}
+
+// COMPONENT PROPS
+export interface CMSecretDraftData extends DraftMetadataDTO {
+ unAuthorized: boolean
+}
+
+export interface CMSecretWrapperProps
+ extends Pick<
+ EnvironmentOverrideComponentProps,
+ 'reloadEnvironments' | 'envConfig' | 'fetchEnvConfig' | 'isJob' | 'onErrorRedirectURL'
+ > {
+ componentType?: CMSecretComponentType
+ parentName?: string
+ parentState?: ComponentStates
+ setParentState?: Dispatch>
+ clusterId?: string
+ isProtected?: boolean
+ envName: string
+ appName: string
+}
+
+export interface ConfigMapSecretContainerProps extends Omit {
+ appChartRef: { id: number; version: string; name: string }
+}
+
+export interface CMSecretConfigData extends ConfigDatum {
+ unAuthorized: boolean
+}
+
+export interface ConfigMapSecretFormProps
+ extends Required> {
+ id: number
+ configMapSecretData: CMSecretConfigData
+ cmSecretStateLabel: CM_SECRET_STATE
+ restoreYAML: boolean
+ setRestoreYAML: Dispatch>
+ isSubmitting: boolean
+ areScopeVariablesResolving: boolean
+ resolvedFormData: ConfigMapSecretUseFormProps
+ isDraft?: boolean
+ onSubmit: UseFormSubmitHandler
+ onError: UseFormErrorHandler
+ onCancel: () => void
+}
+
+export interface ConfigMapSecretDataProps {
+ isESO: boolean
+ isHashiOrAWS: boolean
+ isUnAuthorized: boolean
+ readOnly: boolean
+ useFormProps: ReturnType>
+}
+
+export type ConfigMapSecretReadyOnlyProps = Pick<
+ ConfigMapSecretFormProps,
+ 'configMapSecretData' | 'componentType' | 'isJob' | 'areScopeVariablesResolving'
+>
+
+export type CMSecretDeleteModalType = 'deleteModal' | 'protectedDeleteModal'
+
+export interface ConfigMapSecretDeleteModalProps
+ extends Pick {
+ appId: number
+ envId: number
+ configName: string
+ openDeleteModal: CMSecretDeleteModalType
+ draftData: CMSecretDraftData
+ updateCMSecret: (configName?: string) => void
+ closeDeleteModal: () => void
+ handleError: (actionType: DraftAction, err: any, payloadData?: CMSecretPayloadType) => void
+}
+
+export type ConfigMapSecretNullStateProps =
+ | {
+ componentType?: never
+ componentName: string
+ nullStateType: 'DELETE'
+ }
+ | {
+ componentType?: never
+ componentName?: never
+ nullStateType: 'DELETE_OVERRIDE' | 'NOT_OVERRIDDEN'
+ }
+ | {
+ componentType: ConfigMapSecretFormProps['componentType']
+ componentName?: never
+ nullStateType: 'NO_CM_CS'
+ }
+
+export type ConfigMapSecretProtectedProps = Pick &
+ Pick<
+ ConfigMapSecretFormProps,
+ | 'componentType'
+ | 'cmSecretStateLabel'
+ | 'isJob'
+ | 'id'
+ | 'onError'
+ | 'onSubmit'
+ | 'areScopeVariablesResolving'
+ | 'resolvedFormData'
+ | 'restoreYAML'
+ | 'setRestoreYAML'
+ | 'appChartRef'
+ > &
+ Pick & {
+ componentName: string
+ publishedConfigMapSecretData: ConfigMapSecretFormProps['configMapSecretData']
+ inheritedConfigMapSecretData: ConfigMapSecretFormProps['configMapSecretData']
+ draftData: CMSecretDraftData
+ selectedProtectionViewTab: ProtectConfigTabsType
+ }
+
+// CONTEXT TYPES
+type SetFormStateParams =
+ | {
+ type: 'SET_DATA'
+ data: ConfigMapSecretUseFormProps
+ errors: UseFormErrors
+ isDirty: boolean
+ }
+ | {
+ type: 'RESET'
+ data?: never
+ errors?: never
+ isDirty?: never
+ }
+
+export interface ConfigMapSecretFormContextType {
+ /**
+ * Reference to the current state of the form data.
+ * This persists the form values across renders, preventing data loss when the component unmounts.
+ */
+ formDataRef: MutableRefObject
+ /**
+ * Boolean indicating whether the form has unsaved changes.
+ * This tracks the "dirty" state of the form.
+ */
+ isFormDirty: boolean
+ /**
+ * String containing any error messages related to parsing, \
+ * such as issues encountered when processing YAML.
+ */
+ parsingError: string
+ /**
+ * Function to update the form state based on the provided parameters.
+ * @param params - The new form state parameters to apply.
+ */
+ setFormState: (params: SetFormStateParams) => void
+}
+
+export interface ConfigMapSecretFormProviderProps {
+ children: JSX.Element
+}
+
+// DTO
+export interface CMSecretDTO {
+ id: number
+ appId: number
+ configData: ConfigDatum[]
+}
+
+// API CALLS PROPS
+export interface ConfigMapSecretCommonAPIProps {
+ id: number
+ appId: number
+ envId: number
+ name: string
+ payload: CMSecretPayloadType
+ signal?: AbortSignal
+}
+
+export interface UpdateConfigMapSecretProps
+ extends Pick {}
+
+export interface DeleteConfigMapSecretProps extends Pick {}
+
+export interface DeleteEnvConfigMapSecretProps
+ extends DeleteConfigMapSecretProps,
+ Pick {}
+
+export interface OverrideConfigMapSecretProps
+ extends Pick {}
+
+export interface GetCMSecretProps extends Pick {
+ componentType: CMSecretComponentType
+ envId?: number
+}
+
+// UTILS TYPES
+export type ConfigMapSecretDecodedDataReturnType = IsDraft extends false
+ ? CMSecretConfigData & { isDecoded?: boolean }
+ : CMSecretDraftData & { isDecoded?: boolean; parsedData?: Record }
+
+export type ConfigMapSecretEncodedDataReturnType = IsDraft extends false
+ ? CMSecretConfigData
+ : CMSecretDraftData
+
+export type ConfigMapSecretDecodedDataProps = {
+ configMapSecretData: ConfigMapSecretEncodedDataReturnType
+ isDraft?: IsDraft
+ isSecret?: boolean
+}
+
+export type ConfigMapSecretEncodedDataProps = {
+ configMapSecretData: ConfigMapSecretDecodedDataReturnType
+ isDraft?: IsDraft
+}
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/utils.ts b/apps/web/src/Pages/Shared/ConfigMapSecret/utils.ts
new file mode 100644
index 0000000000..65bbf96bf9
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/utils.ts
@@ -0,0 +1,702 @@
+import YAML from 'yaml'
+
+import {
+ AppEnvDeploymentConfigDTO,
+ CMSecretExternalType,
+ decode,
+ DEFAULT_SECRET_PLACEHOLDER,
+ DraftMetadataDTO,
+ DraftState,
+ ERROR_STATUS_CODE,
+ getSelectPickerOptionByValue,
+ YAMLStringify,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { ResourceConfigStage } from '@Pages/Applications/DevtronApps/service.types'
+
+import {
+ CODE_EDITOR_RADIO_STATE,
+ CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA,
+ configMapDataTypeOptions,
+ configMapSecretMountDataMap,
+ getSecretDataTypeOptions,
+} from './constants'
+import {
+ CMSecretYamlData,
+ ConfigMapSecretFormProps,
+ ConfigMapSecretUseFormProps,
+ CMSecretComponentType,
+ CM_SECRET_STATE,
+ CMSecretDraftData,
+ CMSecretPayloadType,
+ CMSecretConfigData,
+ CMSecretDTO,
+ ESOSecretData,
+ ConfigMapSecretDecodedDataReturnType,
+ ConfigMapSecretDecodedDataProps,
+ ConfigMapSecretEncodedDataProps,
+ ConfigMapSecretEncodedDataReturnType,
+} from './types'
+
+// HELPERS UTILS ----------------------------------------------------------------
+export const hasHashiOrAWS = (externalType: CMSecretExternalType): boolean =>
+ externalType === CMSecretExternalType.AWSSecretsManager ||
+ externalType === CMSecretExternalType.AWSSystemManager ||
+ externalType === CMSecretExternalType.HashiCorpVault
+
+export const hasESO = (externalType: CMSecretExternalType): boolean =>
+ externalType === CMSecretExternalType.ESO_GoogleSecretsManager ||
+ externalType === CMSecretExternalType.ESO_AzureSecretsManager ||
+ externalType === CMSecretExternalType.ESO_AWSSecretsManager ||
+ externalType === CMSecretExternalType.ESO_HashiCorpVault
+
+export const getConfigMapSecretStateLabel = (configStage: ResourceConfigStage, isOverrideView: boolean) => {
+ if (isOverrideView) {
+ switch (configStage) {
+ case ResourceConfigStage.Overridden:
+ return CM_SECRET_STATE.OVERRIDDEN
+ case ResourceConfigStage.Inheriting:
+ return CM_SECRET_STATE.INHERITED
+ default:
+ return configStage === ResourceConfigStage.Unpublished
+ ? CM_SECRET_STATE.UNPUBLISHED
+ : CM_SECRET_STATE.ENV
+ }
+ }
+
+ return configStage === ResourceConfigStage.Unpublished ? CM_SECRET_STATE.UNPUBLISHED : CM_SECRET_STATE.BASE
+}
+// HELPERS UTILS ----------------------------------------------------------------
+
+// FORM UTILS ----------------------------------------------------------------
+const secureValues = (data: Record, decodeData: boolean, hideData: boolean): CMSecretYamlData[] => {
+ const decodedData = !hideData && decodeData ? decode(data) : data
+ const hiddenData = hideData && DEFAULT_SECRET_PLACEHOLDER
+ return Object.keys(decodedData).map((k, id) => ({
+ k,
+ v:
+ typeof decodedData[k] === 'object'
+ ? hiddenData || YAMLStringify(decodedData[k])
+ : hiddenData || decodedData[k],
+ id,
+ }))
+}
+
+const processCurrentData = ({
+ configMapSecretData,
+ cmSecretStateLabel,
+ isSecret,
+}: Pick & {
+ isSecret: boolean
+}) => {
+ if (configMapSecretData.data) {
+ return secureValues(
+ configMapSecretData.data,
+ isSecret && configMapSecretData.externalType === '',
+ isSecret && configMapSecretData.unAuthorized,
+ )
+ }
+ if (cmSecretStateLabel === CM_SECRET_STATE.INHERITED && configMapSecretData.defaultData) {
+ return secureValues(
+ configMapSecretData.defaultData,
+ isSecret && configMapSecretData.externalType === '',
+ isSecret && configMapSecretData.unAuthorized,
+ )
+ }
+
+ return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA
+}
+
+const processExternalSubPathValues = ({
+ data,
+ esoSubPath,
+ external,
+ subPath,
+}: Pick) => {
+ if (subPath && external && data) {
+ return Object.keys(data).join(', ')
+ }
+ if (esoSubPath) {
+ return esoSubPath.join(', ')
+ }
+ return ''
+}
+
+export const convertYAMLToKeyValuePair = (yaml: string): CMSecretYamlData[] => {
+ try {
+ const obj = yaml && YAML.parse(yaml)
+ if (typeof obj !== 'object') {
+ throw new Error()
+ }
+ const keyValueArray: CMSecretYamlData[] = Object.keys(obj).reduce((agg, k, id) => {
+ if (!k && !obj[k]) {
+ return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA
+ }
+ const v = obj[k] && typeof obj[k] === 'object' ? YAMLStringify(obj[k]) : obj[k].toString()
+
+ return [...agg, { k, v: v ?? '', id }]
+ }, [])
+ return keyValueArray
+ } catch {
+ return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA
+ }
+}
+
+export const convertKeyValuePairToYAML = (currentData: CMSecretYamlData[]) =>
+ currentData.length ? YAMLStringify(currentData.reduce((agg, { k, v }) => ({ ...agg, [k]: v }), {})) : ''
+
+export const getLockedYamlString = (yaml: string) => {
+ const obj = YAML.parse(yaml)
+ const keyValueArray = Object.keys(obj).reduce((agg, k) => {
+ if (!k && !obj[k]) {
+ return agg
+ }
+
+ return { ...agg, [k]: DEFAULT_SECRET_PLACEHOLDER }
+ }, obj)
+
+ return YAMLStringify(keyValueArray)
+}
+
+export const getYAMLWithStringifiedNumbers = (yaml: string) => {
+ const parsedYAML = YAML.parse(yaml)
+ const jsonWithStringifiedNumbers = JSON.parse(
+ JSON.stringify(parsedYAML, (_, value) =>
+ // Check if the value is a number (but not NaN or Infinity) and return it as a string
+ typeof value === 'number' && Number.isFinite(value) ? String(value) : value,
+ ),
+ )
+ return YAMLStringify(jsonWithStringifiedNumbers)
+}
+
+const getSecretDataFromConfigData = ({
+ secretData,
+ defaultSecretData,
+ esoSecretData: baseEsoSecretData,
+ defaultESOSecretData,
+}: ConfigMapSecretFormProps['configMapSecretData']): Pick<
+ ConfigMapSecretUseFormProps,
+ 'secretDataYaml' | 'esoSecretYaml'
+> => {
+ let jsonForSecretDataYaml: string
+
+ if (secretData?.length) {
+ jsonForSecretDataYaml = YAMLStringify(secretData)
+ } else if (defaultSecretData?.length) {
+ jsonForSecretDataYaml = YAMLStringify(defaultSecretData)
+ }
+
+ const esoSecretData: Record =
+ !(baseEsoSecretData?.esoData || []).length &&
+ !baseEsoSecretData?.template &&
+ !baseEsoSecretData?.esoDataFrom &&
+ defaultESOSecretData
+ ? defaultESOSecretData
+ : baseEsoSecretData
+
+ const isEsoSecretData: boolean =
+ (esoSecretData?.secretStore || esoSecretData?.secretStoreRef) &&
+ (esoSecretData.esoData || esoSecretData.template || esoSecretData.esoDataFrom)
+
+ return {
+ secretDataYaml: jsonForSecretDataYaml ?? '',
+ esoSecretYaml: isEsoSecretData ? YAMLStringify(esoSecretData) : '',
+ }
+}
+
+export const getConfigMapSecretFormInitialValues = ({
+ configMapSecretData,
+ cmSecretStateLabel,
+ componentType,
+}: Pick<
+ ConfigMapSecretFormProps,
+ 'cmSecretStateLabel' | 'componentType' | 'configMapSecretData'
+>): ConfigMapSecretUseFormProps => {
+ const isSecret = componentType === CMSecretComponentType.Secret
+
+ if (configMapSecretData) {
+ const {
+ name,
+ external,
+ externalType,
+ type,
+ mountPath,
+ defaultMountPath,
+ subPath,
+ data,
+ filePermission,
+ roleARN,
+ esoSubPath,
+ } = configMapSecretData
+ const currentData = processCurrentData({
+ configMapSecretData,
+ cmSecretStateLabel,
+ isSecret,
+ })
+
+ return {
+ name,
+ isSecret,
+ externalType: externalType ?? CMSecretExternalType.Internal,
+ external,
+ selectedType: type ?? configMapSecretMountDataMap.environment.value,
+ isFilePermissionChecked: !!filePermission,
+ isSubPathChecked: !!subPath,
+ externalSubpathValues: processExternalSubPathValues({ data, external, subPath, esoSubPath }),
+ filePermission: filePermission ?? '',
+ volumeMountPath: mountPath ?? defaultMountPath ?? '',
+ roleARN: roleARN ?? '',
+ yamlMode: true,
+ yaml: convertKeyValuePairToYAML(currentData),
+ currentData,
+ hasCurrentDataErr: false,
+ isResolvedData: false,
+ ...getSecretDataFromConfigData(configMapSecretData),
+ }
+ }
+
+ return {
+ name: '',
+ isSecret,
+ externalType: CMSecretExternalType.Internal,
+ external: false,
+ selectedType: configMapSecretMountDataMap.environment.value,
+ isFilePermissionChecked: false,
+ isSubPathChecked: false,
+ externalSubpathValues: '',
+ filePermission: '',
+ volumeMountPath: '',
+ roleARN: '',
+ yamlMode: true,
+ yaml: '"": ""\n',
+ currentData: CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA,
+ hasCurrentDataErr: false,
+ isResolvedData: false,
+ esoSecretYaml: '{}',
+ secretDataYaml: '[]',
+ }
+}
+
+export const getConfigMapSecretReadOnlyValues = ({
+ configMapSecretData,
+ componentType,
+ isJob,
+}: Pick) => {
+ if (!configMapSecretData) {
+ return {
+ configData: [],
+ data: null,
+ }
+ }
+
+ const {
+ external,
+ externalType,
+ esoSecretYaml,
+ externalSubpathValues,
+ filePermission,
+ isSubPathChecked,
+ roleARN,
+ secretDataYaml,
+ selectedType,
+ volumeMountPath,
+ yaml,
+ currentData,
+ isSecret,
+ } = getConfigMapSecretFormInitialValues({
+ configMapSecretData,
+ cmSecretStateLabel: CM_SECRET_STATE.INHERITED,
+ componentType,
+ })
+ const mountExistingExternal =
+ external && externalType === (isSecret ? CMSecretExternalType.KubernetesSecret : CMSecretExternalType.Internal)
+
+ let dataType = ''
+ if (!isSecret) {
+ dataType = configMapDataTypeOptions.find(({ value }) =>
+ external && externalType === ''
+ ? value === CMSecretExternalType.KubernetesConfigMap
+ : value === externalType,
+ ).label as string
+ } else {
+ dataType =
+ external && externalType === ''
+ ? CMSecretExternalType.KubernetesSecret
+ : (getSelectPickerOptionByValue(getSecretDataTypeOptions(isJob, true), externalType).label as string)
+ }
+
+ return {
+ configData: [
+ {
+ displayName: 'DataType',
+ value: dataType,
+ },
+ {
+ displayName: 'Mount data as',
+ value: configMapSecretMountDataMap[selectedType].title,
+ },
+ {
+ displayName: 'Volume mount path',
+ value: volumeMountPath,
+ },
+ {
+ displayName: 'Set Sub Path',
+ value:
+ (configMapSecretMountDataMap[selectedType].value === 'volume' &&
+ (isSubPathChecked ? 'True' : 'False')) ||
+ '',
+ },
+ {
+ displayName: 'Subpath Values',
+ value: externalSubpathValues,
+ },
+ {
+ displayName: 'File Permission',
+ value: filePermission,
+ },
+ {
+ displayName: 'Role ARN',
+ value: roleARN,
+ },
+ ],
+ data: !mountExistingExternal ? (currentData?.[0]?.k && yaml) || esoSecretYaml || secretDataYaml : null,
+ }
+}
+// FORM UTILS ----------------------------------------------------------------
+
+// PAYLOAD UTILS ----------------------------------------------------------------
+export const getESOSecretDataFromYAML = (yaml: string): ESOSecretData => {
+ try {
+ const json = YAML.parse(yaml)
+ if (typeof json === 'object') {
+ const payload = {
+ secretStore: json.secretStore,
+ secretStoreRef: json.secretStoreRef,
+ refreshInterval: json.refreshInterval,
+ // if null don't send these keys which is achieved by `undefined`
+ esoData: undefined,
+ esoDataFrom: undefined,
+ template: undefined,
+ }
+ if (Array.isArray(json?.esoData)) {
+ payload.esoData = json.esoData
+ }
+ if (Array.isArray(json?.esoDataFrom)) {
+ payload.esoDataFrom = json.esoDataFrom
+ }
+ if (typeof json?.template === 'object' && !Array.isArray(json.template)) {
+ payload.template = json.template
+ }
+ return payload
+ }
+ return null
+ } catch {
+ return null
+ }
+}
+
+export const getConfigMapSecretPayload = ({
+ isSecret,
+ external,
+ externalType,
+ externalSubpathValues,
+ yaml,
+ yamlMode,
+ currentData,
+ esoSecretYaml,
+ filePermission,
+ name,
+ selectedType,
+ isFilePermissionChecked,
+ roleARN,
+ volumeMountPath,
+ isSubPathChecked,
+}: ConfigMapSecretUseFormProps) => {
+ const isESO = isSecret && hasESO(externalType)
+ const _currentData = yamlMode ? convertYAMLToKeyValuePair(yaml) : currentData
+ const data = _currentData.reduce((acc, curr) => {
+ if (!curr.k) {
+ return acc
+ }
+ const value = curr.v ?? ''
+
+ return {
+ ...acc,
+ [curr.k]: isSecret && externalType === '' ? btoa(value) : value,
+ }
+ }, {})
+
+ const payload: CMSecretPayloadType = {
+ name,
+ type: selectedType,
+ external,
+ data,
+ roleARN: null,
+ externalType: null,
+ esoSecretData: null,
+ mountPath: null,
+ subPath: null,
+ filePermission: null,
+ esoSubPath: null,
+ }
+
+ if (
+ (isSecret && externalType === CMSecretExternalType.KubernetesSecret) ||
+ (!isSecret && external) ||
+ (isSecret && isESO)
+ ) {
+ delete payload[CODE_EDITOR_RADIO_STATE.DATA]
+ }
+ if (isSecret) {
+ payload.roleARN = ''
+ payload.externalType = externalType
+
+ if (isESO) {
+ const esoSecretData = getESOSecretDataFromYAML(esoSecretYaml)
+ if (esoSecretData) {
+ payload.esoSecretData = {
+ secretStore: esoSecretData.secretStore,
+ esoData: esoSecretData.esoData,
+ secretStoreRef: esoSecretData.secretStoreRef,
+ refreshInterval: esoSecretData.refreshInterval,
+ esoDataFrom: esoSecretData.esoDataFrom,
+ template: esoSecretData.template,
+ }
+ payload.roleARN = roleARN
+ if (isSubPathChecked && externalSubpathValues) {
+ payload.esoSubPath = externalSubpathValues.replace(/\s+/g, '').split(',')
+ }
+ }
+ }
+ }
+ if (selectedType === configMapSecretMountDataMap.volume.value) {
+ payload.mountPath = volumeMountPath
+ payload.subPath = isSubPathChecked
+ if (isFilePermissionChecked) {
+ payload.filePermission = filePermission.length === 3 ? `0${filePermission}` : `${filePermission}`
+ }
+
+ if (
+ isSubPathChecked &&
+ ((isSecret && externalType === CMSecretExternalType.KubernetesSecret) || (!isSecret && external))
+ ) {
+ const externalSubpathKey = externalSubpathValues.replace(/\s+/g, '').split(',')
+ const secretKeys = {}
+ externalSubpathKey.forEach((key) => {
+ secretKeys[key] = ''
+ })
+ payload.data = secretKeys
+ }
+ }
+
+ return payload
+}
+
+const getConfigMapSecretDecodedData = ({
+ configMapSecretData,
+ isDraft,
+ isSecret,
+}: ConfigMapSecretDecodedDataProps): ConfigMapSecretDecodedDataReturnType => {
+ if (!configMapSecretData || !isSecret || configMapSecretData.unAuthorized) {
+ return configMapSecretData
+ }
+
+ if (!isDraft) {
+ const _configMapSecretData =
+ configMapSecretData as ConfigMapSecretDecodedDataProps['configMapSecretData']
+ if (!isSecret || _configMapSecretData.unAuthorized || _configMapSecretData.externalType !== '') {
+ return _configMapSecretData as ConfigMapSecretDecodedDataReturnType
+ }
+
+ return {
+ ..._configMapSecretData,
+ data: decode(_configMapSecretData.data),
+ isDecoded: true,
+ } as ConfigMapSecretDecodedDataReturnType
+ }
+
+ const draftData = configMapSecretData as ConfigMapSecretDecodedDataProps['configMapSecretData']
+ const parsedData = JSON.parse(draftData.data).configData[0]
+ if (!isSecret || draftData.unAuthorized || parsedData.externalType !== '') {
+ return draftData as ConfigMapSecretDecodedDataReturnType
+ }
+
+ return {
+ ...draftData,
+ parsedData: decode(parsedData.data),
+ isDecoded: true,
+ } as ConfigMapSecretDecodedDataReturnType
+}
+
+const getConfigMapSecretEncodedData = ({
+ configMapSecretData,
+ isDraft,
+}: ConfigMapSecretEncodedDataProps): ConfigMapSecretEncodedDataReturnType => {
+ if (!configMapSecretData || !configMapSecretData.isDecoded) {
+ return configMapSecretData
+ }
+
+ if (!isDraft) {
+ const _configMapSecretData =
+ configMapSecretData as ConfigMapSecretEncodedDataProps['configMapSecretData']
+ return {
+ ..._configMapSecretData,
+ data: decode(_configMapSecretData.data, true),
+ } as ConfigMapSecretEncodedDataReturnType
+ }
+
+ const draftData = configMapSecretData as ConfigMapSecretEncodedDataProps['configMapSecretData']
+ const parsedData = JSON.parse(draftData.data)
+ return {
+ ...draftData,
+ data: JSON.stringify({
+ ...parsedData,
+ configData: [{ ...parsedData.configData[0], data: decode(draftData.parsedData, true) }],
+ }),
+ } as ConfigMapSecretEncodedDataReturnType
+}
+
+export const getConfigMapSecretResolvedDataPayload = ({
+ formData,
+ inheritedConfigMapSecretData,
+ configMapSecretData,
+ draftData,
+ isSecret,
+}: {
+ formData: ConfigMapSecretUseFormProps
+ inheritedConfigMapSecretData: CMSecretConfigData
+ configMapSecretData: CMSecretConfigData
+ draftData: CMSecretDraftData
+ isSecret: boolean
+}) => {
+ const values = {
+ formData,
+ inheritedConfigMapSecretData: getConfigMapSecretDecodedData({
+ configMapSecretData: inheritedConfigMapSecretData,
+ isSecret,
+ }),
+ configMapSecretData: getConfigMapSecretDecodedData({ configMapSecretData, isSecret }),
+ draftData: getConfigMapSecretDecodedData({ configMapSecretData: draftData, isSecret, isDraft: true }),
+ }
+
+ return YAMLStringify(values)
+}
+// PAYLOAD UTILS ----------------------------------------------------------------
+
+// DATA UTILS ----------------------------------------------------------------
+export const getConfigMapSecretDraftAndPublishedData = ({
+ isJob,
+ isSecret,
+ cmSecretConfigDataRes,
+ draftConfigDataRes,
+}: Pick & {
+ isSecret: boolean
+ cmSecretConfigDataRes: PromiseSettledResult
+ draftConfigDataRes: PromiseSettledResult
+}) => {
+ let configMapSecretData: CMSecretConfigData = null
+ let draftData: CMSecretDraftData = null
+
+ // DRAFT DATA PROCESSING
+ if (draftConfigDataRes.status === 'fulfilled') {
+ const draftConfigData = draftConfigDataRes.value
+
+ if (
+ draftConfigData &&
+ (draftConfigData.draftState === DraftState.Init || draftConfigData.draftState === DraftState.AwaitApproval)
+ ) {
+ draftData = {
+ ...draftConfigData,
+ unAuthorized: !draftConfigData.isAppAdmin,
+ }
+ }
+ }
+
+ // MAIN DATA PROCESSING
+ if (cmSecretConfigDataRes.status === 'fulfilled') {
+ const cmSecretConfigData = cmSecretConfigDataRes.value
+
+ if (cmSecretConfigData) {
+ const configData = isJob
+ ? (cmSecretConfigData as CMSecretDTO).configData
+ : (cmSecretConfigData as AppEnvDeploymentConfigDTO)[!isSecret ? 'configMapData' : 'secretsData'].data
+ .configData
+
+ // Since, jobs can only be created by super-admin users, modify this once API support is available.
+ const unAuthorized = isJob ? false : !(cmSecretConfigData as AppEnvDeploymentConfigDTO).isAppAdmin
+
+ if (configData?.length) {
+ configMapSecretData = {
+ ...configData[0],
+ unAuthorized,
+ }
+ }
+ } else if (draftConfigDataRes.status === 'fulfilled') {
+ const draftConfigData = draftConfigDataRes.value
+
+ if (draftConfigData && draftConfigData.draftState === DraftState.Published) {
+ configMapSecretData = {
+ ...JSON.parse(draftConfigData.data).configData[0],
+ unAuthorized: !draftConfigData.isAppAdmin,
+ }
+ }
+ }
+ }
+
+ return { configMapSecretData, draftData }
+}
+
+export const getConfigMapSecretInheritedData = ({
+ cmSecretConfigDataRes,
+ isJob,
+ isSecret,
+}: {
+ cmSecretConfigDataRes: PromiseSettledResult
+ isJob: boolean
+ isSecret: boolean
+}): CMSecretConfigData => {
+ if (
+ (cmSecretConfigDataRes.status === 'fulfilled' && !cmSecretConfigDataRes.value) ||
+ cmSecretConfigDataRes.status === 'rejected'
+ ) {
+ return null
+ }
+
+ const cmSecretConfigData = cmSecretConfigDataRes.value
+ return isJob
+ ? { ...(cmSecretConfigData as CMSecretDTO).configData[0], unAuthorized: false }
+ : {
+ ...(cmSecretConfigData as AppEnvDeploymentConfigDTO)[!isSecret ? 'configMapData' : 'secretsData'].data
+ .configData[0],
+ unAuthorized: !(cmSecretConfigData as AppEnvDeploymentConfigDTO).isAppAdmin,
+ }
+}
+
+export const getConfigMapSecretResolvedData = (
+ resolvedData: string,
+): {
+ resolvedFormData: ConfigMapSecretUseFormProps
+ resolvedInheritedConfigMapSecretData: CMSecretConfigData
+ resolvedConfigMapSecretData: CMSecretConfigData
+ resolvedDraftData: CMSecretDraftData
+} => {
+ const parsedResolvedData = YAML.parse(resolvedData)
+
+ return {
+ resolvedFormData: parsedResolvedData.formData ?? null,
+ resolvedInheritedConfigMapSecretData: getConfigMapSecretEncodedData({
+ configMapSecretData: parsedResolvedData.inheritedConfigMapSecretData,
+ }),
+ resolvedConfigMapSecretData: getConfigMapSecretEncodedData({
+ configMapSecretData: parsedResolvedData.configMapSecretData,
+ }),
+ resolvedDraftData: getConfigMapSecretEncodedData({
+ configMapSecretData: parsedResolvedData.draftData,
+ isDraft: true,
+ }),
+ }
+}
+
+export const getConfigMapSecretError = (res: PromiseSettledResult) =>
+ res.status === 'rejected' && res.reason?.code !== ERROR_STATUS_CODE.NOT_FOUND ? res.reason : null
+// DATA UTILS ----------------------------------------------------------------
diff --git a/apps/web/src/Pages/Shared/ConfigMapSecret/validations.ts b/apps/web/src/Pages/Shared/ConfigMapSecret/validations.ts
new file mode 100644
index 0000000000..419b3f61fb
--- /dev/null
+++ b/apps/web/src/Pages/Shared/ConfigMapSecret/validations.ts
@@ -0,0 +1,296 @@
+import YAML from 'yaml'
+
+import {
+ CMSecretExternalType,
+ UseFormValidation,
+ UseFormValidations,
+ YAMLStringify,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { PATTERNS } from '@Config/constants'
+import { ValidationRules } from '@Components/cdPipeline/validationRules'
+
+import { CONFIG_MAP_SECRET_NO_DATA_ERROR, CONFIG_MAP_SECRET_YAML_PARSE_ERROR, SECRET_TOAST_INFO } from './constants'
+import { getESOSecretDataFromYAML, hasESO } from './utils'
+import { CMSecretYamlData, ConfigMapSecretUseFormProps } from './types'
+
+/**
+ * Validates a YAML string for proper structure and specific key/value constraints.
+ *
+ * @param yaml - The YAML string to be validated.
+ * @returns Use Form Custom Validation
+ */
+const validateYaml = (yaml: string): UseFormValidation['custom'] => {
+ try {
+ // Check if the YAML string is empty or undefined, if so, throw a no-data error.
+ if (!yaml) {
+ throw new Error(CONFIG_MAP_SECRET_NO_DATA_ERROR)
+ }
+
+ // Parse the YAML string into a JSON object.
+ const json = YAML.parse(yaml)
+
+ // Ensure the parsed object is of type 'object', otherwise throw an error.
+ if (typeof json === 'object') {
+ // Regular expression pattern to validate ConfigMap and Secret keys.
+ const keyPattern = new RegExp(PATTERNS.CONFIG_MAP_AND_SECRET_KEY)
+ const errorKeys = [] // To store keys that do not match the key pattern.
+ const errorValues = [] // To store values that are boolean or numeric, which should be quoted.
+
+ // Iterate through the object keys and validate each key-value pair.
+ Object.keys(json).forEach((k) => {
+ // If a key or its corresponding value is empty, throw a no-data error.
+ if (!k && !json[k]) {
+ throw new Error(CONFIG_MAP_SECRET_NO_DATA_ERROR)
+ }
+
+ // Convert the value to a string for easier validation, handle nested objects using YAMLStringify.
+ const v = json[k] && typeof json[k] === 'object' ? YAMLStringify(json[k]) : json[k].toString()
+
+ // Check if the key matches the pattern, if not, add it to the errorKeys list.
+ if (!(k && keyPattern.test(k))) {
+ errorKeys.push(k)
+ }
+
+ // If the value is a boolean or number, add it to the errorValues list for improper quoting.
+ if (v && (typeof json[k] === 'boolean' || typeof json[k] === 'number')) {
+ errorValues.push(v)
+ }
+ })
+
+ let updatedError = ''
+
+ // If there are invalid keys, append a message listing the problematic keys.
+ if (errorKeys.length > 0) {
+ updatedError = `Error: Keys can contain: (Alphanumeric) (-) (_) (.) | Invalid key(s): ${errorKeys
+ .map((e) => `"${e}"`)
+ .join(', ')}`
+ }
+
+ // If there are boolean or numeric values not wrapped in quotes, append another error message.
+ if (errorValues.length > 0) {
+ if (updatedError !== '') {
+ updatedError += '\n' // Separate key and value error messages by a new line.
+ }
+ updatedError += `Error: Boolean and numeric values must be wrapped in double quotes Eg. ${errorValues
+ .map((e) => `"${e}"`)
+ .join(', ')}`
+ }
+
+ // Return the validation result
+ return {
+ isValid: () => !updatedError, // Validation is valid if no error messages are present.
+ message: updatedError, // The generated error message (if any).
+ }
+ }
+
+ // If the parsed result is not an object, throw a yaml parse error.
+ throw new Error(CONFIG_MAP_SECRET_YAML_PARSE_ERROR)
+ } catch (err) {
+ // Catch any errors, and return an invalid result with a relevant message.
+ return {
+ // Always return false when an error occurs.
+ isValid: () => false,
+ // Display a parsing error.
+ message:
+ err.name === 'YAMLParseError' ? err.message.replace(/[\s]+/g, ' ') : CONFIG_MAP_SECRET_YAML_PARSE_ERROR,
+ }
+ }
+}
+
+/**
+ * Validates a YAML string representing an External Secret Operator (ESO) secret configuration.
+ *
+ * @param esoSecretYaml - The YAML string to be validated.
+ * @returns Use Form Validation
+ */
+const validateEsoSecretYaml = (esoSecretYaml: string): UseFormValidation => {
+ try {
+ // Check if the provided YAML string is empty or undefined, and throw a no-data error.
+ if (!esoSecretYaml) {
+ throw new Error(CONFIG_MAP_SECRET_NO_DATA_ERROR)
+ }
+
+ // Parse the YAML string into a JSON object.
+ const json = getESOSecretDataFromYAML(esoSecretYaml)
+
+ // Ensure the parsed result is non-null before proceeding with further validation.
+ if (json) {
+ // Validation logic:
+ // 1. Ensure only one of 'esoData' or 'esoDataFrom' is provided, not both.
+ // 2. Either 'esoData' or 'esoDataFrom' must be provided (at least one is required).
+ // 3. Ensure that exactly one of 'secretStore' or 'secretStoreRef' is provided (not both or neither).
+ let isValid =
+ !(json.esoData && json.esoDataFrom) && // Rule 1: Only one of 'esoData' or 'esoDataFrom'
+ (json.esoData || json.esoDataFrom) && // Rule 2: At least one must be present
+ !json.secretStore !== !json.secretStoreRef // Rule 3: Exactly one of 'secretStore' or 'secretStoreRef'
+ let errorMessage = ''
+
+ // Validation logic for 'esoData' array:
+ // 1. Check if 'esoData' exists and the previous validation ('isValid') passed.
+ // 2. For each entry in 'esoData', ensure both 'secretKey' and 'key' are present and truthy.
+ // If any entry is missing either 'secretKey' or 'key', set 'isValid' to false.
+ if (json.esoData && isValid) {
+ isValid = json.esoData?.reduce(
+ (_isValid, s) =>
+ // Rule: Both 'secretKey' and 'key' must exist and be truthy for each entry.
+ _isValid && !!s?.secretKey && !!s.key,
+ isValid, // Keep track of overall validity
+ )
+ }
+
+ // Set error messages based on the provided 'secretStore', 'secretStoreRef', and 'esoData'/'esoDataFrom':
+ if (json.esoDataFrom && json.esoData) {
+ // Error: Both 'esoData' and 'esoDataFrom' are provided, which is invalid.
+ errorMessage = SECRET_TOAST_INFO.BOTH_ESO_DATA_AND_DATA_FROM_AVAILABLE
+ } else if (!json.esoDataFrom && !json.esoData) {
+ // Error: Neither 'esoData' nor 'esoDataFrom' is provided, at least one is required.
+ errorMessage = SECRET_TOAST_INFO.BOTH_ESO_DATA_AND_DATA_FROM_UNAVAILABLE
+ } else if (json.secretStore && json.secretStoreRef) {
+ // Error: Both 'secretStore' and 'secretStoreRef' are provided, only one should be.
+ errorMessage = SECRET_TOAST_INFO.BOTH_STORE_AVAILABLE
+ } else if (json.secretStore || json.secretStoreRef) {
+ // Valid: One of 'secretStore' or 'secretStoreRef' is provided, but ensure the keys are correct.
+ errorMessage = SECRET_TOAST_INFO.CHECK_KEY_SECRET_KEY
+ } else {
+ // Error: Neither 'secretStore' nor 'secretStoreRef' is provided, one is required.
+ errorMessage = SECRET_TOAST_INFO.BOTH_STORE_UNAVAILABLE
+ }
+
+ // Return the validation result with a custom validator and the corresponding error message.
+ return {
+ custom: {
+ isValid: () => isValid, // Validation is successful if all checks pass.
+ message: errorMessage, // Error message based on the validation logic.
+ },
+ }
+ }
+
+ // If the parsed result is not an object, throw a yaml parse error.
+ throw new Error(CONFIG_MAP_SECRET_YAML_PARSE_ERROR)
+ } catch (err) {
+ // Catch any errors and return an invalid result with an appropriate error message.
+ return {
+ custom: {
+ // Always return false when an error occurs.
+ isValid: () => false,
+ // Display a parsing error message.
+ message:
+ err.name === 'YAMLParseError'
+ ? err.message.replace(/[\s]+/g, ' ')
+ : CONFIG_MAP_SECRET_YAML_PARSE_ERROR,
+ },
+ }
+ }
+}
+
+export const getConfigMapSecretFormValidations: UseFormValidations = ({
+ isSecret,
+ external,
+ externalType,
+ selectedType,
+ isFilePermissionChecked,
+ volumeMountPath,
+ isSubPathChecked,
+ yaml,
+ yamlMode,
+ esoSecretYaml,
+}) => {
+ const mountExistingExternal =
+ external && externalType === (isSecret ? CMSecretExternalType.KubernetesSecret : CMSecretExternalType.Internal)
+ const isESO = isSecret && hasESO(externalType)
+
+ const rules = new ValidationRules()
+
+ return {
+ name: {
+ required: true,
+ pattern: {
+ value: PATTERNS.CONFIGMAP_AND_SECRET_NAME,
+ message:
+ "Name must start and end with an alphanumeric character. It can contain only lowercase alphanumeric characters, '-' or '.'",
+ },
+ custom: {
+ isValid: (value) => value.length <= 253,
+ message: 'More than 253 characters are not allowed',
+ },
+ },
+ ...(selectedType === 'volume'
+ ? {
+ volumeMountPath: {
+ required: true,
+ custom: {
+ isValid: (value) => rules.cmVolumeMountPath(value).isValid,
+ message: rules.cmVolumeMountPath(volumeMountPath).message,
+ },
+ },
+ ...(isFilePermissionChecked
+ ? {
+ filePermission: {
+ required: true,
+ pattern: {
+ value: PATTERNS.ALL_DIGITS_BETWEEN_0_AND_7,
+ message: 'This is octal number, use numbers between 0 to 7',
+ },
+ custom: [
+ {
+ isValid: (value) => value.length <= 4,
+ message: 'More than 4 characters are not allowed',
+ },
+ {
+ isValid: (value) => value.length !== 4 || value.startsWith('0'),
+ message:
+ '4 characters are allowed in octal format only, first character should be 0',
+ },
+ {
+ isValid: (value) => value.length >= 3,
+ message: 'Atleast 3 character are required',
+ },
+ ],
+ },
+ }
+ : {}),
+ ...(isSubPathChecked && ((isSecret && externalType === 'KubernetesSecret') || (!isSecret && external))
+ ? {
+ externalSubpathValues: {
+ required: true,
+ pattern: {
+ value: PATTERNS.CONFIG_MAP_AND_SECRET_MULTPLS_KEYS,
+ message: 'Use (a-z), (0-9), (-), (_),(.); Use (,) to separate multiple keys',
+ },
+ },
+ }
+ : {}),
+ }
+ : {}),
+ ...(!isESO && !mountExistingExternal
+ ? {
+ ...(yamlMode
+ ? {
+ yaml: {
+ custom: validateYaml(yaml),
+ },
+ }
+ : {
+ hasCurrentDataErr: {
+ custom: {
+ isValid: (value) => !value,
+ message: 'Please resolve the errors before saving',
+ },
+ },
+ currentData: {
+ custom: {
+ isValid: (value: CMSecretYamlData[]) => !!value.filter(({ k }) => !!k).length,
+ message: CONFIG_MAP_SECRET_NO_DATA_ERROR,
+ },
+ },
+ }),
+ }
+ : {}),
+ ...(isSecret && isESO
+ ? {
+ esoSecretYaml: validateEsoSecretYaml(esoSecretYaml),
+ }
+ : {}),
+ }
+}
diff --git a/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx b/apps/web/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx
similarity index 92%
rename from src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx
rename to apps/web/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx
index 0c82f4ebb0..880385ee93 100644
--- a/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx
+++ b/apps/web/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx
@@ -30,9 +30,9 @@ import {
import { mapByKey, ErrorBoundary, useAppContext } from '@Components/common'
import { APP_COMPOSE_STAGE, URLS, getAppComposeURL } from '@Config/index'
-import DeploymentTemplateOverride from '@Components/deploymentConfig/DeploymentTemplateView/DeploymentTemplateOverride'
import { ConfigMapSecretWrapper } from '@Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper'
-import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/ConfigMapSecret.types'
+import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types'
+import { DeploymentTemplate } from '@Pages/Applications'
import { ComponentStates, EnvironmentOverrideComponentProps } from './EnvironmentOverrides.types'
import './environmentOverride.scss'
@@ -143,30 +143,29 @@ const EnvironmentOverride = ({
return ''
}
+ const clusterId = environmentsMap.get(+params.envId)?.clusterId?.toString()
+
return (
-
void
- fetchEnvConfig: (envId: number) => void
-}
export interface ListComponentType {
name: string
type: string
diff --git a/src/Pages/Shared/EnvironmentOverride/environmentOverride.scss b/apps/web/src/Pages/Shared/EnvironmentOverride/environmentOverride.scss
similarity index 95%
rename from src/Pages/Shared/EnvironmentOverride/environmentOverride.scss
rename to apps/web/src/Pages/Shared/EnvironmentOverride/environmentOverride.scss
index 81c5bc3713..f7b6a13de0 100644
--- a/src/Pages/Shared/EnvironmentOverride/environmentOverride.scss
+++ b/apps/web/src/Pages/Shared/EnvironmentOverride/environmentOverride.scss
@@ -214,17 +214,3 @@ div.override-container {
transition: max-height 0.3s ease-in;
overflow: hidden;
}
-
-.deployment-template-override {
- height: calc(100vh - 102px);
-
- .chart-version.popup-button {
- width: 100%;
- }
-}
-
-.variables-widget-position {
- position: absolute;
- bottom: 120px;
- right: 110px;
-}
diff --git a/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverview.constants.ts b/apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverview.constants.ts
similarity index 100%
rename from src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverview.constants.ts
rename to apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverview.constants.ts
diff --git a/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.component.tsx b/apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.component.tsx
similarity index 100%
rename from src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.component.tsx
rename to apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.component.tsx
diff --git a/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.scss b/apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.scss
similarity index 100%
rename from src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.scss
rename to apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.scss
diff --git a/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.types.ts b/apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.types.ts
similarity index 100%
rename from src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.types.ts
rename to apps/web/src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.types.ts
diff --git a/src/Pages/Shared/EnvironmentOverviewTable/index.ts b/apps/web/src/Pages/Shared/EnvironmentOverviewTable/index.ts
similarity index 100%
rename from src/Pages/Shared/EnvironmentOverviewTable/index.ts
rename to apps/web/src/Pages/Shared/EnvironmentOverviewTable/index.ts
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIAppList.tsx b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIAppList.tsx
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/LinkedCIAppList.tsx
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIAppList.tsx
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetail.tsx b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetail.tsx
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetail.tsx
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetail.tsx
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetailsModal.tsx b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetailsModal.tsx
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetailsModal.tsx
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/LinkedCIDetailsModal.tsx
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/constants.ts b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/constants.ts
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/constants.ts
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/constants.ts
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/index.ts b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/index.ts
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/index.ts
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/index.ts
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/linkedCIAppList.scss b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/linkedCIAppList.scss
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/linkedCIAppList.scss
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/linkedCIAppList.scss
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/service.ts b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/service.ts
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/service.ts
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/service.ts
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/types.ts b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/types.ts
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/types.ts
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/types.ts
diff --git a/src/Pages/Shared/LinkedCIDetailsModal/utils.ts b/apps/web/src/Pages/Shared/LinkedCIDetailsModal/utils.ts
similarity index 100%
rename from src/Pages/Shared/LinkedCIDetailsModal/utils.ts
rename to apps/web/src/Pages/Shared/LinkedCIDetailsModal/utils.ts
diff --git a/src/Pages/Shared/OrganizationFrame/OrganizationFrame.component.tsx b/apps/web/src/Pages/Shared/OrganizationFrame/OrganizationFrame.component.tsx
similarity index 100%
rename from src/Pages/Shared/OrganizationFrame/OrganizationFrame.component.tsx
rename to apps/web/src/Pages/Shared/OrganizationFrame/OrganizationFrame.component.tsx
diff --git a/src/Pages/Shared/OrganizationFrame/OrganizationTextLogo.tsx b/apps/web/src/Pages/Shared/OrganizationFrame/OrganizationTextLogo.tsx
similarity index 100%
rename from src/Pages/Shared/OrganizationFrame/OrganizationTextLogo.tsx
rename to apps/web/src/Pages/Shared/OrganizationFrame/OrganizationTextLogo.tsx
diff --git a/src/Pages/Shared/OrganizationFrame/index.ts b/apps/web/src/Pages/Shared/OrganizationFrame/index.ts
similarity index 100%
rename from src/Pages/Shared/OrganizationFrame/index.ts
rename to apps/web/src/Pages/Shared/OrganizationFrame/index.ts
diff --git a/src/Pages/Shared/index.ts b/apps/web/src/Pages/Shared/index.ts
similarity index 100%
rename from src/Pages/Shared/index.ts
rename to apps/web/src/Pages/Shared/index.ts
diff --git a/src/Pages/index.ts b/apps/web/src/Pages/index.ts
similarity index 95%
rename from src/Pages/index.ts
rename to apps/web/src/Pages/index.ts
index db2e46361e..144b6e193b 100644
--- a/src/Pages/index.ts
+++ b/apps/web/src/Pages/index.ts
@@ -15,3 +15,4 @@
*/
export * from './GlobalConfigurations'
+export * from './Applications'
diff --git a/src/assets/fonts/glyphicons-halflings-regular.eot b/apps/web/src/assets/fonts/glyphicons-halflings-regular.eot
similarity index 100%
rename from src/assets/fonts/glyphicons-halflings-regular.eot
rename to apps/web/src/assets/fonts/glyphicons-halflings-regular.eot
diff --git a/src/assets/fonts/glyphicons-halflings-regular.svg b/apps/web/src/assets/fonts/glyphicons-halflings-regular.svg
similarity index 100%
rename from src/assets/fonts/glyphicons-halflings-regular.svg
rename to apps/web/src/assets/fonts/glyphicons-halflings-regular.svg
diff --git a/src/assets/fonts/glyphicons-halflings-regular.ttf b/apps/web/src/assets/fonts/glyphicons-halflings-regular.ttf
similarity index 100%
rename from src/assets/fonts/glyphicons-halflings-regular.ttf
rename to apps/web/src/assets/fonts/glyphicons-halflings-regular.ttf
diff --git a/src/assets/fonts/glyphicons-halflings-regular.woff b/apps/web/src/assets/fonts/glyphicons-halflings-regular.woff
similarity index 100%
rename from src/assets/fonts/glyphicons-halflings-regular.woff
rename to apps/web/src/assets/fonts/glyphicons-halflings-regular.woff
diff --git a/src/assets/fonts/glyphicons-halflings-regular.woff2 b/apps/web/src/assets/fonts/glyphicons-halflings-regular.woff2
similarity index 100%
rename from src/assets/fonts/glyphicons-halflings-regular.woff2
rename to apps/web/src/assets/fonts/glyphicons-halflings-regular.woff2
diff --git a/src/assets/gif/latest-version-celebration.gif b/apps/web/src/assets/gif/latest-version-celebration.gif
similarity index 100%
rename from src/assets/gif/latest-version-celebration.gif
rename to apps/web/src/assets/gif/latest-version-celebration.gif
diff --git a/src/assets/gif/no-eligible-commit.gif b/apps/web/src/assets/gif/no-eligible-commit.gif
similarity index 100%
rename from src/assets/gif/no-eligible-commit.gif
rename to apps/web/src/assets/gif/no-eligible-commit.gif
diff --git a/src/assets/gif/uploading.gif b/apps/web/src/assets/gif/uploading.gif
similarity index 100%
rename from src/assets/gif/uploading.gif
rename to apps/web/src/assets/gif/uploading.gif
diff --git a/src/assets/ic-info-filled-border.svg b/apps/web/src/assets/ic-info-filled-border.svg
similarity index 100%
rename from src/assets/ic-info-filled-border.svg
rename to apps/web/src/assets/ic-info-filled-border.svg
diff --git a/src/assets/icons/LoginSprite.svg b/apps/web/src/assets/icons/LoginSprite.svg
similarity index 100%
rename from src/assets/icons/LoginSprite.svg
rename to apps/web/src/assets/icons/LoginSprite.svg
diff --git a/src/assets/icons/RectangleLine.svg b/apps/web/src/assets/icons/RectangleLine.svg
similarity index 100%
rename from src/assets/icons/RectangleLine.svg
rename to apps/web/src/assets/icons/RectangleLine.svg
diff --git a/src/assets/icons/appstatus/bg-blue.svg b/apps/web/src/assets/icons/appstatus/bg-blue.svg
similarity index 100%
rename from src/assets/icons/appstatus/bg-blue.svg
rename to apps/web/src/assets/icons/appstatus/bg-blue.svg
diff --git a/src/assets/icons/appstatus/bg-gray.svg b/apps/web/src/assets/icons/appstatus/bg-gray.svg
similarity index 100%
rename from src/assets/icons/appstatus/bg-gray.svg
rename to apps/web/src/assets/icons/appstatus/bg-gray.svg
diff --git a/src/assets/icons/appstatus/bg-white.svg b/apps/web/src/assets/icons/appstatus/bg-white.svg
similarity index 100%
rename from src/assets/icons/appstatus/bg-white.svg
rename to apps/web/src/assets/icons/appstatus/bg-white.svg
diff --git a/src/assets/icons/appstatus/degraded.svg b/apps/web/src/assets/icons/appstatus/degraded.svg
similarity index 100%
rename from src/assets/icons/appstatus/degraded.svg
rename to apps/web/src/assets/icons/appstatus/degraded.svg
diff --git a/src/assets/icons/appstatus/healthy.svg b/apps/web/src/assets/icons/appstatus/healthy.svg
similarity index 100%
rename from src/assets/icons/appstatus/healthy.svg
rename to apps/web/src/assets/icons/appstatus/healthy.svg
diff --git a/src/assets/icons/appstatus/ic-appstatus-failed.svg b/apps/web/src/assets/icons/appstatus/ic-appstatus-failed.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-appstatus-failed.svg
rename to apps/web/src/assets/icons/appstatus/ic-appstatus-failed.svg
diff --git a/src/assets/icons/appstatus/ic-check.svg b/apps/web/src/assets/icons/appstatus/ic-check.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-check.svg
rename to apps/web/src/assets/icons/appstatus/ic-check.svg
diff --git a/src/assets/icons/appstatus/ic-chevron-down.svg b/apps/web/src/assets/icons/appstatus/ic-chevron-down.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-chevron-down.svg
rename to apps/web/src/assets/icons/appstatus/ic-chevron-down.svg
diff --git a/src/assets/icons/appstatus/ic-cpu.svg b/apps/web/src/assets/icons/appstatus/ic-cpu.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-cpu.svg
rename to apps/web/src/assets/icons/appstatus/ic-cpu.svg
diff --git a/src/assets/icons/appstatus/ic-heart-green.svg b/apps/web/src/assets/icons/appstatus/ic-heart-green.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-heart-green.svg
rename to apps/web/src/assets/icons/appstatus/ic-heart-green.svg
diff --git a/src/assets/icons/appstatus/ic-menu-dots.svg b/apps/web/src/assets/icons/appstatus/ic-menu-dots.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-menu-dots.svg
rename to apps/web/src/assets/icons/appstatus/ic-menu-dots.svg
diff --git a/src/assets/icons/appstatus/ic-ram.svg b/apps/web/src/assets/icons/appstatus/ic-ram.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-ram.svg
rename to apps/web/src/assets/icons/appstatus/ic-ram.svg
diff --git a/src/assets/icons/appstatus/ic-sort-down.svg b/apps/web/src/assets/icons/appstatus/ic-sort-down.svg
similarity index 100%
rename from src/assets/icons/appstatus/ic-sort-down.svg
rename to apps/web/src/assets/icons/appstatus/ic-sort-down.svg
diff --git a/src/assets/icons/appstatus/info-filled.svg b/apps/web/src/assets/icons/appstatus/info-filled.svg
similarity index 100%
rename from src/assets/icons/appstatus/info-filled.svg
rename to apps/web/src/assets/icons/appstatus/info-filled.svg
diff --git a/src/assets/icons/appstatus/missing.svg b/apps/web/src/assets/icons/appstatus/missing.svg
similarity index 100%
rename from src/assets/icons/appstatus/missing.svg
rename to apps/web/src/assets/icons/appstatus/missing.svg
diff --git a/src/assets/icons/appstatus/notdeployed.svg b/apps/web/src/assets/icons/appstatus/notdeployed.svg
similarity index 100%
rename from src/assets/icons/appstatus/notdeployed.svg
rename to apps/web/src/assets/icons/appstatus/notdeployed.svg
diff --git a/src/assets/icons/appstatus/progressing-rotating.svg b/apps/web/src/assets/icons/appstatus/progressing-rotating.svg
similarity index 100%
rename from src/assets/icons/appstatus/progressing-rotating.svg
rename to apps/web/src/assets/icons/appstatus/progressing-rotating.svg
diff --git a/src/assets/icons/appstatus/suspended.svg b/apps/web/src/assets/icons/appstatus/suspended.svg
similarity index 100%
rename from src/assets/icons/appstatus/suspended.svg
rename to apps/web/src/assets/icons/appstatus/suspended.svg
diff --git a/src/assets/icons/appstatus/unknown.svg b/apps/web/src/assets/icons/appstatus/unknown.svg
similarity index 100%
rename from src/assets/icons/appstatus/unknown.svg
rename to apps/web/src/assets/icons/appstatus/unknown.svg
diff --git a/src/assets/icons/argo-cd-app.svg b/apps/web/src/assets/icons/argo-cd-app.svg
similarity index 100%
rename from src/assets/icons/argo-cd-app.svg
rename to apps/web/src/assets/icons/argo-cd-app.svg
diff --git a/src/assets/icons/cluster-overview.svg b/apps/web/src/assets/icons/cluster-overview.svg
similarity index 100%
rename from src/assets/icons/cluster-overview.svg
rename to apps/web/src/assets/icons/cluster-overview.svg
diff --git a/src/assets/icons/container.svg b/apps/web/src/assets/icons/container.svg
similarity index 100%
rename from src/assets/icons/container.svg
rename to apps/web/src/assets/icons/container.svg
diff --git a/src/assets/icons/drag.svg b/apps/web/src/assets/icons/drag.svg
similarity index 100%
rename from src/assets/icons/drag.svg
rename to apps/web/src/assets/icons/drag.svg
diff --git a/src/assets/icons/git/azure.svg b/apps/web/src/assets/icons/git/azure.svg
similarity index 100%
rename from src/assets/icons/git/azure.svg
rename to apps/web/src/assets/icons/git/azure.svg
diff --git a/src/assets/icons/git/bitbucket.svg b/apps/web/src/assets/icons/git/bitbucket.svg
similarity index 100%
rename from src/assets/icons/git/bitbucket.svg
rename to apps/web/src/assets/icons/git/bitbucket.svg
diff --git a/src/assets/icons/git/docker.svg b/apps/web/src/assets/icons/git/docker.svg
similarity index 100%
rename from src/assets/icons/git/docker.svg
rename to apps/web/src/assets/icons/git/docker.svg
diff --git a/src/assets/icons/git/git.svg b/apps/web/src/assets/icons/git/git.svg
similarity index 100%
rename from src/assets/icons/git/git.svg
rename to apps/web/src/assets/icons/git/git.svg
diff --git a/src/assets/icons/git/github.svg b/apps/web/src/assets/icons/git/github.svg
similarity index 100%
rename from src/assets/icons/git/github.svg
rename to apps/web/src/assets/icons/git/github.svg
diff --git a/src/assets/icons/git/gitlab.svg b/apps/web/src/assets/icons/git/gitlab.svg
similarity index 100%
rename from src/assets/icons/git/gitlab.svg
rename to apps/web/src/assets/icons/git/gitlab.svg
diff --git a/src/assets/icons/go-to-buildanddeploy.svg b/apps/web/src/assets/icons/go-to-buildanddeploy.svg
similarity index 100%
rename from src/assets/icons/go-to-buildanddeploy.svg
rename to apps/web/src/assets/icons/go-to-buildanddeploy.svg
diff --git a/src/assets/icons/go-to-envoverride.svg b/apps/web/src/assets/icons/go-to-envoverride.svg
similarity index 100%
rename from src/assets/icons/go-to-envoverride.svg
rename to apps/web/src/assets/icons/go-to-envoverride.svg
diff --git a/src/assets/icons/helm-app.svg b/apps/web/src/assets/icons/helm-app.svg
similarity index 100%
rename from src/assets/icons/helm-app.svg
rename to apps/web/src/assets/icons/helm-app.svg
diff --git a/src/assets/icons/ic-CD.svg b/apps/web/src/assets/icons/ic-CD.svg
similarity index 100%
rename from src/assets/icons/ic-CD.svg
rename to apps/web/src/assets/icons/ic-CD.svg
diff --git a/src/assets/icons/ic-CI.svg b/apps/web/src/assets/icons/ic-CI.svg
similarity index 100%
rename from src/assets/icons/ic-CI.svg
rename to apps/web/src/assets/icons/ic-CI.svg
diff --git a/src/assets/icons/ic-CILinked.svg b/apps/web/src/assets/icons/ic-CILinked.svg
similarity index 100%
rename from src/assets/icons/ic-CILinked.svg
rename to apps/web/src/assets/icons/ic-CILinked.svg
diff --git a/src/assets/icons/ic-CIWebhook.svg b/apps/web/src/assets/icons/ic-CIWebhook.svg
similarity index 100%
rename from src/assets/icons/ic-CIWebhook.svg
rename to apps/web/src/assets/icons/ic-CIWebhook.svg
diff --git a/src/assets/icons/ic-abort.svg b/apps/web/src/assets/icons/ic-abort.svg
similarity index 100%
rename from src/assets/icons/ic-abort.svg
rename to apps/web/src/assets/icons/ic-abort.svg
diff --git a/src/assets/icons/ic-activity.svg b/apps/web/src/assets/icons/ic-activity.svg
similarity index 100%
rename from src/assets/icons/ic-activity.svg
rename to apps/web/src/assets/icons/ic-activity.svg
diff --git a/src/assets/icons/ic-add.svg b/apps/web/src/assets/icons/ic-add.svg
similarity index 100%
rename from src/assets/icons/ic-add.svg
rename to apps/web/src/assets/icons/ic-add.svg
diff --git a/src/assets/icons/ic-alert-triangle.svg b/apps/web/src/assets/icons/ic-alert-triangle.svg
similarity index 100%
rename from src/assets/icons/ic-alert-triangle.svg
rename to apps/web/src/assets/icons/ic-alert-triangle.svg
diff --git a/src/assets/icons/ic-app-group.svg b/apps/web/src/assets/icons/ic-app-group.svg
similarity index 100%
rename from src/assets/icons/ic-app-group.svg
rename to apps/web/src/assets/icons/ic-app-group.svg
diff --git a/src/assets/icons/ic-appstatus-cancelled.svg b/apps/web/src/assets/icons/ic-appstatus-cancelled.svg
similarity index 100%
rename from src/assets/icons/ic-appstatus-cancelled.svg
rename to apps/web/src/assets/icons/ic-appstatus-cancelled.svg
diff --git a/src/assets/icons/ic-argocd-app.svg b/apps/web/src/assets/icons/ic-argocd-app.svg
similarity index 100%
rename from src/assets/icons/ic-argocd-app.svg
rename to apps/web/src/assets/icons/ic-argocd-app.svg
diff --git a/src/assets/icons/ic-arrow-anticlockwise.svg b/apps/web/src/assets/icons/ic-arrow-anticlockwise.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-anticlockwise.svg
rename to apps/web/src/assets/icons/ic-arrow-anticlockwise.svg
diff --git a/src/assets/icons/ic-arrow-backward.svg b/apps/web/src/assets/icons/ic-arrow-backward.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-backward.svg
rename to apps/web/src/assets/icons/ic-arrow-backward.svg
diff --git a/src/assets/icons/ic-arrow-clockwise.svg b/apps/web/src/assets/icons/ic-arrow-clockwise.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-clockwise.svg
rename to apps/web/src/assets/icons/ic-arrow-clockwise.svg
diff --git a/src/assets/icons/ic-arrow-counter-clockwise.svg b/apps/web/src/assets/icons/ic-arrow-counter-clockwise.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-counter-clockwise.svg
rename to apps/web/src/assets/icons/ic-arrow-counter-clockwise.svg
diff --git a/src/assets/icons/ic-arrow-down.svg b/apps/web/src/assets/icons/ic-arrow-down.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-down.svg
rename to apps/web/src/assets/icons/ic-arrow-down.svg
diff --git a/src/assets/icons/ic-arrow-forward.svg b/apps/web/src/assets/icons/ic-arrow-forward.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-forward.svg
rename to apps/web/src/assets/icons/ic-arrow-forward.svg
diff --git a/src/assets/icons/ic-arrow-left.svg b/apps/web/src/assets/icons/ic-arrow-left.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-left.svg
rename to apps/web/src/assets/icons/ic-arrow-left.svg
diff --git a/src/assets/icons/ic-arrow-line-down.svg b/apps/web/src/assets/icons/ic-arrow-line-down.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-line-down.svg
rename to apps/web/src/assets/icons/ic-arrow-line-down.svg
diff --git a/src/assets/icons/ic-arrow-line-up.svg b/apps/web/src/assets/icons/ic-arrow-line-up.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-line-up.svg
rename to apps/web/src/assets/icons/ic-arrow-line-up.svg
diff --git a/src/assets/icons/ic-arrow-right.svg b/apps/web/src/assets/icons/ic-arrow-right.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-right.svg
rename to apps/web/src/assets/icons/ic-arrow-right.svg
diff --git a/apps/web/src/assets/icons/ic-arrow-square-in.svg b/apps/web/src/assets/icons/ic-arrow-square-in.svg
new file mode 100644
index 0000000000..99fd43b940
--- /dev/null
+++ b/apps/web/src/assets/icons/ic-arrow-square-in.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/ic-arrow-square-out.svg b/apps/web/src/assets/icons/ic-arrow-square-out.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-square-out.svg
rename to apps/web/src/assets/icons/ic-arrow-square-out.svg
diff --git a/src/assets/icons/ic-arrow-up-down.svg b/apps/web/src/assets/icons/ic-arrow-up-down.svg
similarity index 100%
rename from src/assets/icons/ic-arrow-up-down.svg
rename to apps/web/src/assets/icons/ic-arrow-up-down.svg
diff --git a/src/assets/icons/ic-arrows-left-right.svg b/apps/web/src/assets/icons/ic-arrows-left-right.svg
similarity index 100%
rename from src/assets/icons/ic-arrows-left-right.svg
rename to apps/web/src/assets/icons/ic-arrows-left-right.svg
diff --git a/src/assets/icons/ic-arrows_clockwise.svg b/apps/web/src/assets/icons/ic-arrows_clockwise.svg
similarity index 100%
rename from src/assets/icons/ic-arrows_clockwise.svg
rename to apps/web/src/assets/icons/ic-arrows_clockwise.svg
diff --git a/src/assets/icons/ic-asterisk.svg b/apps/web/src/assets/icons/ic-asterisk.svg
similarity index 100%
rename from src/assets/icons/ic-asterisk.svg
rename to apps/web/src/assets/icons/ic-asterisk.svg
diff --git a/src/assets/icons/ic-aws-codecommit.svg b/apps/web/src/assets/icons/ic-aws-codecommit.svg
similarity index 100%
rename from src/assets/icons/ic-aws-codecommit.svg
rename to apps/web/src/assets/icons/ic-aws-codecommit.svg
diff --git a/src/assets/icons/ic-aws-ses.svg b/apps/web/src/assets/icons/ic-aws-ses.svg
similarity index 100%
rename from src/assets/icons/ic-aws-ses.svg
rename to apps/web/src/assets/icons/ic-aws-ses.svg
diff --git a/src/assets/icons/ic-azure.svg b/apps/web/src/assets/icons/ic-azure.svg
similarity index 100%
rename from src/assets/icons/ic-azure.svg
rename to apps/web/src/assets/icons/ic-azure.svg
diff --git a/src/assets/icons/ic-back.svg b/apps/web/src/assets/icons/ic-back.svg
similarity index 100%
rename from src/assets/icons/ic-back.svg
rename to apps/web/src/assets/icons/ic-back.svg
diff --git a/src/assets/icons/ic-bell.svg b/apps/web/src/assets/icons/ic-bell.svg
similarity index 100%
rename from src/assets/icons/ic-bell.svg
rename to apps/web/src/assets/icons/ic-bell.svg
diff --git a/src/assets/icons/ic-book-open.svg b/apps/web/src/assets/icons/ic-book-open.svg
similarity index 100%
rename from src/assets/icons/ic-book-open.svg
rename to apps/web/src/assets/icons/ic-book-open.svg
diff --git a/src/assets/icons/ic-bot.svg b/apps/web/src/assets/icons/ic-bot.svg
similarity index 100%
rename from src/assets/icons/ic-bot.svg
rename to apps/web/src/assets/icons/ic-bot.svg
diff --git a/src/assets/icons/ic-branch-main.svg b/apps/web/src/assets/icons/ic-branch-main.svg
similarity index 100%
rename from src/assets/icons/ic-branch-main.svg
rename to apps/web/src/assets/icons/ic-branch-main.svg
diff --git a/src/assets/icons/ic-branch.svg b/apps/web/src/assets/icons/ic-branch.svg
similarity index 100%
rename from src/assets/icons/ic-branch.svg
rename to apps/web/src/assets/icons/ic-branch.svg
diff --git a/src/assets/icons/ic-briefcase.svg b/apps/web/src/assets/icons/ic-briefcase.svg
similarity index 100%
rename from src/assets/icons/ic-briefcase.svg
rename to apps/web/src/assets/icons/ic-briefcase.svg
diff --git a/src/assets/icons/ic-browser.svg b/apps/web/src/assets/icons/ic-browser.svg
similarity index 100%
rename from src/assets/icons/ic-browser.svg
rename to apps/web/src/assets/icons/ic-browser.svg
diff --git a/src/assets/icons/ic-bug.svg b/apps/web/src/assets/icons/ic-bug.svg
similarity index 100%
rename from src/assets/icons/ic-bug.svg
rename to apps/web/src/assets/icons/ic-bug.svg
diff --git a/src/assets/icons/ic-builpack.svg b/apps/web/src/assets/icons/ic-builpack.svg
similarity index 100%
rename from src/assets/icons/ic-builpack.svg
rename to apps/web/src/assets/icons/ic-builpack.svg
diff --git a/src/assets/icons/ic-bulb.svg b/apps/web/src/assets/icons/ic-bulb.svg
similarity index 100%
rename from src/assets/icons/ic-bulb.svg
rename to apps/web/src/assets/icons/ic-bulb.svg
diff --git a/src/assets/icons/ic-bulk-check.svg b/apps/web/src/assets/icons/ic-bulk-check.svg
similarity index 100%
rename from src/assets/icons/ic-bulk-check.svg
rename to apps/web/src/assets/icons/ic-bulk-check.svg
diff --git a/src/assets/icons/ic-calendar.svg b/apps/web/src/assets/icons/ic-calendar.svg
similarity index 100%
rename from src/assets/icons/ic-calendar.svg
rename to apps/web/src/assets/icons/ic-calendar.svg
diff --git a/src/assets/icons/ic-camera.svg b/apps/web/src/assets/icons/ic-camera.svg
similarity index 100%
rename from src/assets/icons/ic-camera.svg
rename to apps/web/src/assets/icons/ic-camera.svg
diff --git a/src/assets/icons/ic-caret-left-small.svg b/apps/web/src/assets/icons/ic-caret-left-small.svg
similarity index 100%
rename from src/assets/icons/ic-caret-left-small.svg
rename to apps/web/src/assets/icons/ic-caret-left-small.svg
diff --git a/src/assets/icons/ic-cd-stage.svg b/apps/web/src/assets/icons/ic-cd-stage.svg
similarity index 100%
rename from src/assets/icons/ic-cd-stage.svg
rename to apps/web/src/assets/icons/ic-cd-stage.svg
diff --git a/src/assets/icons/ic-celebration.svg b/apps/web/src/assets/icons/ic-celebration.svg
similarity index 100%
rename from src/assets/icons/ic-celebration.svg
rename to apps/web/src/assets/icons/ic-celebration.svg
diff --git a/src/assets/icons/ic-charts.svg b/apps/web/src/assets/icons/ic-charts.svg
similarity index 100%
rename from src/assets/icons/ic-charts.svg
rename to apps/web/src/assets/icons/ic-charts.svg
diff --git a/src/assets/icons/ic-chat-circle-dots.svg b/apps/web/src/assets/icons/ic-chat-circle-dots.svg
similarity index 100%
rename from src/assets/icons/ic-chat-circle-dots.svg
rename to apps/web/src/assets/icons/ic-chat-circle-dots.svg
diff --git a/src/assets/icons/ic-check-circle-green.svg b/apps/web/src/assets/icons/ic-check-circle-green.svg
similarity index 100%
rename from src/assets/icons/ic-check-circle-green.svg
rename to apps/web/src/assets/icons/ic-check-circle-green.svg
diff --git a/src/assets/icons/ic-check-circle.svg b/apps/web/src/assets/icons/ic-check-circle.svg
similarity index 100%
rename from src/assets/icons/ic-check-circle.svg
rename to apps/web/src/assets/icons/ic-check-circle.svg
diff --git a/src/assets/icons/ic-check.svg b/apps/web/src/assets/icons/ic-check.svg
similarity index 100%
rename from src/assets/icons/ic-check.svg
rename to apps/web/src/assets/icons/ic-check.svg
diff --git a/src/assets/icons/ic-checkbox-intermediate.svg b/apps/web/src/assets/icons/ic-checkbox-intermediate.svg
similarity index 100%
rename from src/assets/icons/ic-checkbox-intermediate.svg
rename to apps/web/src/assets/icons/ic-checkbox-intermediate.svg
diff --git a/src/assets/icons/ic-checkbox-selected.svg b/apps/web/src/assets/icons/ic-checkbox-selected.svg
similarity index 100%
rename from src/assets/icons/ic-checkbox-selected.svg
rename to apps/web/src/assets/icons/ic-checkbox-selected.svg
diff --git a/src/assets/icons/ic-checkbox-unselected.svg b/apps/web/src/assets/icons/ic-checkbox-unselected.svg
similarity index 100%
rename from src/assets/icons/ic-checkbox-unselected.svg
rename to apps/web/src/assets/icons/ic-checkbox-unselected.svg
diff --git a/src/assets/icons/ic-checks.svg b/apps/web/src/assets/icons/ic-checks.svg
similarity index 100%
rename from src/assets/icons/ic-checks.svg
rename to apps/web/src/assets/icons/ic-checks.svg
diff --git a/src/assets/icons/ic-chevron-down.svg b/apps/web/src/assets/icons/ic-chevron-down.svg
similarity index 100%
rename from src/assets/icons/ic-chevron-down.svg
rename to apps/web/src/assets/icons/ic-chevron-down.svg
diff --git a/src/assets/icons/ic-clair-to-trivy.svg b/apps/web/src/assets/icons/ic-clair-to-trivy.svg
similarity index 100%
rename from src/assets/icons/ic-clair-to-trivy.svg
rename to apps/web/src/assets/icons/ic-clair-to-trivy.svg
diff --git a/src/assets/icons/ic-clair.svg b/apps/web/src/assets/icons/ic-clair.svg
similarity index 100%
rename from src/assets/icons/ic-clair.svg
rename to apps/web/src/assets/icons/ic-clair.svg
diff --git a/src/assets/icons/ic-clean-brush-medium.svg b/apps/web/src/assets/icons/ic-clean-brush-medium.svg
similarity index 100%
rename from src/assets/icons/ic-clean-brush-medium.svg
rename to apps/web/src/assets/icons/ic-clean-brush-medium.svg
diff --git a/src/assets/icons/ic-clean-brush.svg b/apps/web/src/assets/icons/ic-clean-brush.svg
similarity index 100%
rename from src/assets/icons/ic-clean-brush.svg
rename to apps/web/src/assets/icons/ic-clean-brush.svg
diff --git a/src/assets/icons/ic-clock-counterclockwise.svg b/apps/web/src/assets/icons/ic-clock-counterclockwise.svg
similarity index 100%
rename from src/assets/icons/ic-clock-counterclockwise.svg
rename to apps/web/src/assets/icons/ic-clock-counterclockwise.svg
diff --git a/src/assets/icons/ic-clock.svg b/apps/web/src/assets/icons/ic-clock.svg
similarity index 100%
rename from src/assets/icons/ic-clock.svg
rename to apps/web/src/assets/icons/ic-clock.svg
diff --git a/src/assets/icons/ic-close-circle.svg b/apps/web/src/assets/icons/ic-close-circle.svg
similarity index 100%
rename from src/assets/icons/ic-close-circle.svg
rename to apps/web/src/assets/icons/ic-close-circle.svg
diff --git a/src/assets/icons/ic-close.svg b/apps/web/src/assets/icons/ic-close.svg
similarity index 100%
rename from src/assets/icons/ic-close.svg
rename to apps/web/src/assets/icons/ic-close.svg
diff --git a/src/assets/icons/ic-cloud.svg b/apps/web/src/assets/icons/ic-cloud.svg
similarity index 100%
rename from src/assets/icons/ic-cloud.svg
rename to apps/web/src/assets/icons/ic-cloud.svg
diff --git a/src/assets/icons/ic-cloudwatch.png b/apps/web/src/assets/icons/ic-cloudwatch.png
similarity index 100%
rename from src/assets/icons/ic-cloudwatch.png
rename to apps/web/src/assets/icons/ic-cloudwatch.png
diff --git a/src/assets/icons/ic-cluster.svg b/apps/web/src/assets/icons/ic-cluster.svg
similarity index 100%
rename from src/assets/icons/ic-cluster.svg
rename to apps/web/src/assets/icons/ic-cluster.svg
diff --git a/src/assets/icons/ic-code-commit.svg b/apps/web/src/assets/icons/ic-code-commit.svg
similarity index 100%
rename from src/assets/icons/ic-code-commit.svg
rename to apps/web/src/assets/icons/ic-code-commit.svg
diff --git a/src/assets/icons/ic-commit.svg b/apps/web/src/assets/icons/ic-commit.svg
similarity index 100%
rename from src/assets/icons/ic-commit.svg
rename to apps/web/src/assets/icons/ic-commit.svg
diff --git a/src/assets/icons/ic-compare.svg b/apps/web/src/assets/icons/ic-compare.svg
similarity index 100%
rename from src/assets/icons/ic-compare.svg
rename to apps/web/src/assets/icons/ic-compare.svg
diff --git a/src/assets/icons/ic-compass.svg b/apps/web/src/assets/icons/ic-compass.svg
similarity index 100%
rename from src/assets/icons/ic-compass.svg
rename to apps/web/src/assets/icons/ic-compass.svg
diff --git a/src/assets/icons/ic-connected.svg b/apps/web/src/assets/icons/ic-connected.svg
similarity index 100%
rename from src/assets/icons/ic-connected.svg
rename to apps/web/src/assets/icons/ic-connected.svg
diff --git a/src/assets/icons/ic-copy.svg b/apps/web/src/assets/icons/ic-copy.svg
similarity index 100%
rename from src/assets/icons/ic-copy.svg
rename to apps/web/src/assets/icons/ic-copy.svg
diff --git a/src/assets/icons/ic-coralogix.png b/apps/web/src/assets/icons/ic-coralogix.png
similarity index 100%
rename from src/assets/icons/ic-coralogix.png
rename to apps/web/src/assets/icons/ic-coralogix.png
diff --git a/src/assets/icons/ic-cordon-medium.svg b/apps/web/src/assets/icons/ic-cordon-medium.svg
similarity index 100%
rename from src/assets/icons/ic-cordon-medium.svg
rename to apps/web/src/assets/icons/ic-cordon-medium.svg
diff --git a/src/assets/icons/ic-cordon.svg b/apps/web/src/assets/icons/ic-cordon.svg
similarity index 100%
rename from src/assets/icons/ic-cordon.svg
rename to apps/web/src/assets/icons/ic-cordon.svg
diff --git a/src/assets/icons/ic-cpu.svg b/apps/web/src/assets/icons/ic-cpu.svg
similarity index 100%
rename from src/assets/icons/ic-cpu.svg
rename to apps/web/src/assets/icons/ic-cpu.svg
diff --git a/src/assets/icons/ic-cross.svg b/apps/web/src/assets/icons/ic-cross.svg
similarity index 100%
rename from src/assets/icons/ic-cross.svg
rename to apps/web/src/assets/icons/ic-cross.svg
diff --git a/src/assets/icons/ic-cube.svg b/apps/web/src/assets/icons/ic-cube.svg
similarity index 100%
rename from src/assets/icons/ic-cube.svg
rename to apps/web/src/assets/icons/ic-cube.svg
diff --git a/src/assets/icons/ic-datadog.png b/apps/web/src/assets/icons/ic-datadog.png
similarity index 100%
rename from src/assets/icons/ic-datadog.png
rename to apps/web/src/assets/icons/ic-datadog.png
diff --git a/src/assets/icons/ic-default-chart.svg b/apps/web/src/assets/icons/ic-default-chart.svg
similarity index 100%
rename from src/assets/icons/ic-default-chart.svg
rename to apps/web/src/assets/icons/ic-default-chart.svg
diff --git a/src/assets/icons/ic-delete-dots.svg b/apps/web/src/assets/icons/ic-delete-dots.svg
similarity index 100%
rename from src/assets/icons/ic-delete-dots.svg
rename to apps/web/src/assets/icons/ic-delete-dots.svg
diff --git a/src/assets/icons/ic-delete-interactive.svg b/apps/web/src/assets/icons/ic-delete-interactive.svg
similarity index 100%
rename from src/assets/icons/ic-delete-interactive.svg
rename to apps/web/src/assets/icons/ic-delete-interactive.svg
diff --git a/src/assets/icons/ic-delete.svg b/apps/web/src/assets/icons/ic-delete.svg
similarity index 100%
rename from src/assets/icons/ic-delete.svg
rename to apps/web/src/assets/icons/ic-delete.svg
diff --git a/src/assets/icons/ic-deploy.svg b/apps/web/src/assets/icons/ic-deploy.svg
similarity index 100%
rename from src/assets/icons/ic-deploy.svg
rename to apps/web/src/assets/icons/ic-deploy.svg
diff --git a/src/assets/icons/ic-devtron-app.svg b/apps/web/src/assets/icons/ic-devtron-app.svg
similarity index 100%
rename from src/assets/icons/ic-devtron-app.svg
rename to apps/web/src/assets/icons/ic-devtron-app.svg
diff --git a/src/assets/icons/ic-devtron-blue-outline.svg b/apps/web/src/assets/icons/ic-devtron-blue-outline.svg
similarity index 100%
rename from src/assets/icons/ic-devtron-blue-outline.svg
rename to apps/web/src/assets/icons/ic-devtron-blue-outline.svg
diff --git a/src/assets/icons/ic-devtron.svg b/apps/web/src/assets/icons/ic-devtron.svg
similarity index 100%
rename from src/assets/icons/ic-devtron.svg
rename to apps/web/src/assets/icons/ic-devtron.svg
diff --git a/src/assets/icons/ic-disconnected.svg b/apps/web/src/assets/icons/ic-disconnected.svg
similarity index 100%
rename from src/assets/icons/ic-disconnected.svg
rename to apps/web/src/assets/icons/ic-disconnected.svg
diff --git a/src/assets/icons/ic-discord-fill.svg b/apps/web/src/assets/icons/ic-discord-fill.svg
similarity index 100%
rename from src/assets/icons/ic-discord-fill.svg
rename to apps/web/src/assets/icons/ic-discord-fill.svg
diff --git a/src/assets/icons/ic-discord-online.svg b/apps/web/src/assets/icons/ic-discord-online.svg
similarity index 100%
rename from src/assets/icons/ic-discord-online.svg
rename to apps/web/src/assets/icons/ic-discord-online.svg
diff --git a/src/assets/icons/ic-docker-with-image.svg b/apps/web/src/assets/icons/ic-docker-with-image.svg
similarity index 100%
rename from src/assets/icons/ic-docker-with-image.svg
rename to apps/web/src/assets/icons/ic-docker-with-image.svg
diff --git a/src/assets/icons/ic-document.svg b/apps/web/src/assets/icons/ic-document.svg
similarity index 100%
rename from src/assets/icons/ic-document.svg
rename to apps/web/src/assets/icons/ic-document.svg
diff --git a/src/assets/icons/ic-down-arrow-full.svg b/apps/web/src/assets/icons/ic-down-arrow-full.svg
similarity index 100%
rename from src/assets/icons/ic-down-arrow-full.svg
rename to apps/web/src/assets/icons/ic-down-arrow-full.svg
diff --git a/src/assets/icons/ic-download.svg b/apps/web/src/assets/icons/ic-download.svg
similarity index 100%
rename from src/assets/icons/ic-download.svg
rename to apps/web/src/assets/icons/ic-download.svg
diff --git a/src/assets/icons/ic-dropdown-filled.svg b/apps/web/src/assets/icons/ic-dropdown-filled.svg
similarity index 100%
rename from src/assets/icons/ic-dropdown-filled.svg
rename to apps/web/src/assets/icons/ic-dropdown-filled.svg
diff --git a/src/assets/icons/ic-ecr.svg b/apps/web/src/assets/icons/ic-ecr.svg
similarity index 100%
rename from src/assets/icons/ic-ecr.svg
rename to apps/web/src/assets/icons/ic-ecr.svg
diff --git a/src/assets/icons/ic-edit-file.svg b/apps/web/src/assets/icons/ic-edit-file.svg
similarity index 100%
rename from src/assets/icons/ic-edit-file.svg
rename to apps/web/src/assets/icons/ic-edit-file.svg
diff --git a/src/assets/icons/ic-edit-lines.svg b/apps/web/src/assets/icons/ic-edit-lines.svg
similarity index 100%
rename from src/assets/icons/ic-edit-lines.svg
rename to apps/web/src/assets/icons/ic-edit-lines.svg
diff --git a/src/assets/icons/ic-edit.svg b/apps/web/src/assets/icons/ic-edit.svg
similarity index 100%
rename from src/assets/icons/ic-edit.svg
rename to apps/web/src/assets/icons/ic-edit.svg
diff --git a/src/assets/icons/ic-empty-data.svg b/apps/web/src/assets/icons/ic-empty-data.svg
similarity index 100%
rename from src/assets/icons/ic-empty-data.svg
rename to apps/web/src/assets/icons/ic-empty-data.svg
diff --git a/src/assets/icons/ic-env.svg b/apps/web/src/assets/icons/ic-env.svg
similarity index 100%
rename from src/assets/icons/ic-env.svg
rename to apps/web/src/assets/icons/ic-env.svg
diff --git a/src/assets/icons/ic-environment-list.svg b/apps/web/src/assets/icons/ic-environment-list.svg
similarity index 100%
rename from src/assets/icons/ic-environment-list.svg
rename to apps/web/src/assets/icons/ic-environment-list.svg
diff --git a/src/assets/icons/ic-environment-temp.svg b/apps/web/src/assets/icons/ic-environment-temp.svg
similarity index 100%
rename from src/assets/icons/ic-environment-temp.svg
rename to apps/web/src/assets/icons/ic-environment-temp.svg
diff --git a/src/assets/icons/ic-ephemeral.svg b/apps/web/src/assets/icons/ic-ephemeral.svg
similarity index 100%
rename from src/assets/icons/ic-ephemeral.svg
rename to apps/web/src/assets/icons/ic-ephemeral.svg
diff --git a/src/assets/icons/ic-error-exclamation.svg b/apps/web/src/assets/icons/ic-error-exclamation.svg
similarity index 100%
rename from src/assets/icons/ic-error-exclamation.svg
rename to apps/web/src/assets/icons/ic-error-exclamation.svg
diff --git a/src/assets/icons/ic-error-medium.svg b/apps/web/src/assets/icons/ic-error-medium.svg
similarity index 100%
rename from src/assets/icons/ic-error-medium.svg
rename to apps/web/src/assets/icons/ic-error-medium.svg
diff --git a/src/assets/icons/ic-error.svg b/apps/web/src/assets/icons/ic-error.svg
similarity index 100%
rename from src/assets/icons/ic-error.svg
rename to apps/web/src/assets/icons/ic-error.svg
diff --git a/src/assets/icons/ic-exit-fullscreen-2.svg b/apps/web/src/assets/icons/ic-exit-fullscreen-2.svg
similarity index 100%
rename from src/assets/icons/ic-exit-fullscreen-2.svg
rename to apps/web/src/assets/icons/ic-exit-fullscreen-2.svg
diff --git a/src/assets/icons/ic-expand.svg b/apps/web/src/assets/icons/ic-expand.svg
similarity index 100%
rename from src/assets/icons/ic-expand.svg
rename to apps/web/src/assets/icons/ic-expand.svg
diff --git a/src/assets/icons/ic-feedback.svg b/apps/web/src/assets/icons/ic-feedback.svg
similarity index 100%
rename from src/assets/icons/ic-feedback.svg
rename to apps/web/src/assets/icons/ic-feedback.svg
diff --git a/src/assets/icons/ic-file-code.svg b/apps/web/src/assets/icons/ic-file-code.svg
similarity index 51%
rename from src/assets/icons/ic-file-code.svg
rename to apps/web/src/assets/icons/ic-file-code.svg
index d8f0955c58..43b7c5247d 100644
--- a/src/assets/icons/ic-file-code.svg
+++ b/apps/web/src/assets/icons/ic-file-code.svg
@@ -15,6 +15,5 @@
-->
-
-
+
diff --git a/src/assets/icons/ic-file-download.svg b/apps/web/src/assets/icons/ic-file-download.svg
similarity index 100%
rename from src/assets/icons/ic-file-download.svg
rename to apps/web/src/assets/icons/ic-file-download.svg
diff --git a/apps/web/src/assets/icons/ic-file-edit.svg b/apps/web/src/assets/icons/ic-file-edit.svg
new file mode 100644
index 0000000000..0796ea0b45
--- /dev/null
+++ b/apps/web/src/assets/icons/ic-file-edit.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/apps/web/src/assets/icons/ic-file-play.svg b/apps/web/src/assets/icons/ic-file-play.svg
new file mode 100644
index 0000000000..1ca6d508ea
--- /dev/null
+++ b/apps/web/src/assets/icons/ic-file-play.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/ic-file-text.svg b/apps/web/src/assets/icons/ic-file-text.svg
similarity index 100%
rename from src/assets/icons/ic-file-text.svg
rename to apps/web/src/assets/icons/ic-file-text.svg
diff --git a/src/assets/icons/ic-file.svg b/apps/web/src/assets/icons/ic-file.svg
similarity index 100%
rename from src/assets/icons/ic-file.svg
rename to apps/web/src/assets/icons/ic-file.svg
diff --git a/src/assets/icons/ic-files.svg b/apps/web/src/assets/icons/ic-files.svg
similarity index 100%
rename from src/assets/icons/ic-files.svg
rename to apps/web/src/assets/icons/ic-files.svg
diff --git a/src/assets/icons/ic-filter.svg b/apps/web/src/assets/icons/ic-filter.svg
similarity index 100%
rename from src/assets/icons/ic-filter.svg
rename to apps/web/src/assets/icons/ic-filter.svg
diff --git a/src/assets/icons/ic-fluxcd-app.svg b/apps/web/src/assets/icons/ic-fluxcd-app.svg
similarity index 100%
rename from src/assets/icons/ic-fluxcd-app.svg
rename to apps/web/src/assets/icons/ic-fluxcd-app.svg
diff --git a/src/assets/icons/ic-fluxcd.svg b/apps/web/src/assets/icons/ic-fluxcd.svg
similarity index 100%
rename from src/assets/icons/ic-fluxcd.svg
rename to apps/web/src/assets/icons/ic-fluxcd.svg
diff --git a/src/assets/icons/ic-folder-filled.svg b/apps/web/src/assets/icons/ic-folder-filled.svg
similarity index 100%
rename from src/assets/icons/ic-folder-filled.svg
rename to apps/web/src/assets/icons/ic-folder-filled.svg
diff --git a/src/assets/icons/ic-folder.svg b/apps/web/src/assets/icons/ic-folder.svg
similarity index 100%
rename from src/assets/icons/ic-folder.svg
rename to apps/web/src/assets/icons/ic-folder.svg
diff --git a/src/assets/icons/ic-fullscreen-2.svg b/apps/web/src/assets/icons/ic-fullscreen-2.svg
similarity index 100%
rename from src/assets/icons/ic-fullscreen-2.svg
rename to apps/web/src/assets/icons/ic-fullscreen-2.svg
diff --git a/src/assets/icons/ic-generated-image.svg b/apps/web/src/assets/icons/ic-generated-image.svg
similarity index 100%
rename from src/assets/icons/ic-generated-image.svg
rename to apps/web/src/assets/icons/ic-generated-image.svg
diff --git a/src/assets/icons/ic-google-artifact-registry.svg b/apps/web/src/assets/icons/ic-google-artifact-registry.svg
similarity index 100%
rename from src/assets/icons/ic-google-artifact-registry.svg
rename to apps/web/src/assets/icons/ic-google-artifact-registry.svg
diff --git a/src/assets/icons/ic-google-container-registry.svg b/apps/web/src/assets/icons/ic-google-container-registry.svg
similarity index 100%
rename from src/assets/icons/ic-google-container-registry.svg
rename to apps/web/src/assets/icons/ic-google-container-registry.svg
diff --git a/src/assets/icons/ic-google.svg b/apps/web/src/assets/icons/ic-google.svg
similarity index 100%
rename from src/assets/icons/ic-google.svg
rename to apps/web/src/assets/icons/ic-google.svg
diff --git a/src/assets/icons/ic-grafana.png b/apps/web/src/assets/icons/ic-grafana.png
similarity index 100%
rename from src/assets/icons/ic-grafana.png
rename to apps/web/src/assets/icons/ic-grafana.png
diff --git a/src/assets/icons/ic-graph.svg b/apps/web/src/assets/icons/ic-graph.svg
similarity index 100%
rename from src/assets/icons/ic-graph.svg
rename to apps/web/src/assets/icons/ic-graph.svg
diff --git a/src/assets/icons/ic-grid-view-blue.svg b/apps/web/src/assets/icons/ic-grid-view-blue.svg
similarity index 100%
rename from src/assets/icons/ic-grid-view-blue.svg
rename to apps/web/src/assets/icons/ic-grid-view-blue.svg
diff --git a/src/assets/icons/ic-grid-view.svg b/apps/web/src/assets/icons/ic-grid-view.svg
similarity index 100%
rename from src/assets/icons/ic-grid-view.svg
rename to apps/web/src/assets/icons/ic-grid-view.svg
diff --git a/src/assets/icons/ic-group-filter-applied.svg b/apps/web/src/assets/icons/ic-group-filter-applied.svg
similarity index 100%
rename from src/assets/icons/ic-group-filter-applied.svg
rename to apps/web/src/assets/icons/ic-group-filter-applied.svg
diff --git a/src/assets/icons/ic-group-filter.svg b/apps/web/src/assets/icons/ic-group-filter.svg
similarity index 100%
rename from src/assets/icons/ic-group-filter.svg
rename to apps/web/src/assets/icons/ic-group-filter.svg
diff --git a/src/assets/icons/ic-helmchart.svg b/apps/web/src/assets/icons/ic-helmchart.svg
similarity index 100%
rename from src/assets/icons/ic-helmchart.svg
rename to apps/web/src/assets/icons/ic-helmchart.svg
diff --git a/src/assets/icons/ic-help-green.svg b/apps/web/src/assets/icons/ic-help-green.svg
similarity index 100%
rename from src/assets/icons/ic-help-green.svg
rename to apps/web/src/assets/icons/ic-help-green.svg
diff --git a/src/assets/icons/ic-help-outline.svg b/apps/web/src/assets/icons/ic-help-outline.svg
similarity index 100%
rename from src/assets/icons/ic-help-outline.svg
rename to apps/web/src/assets/icons/ic-help-outline.svg
diff --git a/src/assets/icons/ic-help.svg b/apps/web/src/assets/icons/ic-help.svg
similarity index 100%
rename from src/assets/icons/ic-help.svg
rename to apps/web/src/assets/icons/ic-help.svg
diff --git a/src/assets/icons/ic-hibernate-2.svg b/apps/web/src/assets/icons/ic-hibernate-2.svg
similarity index 100%
rename from src/assets/icons/ic-hibernate-2.svg
rename to apps/web/src/assets/icons/ic-hibernate-2.svg
diff --git a/src/assets/icons/ic-hibernate-3.svg b/apps/web/src/assets/icons/ic-hibernate-3.svg
similarity index 100%
rename from src/assets/icons/ic-hibernate-3.svg
rename to apps/web/src/assets/icons/ic-hibernate-3.svg
diff --git a/src/assets/icons/ic-hibernate.svg b/apps/web/src/assets/icons/ic-hibernate.svg
similarity index 100%
rename from src/assets/icons/ic-hibernate.svg
rename to apps/web/src/assets/icons/ic-hibernate.svg
diff --git a/src/assets/icons/ic-info-filled-purple.svg b/apps/web/src/assets/icons/ic-info-filled-purple.svg
similarity index 100%
rename from src/assets/icons/ic-info-filled-purple.svg
rename to apps/web/src/assets/icons/ic-info-filled-purple.svg
diff --git a/src/assets/icons/ic-info-filled.svg b/apps/web/src/assets/icons/ic-info-filled.svg
similarity index 100%
rename from src/assets/icons/ic-info-filled.svg
rename to apps/web/src/assets/icons/ic-info-filled.svg
diff --git a/src/assets/icons/ic-info-outline-grey.svg b/apps/web/src/assets/icons/ic-info-outline-grey.svg
similarity index 100%
rename from src/assets/icons/ic-info-outline-grey.svg
rename to apps/web/src/assets/icons/ic-info-outline-grey.svg
diff --git a/src/assets/icons/ic-info-outline-purple.svg b/apps/web/src/assets/icons/ic-info-outline-purple.svg
similarity index 100%
rename from src/assets/icons/ic-info-outline-purple.svg
rename to apps/web/src/assets/icons/ic-info-outline-purple.svg
diff --git a/src/assets/icons/ic-info-outline.svg b/apps/web/src/assets/icons/ic-info-outline.svg
similarity index 100%
rename from src/assets/icons/ic-info-outline.svg
rename to apps/web/src/assets/icons/ic-info-outline.svg
diff --git a/src/assets/icons/ic-info-outlined.svg b/apps/web/src/assets/icons/ic-info-outlined.svg
similarity index 100%
rename from src/assets/icons/ic-info-outlined.svg
rename to apps/web/src/assets/icons/ic-info-outlined.svg
diff --git a/src/assets/icons/ic-info-warn.svg b/apps/web/src/assets/icons/ic-info-warn.svg
similarity index 100%
rename from src/assets/icons/ic-info-warn.svg
rename to apps/web/src/assets/icons/ic-info-warn.svg
diff --git a/src/assets/icons/ic-input.svg b/apps/web/src/assets/icons/ic-input.svg
similarity index 100%
rename from src/assets/icons/ic-input.svg
rename to apps/web/src/assets/icons/ic-input.svg
diff --git a/src/assets/icons/ic-job-node.svg b/apps/web/src/assets/icons/ic-job-node.svg
similarity index 100%
rename from src/assets/icons/ic-job-node.svg
rename to apps/web/src/assets/icons/ic-job-node.svg
diff --git a/src/assets/icons/ic-k8s-job.svg b/apps/web/src/assets/icons/ic-k8s-job.svg
similarity index 100%
rename from src/assets/icons/ic-k8s-job.svg
rename to apps/web/src/assets/icons/ic-k8s-job.svg
diff --git a/src/assets/icons/ic-key-bulb.svg b/apps/web/src/assets/icons/ic-key-bulb.svg
similarity index 100%
rename from src/assets/icons/ic-key-bulb.svg
rename to apps/web/src/assets/icons/ic-key-bulb.svg
diff --git a/src/assets/icons/ic-key.svg b/apps/web/src/assets/icons/ic-key.svg
similarity index 100%
rename from src/assets/icons/ic-key.svg
rename to apps/web/src/assets/icons/ic-key.svg
diff --git a/src/assets/icons/ic-kibana.png b/apps/web/src/assets/icons/ic-kibana.png
similarity index 100%
rename from src/assets/icons/ic-kibana.png
rename to apps/web/src/assets/icons/ic-kibana.png
diff --git a/src/assets/icons/ic-ldap.svg b/apps/web/src/assets/icons/ic-ldap.svg
similarity index 100%
rename from src/assets/icons/ic-ldap.svg
rename to apps/web/src/assets/icons/ic-ldap.svg
diff --git a/src/assets/icons/ic-lego-block.svg b/apps/web/src/assets/icons/ic-lego-block.svg
similarity index 100%
rename from src/assets/icons/ic-lego-block.svg
rename to apps/web/src/assets/icons/ic-lego-block.svg
diff --git a/src/assets/icons/ic-lines.svg b/apps/web/src/assets/icons/ic-lines.svg
similarity index 100%
rename from src/assets/icons/ic-lines.svg
rename to apps/web/src/assets/icons/ic-lines.svg
diff --git a/src/assets/icons/ic-link.svg b/apps/web/src/assets/icons/ic-link.svg
similarity index 100%
rename from src/assets/icons/ic-link.svg
rename to apps/web/src/assets/icons/ic-link.svg
diff --git a/src/assets/icons/ic-linked.svg b/apps/web/src/assets/icons/ic-linked.svg
similarity index 100%
rename from src/assets/icons/ic-linked.svg
rename to apps/web/src/assets/icons/ic-linked.svg
diff --git a/src/assets/icons/ic-list-view.svg b/apps/web/src/assets/icons/ic-list-view.svg
similarity index 100%
rename from src/assets/icons/ic-list-view.svg
rename to apps/web/src/assets/icons/ic-list-view.svg
diff --git a/src/assets/icons/ic-locked-filled.svg b/apps/web/src/assets/icons/ic-locked-filled.svg
similarity index 100%
rename from src/assets/icons/ic-locked-filled.svg
rename to apps/web/src/assets/icons/ic-locked-filled.svg
diff --git a/src/assets/icons/ic-locked.svg b/apps/web/src/assets/icons/ic-locked.svg
similarity index 100%
rename from src/assets/icons/ic-locked.svg
rename to apps/web/src/assets/icons/ic-locked.svg
diff --git a/src/assets/icons/ic-logs.svg b/apps/web/src/assets/icons/ic-logs.svg
similarity index 100%
rename from src/assets/icons/ic-logs.svg
rename to apps/web/src/assets/icons/ic-logs.svg
diff --git a/src/assets/icons/ic-loki.png b/apps/web/src/assets/icons/ic-loki.png
similarity index 100%
rename from src/assets/icons/ic-loki.png
rename to apps/web/src/assets/icons/ic-loki.png
diff --git a/src/assets/icons/ic-mail.svg b/apps/web/src/assets/icons/ic-mail.svg
similarity index 100%
rename from src/assets/icons/ic-mail.svg
rename to apps/web/src/assets/icons/ic-mail.svg
diff --git a/src/assets/icons/ic-medium-delete.svg b/apps/web/src/assets/icons/ic-medium-delete.svg
similarity index 100%
rename from src/assets/icons/ic-medium-delete.svg
rename to apps/web/src/assets/icons/ic-medium-delete.svg
diff --git a/src/assets/icons/ic-medium-hibernate.svg b/apps/web/src/assets/icons/ic-medium-hibernate.svg
similarity index 100%
rename from src/assets/icons/ic-medium-hibernate.svg
rename to apps/web/src/assets/icons/ic-medium-hibernate.svg
diff --git a/src/assets/icons/ic-medium-unhibernate.svg b/apps/web/src/assets/icons/ic-medium-unhibernate.svg
similarity index 100%
rename from src/assets/icons/ic-medium-unhibernate.svg
rename to apps/web/src/assets/icons/ic-medium-unhibernate.svg
diff --git a/src/assets/icons/ic-megaphone.svg b/apps/web/src/assets/icons/ic-megaphone.svg
similarity index 100%
rename from src/assets/icons/ic-megaphone.svg
rename to apps/web/src/assets/icons/ic-megaphone.svg
diff --git a/src/assets/icons/ic-memory.svg b/apps/web/src/assets/icons/ic-memory.svg
similarity index 100%
rename from src/assets/icons/ic-memory.svg
rename to apps/web/src/assets/icons/ic-memory.svg
diff --git a/src/assets/icons/ic-message.svg b/apps/web/src/assets/icons/ic-message.svg
similarity index 100%
rename from src/assets/icons/ic-message.svg
rename to apps/web/src/assets/icons/ic-message.svg
diff --git a/src/assets/icons/ic-microsoft.svg b/apps/web/src/assets/icons/ic-microsoft.svg
similarity index 100%
rename from src/assets/icons/ic-microsoft.svg
rename to apps/web/src/assets/icons/ic-microsoft.svg
diff --git a/src/assets/icons/ic-minus.svg b/apps/web/src/assets/icons/ic-minus.svg
similarity index 100%
rename from src/assets/icons/ic-minus.svg
rename to apps/web/src/assets/icons/ic-minus.svg
diff --git a/src/assets/icons/ic-monitoring.svg b/apps/web/src/assets/icons/ic-monitoring.svg
similarity index 100%
rename from src/assets/icons/ic-monitoring.svg
rename to apps/web/src/assets/icons/ic-monitoring.svg
diff --git a/src/assets/icons/ic-more-option.svg b/apps/web/src/assets/icons/ic-more-option.svg
similarity index 100%
rename from src/assets/icons/ic-more-option.svg
rename to apps/web/src/assets/icons/ic-more-option.svg
diff --git a/src/assets/icons/ic-nav-applications.svg b/apps/web/src/assets/icons/ic-nav-applications.svg
similarity index 100%
rename from src/assets/icons/ic-nav-applications.svg
rename to apps/web/src/assets/icons/ic-nav-applications.svg
diff --git a/src/assets/icons/ic-nav-bug.svg b/apps/web/src/assets/icons/ic-nav-bug.svg
similarity index 100%
rename from src/assets/icons/ic-nav-bug.svg
rename to apps/web/src/assets/icons/ic-nav-bug.svg
diff --git a/src/assets/icons/ic-nav-code.svg b/apps/web/src/assets/icons/ic-nav-code.svg
similarity index 100%
rename from src/assets/icons/ic-nav-code.svg
rename to apps/web/src/assets/icons/ic-nav-code.svg
diff --git a/src/assets/icons/ic-nav-devtron.svg b/apps/web/src/assets/icons/ic-nav-devtron.svg
similarity index 100%
rename from src/assets/icons/ic-nav-devtron.svg
rename to apps/web/src/assets/icons/ic-nav-devtron.svg
diff --git a/src/assets/icons/ic-nav-gear.svg b/apps/web/src/assets/icons/ic-nav-gear.svg
similarity index 100%
rename from src/assets/icons/ic-nav-gear.svg
rename to apps/web/src/assets/icons/ic-nav-gear.svg
diff --git a/src/assets/icons/ic-nav-helm.svg b/apps/web/src/assets/icons/ic-nav-helm.svg
similarity index 100%
rename from src/assets/icons/ic-nav-helm.svg
rename to apps/web/src/assets/icons/ic-nav-helm.svg
diff --git a/apps/web/src/assets/icons/ic-nav-rocket.svg b/apps/web/src/assets/icons/ic-nav-rocket.svg
new file mode 100644
index 0000000000..5ee6b84c4e
--- /dev/null
+++ b/apps/web/src/assets/icons/ic-nav-rocket.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/assets/icons/ic-nav-search.svg b/apps/web/src/assets/icons/ic-nav-search.svg
similarity index 100%
rename from src/assets/icons/ic-nav-search.svg
rename to apps/web/src/assets/icons/ic-nav-search.svg
diff --git a/src/assets/icons/ic-nav-security.svg b/apps/web/src/assets/icons/ic-nav-security.svg
similarity index 100%
rename from src/assets/icons/ic-nav-security.svg
rename to apps/web/src/assets/icons/ic-nav-security.svg
diff --git a/src/assets/icons/ic-nav-stack.svg b/apps/web/src/assets/icons/ic-nav-stack.svg
similarity index 100%
rename from src/assets/icons/ic-nav-stack.svg
rename to apps/web/src/assets/icons/ic-nav-stack.svg
diff --git a/src/assets/icons/ic-newrelic.png b/apps/web/src/assets/icons/ic-newrelic.png
similarity index 100%
rename from src/assets/icons/ic-newrelic.png
rename to apps/web/src/assets/icons/ic-newrelic.png
diff --git a/src/assets/icons/ic-node-build-linked.svg b/apps/web/src/assets/icons/ic-node-build-linked.svg
similarity index 100%
rename from src/assets/icons/ic-node-build-linked.svg
rename to apps/web/src/assets/icons/ic-node-build-linked.svg
diff --git a/src/assets/icons/ic-note.svg b/apps/web/src/assets/icons/ic-note.svg
similarity index 100%
rename from src/assets/icons/ic-note.svg
rename to apps/web/src/assets/icons/ic-note.svg
diff --git a/src/assets/icons/ic-object.svg b/apps/web/src/assets/icons/ic-object.svg
similarity index 100%
rename from src/assets/icons/ic-object.svg
rename to apps/web/src/assets/icons/ic-object.svg
diff --git a/src/assets/icons/ic-oidc.svg b/apps/web/src/assets/icons/ic-oidc.svg
similarity index 100%
rename from src/assets/icons/ic-oidc.svg
rename to apps/web/src/assets/icons/ic-oidc.svg
diff --git a/src/assets/icons/ic-onboarding.svg b/apps/web/src/assets/icons/ic-onboarding.svg
similarity index 100%
rename from src/assets/icons/ic-onboarding.svg
rename to apps/web/src/assets/icons/ic-onboarding.svg
diff --git a/src/assets/icons/ic-open-box.svg b/apps/web/src/assets/icons/ic-open-box.svg
similarity index 100%
rename from src/assets/icons/ic-open-box.svg
rename to apps/web/src/assets/icons/ic-open-box.svg
diff --git a/src/assets/icons/ic-open-in-new.svg b/apps/web/src/assets/icons/ic-open-in-new.svg
similarity index 100%
rename from src/assets/icons/ic-open-in-new.svg
rename to apps/web/src/assets/icons/ic-open-in-new.svg
diff --git a/src/assets/icons/ic-openshift.svg b/apps/web/src/assets/icons/ic-openshift.svg
similarity index 100%
rename from src/assets/icons/ic-openshift.svg
rename to apps/web/src/assets/icons/ic-openshift.svg
diff --git a/apps/web/src/assets/icons/ic-out-of-sync.svg b/apps/web/src/assets/icons/ic-out-of-sync.svg
new file mode 100644
index 0000000000..8ea3e71733
--- /dev/null
+++ b/apps/web/src/assets/icons/ic-out-of-sync.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/ic-outline-check.svg b/apps/web/src/assets/icons/ic-outline-check.svg
similarity index 100%
rename from src/assets/icons/ic-outline-check.svg
rename to apps/web/src/assets/icons/ic-outline-check.svg
diff --git a/src/assets/icons/ic-paper-rocket.svg b/apps/web/src/assets/icons/ic-paper-rocket.svg
similarity index 100%
rename from src/assets/icons/ic-paper-rocket.svg
rename to apps/web/src/assets/icons/ic-paper-rocket.svg
diff --git a/src/assets/icons/ic-pause.svg b/apps/web/src/assets/icons/ic-pause.svg
similarity index 100%
rename from src/assets/icons/ic-pause.svg
rename to apps/web/src/assets/icons/ic-pause.svg
diff --git a/src/assets/icons/ic-pencil.svg b/apps/web/src/assets/icons/ic-pencil.svg
similarity index 100%
rename from src/assets/icons/ic-pencil.svg
rename to apps/web/src/assets/icons/ic-pencil.svg
diff --git a/src/assets/icons/ic-person.svg b/apps/web/src/assets/icons/ic-person.svg
similarity index 100%
rename from src/assets/icons/ic-person.svg
rename to apps/web/src/assets/icons/ic-person.svg
diff --git a/src/assets/icons/ic-pipeline-linked.svg b/apps/web/src/assets/icons/ic-pipeline-linked.svg
similarity index 100%
rename from src/assets/icons/ic-pipeline-linked.svg
rename to apps/web/src/assets/icons/ic-pipeline-linked.svg
diff --git a/src/assets/icons/ic-play-filled.svg b/apps/web/src/assets/icons/ic-play-filled.svg
similarity index 100%
rename from src/assets/icons/ic-play-filled.svg
rename to apps/web/src/assets/icons/ic-play-filled.svg
diff --git a/src/assets/icons/ic-play-media.svg b/apps/web/src/assets/icons/ic-play-media.svg
similarity index 100%
rename from src/assets/icons/ic-play-media.svg
rename to apps/web/src/assets/icons/ic-play-media.svg
diff --git a/src/assets/icons/ic-play-medium.svg b/apps/web/src/assets/icons/ic-play-medium.svg
similarity index 100%
rename from src/assets/icons/ic-play-medium.svg
rename to apps/web/src/assets/icons/ic-play-medium.svg
diff --git a/src/assets/icons/ic-play.svg b/apps/web/src/assets/icons/ic-play.svg
similarity index 100%
rename from src/assets/icons/ic-play.svg
rename to apps/web/src/assets/icons/ic-play.svg
diff --git a/src/assets/icons/ic-plc-chart.svg b/apps/web/src/assets/icons/ic-plc-chart.svg
similarity index 100%
rename from src/assets/icons/ic-plc-chart.svg
rename to apps/web/src/assets/icons/ic-plc-chart.svg
diff --git a/src/assets/icons/ic-plugin.svg b/apps/web/src/assets/icons/ic-plugin.svg
similarity index 100%
rename from src/assets/icons/ic-plugin.svg
rename to apps/web/src/assets/icons/ic-plugin.svg
diff --git a/src/assets/icons/ic-progressing.svg b/apps/web/src/assets/icons/ic-progressing.svg
similarity index 100%
rename from src/assets/icons/ic-progressing.svg
rename to apps/web/src/assets/icons/ic-progressing.svg
diff --git a/src/assets/icons/ic-quay.svg b/apps/web/src/assets/icons/ic-quay.svg
similarity index 100%
rename from src/assets/icons/ic-quay.svg
rename to apps/web/src/assets/icons/ic-quay.svg
diff --git a/src/assets/icons/ic-question.svg b/apps/web/src/assets/icons/ic-question.svg
similarity index 100%
rename from src/assets/icons/ic-question.svg
rename to apps/web/src/assets/icons/ic-question.svg
diff --git a/src/assets/icons/ic-resource.svg b/apps/web/src/assets/icons/ic-resource.svg
similarity index 100%
rename from src/assets/icons/ic-resource.svg
rename to apps/web/src/assets/icons/ic-resource.svg
diff --git a/src/assets/icons/ic-restore.svg b/apps/web/src/assets/icons/ic-restore.svg
similarity index 100%
rename from src/assets/icons/ic-restore.svg
rename to apps/web/src/assets/icons/ic-restore.svg
diff --git a/src/assets/icons/ic-rocket-fail.svg b/apps/web/src/assets/icons/ic-rocket-fail.svg
similarity index 100%
rename from src/assets/icons/ic-rocket-fail.svg
rename to apps/web/src/assets/icons/ic-rocket-fail.svg
diff --git a/src/assets/icons/ic-rollback.svg b/apps/web/src/assets/icons/ic-rollback.svg
similarity index 100%
rename from src/assets/icons/ic-rollback.svg
rename to apps/web/src/assets/icons/ic-rollback.svg
diff --git a/src/assets/icons/ic-sample-app.svg b/apps/web/src/assets/icons/ic-sample-app.svg
similarity index 100%
rename from src/assets/icons/ic-sample-app.svg
rename to apps/web/src/assets/icons/ic-sample-app.svg
diff --git a/src/assets/icons/ic-save.svg b/apps/web/src/assets/icons/ic-save.svg
similarity index 100%
rename from src/assets/icons/ic-save.svg
rename to apps/web/src/assets/icons/ic-save.svg
diff --git a/src/assets/icons/ic-scale-down.svg b/apps/web/src/assets/icons/ic-scale-down.svg
similarity index 100%
rename from src/assets/icons/ic-scale-down.svg
rename to apps/web/src/assets/icons/ic-scale-down.svg
diff --git a/src/assets/icons/ic-scale-objects.svg b/apps/web/src/assets/icons/ic-scale-objects.svg
similarity index 100%
rename from src/assets/icons/ic-scale-objects.svg
rename to apps/web/src/assets/icons/ic-scale-objects.svg
diff --git a/src/assets/icons/ic-search.svg b/apps/web/src/assets/icons/ic-search.svg
similarity index 100%
rename from src/assets/icons/ic-search.svg
rename to apps/web/src/assets/icons/ic-search.svg
diff --git a/src/assets/icons/ic-secure.svg b/apps/web/src/assets/icons/ic-secure.svg
similarity index 100%
rename from src/assets/icons/ic-secure.svg
rename to apps/web/src/assets/icons/ic-secure.svg
diff --git a/src/assets/icons/ic-select-cluster.svg b/apps/web/src/assets/icons/ic-select-cluster.svg
similarity index 100%
rename from src/assets/icons/ic-select-cluster.svg
rename to apps/web/src/assets/icons/ic-select-cluster.svg
diff --git a/src/assets/icons/ic-selected-corner.png b/apps/web/src/assets/icons/ic-selected-corner.png
similarity index 100%
rename from src/assets/icons/ic-selected-corner.png
rename to apps/web/src/assets/icons/ic-selected-corner.png
diff --git a/src/assets/icons/ic-settings.svg b/apps/web/src/assets/icons/ic-settings.svg
similarity index 100%
rename from src/assets/icons/ic-settings.svg
rename to apps/web/src/assets/icons/ic-settings.svg
diff --git a/src/assets/icons/ic-shield-protect-fill.svg b/apps/web/src/assets/icons/ic-shield-protect-fill.svg
similarity index 100%
rename from src/assets/icons/ic-shield-protect-fill.svg
rename to apps/web/src/assets/icons/ic-shield-protect-fill.svg
diff --git a/src/assets/icons/ic-skip.svg b/apps/web/src/assets/icons/ic-skip.svg
similarity index 100%
rename from src/assets/icons/ic-skip.svg
rename to apps/web/src/assets/icons/ic-skip.svg
diff --git a/src/assets/icons/ic-slant-bulb.svg b/apps/web/src/assets/icons/ic-slant-bulb.svg
similarity index 100%
rename from src/assets/icons/ic-slant-bulb.svg
rename to apps/web/src/assets/icons/ic-slant-bulb.svg
diff --git a/src/assets/icons/ic-smiley-party.svg b/apps/web/src/assets/icons/ic-smiley-party.svg
similarity index 100%
rename from src/assets/icons/ic-smiley-party.svg
rename to apps/web/src/assets/icons/ic-smiley-party.svg
diff --git a/src/assets/icons/ic-smtp.svg b/apps/web/src/assets/icons/ic-smtp.svg
similarity index 100%
rename from src/assets/icons/ic-smtp.svg
rename to apps/web/src/assets/icons/ic-smtp.svg
diff --git a/src/assets/icons/ic-sort-arrow-down.svg b/apps/web/src/assets/icons/ic-sort-arrow-down.svg
similarity index 100%
rename from src/assets/icons/ic-sort-arrow-down.svg
rename to apps/web/src/assets/icons/ic-sort-arrow-down.svg
diff --git a/src/assets/icons/ic-sort-arrow.svg b/apps/web/src/assets/icons/ic-sort-arrow.svg
similarity index 100%
rename from src/assets/icons/ic-sort-arrow.svg
rename to apps/web/src/assets/icons/ic-sort-arrow.svg
diff --git a/src/assets/icons/ic-sort-down.svg b/apps/web/src/assets/icons/ic-sort-down.svg
similarity index 100%
rename from src/assets/icons/ic-sort-down.svg
rename to apps/web/src/assets/icons/ic-sort-down.svg
diff --git a/src/assets/icons/ic-sort-up.svg b/apps/web/src/assets/icons/ic-sort-up.svg
similarity index 100%
rename from src/assets/icons/ic-sort-up.svg
rename to apps/web/src/assets/icons/ic-sort-up.svg
diff --git a/src/assets/icons/ic-sort.svg b/apps/web/src/assets/icons/ic-sort.svg
similarity index 100%
rename from src/assets/icons/ic-sort.svg
rename to apps/web/src/assets/icons/ic-sort.svg
diff --git a/src/assets/icons/ic-source-build.svg b/apps/web/src/assets/icons/ic-source-build.svg
similarity index 100%
rename from src/assets/icons/ic-source-build.svg
rename to apps/web/src/assets/icons/ic-source-build.svg
diff --git a/src/assets/icons/ic-source-job.svg b/apps/web/src/assets/icons/ic-source-job.svg
similarity index 100%
rename from src/assets/icons/ic-source-job.svg
rename to apps/web/src/assets/icons/ic-source-job.svg
diff --git a/src/assets/icons/ic-source-linked-build.svg b/apps/web/src/assets/icons/ic-source-linked-build.svg
similarity index 100%
rename from src/assets/icons/ic-source-linked-build.svg
rename to apps/web/src/assets/icons/ic-source-linked-build.svg
diff --git a/src/assets/icons/ic-source-webhook.svg b/apps/web/src/assets/icons/ic-source-webhook.svg
similarity index 100%
rename from src/assets/icons/ic-source-webhook.svg
rename to apps/web/src/assets/icons/ic-source-webhook.svg
diff --git a/src/assets/icons/ic-source.svg b/apps/web/src/assets/icons/ic-source.svg
similarity index 100%
rename from src/assets/icons/ic-source.svg
rename to apps/web/src/assets/icons/ic-source.svg
diff --git a/src/assets/icons/ic-sparkles.svg b/apps/web/src/assets/icons/ic-sparkles.svg
similarity index 100%
rename from src/assets/icons/ic-sparkles.svg
rename to apps/web/src/assets/icons/ic-sparkles.svg
diff --git a/src/assets/icons/ic-spraycan.svg b/apps/web/src/assets/icons/ic-spraycan.svg
similarity index 100%
rename from src/assets/icons/ic-spraycan.svg
rename to apps/web/src/assets/icons/ic-spraycan.svg
diff --git a/src/assets/icons/ic-stamp.svg b/apps/web/src/assets/icons/ic-stamp.svg
similarity index 100%
rename from src/assets/icons/ic-stamp.svg
rename to apps/web/src/assets/icons/ic-stamp.svg
diff --git a/src/assets/icons/ic-stop-filled.svg b/apps/web/src/assets/icons/ic-stop-filled.svg
similarity index 100%
rename from src/assets/icons/ic-stop-filled.svg
rename to apps/web/src/assets/icons/ic-stop-filled.svg
diff --git a/src/assets/icons/ic-stop.svg b/apps/web/src/assets/icons/ic-stop.svg
similarity index 100%
rename from src/assets/icons/ic-stop.svg
rename to apps/web/src/assets/icons/ic-stop.svg
diff --git a/src/assets/icons/ic-storage.svg b/apps/web/src/assets/icons/ic-storage.svg
similarity index 100%
rename from src/assets/icons/ic-storage.svg
rename to apps/web/src/assets/icons/ic-storage.svg
diff --git a/src/assets/icons/ic-success-outline.svg b/apps/web/src/assets/icons/ic-success-outline.svg
similarity index 100%
rename from src/assets/icons/ic-success-outline.svg
rename to apps/web/src/assets/icons/ic-success-outline.svg
diff --git a/src/assets/icons/ic-success-with-light-background.svg b/apps/web/src/assets/icons/ic-success-with-light-background.svg
similarity index 100%
rename from src/assets/icons/ic-success-with-light-background.svg
rename to apps/web/src/assets/icons/ic-success-with-light-background.svg
diff --git a/src/assets/icons/ic-success.svg b/apps/web/src/assets/icons/ic-success.svg
similarity index 100%
rename from src/assets/icons/ic-success.svg
rename to apps/web/src/assets/icons/ic-success.svg
diff --git a/src/assets/icons/ic-tag.svg b/apps/web/src/assets/icons/ic-tag.svg
similarity index 100%
rename from src/assets/icons/ic-tag.svg
rename to apps/web/src/assets/icons/ic-tag.svg
diff --git a/src/assets/icons/ic-terminal-fill.svg b/apps/web/src/assets/icons/ic-terminal-fill.svg
similarity index 100%
rename from src/assets/icons/ic-terminal-fill.svg
rename to apps/web/src/assets/icons/ic-terminal-fill.svg
diff --git a/src/assets/icons/ic-terminal-line.svg b/apps/web/src/assets/icons/ic-terminal-line.svg
similarity index 100%
rename from src/assets/icons/ic-terminal-line.svg
rename to apps/web/src/assets/icons/ic-terminal-line.svg
diff --git a/src/assets/icons/ic-tilde.svg b/apps/web/src/assets/icons/ic-tilde.svg
similarity index 100%
rename from src/assets/icons/ic-tilde.svg
rename to apps/web/src/assets/icons/ic-tilde.svg
diff --git a/src/assets/icons/ic-timeout-red.svg b/apps/web/src/assets/icons/ic-timeout-red.svg
similarity index 100%
rename from src/assets/icons/ic-timeout-red.svg
rename to apps/web/src/assets/icons/ic-timeout-red.svg
diff --git a/src/assets/icons/ic-timer.svg b/apps/web/src/assets/icons/ic-timer.svg
similarity index 100%
rename from src/assets/icons/ic-timer.svg
rename to apps/web/src/assets/icons/ic-timer.svg
diff --git a/src/assets/icons/ic-trivy-to-clair.svg b/apps/web/src/assets/icons/ic-trivy-to-clair.svg
similarity index 100%
rename from src/assets/icons/ic-trivy-to-clair.svg
rename to apps/web/src/assets/icons/ic-trivy-to-clair.svg
diff --git a/src/assets/icons/ic-trivy.svg b/apps/web/src/assets/icons/ic-trivy.svg
similarity index 100%
rename from src/assets/icons/ic-trivy.svg
rename to apps/web/src/assets/icons/ic-trivy.svg
diff --git a/src/assets/icons/ic-unhibernate.svg b/apps/web/src/assets/icons/ic-unhibernate.svg
similarity index 100%
rename from src/assets/icons/ic-unhibernate.svg
rename to apps/web/src/assets/icons/ic-unhibernate.svg
diff --git a/src/assets/icons/ic-update-animated.svg b/apps/web/src/assets/icons/ic-update-animated.svg
similarity index 100%
rename from src/assets/icons/ic-update-animated.svg
rename to apps/web/src/assets/icons/ic-update-animated.svg
diff --git a/src/assets/icons/ic-upload-blue.svg b/apps/web/src/assets/icons/ic-upload-blue.svg
similarity index 100%
rename from src/assets/icons/ic-upload-blue.svg
rename to apps/web/src/assets/icons/ic-upload-blue.svg
diff --git a/src/assets/icons/ic-upload.svg b/apps/web/src/assets/icons/ic-upload.svg
similarity index 100%
rename from src/assets/icons/ic-upload.svg
rename to apps/web/src/assets/icons/ic-upload.svg
diff --git a/src/assets/icons/ic-user-circle.svg b/apps/web/src/assets/icons/ic-user-circle.svg
similarity index 100%
rename from src/assets/icons/ic-user-circle.svg
rename to apps/web/src/assets/icons/ic-user-circle.svg
diff --git a/src/assets/icons/ic-users.svg b/apps/web/src/assets/icons/ic-users.svg
similarity index 100%
rename from src/assets/icons/ic-users.svg
rename to apps/web/src/assets/icons/ic-users.svg
diff --git a/src/assets/icons/ic-var-initial.svg b/apps/web/src/assets/icons/ic-var-initial.svg
similarity index 100%
rename from src/assets/icons/ic-var-initial.svg
rename to apps/web/src/assets/icons/ic-var-initial.svg
diff --git a/src/assets/icons/ic-variable-equal.svg b/apps/web/src/assets/icons/ic-variable-equal.svg
similarity index 100%
rename from src/assets/icons/ic-variable-equal.svg
rename to apps/web/src/assets/icons/ic-variable-equal.svg
diff --git a/src/assets/icons/ic-variable.svg b/apps/web/src/assets/icons/ic-variable.svg
similarity index 100%
rename from src/assets/icons/ic-variable.svg
rename to apps/web/src/assets/icons/ic-variable.svg
diff --git a/src/assets/icons/ic-view-variable-toggle.svg b/apps/web/src/assets/icons/ic-view-variable-toggle.svg
similarity index 100%
rename from src/assets/icons/ic-view-variable-toggle.svg
rename to apps/web/src/assets/icons/ic-view-variable-toggle.svg
diff --git a/src/assets/icons/ic-view-variables.svg b/apps/web/src/assets/icons/ic-view-variables.svg
similarity index 100%
rename from src/assets/icons/ic-view-variables.svg
rename to apps/web/src/assets/icons/ic-view-variables.svg
diff --git a/src/assets/icons/ic-virtual-cluster.svg b/apps/web/src/assets/icons/ic-virtual-cluster.svg
similarity index 100%
rename from src/assets/icons/ic-virtual-cluster.svg
rename to apps/web/src/assets/icons/ic-virtual-cluster.svg
diff --git a/apps/web/src/assets/icons/ic-visibility-off.svg b/apps/web/src/assets/icons/ic-visibility-off.svg
new file mode 100644
index 0000000000..8dab920764
--- /dev/null
+++ b/apps/web/src/assets/icons/ic-visibility-off.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/ic-visibility-on-filter.svg b/apps/web/src/assets/icons/ic-visibility-on-filter.svg
similarity index 100%
rename from src/assets/icons/ic-visibility-on-filter.svg
rename to apps/web/src/assets/icons/ic-visibility-on-filter.svg
diff --git a/src/assets/icons/ic-visibility-on.svg b/apps/web/src/assets/icons/ic-visibility-on.svg
similarity index 100%
rename from src/assets/icons/ic-visibility-on.svg
rename to apps/web/src/assets/icons/ic-visibility-on.svg
diff --git a/src/assets/icons/ic-warning-y5.svg b/apps/web/src/assets/icons/ic-warning-y5.svg
similarity index 100%
rename from src/assets/icons/ic-warning-y5.svg
rename to apps/web/src/assets/icons/ic-warning-y5.svg
diff --git a/src/assets/icons/ic-warning-y6.svg b/apps/web/src/assets/icons/ic-warning-y6.svg
similarity index 100%
rename from src/assets/icons/ic-warning-y6.svg
rename to apps/web/src/assets/icons/ic-warning-y6.svg
diff --git a/src/assets/icons/ic-warning.svg b/apps/web/src/assets/icons/ic-warning.svg
similarity index 100%
rename from src/assets/icons/ic-warning.svg
rename to apps/web/src/assets/icons/ic-warning.svg
diff --git a/src/assets/icons/ic-workflow.svg b/apps/web/src/assets/icons/ic-workflow.svg
similarity index 100%
rename from src/assets/icons/ic-workflow.svg
rename to apps/web/src/assets/icons/ic-workflow.svg
diff --git a/src/assets/icons/ic-world-black.svg b/apps/web/src/assets/icons/ic-world-black.svg
similarity index 100%
rename from src/assets/icons/ic-world-black.svg
rename to apps/web/src/assets/icons/ic-world-black.svg
diff --git a/src/assets/icons/ic-world.svg b/apps/web/src/assets/icons/ic-world.svg
similarity index 100%
rename from src/assets/icons/ic-world.svg
rename to apps/web/src/assets/icons/ic-world.svg
diff --git a/src/assets/icons/img-folder-empty.svg b/apps/web/src/assets/icons/img-folder-empty.svg
similarity index 100%
rename from src/assets/icons/img-folder-empty.svg
rename to apps/web/src/assets/icons/img-folder-empty.svg
diff --git a/apps/web/src/assets/icons/info-filled-n7.svg b/apps/web/src/assets/icons/info-filled-n7.svg
new file mode 100644
index 0000000000..08aee14175
--- /dev/null
+++ b/apps/web/src/assets/icons/info-filled-n7.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/src/assets/icons/info-filled.svg b/apps/web/src/assets/icons/info-filled.svg
similarity index 100%
rename from src/assets/icons/info-filled.svg
rename to apps/web/src/assets/icons/info-filled.svg
diff --git a/src/assets/icons/inject-tag.svg b/apps/web/src/assets/icons/inject-tag.svg
similarity index 100%
rename from src/assets/icons/inject-tag.svg
rename to apps/web/src/assets/icons/inject-tag.svg
diff --git a/src/assets/icons/logo/logo-dt.svg b/apps/web/src/assets/icons/logo/logo-dt.svg
similarity index 100%
rename from src/assets/icons/logo/logo-dt.svg
rename to apps/web/src/assets/icons/logo/logo-dt.svg
diff --git a/src/assets/icons/mdeditor/ic-bold.svg b/apps/web/src/assets/icons/mdeditor/ic-bold.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-bold.svg
rename to apps/web/src/assets/icons/mdeditor/ic-bold.svg
diff --git a/src/assets/icons/mdeditor/ic-checked-list.svg b/apps/web/src/assets/icons/mdeditor/ic-checked-list.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-checked-list.svg
rename to apps/web/src/assets/icons/mdeditor/ic-checked-list.svg
diff --git a/src/assets/icons/mdeditor/ic-code.svg b/apps/web/src/assets/icons/mdeditor/ic-code.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-code.svg
rename to apps/web/src/assets/icons/mdeditor/ic-code.svg
diff --git a/src/assets/icons/mdeditor/ic-header.svg b/apps/web/src/assets/icons/mdeditor/ic-header.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-header.svg
rename to apps/web/src/assets/icons/mdeditor/ic-header.svg
diff --git a/src/assets/icons/mdeditor/ic-image.svg b/apps/web/src/assets/icons/mdeditor/ic-image.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-image.svg
rename to apps/web/src/assets/icons/mdeditor/ic-image.svg
diff --git a/src/assets/icons/mdeditor/ic-italic.svg b/apps/web/src/assets/icons/mdeditor/ic-italic.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-italic.svg
rename to apps/web/src/assets/icons/mdeditor/ic-italic.svg
diff --git a/src/assets/icons/mdeditor/ic-link.svg b/apps/web/src/assets/icons/mdeditor/ic-link.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-link.svg
rename to apps/web/src/assets/icons/mdeditor/ic-link.svg
diff --git a/src/assets/icons/mdeditor/ic-ordered-list.svg b/apps/web/src/assets/icons/mdeditor/ic-ordered-list.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-ordered-list.svg
rename to apps/web/src/assets/icons/mdeditor/ic-ordered-list.svg
diff --git a/src/assets/icons/mdeditor/ic-quote.svg b/apps/web/src/assets/icons/mdeditor/ic-quote.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-quote.svg
rename to apps/web/src/assets/icons/mdeditor/ic-quote.svg
diff --git a/src/assets/icons/mdeditor/ic-strikethrough.svg b/apps/web/src/assets/icons/mdeditor/ic-strikethrough.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-strikethrough.svg
rename to apps/web/src/assets/icons/mdeditor/ic-strikethrough.svg
diff --git a/src/assets/icons/mdeditor/ic-unordered-list.svg b/apps/web/src/assets/icons/mdeditor/ic-unordered-list.svg
similarity index 100%
rename from src/assets/icons/mdeditor/ic-unordered-list.svg
rename to apps/web/src/assets/icons/mdeditor/ic-unordered-list.svg
diff --git a/src/assets/icons/misc/addWhite.svg b/apps/web/src/assets/icons/misc/addWhite.svg
similarity index 100%
rename from src/assets/icons/misc/addWhite.svg
rename to apps/web/src/assets/icons/misc/addWhite.svg
diff --git a/src/assets/icons/misc/arrow-chevron-down-black.svg b/apps/web/src/assets/icons/misc/arrow-chevron-down-black.svg
similarity index 100%
rename from src/assets/icons/misc/arrow-chevron-down-black.svg
rename to apps/web/src/assets/icons/misc/arrow-chevron-down-black.svg
diff --git a/src/assets/icons/misc/arrow-solid-right.svg b/apps/web/src/assets/icons/misc/arrow-solid-right.svg
similarity index 100%
rename from src/assets/icons/misc/arrow-solid-right.svg
rename to apps/web/src/assets/icons/misc/arrow-solid-right.svg
diff --git a/src/assets/icons/misc/arrowSquareOut.svg b/apps/web/src/assets/icons/misc/arrowSquareOut.svg
similarity index 100%
rename from src/assets/icons/misc/arrowSquareOut.svg
rename to apps/web/src/assets/icons/misc/arrowSquareOut.svg
diff --git a/src/assets/icons/misc/branch.svg b/apps/web/src/assets/icons/misc/branch.svg
similarity index 100%
rename from src/assets/icons/misc/branch.svg
rename to apps/web/src/assets/icons/misc/branch.svg
diff --git a/src/assets/icons/misc/branchBlue.svg b/apps/web/src/assets/icons/misc/branchBlue.svg
similarity index 100%
rename from src/assets/icons/misc/branchBlue.svg
rename to apps/web/src/assets/icons/misc/branchBlue.svg
diff --git a/src/assets/icons/misc/checkGreen.svg b/apps/web/src/assets/icons/misc/checkGreen.svg
similarity index 100%
rename from src/assets/icons/misc/checkGreen.svg
rename to apps/web/src/assets/icons/misc/checkGreen.svg
diff --git a/src/assets/icons/misc/delete.svg b/apps/web/src/assets/icons/misc/delete.svg
similarity index 100%
rename from src/assets/icons/misc/delete.svg
rename to apps/web/src/assets/icons/misc/delete.svg
diff --git a/src/assets/icons/misc/deploy.svg b/apps/web/src/assets/icons/misc/deploy.svg
similarity index 100%
rename from src/assets/icons/misc/deploy.svg
rename to apps/web/src/assets/icons/misc/deploy.svg
diff --git a/src/assets/icons/misc/docker.svg b/apps/web/src/assets/icons/misc/docker.svg
similarity index 100%
rename from src/assets/icons/misc/docker.svg
rename to apps/web/src/assets/icons/misc/docker.svg
diff --git a/src/assets/icons/misc/editBlack.svg b/apps/web/src/assets/icons/misc/editBlack.svg
similarity index 100%
rename from src/assets/icons/misc/editBlack.svg
rename to apps/web/src/assets/icons/misc/editBlack.svg
diff --git a/src/assets/icons/misc/editWhite.svg b/apps/web/src/assets/icons/misc/editWhite.svg
similarity index 100%
rename from src/assets/icons/misc/editWhite.svg
rename to apps/web/src/assets/icons/misc/editWhite.svg
diff --git a/src/assets/icons/misc/errorInfo.svg b/apps/web/src/assets/icons/misc/errorInfo.svg
similarity index 100%
rename from src/assets/icons/misc/errorInfo.svg
rename to apps/web/src/assets/icons/misc/errorInfo.svg
diff --git a/src/assets/icons/misc/regex.svg b/apps/web/src/assets/icons/misc/regex.svg
similarity index 100%
rename from src/assets/icons/misc/regex.svg
rename to apps/web/src/assets/icons/misc/regex.svg
diff --git a/src/assets/icons/misc/rollback.svg b/apps/web/src/assets/icons/misc/rollback.svg
similarity index 100%
rename from src/assets/icons/misc/rollback.svg
rename to apps/web/src/assets/icons/misc/rollback.svg
diff --git a/src/assets/icons/misc/sort-down.svg b/apps/web/src/assets/icons/misc/sort-down.svg
similarity index 100%
rename from src/assets/icons/misc/sort-down.svg
rename to apps/web/src/assets/icons/misc/sort-down.svg
diff --git a/src/assets/icons/misc/sort-up.svg b/apps/web/src/assets/icons/misc/sort-up.svg
similarity index 100%
rename from src/assets/icons/misc/sort-up.svg
rename to apps/web/src/assets/icons/misc/sort-up.svg
diff --git a/src/assets/icons/misc/sort.svg b/apps/web/src/assets/icons/misc/sort.svg
similarity index 100%
rename from src/assets/icons/misc/sort.svg
rename to apps/web/src/assets/icons/misc/sort.svg
diff --git a/src/assets/icons/misc/webhook.svg b/apps/web/src/assets/icons/misc/webhook.svg
similarity index 100%
rename from src/assets/icons/misc/webhook.svg
rename to apps/web/src/assets/icons/misc/webhook.svg
diff --git a/src/assets/icons/navigation-sprite.svg b/apps/web/src/assets/icons/navigation-sprite.svg
similarity index 100%
rename from src/assets/icons/navigation-sprite.svg
rename to apps/web/src/assets/icons/navigation-sprite.svg
diff --git a/src/assets/icons/scanner.svg b/apps/web/src/assets/icons/scanner.svg
similarity index 100%
rename from src/assets/icons/scanner.svg
rename to apps/web/src/assets/icons/scanner.svg
diff --git a/src/assets/icons/tools/ic-link-alerts.png b/apps/web/src/assets/icons/tools/ic-link-alerts.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-alerts.png
rename to apps/web/src/assets/icons/tools/ic-link-alerts.png
diff --git a/src/assets/icons/tools/ic-link-bugs.png b/apps/web/src/assets/icons/tools/ic-link-bugs.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-bugs.png
rename to apps/web/src/assets/icons/tools/ic-link-bugs.png
diff --git a/src/assets/icons/tools/ic-link-chat.png b/apps/web/src/assets/icons/tools/ic-link-chat.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-chat.png
rename to apps/web/src/assets/icons/tools/ic-link-chat.png
diff --git a/src/assets/icons/tools/ic-link-confluence.png b/apps/web/src/assets/icons/tools/ic-link-confluence.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-confluence.png
rename to apps/web/src/assets/icons/tools/ic-link-confluence.png
diff --git a/src/assets/icons/tools/ic-link-document.png b/apps/web/src/assets/icons/tools/ic-link-document.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-document.png
rename to apps/web/src/assets/icons/tools/ic-link-document.png
diff --git a/src/assets/icons/tools/ic-link-folder.png b/apps/web/src/assets/icons/tools/ic-link-folder.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-folder.png
rename to apps/web/src/assets/icons/tools/ic-link-folder.png
diff --git a/src/assets/icons/tools/ic-link-jira.png b/apps/web/src/assets/icons/tools/ic-link-jira.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-jira.png
rename to apps/web/src/assets/icons/tools/ic-link-jira.png
diff --git a/src/assets/icons/tools/ic-link-performance.png b/apps/web/src/assets/icons/tools/ic-link-performance.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-performance.png
rename to apps/web/src/assets/icons/tools/ic-link-performance.png
diff --git a/src/assets/icons/tools/ic-link-report.png b/apps/web/src/assets/icons/tools/ic-link-report.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-report.png
rename to apps/web/src/assets/icons/tools/ic-link-report.png
diff --git a/src/assets/icons/tools/ic-link-swagger.png b/apps/web/src/assets/icons/tools/ic-link-swagger.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-swagger.png
rename to apps/web/src/assets/icons/tools/ic-link-swagger.png
diff --git a/src/assets/icons/tools/ic-link-webpage.png b/apps/web/src/assets/icons/tools/ic-link-webpage.png
similarity index 100%
rename from src/assets/icons/tools/ic-link-webpage.png
rename to apps/web/src/assets/icons/tools/ic-link-webpage.png
diff --git a/src/assets/img/Empty-folder.png b/apps/web/src/assets/img/Empty-folder.png
similarity index 100%
rename from src/assets/img/Empty-folder.png
rename to apps/web/src/assets/img/Empty-folder.png
diff --git a/src/assets/img/about-devtron@2x.png b/apps/web/src/assets/img/about-devtron@2x.png
similarity index 100%
rename from src/assets/img/about-devtron@2x.png
rename to apps/web/src/assets/img/about-devtron@2x.png
diff --git a/src/assets/img/app-not-configured.png b/apps/web/src/assets/img/app-not-configured.png
similarity index 100%
rename from src/assets/img/app-not-configured.png
rename to apps/web/src/assets/img/app-not-configured.png
diff --git a/src/assets/img/app-not-deployed.png b/apps/web/src/assets/img/app-not-deployed.png
similarity index 100%
rename from src/assets/img/app-not-deployed.png
rename to apps/web/src/assets/img/app-not-deployed.png
diff --git a/src/assets/img/bg-login.png b/apps/web/src/assets/img/bg-login.png
similarity index 100%
rename from src/assets/img/bg-login.png
rename to apps/web/src/assets/img/bg-login.png
diff --git a/src/assets/img/bug_fixing.svg b/apps/web/src/assets/img/bug_fixing.svg
similarity index 100%
rename from src/assets/img/bug_fixing.svg
rename to apps/web/src/assets/img/bug_fixing.svg
diff --git a/src/assets/img/change-source.png b/apps/web/src/assets/img/change-source.png
similarity index 100%
rename from src/assets/img/change-source.png
rename to apps/web/src/assets/img/change-source.png
diff --git a/src/assets/img/cm-cs-empty-state.png b/apps/web/src/assets/img/cm-cs-empty-state.png
similarity index 100%
rename from src/assets/img/cm-cs-empty-state.png
rename to apps/web/src/assets/img/cm-cs-empty-state.png
diff --git a/src/assets/img/empty-applist@2x.png b/apps/web/src/assets/img/empty-applist@2x.png
similarity index 100%
rename from src/assets/img/empty-applist@2x.png
rename to apps/web/src/assets/img/empty-applist@2x.png
diff --git a/src/assets/img/empty-externallinks@2x.png b/apps/web/src/assets/img/empty-externallinks@2x.png
similarity index 100%
rename from src/assets/img/empty-externallinks@2x.png
rename to apps/web/src/assets/img/empty-externallinks@2x.png
diff --git a/src/assets/img/empty-joblist@2x.png b/apps/web/src/assets/img/empty-joblist@2x.png
similarity index 100%
rename from src/assets/img/empty-joblist@2x.png
rename to apps/web/src/assets/img/empty-joblist@2x.png
diff --git a/src/assets/img/empty-noresult@2x.png b/apps/web/src/assets/img/empty-noresult@2x.png
similarity index 100%
rename from src/assets/img/empty-noresult@2x.png
rename to apps/web/src/assets/img/empty-noresult@2x.png
diff --git a/src/assets/img/empty-pre-deploy.png b/apps/web/src/assets/img/empty-pre-deploy.png
similarity index 100%
rename from src/assets/img/empty-pre-deploy.png
rename to apps/web/src/assets/img/empty-pre-deploy.png
diff --git a/src/assets/img/external-ci.png b/apps/web/src/assets/img/external-ci.png
similarity index 100%
rename from src/assets/img/external-ci.png
rename to apps/web/src/assets/img/external-ci.png
diff --git a/src/assets/img/guide-create-app.png b/apps/web/src/assets/img/guide-create-app.png
similarity index 100%
rename from src/assets/img/guide-create-app.png
rename to apps/web/src/assets/img/guide-create-app.png
diff --git a/src/assets/img/guide-onboard.png b/apps/web/src/assets/img/guide-onboard.png
similarity index 100%
rename from src/assets/img/guide-onboard.png
rename to apps/web/src/assets/img/guide-onboard.png
diff --git a/src/assets/img/guided-chart-repository.png b/apps/web/src/assets/img/guided-chart-repository.png
similarity index 100%
rename from src/assets/img/guided-chart-repository.png
rename to apps/web/src/assets/img/guided-chart-repository.png
diff --git a/src/assets/img/guided-helm-cluster.png b/apps/web/src/assets/img/guided-helm-cluster.png
similarity index 100%
rename from src/assets/img/guided-helm-cluster.png
rename to apps/web/src/assets/img/guided-helm-cluster.png
diff --git a/src/assets/img/guided-helm-collage.png b/apps/web/src/assets/img/guided-helm-collage.png
similarity index 100%
rename from src/assets/img/guided-helm-collage.png
rename to apps/web/src/assets/img/guided-helm-collage.png
diff --git a/src/assets/img/guided-helm-search.png b/apps/web/src/assets/img/guided-helm-search.png
similarity index 100%
rename from src/assets/img/guided-helm-search.png
rename to apps/web/src/assets/img/guided-helm-search.png
diff --git a/src/assets/img/helm-collage.png b/apps/web/src/assets/img/helm-collage.png
similarity index 100%
rename from src/assets/img/helm-collage.png
rename to apps/web/src/assets/img/helm-collage.png
diff --git a/src/assets/img/ic-build-deploy.png b/apps/web/src/assets/img/ic-build-deploy.png
similarity index 100%
rename from src/assets/img/ic-build-deploy.png
rename to apps/web/src/assets/img/ic-build-deploy.png
diff --git a/src/assets/img/ic-checklist-app@2x.png b/apps/web/src/assets/img/ic-checklist-app@2x.png
similarity index 100%
rename from src/assets/img/ic-checklist-app@2x.png
rename to apps/web/src/assets/img/ic-checklist-app@2x.png
diff --git a/src/assets/img/ic-checklist-chart@2x.png b/apps/web/src/assets/img/ic-checklist-chart@2x.png
similarity index 100%
rename from src/assets/img/ic-checklist-chart@2x.png
rename to apps/web/src/assets/img/ic-checklist-chart@2x.png
diff --git a/src/assets/img/ic-checklist-sample-app@2x.png b/apps/web/src/assets/img/ic-checklist-sample-app@2x.png
similarity index 100%
rename from src/assets/img/ic-checklist-sample-app@2x.png
rename to apps/web/src/assets/img/ic-checklist-sample-app@2x.png
diff --git a/src/assets/img/ic-dockerfile-in-use.png b/apps/web/src/assets/img/ic-dockerfile-in-use.png
similarity index 100%
rename from src/assets/img/ic-dockerfile-in-use.png
rename to apps/web/src/assets/img/ic-dockerfile-in-use.png
diff --git a/src/assets/img/ic-empty-chartgroup@2x.png b/apps/web/src/assets/img/ic-empty-chartgroup@2x.png
similarity index 100%
rename from src/assets/img/ic-empty-chartgroup@2x.png
rename to apps/web/src/assets/img/ic-empty-chartgroup@2x.png
diff --git a/src/assets/img/ic-empty-checklist.png b/apps/web/src/assets/img/ic-empty-checklist.png
similarity index 100%
rename from src/assets/img/ic-empty-checklist.png
rename to apps/web/src/assets/img/ic-empty-checklist.png
diff --git a/src/assets/img/ic-empty-custom-charts.png b/apps/web/src/assets/img/ic-empty-custom-charts.png
similarity index 100%
rename from src/assets/img/ic-empty-custom-charts.png
rename to apps/web/src/assets/img/ic-empty-custom-charts.png
diff --git a/src/assets/img/ic-empty-dep-metrics@2x.png b/apps/web/src/assets/img/ic-empty-dep-metrics@2x.png
similarity index 100%
rename from src/assets/img/ic-empty-dep-metrics@2x.png
rename to apps/web/src/assets/img/ic-empty-dep-metrics@2x.png
diff --git a/src/assets/img/ic-empty-done@2x.png b/apps/web/src/assets/img/ic-empty-done@2x.png
similarity index 100%
rename from src/assets/img/ic-empty-done@2x.png
rename to apps/web/src/assets/img/ic-empty-done@2x.png
diff --git a/src/assets/img/ic-empty-ea--security.png b/apps/web/src/assets/img/ic-empty-ea--security.png
similarity index 100%
rename from src/assets/img/ic-empty-ea--security.png
rename to apps/web/src/assets/img/ic-empty-ea--security.png
diff --git a/src/assets/img/ic-empty-ea-app-detail.png b/apps/web/src/assets/img/ic-empty-ea-app-detail.png
similarity index 100%
rename from src/assets/img/ic-empty-ea-app-detail.png
rename to apps/web/src/assets/img/ic-empty-ea-app-detail.png
diff --git a/src/assets/img/ic-empty-ea-charts.png b/apps/web/src/assets/img/ic-empty-ea-charts.png
similarity index 100%
rename from src/assets/img/ic-empty-ea-charts.png
rename to apps/web/src/assets/img/ic-empty-ea-charts.png
diff --git a/src/assets/img/ic-empty-error@2x.png b/apps/web/src/assets/img/ic-empty-error@2x.png
similarity index 100%
rename from src/assets/img/ic-empty-error@2x.png
rename to apps/web/src/assets/img/ic-empty-error@2x.png
diff --git a/src/assets/img/ic-empty-generate-token.png b/apps/web/src/assets/img/ic-empty-generate-token.png
similarity index 100%
rename from src/assets/img/ic-empty-generate-token.png
rename to apps/web/src/assets/img/ic-empty-generate-token.png
diff --git a/src/assets/img/ic-empty-notifications.png b/apps/web/src/assets/img/ic-empty-notifications.png
similarity index 100%
rename from src/assets/img/ic-empty-notifications.png
rename to apps/web/src/assets/img/ic-empty-notifications.png
diff --git a/src/assets/img/ic-empty-scanner-disabled.png b/apps/web/src/assets/img/ic-empty-scanner-disabled.png
similarity index 100%
rename from src/assets/img/ic-empty-scanner-disabled.png
rename to apps/web/src/assets/img/ic-empty-scanner-disabled.png
diff --git a/src/assets/img/ic-empty-tests.svg b/apps/web/src/assets/img/ic-empty-tests.svg
similarity index 100%
rename from src/assets/img/ic-empty-tests.svg
rename to apps/web/src/assets/img/ic-empty-tests.svg
diff --git a/src/assets/img/ic-empty-workflow@3x.png b/apps/web/src/assets/img/ic-empty-workflow@3x.png
similarity index 100%
rename from src/assets/img/ic-empty-workflow@3x.png
rename to apps/web/src/assets/img/ic-empty-workflow@3x.png
diff --git a/src/assets/img/ic-error-hosturl.png b/apps/web/src/assets/img/ic-error-hosturl.png
similarity index 100%
rename from src/assets/img/ic-error-hosturl.png
rename to apps/web/src/assets/img/ic-error-hosturl.png
diff --git a/src/assets/img/ic-error-prometheus.png b/apps/web/src/assets/img/ic-error-prometheus.png
similarity index 100%
rename from src/assets/img/ic-error-prometheus.png
rename to apps/web/src/assets/img/ic-error-prometheus.png
diff --git a/src/assets/img/ic-feature-deploymentgroups@3x.png b/apps/web/src/assets/img/ic-feature-deploymentgroups@3x.png
similarity index 100%
rename from src/assets/img/ic-feature-deploymentgroups@3x.png
rename to apps/web/src/assets/img/ic-feature-deploymentgroups@3x.png
diff --git a/src/assets/img/ic-help-outline.svg b/apps/web/src/assets/img/ic-help-outline.svg
similarity index 100%
rename from src/assets/img/ic-help-outline.svg
rename to apps/web/src/assets/img/ic-help-outline.svg
diff --git a/src/assets/img/ic-loading-failure.png b/apps/web/src/assets/img/ic-loading-failure.png
similarity index 100%
rename from src/assets/img/ic-loading-failure.png
rename to apps/web/src/assets/img/ic-loading-failure.png
diff --git a/src/assets/img/ic-mechanical-operation.svg b/apps/web/src/assets/img/ic-mechanical-operation.svg
similarity index 100%
rename from src/assets/img/ic-mechanical-operation.svg
rename to apps/web/src/assets/img/ic-mechanical-operation.svg
diff --git a/src/assets/img/ic-more-extensions.png b/apps/web/src/assets/img/ic-more-extensions.png
similarity index 100%
rename from src/assets/img/ic-more-extensions.png
rename to apps/web/src/assets/img/ic-more-extensions.png
diff --git a/src/assets/img/ic-no-chart-in-clusters@2x.png b/apps/web/src/assets/img/ic-no-chart-in-clusters@2x.png
similarity index 100%
rename from src/assets/img/ic-no-chart-in-clusters@2x.png
rename to apps/web/src/assets/img/ic-no-chart-in-clusters@2x.png
diff --git a/src/assets/img/ic-no-cluster-select@2x.png b/apps/web/src/assets/img/ic-no-cluster-select@2x.png
similarity index 100%
rename from src/assets/img/ic-no-cluster-select@2x.png
rename to apps/web/src/assets/img/ic-no-cluster-select@2x.png
diff --git a/src/assets/img/ic-not-authorized.svg b/apps/web/src/assets/img/ic-not-authorized.svg
similarity index 100%
rename from src/assets/img/ic-not-authorized.svg
rename to apps/web/src/assets/img/ic-not-authorized.svg
diff --git a/src/assets/img/ic-pipeline-ci@2x.png b/apps/web/src/assets/img/ic-pipeline-ci@2x.png
similarity index 100%
rename from src/assets/img/ic-pipeline-ci@2x.png
rename to apps/web/src/assets/img/ic-pipeline-ci@2x.png
diff --git a/src/assets/img/ic-pipeline-deploy@2x.png b/apps/web/src/assets/img/ic-pipeline-deploy@2x.png
similarity index 100%
rename from src/assets/img/ic-pipeline-deploy@2x.png
rename to apps/web/src/assets/img/ic-pipeline-deploy@2x.png
diff --git a/src/assets/img/ic-preview.png b/apps/web/src/assets/img/ic-preview.png
similarity index 100%
rename from src/assets/img/ic-preview.png
rename to apps/web/src/assets/img/ic-preview.png
diff --git a/src/assets/img/ic-success@2x.png b/apps/web/src/assets/img/ic-success@2x.png
similarity index 100%
rename from src/assets/img/ic-success@2x.png
rename to apps/web/src/assets/img/ic-success@2x.png
diff --git a/src/assets/img/ic-vulnerability-not-found.svg b/apps/web/src/assets/img/ic-vulnerability-not-found.svg
similarity index 100%
rename from src/assets/img/ic-vulnerability-not-found.svg
rename to apps/web/src/assets/img/ic-vulnerability-not-found.svg
diff --git a/src/assets/img/ic_upload_chart_error.png b/apps/web/src/assets/img/ic_upload_chart_error.png
similarity index 100%
rename from src/assets/img/ic_upload_chart_error.png
rename to apps/web/src/assets/img/ic_upload_chart_error.png
diff --git a/src/assets/img/install-devtron-full.png b/apps/web/src/assets/img/install-devtron-full.png
similarity index 100%
rename from src/assets/img/install-devtron-full.png
rename to apps/web/src/assets/img/install-devtron-full.png
diff --git a/src/assets/img/install-devtron-full@2x.png b/apps/web/src/assets/img/install-devtron-full@2x.png
similarity index 100%
rename from src/assets/img/install-devtron-full@2x.png
rename to apps/web/src/assets/img/install-devtron-full@2x.png
diff --git a/src/assets/img/install-devtron-full@3x.png b/apps/web/src/assets/img/install-devtron-full@3x.png
similarity index 100%
rename from src/assets/img/install-devtron-full@3x.png
rename to apps/web/src/assets/img/install-devtron-full@3x.png
diff --git a/src/assets/img/lifebuoy.png b/apps/web/src/assets/img/lifebuoy.png
similarity index 100%
rename from src/assets/img/lifebuoy.png
rename to apps/web/src/assets/img/lifebuoy.png
diff --git a/src/assets/img/linked-cd-bulk-ci.png b/apps/web/src/assets/img/linked-cd-bulk-ci.png
similarity index 100%
rename from src/assets/img/linked-cd-bulk-ci.png
rename to apps/web/src/assets/img/linked-cd-bulk-ci.png
diff --git a/src/assets/img/linked-ci.png b/apps/web/src/assets/img/linked-ci.png
similarity index 100%
rename from src/assets/img/linked-ci.png
rename to apps/web/src/assets/img/linked-ci.png
diff --git a/src/assets/img/login-bg-independence.png b/apps/web/src/assets/img/login-bg-independence.png
similarity index 100%
rename from src/assets/img/login-bg-independence.png
rename to apps/web/src/assets/img/login-bg-independence.png
diff --git a/src/assets/img/mail-icon.svg b/apps/web/src/assets/img/mail-icon.svg
similarity index 100%
rename from src/assets/img/mail-icon.svg
rename to apps/web/src/assets/img/mail-icon.svg
diff --git a/src/assets/img/nav-logo.svg b/apps/web/src/assets/img/nav-logo.svg
similarity index 100%
rename from src/assets/img/nav-logo.svg
rename to apps/web/src/assets/img/nav-logo.svg
diff --git a/src/assets/img/no-approved-images@2x.png b/apps/web/src/assets/img/no-approved-images@2x.png
similarity index 100%
rename from src/assets/img/no-approved-images@2x.png
rename to apps/web/src/assets/img/no-approved-images@2x.png
diff --git a/src/assets/img/no-artifact@2x.png b/apps/web/src/assets/img/no-artifact@2x.png
similarity index 100%
rename from src/assets/img/no-artifact@2x.png
rename to apps/web/src/assets/img/no-artifact@2x.png
diff --git a/src/assets/img/no-cluster-empty-state.png b/apps/web/src/assets/img/no-cluster-empty-state.png
similarity index 100%
rename from src/assets/img/no-cluster-empty-state.png
rename to apps/web/src/assets/img/no-cluster-empty-state.png
diff --git a/src/assets/img/no-eligible-commit.png b/apps/web/src/assets/img/no-eligible-commit.png
similarity index 100%
rename from src/assets/img/no-eligible-commit.png
rename to apps/web/src/assets/img/no-eligible-commit.png
diff --git a/apps/web/src/assets/img/no-offending-pipeline.svg b/apps/web/src/assets/img/no-offending-pipeline.svg
new file mode 100644
index 0000000000..7888fa3d52
--- /dev/null
+++ b/apps/web/src/assets/img/no-offending-pipeline.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/img/no-pending-action@2x.png b/apps/web/src/assets/img/no-pending-action@2x.png
similarity index 100%
rename from src/assets/img/no-pending-action@2x.png
rename to apps/web/src/assets/img/no-pending-action@2x.png
diff --git a/src/assets/img/no_vulnerability.png b/apps/web/src/assets/img/no_vulnerability.png
similarity index 100%
rename from src/assets/img/no_vulnerability.png
rename to apps/web/src/assets/img/no_vulnerability.png
diff --git a/src/assets/img/node-app-thumbnail.png b/apps/web/src/assets/img/node-app-thumbnail.png
similarity index 100%
rename from src/assets/img/node-app-thumbnail.png
rename to apps/web/src/assets/img/node-app-thumbnail.png
diff --git a/src/assets/img/not-scanned.png b/apps/web/src/assets/img/not-scanned.png
similarity index 100%
rename from src/assets/img/not-scanned.png
rename to apps/web/src/assets/img/not-scanned.png
diff --git a/src/assets/img/page-not-found.png b/apps/web/src/assets/img/page-not-found.png
similarity index 100%
rename from src/assets/img/page-not-found.png
rename to apps/web/src/assets/img/page-not-found.png
diff --git a/src/assets/img/paper-rocket-deployment.png b/apps/web/src/assets/img/paper-rocket-deployment.png
similarity index 100%
rename from src/assets/img/paper-rocket-deployment.png
rename to apps/web/src/assets/img/paper-rocket-deployment.png
diff --git a/src/assets/img/pipeline-deploy.png b/apps/web/src/assets/img/pipeline-deploy.png
similarity index 100%
rename from src/assets/img/pipeline-deploy.png
rename to apps/web/src/assets/img/pipeline-deploy.png
diff --git a/src/assets/img/post-build-empty.png b/apps/web/src/assets/img/post-build-empty.png
similarity index 100%
rename from src/assets/img/post-build-empty.png
rename to apps/web/src/assets/img/post-build-empty.png
diff --git a/src/assets/img/post-deployment-empty.png b/apps/web/src/assets/img/post-deployment-empty.png
similarity index 100%
rename from src/assets/img/post-deployment-empty.png
rename to apps/web/src/assets/img/post-deployment-empty.png
diff --git a/src/assets/img/pre-build-empty.png b/apps/web/src/assets/img/pre-build-empty.png
similarity index 100%
rename from src/assets/img/pre-build-empty.png
rename to apps/web/src/assets/img/pre-build-empty.png
diff --git a/src/assets/img/pre-deployment-empty.png b/apps/web/src/assets/img/pre-deployment-empty.png
similarity index 100%
rename from src/assets/img/pre-deployment-empty.png
rename to apps/web/src/assets/img/pre-deployment-empty.png
diff --git a/src/assets/img/select-image-source.png b/apps/web/src/assets/img/select-image-source.png
similarity index 100%
rename from src/assets/img/select-image-source.png
rename to apps/web/src/assets/img/select-image-source.png
diff --git a/src/assets/img/slack-logo.svg b/apps/web/src/assets/img/slack-logo.svg
similarity index 100%
rename from src/assets/img/slack-logo.svg
rename to apps/web/src/assets/img/slack-logo.svg
diff --git a/src/assets/img/terminal.png b/apps/web/src/assets/img/terminal.png
similarity index 100%
rename from src/assets/img/terminal.png
rename to apps/web/src/assets/img/terminal.png
diff --git a/src/assets/img/terminal@2x.png b/apps/web/src/assets/img/terminal@2x.png
similarity index 100%
rename from src/assets/img/terminal@2x.png
rename to apps/web/src/assets/img/terminal@2x.png
diff --git a/src/assets/img/warning-medium.svg b/apps/web/src/assets/img/warning-medium.svg
similarity index 100%
rename from src/assets/img/warning-medium.svg
rename to apps/web/src/assets/img/warning-medium.svg
diff --git a/src/assets/img/webhook.svg b/apps/web/src/assets/img/webhook.svg
similarity index 100%
rename from src/assets/img/webhook.svg
rename to apps/web/src/assets/img/webhook.svg
diff --git a/src/assets/logo/logo.png b/apps/web/src/assets/logo/logo.png
similarity index 100%
rename from src/assets/logo/logo.png
rename to apps/web/src/assets/logo/logo.png
diff --git a/src/components/AppSelector/AppSelector.tsx b/apps/web/src/components/AppSelector/AppSelector.tsx
similarity index 100%
rename from src/components/AppSelector/AppSelector.tsx
rename to apps/web/src/components/AppSelector/AppSelector.tsx
diff --git a/src/components/AppSelector/AppSelectorUtil.tsx b/apps/web/src/components/AppSelector/AppSelectorUtil.tsx
similarity index 100%
rename from src/components/AppSelector/AppSelectorUtil.tsx
rename to apps/web/src/components/AppSelector/AppSelectorUtil.tsx
diff --git a/src/components/AppSelector/ChartSelector.tsx b/apps/web/src/components/AppSelector/ChartSelector.tsx
similarity index 100%
rename from src/components/AppSelector/ChartSelector.tsx
rename to apps/web/src/components/AppSelector/ChartSelector.tsx
diff --git a/src/components/AppSelector/index.ts b/apps/web/src/components/AppSelector/index.ts
similarity index 100%
rename from src/components/AppSelector/index.ts
rename to apps/web/src/components/AppSelector/index.ts
diff --git a/src/components/ApplicationGroup/AppGroup.service.ts b/apps/web/src/components/ApplicationGroup/AppGroup.service.ts
similarity index 98%
rename from src/components/ApplicationGroup/AppGroup.service.ts
rename to apps/web/src/components/ApplicationGroup/AppGroup.service.ts
index 2aec0d51a4..c1415e7aa4 100644
--- a/src/components/ApplicationGroup/AppGroup.service.ts
+++ b/apps/web/src/components/ApplicationGroup/AppGroup.service.ts
@@ -25,8 +25,9 @@ import {
CommonNodeAttr,
WorkflowType,
getUrlWithSearchParams,
+ CiPipeline,
} from '@devtron-labs/devtron-fe-common-lib'
-import { CdPipelineResult, CiPipelineResult, WorkflowResult, CiPipeline } from '../app/details/triggerView/types'
+import { CdPipelineResult, CiPipelineResult, WorkflowResult } from '../app/details/triggerView/types'
import { WebhookListResponse } from '../ciPipeline/Webhook/types'
import { processWorkflow } from '../app/details/triggerView/workflow.service'
import { WorkflowTrigger } from '../app/details/triggerView/config'
diff --git a/src/components/ApplicationGroup/AppGroup.types.ts b/apps/web/src/components/ApplicationGroup/AppGroup.types.ts
similarity index 98%
rename from src/components/ApplicationGroup/AppGroup.types.ts
rename to apps/web/src/components/ApplicationGroup/AppGroup.types.ts
index 17ef1c42de..f8c72d9f25 100644
--- a/src/components/ApplicationGroup/AppGroup.types.ts
+++ b/apps/web/src/components/ApplicationGroup/AppGroup.types.ts
@@ -28,9 +28,10 @@ import {
GVKType,
RuntimeParamsListItemType,
UseUrlFiltersReturnType,
+ CommonNodeAttr,
} from '@devtron-labs/devtron-fe-common-lib'
import { MultiValue } from 'react-select'
-import { WebhookPayloads } from '../app/details/triggerView/types'
+import { CDMaterialProps, WebhookPayloads } from '../app/details/triggerView/types'
import { EditDescRequest, NodeType, Nodes, OptionType } from '../app/types'
import { AppFilterTabs, BulkResponseStatus } from './Constants'
import { WorkloadCheckType } from '../v2/appDetails/sourceInfo/scaleWorkloads/scaleWorkloadsModal.type'
@@ -66,7 +67,7 @@ export interface BulkCDDetailTypeResponse {
uniqueReleaseTags: string[]
}
-export interface BulkCDDetailType extends BulkTriggerAppDetailType {
+export interface BulkCDDetailType extends BulkTriggerAppDetailType, Pick, Partial> {
cdPipelineName?: string
cdPipelineId?: string
stageType?: DeploymentNodeType
diff --git a/src/components/ApplicationGroup/AppGroup.utils.ts b/apps/web/src/components/ApplicationGroup/AppGroup.utils.ts
similarity index 89%
rename from src/components/ApplicationGroup/AppGroup.utils.ts
rename to apps/web/src/components/ApplicationGroup/AppGroup.utils.ts
index b6853fd6ac..730e64afd9 100644
--- a/src/components/ApplicationGroup/AppGroup.utils.ts
+++ b/apps/web/src/components/ApplicationGroup/AppGroup.utils.ts
@@ -27,7 +27,13 @@ import {
} from '@devtron-labs/devtron-fe-common-lib'
import { DEFAULT_GIT_BRANCH_VALUE, DOCKER_FILE_ERROR_TITLE, SOURCE_NOT_CONFIGURED } from '../../config'
import { getEnvAppList } from './AppGroup.service'
-import { AppGroupUrlFilters, CDWorkflowStatusType, CIWorkflowStatusType, ProcessWorkFlowStatusType } from './AppGroup.types'
+import {
+ AppGroupUrlFilters,
+ CDWorkflowStatusType,
+ CIWorkflowStatusType,
+ ProcessWorkFlowStatusType,
+} from './AppGroup.types'
+import { getParsedBranchValuesForPlugin } from '@Components/common'
let timeoutId
@@ -81,17 +87,17 @@ export const processWorkflowStatuses = (
wf.nodes = wf.nodes.map((node) => {
switch (node.type) {
case 'CI':
- node['status'] = node.isLinkedCI ? ciMap[node.parentCiPipeline]?.status : ciMap[node.id]?.status
- node['storageConfigured'] = ciMap[node.id]?.storageConfigured
+ node.status = node.isLinkedCI ? ciMap[node.parentCiPipeline]?.status : ciMap[node.id]?.status
+ node.storageConfigured = ciMap[node.id]?.storageConfigured
break
case 'PRECD':
- node['status'] = preCDMap[node.id]
+ node.status = preCDMap[node.id]
break
case 'POSTCD':
- node['status'] = postCDMap[node.id]
+ node.status = postCDMap[node.id]
break
case 'CD':
- node['status'] = cdMap[node.id]
+ node.status = cdMap[node.id]
break
}
return node
@@ -141,8 +147,8 @@ export const handleSourceNotConfigured = (
}
}
-export const envListOptions = (inputValue: string, signal?: AbortSignal): Promise<[]> => {
- return new Promise((resolve) => {
+export const envListOptions = (inputValue: string, signal?: AbortSignal): Promise<[]> =>
+ new Promise((resolve) => {
if (timeoutId) {
clearTimeout(timeoutId)
}
@@ -156,9 +162,9 @@ export const envListOptions = (inputValue: string, signal?: AbortSignal): Promis
let appList = []
if (response.result) {
appList = response.result.envList?.map((res) => ({
- value: res['id'],
- label: res['environment_name'],
- appCount: res['appCount'],
+ value: res.id,
+ label: res.environment_name,
+ appCount: res.appCount,
...res,
}))
}
@@ -174,7 +180,6 @@ export const envListOptions = (inputValue: string, signal?: AbortSignal): Promis
})
}, 300)
})
-}
export const appGroupAppSelectorStyle = {
control: (base, state) => ({
@@ -220,15 +225,13 @@ export const appGroupAppSelectorStyle = {
width: state.menuIsOpen ? '250px' : 'max-content',
whiteSpace: 'nowrap',
}),
- menuList: (base) => {
- return {
- ...base,
- paddingTop: '0',
- paddingBottom: '0',
- marginBottom: '0',
- borderRadius: '4px',
- }
- },
+ menuList: (base) => ({
+ ...base,
+ paddingTop: '0',
+ paddingBottom: '0',
+ marginBottom: '0',
+ borderRadius: '4px',
+ }),
dropdownIndicator: (base, state) => ({
...base,
padding: '0 4px 0 4px',
@@ -264,7 +267,7 @@ export const getBranchValues = (ciNodeId: string, workflows: WorkflowType[], fil
const selectedCIPipeline = filteredCIPipelines.find((_ci) => _ci.id === +ciNodeId)
if (selectedCIPipeline?.ciMaterial) {
for (const mat of selectedCIPipeline.ciMaterial) {
- branchValues += `${branchValues ? ',' : ''}${mat.source.value}`
+ branchValues += `${branchValues ? ',' : ''}${getParsedBranchValuesForPlugin(mat.source.value)}`
}
}
break
@@ -279,11 +282,11 @@ export const processConsequenceData = (data: BlockedStateData): ConsequenceType
return null
}
if (data.isCITriggerBlocked) {
- return { action: ConsequenceAction.BLOCK, metadataField: null }
+ return { action: ConsequenceAction.BLOCK }
}
return data.ciBlockState
}
export const parseSearchParams = (searchParams: URLSearchParams) => ({
[AppGroupUrlFilters.cluster]: searchParams.getAll(AppGroupUrlFilters.cluster),
-})
\ No newline at end of file
+})
diff --git a/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx b/apps/web/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx
similarity index 96%
rename from src/components/ApplicationGroup/AppGroupAppFilter.components.tsx
rename to apps/web/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx
index f7a2b5e3fe..eeb98ef397 100644
--- a/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx
+++ b/apps/web/src/components/ApplicationGroup/AppGroupAppFilter.components.tsx
@@ -30,6 +30,7 @@ import { ReactComponent as Trash } from '../../assets/icons/ic-delete-interactiv
import { ReactComponent as CheckIcon } from '../../assets/icons/ic-check.svg'
import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types'
import { AppFilterTabs } from './Constants'
+import { ShortcutKeyBadge } from '@Components/common/formFields/Widgets/Widgets'
export const ValueContainer = (props): JSX.Element => {
const {
@@ -53,16 +54,17 @@ export const ValueContainer = (props): JSX.Element => {
{!props.selectProps.inputValue ? (
<>
{!props.selectProps.menuIsOpen ? (
- <>
+
{selectedAppsLength > 0 ? (
-
+
) : (
-
+
)}
{selectorText}
- >
+
+
) : (
<>
diff --git a/src/components/ApplicationGroup/AppGroupAppFilter.tsx b/apps/web/src/components/ApplicationGroup/AppGroupAppFilter.tsx
similarity index 86%
rename from src/components/ApplicationGroup/AppGroupAppFilter.tsx
rename to apps/web/src/components/ApplicationGroup/AppGroupAppFilter.tsx
index e33588d7fb..5b8acdcd8e 100644
--- a/src/components/ApplicationGroup/AppGroupAppFilter.tsx
+++ b/apps/web/src/components/ApplicationGroup/AppGroupAppFilter.tsx
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-import React, { useState } from 'react'
-import ReactSelect from 'react-select'
+import { useEffect, useRef, useState } from 'react'
+import ReactSelect, { SelectInstance } from 'react-select'
import { useAppGroupAppFilterContext } from './AppGroupDetailsRoute'
import { appGroupAppSelectorStyle } from './AppGroup.utils'
import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types'
import { AppFilterTabs } from './Constants'
import { MenuList, Option, ValueContainer } from './AppGroupAppFilter.components'
-import { ReactSelectInputAction } from '@devtron-labs/devtron-fe-common-lib'
+import { OptionType, ReactSelectInputAction, useRegisterShortcut } from '@devtron-labs/devtron-fe-common-lib'
export default function AppGroupAppFilter() {
const {
@@ -37,6 +37,8 @@ export default function AppGroupAppFilter() {
setSelectedGroupFilter,
filterParentType,
}: AppGroupAppFilterContextType = useAppGroupAppFilterContext()
+ const appGroupFilterRef = useRef>()
+ const { registerShortcut, unregisterShortcut } = useRegisterShortcut()
const [appFilterAppInput, setAppFilterAppInput] = useState('')
const [appFilterGroupInput, setAppFilterGroupInput] = useState('')
@@ -101,8 +103,22 @@ export default function AppGroupAppFilter() {
return 'Search filters'
}
+ const handleFilterFocus = () => {
+ appGroupFilterRef.current.focus()
+ appGroupFilterRef.current.onMenuOpen()
+ }
+
+ useEffect(() => {
+ registerShortcut({ keys: ['F'], callback: handleFilterFocus })
+
+ return () => {
+ unregisterShortcut(['F'])
+ }
+ }, [])
+
return (
@@ -53,7 +54,7 @@ const ApplicationRoute = ({ envAppList, envConfig, fetchEnvConfig }: Application
APPLICATIONS
-
+
{envAppList.map(({ name, isProtected, id }) => (
{renderNavItem(
diff --git a/src/components/ApplicationGroup/Details/EnvironmentConfig/EnvConfig.tsx b/apps/web/src/components/ApplicationGroup/Details/EnvironmentConfig/EnvConfig.tsx
similarity index 64%
rename from src/components/ApplicationGroup/Details/EnvironmentConfig/EnvConfig.tsx
rename to apps/web/src/components/ApplicationGroup/Details/EnvironmentConfig/EnvConfig.tsx
index f7e9d15989..659016333d 100644
--- a/src/components/ApplicationGroup/Details/EnvironmentConfig/EnvConfig.tsx
+++ b/apps/web/src/components/ApplicationGroup/Details/EnvironmentConfig/EnvConfig.tsx
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import { useEffect, useState } from 'react'
-import { generatePath, Route, Switch, useLocation, useRouteMatch } from 'react-router-dom'
+import { useEffect, useMemo } from 'react'
+import { generatePath, Route, Switch, useHistory, useLocation, useRouteMatch } from 'react-router-dom'
import { EnvResourceType, GenericEmptyState, Progressing, noop, useAsync } from '@devtron-labs/devtron-fe-common-lib'
@@ -31,18 +31,13 @@ import { AppGroupDetailDefaultType, ConfigAppList } from '../../AppGroup.types'
import ApplicationRoute from './ApplicationRoutes'
const getEnvConfigProtections = importComponentFromFELibrary('getEnvConfigProtections', null, 'function')
-const CompareWithButton = importComponentFromFELibrary('CompareWithButton', null, 'function')
const EnvConfig = ({ filteredAppIds, envName }: AppGroupDetailDefaultType) => {
// HOOKS
- const {
- path,
- params: { appId, envId },
- } = useRouteMatch<{ envId: string; appId: string }>()
+ const { path, params } = useRouteMatch<{ envId: string; appId: string }>()
+ const { appId, envId } = params
const { pathname } = useLocation()
-
- // STATES
- const [envAppList, setEnvAppList] = useState([])
+ const { replace } = useHistory()
// ASYNC CALLS
const [loading, initDataResults] = useAsync(
@@ -65,7 +60,8 @@ const EnvConfig = ({ filteredAppIds, envName }: AppGroupDetailDefaultType) => {
isLoading: envConfigLoading,
}
- useEffect(() => {
+ // CONSTANTS
+ const envAppList = useMemo(() => {
if (
initDataResults?.[0].status === 'fulfilled' &&
initDataResults?.[1].status === 'fulfilled' &&
@@ -78,11 +74,25 @@ const EnvConfig = ({ filteredAppIds, envName }: AppGroupDetailDefaultType) => {
}))
_appList.sort((a, b) => a.name.localeCompare(b.name))
- setEnvAppList(_appList)
+ return _appList
}
+
+ return []
}, [initDataResults])
- if (loading || !envAppList.length) {
+ const isAppNotPresentInEnv = useMemo(
+ () => envAppList.length && appId && !envAppList.some(({ id }) => id === +appId),
+ [envAppList, appId],
+ )
+
+ useEffect(() => {
+ // If the app is unavailable in the current environment, redirect to the app selection page
+ if (isAppNotPresentInEnv) {
+ replace(generatePath(path, { ...params, appId: null }))
+ }
+ }, [isAppNotPresentInEnv])
+
+ if (loading || !envAppList.length || isAppNotPresentInEnv) {
return (
@@ -92,34 +102,32 @@ const EnvConfig = ({ filteredAppIds, envName }: AppGroupDetailDefaultType) => {
return (
- {CompareWithButton && (
-
- {({ match, location }) => {
- const basePath = generatePath(path, match.params)
- // Set the resourceTypePath based on the resourceType from the URL parameters.
- // If the resourceType is 'Manifest', use 'deployment-template' as the back URL.
- // Otherwise, use the actual resourceType from the URL, which could be 'deployment-template', 'configmap', or 'secrets'.
- const resourceTypePath = `/${match.params.resourceType === EnvResourceType.Manifest ? EnvResourceType.DeploymentTemplate : match.params.resourceType}`
- const resourceNamePath = match.params.resourceName ? `/${match.params.resourceName}` : ''
-
- const goBackURL = `${basePath}${resourceTypePath}${resourceNamePath}`
-
- return (
-
- `${generatePath(match.path, { ...match.params, resourceType, resourceName })}${location.search}`
- }
- />
- )
- }}
-
- )}
+
+ {({ match, location }) => {
+ const basePath = generatePath(path, match.params)
+ // Set the resourceTypePath based on the resourceType from the URL parameters.
+ // If the resourceType is 'Manifest' or 'PipelineStrategy', use 'deployment-template' as the back URL.
+ // Otherwise, use the actual resourceType from the URL, which could be 'deployment-template', 'configmap', or 'secrets'.
+ const resourceTypePath = `/${match.params.resourceType === EnvResourceType.Manifest || match.params.resourceType === EnvResourceType.PipelineStrategy ? EnvResourceType.DeploymentTemplate : match.params.resourceType}`
+ const resourceNamePath = match.params.resourceName ? `/${match.params.resourceName}` : ''
+
+ const goBackURL = `${basePath}${resourceTypePath}${resourceNamePath}`
+
+ return (
+
+ `${generatePath(match.path, { ...match.params, resourceType, resourceName })}${location.search}`
+ }
+ />
+ )
+ }}
+
{
responses.forEach(resolveMaterialData(_cdMaterialResponse, _unauthorizedAppList))
@@ -319,6 +327,7 @@ export default function BulkCDTrigger({
setLoading(false)
})
.catch((error) => {
+ setLoading(false)
showError(error)
})
}
@@ -371,6 +380,18 @@ export default function BulkCDTrigger({
}
const renderEmptyView = (): JSX.Element => {
+ if (selectedApp.isTriggerBlockedDueToPlugin) {
+ const commonNodeAttrType: CommonNodeAttr['type'] =
+ selectedApp.stageType === DeploymentNodeType.PRECD ? 'PRECD' : 'POSTCD'
+
+ return (
+
+ )
+ }
+
if (unauthorizedAppList[selectedApp.appId]) {
return (
{
+ const commonNodeAttrType: CommonNodeAttr['type'] =
+ app.stageType === DeploymentNodeType.PRECD ? 'PRECD' : 'POSTCD'
+
+ const warningMessage = app.warningMessage || appDeploymentWindowMap[app.appId]?.warningMessage
+
+ const isAppSelected = selectedApp.appId === app.appId
+
+ if (unauthorizedAppList[app.appId]) {
+ return (
+
+
+ {BULK_CD_MESSAGING.unauthorized.title}
+
+ )
+ }
+
+ if (tagNotFoundWarningsMap.has(app.appId)) {
+ return (
+
+
+
+
+ {tagNotFoundWarningsMap.get(app.appId)}
+
+
+ )
+ }
+
+ if (app.isTriggerBlockedDueToPlugin) {
+ return (
+
+ )
+ }
+
+ if (!!warningMessage && !app.showPluginWarning) {
+ return (
+
+
+
+
+ {warningMessage}
+
+
+ )
+ }
+
+ if (app.showPluginWarning) {
+ return (
+
+ )
+ }
+
+ return null
+ }
+
const renderBodySection = (): JSX.Element => {
if (isLoading) {
const message = isBulkDeploymentTriggered.current
@@ -473,6 +564,7 @@ export default function BulkCDTrigger({
materials: updatedMaterials ?? appListData.material,
approvalUsers: appListData.approvalUsers,
requestedUserId: appListData.requestedUserId,
+ // FIXME: Not using anywhere
userApprovalConfig: appListData.userApprovalConfig,
appReleaseTagNames: appListData.appReleaseTags,
tagsEditable: appListData.tagsEditable,
@@ -613,7 +705,7 @@ export default function BulkCDTrigger({
return (
-
+
{showRuntimeParams && (
(
{app.name}
- {(app.warningMessage ||
- tagNotFoundWarningsMap.has(app.appId) ||
- appDeploymentWindowMap[app.appId]?.warningMessage) && (
-
-
-
- {app.warningMessage ||
- appDeploymentWindowMap[app.appId]?.warningMessage ||
- tagNotFoundWarningsMap.get(app.appId)}
-
-
- )}
- {unauthorizedAppList[app.appId] && (
-
-
- {BULK_CD_MESSAGING.unauthorized.title}
-
- )}
+ {renderAppWarningAndErrors(app)}
))}
diff --git a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx b/apps/web/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx
similarity index 93%
rename from src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx
index cc6a199e1a..af74c1953e 100644
--- a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx
+++ b/apps/web/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx
@@ -32,9 +32,12 @@ import {
SourceTypeMap,
ToastManager,
ToastVariantType,
+ BlockedStateData,
+ PromiseAllStatusType,
+ CommonNodeAttr,
} from '@devtron-labs/devtron-fe-common-lib'
import Tippy from '@tippyjs/react'
-import { importComponentFromFELibrary } from '../../../common'
+import { getCIPipelineURL, getParsedBranchValuesForPlugin, importComponentFromFELibrary } from '../../../common'
import { ReactComponent as Close } from '../../../../assets/icons/ic-cross.svg'
import { ReactComponent as PlayIcon } from '../../../../assets/icons/misc/arrow-solid-right.svg'
import { ReactComponent as Warning } from '../../../../assets/icons/ic-warning.svg'
@@ -65,7 +68,11 @@ import { ReactComponent as MechanicalOperation } from '../../../../assets/img/ic
import { BULK_ERROR_MESSAGES } from './constants'
const PolicyEnforcementMessage = importComponentFromFELibrary('PolicyEnforcementMessage')
-const getCIBlockState = importComponentFromFELibrary('getCIBlockState', null, 'function')
+const getCIBlockState: (...props) => Promise
= importComponentFromFELibrary(
+ 'getCIBlockState',
+ null,
+ 'function',
+)
const getRuntimeParams = importComponentFromFELibrary('getRuntimeParams', null, 'function')
const RuntimeParamTabs = importComponentFromFELibrary('RuntimeParamTabs', null, 'function')
@@ -171,10 +178,7 @@ const BulkCITrigger = ({
responses.forEach((res, index) => {
_materialListMap[appList[index]?.appId] = res.value?.['result']
})
- // These two handlers should be imported from elsewhere
- if (getCIBlockState) {
- await getPolicyEnforcementData(_materialListMap)
- }
+ await getPolicyEnforcementData(_materialListMap)
if (getRuntimeParams) {
await getRuntimeParamsData(_materialListMap)
}
@@ -227,6 +231,10 @@ const BulkCITrigger = ({
}
const getPolicyEnforcementData = async (_materialListMap: Record): Promise => {
+ if (!getCIBlockState) {
+ return null
+ }
+
const policyPromiseFunctionList = appList.map((appDetails) => {
if (getIsAppUnorthodox(appDetails) || !_materialListMap[appDetails.appId]) {
return () => null
@@ -237,23 +245,25 @@ const BulkCITrigger = ({
(!material.isBranchError && !material.isRepoError && !material.isRegex) ||
material.value !== '--'
) {
- branchNames += `${branchNames ? ',' : ''}${material.value}`
+ branchNames += `${branchNames ? ',' : ''}${getParsedBranchValuesForPlugin(material.value)}`
}
}
- return !branchNames
- ? () => null
- : () => getCIBlockState(appDetails.ciPipelineId, appDetails.appId, branchNames)
+
+ return () => getCIBlockState(appDetails.ciPipelineId, appDetails.appId, branchNames, appDetails.name)
})
if (policyPromiseFunctionList?.length) {
const policyListMap: Record = {}
try {
- // Appending any for legacy code, since we did not had generics in APIQueuingWithBatch
- const responses: any[] = await ApiQueuingWithBatch(policyPromiseFunctionList, httpProtocol, true)
+ const responses = await ApiQueuingWithBatch(
+ policyPromiseFunctionList,
+ httpProtocol,
+ true,
+ )
responses.forEach((res, index) => {
- policyListMap[appList[index]?.appId] = res.value?.['result']
- ? processConsequenceData(res.value['result'])
- : null
+ if (res.status === PromiseAllStatusType.FULFILLED) {
+ policyListMap[appList[index]?.appId] = res.value ? processConsequenceData(res.value) : null
+ }
})
setAppPolicy(policyListMap)
} catch (error) {
@@ -572,6 +582,8 @@ const BulkCITrigger = ({
}
const renderAppName = (app: BulkCIDetailType, index: number): JSX.Element | null => {
+ const nodeType: CommonNodeAttr['type'] = 'CI'
+
return (
)}
{appPolicy[app.appId] && PolicyEnforcementMessage && (
-
+
)}
)
@@ -670,11 +694,12 @@ const BulkCITrigger = ({
const isStartBuildDisabled = (): boolean => {
return appList.some(
(app) =>
- app.errorMessage &&
- (app.errorMessage !== SOURCE_NOT_CONFIGURED ||
- !app.material.some(
- (_mat) => !_mat.isBranchError && !_mat.isRepoError && !_mat.isMaterialSelectionError,
- )),
+ appPolicy[app.appId]?.action === ConsequenceAction.BLOCK ||
+ (app.errorMessage &&
+ (app.errorMessage !== SOURCE_NOT_CONFIGURED ||
+ !app.material.some(
+ (_mat) => !_mat.isBranchError && !_mat.isRepoError && !_mat.isMaterialSelectionError,
+ ))),
)
}
diff --git a/src/components/ApplicationGroup/Details/TriggerView/BulkSourceChange.tsx b/apps/web/src/components/ApplicationGroup/Details/TriggerView/BulkSourceChange.tsx
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/BulkSourceChange.tsx
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/BulkSourceChange.tsx
diff --git a/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.scss b/apps/web/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.scss
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.scss
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.scss
diff --git a/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx b/apps/web/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx
similarity index 95%
rename from src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx
index 48e6780683..5023409521 100644
--- a/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx
+++ b/apps/web/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx
@@ -46,6 +46,7 @@ import {
preventBodyScroll,
ToastManager,
ToastVariantType,
+ BlockedStateData,
} from '@devtron-labs/devtron-fe-common-lib'
import Tippy from '@tippyjs/react'
import {
@@ -73,7 +74,7 @@ import {
triggerCINode,
triggerBranchChange,
} from '../../../app/service'
-import { importComponentFromFELibrary, sortObjectArrayAlphabetically } from '../../../common'
+import { getCDPipelineURL, importComponentFromFELibrary, sortObjectArrayAlphabetically } from '../../../common'
import { ReactComponent as Pencil } from '../../../../assets/icons/ic-pencil.svg'
import { getWorkflows, getWorkflowStatus } from '../../AppGroup.service'
import {
@@ -122,9 +123,14 @@ import BulkSourceChange from './BulkSourceChange'
import { CIPipelineBuildType } from '../../../ciPipeline/types'
import { LinkedCIDetail } from '../../../../Pages/Shared/LinkedCIDetailsModal'
import CIMaterialModal from '../../../app/details/triggerView/CIMaterialModal'
+import { RenderCDMaterialContentProps } from './types'
const ApprovalMaterialModal = importComponentFromFELibrary('ApprovalMaterialModal')
-const getCIBlockState = importComponentFromFELibrary('getCIBlockState', null, 'function')
+const getCIBlockState: (...props) => Promise = importComponentFromFELibrary(
+ 'getCIBlockState',
+ null,
+ 'function',
+)
const getRuntimeParams = importComponentFromFELibrary('getRuntimeParams', null, 'function')
const processDeploymentWindowStateAppGroup = importComponentFromFELibrary(
'processDeploymentWindowStateAppGroup',
@@ -854,10 +860,12 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
abortControllerRef.current.abort()
abortControllerRef.current = new AbortController()
let _appID
+ let _appName
for (const _wf of filteredWorkflows) {
const nd = _wf.nodes.find((node) => +node.id == +ciNodeId && node.type === 'CI')
if (nd) {
_appID = _wf.appId
+ _appName = _wf.name
break
}
}
@@ -873,19 +881,20 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
ciNodeId,
_appID,
getBranchValues(ciNodeId, filteredWorkflows, filteredCIPipelines.get(_appID)),
+ _appName,
)
- : { result: null },
+ : null,
getRuntimeParams ? getRuntimeParams(ciNodeId) : null,
])
.then((resp) => {
// need to set result for getCIBlockState call only as for updateCIMaterialList
// it's already being set inside the same function
- if (resp[1].result) {
+ if (resp[1]) {
const workflows = [...filteredWorkflows].map((workflow) => {
workflow.nodes.map((node) => {
if (node.type === 'CI' && node.id == ciNodeId) {
- node.ciBlockState = processConsequenceData(resp[1].result)
- node.isCITriggerBlocked = resp[1].result.isCITriggerBlocked
+ node.pluginBlockState = processConsequenceData(resp[1])
+ node.isTriggerBlocked = resp[1].isCITriggerBlocked
return node
}
return node
@@ -927,6 +936,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
const _workflows = [...filteredWorkflows].map((workflow) => {
const nodes = workflow.nodes.map((node) => {
if (cdNodeId == node.id && node.type === nodeType) {
+ // TODO: Ig not using this, can remove it
node.userApprovalConfig = workflow.approvalConfiguredIdsMap[cdNodeId]
_selectedNode = node
_workflowId = workflow.id
@@ -1738,6 +1748,11 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
const _cdNode = wf.nodes.find(
(node) => node.type === WorkflowNodeType.CD && node.environmentId === +envId,
)
+ const selectedCINode = wf.nodes.find(
+ (node) => node.type === WorkflowNodeType.CI || node.type === WorkflowNodeType.WEBHOOK,
+ )
+ const doesWorkflowContainsWebhook = selectedCINode?.type === WorkflowNodeType.WEBHOOK
+
let _selectedNode: CommonNodeAttr
if (bulkTriggerType === DeploymentNodeType.PRECD) {
_selectedNode = _cdNode.preNode
@@ -1747,13 +1762,18 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
_selectedNode = _cdNode.postNode
}
if (_selectedNode) {
+ const stageType = DeploymentNodeType[_selectedNode.type]
+ const isTriggerBlockedDueToPlugin =
+ _selectedNode.isTriggerBlocked && _selectedNode.showPluginWarning
+ const stageText = stageType === DeploymentNodeType.PRECD ? 'Pre-Deployment' : 'Post-Deployment'
+
_selectedAppWorkflowList.push({
workFlowId: wf.id,
appId: wf.appId,
name: wf.name,
cdPipelineName: _cdNode.title,
cdPipelineId: _cdNode.id,
- stageType: DeploymentNodeType[_selectedNode.type],
+ stageType,
triggerType: _cdNode.triggerType,
envName: _selectedNode.environmentName,
envId: _selectedNode.environmentId,
@@ -1768,6 +1788,18 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
tagsEditable: wf.tagsEditable,
ciPipelineId: _selectedNode.connectingCiPipelineId,
hideImageTaggingHardDelete: wf.hideImageTaggingHardDelete,
+ showPluginWarning: _selectedNode.showPluginWarning,
+ isTriggerBlockedDueToPlugin,
+ configurePluginURL: getCDPipelineURL(
+ String(wf.appId),
+ wf.id,
+ doesWorkflowContainsWebhook ? '0' : selectedCINode?.id,
+ doesWorkflowContainsWebhook,
+ _selectedNode.id,
+ true,
+ ),
+ consequence: _selectedNode.pluginBlockState,
+ warningMessage: isTriggerBlockedDueToPlugin ? `${stageText} is blocked` : '',
})
} else {
let warningMessage = ''
@@ -1994,8 +2026,8 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
isCacheAvailable={nd?.storageConfigured}
fromAppGrouping
appId={_appID?.toString()}
- isCITriggerBlocked={nd?.isCITriggerBlocked}
- ciBlockState={nd?.ciBlockState}
+ isCITriggerBlocked={nd?.isTriggerBlocked}
+ ciBlockState={nd?.pluginBlockState}
isJobCI={!!nd?.isJobCI}
runtimeParams={runtimeParams[nd?.id] ?? []}
handleRuntimeParamChange={handleRuntimeParamChange}
@@ -2088,11 +2120,59 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
)
}
+ const renderCDMaterialContent = ({
+ node,
+ appId,
+ workflowId,
+ selectedAppName,
+ doesWorkflowContainsWebhook,
+ ciNodeId,
+ }: RenderCDMaterialContentProps) => {
+ const configurePluginURL = getCDPipelineURL(
+ String(appId),
+ workflowId,
+ doesWorkflowContainsWebhook ? '0' : ciNodeId,
+ doesWorkflowContainsWebhook,
+ node?.id,
+ true,
+ )
+
+ return (
+
+ )
+ }
+
const renderCDMaterial = (): JSX.Element | null => {
+ if (!selectedCDNode?.id) {
+ return null
+ }
+
if (location.search.includes('cd-node') || location.search.includes('rollback-node')) {
let node: CommonNodeAttr
let _appID
let selectedAppName: string
+ let workflowId: string
+ let selectedCINode: CommonNodeAttr
if (selectedCDNode?.id) {
for (const _wf of filteredWorkflows) {
@@ -2100,6 +2180,10 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
return +el.id == selectedCDNode.id && el.type == selectedCDNode.type
})
if (node) {
+ selectedCINode = _wf.nodes.find(
+ (node) => node.type === WorkflowNodeType.CI || node.type === WorkflowNodeType.WEBHOOK,
+ )
+ workflowId = _wf.id
_appID = _wf.appId
selectedAppName = _wf.name
break
@@ -2128,23 +2212,14 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
>
) : (
-
+ renderCDMaterialContent({
+ node,
+ appId: _appID,
+ selectedAppName,
+ workflowId,
+ doesWorkflowContainsWebhook: selectedCINode?.type === WorkflowNodeType.WEBHOOK,
+ ciNodeId: selectedCINode?.id,
+ })
)}
diff --git a/src/components/ApplicationGroup/Details/TriggerView/SourceUpdateResponseModal.tsx b/apps/web/src/components/ApplicationGroup/Details/TriggerView/SourceUpdateResponseModal.tsx
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/SourceUpdateResponseModal.tsx
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/SourceUpdateResponseModal.tsx
diff --git a/src/components/ApplicationGroup/Details/TriggerView/TriggerModalTableRow.tsx b/apps/web/src/components/ApplicationGroup/Details/TriggerView/TriggerModalTableRow.tsx
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/TriggerModalTableRow.tsx
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/TriggerModalTableRow.tsx
diff --git a/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx b/apps/web/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx
diff --git a/src/components/ApplicationGroup/Details/TriggerView/constants.ts b/apps/web/src/components/ApplicationGroup/Details/TriggerView/constants.ts
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/constants.ts
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/constants.ts
diff --git a/src/components/ApplicationGroup/Details/TriggerView/types.ts b/apps/web/src/components/ApplicationGroup/Details/TriggerView/types.ts
similarity index 76%
rename from src/components/ApplicationGroup/Details/TriggerView/types.ts
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/types.ts
index 157e33bf85..b76a8d66ac 100644
--- a/src/components/ApplicationGroup/Details/TriggerView/types.ts
+++ b/apps/web/src/components/ApplicationGroup/Details/TriggerView/types.ts
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import { CommonNodeAttr } from '@devtron-labs/devtron-fe-common-lib'
+
// Legacy components with no props so setting to any
export interface BulkSourceChangeProps {
closePopup: any
@@ -28,3 +30,12 @@ export interface SourceUpdateResponseModalProps {
responseList: any
isLoading: any
}
+
+export interface RenderCDMaterialContentProps {
+ node: CommonNodeAttr
+ appId: number
+ selectedAppName: string
+ workflowId: string
+ doesWorkflowContainsWebhook: boolean
+ ciNodeId: string
+}
diff --git a/src/components/ApplicationGroup/Details/TriggerView/utils.ts b/apps/web/src/components/ApplicationGroup/Details/TriggerView/utils.ts
similarity index 100%
rename from src/components/ApplicationGroup/Details/TriggerView/utils.ts
rename to apps/web/src/components/ApplicationGroup/Details/TriggerView/utils.ts
diff --git a/src/components/ApplicationGroup/EnvEmptyStates.tsx b/apps/web/src/components/ApplicationGroup/EnvEmptyStates.tsx
similarity index 100%
rename from src/components/ApplicationGroup/EnvEmptyStates.tsx
rename to apps/web/src/components/ApplicationGroup/EnvEmptyStates.tsx
diff --git a/src/components/ApplicationGroup/EnvSelector.tsx b/apps/web/src/components/ApplicationGroup/EnvSelector.tsx
similarity index 100%
rename from src/components/ApplicationGroup/EnvSelector.tsx
rename to apps/web/src/components/ApplicationGroup/EnvSelector.tsx
diff --git a/src/components/ApplicationGroup/List/EnvironmentListView.tsx b/apps/web/src/components/ApplicationGroup/List/EnvironmentListView.tsx
similarity index 100%
rename from src/components/ApplicationGroup/List/EnvironmentListView.tsx
rename to apps/web/src/components/ApplicationGroup/List/EnvironmentListView.tsx
diff --git a/src/components/ApplicationGroup/List/EnvironmentsList.scss b/apps/web/src/components/ApplicationGroup/List/EnvironmentsList.scss
similarity index 98%
rename from src/components/ApplicationGroup/List/EnvironmentsList.scss
rename to apps/web/src/components/ApplicationGroup/List/EnvironmentsList.scss
index bd1374540b..d44352991f 100644
--- a/src/components/ApplicationGroup/List/EnvironmentsList.scss
+++ b/apps/web/src/components/ApplicationGroup/List/EnvironmentsList.scss
@@ -66,12 +66,6 @@
overflow: hidden;
margin-bottom: 8px;
}
-
- .shebang {
- padding: 0 60px;
- color: #151515;
- opacity: 0.6;
- }
}
.env-compose__nav-item {
diff --git a/src/components/ApplicationGroup/List/EnvironmentsList.tsx b/apps/web/src/components/ApplicationGroup/List/EnvironmentsList.tsx
similarity index 100%
rename from src/components/ApplicationGroup/List/EnvironmentsList.tsx
rename to apps/web/src/components/ApplicationGroup/List/EnvironmentsList.tsx
diff --git a/src/components/ApplicationGroup/List/__mocks__/EnvironmentList.mock.ts b/apps/web/src/components/ApplicationGroup/List/__mocks__/EnvironmentList.mock.ts
similarity index 100%
rename from src/components/ApplicationGroup/List/__mocks__/EnvironmentList.mock.ts
rename to apps/web/src/components/ApplicationGroup/List/__mocks__/EnvironmentList.mock.ts
diff --git a/src/components/ApplicationGroup/List/__tests__/EnvironmentListView.test.tsx b/apps/web/src/components/ApplicationGroup/List/__tests__/EnvironmentListView.test.tsx
similarity index 100%
rename from src/components/ApplicationGroup/List/__tests__/EnvironmentListView.test.tsx
rename to apps/web/src/components/ApplicationGroup/List/__tests__/EnvironmentListView.test.tsx
diff --git a/src/components/CIPipelineN/AdvancedConfigOptions.tsx b/apps/web/src/components/CIPipelineN/AdvancedConfigOptions.tsx
similarity index 100%
rename from src/components/CIPipelineN/AdvancedConfigOptions.tsx
rename to apps/web/src/components/CIPipelineN/AdvancedConfigOptions.tsx
diff --git a/src/components/CIPipelineN/Build.tsx b/apps/web/src/components/CIPipelineN/Build.tsx
similarity index 100%
rename from src/components/CIPipelineN/Build.tsx
rename to apps/web/src/components/CIPipelineN/Build.tsx
diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/apps/web/src/components/CIPipelineN/CIPipeline.tsx
similarity index 93%
rename from src/components/CIPipelineN/CIPipeline.tsx
rename to apps/web/src/components/CIPipelineN/CIPipeline.tsx
index a387d58766..febf893a01 100644
--- a/src/components/CIPipelineN/CIPipeline.tsx
+++ b/apps/web/src/components/CIPipelineN/CIPipeline.tsx
@@ -44,23 +44,18 @@ import {
PipelineFormType,
ToastVariantType,
ToastManager,
- Environment,
+ ProcessPluginDataParamsType,
+ ResourceKindType,
} from '@devtron-labs/devtron-fe-common-lib'
import Tippy from '@tippyjs/react'
import {
FloatingVariablesSuggestions,
+ getParsedBranchValuesForPlugin,
getPluginIdsFromBuildStage,
importComponentFromFELibrary,
sortObjectArrayAlphabetically,
} from '../common'
-import {
- BuildStageVariable,
- BuildTabText,
- JobPipelineTabText,
- TriggerType,
- URLS,
- ViewType,
-} from '../../config'
+import { BuildStageVariable, BuildTabText, JobPipelineTabText, TriggerType, URLS, ViewType } from '../../config'
import {
deleteCIPipeline,
getGlobalVariable,
@@ -83,9 +78,9 @@ import { calculateLastStepDetailsLogic, checkUniqueness, validateTask } from '..
import { PipelineContext, PipelineFormDataErrorType } from '../workflowEditor/types'
import { EnvironmentWithSelectPickerType } from './types'
-const processPluginData = importComponentFromFELibrary('processPluginData', null, 'function')
+const processPluginData: (params: ProcessPluginDataParamsType) => Promise
=
+ importComponentFromFELibrary('processPluginData', null, 'function')
const validatePlugins = importComponentFromFELibrary('validatePlugins', null, 'function')
-const prepareFormData = importComponentFromFELibrary('prepareFormData', null, 'function')
export default function CIPipeline({
appName,
connectCDPipelines,
@@ -211,16 +206,6 @@ export default function CIPipeline({
const selectedBranchRef = useRef(null)
- const mandatoryPluginsMap: PipelineContext['mandatoryPluginsMap'] = useMemo(() => {
- const _mandatoryPluginsMap: PipelineContext['mandatoryPluginsMap'] = {}
- if (mandatoryPluginData?.pluginData.length) {
- for (const plugin of mandatoryPluginData.pluginData) {
- _mandatoryPluginsMap[plugin.parentPluginId] = plugin
- }
- }
- return _mandatoryPluginsMap
- }, [mandatoryPluginData])
-
const handlePluginDataStoreUpdate: PipelineContext['handlePluginDataStoreUpdate'] = (updatedPluginDataStore) => {
const { parentPluginStore, pluginVersionStore } = updatedPluginDataStore
@@ -256,7 +241,7 @@ export default function CIPipeline({
}
}
- const areMandatoryPluginPossible = !isJobCard && processPluginData && prepareFormData
+ const areMandatoryPluginPossible = !isJobCard && !!processPluginData
// NOTE: Wrap this method in try catch block to handle error
const getMandatoryPluginData = async (
@@ -271,29 +256,26 @@ export default function CIPipeline({
if (_formData?.materials?.length) {
for (const material of _formData.materials) {
if (!material.isRegex || material.value) {
- branchName += `${branchName ? ',' : ''}${material.value}`
+ branchName += `${branchName ? ',' : ''}${getParsedBranchValuesForPlugin(material.value)}`
}
}
}
if (selectedBranchRef.current !== branchName) {
selectedBranchRef.current = branchName
- const {
- mandatoryPluginData: processedPluginData,
- pluginDataStore: updatedPluginDataStore,
- }: ProcessPluginDataReturnType = await processPluginData(
- _formData,
- pluginDataStore,
- appId,
- ciPipelineId,
- branchName,
- requiredPluginIds,
- )
+ const { mandatoryPluginData: processedPluginData, pluginDataStore: updatedPluginDataStore } =
+ await processPluginData({
+ formData: _formData,
+ pluginDataStoreState: pluginDataStore,
+ appId: +appId,
+ appName,
+ ciPipelineId: +ciPipelineId,
+ branchName,
+ requiredPluginIds,
+ resourceKind: ResourceKindType.ciPipeline,
+ })
setMandatoryPluginData(processedPluginData)
handlePluginDataStoreUpdate(updatedPluginDataStore)
- setFormData((prevForm) =>
- prepareFormData({ ...prevForm }, processedPluginData?.pluginData ?? [], updatedPluginDataStore),
- )
}
}
}
@@ -414,6 +396,17 @@ export default function CIPipeline({
return { index: stepsLength + 1, calculatedStageVariables: _inputVariablesListPerTask }
}
+ const handleValidateMandatoryPlugins: PipelineContext['handleValidateMandatoryPlugins'] = ({
+ newFormData = formData,
+ newPluginDataStore = pluginDataStore,
+ }) => {
+ if (!validatePlugins || !mandatoryPluginData?.pluginData?.length) {
+ return
+ }
+
+ setMandatoryPluginData(validatePlugins(newFormData, mandatoryPluginData.pluginData, newPluginDataStore))
+ }
+
const validateStage = (
stageName: string,
_formData: PipelineFormType,
@@ -455,11 +448,10 @@ export default function CIPipeline({
validateTask(_formData[stageName].steps[i], _formDataErrorObj[stageName].steps[i])
isStageValid = isStageValid && _formDataErrorObj[stageName].steps[i].isValid
}
- if (mandatoryPluginData?.pluginData?.length && validatePlugins) {
- setMandatoryPluginData(
- validatePlugins(_formData, mandatoryPluginData.pluginData, clonedPluginDataStore),
- )
- }
+ handleValidateMandatoryPlugins({
+ newFormData: _formData,
+ newPluginDataStore: clonedPluginDataStore,
+ })
_formDataErrorObj[stageName].isValid = isStageValid
}
setFormDataErrorObj(_formDataErrorObj)
@@ -726,10 +718,10 @@ export default function CIPipeline({
)
.then((response) => {
if (response) {
- ToastManager.showToast({
- variant: ToastVariantType.success,
- description: msg,
- })
+ ToastManager.showToast({
+ variant: ToastVariantType.success,
+ description: msg,
+ })
setApiInProgress(false)
handleClose()
getWorkflows()
@@ -788,8 +780,8 @@ export default function CIPipeline({
}
}
- const contextValue = useMemo(() => {
- return {
+ const contextValue = useMemo(
+ () => ({
formData,
setFormData,
loadingState,
@@ -813,21 +805,23 @@ export default function CIPipeline({
handleUpdateAvailableTags,
handleHideScopedVariableWidgetUpdate,
handleDisableParentModalCloseUpdate,
- mandatoryPluginsMap,
- }
- }, [
- formData,
- activeStageName,
- loadingState,
- formDataErrorObj,
- inputVariablesListFromPrevStep,
- selectedTaskIndex,
- pageState,
- globalVariables,
- pluginDataStore,
- availableTags,
- mandatoryPluginsMap,
- ])
+ handleValidateMandatoryPlugins,
+ mandatoryPluginData,
+ }),
+ [
+ formData,
+ activeStageName,
+ loadingState,
+ formDataErrorObj,
+ inputVariablesListFromPrevStep,
+ selectedTaskIndex,
+ pageState,
+ globalVariables,
+ pluginDataStore,
+ availableTags,
+ mandatoryPluginData,
+ ],
+ )
const renderCIPipelineModalContent = () => {
if (pageState === ViewType.ERROR) {
@@ -864,7 +858,6 @@ export default function CIPipeline({
{
const { appId } = useParams()
const {
@@ -63,7 +60,6 @@ const CreatePluginModal = ({ handleClose }: CreatePluginModalProps) => {
handlePluginDataStoreUpdate,
calculateLastStepDetail,
validateStage,
- mandatoryPluginsMap = {},
} = useContext(pipelineContext)
const currentStepData: StepType = formData[activeStageName].steps[selectedTaskIndex]
@@ -305,8 +301,7 @@ const CreatePluginModal = ({ handleClose }: CreatePluginModalProps) => {
},
{} as Record,
)
- const { parentPluginId, inputVariables, outputVariables } =
- updatedPluginDataStore.pluginVersionStore[pluginVersionId]
+ const { inputVariables, outputVariables } = updatedPluginDataStore.pluginVersionStore[pluginVersionId]
const selectedTask: StepType = clonedFormData[activeStageName].steps[selectedTaskIndex]
selectedTask.name = pluginForm.name
@@ -325,14 +320,6 @@ const CreatePluginModal = ({ handleClose }: CreatePluginModalProps) => {
outputVariables: outputVariables || [],
conditionDetails: [],
}
- selectedTask.isMandatory = isRequired?.(
- clonedFormData,
- mandatoryPluginsMap,
- activeStageName,
- parentPluginId,
- updatedPluginDataStore,
- false,
- )
calculateLastStepDetail(false, clonedFormData, activeStageName)
validateStage(BuildStageVariable.PreBuild, clonedFormData, undefined, updatedPluginDataStore)
validateStage(BuildStageVariable.PostBuild, clonedFormData, undefined, updatedPluginDataStore)
diff --git a/src/components/CIPipelineN/CreatePluginModal/CreatePluginModal.scss b/apps/web/src/components/CIPipelineN/CreatePluginModal/CreatePluginModal.scss
similarity index 100%
rename from src/components/CIPipelineN/CreatePluginModal/CreatePluginModal.scss
rename to apps/web/src/components/CIPipelineN/CreatePluginModal/CreatePluginModal.scss
diff --git a/src/components/CIPipelineN/CreatePluginModal/constants.ts b/apps/web/src/components/CIPipelineN/CreatePluginModal/constants.ts
similarity index 100%
rename from src/components/CIPipelineN/CreatePluginModal/constants.ts
rename to apps/web/src/components/CIPipelineN/CreatePluginModal/constants.ts
diff --git a/src/components/CIPipelineN/CreatePluginModal/index.ts b/apps/web/src/components/CIPipelineN/CreatePluginModal/index.ts
similarity index 100%
rename from src/components/CIPipelineN/CreatePluginModal/index.ts
rename to apps/web/src/components/CIPipelineN/CreatePluginModal/index.ts
diff --git a/src/components/CIPipelineN/CreatePluginModal/service.tsx b/apps/web/src/components/CIPipelineN/CreatePluginModal/service.tsx
similarity index 95%
rename from src/components/CIPipelineN/CreatePluginModal/service.tsx
rename to apps/web/src/components/CIPipelineN/CreatePluginModal/service.tsx
index ead75710ad..b7449169de 100644
--- a/src/components/CIPipelineN/CreatePluginModal/service.tsx
+++ b/apps/web/src/components/CIPipelineN/CreatePluginModal/service.tsx
@@ -16,7 +16,7 @@ import { getCreatePluginPayload } from './utils'
export const getParentPluginList = async (appId: number): Promise => {
try {
- const { result } = await getParentPluginListService(appId)
+ const { result } = await getParentPluginListService({ appId })
if (!result) {
return []
}
diff --git a/src/components/CIPipelineN/CreatePluginModal/types.ts b/apps/web/src/components/CIPipelineN/CreatePluginModal/types.ts
similarity index 100%
rename from src/components/CIPipelineN/CreatePluginModal/types.ts
rename to apps/web/src/components/CIPipelineN/CreatePluginModal/types.ts
diff --git a/src/components/CIPipelineN/CreatePluginModal/utils.tsx b/apps/web/src/components/CIPipelineN/CreatePluginModal/utils.tsx
similarity index 100%
rename from src/components/CIPipelineN/CreatePluginModal/utils.tsx
rename to apps/web/src/components/CIPipelineN/CreatePluginModal/utils.tsx
diff --git a/src/components/CIPipelineN/CustomImageTag.type.ts b/apps/web/src/components/CIPipelineN/CustomImageTag.type.ts
similarity index 100%
rename from src/components/CIPipelineN/CustomImageTag.type.ts
rename to apps/web/src/components/CIPipelineN/CustomImageTag.type.ts
diff --git a/src/components/CIPipelineN/CustomImageTags.tsx b/apps/web/src/components/CIPipelineN/CustomImageTags.tsx
similarity index 100%
rename from src/components/CIPipelineN/CustomImageTags.tsx
rename to apps/web/src/components/CIPipelineN/CustomImageTags.tsx
diff --git a/src/components/CIPipelineN/CustomInputOutputVariables.tsx b/apps/web/src/components/CIPipelineN/CustomInputOutputVariables.tsx
similarity index 100%
rename from src/components/CIPipelineN/CustomInputOutputVariables.tsx
rename to apps/web/src/components/CIPipelineN/CustomInputOutputVariables.tsx
diff --git a/src/components/CIPipelineN/CustomInputVariableSelect.tsx b/apps/web/src/components/CIPipelineN/CustomInputVariableSelect.tsx
similarity index 100%
rename from src/components/CIPipelineN/CustomInputVariableSelect.tsx
rename to apps/web/src/components/CIPipelineN/CustomInputVariableSelect.tsx
diff --git a/src/components/CIPipelineN/CustomScript.tsx b/apps/web/src/components/CIPipelineN/CustomScript.tsx
similarity index 100%
rename from src/components/CIPipelineN/CustomScript.tsx
rename to apps/web/src/components/CIPipelineN/CustomScript.tsx
diff --git a/src/components/CIPipelineN/CustomScriptCard.tsx b/apps/web/src/components/CIPipelineN/CustomScriptCard.tsx
similarity index 100%
rename from src/components/CIPipelineN/CustomScriptCard.tsx
rename to apps/web/src/components/CIPipelineN/CustomScriptCard.tsx
diff --git a/src/components/CIPipelineN/DockerArgs.tsx b/apps/web/src/components/CIPipelineN/DockerArgs.tsx
similarity index 100%
rename from src/components/CIPipelineN/DockerArgs.tsx
rename to apps/web/src/components/CIPipelineN/DockerArgs.tsx
diff --git a/src/components/CIPipelineN/EnvironmentList.tsx b/apps/web/src/components/CIPipelineN/EnvironmentList.tsx
similarity index 100%
rename from src/components/CIPipelineN/EnvironmentList.tsx
rename to apps/web/src/components/CIPipelineN/EnvironmentList.tsx
diff --git a/src/components/CIPipelineN/InputPluginSelect.tsx b/apps/web/src/components/CIPipelineN/InputPluginSelect.tsx
similarity index 100%
rename from src/components/CIPipelineN/InputPluginSelect.tsx
rename to apps/web/src/components/CIPipelineN/InputPluginSelect.tsx
diff --git a/src/components/CIPipelineN/MountFromHost.tsx b/apps/web/src/components/CIPipelineN/MountFromHost.tsx
similarity index 100%
rename from src/components/CIPipelineN/MountFromHost.tsx
rename to apps/web/src/components/CIPipelineN/MountFromHost.tsx
diff --git a/src/components/CIPipelineN/MultiplsPort.tsx b/apps/web/src/components/CIPipelineN/MultiplsPort.tsx
similarity index 100%
rename from src/components/CIPipelineN/MultiplsPort.tsx
rename to apps/web/src/components/CIPipelineN/MultiplsPort.tsx
diff --git a/src/components/CIPipelineN/OutputDirectoryPath.tsx b/apps/web/src/components/CIPipelineN/OutputDirectoryPath.tsx
similarity index 100%
rename from src/components/CIPipelineN/OutputDirectoryPath.tsx
rename to apps/web/src/components/CIPipelineN/OutputDirectoryPath.tsx
diff --git a/src/components/CIPipelineN/PluginDetailHeader/CreatePluginButton.tsx b/apps/web/src/components/CIPipelineN/PluginDetailHeader/CreatePluginButton.tsx
similarity index 94%
rename from src/components/CIPipelineN/PluginDetailHeader/CreatePluginButton.tsx
rename to apps/web/src/components/CIPipelineN/PluginDetailHeader/CreatePluginButton.tsx
index 3b403b7431..4a18c48f13 100644
--- a/src/components/CIPipelineN/PluginDetailHeader/CreatePluginButton.tsx
+++ b/apps/web/src/components/CIPipelineN/PluginDetailHeader/CreatePluginButton.tsx
@@ -1,4 +1,5 @@
import { useContext, useState } from 'react'
+import ReactGA from 'react-ga4'
import Tippy from '@tippyjs/react'
import { pipelineContext } from '@Components/workflowEditor/workflowEditor'
import { ReactComponent as ICSave } from '@Icons/ic-save.svg'
@@ -11,6 +12,11 @@ const CreatePluginButton = () => {
const [openCreatePluginModal, setOpenCreatePluginModal] = useState(false)
const handleOpenCreatePluginModal = () => {
+ ReactGA.event({
+ category: 'devtronapp-configuration-pipeline',
+ action: 'save-as-plugin',
+ })
+
// Before opening the modal we will check if the given task is valid, if not would just showError
const clonedFormErrorObj = structuredClone(formDataErrorObj)
validateTask(
diff --git a/src/components/CIPipelineN/PluginDetailHeader/PluginDetailHeader.component.tsx b/apps/web/src/components/CIPipelineN/PluginDetailHeader/PluginDetailHeader.component.tsx
similarity index 100%
rename from src/components/CIPipelineN/PluginDetailHeader/PluginDetailHeader.component.tsx
rename to apps/web/src/components/CIPipelineN/PluginDetailHeader/PluginDetailHeader.component.tsx
diff --git a/src/components/CIPipelineN/PluginDetailHeader/PluginVersionSelect.tsx b/apps/web/src/components/CIPipelineN/PluginDetailHeader/PluginVersionSelect.tsx
similarity index 90%
rename from src/components/CIPipelineN/PluginDetailHeader/PluginVersionSelect.tsx
rename to apps/web/src/components/CIPipelineN/PluginDetailHeader/PluginVersionSelect.tsx
index 03fcbeb775..10cf2b4269 100644
--- a/src/components/CIPipelineN/PluginDetailHeader/PluginVersionSelect.tsx
+++ b/apps/web/src/components/CIPipelineN/PluginDetailHeader/PluginVersionSelect.tsx
@@ -15,8 +15,9 @@ const PluginVersionSelect = ({ handlePluginVersionChange }: PluginVersionSelectP
const { formData, activeStageName, selectedTaskIndex, pluginDataStore } = useContext(pipelineContext)
const selectedPluginId = formData[activeStageName].steps[selectedTaskIndex].pluginRefStepDetail.pluginId
- const { parentPluginId, id, isLatest, pluginVersion } = pluginDataStore.pluginVersionStore[selectedPluginId]
- const pluginVersionList = pluginDataStore.parentPluginStore[parentPluginId].pluginVersions
+ const { parentPluginId, id, pluginVersion } = pluginDataStore.pluginVersionStore[selectedPluginId]
+ const { pluginVersions: pluginVersionList, latestVersionId } = pluginDataStore.parentPluginStore[parentPluginId]
+ const isLatest = latestVersionId === selectedPluginId
const options: SelectPickerOptionType[] = pluginVersionList.map((plugin) =>
getPluginVersionSelectOption(plugin.pluginVersion, plugin.id, plugin.isLatest),
)
diff --git a/src/components/CIPipelineN/PluginDetailHeader/index.ts b/apps/web/src/components/CIPipelineN/PluginDetailHeader/index.ts
similarity index 100%
rename from src/components/CIPipelineN/PluginDetailHeader/index.ts
rename to apps/web/src/components/CIPipelineN/PluginDetailHeader/index.ts
diff --git a/src/components/CIPipelineN/PluginDetailHeader/utils.ts b/apps/web/src/components/CIPipelineN/PluginDetailHeader/utils.ts
similarity index 100%
rename from src/components/CIPipelineN/PluginDetailHeader/utils.ts
rename to apps/web/src/components/CIPipelineN/PluginDetailHeader/utils.ts
diff --git a/src/components/CIPipelineN/PreBuild.tsx b/apps/web/src/components/CIPipelineN/PreBuild.tsx
similarity index 91%
rename from src/components/CIPipelineN/PreBuild.tsx
rename to apps/web/src/components/CIPipelineN/PreBuild.tsx
index 0a9db5d323..8f82c5fe5b 100644
--- a/src/components/CIPipelineN/PreBuild.tsx
+++ b/apps/web/src/components/CIPipelineN/PreBuild.tsx
@@ -34,10 +34,8 @@ import { BuildStageVariable, ViewType } from '../../config'
import { ReactComponent as Add } from '../../assets/icons/ic-add.svg'
import { TaskDetailComponent } from './TaskDetailComponent'
import nojobs from '../../assets/img/empty-joblist@2x.png'
-import { importComponentFromFELibrary } from '../common'
import { pipelineContext } from '../workflowEditor/workflowEditor'
-const isRequired = importComponentFromFELibrary('isRequired', null, 'function')
export const PreBuild: React.FC = ({ isJobView }) => {
const {
formData,
@@ -50,13 +48,12 @@ export const PreBuild: React.FC = ({ isJobView }) => {
formDataErrorObj,
setFormDataErrorObj,
calculateLastStepDetail,
- validateStage,
pageState,
pluginDataStore,
handlePluginDataStoreUpdate,
availableTags,
handleUpdateAvailableTags,
- mandatoryPluginsMap = {},
+ handleValidateMandatoryPlugins,
} = useContext(pipelineContext)
useEffect(() => {
@@ -83,7 +80,7 @@ export const PreBuild: React.FC = ({ isJobView }) => {
): void {
const _form = { ...formData }
const _formDataErrorObj = { ...formDataErrorObj }
- let isPluginRequired = false
+
_form[activeStageName].steps[selectedTaskIndex].stepType = pluginType
if (pluginType === PluginType.INLINE) {
_form[activeStageName].steps[selectedTaskIndex].inlineStepDetail = {
@@ -107,15 +104,8 @@ export const PreBuild: React.FC = ({ isJobView }) => {
inlineStepDetail: { inputVariables: [], outputVariables: [] },
}
} else {
- const parentPluginId = pluginDataStore.pluginVersionStore[pluginId].parentPluginId
- isPluginRequired =
- !isJobView &&
- isRequired &&
- !isCdPipeline &&
- isRequired(formData, mandatoryPluginsMap, activeStageName, parentPluginId, pluginDataStore, false)
_form[activeStageName].steps[selectedTaskIndex].description = pluginDescription
_form[activeStageName].steps[selectedTaskIndex].name = pluginName
- _form[activeStageName].steps[selectedTaskIndex].isMandatory = isPluginRequired
_form[activeStageName].steps[selectedTaskIndex].pluginRefStepDetail = {
id: 0,
pluginId,
@@ -132,11 +122,11 @@ export const PreBuild: React.FC = ({ isJobView }) => {
}
}
setFormData(_form)
- if (isPluginRequired) {
- validateStage(activeStageName, _form, _formDataErrorObj)
- } else {
- setFormDataErrorObj(_formDataErrorObj)
- }
+ setFormDataErrorObj(_formDataErrorObj)
+
+ handleValidateMandatoryPlugins({
+ newFormData: _form,
+ })
}
const handlePluginSelection = (parentPluginId: number) => {
diff --git a/src/components/CIPipelineN/Sidebar.tsx b/apps/web/src/components/CIPipelineN/Sidebar.tsx
similarity index 98%
rename from src/components/CIPipelineN/Sidebar.tsx
rename to apps/web/src/components/CIPipelineN/Sidebar.tsx
index 376e36fd8a..2c920cd56c 100644
--- a/src/components/CIPipelineN/Sidebar.tsx
+++ b/apps/web/src/components/CIPipelineN/Sidebar.tsx
@@ -21,6 +21,7 @@ import {
CHECKBOX_VALUE,
PipelineFormType,
SelectPicker,
+ ResourceKindType,
} from '@devtron-labs/devtron-fe-common-lib'
import Tippy from '@tippyjs/react'
import { BuildStageVariable, DOCUMENTATION, TriggerType } from '../../config'
@@ -40,7 +41,6 @@ const MandatoryPluginWarning = importComponentFromFELibrary('MandatoryPluginWarn
export const Sidebar = ({
isJobView,
isJobCI,
- mandatoryPluginData,
setInputVariablesListFromPrevStep,
environments,
selectedEnv,
@@ -59,7 +59,7 @@ export const Sidebar = ({
configMapAndSecrets,
isVirtualEnvironment,
getPrePostStageInEnv,
- pluginDataStore,
+ mandatoryPluginData,
} = useContext(pipelineContext)
const [addConfigSecret, setAddConfigSecret] = useState(false)
@@ -88,6 +88,8 @@ export const Sidebar = ({
const showMandatoryWarning = (): boolean => {
return (
+ !!MandatoryPluginWarning &&
+ !isJobCard &&
mandatoryPluginData &&
((isPreBuildTab && !mandatoryPluginData.isValidPre) ||
(activeStageName === BuildStageVariable.PostBuild && !mandatoryPluginData.isValidPost))
@@ -311,16 +313,16 @@ export const Sidebar = ({
{activeStageName !== BuildStageVariable.Build ? (
- {!isCdPipeline && !isJobCard && MandatoryPluginWarning && showMandatoryWarning() && (
+ {showMandatoryWarning() && (
)}
Tasks (IN ORDER OF EXECUTION)
diff --git a/src/components/CIPipelineN/TaskDetailComponent.tsx b/apps/web/src/components/CIPipelineN/TaskDetailComponent.tsx
similarity index 100%
rename from src/components/CIPipelineN/TaskDetailComponent.tsx
rename to apps/web/src/components/CIPipelineN/TaskDetailComponent.tsx
diff --git a/src/components/CIPipelineN/TaskFieldTippyDescription.tsx b/apps/web/src/components/CIPipelineN/TaskFieldTippyDescription.tsx
similarity index 100%
rename from src/components/CIPipelineN/TaskFieldTippyDescription.tsx
rename to apps/web/src/components/CIPipelineN/TaskFieldTippyDescription.tsx
diff --git a/src/components/CIPipelineN/TaskList.tsx b/apps/web/src/components/CIPipelineN/TaskList.tsx
similarity index 72%
rename from src/components/CIPipelineN/TaskList.tsx
rename to apps/web/src/components/CIPipelineN/TaskList.tsx
index a240203fa1..30f53a5628 100644
--- a/src/components/CIPipelineN/TaskList.tsx
+++ b/apps/web/src/components/CIPipelineN/TaskList.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { useState, useContext, Fragment } from 'react'
+import { useState, useContext, Fragment, SyntheticEvent } from 'react'
import {
PopupMenu,
BuildStageVariable,
@@ -22,7 +22,13 @@ import {
RefVariableStageType,
RefVariableType,
PipelineFormType,
+ ValidationResponseType,
+ StepType,
+ PipelineStageTaskActionModalType,
+ PipelineStageTaskActionModalStateType,
+ ResourceKindType,
} from '@devtron-labs/devtron-fe-common-lib'
+import { importComponentFromFELibrary } from '@Components/common'
import TaskTitle from './TaskTitle'
import { ReactComponent as Add } from '../../assets/icons/ic-add.svg'
import { ReactComponent as Drag } from '../../assets/icons/drag.svg'
@@ -31,20 +37,24 @@ import { ReactComponent as Trash } from '../../assets/icons/ic-delete-interactiv
import { ReactComponent as AlertTriangle } from '../../assets/icons/ic-alert-triangle.svg'
import { ReactComponent as MoveToPre } from '../../assets/icons/ic-arrow-backward.svg'
import { TaskListType } from '../ciConfig/types'
-import { importComponentFromFELibrary } from '../common'
import { pipelineContext } from '../workflowEditor/workflowEditor'
-const MandatoryPluginMenuOptionTippy = importComponentFromFELibrary('MandatoryPluginMenuOptionTippy')
-const isRequired = importComponentFromFELibrary('isRequired', null, 'function')
-export const TaskList = ({
- withWarning,
- setInputVariablesListFromPrevStep,
- isJobView,
-}: TaskListType) => {
+const getTaskActionPluginValidationStatus: (params) => ValidationResponseType = importComponentFromFELibrary(
+ 'getTaskActionPluginValidationStatus',
+ null,
+ 'function',
+)
+
+const PipelineTaskActionConfirmationDialog = importComponentFromFELibrary(
+ 'PipelineTaskActionConfirmationDialog',
+ null,
+ 'function',
+)
+
+export const TaskList = ({ withWarning, setInputVariablesListFromPrevStep, isJobView }: TaskListType) => {
const {
formData,
setFormData,
- isCdPipeline,
addNewTask,
activeStageName,
selectedTaskIndex,
@@ -54,12 +64,16 @@ export const TaskList = ({
setFormDataErrorObj,
validateTask,
validateStage,
+ handleValidateMandatoryPlugins,
+ isCdPipeline,
pluginDataStore,
- mandatoryPluginsMap = {},
+ mandatoryPluginData,
} = useContext(pipelineContext)
const [dragItemStartIndex, setDragItemStartIndex] = useState
(0)
const [dragItemIndex, setDragItemIndex] = useState(0)
const [dragAllowed, setDragAllowed] = useState(false)
+ const [taskActionModalState, setTaskActionModalState] = useState(null)
+
const handleDragStart = (index: number): void => {
setDragItemIndex(index)
setDragItemStartIndex(index)
@@ -98,29 +112,11 @@ export const TaskList = ({
setDragItemStartIndex(index)
}
- const deleteTask = (e): void => {
- const taskIndex = +e.currentTarget.dataset.index
+ const deleteTask = (taskIndex: number): void => {
const _formData = { ...formData }
const newList = [..._formData[activeStageName].steps]
- const _taskDetail = newList.splice(taskIndex, 1)
- let isMandatoryMissing = false
- if (_taskDetail[0].isMandatory) {
- isMandatoryMissing = true
- const deletedTaskPluginId = _taskDetail[0].pluginRefStepDetail.pluginId
- const deletedTaskParentPluginId = pluginDataStore.pluginVersionStore[deletedTaskPluginId]?.parentPluginId
-
- for (const task of newList) {
- const currentTaskPluginId = task.pluginRefStepDetail?.pluginId
- const currentTaskParentPluginId =
- pluginDataStore.pluginVersionStore[currentTaskPluginId]?.parentPluginId
+ newList.splice(taskIndex, 1)
- if (currentTaskParentPluginId === deletedTaskParentPluginId) {
- task.isMandatory = true
- isMandatoryMissing = false
- break
- }
- }
- }
_formData[activeStageName].steps = newList
const newListLength = newList.length
const newListIndex = newListLength > 1 ? newListLength - 1 : 0
@@ -139,44 +135,21 @@ export const TaskList = ({
newErrorList.splice(taskIndex, 1)
_formDataErrorObj[activeStageName].steps = newErrorList
- if (isMandatoryMissing) {
- validateStage(activeStageName, _formData, _formDataErrorObj)
- } else {
- setFormDataErrorObj(_formDataErrorObj)
- }
+ setFormDataErrorObj(_formDataErrorObj)
+ handleValidateMandatoryPlugins({
+ newFormData: _formData,
+ })
}
- const moveTaskToOtherStage = (e): void => {
- const taskIndex = +e.currentTarget.dataset.index
+ const moveTaskToOtherStage = (taskIndex: number): void => {
const moveToStage =
activeStageName === BuildStageVariable.PreBuild ? BuildStageVariable.PostBuild : BuildStageVariable.PreBuild
const _formData = { ...formData }
const newList = [..._formData[activeStageName].steps]
const _taskDetail = newList.splice(taskIndex, 1)
- let isMandatoryMissing = false
+
if (_taskDetail[0].pluginRefStepDetail) {
const pluginId = _taskDetail[0].pluginRefStepDetail.pluginId
- const parentPluginId = pluginDataStore.pluginVersionStore[pluginId]?.parentPluginId
- const isPluginRequired =
- !isJobView &&
- isRequired &&
- !isCdPipeline &&
- isRequired(_formData, mandatoryPluginsMap, moveToStage, parentPluginId, pluginDataStore, true)
- if (_taskDetail[0].isMandatory && !isPluginRequired) {
- isMandatoryMissing = true
- for (const task of newList) {
- const taskParentPluginId =
- pluginDataStore.pluginVersionStore[task.pluginRefStepDetail?.pluginId]?.parentPluginId
- if (!!parentPluginId && !!taskParentPluginId && taskParentPluginId === parentPluginId) {
- task.isMandatory = true
- isMandatoryMissing = false
- break
- }
- }
- _taskDetail[0].isMandatory = false
- } else {
- _taskDetail[0].isMandatory = isPluginRequired
- }
_taskDetail[0].pluginRefStepDetail = {
id: 0,
@@ -212,12 +185,63 @@ export const TaskList = ({
} else {
setFormData(_formData)
}
- if (isMandatoryMissing) {
- validateStage(activeStageName, _formData, _formDataErrorObj)
- } else {
- validateTask(formData[moveToStage].steps[taskIndex], _formDataErrorObj[moveToStage].steps[taskIndex])
- setFormDataErrorObj(_formDataErrorObj)
+
+ // FIXME: This is wrong ideally should be done on _formData[moveToStage].steps[_formData[moveToStage].steps.length-1]
+ validateTask(formData[moveToStage].steps[taskIndex], _formDataErrorObj[moveToStage].steps[taskIndex])
+ setFormDataErrorObj(_formDataErrorObj)
+ handleValidateMandatoryPlugins({
+ newFormData: _formData,
+ })
+ }
+
+ const handleTaskAction = (taskIndex: number, taskType: PipelineStageTaskActionModalType) => {
+ const taskDetails: StepType = formData[activeStageName].steps[taskIndex]
+
+ if (
+ getTaskActionPluginValidationStatus &&
+ taskDetails?.stepType === PluginType.PLUGIN_REF &&
+ taskDetails.pluginRefStepDetail?.pluginId
+ ) {
+ const pluginId = taskDetails.pluginRefStepDetail.pluginId
+
+ const pluginValidationStatus = getTaskActionPluginValidationStatus({
+ mandatoryPluginList: mandatoryPluginData?.pluginData || [],
+ formData,
+ targetPluginId: pluginId,
+ activeStageName,
+ pluginDataStore,
+ isFromMoveTask: taskType === PipelineStageTaskActionModalType.MOVE_PLUGIN,
+ })
+
+ if (!pluginValidationStatus.isValid) {
+ setTaskActionModalState({
+ type: taskType,
+ pluginId,
+ taskIndex,
+ })
+ return
+ }
}
+
+ if (taskType === PipelineStageTaskActionModalType.MOVE_PLUGIN) {
+ moveTaskToOtherStage(taskIndex)
+ } else if (taskType === PipelineStageTaskActionModalType.DELETE) {
+ deleteTask(taskIndex)
+ }
+ }
+
+ const handleTriggerDelete = (e: SyntheticEvent) => {
+ const taskIndex = +(e.currentTarget as HTMLButtonElement).dataset.index
+ handleTaskAction(taskIndex, PipelineStageTaskActionModalType.DELETE)
+ }
+
+ const handleTriggerMoveToOtherStage = (e: SyntheticEvent): void => {
+ const taskIndex = +(e.currentTarget as HTMLButtonElement).dataset.index
+ handleTaskAction(taskIndex, PipelineStageTaskActionModalType.MOVE_PLUGIN)
+ }
+
+ const handleClearTaskActionModalState = () => {
+ setTaskActionModalState(null)
}
const clearDependentPostVariables = (
@@ -311,6 +335,8 @@ export const TaskList = ({
setSelectedTaskIndex(index)
}
+ const currentStageText = isCdPipeline ? 'deploy' : 'build'
+
return (
<>
@@ -327,10 +353,12 @@ export const TaskList = ({
onDragOver={(e) => e.preventDefault()}
onClick={() => handleSelectedTaskChange(index)}
>
-
setDragAllowed(true)} />
+ setDragAllowed(true)}
+ />
- {taskDetail.isMandatory && * }
{formDataErrorObj[activeStageName].steps[index] &&
!formDataErrorObj[activeStageName].steps[index].isValid && (
@@ -344,56 +372,58 @@ export const TaskList = ({
/>
-
-
+
Remove
-
+
{!isJobView && taskDetail.stepType && (
-
{activeStageName === BuildStageVariable.PreBuild ? (
<>
- Move to post-build stage
+ Move to post-{currentStageText} stage
>
) : (
<>
-
- Move to pre-build stage
+
+ Move to pre-{currentStageText} stage
>
)}
-
+
)}
- {!isJobView &&
- !isCdPipeline &&
- taskDetail.isMandatory &&
- MandatoryPluginMenuOptionTippy && (
-
- )}
))}
+
+ {PipelineTaskActionConfirmationDialog && taskActionModalState && (
+
+ )}
{
const { pluginDataStore } = useContext(pipelineContext)
const isInline = taskDetail.stepType === PluginType.INLINE
const pluginId = taskDetail.pluginRefStepDetail?.pluginId
- const { isLatest, icon, name: pluginName, pluginVersion } = pluginDataStore.pluginVersionStore?.[pluginId] || {}
+ const {
+ icon,
+ name: pluginName,
+ pluginVersion,
+ parentPluginId,
+ } = pluginDataStore.pluginVersionStore?.[pluginId] || {}
+
+ const isLatest = pluginDataStore.parentPluginStore?.[parentPluginId]?.latestVersionId === pluginId
const renderPluginImageContainer = () => (
{
const errorObj =
formDataErrorObj[activeStageName].steps[selectedTaskIndex]?.pluginRefStepDetail
.inputVariables[index]
+
+ const isInputVariableRequired = type === PluginVariableType.INPUT && !variable.allowEmptyValue
return (
{type === PluginVariableType.INPUT && variable.description ? (
@@ -100,7 +102,7 @@ export const VariableContainer = ({ type }: { type: PluginVariableType }) => {
>
{
) : (
-
{variable.name}
+
{variable.name}
)}
{variable.format}
diff --git a/src/components/CIPipelineN/ciPipeline.utils.tsx b/apps/web/src/components/CIPipelineN/ciPipeline.utils.tsx
similarity index 100%
rename from src/components/CIPipelineN/ciPipeline.utils.tsx
rename to apps/web/src/components/CIPipelineN/ciPipeline.utils.tsx
diff --git a/src/components/CIPipelineN/types.ts b/apps/web/src/components/CIPipelineN/types.ts
similarity index 94%
rename from src/components/CIPipelineN/types.ts
rename to apps/web/src/components/CIPipelineN/types.ts
index 7b24f2f68b..5769a0adbf 100644
--- a/src/components/CIPipelineN/types.ts
+++ b/apps/web/src/components/CIPipelineN/types.ts
@@ -16,11 +16,11 @@
import {
OptionType,
- PluginDetailType,
StepType,
PipelineFormType,
Environment,
SelectPickerOptionType,
+ ParentPluginType,
} from '@devtron-labs/devtron-fe-common-lib'
import { ExtendedOptionType } from '@Components/app/types'
@@ -62,7 +62,9 @@ export interface PluginDetailHeaderProps {
export interface PluginVersionSelectProps extends PluginDetailHeaderProps {}
-export interface PluginVersionSelectOptionType extends OptionType
, Pick {}
+export interface PluginVersionSelectOptionType
+ extends OptionType,
+ Pick {}
export interface TaskDetailComponentParamsType {
appId: string
}
diff --git a/src/components/ClusterNodes/ClusterEvents.tsx b/apps/web/src/components/ClusterNodes/ClusterEvents.tsx
similarity index 100%
rename from src/components/ClusterNodes/ClusterEvents.tsx
rename to apps/web/src/components/ClusterNodes/ClusterEvents.tsx
diff --git a/src/components/ClusterNodes/ClusterManifest.tsx b/apps/web/src/components/ClusterNodes/ClusterManifest.tsx
similarity index 100%
rename from src/components/ClusterNodes/ClusterManifest.tsx
rename to apps/web/src/components/ClusterNodes/ClusterManifest.tsx
diff --git a/src/components/ClusterNodes/ClusterNodeEmptyStates.tsx b/apps/web/src/components/ClusterNodes/ClusterNodeEmptyStates.tsx
similarity index 100%
rename from src/components/ClusterNodes/ClusterNodeEmptyStates.tsx
rename to apps/web/src/components/ClusterNodes/ClusterNodeEmptyStates.tsx
diff --git a/src/components/ClusterNodes/ClusterOverview.tsx b/apps/web/src/components/ClusterNodes/ClusterOverview.tsx
similarity index 100%
rename from src/components/ClusterNodes/ClusterOverview.tsx
rename to apps/web/src/components/ClusterNodes/ClusterOverview.tsx
diff --git a/src/components/ClusterNodes/ClusterSelectionList.tsx b/apps/web/src/components/ClusterNodes/ClusterSelectionList.tsx
similarity index 100%
rename from src/components/ClusterNodes/ClusterSelectionList.tsx
rename to apps/web/src/components/ClusterNodes/ClusterSelectionList.tsx
diff --git a/src/components/ClusterNodes/ClusterTerminal.tsx b/apps/web/src/components/ClusterNodes/ClusterTerminal.tsx
similarity index 100%
rename from src/components/ClusterNodes/ClusterTerminal.tsx
rename to apps/web/src/components/ClusterNodes/ClusterTerminal.tsx
diff --git a/src/components/ClusterNodes/ColumnSelector.tsx b/apps/web/src/components/ClusterNodes/ColumnSelector.tsx
similarity index 100%
rename from src/components/ClusterNodes/ColumnSelector.tsx
rename to apps/web/src/components/ClusterNodes/ColumnSelector.tsx
diff --git a/src/components/ClusterNodes/NodeActions/CordonNodeModal.tsx b/apps/web/src/components/ClusterNodes/NodeActions/CordonNodeModal.tsx
similarity index 100%
rename from src/components/ClusterNodes/NodeActions/CordonNodeModal.tsx
rename to apps/web/src/components/ClusterNodes/NodeActions/CordonNodeModal.tsx
diff --git a/src/components/ClusterNodes/NodeActions/DeleteNodeModal.tsx b/apps/web/src/components/ClusterNodes/NodeActions/DeleteNodeModal.tsx
similarity index 100%
rename from src/components/ClusterNodes/NodeActions/DeleteNodeModal.tsx
rename to apps/web/src/components/ClusterNodes/NodeActions/DeleteNodeModal.tsx
diff --git a/src/components/ClusterNodes/NodeActions/DrainNodeModal.tsx b/apps/web/src/components/ClusterNodes/NodeActions/DrainNodeModal.tsx
similarity index 100%
rename from src/components/ClusterNodes/NodeActions/DrainNodeModal.tsx
rename to apps/web/src/components/ClusterNodes/NodeActions/DrainNodeModal.tsx
diff --git a/src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx b/apps/web/src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx
similarity index 100%
rename from src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx
rename to apps/web/src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx
diff --git a/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx b/apps/web/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx
similarity index 100%
rename from src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx
rename to apps/web/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx
diff --git a/src/components/ClusterNodes/NodeActions/validationRules.ts b/apps/web/src/components/ClusterNodes/NodeActions/validationRules.ts
similarity index 100%
rename from src/components/ClusterNodes/NodeActions/validationRules.ts
rename to apps/web/src/components/ClusterNodes/NodeActions/validationRules.ts
diff --git a/src/components/ClusterNodes/NodeDetails.tsx b/apps/web/src/components/ClusterNodes/NodeDetails.tsx
similarity index 100%
rename from src/components/ClusterNodes/NodeDetails.tsx
rename to apps/web/src/components/ClusterNodes/NodeDetails.tsx
diff --git a/src/components/ClusterNodes/NodeDetailsList.tsx b/apps/web/src/components/ClusterNodes/NodeDetailsList.tsx
similarity index 98%
rename from src/components/ClusterNodes/NodeDetailsList.tsx
rename to apps/web/src/components/ClusterNodes/NodeDetailsList.tsx
index 91cecb63f5..a83eaced5d 100644
--- a/src/components/ClusterNodes/NodeDetailsList.tsx
+++ b/apps/web/src/components/ClusterNodes/NodeDetailsList.tsx
@@ -30,6 +30,7 @@ import {
SortingOrder,
Tooltip,
ClipboardButton,
+ useResizableTableConfig,
} from '@devtron-labs/devtron-fe-common-lib'
import { getNodeList, getClusterCapacity } from './clusterNodes.service'
import 'react-mde/lib/styles/css/react-mde-all.css'
@@ -76,6 +77,13 @@ export default function NodeDetailsList({ isSuperAdmin, renderRefreshBar, addTab
const [appliedColumns, setAppliedColumns] = useState>([])
const abortControllerRef = useRef(new AbortController())
const nodeListRef = useRef(null)
+ const { gridTemplateColumns, handleResize } = useResizableTableConfig({
+ headersConfig: appliedColumns.map((column, index) => ({
+ id: column.label,
+ minWidth: index === 0 ? 120 : null,
+ width: index === 0 ? 260 : index === 1 ? 180 : 120,
+ })),
+ })
const [, nodeK8sVersions] = useAsync(
() =>
@@ -365,6 +373,9 @@ export default function NodeDetailsList({ isSuperAdmin, renderRefreshBar, addTab
const renderNodeListHeader = (column: ColumnMetadataType): JSX.Element => (
{
return (
(
diff --git a/src/components/ResourceBrowser/ResourceBrowser.scss b/apps/web/src/components/ResourceBrowser/ResourceBrowser.scss
similarity index 96%
rename from src/components/ResourceBrowser/ResourceBrowser.scss
rename to apps/web/src/components/ResourceBrowser/ResourceBrowser.scss
index c7ac9e265b..f798b9d23f 100644
--- a/src/components/ResourceBrowser/ResourceBrowser.scss
+++ b/apps/web/src/components/ResourceBrowser/ResourceBrowser.scss
@@ -104,14 +104,6 @@
}
.resource-filter-options-container {
- .resource-filter-options-wrapper {
- .resource-filter-select__input-container {
- input {
- max-width: 150px;
- }
- }
- }
-
&__search-box {
width: 250px;
.resource-search-shortcut-key {
diff --git a/src/components/ResourceBrowser/ResourceBrowser.service.tsx b/apps/web/src/components/ResourceBrowser/ResourceBrowser.service.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceBrowser.service.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceBrowser.service.tsx
diff --git a/src/components/ResourceBrowser/ResourceBrowser.tsx b/apps/web/src/components/ResourceBrowser/ResourceBrowser.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceBrowser.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceBrowser.tsx
diff --git a/src/components/ResourceBrowser/ResourceBrowserRouter.tsx b/apps/web/src/components/ResourceBrowser/ResourceBrowserRouter.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceBrowserRouter.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceBrowserRouter.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/AdminTerminal.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/AdminTerminal.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/AdminTerminal.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/AdminTerminal.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/ClusterSelector.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ClusterSelector.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/ClusterSelector.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ClusterSelector.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/ConnectingToClusterState.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ConnectingToClusterState.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/ConnectingToClusterState.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ConnectingToClusterState.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/CreateResource.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/CreateResource.tsx
similarity index 98%
rename from src/components/ResourceBrowser/ResourceList/CreateResource.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/CreateResource.tsx
index 00a3913548..ff6187cac3 100644
--- a/src/components/ResourceBrowser/ResourceList/CreateResource.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/CreateResource.tsx
@@ -36,7 +36,7 @@ import { createNewResource } from '../ResourceBrowser.service'
import { CREATE_RESOURCE_MODAL_MESSAGING } from '../Constants'
export const CreateResource: React.FC = ({ closePopup, clusterId }) => {
- const { setRegisterShortcut } = useRegisterShortcut()
+ const { setDisableShortcuts } = useRegisterShortcut()
const [showCodeEditorView, toggleCodeEditorView] = useState(true)
const [loader, setLoader] = useState(false)
const [resourceYAML, setResourceYAML] = useState('')
@@ -77,10 +77,10 @@ export const CreateResource: React.FC = ({ closePopup, clust
}, [outsideClickHandler])
useEffect(() => {
- setRegisterShortcut(false)
+ setDisableShortcuts(true)
return () => {
- setRegisterShortcut(true)
+ setDisableShortcuts(false)
}
}, [])
diff --git a/src/components/ResourceBrowser/ResourceList/DeleteResourcePopup.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/DeleteResourcePopup.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/DeleteResourcePopup.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/DeleteResourcePopup.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/EventList.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/EventList.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/EventList.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/EventList.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx
similarity index 97%
rename from src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx
index c6290a4e7c..e9f90084eb 100644
--- a/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx
@@ -33,6 +33,7 @@ import {
useStateFilters,
ClipboardButton,
Tooltip,
+ useResizableTableConfig,
} from '@devtron-labs/devtron-fe-common-lib'
import WebWorker from '../../app/WebWorker'
import searchWorker from '../../../config/searchWorker'
@@ -147,6 +148,14 @@ export const K8SResourceList = ({
return result
}, [_resourceList])
+ const { gridTemplateColumns, handleResize } = useResizableTableConfig({
+ headersConfig: (resourceList?.headers ?? []).map((columnName, index) => ({
+ id: columnName,
+ minWidth: index === 0 ? 120 : null,
+ width: index === 0 ? 350 : 180,
+ })),
+ })
+
const showPaginatedView = resourceList?.data?.length >= pageSize
/**
@@ -279,8 +288,6 @@ export const K8SResourceList = ({
return `f-${statusPostfix}`
}
- const gridTemplateColumns = `350px repeat(${(resourceList?.headers.length ?? 1) - 1}, 180px)`
-
const renderResourceRow = (resourceData: ResourceDetailDataType): JSX.Element => (
))}
diff --git a/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/ResourceBrowserActionMenu.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceBrowserActionMenu.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/ResourceBrowserActionMenu.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ResourceBrowserActionMenu.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/ResourceFilterOptions.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceFilterOptions.tsx
similarity index 65%
rename from src/components/ResourceBrowser/ResourceList/ResourceFilterOptions.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ResourceFilterOptions.tsx
index bc4a19328d..643e40f76f 100644
--- a/src/components/ResourceBrowser/ResourceList/ResourceFilterOptions.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceFilterOptions.tsx
@@ -16,24 +16,10 @@
import { useEffect, useRef, useState, useMemo, ComponentProps, KeyboardEvent } from 'react'
import { useLocation, useParams, useHistory } from 'react-router-dom'
-import ReactSelect from 'react-select'
-import { withShortcut, IWithShortcut } from 'react-keybind'
-import {
- useAsync,
- useRegisterShortcut,
- OptionType,
- SearchBar,
- Option,
- Tooltip,
-} from '@devtron-labs/devtron-fe-common-lib'
+import { useAsync, useRegisterShortcut, OptionType, SearchBar, SelectPicker } from '@devtron-labs/devtron-fe-common-lib'
+import { ReactComponent as NamespaceIcon } from '@Icons/ic-env.svg'
import { ResourceFilterOptionsProps, URLParams } from '../Types'
-import { ResourceValueContainerWithIcon } from './ResourceList.component'
-import {
- ALL_NAMESPACE_OPTION,
- FILTER_SELECT_COMMON_STYLES,
- NAMESPACE_NOT_APPLICABLE_OPTION,
- NAMESPACE_NOT_APPLICABLE_TEXT,
-} from '../Constants'
+import { ALL_NAMESPACE_OPTION, NAMESPACE_NOT_APPLICABLE_OPTION, NAMESPACE_NOT_APPLICABLE_TEXT } from '../Constants'
import { ShortcutKeyBadge } from '../../common/formFields/Widgets/Widgets'
import { convertToOptionsList, importComponentFromFELibrary } from '../../common'
import { namespaceListByClusterId } from '../ResourceBrowser.service'
@@ -50,11 +36,10 @@ const ResourceFilterOptions = ({
isOpen,
setSearchText,
isSearchInputDisabled,
- shortcut,
renderRefreshBar,
updateK8sResourceTab,
-}: ResourceFilterOptionsProps & IWithShortcut) => {
- const { registerShortcut } = useRegisterShortcut()
+}: ResourceFilterOptionsProps) => {
+ const { registerShortcut, unregisterShortcut } = useRegisterShortcut()
const location = useLocation()
const { replace } = useHistory()
const { clusterId, namespace, group } = useParams
()
@@ -81,19 +66,14 @@ const ResourceFilterOptions = ({
useEffect(() => {
if (registerShortcut && isOpen) {
- shortcut.registerShortcut(handleInputShortcut, ['r'], 'ResourceSearchFocus', 'Focus resource search')
- shortcut.registerShortcut(
- handleShowFilterModal,
- ['f'],
- 'ResourceFilterDrawer',
- 'Open resource filter drawer',
- )
+ registerShortcut({ keys: ['R'], callback: handleInputShortcut })
+ registerShortcut({ keys: ['F'], callback: handleShowFilterModal })
}
return (): void => {
- shortcut.unregisterShortcut(['f'])
- shortcut.unregisterShortcut(['r'])
+ unregisterShortcut(['F'])
+ unregisterShortcut(['R'])
}
- }, [registerShortcut, isOpen])
+ }, [isOpen])
const handleFilterKeyUp = (e: KeyboardEvent): void => {
if (e.key === 'Escape' || e.key === 'Esc') {
@@ -155,41 +135,30 @@ const ResourceFilterOptions = ({
/>
)}
-
- {FilterButton && (
-
- )}
-
-
-
+ {FilterButton && (
+
-
-
+ )}
+ }
+ disabledTippyContent={NAMESPACE_NOT_APPLICABLE_TEXT}
+ shouldMenuAlignRight
+ />
+
>
)
}
-export default withShortcut(ResourceFilterOptions)
+export default ResourceFilterOptions
diff --git a/src/components/ResourceBrowser/ResourceList/ResourceList.component.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceList.component.tsx
similarity index 72%
rename from src/components/ResourceBrowser/ResourceList/ResourceList.component.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ResourceList.component.tsx
index 2b9c884fa2..dcb05c18ff 100644
--- a/src/components/ResourceBrowser/ResourceList/ResourceList.component.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceList.component.tsx
@@ -16,8 +16,6 @@
import React, { useState, useEffect } from 'react'
import { components } from 'react-select'
-import { ReactComponent as ClusterIcon } from '../../../assets/icons/ic-cluster.svg'
-import { ReactComponent as NamespaceIcon } from '../../../assets/icons/ic-env.svg'
import { ReactComponent as SearchIcon } from '../../../assets/icons/ic-search.svg'
import { ReactComponent as ClearIcon } from '../../../assets/icons/ic-error.svg'
import { ReactComponent as Warning } from '../../../assets/icons/ic-warning.svg'
@@ -25,39 +23,6 @@ import { handleUTCTime } from '../../common'
import { ShortcutKeyBadge } from '../../common/formFields/Widgets/Widgets'
import { SidebarChildButtonPropsType } from '../Types'
-export const ResourceValueContainerWithIcon = (props) => {
- const { selectProps } = props
- return (
-
- {selectProps.value ? (
- <>
-
-
- {selectProps.placeholder.includes('Cluster') ? (
-
- ) : (
-
- )}
-
- {!selectProps.inputValue && (
- <>
- {selectProps.value.label ? (
- {selectProps.value.label}
- ) : (
- {selectProps.placeholder}
- )}
- >
- )}
-
- {React.cloneElement(props.children[1])}
- >
- ) : (
- <>{props.children}>
- )}
-
- )
-}
-
export const KindSearchValueContainer = (props) => {
const { selectProps } = props
return (
diff --git a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceList.tsx
similarity index 96%
rename from src/components/ResourceBrowser/ResourceList/ResourceList.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ResourceList.tsx
index 5a0506db2b..5c2bab6b6d 100644
--- a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceList.tsx
@@ -25,7 +25,6 @@ import {
useAsync,
useEffectAfterMount,
PageHeader,
- UseRegisterShortcutProvider,
getResourceGroupListRaw,
noop,
} from '@devtron-labs/devtron-fe-common-lib'
@@ -373,17 +372,15 @@ const ResourceList = () => {
}
return (
-
-
-
+
)
}
diff --git a/src/components/ResourceBrowser/ResourceList/ResourceListEmptyState.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/ResourceListEmptyState.tsx
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/ResourceListEmptyState.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/ResourceListEmptyState.tsx
diff --git a/src/components/ResourceBrowser/ResourceList/Sidebar.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/Sidebar.tsx
similarity index 96%
rename from src/components/ResourceBrowser/ResourceList/Sidebar.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/Sidebar.tsx
index f41ec9b069..ac8ceea838 100644
--- a/src/components/ResourceBrowser/ResourceList/Sidebar.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/Sidebar.tsx
@@ -18,7 +18,6 @@ import React, { Fragment, useEffect, useRef, useState, useMemo } from 'react'
import { useLocation, useParams, useHistory } from 'react-router-dom'
import ReactSelect, { InputActionMeta, GroupBase } from 'react-select'
import Select, { FormatOptionLabelMeta } from 'react-select/base'
-import { withShortcut, IWithShortcut } from 'react-keybind'
import DOMPurify from 'dompurify'
import {
highlightSearchText,
@@ -46,9 +45,8 @@ const Sidebar = ({
updateK8sResourceTab,
updateK8sResourceTabLastSyncMoment,
isOpen,
- shortcut,
-}: SidebarType & IWithShortcut) => {
- const { registerShortcut } = useRegisterShortcut()
+}: SidebarType) => {
+ const { registerShortcut, unregisterShortcut } = useRegisterShortcut()
const location = useLocation()
const { push } = useHistory()
const { clusterId, namespace, nodeType } = useParams
()
@@ -78,26 +76,26 @@ const Sidebar = ({
})
}, [searchText])
- const handleInputShortcut = (e: React.KeyboardEvent) => {
- switch (e.key) {
- case 'k':
- searchInputRef.current?.focus()
- break
+ const handleInputShortcut = (e?: React.KeyboardEvent | KeyboardEvent) => {
+ switch (e?.key) {
case 'Escape':
case 'Esc':
searchInputRef.current?.blur()
break
default:
+ searchInputRef.current?.focus()
}
}
useEffect(() => {
- if (registerShortcut && isOpen) {
- shortcut.registerShortcut(handleInputShortcut, ['k'], 'KindSearchFocus', 'Focus kind search')
+ if (isOpen) {
+ registerShortcut({ callback: handleInputShortcut, keys: ['K'] })
}
- return () => shortcut.unregisterShortcut(['k'])
- }, [registerShortcut, isOpen])
+ return () => {
+ unregisterShortcut(['K'])
+ }
+ }, [isOpen])
const getGroupHeadingClickHandler =
(preventCollapse = false, preventScroll = false) =>
@@ -421,4 +419,4 @@ const Sidebar = ({
)
}
-export default withShortcut(Sidebar)
+export default Sidebar
diff --git a/src/components/ResourceBrowser/ResourceList/__tests__/Sidebar.test.tsx b/apps/web/src/components/ResourceBrowser/ResourceList/__tests__/Sidebar.test.tsx
similarity index 95%
rename from src/components/ResourceBrowser/ResourceList/__tests__/Sidebar.test.tsx
rename to apps/web/src/components/ResourceBrowser/ResourceList/__tests__/Sidebar.test.tsx
index d8202f5b75..994b5024c4 100644
--- a/src/components/ResourceBrowser/ResourceList/__tests__/Sidebar.test.tsx
+++ b/apps/web/src/components/ResourceBrowser/ResourceList/__tests__/Sidebar.test.tsx
@@ -24,16 +24,15 @@ import {
PodSelectedResource,
} from '../__mocks__/Sidebar.mock'
import Sidebar from '../Sidebar'
-import { IWithShortcut, ShortcutProvider, withShortcut } from 'react-keybind'
+import { ShortcutProvider } from 'react-keybind'
import { BrowserRouter } from 'react-router-dom'
import { K8SObjectMapType, SidebarType } from '../../Types'
describe('Sidebar component', () => {
- const SidebarComponentWithShortcut = withShortcut((props: SidebarType & IWithShortcut) => )
- const SidebarComponentWithProvider = (props: SidebarType & IWithShortcut) => {
+ const SidebarComponentWithProvider = (props: SidebarType) => {
return (
-
+
)
}
diff --git a/src/components/ResourceBrowser/ResourceList/index.ts b/apps/web/src/components/ResourceBrowser/ResourceList/index.ts
similarity index 100%
rename from src/components/ResourceBrowser/ResourceList/index.ts
rename to apps/web/src/components/ResourceBrowser/ResourceList/index.ts
diff --git a/src/components/ResourceBrowser/Types.ts b/apps/web/src/components/ResourceBrowser/Types.ts
similarity index 100%
rename from src/components/ResourceBrowser/Types.ts
rename to apps/web/src/components/ResourceBrowser/Types.ts
diff --git a/src/components/ResourceBrowser/Utils.tsx b/apps/web/src/components/ResourceBrowser/Utils.tsx
similarity index 100%
rename from src/components/ResourceBrowser/Utils.tsx
rename to apps/web/src/components/ResourceBrowser/Utils.tsx
diff --git a/src/components/__mocks__/monaco-editor.js b/apps/web/src/components/__mocks__/monaco-editor.js
similarity index 100%
rename from src/components/__mocks__/monaco-editor.js
rename to apps/web/src/components/__mocks__/monaco-editor.js
diff --git a/src/components/__mocks__/xterm-webfont.js b/apps/web/src/components/__mocks__/xterm-webfont.js
similarity index 100%
rename from src/components/__mocks__/xterm-webfont.js
rename to apps/web/src/components/__mocks__/xterm-webfont.js
diff --git a/src/components/app/LogFilter.test.ts b/apps/web/src/components/app/LogFilter.test.ts
similarity index 100%
rename from src/components/app/LogFilter.test.ts
rename to apps/web/src/components/app/LogFilter.test.ts
diff --git a/src/components/app/LogFilter.ts b/apps/web/src/components/app/LogFilter.ts
similarity index 100%
rename from src/components/app/LogFilter.ts
rename to apps/web/src/components/app/LogFilter.ts
diff --git a/src/components/app/Overview/EnvironmentList.tsx b/apps/web/src/components/app/Overview/EnvironmentList.tsx
similarity index 99%
rename from src/components/app/Overview/EnvironmentList.tsx
rename to apps/web/src/components/app/Overview/EnvironmentList.tsx
index 15adebbb4f..a83418fd4f 100644
--- a/src/components/app/Overview/EnvironmentList.tsx
+++ b/apps/web/src/components/app/Overview/EnvironmentList.tsx
@@ -31,6 +31,7 @@ import {
ArtifactInfoModalProps,
ImageChipCell,
RegistryType,
+ AppEnvironment,
} from '@devtron-labs/devtron-fe-common-lib'
import { Link, useHistory } from 'react-router-dom'
import { ReactComponent as ActivityIcon } from '../../../assets/icons/ic-activity.svg'
@@ -41,7 +42,6 @@ import { ReactComponent as VirtualEnvIcon } from '../../../assets/icons/ic-envir
import { ModuleNameMap, URLS } from '../../../config'
import { EMPTY_STATE_STATUS } from '../../../config/constantMessaging'
import { getAppOtherEnvironment } from '@Services/service'
-import { AppEnvironment } from '../../../services/service.types'
import { getModuleInfo } from '../../v2/devtronStackManager/DevtronStackManager.service'
import { ModuleStatus } from '../../v2/devtronStackManager/DevtronStackManager.type'
import { StatusConstants } from '../list-new/Constants'
diff --git a/src/components/app/Overview/Overview.scss b/apps/web/src/components/app/Overview/Overview.scss
similarity index 100%
rename from src/components/app/Overview/Overview.scss
rename to apps/web/src/components/app/Overview/Overview.scss
diff --git a/src/components/app/Overview/Overview.tsx b/apps/web/src/components/app/Overview/Overview.tsx
similarity index 100%
rename from src/components/app/Overview/Overview.tsx
rename to apps/web/src/components/app/Overview/Overview.tsx
diff --git a/src/components/app/Overview/TagChipsContainer.tsx b/apps/web/src/components/app/Overview/TagChipsContainer.tsx
similarity index 100%
rename from src/components/app/Overview/TagChipsContainer.tsx
rename to apps/web/src/components/app/Overview/TagChipsContainer.tsx
diff --git a/src/components/app/Overview/constants.ts b/apps/web/src/components/app/Overview/constants.ts
similarity index 95%
rename from src/components/app/Overview/constants.ts
rename to apps/web/src/components/app/Overview/constants.ts
index 19168e7c75..b853f339f2 100644
--- a/src/components/app/Overview/constants.ts
+++ b/apps/web/src/components/app/Overview/constants.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
+import { AppEnvironment } from '@devtron-labs/devtron-fe-common-lib'
import { DEFAULT_SHIMMER_LOADING_TABLE_ROWS } from '../../../config'
-import { AppEnvironment } from '../../../services/service.types'
/**
* Mock data for the shimmer loader
diff --git a/src/components/app/Overview/utils.tsx b/apps/web/src/components/app/Overview/utils.tsx
similarity index 100%
rename from src/components/app/Overview/utils.tsx
rename to apps/web/src/components/app/Overview/utils.tsx
diff --git a/src/components/app/ResourceTreeNodes.tsx b/apps/web/src/components/app/ResourceTreeNodes.tsx
similarity index 100%
rename from src/components/app/ResourceTreeNodes.tsx
rename to apps/web/src/components/app/ResourceTreeNodes.tsx
diff --git a/src/components/app/ResponsiveDrawer.tsx b/apps/web/src/components/app/ResponsiveDrawer.tsx
similarity index 100%
rename from src/components/app/ResponsiveDrawer.tsx
rename to apps/web/src/components/app/ResponsiveDrawer.tsx
diff --git a/src/components/app/WebWorker.ts b/apps/web/src/components/app/WebWorker.ts
similarity index 100%
rename from src/components/app/WebWorker.ts
rename to apps/web/src/components/app/WebWorker.ts
diff --git a/src/components/app/appLabelCommon.tsx b/apps/web/src/components/app/appLabelCommon.tsx
similarity index 100%
rename from src/components/app/appLabelCommon.tsx
rename to apps/web/src/components/app/appLabelCommon.tsx
diff --git a/src/components/app/config.tsx b/apps/web/src/components/app/config.tsx
similarity index 100%
rename from src/components/app/config.tsx
rename to apps/web/src/components/app/config.tsx
diff --git a/src/components/app/create/CreateApp.tsx b/apps/web/src/components/app/create/CreateApp.tsx
similarity index 100%
rename from src/components/app/create/CreateApp.tsx
rename to apps/web/src/components/app/create/CreateApp.tsx
diff --git a/src/components/app/create/createApp.scss b/apps/web/src/components/app/create/createApp.scss
similarity index 100%
rename from src/components/app/create/createApp.scss
rename to apps/web/src/components/app/create/createApp.scss
diff --git a/src/components/app/create/service.tsx b/apps/web/src/components/app/create/service.tsx
similarity index 100%
rename from src/components/app/create/service.tsx
rename to apps/web/src/components/app/create/service.tsx
diff --git a/src/components/app/create/validationRules.ts b/apps/web/src/components/app/create/validationRules.ts
similarity index 100%
rename from src/components/app/create/validationRules.ts
rename to apps/web/src/components/app/create/validationRules.ts
diff --git a/src/components/app/details/AboutAppInfoModal.tsx b/apps/web/src/components/app/details/AboutAppInfoModal.tsx
similarity index 100%
rename from src/components/app/details/AboutAppInfoModal.tsx
rename to apps/web/src/components/app/details/AboutAppInfoModal.tsx
diff --git a/src/components/app/details/AboutTagEditModal.tsx b/apps/web/src/components/app/details/AboutTagEditModal.tsx
similarity index 100%
rename from src/components/app/details/AboutTagEditModal.tsx
rename to apps/web/src/components/app/details/AboutTagEditModal.tsx
diff --git a/src/components/app/details/AppHeader.tsx b/apps/web/src/components/app/details/AppHeader.tsx
similarity index 100%
rename from src/components/app/details/AppHeader.tsx
rename to apps/web/src/components/app/details/AppHeader.tsx
diff --git a/src/components/app/details/app.scss b/apps/web/src/components/app/details/app.scss
similarity index 100%
rename from src/components/app/details/app.scss
rename to apps/web/src/components/app/details/app.scss
diff --git a/src/components/app/details/appConfig/constants.ts b/apps/web/src/components/app/details/appConfig/constants.ts
similarity index 100%
rename from src/components/app/details/appConfig/constants.ts
rename to apps/web/src/components/app/details/appConfig/constants.ts
diff --git a/src/components/app/details/appDetails/AppDetails.tsx b/apps/web/src/components/app/details/appDetails/AppDetails.tsx
similarity index 98%
rename from src/components/app/details/appDetails/AppDetails.tsx
rename to apps/web/src/components/app/details/appDetails/AppDetails.tsx
index 7c8b5f3026..0daca73e57 100644
--- a/src/components/app/details/appDetails/AppDetails.tsx
+++ b/apps/web/src/components/app/details/appDetails/AppDetails.tsx
@@ -53,7 +53,7 @@ import {
DEFAULT_STATUS_TEXT,
} from '../../../../config'
import { NavigationArrow, useAppContext, FragmentHOC } from '../../../common'
-import { CustomValueContainer, groupHeaderStyle, GroupHeading, Option } from '../../../v2/common/ReactSelect.utils'
+import { groupHeaderStyle, Option } from '../../../v2/common/ReactSelect.utils'
import { getAppConfigStatus, getAppOtherEnvironmentMin, stopStartApp } from '../../../../services/service'
// @ts-check
import AppNotDeployedIcon from '../../../../assets/img/app-not-deployed.png'
@@ -116,7 +116,7 @@ import { renderCIListHeader } from '../cdDetails/utils'
const VirtualAppDetailsEmptyState = importComponentFromFELibrary('VirtualAppDetailsEmptyState')
const DeploymentWindowStatusModal = importComponentFromFELibrary('DeploymentWindowStatusModal')
const DeploymentWindowConfirmationDialog = importComponentFromFELibrary('DeploymentWindowConfirmationDialog')
-
+const ConfigDriftModalRoute = importComponentFromFELibrary('ConfigDriftModalRoute', null, 'function')
const processVirtualEnvironmentDeploymentData = importComponentFromFELibrary(
'processVirtualEnvironmentDeploymentData',
null,
@@ -269,6 +269,7 @@ export const Details: React.FC = ({
deploymentUserActionState,
}) => {
const params = useParams<{ appId: string; envId: string }>()
+ const { path } = useRouteMatch()
const location = useLocation()
// fixme: the state is not being set anywhere and just being drilled down
const [detailedStatus, toggleDetailedStatus] = useState(false)
@@ -304,6 +305,7 @@ export const Details: React.FC = ({
deploymentStatus: DEFAULT_STATUS,
deploymentStatusText: DEFAULT_STATUS_TEXT,
})
+ const isConfigDriftEnabled: boolean = window._env_.FEATURE_CONFIG_DRIFT_ENABLE
const isExternalToolAvailable: boolean =
externalLinksAndTools.externalLinks.length > 0 && externalLinksAndTools.monitoringTools.length > 0
const interval = Number(window._env_.DEVTRON_APP_DETAILS_POLLING_INTERVAL) || 30000
@@ -781,7 +783,13 @@ export const Details: React.FC = ({
) : (
renderAppDetails()
)}
- {detailedStatus && }
+ {detailedStatus && (
+
+ )}
{location.search.includes(DEPLOYMENT_STATUS_QUERY_PARAM) && (
= ({
isVirtualEnvironment={isVirtualEnvRef.current}
/>
}
+ {isConfigDriftEnabled && ConfigDriftModalRoute && !isVirtualEnvRef.current && }
>
)
}
diff --git a/src/components/app/details/appDetails/AppDetailsCDButton.tsx b/apps/web/src/components/app/details/appDetails/AppDetailsCDButton.tsx
similarity index 88%
rename from src/components/app/details/appDetails/AppDetailsCDButton.tsx
rename to apps/web/src/components/app/details/appDetails/AppDetailsCDButton.tsx
index e832279944..3df6a2a78a 100644
--- a/src/components/app/details/appDetails/AppDetailsCDButton.tsx
+++ b/apps/web/src/components/app/details/appDetails/AppDetailsCDButton.tsx
@@ -18,12 +18,16 @@ import React from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import {
ACTION_STATE,
+ Button,
+ ButtonComponentType,
+ ButtonVariantType,
+ ComponentSizeType,
DeploymentNodeType,
VisibleModal,
stopPropagation,
useSearchString,
} from '@devtron-labs/devtron-fe-common-lib'
-import { getCTAClass, importComponentFromFELibrary } from '../../../common'
+import { importComponentFromFELibrary } from '../../../common'
import { URL_PARAM_MODE_TYPE } from '../../../common/helpers/types'
import CDMaterial from '../triggerView/cdMaterial'
import { MATERIAL_TYPE } from '../triggerView/types'
@@ -33,6 +37,7 @@ import { ReactComponent as DeployIcon } from '../../../../assets/icons/ic-nav-ro
import { ReactComponent as InfoOutline } from '../../../../assets/icons/ic-info-outline.svg'
import { TRIGGER_VIEW_PARAMS } from '../triggerView/Constants'
import { getModuleInfo } from '../../../v2/devtronStackManager/DevtronStackManager.service'
+import { getDeployButtonStyle } from './utils'
const ApprovalMaterialModal = importComponentFromFELibrary('ApprovalMaterialModal')
@@ -70,21 +75,16 @@ const AppDetailsCDButton = ({
}
const renderDeployButton = () => (
- : }
onClick={onClickDeployButton}
- type="button"
- >
- {deploymentUserActionState === ACTION_STATE.BLOCKED ? (
-
- ) : (
-
- )}
- {BUTTON_TITLE[DeploymentNodeType.CD]}
-
+ component={ButtonComponentType.button}
+ style={getDeployButtonStyle(deploymentUserActionState)}
+ />
)
const node = {
diff --git a/src/components/app/details/appDetails/AppMetrics.tsx b/apps/web/src/components/app/details/appDetails/AppMetrics.tsx
similarity index 100%
rename from src/components/app/details/appDetails/AppMetrics.tsx
rename to apps/web/src/components/app/details/appDetails/AppMetrics.tsx
diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/apps/web/src/components/app/details/appDetails/AppSecurity.tsx
similarity index 100%
rename from src/components/app/details/appDetails/AppSecurity.tsx
rename to apps/web/src/components/app/details/appDetails/AppSecurity.tsx
diff --git a/src/components/app/details/appDetails/AppStatusCard.tsx b/apps/web/src/components/app/details/appDetails/AppStatusCard.tsx
similarity index 100%
rename from src/components/app/details/appDetails/AppStatusCard.tsx
rename to apps/web/src/components/app/details/appDetails/AppStatusCard.tsx
diff --git a/src/components/app/details/appDetails/DeployedCommitCard.tsx b/apps/web/src/components/app/details/appDetails/DeployedCommitCard.tsx
similarity index 100%
rename from src/components/app/details/appDetails/DeployedCommitCard.tsx
rename to apps/web/src/components/app/details/appDetails/DeployedCommitCard.tsx
diff --git a/src/components/app/details/appDetails/DeploymentStatusCard.tsx b/apps/web/src/components/app/details/appDetails/DeploymentStatusCard.tsx
similarity index 100%
rename from src/components/app/details/appDetails/DeploymentStatusCard.tsx
rename to apps/web/src/components/app/details/appDetails/DeploymentStatusCard.tsx
diff --git a/src/components/app/details/appDetails/DeploymentStatusDetailModal.tsx b/apps/web/src/components/app/details/appDetails/DeploymentStatusDetailModal.tsx
similarity index 100%
rename from src/components/app/details/appDetails/DeploymentStatusDetailModal.tsx
rename to apps/web/src/components/app/details/appDetails/DeploymentStatusDetailModal.tsx
diff --git a/src/components/app/details/appDetails/GraphsModal.tsx b/apps/web/src/components/app/details/appDetails/GraphsModal.tsx
similarity index 100%
rename from src/components/app/details/appDetails/GraphsModal.tsx
rename to apps/web/src/components/app/details/appDetails/GraphsModal.tsx
diff --git a/src/components/app/details/appDetails/IssuesCard.tsx b/apps/web/src/components/app/details/appDetails/IssuesCard.tsx
similarity index 100%
rename from src/components/app/details/appDetails/IssuesCard.tsx
rename to apps/web/src/components/app/details/appDetails/IssuesCard.tsx
diff --git a/src/components/app/details/appDetails/IssuesListingModal.tsx b/apps/web/src/components/app/details/appDetails/IssuesListingModal.tsx
similarity index 100%
rename from src/components/app/details/appDetails/IssuesListingModal.tsx
rename to apps/web/src/components/app/details/appDetails/IssuesListingModal.tsx
diff --git a/src/components/app/details/appDetails/LastUpdatedCard.tsx b/apps/web/src/components/app/details/appDetails/LastUpdatedCard.tsx
similarity index 100%
rename from src/components/app/details/appDetails/LastUpdatedCard.tsx
rename to apps/web/src/components/app/details/appDetails/LastUpdatedCard.tsx
diff --git a/src/components/app/details/appDetails/LoadingCard.tsx b/apps/web/src/components/app/details/appDetails/LoadingCard.tsx
similarity index 100%
rename from src/components/app/details/appDetails/LoadingCard.tsx
rename to apps/web/src/components/app/details/appDetails/LoadingCard.tsx
diff --git a/src/components/app/details/appDetails/Readme.md b/apps/web/src/components/app/details/appDetails/Readme.md
similarity index 100%
rename from src/components/app/details/appDetails/Readme.md
rename to apps/web/src/components/app/details/appDetails/Readme.md
diff --git a/src/components/app/details/appDetails/SecurityVulnerabilityCard.tsx b/apps/web/src/components/app/details/appDetails/SecurityVulnerabilityCard.tsx
similarity index 99%
rename from src/components/app/details/appDetails/SecurityVulnerabilityCard.tsx
rename to apps/web/src/components/app/details/appDetails/SecurityVulnerabilityCard.tsx
index 5cac3dacf9..1cfbb7b262 100644
--- a/src/components/app/details/appDetails/SecurityVulnerabilityCard.tsx
+++ b/apps/web/src/components/app/details/appDetails/SecurityVulnerabilityCard.tsx
@@ -17,10 +17,8 @@
import React, { useState } from 'react'
import Tippy from '@tippyjs/react'
import {
- getSeverityCountFromSummary,
getTotalSeverityCount,
SecurityModal,
- SeverityCount,
} from '@devtron-labs/devtron-fe-common-lib'
import { ReactComponent as Question } from '../../../../assets/icons/ic-help-outline.svg'
import { ReactComponent as Bug } from '../../../../assets/icons/ic-nav-bug.svg'
diff --git a/src/components/app/details/appDetails/SourceInfo.tsx b/apps/web/src/components/app/details/appDetails/SourceInfo.tsx
similarity index 77%
rename from src/components/app/details/appDetails/SourceInfo.tsx
rename to apps/web/src/components/app/details/appDetails/SourceInfo.tsx
index eb79442030..bc09641539 100644
--- a/src/components/app/details/appDetails/SourceInfo.tsx
+++ b/apps/web/src/components/app/details/appDetails/SourceInfo.tsx
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-import { useEffect, useMemo, useState } from 'react'
+import { useMemo } from 'react'
import { Link, useParams } from 'react-router-dom'
-import Tippy from '@tippyjs/react'
import moment from 'moment'
import {
- ConditionalWrap,
+ Button,
+ ButtonComponentType,
+ ButtonStyleType,
+ ButtonVariantType,
+ ComponentSizeType,
DATE_TIME_FORMATS,
DeploymentAppTypes,
getIsManualApprovalConfigured,
handleUTCTime,
ReleaseMode,
- showError,
Tooltip,
} from '@devtron-labs/devtron-fe-common-lib'
import { ReactComponent as ICCamera } from '@Icons/ic-camera.svg'
@@ -40,7 +42,6 @@ import DeployedCommitCard from './DeployedCommitCard'
import IssuesCard from './IssuesCard'
import SecurityVulnerabilityCard from './SecurityVulnerabilityCard'
import AppStatusCard from './AppStatusCard'
-import { getLastExecutionByAppArtifactId } from '../../../../services/service'
import LoadingCard from './LoadingCard'
import AppDetailsCDButton from './AppDetailsCDButton'
import { ReactComponent as RotateIcon } from '../../../../assets/icons/ic-arrows_clockwise.svg'
@@ -51,6 +52,7 @@ import HelmAppConfigApplyStatusCard from '@Components/v2/appDetails/sourceInfo/e
const AppDetailsDownloadCard = importComponentFromFELibrary('AppDetailsDownloadCard')
const DeploymentWindowStatusCard = importComponentFromFELibrary('DeploymentWindowStatusCard')
+const ConfigSyncStatusButton = importComponentFromFELibrary('ConfigSyncStatusButton', null, 'function')
export const SourceInfo = ({
appDetails,
@@ -119,19 +121,6 @@ export const SourceInfo = ({
return {loadingCards}
}
- const conditionalScalePodsButton = (children) => {
- return (
-
- {children}
-
- )
- }
-
const getIsApprovalConfigured = (): boolean => {
try {
const userApprovalConfig = appDetails?.userApprovalConfig || '{}'
@@ -145,10 +134,9 @@ export const SourceInfo = ({
const renderDevtronAppsEnvironmentSelector = (environment) => {
// If moving to a component then move getIsApprovalConfigured with it as well with memoization.
const isApprovalConfigured = getIsApprovalConfigured()
- const relativeSnapshotTime = appDetails?.resourceTree?.lastSnapshotTime ? handleUTCTime(
- appDetails.resourceTree.lastSnapshotTime,
- true,
- ) : ''
+ const relativeSnapshotTime = appDetails?.resourceTree?.lastSnapshotTime
+ ? handleUTCTime(appDetails.resourceTree.lastSnapshotTime, true)
+ : ''
return (
@@ -170,6 +158,15 @@ export const SourceInfo = ({
)}
+ {appDetails?.resourceTree && !isIsolatedEnv && window._env_.FEATURE_CONFIG_DRIFT_ENABLE && ConfigSyncStatusButton && (
+
+
+
+ )}
{isdeploymentAppDeleting && (
@@ -204,47 +201,58 @@ export const SourceInfo = ({
{!loadingResourceTree && environment && (
<>
{!isdeploymentAppDeleting && (
-
+
{!isVirtualEnvironment && showUrlInfo && (
-
}
onClick={onClickShowUrlInfo}
- data-testid="app-details-urls"
- >
-
- URLs
-
+ component={ButtonComponentType.button}
+ style={ButtonStyleType.neutral}
+ />
)}
{!isVirtualEnvironment && showHibernateModal && (
-
-
+
- {isHibernated ? 'Restore pod count' : 'Scale pods to 0'}
-
-
+ }
+ onClick={onClickShowHibernateModal}
+ component={ButtonComponentType.button}
+ disabled={isApprovalConfigured}
+ style={ButtonStyleType.neutral}
+ showTooltip={isApprovalConfigured}
+ tooltipProps={{
+ content: 'Application deployment requiring approval cannot be hibernated.',
+ placement: 'bottom-end',
+ }}
+ />
)}
{window._env_.ENABLE_RESTART_WORKLOAD && !isVirtualEnvironment && setRotateModal && (
-
- setRotateModal(true)}
- disabled={isApprovalConfigured}
- >
-
- Restart workloads
-
-
+
setRotateModal(true)}
+ disabled={isApprovalConfigured}
+ startIcon={ }
+ text="Restart workloads"
+ component={ButtonComponentType.button}
+ style={ButtonStyleType.neutral}
+ showTooltip={isApprovalConfigured}
+ tooltipProps={{
+ content: 'Application deployment requiring approval cannot be hibernated.',
+ placement: 'bottom-end',
+ }}
+ />
)}
{status && (
-
+
)}
{!helmMigratedAppNotTriggered && (
<>
@@ -328,8 +336,7 @@ export const SourceInfo = ({
deploymentStatusDetailsBreakdownData={deploymentStatusDetailsBreakdownData}
cardLoading={cardLoading}
hideDetails={
- appDetails?.deploymentAppType === DeploymentAppTypes.HELM ||
- isIsolatedEnv
+ appDetails?.deploymentAppType === DeploymentAppTypes.HELM || isIsolatedEnv
}
isVirtualEnvironment={isVirtualEnvironment}
refetchDeploymentStatus={refetchDeploymentStatus}
diff --git a/src/components/app/details/appDetails/__mocks__/appDetails.mock.ts b/apps/web/src/components/app/details/appDetails/__mocks__/appDetails.mock.ts
similarity index 100%
rename from src/components/app/details/appDetails/__mocks__/appDetails.mock.ts
rename to apps/web/src/components/app/details/appDetails/__mocks__/appDetails.mock.ts
diff --git a/src/components/app/details/appDetails/__tests__/GenericInfo.test.tsx b/apps/web/src/components/app/details/appDetails/__tests__/GenericInfo.test.tsx
similarity index 100%
rename from src/components/app/details/appDetails/__tests__/GenericInfo.test.tsx
rename to apps/web/src/components/app/details/appDetails/__tests__/GenericInfo.test.tsx
diff --git a/src/components/app/details/appDetails/appDetails.scss b/apps/web/src/components/app/details/appDetails/appDetails.scss
similarity index 100%
rename from src/components/app/details/appDetails/appDetails.scss
rename to apps/web/src/components/app/details/appDetails/appDetails.scss
diff --git a/src/components/app/details/appDetails/appDetails.service.ts b/apps/web/src/components/app/details/appDetails/appDetails.service.ts
similarity index 100%
rename from src/components/app/details/appDetails/appDetails.service.ts
rename to apps/web/src/components/app/details/appDetails/appDetails.service.ts
diff --git a/src/components/app/details/appDetails/appDetails.type.ts b/apps/web/src/components/app/details/appDetails/appDetails.type.ts
similarity index 100%
rename from src/components/app/details/appDetails/appDetails.type.ts
rename to apps/web/src/components/app/details/appDetails/appDetails.type.ts
diff --git a/src/components/app/details/appDetails/index.tsx b/apps/web/src/components/app/details/appDetails/index.tsx
similarity index 100%
rename from src/components/app/details/appDetails/index.tsx
rename to apps/web/src/components/app/details/appDetails/index.tsx
diff --git a/src/components/app/details/appDetails/utils.tsx b/apps/web/src/components/app/details/appDetails/utils.tsx
similarity index 96%
rename from src/components/app/details/appDetails/utils.tsx
rename to apps/web/src/components/app/details/appDetails/utils.tsx
index 073570d50b..bc4a000bd9 100644
--- a/src/components/app/details/appDetails/utils.tsx
+++ b/apps/web/src/components/app/details/appDetails/utils.tsx
@@ -21,7 +21,7 @@ import { AggregationKeys } from '../../types'
import { getVersionArr, isVersionLessThanOrEqualToTarget, DayPickerRangeControllerPresets } from '../../../common'
import { ReactComponent as ArrowDown } from '../../../../assets/icons/ic-chevron-down.svg'
import { ChartTypes, AppMetricsTabType, StatusType, StatusTypes } from './appDetails.type'
-import { ZERO_TIME_STRING, Nodes, NodeType } from '@devtron-labs/devtron-fe-common-lib'
+import { ZERO_TIME_STRING, Nodes, NodeType, ACTION_STATE, ButtonStyleType } from '@devtron-labs/devtron-fe-common-lib'
import CreatableSelect from 'react-select/creatable'
export function getAggregator(nodeType: NodeType, defaultAsOtherResources?: boolean): AggregationKeys {
@@ -451,3 +451,14 @@ export class ParamsAndEnvContext extends EnvironmentSelection {
return _envList[0].environmentId
}
}
+
+export const getDeployButtonStyle = (actionState: ACTION_STATE): ButtonStyleType => {
+ switch (actionState) {
+ case ACTION_STATE.BLOCKED:
+ return ButtonStyleType.negative
+ case ACTION_STATE.PARTIAL:
+ return ButtonStyleType.warning
+ default:
+ return ButtonStyleType.default
+ }
+}
diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/apps/web/src/components/app/details/cIDetails/CIDetails.tsx
similarity index 99%
rename from src/components/app/details/cIDetails/CIDetails.tsx
rename to apps/web/src/components/app/details/cIDetails/CIDetails.tsx
index 244868ba24..27262a1bda 100644
--- a/src/components/app/details/cIDetails/CIDetails.tsx
+++ b/apps/web/src/components/app/details/cIDetails/CIDetails.tsx
@@ -41,13 +41,10 @@ import {
ModuleNameMap,
EMPTY_STATE_STATUS,
SecuritySummaryCard,
- SeverityCount,
TabGroup,
TRIGGER_STATUS_PROGRESSING,
SCAN_TOOL_ID_TRIVY,
ErrorScreenManager,
- getTotalSeverityCount,
- getSeverityCountFromSummary,
parseExecutionDetailResponse,
} from '@devtron-labs/devtron-fe-common-lib'
import { Switch, Route, Redirect, useRouteMatch, useParams, useHistory, generatePath } from 'react-router-dom'
diff --git a/src/components/app/details/cIDetails/CISecurity.utils.tsx b/apps/web/src/components/app/details/cIDetails/CISecurity.utils.tsx
similarity index 94%
rename from src/components/app/details/cIDetails/CISecurity.utils.tsx
rename to apps/web/src/components/app/details/cIDetails/CISecurity.utils.tsx
index 11fe31b935..9533ddfebe 100644
--- a/src/components/app/details/cIDetails/CISecurity.utils.tsx
+++ b/apps/web/src/components/app/details/cIDetails/CISecurity.utils.tsx
@@ -40,7 +40,7 @@ export const useGetCISecurityDetails = ({
const severityCount: SeverityCount = isSecurityScanV2Enabled
? getSeverityCountFromSummary(scanResultResponse?.result.imageScan.vulnerability?.summary.severities)
- : executionDetailsResponse?.result.severityCount ?? { critical: 0, high: 0, medium: 0, low: 0, unknown: 0 }
+ : (executionDetailsResponse?.result.severityCount ?? { critical: 0, high: 0, medium: 0, low: 0, unknown: 0 })
const totalCount = getTotalSeverityCount(severityCount)
diff --git a/src/components/app/details/cIDetails/Readme.md b/apps/web/src/components/app/details/cIDetails/Readme.md
similarity index 100%
rename from src/components/app/details/cIDetails/Readme.md
rename to apps/web/src/components/app/details/cIDetails/Readme.md
diff --git a/src/components/app/details/cIDetails/cIDetails.util.tsx b/apps/web/src/components/app/details/cIDetails/cIDetails.util.tsx
similarity index 100%
rename from src/components/app/details/cIDetails/cIDetails.util.tsx
rename to apps/web/src/components/app/details/cIDetails/cIDetails.util.tsx
diff --git a/src/components/app/details/cIDetails/ciDetails.scss b/apps/web/src/components/app/details/cIDetails/ciDetails.scss
similarity index 100%
rename from src/components/app/details/cIDetails/ciDetails.scss
rename to apps/web/src/components/app/details/cIDetails/ciDetails.scss
diff --git a/src/components/app/details/cIDetails/types.tsx b/apps/web/src/components/app/details/cIDetails/types.tsx
similarity index 100%
rename from src/components/app/details/cIDetails/types.tsx
rename to apps/web/src/components/app/details/cIDetails/types.tsx
diff --git a/src/components/app/details/cdDetails/CDDetails.tsx b/apps/web/src/components/app/details/cdDetails/CDDetails.tsx
similarity index 99%
rename from src/components/app/details/cdDetails/CDDetails.tsx
rename to apps/web/src/components/app/details/cdDetails/CDDetails.tsx
index d237a7a108..41609fcc7a 100644
--- a/src/components/app/details/cdDetails/CDDetails.tsx
+++ b/apps/web/src/components/app/details/cdDetails/CDDetails.tsx
@@ -37,6 +37,7 @@ import {
getTriggerHistory,
useScrollable,
TRIGGER_STATUS_PROGRESSING,
+ AppEnvironment,
} from '@devtron-labs/devtron-fe-common-lib'
import { useHistory, useRouteMatch, useParams, generatePath, useLocation, Route } from 'react-router-dom'
import { useAppContext } from '@Components/common'
@@ -45,7 +46,6 @@ import { AppNotConfigured } from '../appDetails/AppDetails'
import './cdDetail.scss'
import { DeploymentTemplateList } from './cd.type'
import { getModuleConfigured } from '../appDetails/appDetails.service'
-import { AppEnvironment } from '../../../../services/service.types'
import { EMPTY_STATE_STATUS } from '../../../../config/constantMessaging'
import {
processVirtualEnvironmentDeploymentData,
diff --git a/src/components/app/details/cdDetails/cd.type.ts b/apps/web/src/components/app/details/cdDetails/cd.type.ts
similarity index 100%
rename from src/components/app/details/cdDetails/cd.type.ts
rename to apps/web/src/components/app/details/cdDetails/cd.type.ts
diff --git a/src/components/app/details/cdDetails/cdDetail.scss b/apps/web/src/components/app/details/cdDetails/cdDetail.scss
similarity index 97%
rename from src/components/app/details/cdDetails/cdDetail.scss
rename to apps/web/src/components/app/details/cdDetails/cdDetail.scss
index dcab24a4ac..76a503a681 100644
--- a/src/components/app/details/cdDetails/cdDetail.scss
+++ b/apps/web/src/components/app/details/cdDetails/cdDetail.scss
@@ -29,8 +29,7 @@
.deployment-diff__upper {
display: grid;
- grid-template-columns: 50% 50%;
- grid-template-rows: auto;
+ grid-template-columns: repeat(2, 1fr);
position: relative;
&::after {
diff --git a/src/components/app/details/cdDetails/service.ts b/apps/web/src/components/app/details/cdDetails/service.ts
similarity index 97%
rename from src/components/app/details/cdDetails/service.ts
rename to apps/web/src/components/app/details/cdDetails/service.ts
index 0d235c554b..b26764d50b 100644
--- a/src/components/app/details/cdDetails/service.ts
+++ b/apps/web/src/components/app/details/cdDetails/service.ts
@@ -96,7 +96,10 @@ export const prepareConfigMapAndSecretData = (
if (rawData['type'] === 'volume') {
typeValue = 'Data Volume'
if (rawData['mountPath'] || rawData['defaultMountPath']) {
- secretValues['mountPath'] = { displayName: 'Volume mount path', value: rawData['mountPath'] ?? rawData['defaultMountPath'] }
+ secretValues['mountPath'] = {
+ displayName: 'Volume mount path',
+ value: rawData['mountPath'] || rawData['defaultMountPath'],
+ }
}
if (rawData['subPath']) {
secretValues['subPath'] = { displayName: 'Set SubPath', value: 'Yes' }
diff --git a/src/components/app/details/cdDetails/utils.tsx b/apps/web/src/components/app/details/cdDetails/utils.tsx
similarity index 100%
rename from src/components/app/details/cdDetails/utils.tsx
rename to apps/web/src/components/app/details/cdDetails/utils.tsx
diff --git a/src/components/app/details/cicdHistory/History.components.tsx b/apps/web/src/components/app/details/cicdHistory/History.components.tsx
similarity index 100%
rename from src/components/app/details/cicdHistory/History.components.tsx
rename to apps/web/src/components/app/details/cicdHistory/History.components.tsx
diff --git a/src/components/app/details/cicdHistory/types.tsx b/apps/web/src/components/app/details/cicdHistory/types.tsx
similarity index 100%
rename from src/components/app/details/cicdHistory/types.tsx
rename to apps/web/src/components/app/details/cicdHistory/types.tsx
diff --git a/src/components/app/details/main.tsx b/apps/web/src/components/app/details/main.tsx
similarity index 100%
rename from src/components/app/details/main.tsx
rename to apps/web/src/components/app/details/main.tsx
diff --git a/src/components/app/details/metrics/BenchmarkModal.tsx b/apps/web/src/components/app/details/metrics/BenchmarkModal.tsx
similarity index 100%
rename from src/components/app/details/metrics/BenchmarkModal.tsx
rename to apps/web/src/components/app/details/metrics/BenchmarkModal.tsx
diff --git a/src/components/app/details/metrics/DeploymentMetrics.tsx b/apps/web/src/components/app/details/metrics/DeploymentMetrics.tsx
similarity index 100%
rename from src/components/app/details/metrics/DeploymentMetrics.tsx
rename to apps/web/src/components/app/details/metrics/DeploymentMetrics.tsx
diff --git a/src/components/app/details/metrics/DeploymentTable.tsx b/apps/web/src/components/app/details/metrics/DeploymentTable.tsx
similarity index 100%
rename from src/components/app/details/metrics/DeploymentTable.tsx
rename to apps/web/src/components/app/details/metrics/DeploymentTable.tsx
diff --git a/src/components/app/details/metrics/DeploymentTableModal.tsx b/apps/web/src/components/app/details/metrics/DeploymentTableModal.tsx
similarity index 100%
rename from src/components/app/details/metrics/DeploymentTableModal.tsx
rename to apps/web/src/components/app/details/metrics/DeploymentTableModal.tsx
diff --git a/src/components/app/details/metrics/deploymentMetrics.data.json b/apps/web/src/components/app/details/metrics/deploymentMetrics.data.json
similarity index 100%
rename from src/components/app/details/metrics/deploymentMetrics.data.json
rename to apps/web/src/components/app/details/metrics/deploymentMetrics.data.json
diff --git a/src/components/app/details/metrics/deploymentMetrics.scss b/apps/web/src/components/app/details/metrics/deploymentMetrics.scss
similarity index 100%
rename from src/components/app/details/metrics/deploymentMetrics.scss
rename to apps/web/src/components/app/details/metrics/deploymentMetrics.scss
diff --git a/src/components/app/details/metrics/deploymentMetrics.service.ts b/apps/web/src/components/app/details/metrics/deploymentMetrics.service.ts
similarity index 100%
rename from src/components/app/details/metrics/deploymentMetrics.service.ts
rename to apps/web/src/components/app/details/metrics/deploymentMetrics.service.ts
diff --git a/src/components/app/details/metrics/deploymentMetrics.test.ts b/apps/web/src/components/app/details/metrics/deploymentMetrics.test.ts
similarity index 100%
rename from src/components/app/details/metrics/deploymentMetrics.test.ts
rename to apps/web/src/components/app/details/metrics/deploymentMetrics.test.ts
diff --git a/src/components/app/details/metrics/deploymentMetrics.types.ts b/apps/web/src/components/app/details/metrics/deploymentMetrics.types.ts
similarity index 100%
rename from src/components/app/details/metrics/deploymentMetrics.types.ts
rename to apps/web/src/components/app/details/metrics/deploymentMetrics.types.ts
diff --git a/src/components/app/details/metrics/deploymentMetrics.util.tsx b/apps/web/src/components/app/details/metrics/deploymentMetrics.util.tsx
similarity index 100%
rename from src/components/app/details/metrics/deploymentMetrics.util.tsx
rename to apps/web/src/components/app/details/metrics/deploymentMetrics.util.tsx
diff --git a/src/components/app/details/testViewer/Contributing.md b/apps/web/src/components/app/details/testViewer/Contributing.md
similarity index 100%
rename from src/components/app/details/testViewer/Contributing.md
rename to apps/web/src/components/app/details/testViewer/Contributing.md
diff --git a/src/components/app/details/testViewer/List.tsx b/apps/web/src/components/app/details/testViewer/List.tsx
similarity index 100%
rename from src/components/app/details/testViewer/List.tsx
rename to apps/web/src/components/app/details/testViewer/List.tsx
diff --git a/src/components/app/details/testViewer/Test.types.ts b/apps/web/src/components/app/details/testViewer/Test.types.ts
similarity index 100%
rename from src/components/app/details/testViewer/Test.types.ts
rename to apps/web/src/components/app/details/testViewer/Test.types.ts
diff --git a/src/components/app/details/testViewer/TestRunDetails.scss b/apps/web/src/components/app/details/testViewer/TestRunDetails.scss
similarity index 100%
rename from src/components/app/details/testViewer/TestRunDetails.scss
rename to apps/web/src/components/app/details/testViewer/TestRunDetails.scss
diff --git a/src/components/app/details/testViewer/TestRunDetails.tsx b/apps/web/src/components/app/details/testViewer/TestRunDetails.tsx
similarity index 100%
rename from src/components/app/details/testViewer/TestRunDetails.tsx
rename to apps/web/src/components/app/details/testViewer/TestRunDetails.tsx
diff --git a/src/components/app/details/testViewer/TestRunList.tsx b/apps/web/src/components/app/details/testViewer/TestRunList.tsx
similarity index 100%
rename from src/components/app/details/testViewer/TestRunList.tsx
rename to apps/web/src/components/app/details/testViewer/TestRunList.tsx
diff --git a/src/components/app/details/testViewer/service.ts b/apps/web/src/components/app/details/testViewer/service.ts
similarity index 100%
rename from src/components/app/details/testViewer/service.ts
rename to apps/web/src/components/app/details/testViewer/service.ts
diff --git a/src/components/app/details/triggerView/BranchRegexModal.tsx b/apps/web/src/components/app/details/triggerView/BranchRegexModal.tsx
similarity index 100%
rename from src/components/app/details/triggerView/BranchRegexModal.tsx
rename to apps/web/src/components/app/details/triggerView/BranchRegexModal.tsx
diff --git a/src/components/app/details/triggerView/CIMaterialModal.tsx b/apps/web/src/components/app/details/triggerView/CIMaterialModal.tsx
similarity index 100%
rename from src/components/app/details/triggerView/CIMaterialModal.tsx
rename to apps/web/src/components/app/details/triggerView/CIMaterialModal.tsx
diff --git a/src/components/app/details/triggerView/CiWebhookDebuggingModal.tsx b/apps/web/src/components/app/details/triggerView/CiWebhookDebuggingModal.tsx
similarity index 100%
rename from src/components/app/details/triggerView/CiWebhookDebuggingModal.tsx
rename to apps/web/src/components/app/details/triggerView/CiWebhookDebuggingModal.tsx
diff --git a/src/components/app/details/triggerView/Constants.ts b/apps/web/src/components/app/details/triggerView/Constants.ts
similarity index 100%
rename from src/components/app/details/triggerView/Constants.ts
rename to apps/web/src/components/app/details/triggerView/Constants.ts
diff --git a/src/components/app/details/triggerView/EmptyStateCIMaterial.tsx b/apps/web/src/components/app/details/triggerView/EmptyStateCIMaterial.tsx
similarity index 100%
rename from src/components/app/details/triggerView/EmptyStateCIMaterial.tsx
rename to apps/web/src/components/app/details/triggerView/EmptyStateCIMaterial.tsx
diff --git a/src/components/app/details/triggerView/MaterialSource.tsx b/apps/web/src/components/app/details/triggerView/MaterialSource.tsx
similarity index 100%
rename from src/components/app/details/triggerView/MaterialSource.tsx
rename to apps/web/src/components/app/details/triggerView/MaterialSource.tsx
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.scss b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.scss
similarity index 100%
rename from src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.scss
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.scss
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.tsx b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.tsx
similarity index 98%
rename from src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.tsx
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.tsx
index 169624d678..6737ea216d 100644
--- a/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.tsx
+++ b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiff.tsx
@@ -20,7 +20,7 @@ export const PipelineConfigDiff = ({
// Extracting resourceType and resourceName from the pathname.
// No route parameters are used here; they are only appended when the diff is opened.
- const [resourceType, resourceName] = pathname.split(`${URLS.APP_TRIGGER}/`)[1].split('/')
+ const [resourceType, resourceName] = pathname.split(`${URLS.APP_DIFF_VIEW}/`)[1].split('/')
// METHODS
const onSorting = () => handleSorting(sortOrder !== SortingOrder.DESC ? 'sort-config' : '')
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiffStatusTile.tsx b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiffStatusTile.tsx
similarity index 100%
rename from src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiffStatusTile.tsx
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/PipelineConfigDiffStatusTile.tsx
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/index.ts b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/index.ts
similarity index 100%
rename from src/components/app/details/triggerView/PipelineConfigDiff/index.ts
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/index.ts
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/types.ts b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/types.ts
similarity index 100%
rename from src/components/app/details/triggerView/PipelineConfigDiff/types.ts
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/types.ts
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/usePipelineDeploymentConfig.ts b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/usePipelineDeploymentConfig.ts
similarity index 96%
rename from src/components/app/details/triggerView/PipelineConfigDiff/usePipelineDeploymentConfig.ts
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/usePipelineDeploymentConfig.ts
index a7f5b55f24..0fcf5c2c88 100644
--- a/src/components/app/details/triggerView/PipelineConfigDiff/usePipelineDeploymentConfig.ts
+++ b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/usePipelineDeploymentConfig.ts
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react'
-import { generatePath, useLocation, useRouteMatch } from 'react-router-dom'
+import { useLocation } from 'react-router-dom'
import {
AppEnvDeploymentConfigType,
@@ -18,7 +18,8 @@ import {
showError,
} from '@devtron-labs/devtron-fe-common-lib'
-import { getOptions } from '@Components/deploymentConfig/service'
+import { URLS } from '@Config/routes'
+import { getTemplateOptions } from '@Services/service'
import {
PipelineConfigDiffQueryParams,
@@ -43,8 +44,7 @@ export const usePipelineDeploymentConfig = ({
wfrId,
}: UsePipelineDeploymentConfigProps) => {
// HOOKS
- const { path, params } = useRouteMatch()
- const { search } = useLocation()
+ const { pathname, search } = useLocation()
// STATES
const [convertVariables, setConvertVariables] = useState(false)
@@ -59,7 +59,7 @@ export const usePipelineDeploymentConfig = ({
const [previousDeploymentsLoader, previousDeployments, previousDeploymentsErr, reloadPreviousDeployments] =
useAsync(
() =>
- getOptions(appId, envId).then(
+ getTemplateOptions(appId, envId).then(
({ result }) => getDefaultVersionAndPreviousDeploymentOptions(result).previousDeployments,
),
[envId],
@@ -176,7 +176,7 @@ export const usePipelineDeploymentConfig = ({
}
const getNavItemHref = (resourceType: EnvResourceType, resourceName: string) =>
- `${generatePath(path, params)}/${resourceType}${resourceName ? `/${resourceName}` : ''}${search}`
+ `${pathname.split(`/${URLS.APP_DIFF_VIEW}/`)[0]}/${URLS.APP_DIFF_VIEW}/${resourceType}${resourceName ? `/${resourceName}` : ''}${search}`
const pipelineDeploymentConfig = useMemo(() => {
if (!pipelineDeploymentConfigLoading && pipelineDeploymentConfigRes) {
diff --git a/src/components/app/details/triggerView/PipelineConfigDiff/utils.tsx b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/utils.tsx
similarity index 98%
rename from src/components/app/details/triggerView/PipelineConfigDiff/utils.tsx
rename to apps/web/src/components/app/details/triggerView/PipelineConfigDiff/utils.tsx
index b2a6ef3e0e..1bb5688e48 100644
--- a/src/components/app/details/triggerView/PipelineConfigDiff/utils.tsx
+++ b/apps/web/src/components/app/details/triggerView/PipelineConfigDiff/utils.tsx
@@ -105,7 +105,7 @@ export const parseCompareWithSearchParams =
export const getPipelineDeploymentConfigFromPromiseSettled = (
res: PromiseSettledResult>,
-) => (res.status === 'fulfilled' ? res.value?.result ?? null : null)
+) => (res.status === 'fulfilled' ? (res.value?.result ?? null) : null)
export const getPipelineDeploymentConfigErrFromPromiseSettled = (
res: PromiseSettledResult>,
diff --git a/src/components/app/details/triggerView/TriggerView.tsx b/apps/web/src/components/app/details/triggerView/TriggerView.tsx
similarity index 95%
rename from src/components/app/details/triggerView/TriggerView.tsx
rename to apps/web/src/components/app/details/triggerView/TriggerView.tsx
index 89f954e013..32b33e687a 100644
--- a/src/components/app/details/triggerView/TriggerView.tsx
+++ b/apps/web/src/components/app/details/triggerView/TriggerView.tsx
@@ -29,10 +29,10 @@ import {
handleUTCTime,
createGitCommitUrl,
CIMaterialType,
- Environment,
ToastManager,
ToastVariantType,
TOAST_ACCESS_DENIED,
+ BlockedStateData,
} from '@devtron-labs/devtron-fe-common-lib'
import ReactGA from 'react-ga4'
import { withRouter, NavLink, Route, Switch } from 'react-router-dom'
@@ -44,10 +44,12 @@ import {
getGitMaterialByCommitHash,
} from '../../service'
import {
+ getCDPipelineURL,
getCIPipelineURL,
importComponentFromFELibrary,
preventBodyScroll,
sortObjectArrayAlphabetically,
+ withAppContext,
} from '../../../common'
import { getTriggerWorkflows } from './workflow.service'
import { Workflow } from './workflow/Workflow'
@@ -92,7 +94,11 @@ import { LinkedCIDetail } from '../../../../Pages/Shared/LinkedCIDetailsModal'
import { CIMaterialModal } from './CIMaterialModal'
const ApprovalMaterialModal = importComponentFromFELibrary('ApprovalMaterialModal')
-const getCIBlockState = importComponentFromFELibrary('getCIBlockState', null, 'function')
+const getCIBlockState: (...props) => Promise = importComponentFromFELibrary(
+ 'getCIBlockState',
+ null,
+ 'function',
+)
const ImagePromotionRouter = importComponentFromFELibrary('ImagePromotionRouter', null, 'function')
const getRuntimeParams = importComponentFromFELibrary('getRuntimeParams', null, 'function')
const getRuntimeParamsPayload = importComponentFromFELibrary('getRuntimeParamsPayload', null, 'function')
@@ -701,18 +707,19 @@ class TriggerView extends Component {
ciNodeId,
this.props.match.params.appId,
getBranchValues(ciNodeId, this.state.workflows, this.state.filteredCIPipelines),
+ this.props.appContext.currentAppName,
)
- : { result: null },
+ : null,
getRuntimeParams?.(ciNodeId) ?? null,
])
.then((resp) => {
// For updateCIMaterialList, it's already being set inside the same function so not setting that
- if (resp[1].result) {
+ if (resp[1]) {
const workflows = [...this.state.workflows].map((workflow) => {
workflow.nodes.map((node) => {
if (node.type === 'CI' && node.id == ciNodeId) {
- node.ciBlockState = processConsequenceData(resp[1].result)
- node.isCITriggerBlocked = resp[1].result.isCITriggerBlocked
+ node.pluginBlockState = processConsequenceData(resp[1])
+ node.isTriggerBlocked = resp[1].isCITriggerBlocked
return node
}
return node
@@ -954,6 +961,7 @@ class TriggerView extends Component {
buttonProps: {
text: 'Edit Pipeline',
dataTestId: 'edit-pipeline-btn',
+ onClick: this.redirectToCIPipeline,
},
})
} else {
@@ -1203,8 +1211,8 @@ class TriggerView extends Component {
isCacheAvailable={nd?.storageConfigured}
appId={this.props.match.params.appId}
isJobView={this.props.isJobView}
- isCITriggerBlocked={nd?.isCITriggerBlocked}
- ciBlockState={nd?.ciBlockState}
+ isCITriggerBlocked={nd?.isTriggerBlocked}
+ ciBlockState={nd?.pluginBlockState}
selectedEnv={this.state.selectedEnv}
setSelectedEnv={this.setSelectedEnv}
environmentLists={this.state.environmentLists}
@@ -1239,10 +1247,50 @@ class TriggerView extends Component {
return node ?? ({} as CommonNodeAttr)
}
+ renderCDMaterialContent = (cdNode: CommonNodeAttr) => {
+ const selectedWorkflow = this.state.workflows.find((wf) => wf.nodes.some((node) => node.id === cdNode.id))
+ const selectedCINode = selectedWorkflow?.nodes.find((node) => node.type === 'CI' || node.type === 'WEBHOOK')
+ const doesWorkflowContainsWebhook = selectedCINode?.type === 'WEBHOOK'
+ const configurePluginURL = getCDPipelineURL(
+ this.props.match.params.appId,
+ selectedWorkflow.id,
+ doesWorkflowContainsWebhook ? '0' : selectedCINode?.id,
+ doesWorkflowContainsWebhook,
+ cdNode.id,
+ true,
+ )
+
+ return (
+
+ )
+ }
+
renderCDMaterial() {
if (this.props.location.search.includes('cd-node') || this.props.location.search.includes('rollback-node')) {
- const node: CommonNodeAttr = this.getCDNode()
- const material = node[this.state.materialType] || []
+ const cdNode: CommonNodeAttr = this.getCDNode()
+ if (!cdNode.id) {
+ return null
+ }
+ const material = cdNode[this.state.materialType] || []
return (
@@ -1254,7 +1302,7 @@ class TriggerView extends Component {
>
{this.state.isLoading ? (
<>
-
+
@@ -1264,22 +1312,7 @@ class TriggerView extends Component {
>
) : (
-
+ this.renderCDMaterialContent(cdNode)
)}
@@ -1441,4 +1474,4 @@ class TriggerView extends Component {
}
}
-export default withRouter(TriggerView)
+export default withRouter(withAppContext(TriggerView))
diff --git a/src/components/app/details/triggerView/TriggerView.utils.tsx b/apps/web/src/components/app/details/triggerView/TriggerView.utils.tsx
similarity index 100%
rename from src/components/app/details/triggerView/TriggerView.utils.tsx
rename to apps/web/src/components/app/details/triggerView/TriggerView.utils.tsx
diff --git a/src/components/app/details/triggerView/__mocks__/trigger.view.mock.ts b/apps/web/src/components/app/details/triggerView/__mocks__/trigger.view.mock.ts
similarity index 100%
rename from src/components/app/details/triggerView/__mocks__/trigger.view.mock.ts
rename to apps/web/src/components/app/details/triggerView/__mocks__/trigger.view.mock.ts
diff --git a/src/components/app/details/triggerView/__mocks__/workflow.mock.ts b/apps/web/src/components/app/details/triggerView/__mocks__/workflow.mock.ts
similarity index 100%
rename from src/components/app/details/triggerView/__mocks__/workflow.mock.ts
rename to apps/web/src/components/app/details/triggerView/__mocks__/workflow.mock.ts
diff --git a/src/components/app/details/triggerView/__mocks__/workflow.sequential.mock.ts b/apps/web/src/components/app/details/triggerView/__mocks__/workflow.sequential.mock.ts
similarity index 100%
rename from src/components/app/details/triggerView/__mocks__/workflow.sequential.mock.ts
rename to apps/web/src/components/app/details/triggerView/__mocks__/workflow.sequential.mock.ts
diff --git a/src/components/app/details/triggerView/__tests__/triggerview.test.tsx b/apps/web/src/components/app/details/triggerView/__tests__/triggerview.test.tsx
similarity index 100%
rename from src/components/app/details/triggerView/__tests__/triggerview.test.tsx
rename to apps/web/src/components/app/details/triggerView/__tests__/triggerview.test.tsx
diff --git a/src/components/app/details/triggerView/__tests__/workflow.service.test.ts b/apps/web/src/components/app/details/triggerView/__tests__/workflow.service.test.ts
similarity index 100%
rename from src/components/app/details/triggerView/__tests__/workflow.service.test.ts
rename to apps/web/src/components/app/details/triggerView/__tests__/workflow.service.test.ts
diff --git a/src/components/app/details/triggerView/assets/cdMaterialFooter.png b/apps/web/src/components/app/details/triggerView/assets/cdMaterialFooter.png
similarity index 100%
rename from src/components/app/details/triggerView/assets/cdMaterialFooter.png
rename to apps/web/src/components/app/details/triggerView/assets/cdMaterialFooter.png
diff --git a/src/components/app/details/triggerView/assets/configDiff.png b/apps/web/src/components/app/details/triggerView/assets/configDiff.png
similarity index 100%
rename from src/components/app/details/triggerView/assets/configDiff.png
rename to apps/web/src/components/app/details/triggerView/assets/configDiff.png
diff --git a/src/components/app/details/triggerView/assets/configHeader.png b/apps/web/src/components/app/details/triggerView/assets/configHeader.png
similarity index 100%
rename from src/components/app/details/triggerView/assets/configHeader.png
rename to apps/web/src/components/app/details/triggerView/assets/configHeader.png
diff --git a/src/components/app/details/triggerView/cdMaterial.readme.md b/apps/web/src/components/app/details/triggerView/cdMaterial.readme.md
similarity index 100%
rename from src/components/app/details/triggerView/cdMaterial.readme.md
rename to apps/web/src/components/app/details/triggerView/cdMaterial.readme.md
diff --git a/src/components/app/details/triggerView/cdMaterial.tsx b/apps/web/src/components/app/details/triggerView/cdMaterial.tsx
similarity index 95%
rename from src/components/app/details/triggerView/cdMaterial.tsx
rename to apps/web/src/components/app/details/triggerView/cdMaterial.tsx
index 4809155d78..8fa41f5082 100644
--- a/src/components/app/details/triggerView/cdMaterial.tsx
+++ b/apps/web/src/components/app/details/triggerView/cdMaterial.tsx
@@ -16,7 +16,7 @@
import React, { useContext, useEffect, useRef, useState } from 'react'
import ReactGA from 'react-ga4'
-import { generatePath, Prompt, useHistory, useRouteMatch } from 'react-router-dom'
+import { Prompt, useHistory, useLocation } from 'react-router-dom'
import {
CDMaterialType,
showError,
@@ -77,6 +77,7 @@ import {
AppDetailsPayload,
ResponseType,
ApiResponseResultType,
+ CommonNodeAttr,
} from '@devtron-labs/devtron-fe-common-lib'
import Tippy from '@tippyjs/react'
import {
@@ -111,7 +112,7 @@ import {
import { TRIGGER_VIEW_GA_EVENTS, CD_MATERIAL_GA_EVENT, TRIGGER_VIEW_PARAMS } from './Constants'
import { EMPTY_STATE_STATUS, TOAST_BUTTON_TEXT_VIEW_DETAILS } from '../../../../config/constantMessaging'
import { getInitialState, getWfrId } from './cdMaterials.utils'
-import { DEFAULT_ROUTE_PROMPT_MESSAGE } from '../../../../config'
+import { DEFAULT_ROUTE_PROMPT_MESSAGE, URLS } from '../../../../config'
import { PipelineConfigDiff } from './PipelineConfigDiff'
import { usePipelineDeploymentConfig } from './PipelineConfigDiff/usePipelineDeploymentConfig'
import { PipelineConfigDiffStatusTile } from './PipelineConfigDiff/PipelineConfigDiffStatusTile'
@@ -149,6 +150,8 @@ const getSecurityScan: ({
'function',
)
const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function')
+const AllowedWithWarningTippy = importComponentFromFELibrary('AllowedWithWarningTippy')
+const MissingPluginBlockState = importComponentFromFELibrary('MissingPluginBlockState', null, 'function')
const CDMaterial = ({
materialType,
@@ -179,11 +182,15 @@ const CDMaterial = ({
handleBulkRuntimeParamChange,
handleBulkRuntimeParamError,
bulkSidebarTab,
+ showPluginWarningBeforeTrigger: _showPluginWarningBeforeTrigger = false,
+ consequence,
+ configurePluginURL,
+ isTriggerBlockedDueToPlugin,
}: Readonly) => {
// stageType should handle approval node, compute CDMaterialServiceEnum, create queryParams state
// FIXME: the query params returned by useSearchString seems faulty
const history = useHistory()
- const { path, params } = useRouteMatch()
+ const { pathname } = useLocation()
const { searchParams } = useSearchString()
const { handleDownload } = useDownload()
// Add dep here
@@ -196,17 +203,21 @@ const CDMaterial = ({
const { email } = useUserEmail()
const searchImageTag = searchParams.search
- const isScanV2Enabled = window._env_.ENABLE_RESOURCE_SCAN_V2 && !!isFELibAvailable
const [material, setMaterial] = useState([])
const [state, setState] = useState(getInitialState(materialType, material, searchImageTag))
// It is derived from materialResult and can be fixed as a constant fix this
const [isConsumedImageAvailable, setIsConsumedImageAvailable] = useState(false)
+ const [showPluginWarningOverlay, setShowPluginWarningOverlay] = useState(false)
// Should be able to abort request using useAsync
const abortControllerRef = useRef(new AbortController())
const abortDeployRef = useRef(null)
const isPreOrPostCD = stageType === DeploymentNodeType.PRECD || stageType === DeploymentNodeType.POSTCD
+ const showPluginWarningBeforeTrigger = _showPluginWarningBeforeTrigger && isPreOrPostCD
+ // This check assumes we have isPreOrPostCD as true
+ const allowWarningWithTippyNodeTypeProp: CommonNodeAttr['type'] =
+ stageType === DeploymentNodeType.PRECD ? 'PRECD' : 'POSTCD'
// TODO: Ask if pipelineId always changes on change of app else add appId as dependency
const [loadingMaterials, responseList, materialsError, reloadMaterials] = useAsync(
@@ -244,7 +255,7 @@ const CDMaterial = ({
),
// NOTE: Add state.filterView if want to add filtering support from backend
[pipelineId, stageType, materialType, searchImageTag],
- !!pipelineId,
+ !!pipelineId && !isTriggerBlockedDueToPlugin,
)
const materialsResult: CDMaterialResponseType = responseList?.[0]
@@ -270,7 +281,7 @@ const CDMaterial = ({
const userApprovalConfig = materialsResult?.userApprovalConfig
const isApprovalConfigured = getIsManualApprovalConfigured(userApprovalConfig)
const canApproverDeploy = materialsResult?.canApproverDeploy ?? false
- const showConfigDiffView = searchParams.mode === 'review-config' && searchParams.deploy && searchParams.diffView
+ const showConfigDiffView = searchParams.mode === 'review-config' && searchParams.deploy
const {
pipelineDeploymentConfigLoading,
@@ -337,6 +348,7 @@ const CDMaterial = ({
abortDeployRef.current = new AbortController()
return () => {
abortDeployRef.current.abort()
+ history.replace(pathname.split(URLS.APP_DIFF_VIEW)[0])
}
}, [])
@@ -743,11 +755,9 @@ const CDMaterial = ({
...searchParams,
mode: modeParamValue,
deploy: getConfigToDeployValue(),
- diffView: 'true',
})
if (modeParamValue === 'list') {
- newParams.delete('diffView')
newParams.delete('sortOrder')
newParams.delete('sortBy')
}
@@ -755,8 +765,8 @@ const CDMaterial = ({
history.push({
pathname:
modeParamValue === 'review-config'
- ? `${generatePath(path, params)}/${EnvResourceType.DeploymentTemplate}`
- : generatePath(path, params),
+ ? `${pathname}/${URLS.APP_DIFF_VIEW}/${EnvResourceType.DeploymentTemplate}`
+ : `${pathname.split(`/${URLS.APP_DIFF_VIEW}`)[0]}`,
search: newParams.toString(),
})
}
@@ -1063,6 +1073,15 @@ const CDMaterial = ({
consumedImagePresent?: boolean,
noEligibleImages?: boolean,
) => {
+ if (isTriggerBlockedDueToPlugin && MissingPluginBlockState) {
+ return (
+
+ )
+ }
+
if (
resourceFilters?.length &&
noEligibleImages &&
@@ -1603,6 +1622,11 @@ const CDMaterial = ({
const onClickDeploy = (e, disableDeployButton: boolean) => {
e.stopPropagation()
if (!disableDeployButton) {
+ if (!showPluginWarningOverlay && showPluginWarningBeforeTrigger) {
+ setShowPluginWarningOverlay(true)
+ return
+ }
+
if (
deploymentWindowMetadata.userActionState &&
deploymentWindowMetadata.userActionState !== ACTION_STATE.ALLOWED
@@ -1615,11 +1639,32 @@ const CDMaterial = ({
}
}
+ const renderTriggerDeployButton = (disableDeployButton: boolean) => (
+ onClickDeploy(e, disableDeployButton)}
+ type="button"
+ >
+ {deploymentLoading || isSaveLoading ? (
+
+ ) : (
+ <>
+ {getDeployButtonIcon()}
+ {deploymentWindowMetadata.userActionState === ACTION_STATE.BLOCKED
+ ? 'Deployment is blocked'
+ : CDButtonLabelMap[stageType]}
+ {isVirtualEnvironment && ' to isolated env'}
+ {deploymentWindowMetadata.userActionState === ACTION_STATE.BLOCKED && (
+
+ )}
+ >
+ )}
+
+ )
+
const renderTriggerModalCTA = (isApprovalConfigured: boolean) => {
- const buttonLabel =
- deploymentWindowMetadata.userActionState === ACTION_STATE.BLOCKED
- ? 'Deployment is blocked'
- : CDButtonLabelMap[stageType]
const disableDeployButton =
isDeployButtonDisabled() ||
(material.length > 0 && getIsImageApprover(state.selectedMaterial?.userApprovalMetadata))
@@ -1665,26 +1710,22 @@ const CDMaterial = ({
)}
>
- onClickDeploy(e, disableDeployButton)}
- type="button"
- >
- {deploymentLoading || isSaveLoading ? (
-
- ) : (
- <>
- {getDeployButtonIcon()}
- {buttonLabel}
- {isVirtualEnvironment && ' to isolated env'}
- {deploymentWindowMetadata.userActionState === ACTION_STATE.BLOCKED && (
-
- )}
- >
- )}
-
+ {AllowedWithWarningTippy &&
+ showPluginWarningBeforeTrigger ? (
+ onClickDeploy(e, disableDeployButton)}
+ nodeType={allowWarningWithTippyNodeTypeProp}
+ visible={showPluginWarningOverlay}
+ onClose={handleClosePluginWarningOverlay}
+ >
+ {renderTriggerDeployButton(disableDeployButton)}
+
+ ) : (
+ renderTriggerDeployButton(disableDeployButton)
+ )}
)
@@ -1711,8 +1752,13 @@ const CDMaterial = ({
)
+ const handleClosePluginWarningOverlay = () => {
+ setShowPluginWarningOverlay(false)
+ }
+
const handleConfirmationClose = (e) => {
e.stopPropagation()
+ handleClosePluginWarningOverlay()
setShowDeploymentWindowConfirmation(false)
}
diff --git a/src/components/app/details/triggerView/cdMaterials.utils.ts b/apps/web/src/components/app/details/triggerView/cdMaterials.utils.ts
similarity index 100%
rename from src/components/app/details/triggerView/cdMaterials.utils.ts
rename to apps/web/src/components/app/details/triggerView/cdMaterials.utils.ts
diff --git a/src/components/app/details/triggerView/ciMaterial.tsx b/apps/web/src/components/app/details/triggerView/ciMaterial.tsx
similarity index 96%
rename from src/components/app/details/triggerView/ciMaterial.tsx
rename to apps/web/src/components/app/details/triggerView/ciMaterial.tsx
index 0543910ae7..a3e41ed7d2 100644
--- a/src/components/app/details/triggerView/ciMaterial.tsx
+++ b/apps/web/src/components/app/details/triggerView/ciMaterial.tsx
@@ -27,6 +27,7 @@ import {
SourceTypeMap,
ToastManager,
ToastVariantType,
+ CommonNodeAttr,
} from '@devtron-labs/devtron-fe-common-lib'
import { CIMaterialProps, CIMaterialState, RegexValueType } from './types'
import { ReactComponent as Play } from '../../../../assets/icons/misc/arrow-solid-right.svg'
@@ -212,12 +213,23 @@ class CIMaterial extends Component
{
}
renderCTAButton = (canTrigger) => {
+ const nodeType: CommonNodeAttr['type'] = 'CI'
+
if (AllowedWithWarningTippy && this.props.ciBlockState && !this.props.isJobView) {
return (
void
@@ -65,6 +69,18 @@ type CDMaterialBulkRuntimeParams =
bulkSidebarTab?: never
}
+type CDMaterialPluginWarningProps =
+ | {
+ showPluginWarningBeforeTrigger: boolean
+ consequence: ConsequenceType
+ configurePluginURL: string
+ }
+ | {
+ showPluginWarningBeforeTrigger?: never
+ consequence?: never
+ configurePluginURL?: never
+ }
+
export type CDMaterialProps = {
material?: CDMaterialType[]
isLoading: boolean
@@ -133,7 +149,9 @@ export type CDMaterialProps = {
* To be consumed through variable called appName
*/
selectedAppName?: string
-} & CDMaterialBulkRuntimeParams
+ isTriggerBlockedDueToPlugin?: boolean
+} & CDMaterialBulkRuntimeParams &
+ CDMaterialPluginWarningProps
export interface ConfigToDeployOptionType {
label: string
@@ -211,10 +229,7 @@ export interface CIMaterialProps extends RouteComponentProps>
environmentLists?: any[]
@@ -287,7 +302,9 @@ export interface TriggerCDNodeState {
gitOpsRepoWarningCondition: boolean
}
-export interface TriggerPrePostCDNodeProps extends RouteComponentProps<{ appId: string }> {
+export interface TriggerPrePostCDNodeProps
+ extends RouteComponentProps<{ appId: string }>,
+ Pick {
x: number
y: number
height: number
@@ -369,6 +386,7 @@ export interface TriggerViewRouterProps {
export interface TriggerViewProps extends RouteComponentProps {
isJobView?: boolean
filteredEnvIds?: string
+ appContext: AppContextType
}
export interface WebhookPayloadDataResponse {
@@ -520,40 +538,6 @@ export interface CiScript {
outputLocation?: string
}
-export interface CiPipeline {
- isManual: boolean
- dockerArgs?: Map
- isExternal: boolean
- parentCiPipeline: number
- parentAppId: number
- externalCiConfig: ExternalCiConfig
- ciMaterial?: CiMaterial[]
- name?: string
- id?: number
- active?: boolean
- linkedCount: number
- scanEnabled: boolean
- deleted?: boolean
- version?: string
- beforeDockerBuild?: Array
- afterDockerBuild?: Array
- appWorkflowId?: number
- beforeDockerBuildScripts?: Array
- afterDockerBuildScripts?: Array
- isDockerConfigOverridden?: boolean
- dockerConfigOverride?: DockerConfigOverrideType
- appName?: string
- appId?: string
- componentId?: number
- isCITriggerBlocked?: boolean
- ciBlockState?: {
- action: any
- metadataField: string
- }
- isOffendingMandatoryPlugin?: boolean
- pipelineType?: string
-}
-
export interface CiPipelineResult {
id?: number
appId?: number
@@ -599,43 +583,6 @@ export interface PrePostDeployStageType {
status: string
}
-// Remove this and use from fe-common
-/**
- * @deprecated
- */
-export interface CdPipeline {
- id: number
- environmentId: number
- environmentName?: string
- description?: string
- ciPipelineId: number
- triggerType: 'AUTOMATIC' | 'MANUAL'
- name: string
- strategies?: Strategy[]
- namespace?: string
- appWorkflowId?: number
- deploymentTemplate?: string
- preStage?: CDStage
- postStage?: CDStage
- preStageConfigMapSecretNames?: CDStageConfigMapSecretNames
- postStageConfigMapSecretNames?: CDStageConfigMapSecretNames
- runPreStageInEnv?: boolean
- runPostStageInEnv?: boolean
- isClusterCdActive?: boolean
- parentPipelineId?: number
- parentPipelineType?: string
- deploymentAppDeleteRequest?: boolean
- deploymentAppCreated?: boolean
- userApprovalConfig?: UserApprovalConfigType
- isVirtualEnvironment?: boolean
- deploymentAppType: DeploymentAppTypes
- helmPackageName?: string
- preDeployStage?: PrePostDeployStageType
- postDeployStage?: PrePostDeployStageType
- isGitOpsRepoNotConfigured?: boolean
- isDeploymentBlocked?: boolean
-}
-
export interface CdPipelineResult {
pipelines?: CdPipeline[]
appId: number
@@ -751,3 +698,16 @@ export interface CIMaterialModalProps extends CIMaterialProps {
abortController: AbortController
resetAbortController: () => void
}
+
+export type OffendingWorkflowQueryParamType = `policy/${PolicyKindType}|identifier|${string}`
+
+export interface GetInitialWorkflowsParamsType {
+ id: any
+ dimensions: WorkflowDimensions
+ workflowOffset: Offset
+ useAppWfViewAPI?: boolean
+ isJobView?: boolean
+ filteredEnvIds?: string
+ shouldCheckDeploymentWindow?: boolean
+ offending?: OffendingWorkflowQueryParamType
+}
diff --git a/src/components/app/details/triggerView/workflow.service.ts b/apps/web/src/components/app/details/triggerView/workflow.service.ts
similarity index 92%
rename from src/components/app/details/triggerView/workflow.service.ts
rename to apps/web/src/components/app/details/triggerView/workflow.service.ts
index 6e5d992e88..597d6a2ced 100644
--- a/src/components/app/details/triggerView/workflow.service.ts
+++ b/apps/web/src/components/app/details/triggerView/workflow.service.ts
@@ -22,16 +22,17 @@ import {
DownstreamNodesEnvironmentsType,
WorkflowType,
getIsManualApprovalConfigured,
+ CiPipeline,
+ CdPipeline,
} from '@devtron-labs/devtron-fe-common-lib'
import { getCDConfig, getCIConfig, getWorkflowList, getWorkflowViewList } from '../../../../services/service'
import {
- CdPipeline,
CdPipelineResult,
- CiPipeline,
CiPipelineResult,
Workflow,
WorkflowResult,
AddDimensionsToDownstreamDeploymentsParams,
+ GetInitialWorkflowsParamsType,
} from './types'
import { WorkflowTrigger, WorkflowCreate, Offset, WorkflowDimensions, WorkflowDimensionType } from './config'
import { TriggerType, DEFAULT_STATUS, GIT_BRANCH_NOT_CONFIGURED } from '../../../../config'
@@ -43,22 +44,23 @@ import { BlackListedCI } from '../../../workflowEditor/types'
const getDeploymentWindowState = importComponentFromFELibrary('getDeploymentWindowState', null, 'function')
const getDeploymentNotAllowedState = importComponentFromFELibrary('getDeploymentNotAllowedState', null, 'function')
+const getParsedPluginPolicyConsequenceData = importComponentFromFELibrary('getParsedPluginPolicyConsequenceData', () => null, 'function')
export const getTriggerWorkflows = (
appId,
useAppWfViewAPI: boolean,
isJobView: boolean,
filteredEnvIds?: string,
-): Promise<{ appName: string; workflows: WorkflowType[]; filteredCIPipelines }> => {
- return getInitialWorkflows(
- appId,
- WorkflowTrigger,
- WorkflowTrigger.workflow,
+): Promise<{ appName: string; workflows: WorkflowType[]; filteredCIPipelines }> =>
+ getInitialWorkflows({
+ id: appId,
+ dimensions: WorkflowTrigger,
+ workflowOffset: WorkflowTrigger.workflow,
useAppWfViewAPI,
isJobView,
filteredEnvIds,
- )
-}
+ })
+
export const getCreateWorkflows = (
appId,
@@ -71,18 +73,26 @@ export const getCreateWorkflows = (
filteredCIPipelines
cachedCDConfigResponse: CdPipelineResult
blackListedCI: BlackListedCI
-}> => {
- return getInitialWorkflows(appId, WorkflowCreate, WorkflowCreate.workflow, false, isJobView, filteredEnvIds)
-}
+}> =>
+ getInitialWorkflows({
+ id: appId,
+ dimensions: WorkflowCreate,
+ workflowOffset: WorkflowCreate.workflow,
+ useAppWfViewAPI: false,
+ isJobView,
+ filteredEnvIds,
+ })
-const getInitialWorkflows = (
+export const getInitialWorkflows = ({
id,
- dimensions: WorkflowDimensions,
- workflowOffset: Offset,
- useAppWfViewAPI?: boolean,
- isJobView?: boolean,
- filteredEnvIds?: string,
-): Promise<{
+ dimensions,
+ workflowOffset,
+ useAppWfViewAPI,
+ isJobView,
+ filteredEnvIds,
+ shouldCheckDeploymentWindow = true,
+ offending = null,
+}: GetInitialWorkflowsParamsType): Promise<{
isGitOpsRepoNotConfigured: boolean
appName: string
workflows: WorkflowType[]
@@ -91,9 +101,12 @@ const getInitialWorkflows = (
blackListedCI: BlackListedCI
}> => {
if (useAppWfViewAPI) {
+ // TODO: Seems like a good candidate for Promise.allSettled
return Promise.all([
- getWorkflowViewList(id, filteredEnvIds),
- getDeploymentWindowState ? getDeploymentWindowState(id, filteredEnvIds) : null,
+ getWorkflowViewList(id, filteredEnvIds, offending),
+ shouldCheckDeploymentWindow && getDeploymentWindowState
+ ? getDeploymentWindowState(id, filteredEnvIds)
+ : null,
]).then((response) => {
const workflows = {
appId: id,
@@ -140,7 +153,7 @@ const getInitialWorkflows = (
getCIConfig(id),
getCDConfig(id),
getExternalCIList(id),
- getDeploymentWindowState ? getDeploymentWindowState(id, filteredEnvIds) : null,
+ shouldCheckDeploymentWindow && getDeploymentWindowState ? getDeploymentWindowState(id, filteredEnvIds) : null,
]).then(([workflow, ciConfig, cdConfig, externalCIConfig, deploymentWindowState]) => {
if (Object.keys(deploymentWindowState?.result || {}).length > 0 && cdConfig) {
cdConfig.pipelines?.forEach((pipeline) => {
@@ -534,7 +547,7 @@ function ciPipelineToNode(
dimensions: WorkflowDimensions,
cdPipelineMap: Map,
): CommonNodeAttr {
- const sourceNodes = (ciPipeline?.ciMaterial ?? []).map((ciMaterial, index) => {
+ const sourceNodes: CommonNodeAttr[] = (ciPipeline?.ciMaterial ?? []).map((ciMaterial, index) => {
const materialName = ciMaterial.gitMaterialName || ''
return {
parents: [],
@@ -559,7 +572,10 @@ function ciPipelineToNode(
primaryBranchAfterRegex: ciMaterial?.source?.value,
cipipelineId: ciMaterial?.id,
isJobCI: ciPipeline?.pipelineType === CIPipelineBuildType.CI_JOB,
- } as CommonNodeAttr
+ showPluginWarning: false,
+ isTriggerBlocked: false,
+ pluginBlockState: getParsedPluginPolicyConsequenceData(),
+ }
})
const trigger = ciPipeline.isManual ? TriggerType.Manual.toLocaleLowerCase() : TriggerType.Auto.toLocaleLowerCase()
@@ -569,7 +585,7 @@ function ciPipelineToNode(
? cdPipelineMap.get(ciPipeline.parentCiPipeline).environmentName
: ciPipeline.name
- const ciNode = {
+ const ciNode: CommonNodeAttr = {
isSource: true,
isGitSource: false,
isRoot: false,
@@ -597,9 +613,9 @@ function ciPipelineToNode(
sourceNodes,
downstreamNodes: new Array(),
showPluginWarning: ciPipeline.isOffendingMandatoryPlugin,
- isCITriggerBlocked: ciPipeline.isCITriggerBlocked,
- ciBlockState: ciPipeline.ciBlockState,
- } as CommonNodeAttr
+ isTriggerBlocked: ciPipeline.isCITriggerBlocked,
+ pluginBlockState: getParsedPluginPolicyConsequenceData(ciPipeline.ciBlockState),
+ }
return ciNode
}
@@ -638,6 +654,7 @@ function cdPipelineToNode(
let preCD: CommonNodeAttr | undefined
let postCD: CommonNodeAttr | undefined
let stageIndex = 1
+
if (!isEmpty(cdPipeline?.preDeployStage?.steps || cdPipeline?.preStage?.config)) {
const trigger =
cdPipeline.preDeployStage?.triggerType?.toLowerCase() ||
@@ -653,8 +670,6 @@ function cdPipelineToNode(
isSource: false,
isGitSource: false,
id: String(cdPipeline.id),
- activeIn: false,
- activeOut: false,
downstreams: [`${WorkflowNodeType.CD}-${cdPipeline.id}`],
downstreamEnvironments: [],
type: WorkflowNodeType.PRE_CD,
@@ -675,7 +690,10 @@ function cdPipelineToNode(
helmPackageName: cdPipeline?.helmPackageName || '',
isGitOpsRepoNotConfigured: cdPipeline.isGitOpsRepoNotConfigured,
isDeploymentBlocked: cdPipeline.isDeploymentBlocked,
- } as CommonNodeAttr
+ showPluginWarning: cdPipeline.preDeployStage?.isOffendingMandatoryPlugin,
+ isTriggerBlocked: cdPipeline.preDeployStage?.isTriggerBlocked,
+ pluginBlockState: getParsedPluginPolicyConsequenceData(cdPipeline.preDeployStage?.pluginBlockState),
+ }
stageIndex++
}
let cdDownstreams = []
@@ -686,7 +704,7 @@ function cdPipelineToNode(
cdDownstreams = [`${WorkflowNodeType.POST_CD}-${cdPipeline.id}`]
}
- const CD = {
+ const CD: CommonNodeAttr = {
connectingCiPipelineId: cdPipeline.ciPipelineId,
parents: [String(parentId)],
height: dimensions.cDNodeSizes.nodeHeight,
@@ -695,8 +713,6 @@ function cdPipelineToNode(
isSource: false,
isGitSource: false,
id: String(cdPipeline.id),
- activeIn: false,
- activeOut: false,
downstreams: cdDownstreams,
downstreamEnvironments: [],
type: WorkflowNodeType.CD,
@@ -726,7 +742,11 @@ function cdPipelineToNode(
isGitOpsRepoNotConfigured: cdPipeline.isGitOpsRepoNotConfigured,
deploymentAppCreated: cdPipeline?.deploymentAppCreated,
isDeploymentBlocked: cdPipeline.isDeploymentBlocked,
- } as CommonNodeAttr
+ // Will populate this after initializing postCD
+ showPluginWarning: false,
+ isTriggerBlocked: false,
+ pluginBlockState: getParsedPluginPolicyConsequenceData(),
+ }
stageIndex++
if (!isEmpty(cdPipeline?.postDeployStage?.steps || cdPipeline?.postStage?.config)) {
@@ -744,8 +764,6 @@ function cdPipelineToNode(
isSource: false,
isGitSource: false,
id: String(cdPipeline.id),
- activeIn: false,
- activeOut: false,
downstreams: [],
downstreamEnvironments: [],
type: WorkflowNodeType.POST_CD,
@@ -766,12 +784,17 @@ function cdPipelineToNode(
helmPackageName: cdPipeline?.helmPackageName || '',
isGitOpsRepoNotConfigured: cdPipeline.isGitOpsRepoNotConfigured,
isDeploymentBlocked: cdPipeline.isDeploymentBlocked,
- } as CommonNodeAttr
+ showPluginWarning: cdPipeline.postDeployStage?.isOffendingMandatoryPlugin,
+ isTriggerBlocked: cdPipeline.postDeployStage?.isTriggerBlocked,
+ pluginBlockState: getParsedPluginPolicyConsequenceData(cdPipeline.postDeployStage?.pluginBlockState),
+ }
}
+
if (dimensions.type === WorkflowDimensionType.TRIGGER) {
CD.preNode = preCD
CD.postNode = postCD
}
+
if (dimensions.type === WorkflowDimensionType.CREATE) {
let title = ''
title += preCD ? 'Pre-deploy, ' : ''
@@ -779,6 +802,10 @@ function cdPipelineToNode(
title += postCD ? ', Post-deploy' : ''
CD.title = title
}
+
+ CD.showPluginWarning = preCD?.showPluginWarning || postCD?.showPluginWarning
+ CD.isTriggerBlocked = false
+ CD.pluginBlockState = getParsedPluginPolicyConsequenceData()
return CD
}
diff --git a/src/components/app/details/triggerView/workflow/Workflow.tsx b/apps/web/src/components/app/details/triggerView/workflow/Workflow.tsx
similarity index 98%
rename from src/components/app/details/triggerView/workflow/Workflow.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/Workflow.tsx
index ae66155803..85b84fedc1 100644
--- a/src/components/app/details/triggerView/workflow/Workflow.tsx
+++ b/apps/web/src/components/app/details/triggerView/workflow/Workflow.tsx
@@ -166,7 +166,7 @@ export class Workflow extends Component {
location={this.props.location}
match={this.props.match}
fromAppGrouping={this.props.fromAppGrouping}
- isCITriggerBlocked={node.isCITriggerBlocked}
+ isCITriggerBlocked={node.isTriggerBlocked}
/>
)
}
@@ -222,8 +222,8 @@ export class Workflow extends Component {
fromAppGrouping={this.props.fromAppGrouping}
isJobView={this.props.isJobView}
index={this.props.index}
- isCITriggerBlocked={node.isCITriggerBlocked}
- ciBlockState={node.ciBlockState}
+ isCITriggerBlocked={node.isTriggerBlocked}
+ ciBlockState={node.pluginBlockState}
filteredCIPipelines={this.props.filteredCIPipelines}
environmentLists={this.props.environmentLists}
/>
@@ -296,6 +296,7 @@ export class Workflow extends Component {
isGitOpsRepoNotConfigured={node.isGitOpsRepoNotConfigured}
deploymentAppType={node.deploymentAppType}
isDeploymentBlocked={node.isDeploymentBlocked}
+ isTriggerBlocked={node.isTriggerBlocked}
/>
)
}
diff --git a/src/components/app/details/triggerView/workflow/nodes/TriggerExternalCINode.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/TriggerExternalCINode.tsx
similarity index 100%
rename from src/components/app/details/triggerView/workflow/nodes/TriggerExternalCINode.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/TriggerExternalCINode.tsx
diff --git a/src/components/app/details/triggerView/workflow/nodes/TriggerLinkedCINode.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/TriggerLinkedCINode.tsx
similarity index 100%
rename from src/components/app/details/triggerView/workflow/nodes/TriggerLinkedCINode.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/TriggerLinkedCINode.tsx
diff --git a/src/components/app/details/triggerView/workflow/nodes/staticNode.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/staticNode.tsx
similarity index 100%
rename from src/components/app/details/triggerView/workflow/nodes/staticNode.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/staticNode.tsx
diff --git a/src/components/app/details/triggerView/workflow/nodes/triggerCDNode.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/triggerCDNode.tsx
similarity index 100%
rename from src/components/app/details/triggerView/workflow/nodes/triggerCDNode.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/triggerCDNode.tsx
diff --git a/src/components/app/details/triggerView/workflow/nodes/triggerCINode.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/triggerCINode.tsx
similarity index 98%
rename from src/components/app/details/triggerView/workflow/nodes/triggerCINode.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/triggerCINode.tsx
index 975a4d293c..5bad69a470 100644
--- a/src/components/app/details/triggerView/workflow/nodes/triggerCINode.tsx
+++ b/apps/web/src/components/app/details/triggerView/workflow/nodes/triggerCINode.tsx
@@ -17,7 +17,7 @@
import React, { Component } from 'react'
import { RouteComponentProps, Link } from 'react-router-dom'
import Tippy from '@tippyjs/react'
-import { CIMaterialType } from '@devtron-labs/devtron-fe-common-lib'
+import { CIMaterialType, ConsequenceType } from '@devtron-labs/devtron-fe-common-lib'
import { TriggerStatus } from '../../../../config'
import { BUILD_STATUS, DEFAULT_STATUS, URLS } from '../../../../../../config'
import { ReactComponent as IcLink } from '../../../../../../assets/icons/ic-link.svg'
@@ -49,10 +49,7 @@ export interface TriggerCINodeProps extends RouteComponentProps<{ appId: string
isJobView?: boolean
index?: number
isCITriggerBlocked?: boolean
- ciBlockState?: {
- action: any
- metadataField: string
- }
+ ciBlockState?: ConsequenceType
filteredCIPipelines?: any[]
environmentLists?: any[]
}
diff --git a/src/components/app/details/triggerView/workflow/nodes/triggerPrePostCDNode.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/triggerPrePostCDNode.tsx
similarity index 90%
rename from src/components/app/details/triggerView/workflow/nodes/triggerPrePostCDNode.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/triggerPrePostCDNode.tsx
index 0641a45fa7..5b1f540e0e 100644
--- a/src/components/app/details/triggerView/workflow/nodes/triggerPrePostCDNode.tsx
+++ b/apps/web/src/components/app/details/triggerView/workflow/nodes/triggerPrePostCDNode.tsx
@@ -89,6 +89,30 @@ export class TriggerPrePostCDNode extends Component {
+ if (this.props.isTriggerBlocked) {
+ return 'bcr-1 er-2 bw-1 cr-5 dc__opacity-1'
+ }
+
+ if (this.props.isDeploymentBlocked ) {
+ return 'bcy-5 cn-9 dc__opacity-1'
+ }
+
+ return ''
+ }
+
+ getNodeSideHeadingText = (): string => {
+ if (this.props.isTriggerBlocked) {
+ return 'BLOCKED'
+ }
+
+ if (this.props.isDeploymentBlocked) {
+ return DO_NOT_DEPLOY
+ }
+
+ return this.props.triggerType
+ }
+
renderCardContent() {
const status = this.props.status ? this.props.status.toLocaleLowerCase() : ''
const stage = this.props.type === 'PRECD' ? 'Pre-deployment' : 'Post-deployment'
@@ -111,9 +135,9 @@ export class TriggerPrePostCDNode extends Component
- {this.props.isDeploymentBlocked ? DO_NOT_DEPLOY : this.props.triggerType}
+ {this.getNodeSideHeadingText()}
diff --git a/src/components/app/details/triggerView/workflow/nodes/workflow.utils.tsx b/apps/web/src/components/app/details/triggerView/workflow/nodes/workflow.utils.tsx
similarity index 100%
rename from src/components/app/details/triggerView/workflow/nodes/workflow.utils.tsx
rename to apps/web/src/components/app/details/triggerView/workflow/nodes/workflow.utils.tsx
diff --git a/src/components/app/grepSSEworker.ts b/apps/web/src/components/app/grepSSEworker.ts
similarity index 100%
rename from src/components/app/grepSSEworker.ts
rename to apps/web/src/components/app/grepSSEworker.ts
diff --git a/src/components/app/list-new/AppList.tsx b/apps/web/src/components/app/list-new/AppList.tsx
similarity index 90%
rename from src/components/app/list-new/AppList.tsx
rename to apps/web/src/components/app/list-new/AppList.tsx
index b009ebf9a4..e192b46791 100644
--- a/src/components/app/list-new/AppList.tsx
+++ b/apps/web/src/components/app/list-new/AppList.tsx
@@ -30,7 +30,6 @@ import {
FilterChips,
handleUTCTime,
ModuleNameMap,
- getUrlWithSearchParams,
getNamespaceListMin,
} from '@devtron-labs/devtron-fe-common-lib'
import { getCommonAppFilters } from '@Services/service'
@@ -42,11 +41,12 @@ import { AppListPropType } from '../list/types'
import { AddNewApp } from '../create/CreateApp'
import '../list/list.scss'
import EAEmptyState, { EAEmptyStateType } from '../../common/eaEmptyState/EAEmptyState'
-import { APP_LISTING_URLS, FLUX_CD_HELM_RELEASE_LABEL } from './Constants'
+import { APP_LIST_LOCAL_STORAGE_KEY, APP_LISTING_URLS, FLUX_CD_HELM_RELEASE_LABEL } from './Constants'
import { getModuleInfo } from '../../v2/devtronStackManager/DevtronStackManager.service'
import {
getAppStatusFormattedValue,
getChangeAppTabURL,
+ getFilterChipConfig,
getFormattedFilterLabel,
parseSearchParams,
} from './list.utils'
@@ -93,6 +93,7 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
const urlFilters = useUrlFilters
({
initialSortKey: AppListSortableKeys.APP_NAME,
parseSearchParams,
+ localStorageKey: APP_LIST_LOCAL_STORAGE_KEY,
})
const {
searchKey,
@@ -214,7 +215,12 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
(currentNamespace) => clusterIdsMap.get(+currentNamespace.split('_')[0]) ?? false,
)
// To clear template type filter if cluster filter is cleared
- updateSearchParams({ namespace: updatedNamespaces, templateType: clusterIdsCsv ? templateType : [] })
+ // and clear environment filter if cluster is selected
+ updateSearchParams({
+ namespace: updatedNamespaces,
+ templateType: clusterIdsCsv ? templateType : [],
+ environment: clusterIdsCsv ? [] : environment,
+ })
}
}, [`${cluster}`])
@@ -262,16 +268,21 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
setFetchingExternalApps(fetching)
}
- const renderAppliedFilters = () => (
-
- filterConfig={{ appStatus, project, cluster, environment, namespace, templateType }}
- onRemoveFilter={updateSearchParams}
- clearFilters={clearFilters}
- className="px-20"
- getFormattedLabel={getFormattedFilterLabel}
- getFormattedValue={getFormattedFilterValue}
- />
- )
+ const renderAppliedFilters = () =>
+ !appListFiltersLoading &&
+ !appListFiltersError && (
+ >
+ filterConfig={getFilterChipConfig(
+ { appStatus, project, environment, cluster, namespace, templateType },
+ params.appType,
+ )}
+ onRemoveFilter={updateSearchParams}
+ clearFilters={clearFilters}
+ className="px-20"
+ getFormattedLabel={getFormattedFilterLabel}
+ getFormattedValue={getFormattedFilterValue}
+ />
+ )
const renderAppTabs = () => {
const tabs: TabProps[] = [
@@ -284,14 +295,7 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
props: {
to: {
pathname: getChangeAppTabURL(AppListConstants.AppTabs.DEVTRON_APPS),
- search: getUrlWithSearchParams('', {
- appStatus,
- project,
- environment,
- cluster,
- namespace,
- searchKey,
- }),
+ search: location.search,
},
'data-testid': 'devtron-app-list-button',
},
@@ -305,14 +309,7 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
props: {
to: {
pathname: getChangeAppTabURL(AppListConstants.AppTabs.HELM_APPS),
- search: getUrlWithSearchParams('', {
- appStatus,
- project,
- environment,
- cluster,
- namespace,
- searchKey,
- }),
+ search: location.search,
},
'data-testid': 'helm-app-list-button',
},
@@ -326,11 +323,7 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
props: {
to: {
pathname: getChangeAppTabURL(AppListConstants.AppTabs.ARGO_APPS),
- search: getUrlWithSearchParams('', {
- cluster,
- namespace,
- searchKey,
- }),
+ search: location.search,
},
'data-testid': 'argo-app-list-button',
},
@@ -346,12 +339,7 @@ const AppList = ({ isArgoInstalled }: AppListPropType) => {
props: {
to: {
pathname: getChangeAppTabURL(AppListConstants.AppTabs.FLUX_APPS),
- search: getUrlWithSearchParams('', {
- cluster,
- namespace,
- templateType,
- searchKey,
- }),
+ search: location.search,
},
'data-testid': 'flux-app-list-button',
},
diff --git a/src/components/app/list-new/AppListFilters.tsx b/apps/web/src/components/app/list-new/AppListFilters.tsx
similarity index 90%
rename from src/components/app/list-new/AppListFilters.tsx
rename to apps/web/src/components/app/list-new/AppListFilters.tsx
index 3302b9003f..5888fe6742 100644
--- a/src/components/app/list-new/AppListFilters.tsx
+++ b/apps/web/src/components/app/list-new/AppListFilters.tsx
@@ -48,6 +48,17 @@ const AppListFilters = ({
isExternalFlux,
})
+ const getIsClusterOptionDisabled = (option: SelectPickerOptionType): boolean => {
+ const clusterList = appListFiltersResponse?.isFullMode
+ ? appListFiltersResponse?.appListFilters.result.clusters
+ : appListFiltersResponse?.clusterList.result
+ if (!clusterList || (!isExternalArgo && !isExternalFlux)) return false
+ return clusterList.find((clusterItem) => clusterItem.id === +option.value)?.isVirtualCluster
+ }
+
+ const getIsAppStatusDisabled = (option: SelectPickerOptionType): boolean =>
+ appType === AppListConstants.AppType.HELM_APPS && option.label === AppStatuses.NOT_DEPLOYED
+
const selectedAppStatus = appStatus.map((status) => ({ label: status, value: status })) || []
const selectedProjects =
@@ -80,10 +91,7 @@ const AppListFilters = ({
value: templateTypeItem,
})) || []
- const appStatusFilters: SelectPickerOptionType[] =
- appType === AppListConstants.AppType.HELM_APPS
- ? structuredClone(APP_STATUS_FILTER_OPTIONS).filter((status) => status.label !== AppStatuses.NOT_DEPLOYED)
- : structuredClone(APP_STATUS_FILTER_OPTIONS)
+ const appStatusFilters: SelectPickerOptionType[] = structuredClone(APP_STATUS_FILTER_OPTIONS)
const showExportCsvButton =
isSuperAdmin && appType === AppListConstants.AppType.DEVTRON_APPS && serverMode !== SERVER_MODE.EA_ONLY
@@ -122,6 +130,7 @@ const AppListFilters = ({
handleApplyFilter={handleUpdateFilters(AppListUrlFilters.appStatus)}
isDisabled={false}
isLoading={false}
+ isOptionDisabled={getIsAppStatusDisabled}
/>
>
@@ -184,7 +193,7 @@ const AppListFilters = ({
)}
@@ -193,11 +202,12 @@ const AppListFilters = ({
inputId="app-list-cluster-filter"
options={clusterOptions}
appliedFilterOptions={selectedClusters}
- isDisabled={!!environment.length}
+ isDisabled={!(isExternalArgo || isExternalFlux) && !!environment.length}
isLoading={appListFiltersLoading}
handleApplyFilter={handleUpdateFilters(AppListUrlFilters.cluster)}
optionListError={appListFiltersError}
reloadOptionList={reloadAppListFilters}
+ isOptionDisabled={getIsClusterOptionDisabled}
/>
{showPulsatingDot &&
}
diff --git a/src/components/app/list-new/AppListService.ts b/apps/web/src/components/app/list-new/AppListService.ts
similarity index 100%
rename from src/components/app/list-new/AppListService.ts
rename to apps/web/src/components/app/list-new/AppListService.ts
diff --git a/src/components/app/list-new/AppListType.ts b/apps/web/src/components/app/list-new/AppListType.ts
similarity index 100%
rename from src/components/app/list-new/AppListType.ts
rename to apps/web/src/components/app/list-new/AppListType.ts
diff --git a/src/components/app/list-new/Constants.ts b/apps/web/src/components/app/list-new/Constants.ts
similarity index 95%
rename from src/components/app/list-new/Constants.ts
rename to apps/web/src/components/app/list-new/Constants.ts
index fbf18b77cc..238a0d7e8a 100644
--- a/src/components/app/list-new/Constants.ts
+++ b/apps/web/src/components/app/list-new/Constants.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { OptionType, SelectPickerOptionType } from '@devtron-labs/devtron-fe-common-lib'
+import { OptionType, SelectPickerOptionType, UseUrlFiltersProps } from '@devtron-labs/devtron-fe-common-lib'
import { URLS } from '@Config/routes'
import { AppStatuses, AppStatusesDTO, FluxCDTemplateType } from './AppListType'
@@ -98,3 +98,5 @@ export const APPS_WITH_NO_PROJECT_OPTION: OptionType = {
}
export const APP_LISTING_URLS = [URLS.DEVTRON_APP_LIST, URLS.HELM_APP_LIST, URLS.ARGO_APP_LIST, URLS.FLUX_APP_LIST]
+
+export const APP_LIST_LOCAL_STORAGE_KEY: UseUrlFiltersProps['localStorageKey'] = 'app-list__filters'
diff --git a/src/components/app/list-new/GenericAppList.tsx b/apps/web/src/components/app/list-new/GenericAppList.tsx
similarity index 100%
rename from src/components/app/list-new/GenericAppList.tsx
rename to apps/web/src/components/app/list-new/GenericAppList.tsx
diff --git a/src/components/app/list-new/HelmAppList.tsx b/apps/web/src/components/app/list-new/HelmAppList.tsx
similarity index 100%
rename from src/components/app/list-new/HelmAppList.tsx
rename to apps/web/src/components/app/list-new/HelmAppList.tsx
diff --git a/src/components/app/list-new/list.utils.ts b/apps/web/src/components/app/list-new/list.utils.ts
similarity index 87%
rename from src/components/app/list-new/list.utils.ts
rename to apps/web/src/components/app/list-new/list.utils.ts
index 2b798cd348..3a4639b718 100644
--- a/src/components/app/list-new/list.utils.ts
+++ b/apps/web/src/components/app/list-new/list.utils.ts
@@ -27,7 +27,13 @@ import { Cluster } from '@Services/service.types'
import { URLS } from '../../../config'
import ArgoCDAppIcon from '../../../assets/icons/ic-argocd-app.svg'
import FluxCDAppIcon from '../../../assets/icons/ic-fluxcd-app.svg'
-import { AppListUrlFilters, AppStatuses, AppStatusesDTO, useFilterOptionsProps } from './AppListType'
+import {
+ AppListUrlFilters,
+ AppListUrlFiltersType,
+ AppStatuses,
+ AppStatusesDTO,
+ useFilterOptionsProps,
+} from './AppListType'
import { APPS_WITH_NO_PROJECT_OPTION } from './Constants'
export const getChangeAppTabURL = (appTabType) => {
@@ -148,24 +154,18 @@ export const useFilterOptions = ({
() =>
clusterGroupedEnvOptions?.map((clusterItem) => ({
label: getFormattedFilterValue(AppListUrlFilters.cluster, clusterItem.label),
- options: clusterItem.options,
+ options: clusterItem.options?.sort((a, b) => stringComparatorBySortOrder(a.label, b.label)),
})) ?? [],
[clusterGroupedEnvOptions],
)
- const handleVirtualClusterFiltering = (clusterList: Cluster[]): Cluster[] => {
- if (!clusterList) return []
- if (isExternalArgo || isExternalFlux) {
- return clusterList.filter((clusterItem) => !clusterItem.isVirtualCluster)
- }
- return clusterList
- }
-
const getClusterOptions = (clusterList: Cluster[]): SelectPickerOptionType[] =>
- handleVirtualClusterFiltering(clusterList).map((clusterItem) => ({
- label: clusterItem.cluster_name,
- value: String(clusterItem.id),
- }))
+ (clusterList ?? [])
+ .map((clusterItem) => ({
+ label: clusterItem.cluster_name,
+ value: String(clusterItem.id),
+ }))
+ .sort((a, b) => stringComparatorBySortOrder(a.label, b.label))
const clusterOptions: SelectPickerOptionType[] = useMemo(
() =>
@@ -194,3 +194,18 @@ export const useFilterOptions = ({
return { projectOptions, clusterOptions, environmentOptions, namespaceOptions }
}
+
+export const getFilterChipConfig = (
+ filterConfig: AppListUrlFiltersType,
+ appType: string,
+): Partial => {
+ const { cluster, namespace, templateType } = filterConfig
+ switch (appType) {
+ case AppListConstants.AppType.ARGO_APPS:
+ return { cluster, namespace }
+ case AppListConstants.AppType.FLUX_APPS:
+ return { cluster, namespace, templateType }
+ default:
+ return filterConfig
+ }
+}
diff --git a/src/components/app/list/DevtronAppListContainer.tsx b/apps/web/src/components/app/list/DevtronAppListContainer.tsx
similarity index 100%
rename from src/components/app/list/DevtronAppListContainer.tsx
rename to apps/web/src/components/app/list/DevtronAppListContainer.tsx
diff --git a/src/components/app/list/TriggerUrl.tsx b/apps/web/src/components/app/list/TriggerUrl.tsx
similarity index 100%
rename from src/components/app/list/TriggerUrl.tsx
rename to apps/web/src/components/app/list/TriggerUrl.tsx
diff --git a/src/components/app/list/appList.modal.ts b/apps/web/src/components/app/list/appList.modal.ts
similarity index 100%
rename from src/components/app/list/appList.modal.ts
rename to apps/web/src/components/app/list/appList.modal.ts
diff --git a/src/components/app/list/constants.tsx b/apps/web/src/components/app/list/constants.tsx
similarity index 100%
rename from src/components/app/list/constants.tsx
rename to apps/web/src/components/app/list/constants.tsx
diff --git a/src/components/app/list/emptyView/Empty.tsx b/apps/web/src/components/app/list/emptyView/Empty.tsx
similarity index 100%
rename from src/components/app/list/emptyView/Empty.tsx
rename to apps/web/src/components/app/list/emptyView/Empty.tsx
diff --git a/src/components/app/list/emptyView/types.tsx b/apps/web/src/components/app/list/emptyView/types.tsx
similarity index 100%
rename from src/components/app/list/emptyView/types.tsx
rename to apps/web/src/components/app/list/emptyView/types.tsx
diff --git a/src/components/app/list/expandedRow/ExpandedRow.tsx b/apps/web/src/components/app/list/expandedRow/ExpandedRow.tsx
similarity index 100%
rename from src/components/app/list/expandedRow/ExpandedRow.tsx
rename to apps/web/src/components/app/list/expandedRow/ExpandedRow.tsx
diff --git a/src/components/app/list/expandedRow/expandedRow.css b/apps/web/src/components/app/list/expandedRow/expandedRow.css
similarity index 100%
rename from src/components/app/list/expandedRow/expandedRow.css
rename to apps/web/src/components/app/list/expandedRow/expandedRow.css
diff --git a/src/components/app/list/expandedRow/types.tsx b/apps/web/src/components/app/list/expandedRow/types.tsx
similarity index 100%
rename from src/components/app/list/expandedRow/types.tsx
rename to apps/web/src/components/app/list/expandedRow/types.tsx
diff --git a/src/components/app/list/list.scss b/apps/web/src/components/app/list/list.scss
similarity index 100%
rename from src/components/app/list/list.scss
rename to apps/web/src/components/app/list/list.scss
diff --git a/src/components/app/list/types.tsx b/apps/web/src/components/app/list/types.tsx
similarity index 100%
rename from src/components/app/list/types.tsx
rename to apps/web/src/components/app/list/types.tsx
diff --git a/src/components/app/service.ts b/apps/web/src/components/app/service.ts
similarity index 100%
rename from src/components/app/service.ts
rename to apps/web/src/components/app/service.ts
diff --git a/src/components/app/types.ts b/apps/web/src/components/app/types.ts
similarity index 99%
rename from src/components/app/types.ts
rename to apps/web/src/components/app/types.ts
index 99f1968224..4145e6b985 100644
--- a/src/components/app/types.ts
+++ b/apps/web/src/components/app/types.ts
@@ -15,20 +15,20 @@
*/
import React, { ReactNode } from 'react'
+import { RouteComponentProps } from 'react-router-dom'
import {
ACTION_STATE,
DeploymentAppTypes,
TagType,
Teams,
PodMetadatum,
+ ReleaseMode,
+ AppEnvironment,
DeploymentNodeType,
RuntimeParamsListItemType,
RuntimeParamsTriggerPayloadType,
- ReleaseMode,
HelmReleaseStatus,
} from '@devtron-labs/devtron-fe-common-lib'
-import { RouteComponentProps } from 'react-router'
-import { AppEnvironment } from '../../services/service.types'
import { DeploymentStatusDetailsBreakdownDataType, ErrorItem } from './details/appDetails/appDetails.type'
import { GroupFilterType } from '../ApplicationGroup/AppGroup.types'
import { APP_TYPE } from '@Config/constants'
@@ -197,6 +197,7 @@ interface ResourceTree {
// lastSnapshotTime and wfrId are only available for isolated
lastSnapshotTime?: string
wfrId?: number
+ hasDrift?: boolean
}
interface Node {
@@ -226,6 +227,7 @@ interface Node {
}[]
images?: string[]
url?: string
+ hasDrift?: boolean
}
export interface Pod extends Node {
diff --git a/src/components/app/typing.d.ts b/apps/web/src/components/app/typing.d.ts
similarity index 100%
rename from src/components/app/typing.d.ts
rename to apps/web/src/components/app/typing.d.ts
diff --git a/src/components/bulkEdits/BulkEdits.tsx b/apps/web/src/components/bulkEdits/BulkEdits.tsx
similarity index 100%
rename from src/components/bulkEdits/BulkEdits.tsx
rename to apps/web/src/components/bulkEdits/BulkEdits.tsx
diff --git a/src/components/bulkEdits/bulkEdit.scss b/apps/web/src/components/bulkEdits/bulkEdit.scss
similarity index 100%
rename from src/components/bulkEdits/bulkEdit.scss
rename to apps/web/src/components/bulkEdits/bulkEdit.scss
diff --git a/src/components/bulkEdits/bulkEdits.type.tsx b/apps/web/src/components/bulkEdits/bulkEdits.type.tsx
similarity index 100%
rename from src/components/bulkEdits/bulkEdits.type.tsx
rename to apps/web/src/components/bulkEdits/bulkEdits.type.tsx
diff --git a/src/components/bulkEdits/bulkedit.utils.tsx b/apps/web/src/components/bulkEdits/bulkedit.utils.tsx
similarity index 100%
rename from src/components/bulkEdits/bulkedit.utils.tsx
rename to apps/web/src/components/bulkEdits/bulkedit.utils.tsx
diff --git a/src/components/bulkEdits/bulkedits.service.tsx b/apps/web/src/components/bulkEdits/bulkedits.service.tsx
similarity index 100%
rename from src/components/bulkEdits/bulkedits.service.tsx
rename to apps/web/src/components/bulkEdits/bulkedits.service.tsx
diff --git a/src/components/bulkEdits/constants.ts b/apps/web/src/components/bulkEdits/constants.ts
similarity index 100%
rename from src/components/bulkEdits/constants.ts
rename to apps/web/src/components/bulkEdits/constants.ts
diff --git a/src/components/cdPipeline/BuildCD.tsx b/apps/web/src/components/cdPipeline/BuildCD.tsx
similarity index 98%
rename from src/components/cdPipeline/BuildCD.tsx
rename to apps/web/src/components/cdPipeline/BuildCD.tsx
index 87806a4724..8075e7cb06 100644
--- a/src/components/cdPipeline/BuildCD.tsx
+++ b/apps/web/src/components/cdPipeline/BuildCD.tsx
@@ -33,7 +33,7 @@ import {
ToastVariantType,
ToastManager,
ComponentSizeType,
- SelectPickerOptionType,
+ showError,
} from '@devtron-labs/devtron-fe-common-lib'
import { useContext, useState } from 'react'
import { useParams, useHistory } from 'react-router-dom'
@@ -62,6 +62,7 @@ import { ReactComponent as ICInfo } from '../../assets/icons/ic-info-filled.svg'
import PullImageDigestToggle from './PullImageDigestToggle'
import { PipelineFormDataErrorType } from '@Components/workflowEditor/types'
import { EnvironmentWithSelectPickerType } from '@Components/CIPipelineN/types'
+import { BuildCDProps } from './types'
const VirtualEnvSelectionInfoText = importComponentFromFELibrary('VirtualEnvSelectionInfoText')
const HelmManifestPush = importComponentFromFELibrary('HelmManifestPush')
@@ -89,7 +90,8 @@ export default function BuildCD({
isGitOpsRepoNotConfigured,
noGitOpsModuleInstalledAndConfigured,
releaseMode,
-}) {
+ getMandatoryPluginData,
+}: BuildCDProps) {
const {
formData,
setFormData,
@@ -149,7 +151,7 @@ export default function BuildCD({
setFormData(_form)
}
- const selectEnvironment = (selection: EnvironmentWithSelectPickerType): void => {
+ const selectEnvironment = async (selection: EnvironmentWithSelectPickerType) => {
const _form = { ...formData, deploymentAppName: '' }
const _formDataErrorObj = { ...formDataErrorObj }
@@ -195,8 +197,6 @@ export default function BuildCD({
_form.isDigestEnforcedForEnv = _form.environments.find(
(env) => env.id == selection.id,
)?.isDigestEnforcedForEnv
- setFormDataErrorObj(_formDataErrorObj)
- setFormData(_form)
} else {
const list = _form.environments.map((item) => {
return {
@@ -209,8 +209,15 @@ export default function BuildCD({
_form.environments = list
setIsVirtualEnvironment(false)
_formDataErrorObj.envNameError = validationRules.environment(_form.environmentId)
- setFormData(_form)
- setFormDataErrorObj(_formDataErrorObj)
+ }
+
+ setFormData(_form)
+ setFormDataErrorObj(_formDataErrorObj)
+
+ try {
+ await getMandatoryPluginData(_form)
+ } catch (error) {
+ showError(error)
}
}
@@ -335,7 +342,8 @@ export default function BuildCD({
formDataError.containerRegistryError = validationRules.containerRegistry(
selectedRegistry.id || formData.containerRegistryName,
)
- form.selectedRegistry = { ...selectedRegistry,
+ form.selectedRegistry = {
+ ...selectedRegistry,
value: selectedRegistry.id,
label: selectedRegistry.id,
} as RegistryPayloadWithSelectType
@@ -377,7 +385,7 @@ export default function BuildCD({
const renderEnvSelector = () => {
const envId = formData.environmentId
const _environment = formData.environments.find((env) => env.id == envId)
- const selectedEnv: EnvironmentWithSelectPickerType = _environment &&{
+ const selectedEnv: EnvironmentWithSelectPickerType = _environment && {
..._environment,
label: _environment.name,
value: _environment.id.toString(),
@@ -754,7 +762,7 @@ export default function BuildCD({
handleStrategyChange(event, strategy.deploymentTemplate, 'yaml')
diff --git a/src/components/cdPipeline/CDPipeline.tsx b/apps/web/src/components/cdPipeline/CDPipeline.tsx
similarity index 94%
rename from src/components/cdPipeline/CDPipeline.tsx
rename to apps/web/src/components/cdPipeline/CDPipeline.tsx
index dc05e1dacd..2db6074bc6 100644
--- a/src/components/cdPipeline/CDPipeline.tsx
+++ b/apps/web/src/components/cdPipeline/CDPipeline.tsx
@@ -44,9 +44,14 @@ import {
TabProps,
ToastVariantType,
ToastManager,
+ MandatoryPluginDataType,
+ ProcessPluginDataParamsType,
+ ProcessPluginDataReturnType,
+ ResourceKindType,
} from '@devtron-labs/devtron-fe-common-lib'
import { useEffect, useMemo, useRef, useState } from 'react'
import { Redirect, Route, Switch, useParams, useRouteMatch } from 'react-router-dom'
+import { ReactComponent as ICWarning } from '@Icons/ic-warning.svg'
import { ReactComponent as Close } from '../../assets/icons/ic-close.svg'
import { CDDeploymentTabText, RegistryPayloadType, SourceTypeMap, TriggerType, ViewType } from '../../config'
import {
@@ -99,9 +104,12 @@ import {
gitOpsRepoNotConfiguredWithEnforcedEnv,
gitOpsRepoNotConfiguredWithOptionsHidden,
} from '../gitOps/constants'
-import { CDPipelineProps, DeleteDialogType, ForceDeleteMessageType } from './types'
+import { BuildCDProps, CDPipelineProps, DeleteDialogType, ForceDeleteMessageType } from './types'
const DeploymentWindowConfirmationDialog = importComponentFromFELibrary('DeploymentWindowConfirmationDialog')
+const processPluginData: (params: ProcessPluginDataParamsType) => Promise =
+ importComponentFromFELibrary('processPluginData', null, 'function')
+const validatePlugins = importComponentFromFELibrary('validatePlugins', null, 'function')
const getDeploymentWindowProfileMetaData = importComponentFromFELibrary(
'getDeploymentWindowProfileMetaData',
null,
@@ -258,6 +266,7 @@ export default function CDPipeline({
)
const [hideScopedVariableWidget, setHideScopedVariableWidget] = useState(false)
const [disableParentModalClose, setDisableParentModalClose] = useState(false)
+ const [mandatoryPluginData, setMandatoryPluginData] = useState(null)
const handleHideScopedVariableWidgetUpdate: PipelineContext['handleHideScopedVariableWidgetUpdate'] = (
hideScopedVariableWidgetValue: boolean,
@@ -283,15 +292,65 @@ export default function CDPipeline({
setAvailableTags(tags)
}
- useEffect(() => {
- getInit()
- }, [])
+ const handlePopulatePluginDataStore = async (form: PipelineFormType) => {
+ if (!form) {
+ return
+ }
- useEffect(() => {
- if (formData.environmentId) {
- getConfigMapSecrets()
+ const preBuildPluginIds = getPluginIdsFromBuildStage(form.preBuildStage)
+ const postBuildPluginIds = getPluginIdsFromBuildStage(form.postBuildStage)
+ const uniquePluginIds = Array.from(new Set([...preBuildPluginIds, ...postBuildPluginIds]))
+
+ if (processPluginData) {
+ await getMandatoryPluginData(form, uniquePluginIds)
+ return
+ }
+
+ if (uniquePluginIds.length === 0) {
+ return
}
- }, [formData.environmentId])
+
+ const {
+ pluginStore: { parentPluginStore, pluginVersionStore },
+ } = await getPluginsDetail({
+ appId: +appId,
+ pluginIds: uniquePluginIds,
+ shouldShowError: false,
+ })
+
+ handlePluginDataStoreUpdate(getUpdatedPluginStore(pluginDataStore, parentPluginStore, pluginVersionStore))
+ }
+
+ const getEnvCDPipelineName = (form) => {
+ Promise.all([getCDPipelineNameSuggestion(appId), getEnvironmentListMinPublic(true)])
+ .then(([cpPipelineName, envList]) => {
+ form.name = cpPipelineName.result
+ let list = envList.result || []
+ list = list.map((env) => {
+ return {
+ id: env.id,
+ clusterId: env.cluster_id,
+ clusterName: env.cluster_name,
+ name: env.environment_name,
+ namespace: env.namespace || '',
+ active: false,
+ isClusterCdActive: env.isClusterCdActive,
+ description: env.description,
+ isVirtualEnvironment: env.isVirtualEnvironment,
+ allowedDeploymentTypes: env.allowedDeploymentTypes || [],
+ isDigestEnforcedForEnv: env.isDigestEnforcedForEnv,
+ }
+ })
+ sortObjectArrayAlphabetically(list, 'name')
+ form.environments = list
+ setFormData(form)
+ setPageState(ViewType.FORM)
+ setIsAdvanced(false)
+ })
+ .catch((error) => {
+ showError(error)
+ })
+ }
const getInit = () => {
Promise.all([
@@ -344,41 +403,20 @@ export default function CDPipeline({
})
}
+ useEffect(() => {
+ getInit()
+ }, [])
+
+ useEffect(() => {
+ if (formData.environmentId) {
+ getConfigMapSecrets()
+ }
+ }, [formData.environmentId])
+
const handleShowGitOpsRepoConfiguredWarning = () => {
setGitOpsRepoConfiguredWarning({ show: false, text: '' })
}
- const getEnvCDPipelineName = (form) => {
- Promise.all([getCDPipelineNameSuggestion(appId), getEnvironmentListMinPublic(true)])
- .then(([cpPipelineName, envList]) => {
- form.name = cpPipelineName.result
- let list = envList.result || []
- list = list.map((env) => {
- return {
- id: env.id,
- clusterId: env.cluster_id,
- clusterName: env.cluster_name,
- name: env.environment_name,
- namespace: env.namespace || '',
- active: false,
- isClusterCdActive: env.isClusterCdActive,
- description: env.description,
- isVirtualEnvironment: env.isVirtualEnvironment,
- allowedDeploymentTypes: env.allowedDeploymentTypes || [],
- isDigestEnforcedForEnv: env.isDigestEnforcedForEnv,
- }
- })
- sortObjectArrayAlphabetically(list, 'name')
- form.environments = list
- setFormData(form)
- setPageState(ViewType.FORM)
- setIsAdvanced(false)
- })
- .catch((error) => {
- showError(error)
- })
- }
-
const calculateLastStepDetail = (
isFromAddNewTask: boolean,
_formData: PipelineFormType,
@@ -407,23 +445,25 @@ export default function CDPipeline({
return { index: stepsLength + 1, calculatedStageVariables: _inputVariablesListPerTask }
}
- const handlePopulatePluginDataStore = async (form: PipelineFormType) => {
- const preBuildPluginIds = getPluginIdsFromBuildStage(form.preBuildStage)
- const postBuildPluginIds = getPluginIdsFromBuildStage(form.postBuildStage)
- const pluginIds = Array.from(new Set([...preBuildPluginIds, ...postBuildPluginIds]))
- if (pluginIds.length === 0) {
+ const getMandatoryPluginData: BuildCDProps['getMandatoryPluginData'] = async (form, requiredPluginIds = []) => {
+ if (!processPluginData) {
return
}
- const {
- pluginStore: { parentPluginStore, pluginVersionStore },
- } = await getPluginsDetail({
- appId: +appId,
- pluginIds,
- shouldShowError: false,
- })
+ const { mandatoryPluginData: processedPluginData, pluginDataStore: updatedPluginDataStore } =
+ await processPluginData({
+ formData: form,
+ pluginDataStoreState: pluginDataStore,
+ appId: +appId,
+ appName,
+ envName: form.environmentName,
+ requiredPluginIds,
+ resourceKind: ResourceKindType.cdPipeline,
+ })
- handlePluginDataStoreUpdate(getUpdatedPluginStore(pluginDataStore, parentPluginStore, pluginVersionStore))
+ setMandatoryPluginData(processedPluginData)
+ // The method itself adds over existing plugins, so no need to worry about of overriding
+ handlePluginDataStoreUpdate(updatedPluginDataStore)
}
const getCDPipeline = (form, dockerRegistries): void => {
@@ -450,7 +490,7 @@ export default function CDPipeline({
validateStage(BuildStageVariable.PostBuild, result.form)
setIsAdvanced(true)
setIsVirtualEnvironment(pipelineConfigFromRes.isVirtualEnvironment)
- await handlePopulatePluginDataStore(result.form)
+ await handlePopulatePluginDataStore(form)
setFormData({
...form,
clusterId: result.form?.clusterId,
@@ -522,6 +562,7 @@ export default function CDPipeline({
...pipelineConfigFromRes.strategies[i],
defaultConfig: allStrategies.current[pipelineConfigFromRes.strategies[i].deploymentTemplate],
jsonStr: JSON.stringify(pipelineConfigFromRes.strategies[i].config, null, 4),
+ yamlStr: YAMLStringify(pipelineConfigFromRes.strategies[i].config),
selection: YAMLStringify(allStrategies.current[pipelineConfigFromRes.strategies[i].config], {
indent: 2,
}),
@@ -796,11 +837,22 @@ export default function CDPipeline({
setFormData(_form)
}
+ const handleValidateMandatoryPlugins: PipelineContext['handleValidateMandatoryPlugins'] = ({
+ newFormData = formData,
+ newPluginDataStore = pluginDataStore,
+ }) => {
+ if (!validatePlugins || !mandatoryPluginData?.pluginData?.length) {
+ return
+ }
+
+ setMandatoryPluginData(validatePlugins(newFormData, mandatoryPluginData.pluginData, newPluginDataStore))
+ }
+
const validateStage = (
stageName: string,
_formData: PipelineFormType,
- formDataErrorObject?,
- _clonedPluginDataStore?: typeof pluginDataStore,
+ formDataErrorObject?: typeof formDataErrorObj,
+ clonedPluginDataStore: typeof pluginDataStore = pluginDataStore,
): void => {
const _formDataErrorObj = {
...(formDataErrorObject ?? formDataErrorObj),
@@ -837,6 +889,11 @@ export default function CDPipeline({
isStageValid = isStageValid && _formDataErrorObj[stageName].steps[i].isValid
}
+ handleValidateMandatoryPlugins({
+ newFormData: _formData,
+ newPluginDataStore: clonedPluginDataStore,
+ })
+
_formDataErrorObj[stageName].isValid = isStageValid
}
setFormDataErrorObj(_formDataErrorObj)
@@ -1038,7 +1095,7 @@ export default function CDPipeline({
})
.catch((error: ServerErrors) => {
// 412 is for linked pipeline and 403 is for RBAC
- //For now we are removing check for error code 422 which is of deployment window,
+ //For now we are removing check for error code 422 which is of deployment window,
// so in that case force delete modal would be shown.
// This should be done at BE and when done we will revert our changes
if (!force && error.code != 403 && error.code != 412) {
@@ -1071,13 +1128,17 @@ export default function CDPipeline({
}
const getNavLink = (toLink: string, stageName: string): TabProps => {
- const showAlert = !formDataErrorObj[stageName].isValid
+ const showWarning =
+ mandatoryPluginData &&
+ ((stageName === BuildStageVariable.PreBuild && !mandatoryPluginData.isValidPre) ||
+ (stageName === BuildStageVariable.PostBuild && !mandatoryPluginData.isValidPost))
return {
id: `${CDDeploymentTabText[stageName]}-tab`,
label: CDDeploymentTabText[stageName],
tabType: 'navLink',
- showError: showAlert,
+ showError: !formDataErrorObj[stageName].isValid,
+ showWarning,
props: {
to: `${toLink}?${urlParams}`,
replace: true,
@@ -1108,10 +1169,14 @@ export default function CDPipeline({
Advanced options
+ {mandatoryPluginData &&
+ (!mandatoryPluginData.isValidPre || !mandatoryPluginData.isValidPost) && (
+
+ )}
)
)
@@ -1170,6 +1235,8 @@ export default function CDPipeline({
handleUpdateAvailableTags,
handleHideScopedVariableWidgetUpdate,
handleDisableParentModalCloseUpdate,
+ handleValidateMandatoryPlugins,
+ mandatoryPluginData,
}
}, [
formData,
@@ -1183,6 +1250,7 @@ export default function CDPipeline({
isVirtualEnvironment,
pluginDataStore,
availableTags,
+ mandatoryPluginData,
])
const renderCDPipelineBody = () => {
@@ -1248,6 +1316,7 @@ export default function CDPipeline({
isGitOpsRepoNotConfigured={isGitOpsRepoNotConfigured}
noGitOpsModuleInstalledAndConfigured={noGitOpsModuleInstalledAndConfigured}
releaseMode={formData.releaseMode}
+ getMandatoryPluginData={getMandatoryPluginData}
/>
@@ -1353,7 +1422,7 @@ export default function CDPipeline({
deleteCD={deleteCD}
deploymentAppType={formData.deploymentAppType}
forceDeleteData={forceDeleteData}
- deleteTitleName={formData.name}
+ deleteTitleName={formData.environmentName}
/>
)}
diff --git a/src/components/cdPipeline/DeleteCDNode.tsx b/apps/web/src/components/cdPipeline/DeleteCDNode.tsx
similarity index 69%
rename from src/components/cdPipeline/DeleteCDNode.tsx
rename to apps/web/src/components/cdPipeline/DeleteCDNode.tsx
index 78a11311f1..e1a62500d5 100644
--- a/src/components/cdPipeline/DeleteCDNode.tsx
+++ b/apps/web/src/components/cdPipeline/DeleteCDNode.tsx
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import React, { useState } from 'react'
-import { CustomInput, DeleteDialog, DeploymentAppTypes, ForceDeleteDialog } from '@devtron-labs/devtron-fe-common-lib'
+import { DeleteDialog, DeploymentAppTypes, ForceDeleteDialog } from '@devtron-labs/devtron-fe-common-lib'
import ClusterNotReachableDailog from '../common/ClusterNotReachableDailog/ClusterNotReachableDialog'
import { DELETE_ACTION } from '../../config'
import { DeleteCDNodeProps, DeleteDialogType } from './types'
@@ -32,17 +31,7 @@ const DeleteCDNode = ({
forceDeleteData,
deleteTitleName,
isLoading,
- showConfirmationBar,
}: Readonly) => {
- const [deleteInput, setDeleteInput] = useState('')
- const deleteTitle = showConfirmationBar
- ? `Delete Pipeline for '${deleteTitleName}' ?`
- : `Delete '${deleteTitleName}' ?`
-
- const handleDeleteInputChange = (e: React.ChangeEvent) => {
- setDeleteInput(e.target.value)
- }
-
const onClickHideNonCascadeDeletePopup = () => {
setDeleteDialog(DeleteDialogType.showNormalDeleteDialog)
}
@@ -79,25 +68,14 @@ const DeleteCDNode = ({
return (
handleDeleteCDNodePipeline(deleteCD, deploymentAppType as DeploymentAppTypes)}
closeDelete={hideDeleteModal}
apiCallInProgress={isLoading}
- disabled={showConfirmationBar && deleteInput !== deleteTitleName}
- >
- {showConfirmationBar && (
-
- )}
-
+ showDeleteConfirmation
+ deleteConfirmationText={deleteTitleName}
+ />
)
}
diff --git a/src/components/cdPipeline/PullImageDigestToggle.tsx b/apps/web/src/components/cdPipeline/PullImageDigestToggle.tsx
similarity index 100%
rename from src/components/cdPipeline/PullImageDigestToggle.tsx
rename to apps/web/src/components/cdPipeline/PullImageDigestToggle.tsx
diff --git a/src/components/cdPipeline/cdPipeline.scss b/apps/web/src/components/cdPipeline/cdPipeline.scss
similarity index 100%
rename from src/components/cdPipeline/cdPipeline.scss
rename to apps/web/src/components/cdPipeline/cdPipeline.scss
diff --git a/src/components/cdPipeline/cdPipeline.service.ts b/apps/web/src/components/cdPipeline/cdPipeline.service.ts
similarity index 100%
rename from src/components/cdPipeline/cdPipeline.service.ts
rename to apps/web/src/components/cdPipeline/cdPipeline.service.ts
diff --git a/src/components/cdPipeline/cdPipeline.types.ts b/apps/web/src/components/cdPipeline/cdPipeline.types.ts
similarity index 100%
rename from src/components/cdPipeline/cdPipeline.types.ts
rename to apps/web/src/components/cdPipeline/cdPipeline.types.ts
diff --git a/src/components/cdPipeline/cdpipeline.util.tsx b/apps/web/src/components/cdPipeline/cdpipeline.util.tsx
similarity index 100%
rename from src/components/cdPipeline/cdpipeline.util.tsx
rename to apps/web/src/components/cdPipeline/cdpipeline.util.tsx
diff --git a/src/components/cdPipeline/types.ts b/apps/web/src/components/cdPipeline/types.ts
similarity index 72%
rename from src/components/cdPipeline/types.ts
rename to apps/web/src/components/cdPipeline/types.ts
index bfdf752e5d..352a200dcd 100644
--- a/src/components/cdPipeline/types.ts
+++ b/apps/web/src/components/cdPipeline/types.ts
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-import { PipelineFormType } from '@devtron-labs/devtron-fe-common-lib'
+import { Dispatch, MutableRefObject, SetStateAction } from 'react'
+import { PipelineFormType, PluginDetailPayloadType, ReleaseMode } from '@devtron-labs/devtron-fe-common-lib'
import { ChangeCIPayloadType } from '../workflowEditor/types'
// Have added any type for most of these since they were legacy do not know the implications of changing them
@@ -55,10 +56,27 @@ export interface DeleteCDNodeProps {
forceDeleteData: ForceDeleteMessageType
deleteTitleName: string
isLoading?: boolean
- showConfirmationBar?: boolean
}
export interface PullImageDigestToggleType {
formData: PipelineFormType
setFormData: React.Dispatch>
}
+
+export interface BuildCDProps
+ extends Pick {
+ isAdvanced: boolean
+ setIsVirtualEnvironment: Dispatch>
+ noStrategyAvailable: MutableRefObject
+ allStrategies: MutableRefObject<{
+ [key: string]: any
+ }>
+ parentPipelineId: string
+ isWebhookCD: boolean
+ dockerRegistries: any[]
+ releaseMode: ReleaseMode
+ getMandatoryPluginData: (
+ form: PipelineFormType,
+ requiredPluginIds?: PluginDetailPayloadType['pluginId'],
+ ) => Promise
+}
diff --git a/src/components/cdPipeline/validationRules.ts b/apps/web/src/components/cdPipeline/validationRules.ts
similarity index 100%
rename from src/components/cdPipeline/validationRules.ts
rename to apps/web/src/components/cdPipeline/validationRules.ts
diff --git a/src/components/chartRepo/ChartRepo.tsx b/apps/web/src/components/chartRepo/ChartRepo.tsx
similarity index 98%
rename from src/components/chartRepo/ChartRepo.tsx
rename to apps/web/src/components/chartRepo/ChartRepo.tsx
index 2fd2b235b1..8bac83fa69 100644
--- a/src/components/chartRepo/ChartRepo.tsx
+++ b/apps/web/src/components/chartRepo/ChartRepo.tsx
@@ -297,7 +297,7 @@ const ChartForm = ({
: {}),
}
- const isFormInvalid = () => {
+ const isFormValid = () => {
let isValid = true
if (state.name.error.length > 0 || state.url.error.length > 0) {
@@ -323,16 +323,14 @@ const ChartForm = ({
}
async function onClickValidate() {
- setValidationStatus(VALIDATION_STATUS.LOADER)
- const isInvalid = isFormInvalid()
- if (!isInvalid) {
+ if (!isFormValid()) {
ToastManager.showToast({
variant: ToastVariantType.error,
description: 'Some Required Fields are missing',
})
return
}
-
+ setValidationStatus(VALIDATION_STATUS.LOADER)
const promise = validateChartRepoConfiguration(chartRepoPayload)
await promise
.then((response) => {
@@ -360,15 +358,14 @@ const ChartForm = ({
}
async function onClickSave(e) {
- setValidationStatus(VALIDATION_STATUS.LOADER)
- const isInvalid = isFormInvalid()
- if (!isInvalid) {
+ if (!isFormValid()) {
ToastManager.showToast({
variant: ToastVariantType.error,
description: 'Some Required Fields are missing',
})
return
}
+ setValidationStatus(VALIDATION_STATUS.LOADER)
const api = id ? updateChartProviderConfig : saveChartProviderConfig
try {
@@ -453,7 +450,7 @@ const ChartForm = ({
}
return (
-