From f8b971780610dda9f1ba6168d1ea7012a3bbd675 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 14 Nov 2024 11:46:10 +0530 Subject: [PATCH 01/52] fix: AppListFilters - Filter Dropdown - when timer updates, filter selection gets cleared fix --- src/components/app/list-new/AppListFilters.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/app/list-new/AppListFilters.tsx b/src/components/app/list-new/AppListFilters.tsx index 5888fe6742..6009062518 100644 --- a/src/components/app/list-new/AppListFilters.tsx +++ b/src/components/app/list-new/AppListFilters.tsx @@ -11,6 +11,7 @@ import { import ReactGA from 'react-ga4' import { FILE_NAMES } from '@Components/common/ExportToCsv/constants' import ExportToCsv from '@Components/common/ExportToCsv/ExportToCsv' +import { useMemo } from 'react' import { APP_STATUS_FILTER_OPTIONS, SELECT_CLUSTER_TIPPY, TEMPLATE_TYPE_FILTER_OPTIONS } from './Constants' import { AppListFiltersProps, AppListUrlFilters, AppStatuses } from './AppListType' import { getDevtronAppListDataToExport } from './AppListService' @@ -59,7 +60,10 @@ const AppListFilters = ({ 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 selectedAppStatus = useMemo( + () => appStatus.map((status) => ({ label: status, value: status })) || [], + [appStatus], + ) const selectedProjects = project.map((team) => ({ From efa2989ee4e8a52d6e0015289d44365c59a31216 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 14 Nov 2024 11:48:40 +0530 Subject: [PATCH 02/52] fix: ChartUsedCard - color update --- .../appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx index 5f52439607..fb1f23a923 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx @@ -33,7 +33,7 @@ const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading }: Cha
-
+
Chart used
Date: Mon, 18 Nov 2024 16:42:13 +0530 Subject: [PATCH 03/52] feat: replace hibernate modal with confirmation dialog --- .../app/details/appDetails/AppDetails.tsx | 82 +++++++++---------- src/components/cdPipeline/DeleteCDNode.tsx | 35 ++++++-- src/css/base.scss | 4 + 3 files changed, 70 insertions(+), 51 deletions(-) diff --git a/src/components/app/details/appDetails/AppDetails.tsx b/src/components/app/details/appDetails/AppDetails.tsx index 0daca73e57..1ec55aec07 100644 --- a/src/components/app/details/appDetails/AppDetails.tsx +++ b/src/components/app/details/appDetails/AppDetails.tsx @@ -35,6 +35,8 @@ import { ToastVariantType, ToastManager, SelectPicker, + ConfirmationModal, + ConfirmationModalVariantType, } from '@devtron-labs/devtron-fe-common-lib' import { Link, useParams, useHistory, useRouteMatch, generatePath, Route, useLocation } from 'react-router-dom' import Tippy from '@tippyjs/react' @@ -58,8 +60,8 @@ import { getAppConfigStatus, getAppOtherEnvironmentMin, stopStartApp } from '../ // @ts-check import AppNotDeployedIcon from '../../../../assets/img/app-not-deployed.png' import AppNotConfiguredIcon from '../../../../assets/img/app-not-configured.png' -import restoreIcon from '../../../../assets/icons/ic-restore.svg' -import warningIcon from '../../../../assets/icons/ic-warning.svg' +import { ReactComponent as ICRestore } from '../../../../assets/icons/ic-restore.svg' +import { ReactComponent as ICWarning } from '../../../../assets/icons/ic-warning-y5.svg' import { ReactComponent as PlayButton } from '../../../../assets/icons/ic-play.svg' import { ReactComponent as Connect } from '../../../../assets/icons/ic-connected.svg' import { ReactComponent as Disconnect } from '../../../../assets/icons/ic-disconnected.svg' @@ -677,46 +679,38 @@ export const Details: React.FC = ({ ) } return ( - - - - Pods for this application will be - - scaled - {hibernateConfirmationModal === 'hibernate' - ? ' down to 0 ' - : ' up to its original count '} - on {appDetails.environmentName} - - environment. -

- } - > -

Are you sure you want to continue?

-
- - - - -
+ + Pods for this application will be + + scaled + {hibernateConfirmationModal === 'hibernate' ? ' down to 0 ' : ' up to its original count '} + on {appDetails.environmentName} + + environment. +

+ } + buttonConfig={{ + secondaryButtonConfig: { + dataTestId: 'cancel-hibernate-unhibernate-dialog', + disabled: hibernating, + onClick: handleHibernateConfirmationModalClose, + text: 'Cancel', + }, + primaryButtonConfig: { + dataTestId: `app-details-${hibernateConfirmationModal === 'hibernate' ? 'hibernate' : 'restore'}`, + isLoading: hibernating, + onClick: handleHibernate, + text: getHibernateText(), + }, + }} + > + Are you sure you want to continue? +
) } @@ -831,7 +825,9 @@ export const Details: React.FC = ({ isVirtualEnvironment={isVirtualEnvRef.current} /> } - {isConfigDriftEnabled && ConfigDriftModalRoute && !isVirtualEnvRef.current && } + {isConfigDriftEnabled && ConfigDriftModalRoute && !isVirtualEnvRef.current && ( + + )} ) } diff --git a/src/components/cdPipeline/DeleteCDNode.tsx b/src/components/cdPipeline/DeleteCDNode.tsx index e1a62500d5..ea698aaca4 100644 --- a/src/components/cdPipeline/DeleteCDNode.tsx +++ b/src/components/cdPipeline/DeleteCDNode.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ -import { DeleteDialog, DeploymentAppTypes, ForceDeleteDialog } from '@devtron-labs/devtron-fe-common-lib' +import { + DeploymentAppTypes, + ForceDeleteDialog, + ConfirmationModal, + ConfirmationModalVariantType, +} from '@devtron-labs/devtron-fe-common-lib' import ClusterNotReachableDailog from '../common/ClusterNotReachableDailog/ClusterNotReachableDialog' import { DELETE_ACTION } from '../../config' import { DeleteCDNodeProps, DeleteDialogType } from './types' @@ -67,14 +72,28 @@ const DeleteCDNode = ({ } return ( - handleDeleteCDNodePipeline(deleteCD, deploymentAppType as DeploymentAppTypes)} - closeDelete={hideDeleteModal} - apiCallInProgress={isLoading} - showDeleteConfirmation - deleteConfirmationText={deleteTitleName} + subtitle={`Are you sure you want to delete this CD Pipeline from '${appName}' application?`} + buttonConfig={{ + secondaryButtonConfig: { + dataTestId: 'delete-cd-node-cancel', + text: 'Cancel', + onClick: hideDeleteModal, + disabled: isLoading, + }, + primaryButtonConfig: { + dataTestId: 'delete-cd-node-delete', + text: 'Delete', + onClick: () => handleDeleteCDNodePipeline(deleteCD, deploymentAppType as DeploymentAppTypes), + isLoading, + }, + }} + customInputConfig={{ + identifier: 'delete-cd-node-input', + confirmationKeyword: deleteTitleName, + }} /> ) } diff --git a/src/css/base.scss b/src/css/base.scss index 1dbe9dacf3..a0daf1cf93 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -3988,6 +3988,10 @@ textarea, margin-bottom: 0px !important; } +.mt-40 { + margin-top: 40px; +} + .mt-120 { margin-top: 120px; } From d41950955c319febc2f5d8fdcc77f0799a4e8596 Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Mon, 18 Nov 2024 18:14:04 +0530 Subject: [PATCH 04/52] chore: add prop handleClose --- src/components/app/details/appDetails/AppDetails.tsx | 1 + src/components/cdPipeline/DeleteCDNode.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/app/details/appDetails/AppDetails.tsx b/src/components/app/details/appDetails/AppDetails.tsx index 1ec55aec07..3c3ed4876b 100644 --- a/src/components/app/details/appDetails/AppDetails.tsx +++ b/src/components/app/details/appDetails/AppDetails.tsx @@ -708,6 +708,7 @@ export const Details: React.FC = ({ text: getHibernateText(), }, }} + handleClose={handleHibernateConfirmationModalClose} > Are you sure you want to continue? diff --git a/src/components/cdPipeline/DeleteCDNode.tsx b/src/components/cdPipeline/DeleteCDNode.tsx index ea698aaca4..8e5032a1a4 100644 --- a/src/components/cdPipeline/DeleteCDNode.tsx +++ b/src/components/cdPipeline/DeleteCDNode.tsx @@ -94,6 +94,7 @@ const DeleteCDNode = ({ identifier: 'delete-cd-node-input', confirmationKeyword: deleteTitleName, }} + handleClose={hideDeleteModal} /> ) } From 3030d5f5faca8f146f07d3aeb33c10a8a769f8e2 Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Tue, 19 Nov 2024 11:35:36 +0530 Subject: [PATCH 05/52] Chore: version bump --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f41f5f4524..e78d4e9723 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.0.8", + "@devtron-labs/devtron-fe-common-lib": "1.0.8-beta-1", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index d987f19c5e..508621278b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.8.tgz#74105a389dab077e52ba34e6d44242606465c7e9" - integrity sha512-gwcFjnuQKVlLG65sNwQ8pDaikDsTl9/fovUWxEUAbgv/yokL5TasHj0wN6FaH3nLwxerJFTF+CAub60rrwohGw== +"@devtron-labs/devtron-fe-common-lib@1.0.8-beta-1": + version "1.0.8-beta-1" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.8-beta-1.tgz#c25162c9165713954b40a96f163539b493be9be5" + integrity sha512-2FlFmX9ziKQFQ2NKnfWZRzj7VVtsgDG0zkq8q6q8LR/u5ZHV4ya/bAro6IXEtHkjotCI0wi5dYnCgo2nUNY5qA== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" From f61456e0467a437762174b040a1c091535492a97 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 19 Nov 2024 14:10:51 +0530 Subject: [PATCH 06/52] chore: chart UI fixes --- .../Details/TriggerView/BulkCITrigger.tsx | 9 --- .../app/details/appDetails/AppStatusCard.tsx | 3 +- .../details/triggerView/BranchRegexModal.tsx | 62 +++++++++---------- .../appDetails/sourceInfo/environment.type.ts | 7 +++ .../environmentStatus/ChartUsedCard.tsx | 36 +++++++---- .../EnvironmentStatus.component.tsx | 2 +- .../v2/appDetails/sourceInfo/utils.tsx | 29 +++++++++ src/css/base.scss | 4 ++ 8 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx b/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx index 03c5daefba..e6ad119639 100644 --- a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx +++ b/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx @@ -418,17 +418,8 @@ const BulkCITrigger = ({ handleRegexInputValue={handleRegexInputValueChange} regexValue={regexValue} onCloseBranchRegexModal={hideBranchEditModal} - hideHeaderFooter savingRegexValue={isLoading} /> -
- - -
) } diff --git a/src/components/app/details/appDetails/AppStatusCard.tsx b/src/components/app/details/appDetails/AppStatusCard.tsx index 49a6ff5ccb..6d6488166a 100644 --- a/src/components/app/details/appDetails/AppStatusCard.tsx +++ b/src/components/app/details/appDetails/AppStatusCard.tsx @@ -93,8 +93,7 @@ const AppStatusCard = ({ appDetails, status, cardLoading, setDetailed, message }
diff --git a/src/components/app/details/triggerView/BranchRegexModal.tsx b/src/components/app/details/triggerView/BranchRegexModal.tsx index df6535bd12..cc90a78b50 100644 --- a/src/components/app/details/triggerView/BranchRegexModal.tsx +++ b/src/components/app/details/triggerView/BranchRegexModal.tsx @@ -42,7 +42,6 @@ const BranchRegexModal = ({ handleRegexInputValue, regexValue, onCloseBranchRegexModal, - hideHeaderFooter, savingRegexValue, }: BranchRegexModalProps) => { const getBranchRegexName = (gitMaterialId: number) => { @@ -55,40 +54,35 @@ const BranchRegexModal = ({ return ciMaterial?.source?.regex || '' } - const renderBranchRegexMaterialHeader = () => { - if (hideHeaderFooter) { - return null - } - return ( -
-
- {isChangeBranchClicked && ( -
-
- ) - } +
+ ) const renderRegexInfo = () => (
diff --git a/src/components/v2/appDetails/sourceInfo/environment.type.ts b/src/components/v2/appDetails/sourceInfo/environment.type.ts index c259042ea2..daccdcf3f8 100644 --- a/src/components/v2/appDetails/sourceInfo/environment.type.ts +++ b/src/components/v2/appDetails/sourceInfo/environment.type.ts @@ -15,6 +15,7 @@ */ import { AppEnvironment as BaseAppEnvironmentType } from '@devtron-labs/devtron-fe-common-lib' +import { TooltipProps } from '@devtron-labs/devtron-fe-common-lib/dist/Common/Tooltip/types' import { DeploymentStatusDetailsBreakdownDataType } from '../../../app/details/appDetails/appDetails.type' import { HelmReleaseStatus } from '../../../external-apps/ExternalAppService' import { AppDetails } from '../appDetails.type' @@ -46,9 +47,15 @@ export interface ChartUsedCardType { notes: string onClickShowNotes: () => void cardLoading: boolean + onClickUpgrade: () => void } export interface HelmAppConfigApplyStatusCardType { releaseStatus: HelmReleaseStatus cardLoading: boolean } + +export interface ChartToolTipType extends Pick { + children: TooltipProps['children'] + isDeprecated: boolean +} diff --git a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx index fb1f23a923..d2988fa789 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx @@ -20,10 +20,24 @@ import { ReactComponent as QuestionIcon } from '../../../assets/icons/ic-questio import { ReactComponent as File } from '../../../../../assets/icons/ic-file.svg' import { ReactComponent as DefaultChart } from '../../../../../assets/icons/ic-default-chart.svg' import { URLS } from '../../../../../config' -import { ChartUsedCardType } from '../environment.type' +import { ChartToolTipType, ChartUsedCardType } from '../environment.type' import LoadingCard from '../../../../app/details/appDetails/LoadingCard' +import { getUsedChartContent } from '../utils' -const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading }: ChartUsedCardType) => { +const ChartToolTip = ({ children, isDeprecated, onClickUpgrade }: ChartToolTipType) => ( + document.body} // To fix the issue of tippy not showing the content outside the container + > + {children} + +) + +const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading, onClickUpgrade }: ChartUsedCardType) => { if (cardLoading) { return } @@ -33,23 +47,21 @@ const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading }: Cha
-
- Chart used -
- + Chart {appDetails.deprecated ? 'deprecated' : 'used'} +
+
- +
diff --git a/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx b/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx index a7b786dd7f..dfade58dd9 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx @@ -169,6 +169,7 @@ const EnvironmentStatusComponent = ({ notes={notes} onClickShowNotes={onClickShowNotes} cardLoading={cardLoading} + onClickUpgrade={onClickUpgrade} /> ) } @@ -201,7 +202,6 @@ const EnvironmentStatusComponent = ({ {renderHelmConfigApplyStatusBlock()} {renderLastUpdatedBlock()} {renderChartUsedBlock()} - {renderUpgraderChartBlock()} {isScanV2Enabled && appDetails?.appType === AppType.DEVTRON_HELM_CHART && }
)} diff --git a/src/components/v2/appDetails/sourceInfo/utils.tsx b/src/components/v2/appDetails/sourceInfo/utils.tsx index 791317b7e3..cedb105e2d 100644 --- a/src/components/v2/appDetails/sourceInfo/utils.tsx +++ b/src/components/v2/appDetails/sourceInfo/utils.tsx @@ -1,3 +1,5 @@ +import { ReactComponent as RightArrow } from '@Icons/ic-arrow-right.svg' + export const getEnvironmentName = ( clusterName: string, namespace: string, @@ -11,3 +13,30 @@ export const getEnvironmentName = ( } return   } + +export const getUsedChartContent = (isDeprecated: boolean, onClickUpgrade: () => void) => ( +
+
+ {isDeprecated ? ( +
+ Chart deprecated +
This chart has been deprecated. Change chart or chart version.
+
+ ) : ( +
+ Chart used +
Chart used to deploy to this application
+
+ )} +
+ +
+) diff --git a/src/css/base.scss b/src/css/base.scss index f46b5056e8..e9b24d8fff 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -2463,6 +2463,10 @@ button.anchor { background-color: var(--N100); } +.dc__bs-contain { + background-size: contain; +} + .dc__registry-icon, .repository-icon { width: 20px; From 2bbc47062f2043199dacd3b602825fe300d0ae97 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 19 Nov 2024 14:16:58 +0530 Subject: [PATCH 07/52] chore: removed unused code --- .../EnvironmentStatus.component.tsx | 17 ----------------- .../v2/appDetails/sourceInfo/utils.tsx | 1 + 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx b/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx index dfade58dd9..e1dc323126 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx @@ -174,23 +174,6 @@ const EnvironmentStatusComponent = ({ ) } - const renderUpgraderChartBlock = () => { - return ( - appDetails?.deprecated && ( -
-
- Chart deprecated - -
-
Upgrade required
-
- Upgrade chart -
-
- ) - ) - } - return (
{loadingDetails ? ( diff --git a/src/components/v2/appDetails/sourceInfo/utils.tsx b/src/components/v2/appDetails/sourceInfo/utils.tsx index cedb105e2d..c45a38d01c 100644 --- a/src/components/v2/appDetails/sourceInfo/utils.tsx +++ b/src/components/v2/appDetails/sourceInfo/utils.tsx @@ -29,6 +29,7 @@ export const getUsedChartContent = (isDeprecated: boolean, onClickUpgrade: () =>
)}
+ {/* Due to missing support of white text, unable to use Button component */}
- + {ClusterMap && } {!filteredList.length ? (
diff --git a/src/components/ClusterNodes/ColumnSelector.tsx b/src/components/ClusterNodes/ColumnSelector.tsx deleted file mode 100644 index 4a57712561..0000000000 --- a/src/components/ClusterNodes/ColumnSelector.tsx +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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 React, { useState, useEffect } from 'react' -import ReactSelect, { components, MultiValue, SelectInstance } from 'react-select' -import { Option, ReactSelectInputAction } from '@devtron-labs/devtron-fe-common-lib' -import { ColumnMetadataType } from './types' -import { ReactComponent as Setting } from '../../assets/icons/ic-nav-gear.svg' -import { containerImageSelectStyles } from '../CIPipelineN/ciPipeline.utils' -import { useColumnFilterContext } from './NodeListSearchFilter' -import { COLUMN_METADATA } from './constants' - -const ValueContainer = (props: any): JSX.Element => { - const { length } = props.getValue() - - return ( - - {length > 0 ? ( - <> - {!props.selectProps.menuIsOpen && ( - <> - - Columns - - )} - {React.cloneElement(props.children[1])} - - ) : ( - <>{props.children} - )} - - ) -} - -const MenuList = (props: any): JSX.Element => { - const { - selectedColumns, - setAppliedColumns, - setMenuOpen, - selectRef, - }: { - selectedColumns: MultiValue - appliedColumns: MultiValue - setAppliedColumns: React.Dispatch>> - isMenuOpen: boolean - setMenuOpen: React.Dispatch> - selectRef: React.MutableRefObject> - } = useColumnFilterContext() - const handleApplySelectedColumns = (): void => { - setMenuOpen(false) - const _appliedColumns = [...selectedColumns].sort((a, b) => a['columnIndex'] - b['columnIndex']) - if (typeof Storage !== 'undefined') { - localStorage.appliedColumns = JSON.stringify(_appliedColumns) - } - selectRef.current?.blur() - setAppliedColumns(_appliedColumns) - } - return ( - - {props.children} -
- -
-
- ) -} -export default function ColumnSelector() { - const { - appliedColumns, - selectedColumns, - setSelectedColumns, - isMenuOpen, - setMenuOpen, - selectRef, - }: { - appliedColumns: MultiValue - selectedColumns: MultiValue - setSelectedColumns: React.Dispatch>> - isMenuOpen: boolean - setMenuOpen: React.Dispatch> - selectRef: React.MutableRefObject> - } = useColumnFilterContext() - const [columnOptions, setColumnOptions] = useState>([]) - const [columnFilterInput, setColumnFilterInput] = useState('') - - useEffect(() => { - setColumnOptions(COLUMN_METADATA.filter((columnData) => !columnData.isDisabled)) - setSelectedColumns(appliedColumns) - }, []) - - const handleMenuState = (menuOpenState: boolean): void => { - if (menuOpenState) { - setSelectedColumns(appliedColumns) - } - setMenuOpen(menuOpenState) - } - - const handleCloseFilter = (): void => { - handleMenuState(false) - setSelectedColumns(appliedColumns) - } - - return ( - handleMenuState(true)} - onMenuClose={handleCloseFilter} - blurInputOnSelect={false} - inputValue={columnFilterInput} - onBlur={() => { - setColumnFilterInput('') - }} - onInputChange={(value, action) => { - if (action.action === ReactSelectInputAction.inputChange) { - setColumnFilterInput(value) - } - }} - components={{ - Option, - ValueContainer, - IndicatorSeparator: null, - ClearIndicator: null, - MenuList, - }} - styles={{ - ...containerImageSelectStyles, - menu: (base, state) => ({ - ...base, - zIndex: 6, - }), - menuList: (base, state) => ({ - ...base, - borderRadius: '4px', - paddingTop: 0, - paddingBottom: 0, - }), - option: (base, state) => ({ - ...base, - padding: '10px 0', - backgroundColor: state.isFocused ? 'var(--N100) !important' : 'var(--N0) !important', - color: 'var(--N900)', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - cursor: 'pointer', - }), - dropdownIndicator: (base, state) => ({ - ...base, - color: 'var(--N400)', - transition: 'all .2s ease', - transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : 'rotate(0deg)', - padding: '0 8px', - }), - }} - /> - ) -} diff --git a/src/components/ClusterNodes/NodeDetailsList.tsx b/src/components/ClusterNodes/NodeDetailsList.tsx deleted file mode 100644 index febc822d7e..0000000000 --- a/src/components/ClusterNodes/NodeDetailsList.tsx +++ /dev/null @@ -1,761 +0,0 @@ -/* - * 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 { useState, useEffect, useRef } from 'react' -import { NavLink, useLocation, useRouteMatch, useHistory, useParams } from 'react-router-dom' -import * as queryString from 'query-string' -import { MultiValue } from 'react-select' -import { - useAsync, - abortPreviousRequests, - showError, - Progressing, - ConditionalWrap, - ErrorScreenManager, - Pagination, - SortableTableHeaderCell, - SortingOrder, - Tooltip, - ClipboardButton, - useResizableTableConfig, - useBulkSelection, - BulkOperationModalState, - BulkSelectionEvents, - BulkSelection, - Checkbox, - CHECKBOX_VALUE, - noop, -} from '@devtron-labs/devtron-fe-common-lib' -import { getNodeList, getClusterCapacity, deleteNodeCapacity } from './clusterNodes.service' -import 'react-mde/lib/styles/css/react-mde-all.css' -import { ColumnMetadataType, TEXT_COLOR_CLASS, NodeDetail, SearchTextType } from './types' -import { ReactComponent as Error } from '../../assets/icons/ic-error-exclamation.svg' -import { OptionType } from '../app/types' -import NodeListSearchFilter from './NodeListSearchFilter' -import { OrderBy } from '../app/list/types' -import ClusterNodeEmptyState from './ClusterNodeEmptyStates' -import { COLUMN_METADATA, NODE_SEARCH_TEXT } from './constants' -import NodeActionsMenu from './NodeActions/NodeActionsMenu' -import { AppDetailsTabs } from '../v2/appDetails/appDetails.store' -import { unauthorizedInfoText } from '../ResourceBrowser/ResourceList/ClusterSelector' -import { K8S_EMPTY_GROUP, SIDEBAR_KEYS, NODE_DETAILS_PAGE_SIZE_OPTIONS } from '../ResourceBrowser/Constants' -import { importComponentFromFELibrary } from '@Components/common' -import { URLParams } from '../ResourceBrowser/Types' -import './clusterNodes.scss' - -const RBBulkSelectionActionWidget = importComponentFromFELibrary('RBBulkSelectionActionWidget', null, 'function') -const RBBulkOperations = importComponentFromFELibrary('RBBulkOperations', null, 'function') - -export default function NodeDetailsList({ isSuperAdmin, renderRefreshBar, addTab, showStaleDataWarning, clusterName }) { - const { clusterId, nodeType } = useParams() - const match = useRouteMatch() - const location = useLocation() - const history = useHistory() - const urlParams = new URLSearchParams(location.search) - const k8sVersion = urlParams.get('k8sversion') ? decodeURIComponent(urlParams.get('k8sversion')) : '' - const name = decodeURIComponent(urlParams.get(NODE_SEARCH_TEXT.NAME) || '') - const label = decodeURIComponent(urlParams.get(NODE_SEARCH_TEXT.LABEL) || '') - const group = decodeURIComponent(urlParams.get(NODE_SEARCH_TEXT.NODE_GROUP) || '') - const [clusterDetailsLoader, setClusterDetailsLoader] = useState(false) - const [errorResponseCode, setErrorResponseCode] = useState() - const [searchText, setSearchText] = useState(name || label || group || '') - const defaultVersion = { label: 'K8s version: Any', value: 'K8s version: Any' } - const [flattenNodeList, setFlattenNodeList] = useState<(NodeDetail & Record<'id', number>)[]>([]) - const [filteredFlattenNodeList, setFilteredFlattenNodeList] = useState([]) - const [selectedVersion, setSelectedVersion] = useState( - k8sVersion ? { label: `K8s version: ${k8sVersion}`, value: k8sVersion } : defaultVersion, - ) - - const initialSeachType = getInitialSearchType(name, label, group) - const [selectedSearchTextType, setSelectedSearchTextType] = useState(initialSeachType) - - const [bulkOperationModalState, setBulkOperationModalState] = useState('closed') - - const [sortByColumn, setSortByColumn] = useState(COLUMN_METADATA[0]) - const [sortOrder, setSortOrder] = useState(OrderBy.ASC) - const [noResults, setNoResults] = useState(false) - 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 parentRef = useRef() - - const [, nodeK8sVersions] = useAsync( - () => - abortPreviousRequests(async () => { - const { result } = await getClusterCapacity(clusterId, abortControllerRef.current?.signal) - return result?.nodeK8sVersions || null - }, abortControllerRef), - [clusterId], - ) - - const { - selectedIdentifiers: bulkSelectionState, - handleBulkSelection, - setIdentifiers, - isBulkSelectionApplied, - getSelectedIdentifiersCount, - } = useBulkSelection>() - - function getInitialSearchType(name: string, label: string, group: string): SearchTextType | '' { - if (name) { - return NODE_SEARCH_TEXT.NAME - } - if (label) { - return NODE_SEARCH_TEXT.LABEL - } - if (group) { - return NODE_SEARCH_TEXT.NODE_GROUP - } - return '' - } - - const getSearchTextMap = (searchText: string): Map => { - const _searchedTextMap = new Map() - if (!searchText) { - return _searchedTextMap - } - const searchedLabelArr = searchText.split(',').map((item) => item.trim()) - - for (const currentItem of searchedLabelArr) { - if (!currentItem) { - continue - } - if (selectedSearchTextType === NODE_SEARCH_TEXT.LABEL) { - const element = currentItem.split('=') - const key = element[0] ? element[0].trim() : null - if (!key) { - continue - } - const value = element[1] ? element[1].trim() : null - _searchedTextMap.set(key, value) - } else { - _searchedTextMap.set(currentItem, true) - } - } - return _searchedTextMap - } - - const [searchedTextMap, setSearchedTextMap] = useState>(getSearchTextMap(searchText)) - const [nodeListOffset, setNodeListOffset] = useState(0) - const [pageSize, setPageSize] = useState(NODE_DETAILS_PAGE_SIZE_OPTIONS[0].value) - - const showPaginatedView = filteredFlattenNodeList.length > pageSize - - useEffect(() => { - const qs = queryString.parse(location.search) - const offset = Number(qs['offset']) - setNodeListOffset(offset || 0) - const version = qs['k8sversion'] - if (version && typeof version === 'string' && selectedVersion.value !== version) { - setSelectedVersion({ label: `K8s version: ${version}`, value: version }) - } - }, [location.search]) - - useEffect(() => { - if (filteredFlattenNodeList) { - handleUrlChange(filteredFlattenNodeList) - } - }, [filteredFlattenNodeList]) - - useEffect(() => { - setIdentifiers( - (filteredFlattenNodeList?.slice(nodeListOffset, nodeListOffset + pageSize).reduce((acc, curr) => { - acc[curr.id] = curr - return acc - }, {}) as Record) ?? {}, - ) - }, [nodeListOffset, filteredFlattenNodeList, pageSize]) - - - const getUpdatedAppliedColumn = () => { - let isMissingColumn = false // intialized this to check if sortingFieldName is missing - let appliedColumnsFromLocalStorage - const sortableColumnMap = new Map([]) - const _defaultColumns = [] - - for (const metaData of COLUMN_METADATA) { - if (metaData.isDefault) { - _defaultColumns.push(metaData) - } - - if (metaData.isSortingAllowed) { - sortableColumnMap.set(metaData.value, metaData) - } - } - - if (typeof Storage !== 'undefined') { - if (!localStorage.appliedColumns) { - localStorage.appliedColumns = JSON.stringify(_defaultColumns) // in case of no appliedColumns add COLUMN_METADATA as it is in the storage - } else { - try { - appliedColumnsFromLocalStorage = JSON.parse(localStorage.appliedColumns) - for (const _updatedLocalMetaData of appliedColumnsFromLocalStorage as ColumnMetadataType[]) { - if (_updatedLocalMetaData.isSortingAllowed && !_updatedLocalMetaData.sortingFieldName) { - _updatedLocalMetaData.sortingFieldName = sortableColumnMap.get( - _updatedLocalMetaData.value, - ).sortingFieldName // updating column meta data when sortingFieldName is missing - isMissingColumn = true - } - } - if (isMissingColumn) { - localStorage.appliedColumns = JSON.stringify(appliedColumnsFromLocalStorage) - } - } catch (error) {} - } - } - setAppliedColumns(appliedColumnsFromLocalStorage || _defaultColumns) - } - - useEffect(() => { - getUpdatedAppliedColumn() - }, []) - - const flattenObject = (ob: Object): Object => { - const toReturn = {} - for (const i in ob) { - if (!ob.hasOwnProperty(i)) { - continue - } - const currentElement = ob[i] - if (typeof currentElement === 'object' && currentElement !== null && !Array.isArray(currentElement)) { - const flatObject = flattenObject(currentElement) - for (const x in flatObject) { - if (!flatObject.hasOwnProperty(x)) { - continue - } - - toReturn[`${i}.${x}`] = flatObject[x] - } - } else { - toReturn[i] = currentElement - } - } - return toReturn - } - - const getNodeListData = (): void => { - setClusterDetailsLoader(true) - setErrorResponseCode(null) - getNodeList(clusterId) - .then((response) => { - if (response.result) { - const _flattenNodeList = response.result.map((data, index) => { - const _flattenNodeData = flattenObject(data) - if (data['errors']) { - _flattenNodeData['errorCount'] = Object.keys(data['errors']).length - } - if (data['taints']) { - _flattenNodeData['taintCount'] = Object.keys(data['taints']).length - } - _flattenNodeData['id'] = index - return _flattenNodeData - }) - setFlattenNodeList(_flattenNodeList as typeof flattenNodeList) - } - setClusterDetailsLoader(false) - }) - .catch((error) => { - if (error['code'] !== 403) { - showError(error) - } - setErrorResponseCode(error.code) - setClusterDetailsLoader(false) - }) - } - - useEffect(() => { - getNodeListData() - }, [clusterId]) - - const handleClearBulkSelection = () => { - handleBulkSelection({ - action: BulkSelectionEvents.CLEAR_ALL_SELECTIONS, - }) - } - - const handleUrlChange = (sortedResult) => { - const queryParams = new URLSearchParams(location.search) - const selectedNode = sortedResult.find((item) => item.name === queryParams.get('node')) - if (selectedNode) { - openTerminalComponent(selectedNode) - } - } - - const handleFilterChanges = (): void => { - const _flattenNodeList = [] - for (const element of flattenNodeList) { - if (selectedVersion.value !== defaultVersion.value && element['k8sVersion'] !== selectedVersion.value) { - continue - } - if (selectedSearchTextType === NODE_SEARCH_TEXT.NAME && searchedTextMap.size > 0) { - let matchFound = false - for (const [key] of searchedTextMap.entries()) { - if (element[NODE_SEARCH_TEXT.NAME].indexOf(key) >= 0) { - matchFound = true - break - } - } - if (!matchFound) { - continue - } - } else if (selectedSearchTextType === NODE_SEARCH_TEXT.LABEL) { - let matchedLabelCount = 0 - for (let i = 0; i < element[NODE_SEARCH_TEXT.LABELS]?.length; i++) { - const currentLabel = element[NODE_SEARCH_TEXT.LABELS][i] - const matchedLabel = searchedTextMap.get(currentLabel.key) - if (matchedLabel === undefined || (matchedLabel !== null && currentLabel.value !== matchedLabel)) { - continue - } - matchedLabelCount++ - } - if (searchedTextMap.size !== matchedLabelCount) { - continue - } - } else if (selectedSearchTextType === NODE_SEARCH_TEXT.NODE_GROUP) { - let matchFound = false - for (const [key] of searchedTextMap.entries()) { - if (element[NODE_SEARCH_TEXT.NODE_GROUP].indexOf(key) >= 0) { - matchFound = true - break - } - } - if (!matchFound) { - continue - } - } - - _flattenNodeList.push(element) - } - if (sortByColumn) { - const comparatorMethod = - sortByColumn.sortType === 'number' ? numericComparatorMethod : alphabeticalComparatorMethod - _flattenNodeList.sort(comparatorMethod) - } - setFilteredFlattenNodeList(_flattenNodeList) - setNoResults(_flattenNodeList.length === 0) - setNodeListOffset(0) - } - - const numericComparatorMethod = (a, b) => { - let firstValue = a[sortByColumn.sortingFieldName] || 0 - let secondValue = b[sortByColumn.sortingFieldName] || 0 - - if (typeof firstValue === 'string' && firstValue.endsWith('%')) { - firstValue = firstValue.slice(0, -1) - } - - if (typeof secondValue === 'string' && secondValue.endsWith('%')) { - secondValue = secondValue.slice(0, -1) - } - - return sortOrder === OrderBy.ASC ? firstValue - secondValue : secondValue - firstValue - } - - const alphabeticalComparatorMethod = (a, b) => { - const firstValue = a[sortByColumn.sortingFieldName] || '' - const secondValue = b[sortByColumn.sortingFieldName] || '' - if ( - (sortOrder === OrderBy.ASC && sortByColumn.sortingFieldName !== 'createdAt') || - (sortOrder === OrderBy.DESC && sortByColumn.sortingFieldName === 'createdAt') - ) { - return firstValue.localeCompare(secondValue) - } - return secondValue.localeCompare(firstValue) - } - - const clearFilter = (): void => { - const qs = queryString.parse(location.search) - const keys = Object.keys(qs) - const query = {} - keys.forEach((key) => { - query[key] = qs[key] - }) - setSearchText('') - setSelectedSearchTextType('') - setSearchedTextMap(new Map()) - delete query[selectedSearchTextType] - const queryStr = queryString.stringify(query) - history.push(`?${queryStr}`) - } - - useEffect(() => { - handleFilterChanges() - }, [searchedTextMap, searchText, flattenNodeList, sortByColumn, sortOrder, selectedVersion]) - - const handleSortClick = (column: ColumnMetadataType) => () => { - if (sortByColumn.label === column.label) { - setSortOrder(sortOrder === OrderBy.ASC ? OrderBy.DESC : OrderBy.ASC) - } else { - setSortByColumn(column) - setSortOrder(OrderBy.ASC) - } - } - - const changePageSize = (size: number) => { - setPageSize(size) - setNodeListOffset(0) - } - - const getBulkOperationModalStateSetter = (state: BulkOperationModalState) => () => { - setBulkOperationModalState(state) - } - - const getHandleCheckedForId = (nodeData: (typeof filteredFlattenNodeList)[number]) => () => { - const id = nodeData.id - - if (isBulkSelectionApplied) { - handleBulkSelection({ - action: BulkSelectionEvents.CLEAR_IDENTIFIERS_AFTER_ACROSS_SELECTION, - data: { - identifierIds: [id], - }, - }) - } else if (bulkSelectionState[id]) { - handleBulkSelection({ - action: BulkSelectionEvents.CLEAR_IDENTIFIERS, - data: { - identifierIds: [id], - }, - }) - } else { - handleBulkSelection({ - action: BulkSelectionEvents.SELECT_IDENTIFIER, - data: { - identifierObject: { - ...bulkSelectionState, - [id]: nodeData, - }, - }, - }) - } - } - - const renderNodeListHeader = (column: ColumnMetadataType): JSX.Element => ( -
- {column.label.toUpperCase() === 'NODE' && } - -
- ) - - const getBulkOperations = () => { - if (bulkOperationModalState === 'closed') { - return [] - } - - const selections = (isBulkSelectionApplied ? filteredFlattenNodeList : Object.values(bulkSelectionState)) ?? [] - - return selections.map((selection) => ({ - id: selection.id, - name: selection.name, - operation: async (signal: AbortSignal) => { - const payload = { - clusterId: Number(clusterId), - name: selection.name, - version: selection.version, - kind: selection.kind, - } - - await deleteNodeCapacity(payload, signal) - } - })) - } - - const handleReloadDataAfterBulkDelete = () => { - handleClearBulkSelection() - getNodeListData() - } - - const renderPercentageTippy = (nodeData: Object, column: ColumnMetadataType, children: any): JSX.Element => { - return ( - - - {column.value === 'cpu.usagePercentage' - ? `CPU Usage: ${nodeData['cpu.usage']}` - : `Memory Usage: ${nodeData['memory.usage']}`} - - - {column.value === 'cpu.usagePercentage' - ? `Allocatable CPU: ${nodeData['cpu.allocatable']}` - : `Allocatable Memory: ${nodeData['memory.allocatable']}`} - - - } - > -
{children}
-
- ) - } - - const renderConditionalWrap = (column, nodeData) => { - if (column.value === 'status' && nodeData['unschedulable']) { - return ( - - {nodeData[column.value]} - - SchedulingDisabled - - ) - } - if (column.value === 'k8sVersion') { - return ( - - {nodeData[column.value]} - - ) - } - return nodeData[column.value] - } - - const renderNodeRow = (column, nodeData) => { - if (column.value === 'errorCount') { - return ( - nodeData['errorCount'] > 0 && ( - - - {nodeData['errorCount'] || '-'} - - ) - ) - } - if (column.sortType === 'boolean') { - return `${nodeData[column.value]}` - } - if (nodeData[column.value] !== undefined) { - return ( - 0} - wrap={(children) => renderPercentageTippy(nodeData, column, children)} - > - {renderConditionalWrap(column, nodeData)} - - ) - } - return '-' - } - - const clusterNodeClickEvent = (nodeData, column) => { - const url = `${match.url}/${nodeData[column.value]}` - return () => { - addTab(K8S_EMPTY_GROUP, nodeType, nodeData[column.value], url) - history.push(url) - } - } - - const renderNodeList = (nodeData: (typeof filteredFlattenNodeList)[number]): JSX.Element => { - return ( -
- {appliedColumns.map((column) => { - return column.label === 'Node' ? ( -
- - - - {nodeData[column.value]} - - - - -
- ) : ( -
- {renderNodeRow(column, nodeData)} -
- ) - })} -
- ) - } - const changePage = (pageNo: number): void => { - const offset = pageSize * (pageNo - 1) - setNodeListOffset(offset) - const qs = queryString.parse(location.search) - const keys = Object.keys(qs) - const query = {} - keys.forEach((key) => { - query[key] = qs[key] - }) - query['offset'] = offset - const queryStr = queryString.stringify(query) - const url = `${match.url}?${queryStr}` - nodeListRef.current?.scrollTo({ top: 0, behavior: 'smooth' }) - history.push(url) - } - const renderPagination = (): JSX.Element => { - return ( - showPaginatedView && ( - - ) - ) - } - - const openTerminalComponent = (nodeData) => { - const queryParams = new URLSearchParams(location.search) - queryParams.set('node', nodeData.name) - const url = location.pathname - history.push( - `${url.split('/').slice(0, -2).join('/')}/${AppDetailsTabs.terminal}/${K8S_EMPTY_GROUP}?${queryParams.toString()}`, - ) - } - - if (errorResponseCode) { - return ( -
- -
- ) - } - - return ( -
- {typeof renderRefreshBar === 'function' && renderRefreshBar()} - {clusterDetailsLoader ? ( -
- -
- ) : ( -
-
- -
- {noResults ? ( - - ) : ( - <> -
-
- {appliedColumns.map((column) => renderNodeListHeader(column))} -
- {filteredFlattenNodeList - .slice(nodeListOffset, nodeListOffset + pageSize) - ?.map((nodeData) => renderNodeList(nodeData))} -
- {renderPagination()} - - )} -
- )} - {RBBulkSelectionActionWidget && (getSelectedIdentifiersCount() > 0 || isBulkSelectionApplied) && ( - - )} - {RBBulkOperations && bulkOperationModalState !== 'closed' && ( - - )} -
- ) -} diff --git a/src/components/ClusterNodes/NodeListSearchFilter.tsx b/src/components/ClusterNodes/NodeListSearchFilter.tsx deleted file mode 100644 index 3f0f6fb1ac..0000000000 --- a/src/components/ClusterNodes/NodeListSearchFilter.tsx +++ /dev/null @@ -1,299 +0,0 @@ -/* - * 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 React, { useState, useEffect, useRef } from 'react' -import { MultiValue } from 'react-select' -import { useLocation, useHistory } from 'react-router-dom' -import * as queryString from 'query-string' -import { ReactComponent as Search } from '../../assets/icons/ic-search.svg' -import { ReactComponent as Clear } from '../../assets/icons/ic-error.svg' -import { ColumnMetadataType, NodeListSearchFliterType } from './types' -import ColumnSelector from './ColumnSelector' -import { NodeSearchOption, SEARCH_OPTION_LABEL } from './constants' -import { ShortcutKeyBadge } from '../common/formFields/Widgets/Widgets' -import { SelectPicker } from '@devtron-labs/devtron-fe-common-lib' - -const ColumnFilterContext = React.createContext(null) - -export function useColumnFilterContext() { - const context = React.useContext(ColumnFilterContext) - if (!context) { - throw new Error(`cannot be rendered outside the component`) - } - return context -} - -export default function NodeListSearchFilter({ - defaultVersion, - nodeK8sVersions, - selectedVersion, - setSelectedVersion, - appliedColumns, - setAppliedColumns, - selectedSearchTextType, - setSelectedSearchTextType, - searchText, - setSearchText, - searchedTextMap, - setSearchedTextMap, -}: NodeListSearchFliterType) { - const [searchApplied, setSearchApplied] = useState(false) - const [openFilterPopup, setOpenFilterPopup] = useState(false) - const [searchInputText, setSearchInputText] = useState('') - const [isMenuOpen, setMenuOpen] = useState(false) - const [selectedColumns, setSelectedColumns] = useState>([]) - const selectRef = useRef() - const location = useLocation() - const { push } = useHistory() - useEffect(() => { - if (searchInputText !== searchText) { - setSearchInputText(searchText) - if (!searchText) { - setSearchApplied(false) - } else { - setSearchApplied(true) - } - } - }, [searchText, searchedTextMap]) - - const handleFocus = () => { - document.removeEventListener('keydown', keyPressHandler) - } - - const handleBlur = () => { - document.addEventListener('keydown', keyPressHandler) - } - - useEffect(() => { - handleBlur() - document.addEventListener('focusin', handleFocus) - document.addEventListener('focusout', handleBlur) - return () => { - document.removeEventListener('keydown', keyPressHandler) - document.removeEventListener('focusin', handleFocus) - document.removeEventListener('focusout', handleBlur) - } - }, []) - - const keyPressHandler = (e) => { - if (e.key === 'r') { - setOpenFilterPopup(true) - } - } - - const clearTextFilter = (): void => { - handleQueryParamsSeacrh('') - setSearchInputText('') - setSearchText('') - setSelectedSearchTextType('') - setSearchedTextMap(new Map()) - setSearchApplied(false) - } - - const handleFilterInput = (event): void => { - setSearchInputText(event.target.value) - } - const handleQueryParamsSeacrh = (searchString: string) => { - const qs = queryString.parse(location.search) - const keys = Object.keys(qs) - const query = {} - keys.forEach((key) => { - query[key] = qs[key] - }) - if (searchString) { - query[selectedSearchTextType] = searchInputText - } else { - delete query[selectedSearchTextType] - } - const queryStr = queryString.stringify(query) - push(`?${queryStr}`) - } - - const handleFilterTag = (event): void => { - const theKeyCode = event.key - if (theKeyCode === 'Enter') { - const _searchedTextMap = new Map() - const searchedLabelArr = searchInputText.split(',') - for (let index = 0; index < searchedLabelArr.length; index++) { - const currentItem = searchedLabelArr[index].trim() - if (!currentItem) { - continue - } - if (selectedSearchTextType === SEARCH_OPTION_LABEL.LABEL) { - const element = currentItem.split('=') - const key = element[0] ? element[0].trim() : null - if (!key) { - continue - } - const value = element[1] ? element[1].trim() : null - _searchedTextMap.set(key, value) - } else { - _searchedTextMap.set(currentItem, true) - } - } - - handleQueryParamsSeacrh(searchInputText) - setSearchText(searchInputText) - setSearchedTextMap(_searchedTextMap) - setSearchApplied(true) - setOpenFilterPopup(false) - } else if (theKeyCode === 'Backspace') { - if (searchInputText.length === 0 && selectedSearchTextType) { - handleQueryParamsSeacrh('') - setSelectedSearchTextType('') - setSearchText('') - setOpenFilterPopup(false) - setSearchApplied(false) - } - } - } - - const toggleSelectPopup = (): void => { - setOpenFilterPopup(!openFilterPopup) - } - - const selectFilterType = (filter: { label: string; value: string | number; type: string }): void => { - setSelectedSearchTextType(filter.label) - setSearchInputText('') - setOpenFilterPopup(false) - } - - const applyFilter = (selected) => { - setSelectedVersion(selected) - const qs = queryString.parse(location.search) - const keys = Object.keys(qs) - const query = {} - keys.forEach((key) => { - query[key] = qs[key] - }) - if (selected.value === defaultVersion.value) { - delete query['k8sversion'] - } else { - query['k8sversion'] = selected.value - } - const queryStr = queryString.stringify(query) - push(`?${queryStr}`) - } - const renderTextFilter = (): JSX.Element => { - let placeholderText = '' - if (selectedSearchTextType === SEARCH_OPTION_LABEL.NAME) { - placeholderText = 'Search by node name Eg. ip-172-31-2-152.us-east-2.compute.internal' - } else if (selectedSearchTextType === SEARCH_OPTION_LABEL.LABEL) { - placeholderText = 'Search by key=value Eg. environment=production, tier=frontend' - } else { - placeholderText = 'Search by node group name Eg. mainnode' - } - - return ( -
-
setOpenFilterPopup(true)} - > - - {selectedSearchTextType ? ( - <> - - {selectedSearchTextType === SEARCH_OPTION_LABEL.NODE_GROUP - ? SEARCH_OPTION_LABEL.NODE_GROUP_TEXT - : selectedSearchTextType} - : - - - - ) : ( - Search nodes by name, labels or node group - )} - {!selectedSearchTextType && ( - - )} -
- {openFilterPopup && ( - <> -
- {!selectedSearchTextType && ( -
-
Search by
- {NodeSearchOption.map((o) => { - return ( -
{ - selectFilterType(o) - }} - > - {o.label === SEARCH_OPTION_LABEL.NODE_GROUP - ? SEARCH_OPTION_LABEL.NODE_GROUP_TEXT - : o.label} -
- ) - })} -
- )} - - )} - {searchApplied && ( - - )} -
- ) - } - return ( -
- {renderTextFilter()} - ({ - label: `K8s version: ${version}`, - value: version, - })) || []), - ]} - onChange={applyFilter} - value={selectedVersion} - /> -
- - - -
- ) -} diff --git a/src/components/ClusterNodes/clusterNodes.scss b/src/components/ClusterNodes/clusterNodes.scss index 8a6ac3483a..10eae67dc4 100644 --- a/src/components/ClusterNodes/clusterNodes.scss +++ b/src/components/ClusterNodes/clusterNodes.scss @@ -26,90 +26,6 @@ } } -.node-list { - .list-min-height { - min-height: calc(100vh - 120px); - &.no-result-container { - overflow: hidden; - display: grid; - grid-template-rows: 32px; - } - &.sync-error { - min-height: calc(100vh - 152px); - } - &.with-error-bar { - min-height: calc(100vh - 270px); - } - } - .search-wrapper { - display: grid; - grid-template-columns: auto 160px 1px 180px; - column-gap: 8px; - - .outline-none { - outline: none !important; - } - .bottom-2px { - bottom: 2px; - } - .search-popup { - z-index: 6; - .search-title { - background: var(--window-bg); - } - } - } - - .setting-icon path { - stroke: var(--N600); - } - - .node-list-row { - display: grid; - column-gap: 16px; - width: max-content; - min-width: 100%; - - &:hover > :first-child { - background-color: var(--N50); - } - - & > :first-child { - position: sticky; - left: 0; - z-index: 1; - padding-left: 20px; - background-color: var(--N0); - border-right: 1px solid var(--N200); - } - - & > :last-child { - padding-right: 20px; - } - - &.no-hover-bg { - &:hover > :first-child { - background-color: var(--N0); - } - } - } - - .node-actions-menu-icon { - path:nth-child(2) { - fill: var(--N600); - } - } - - &.show-terminal { - height: calc(50vh - 78px) !important; - } -} - -.node-list-search-key { - right: 10px; - top: 6px; -} - .node-details-container { .w-49 { width: 49%; @@ -237,7 +153,6 @@ } } .cluster-list-main-container, -.node-list, .node-details-container { .top-3 { top: 3px; diff --git a/src/components/ClusterNodes/clusterNodes.service.tsx b/src/components/ClusterNodes/clusterNodes.service.tsx index 532e75ab37..ba6adc0aff 100644 --- a/src/components/ClusterNodes/clusterNodes.service.tsx +++ b/src/components/ClusterNodes/clusterNodes.service.tsx @@ -23,7 +23,6 @@ import { NodeActionRequest, NodeDetailResponse, NodeDrainRequest, - NodeListResponse, UpdateNodeRequestBody, EditTaintsRequest, TerminalDataType, @@ -55,9 +54,6 @@ export const updateClusterShortDescription = ( requestPayload: ClusterShortDescriptionPatchRequest, ): Promise => put(`${Routes.CLUSTER_DESCRIPTION}`, requestPayload) -export const getNodeList = (clusterId: string): Promise => - get(`${Routes.NODE_LIST}?clusterId=${clusterId}`) - export const getNodeCapacity = (clusterId: string, nodeName: string): Promise => get(`${Routes.NODE_CAPACITY}?clusterId=${clusterId}&name=${nodeName}`) diff --git a/src/components/ClusterNodes/constants.ts b/src/components/ClusterNodes/constants.ts index 8a0083072f..5affa5c795 100644 --- a/src/components/ClusterNodes/constants.ts +++ b/src/components/ClusterNodes/constants.ts @@ -17,7 +17,7 @@ import { ClusterFiltersType, ClusterStatusType } from '@devtron-labs/devtron-fe-common-lib' import { multiSelectStyles } from '../v2/common/ReactSelectCustomization' -import { ColumnMetadataType, EFFECT_TYPE } from './types' +import { EFFECT_TYPE } from './types' export const clusterSelectStyle = { ...multiSelectStyles, @@ -62,119 +62,6 @@ export const CLUSTER_STATUS = { POD_TERMINATED: 'pod-terminated', } -export const COLUMN_METADATA: ColumnMetadataType[] = [ - { - sortType: 'string', - columnIndex: 0, - label: 'Node', - value: 'name', - isDefault: true, - isSortingAllowed: true, - isDisabled: true, - sortingFieldName: 'name', - }, - { sortType: 'string', columnIndex: 1, label: 'Status', value: 'status', isDefault: true, isDisabled: true }, - { sortType: 'string', columnIndex: 2, label: 'Roles', value: 'roles', isDefault: true }, - { - sortType: 'number', - columnIndex: 3, - label: 'Errors', - value: 'errorCount', - isDefault: true, - isDisabled: true, - isSortingAllowed: true, - sortingFieldName: 'errorCount', - }, - { sortType: 'string', columnIndex: 4, label: 'K8S Version', value: 'k8sVersion', isDefault: true }, - { - sortType: 'string', - columnIndex: 5, - label: 'Node Group', - value: 'nodeGroup', - isSortingAllowed: true, - isDefault: true, - sortingFieldName: 'nodeGroup', - }, - { - sortType: 'number', - columnIndex: 6, - label: 'No.of pods', - value: 'podCount', - isDefault: true, - isSortingAllowed: true, - sortingFieldName: 'podCount', - }, - { - sortType: 'number', - columnIndex: 7, - label: 'Taints', - value: 'taintCount', - isDefault: true, - isSortingAllowed: true, - sortingFieldName: 'taintCount', - }, - { - sortType: 'number', - columnIndex: 8, - label: 'CPU Usage (%)', - value: 'cpu.usagePercentage', - isDefault: true, - isSortingAllowed: true, - sortingFieldName: 'cpu.usagePercentage', - }, - { - sortType: 'number', - columnIndex: 9, - label: 'CPU Usage (Absolute)', - value: 'cpu.usage', - isSortingAllowed: true, - sortingFieldName: 'cpu.usageInBytes', - }, - { - sortType: 'number', - columnIndex: 10, - label: 'CPU Allocatable', - value: 'cpu.allocatable', - isSortingAllowed: true, - sortingFieldName: 'cpu.allocatableInBytes', - }, - { - sortType: 'number', - columnIndex: 11, - label: 'Mem Usage (%)', - value: 'memory.usagePercentage', - isDefault: true, - isSortingAllowed: true, - sortingFieldName: 'memory.usagePercentage', - }, - { - sortType: 'number', - columnIndex: 12, - label: 'Mem Usage (Absolute)', - value: 'memory.usage', - isSortingAllowed: true, - sortingFieldName: 'memory.usageInBytes', - }, - { - sortType: 'number', - columnIndex: 13, - label: 'Mem Allocatable', - value: 'memory.allocatable', - isSortingAllowed: true, - sortingFieldName: 'memory.allocatableInBytes', - }, - { - sortType: 'string', - columnIndex: 14, - label: 'Age', - value: 'age', - isDefault: true, - isSortingAllowed: true, - sortingFieldName: 'createdAt', - }, - { sortType: 'boolean', columnIndex: 15, label: 'Unschedulable', value: 'unschedulable' }, -] - export const NODE_DETAILS_TABS = { summary: 'Summary', yaml: 'YAML', @@ -322,13 +209,6 @@ export const SELECT_TITLE = { export const AUTO_SELECT = { label: 'Auto select', value: 'autoSelectNode' } -export const NODE_SEARCH_TEXT = { - NAME: 'name', - LABEL: 'label', - LABELS: 'labels', - NODE_GROUP: 'nodeGroup', -} as const - export const clusterImageSelect = { ...clusterSelectStyle, menu: (base, state) => ({ @@ -344,19 +224,6 @@ export const clusterImageSelect = { }), } -export const SEARCH_OPTION_LABEL = { - NAME: NODE_SEARCH_TEXT.NAME, - LABEL: NODE_SEARCH_TEXT.LABEL, - NODE_GROUP: NODE_SEARCH_TEXT.NODE_GROUP, - NODE_GROUP_TEXT: 'node group', -} - -export const NodeSearchOption = [ - { value: 1, label: SEARCH_OPTION_LABEL.NAME, type: 'main' }, - { value: 2, label: SEARCH_OPTION_LABEL.LABEL, type: 'main' }, - { value: 3, label: SEARCH_OPTION_LABEL.NODE_GROUP, type: 'main' }, -] - export const nodeSelect = { ...clusterSelectStyle, group: (base) => ({ diff --git a/src/components/ClusterNodes/types.ts b/src/components/ClusterNodes/types.ts index 16630771fe..bc1b4aee12 100644 --- a/src/components/ClusterNodes/types.ts +++ b/src/components/ClusterNodes/types.ts @@ -14,11 +14,9 @@ * limitations under the License. */ -import React from 'react' -import { MultiValue } from 'react-select' import { ResponseType, ClusterStatusType, K8sResourceDetailDataType } from '@devtron-labs/devtron-fe-common-lib' import { LabelTag, OptionType } from '../app/types' -import { CLUSTER_PAGE_TAB, NODE_SEARCH_TEXT } from './constants' +import { CLUSTER_PAGE_TAB } from './constants' import { EditModeType } from '../v2/appDetails/k8Resource/nodeDetail/NodeDetailTabs/terminal/constants' import { ClusterOptionType, K8SResourceListType } from '../ResourceBrowser/Types' import { useTabs } from '../common/DynamicTabs' @@ -33,20 +31,7 @@ export enum EFFECT_TYPE { PreferNoSchedule = 'PreferNoSchedule', NoExecute = 'NoExecute', } -export interface NodeListSearchFliterType { - defaultVersion: OptionType - nodeK8sVersions: string[] - selectedVersion: OptionType - setSelectedVersion: React.Dispatch> - appliedColumns: MultiValue - setAppliedColumns: React.Dispatch>> - selectedSearchTextType: string - setSelectedSearchTextType: React.Dispatch> - searchText: string - setSearchText: React.Dispatch> - searchedTextMap: Map - setSearchedTextMap: React.Dispatch>> -} + export interface ResourceDetail { name: string capacity: string @@ -102,19 +87,6 @@ export interface ClusterNoteType { updatedOn: string } -export interface NodeRowDetail { - name: string - status: string - roles: string[] - errors: Record[] - k8sVersion: string - podCount: number - taintCount: number - cpu: ResourceDetail - memory: ResourceDetail - age: string -} - export interface ClusterListResponse extends ResponseType { result?: ClusterDetail[] } @@ -131,10 +103,6 @@ export interface ClusterCapacityResponse extends ResponseType { result?: ClusterCapacityType } -export interface NodeListResponse extends ResponseType { - result?: NodeRowDetail[] -} - export interface PodType extends K8sResourceDetailDataType { name: string namespace: string @@ -240,16 +208,6 @@ export interface TaintErrorObj { value: ErrorObj }[] } -interface NodeDataPropType { - nodeData: NodeDetail - getNodeListData: () => void -} - -export interface NodeActionsMenuProps extends NodeDataPropType { - openTerminal: (clusterData: NodeDetail) => void - isSuperAdmin: boolean - addTab: ReturnType['addTab'] -} export interface NodeActionRequest { clusterId?: number @@ -389,5 +347,3 @@ export interface ClusterOverviewProps { selectedCluster: ClusterOptionType addTab: ReturnType['addTab'] } - -export type SearchTextType = (typeof NODE_SEARCH_TEXT)[keyof typeof NODE_SEARCH_TEXT] diff --git a/src/components/ResourceBrowser/Constants.ts b/src/components/ResourceBrowser/Constants.ts index a3c245bf3e..e568da5425 100644 --- a/src/components/ResourceBrowser/Constants.ts +++ b/src/components/ResourceBrowser/Constants.ts @@ -18,7 +18,7 @@ import { Nodes } from '@devtron-labs/devtron-fe-common-lib' import ICArrowUpCircle from '@Icons/ic-arrow-up-circle.svg' import { AggregationKeys, AggregationKeysType } from '../app/types' import { multiSelectStyles } from '../v2/common/ReactSelectCustomization' -import { RBSidebarKeysType } from './Types' +import { RBSidebarKeysType, NODE_SEARCH_KEYS } from './Types' export const FILTER_SELECT_COMMON_STYLES = { ...multiSelectStyles, @@ -281,3 +281,83 @@ export const CONNECTION_TIMEOUT_TIME = 10000 export const DEFAULT_K8SLIST_PAGE_SIZE = 100 export const TARGET_K8S_VERSION_SEARCH_KEY = 'targetK8sVersion' + +export const NODE_LIST_HEADERS = [ + 'name', + 'status', + 'roles', + 'errors', + 'k8s version', + 'node group', + 'pods', + 'taints', + 'cpu usage (%)', + 'cpu usage (absolute)', + 'cpu allocatable', + 'mem usage (%)', + 'mem usage (absolute)', + 'mem allocatable', + 'age', + 'unschedulable', +] as const + +export const MANDATORY_NODE_LIST_HEADERS: Array<(typeof NODE_LIST_HEADERS)[number]> = [ + 'name', + 'status', + 'errors', +] as const + +export const OPTIONAL_NODE_LIST_HEADERS = NODE_LIST_HEADERS.filter( + (header) => !MANDATORY_NODE_LIST_HEADERS.includes(header), +) + +export const NODE_LIST_HEADERS_TO_KEY_MAP: Record<(typeof NODE_LIST_HEADERS)[number], string> = { + name: 'name', + status: 'status', + roles: 'roles', + errors: 'errorCount', + 'k8s version': 'k8sVersion', + 'node group': 'nodeGroup', + pods: 'podCount', + taints: 'taintCount', + 'cpu usage (%)': 'cpu.usagePercentage', + 'cpu usage (absolute)': 'cpu.usage', + 'cpu allocatable': 'cpu.allocatable', + 'mem usage (%)': 'memory.usagePercentage', + 'mem usage (absolute)': 'memory.usageInBytes', + 'mem allocatable': 'memory.allocatable', + age: 'age', + unschedulable: 'unschedulable', +} as const + +export const NODE_SEARCH_KEY_OPTIONS = [ + { value: NODE_SEARCH_KEYS.NAME, label: 'Name' }, + { value: NODE_SEARCH_KEYS.LABEL, label: 'Label' }, + { value: NODE_SEARCH_KEYS.NODE_GROUP, label: 'Node group' }, +] as const + +export const DEFAULT_NODE_K8S_VERSION = { + label: 'K8s version: Any', + value: 'K8s version: Any', +} + +export const NODE_SEARCH_KEY_PLACEHOLDER: Record = { + [NODE_SEARCH_KEYS.NAME]: 'Search by node name Eg. ip-172-31-2-152.us-east-2.compute.internal', + [NODE_SEARCH_KEYS.LABEL]: 'Search by key=value Eg. environment=production, tier=frontend', + [NODE_SEARCH_KEYS.NODE_GROUP]: 'Search by node group name Eg. mainnode', +} + +export const NODE_SEARCH_KEYS_TO_OBJECT_KEYS: Record< + NODE_SEARCH_KEYS, + (typeof NODE_LIST_HEADERS_TO_KEY_MAP)[keyof typeof NODE_LIST_HEADERS_TO_KEY_MAP] +> = { + [NODE_SEARCH_KEYS.LABEL]: 'labels', + [NODE_SEARCH_KEYS.NAME]: 'name', + [NODE_SEARCH_KEYS.NODE_GROUP]: 'nodeGroup', +} + +export const LOCAL_STORAGE_EXISTS = !!(Storage && localStorage) + +export const LOCAL_STORAGE_KEY_FOR_APPLIED_COLUMNS = 'appliedColumns' + +export const NODE_K8S_VERSION_FILTER_KEY = 'k8sVersion' diff --git a/src/components/ResourceBrowser/ResourceBrowser.scss b/src/components/ResourceBrowser/ResourceBrowser.scss index a5aa3e699d..058eb374aa 100644 --- a/src/components/ResourceBrowser/ResourceBrowser.scss +++ b/src/components/ResourceBrowser/ResourceBrowser.scss @@ -98,6 +98,17 @@ } .resource-list-container { + .node-listing-search-container { + display: grid; + grid-template-columns: auto 160px 1px 180px; + column-gap: 8px; + + &__shortcut-key { + right: 10px; + top: 6px; + } + } + &.no-result-container { height: calc(100vh - 92px); overflow: hidden; @@ -113,6 +124,7 @@ } } } + .scrollable-resource-list { height: calc(100vh - 144px); &.paginated-list-view { diff --git a/src/components/ResourceBrowser/ResourceBrowser.service.tsx b/src/components/ResourceBrowser/ResourceBrowser.service.tsx index 8e1f281415..09b787e010 100644 --- a/src/components/ResourceBrowser/ResourceBrowser.service.tsx +++ b/src/components/ResourceBrowser/ResourceBrowser.service.tsx @@ -31,7 +31,7 @@ import { JSONPath } from 'jsonpath-plus' import { SelectedResourceType } from '@Components/v2/appDetails/appDetails.type' import { Routes } from '../../config' import { ClusterListResponse } from '../../services/service.types' -import { CreateResourcePayload, CreateResourceResponse, ResourceListPayloadType } from './Types' +import { CreateResourcePayload, CreateResourceResponse, NodeListResponse, ResourceListPayloadType } from './Types' import { ALL_NAMESPACE_OPTION } from './Constants' export const getClusterList = (): Promise => get(Routes.CLUSTER_LIST_PERMISSION) @@ -140,3 +140,6 @@ export const restartWorkload = async (resource: SelectedResourceType, signal: Ab await updateManifestResourceHelmApps(null, '', '', JSON.stringify(manifest), true, resource, signal) } + +export const getNodeList = (clusterId: string, signal?: AbortSignal): Promise => + get(`${Routes.NODE_LIST}?clusterId=${clusterId}`, { signal }) diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index 70d1f9999b..d8d70ef293 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -31,11 +31,14 @@ import { useEffect, useMemo, useRef, useState } from 'react' import WebWorker from '@Components/app/WebWorker' import searchWorker from '@Config/searchWorker' import { URLS } from '@Config/routes' +import { deleteNodeCapacity } from '@Components/ClusterNodes/clusterNodes.service' +import NodeActionsMenu from '@Components/ResourceBrowser/ResourceList/NodeActionsMenu' import ResourceListEmptyState from './ResourceListEmptyState' import { ALL_NAMESPACE_OPTION, DEFAULT_K8SLIST_PAGE_SIZE, K8S_EMPTY_GROUP, + NODE_LIST_HEADERS_TO_KEY_MAP, RESOURCE_EMPTY_PAGE_STATE, RESOURCE_LIST_EMPTY_STATE, RESOURCE_PAGE_SIZE_OPTIONS, @@ -50,6 +53,8 @@ import { EventList } from './EventList' import ResourceFilterOptions from './ResourceFilterOptions' import { BaseResourceListProps } from './types' import { deleteResource, restartWorkload } from '../ResourceBrowser.service' +import { getAppliedColumnsFromLocalStorage } from './utils' +import NodeListSearchFilter from './NodeListSearchFilter' const PodRestartIcon = importComponentFromFELibrary('PodRestartIcon') const RBBulkSelectionActionWidget = importComponentFromFELibrary('RBBulkSelectionActionWidget', null, 'function') @@ -83,12 +88,19 @@ const BaseResourceListContent = ({ setWidgetEventDetails, lowercaseKindToResourceGroupMap, handleResourceClick: onResourceClick, + nodeK8sVersions, }: BaseResourceListProps) => { const [filteredResourceList, setFilteredResourceList] = useState(null) const [pageSize, setPageSize] = useState(DEFAULT_K8SLIST_PAGE_SIZE) const [resourceListOffset, setResourceListOffset] = useState(0) const [bulkOperationModalState, setBulkOperationModalState] = useState('closed') + // NOTE: this is to re-mount node filters component & avoid useEffects inside it + const [lastTimeStringSinceClearAllFilters, setLastTimeStringSinceClearAllFilters] = useState(null) + + // NOTE: this is only being used for node listing currently + const [visibleColumns, setVisibleColumns] = useState(getAppliedColumnsFromLocalStorage()) + const searchWorkerRef = useRef(null) const resourceListRef = useRef(null) const parentRef = useRef(null) @@ -99,6 +111,8 @@ const BaseResourceListContent = ({ const { searchParams } = useSearchString() + const isNodeListing = selectedResource.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind + const { selectedIdentifiers: bulkSelectionState, handleBulkSelection, @@ -107,8 +121,18 @@ const BaseResourceListContent = ({ getSelectedIdentifiersCount, } = useBulkSelection>() + const headers = useMemo(() => { + if (!isNodeListing) { + return resourceList?.headers ?? [] + } + + const visibleColumnsSet = new Set(visibleColumns) + + return resourceList?.headers.filter((header) => visibleColumnsSet.has(header)) ?? [] + }, [resourceList, visibleColumns, isNodeListing]) + const { gridTemplateColumns, handleResize } = useResizableTableConfig({ - headersConfig: (resourceList?.headers ?? []).map((columnName, index) => ({ + headersConfig: headers.map((columnName, index) => ({ id: columnName, minWidth: index === 0 ? 120 : null, width: index === 0 ? 350 : 180, @@ -169,6 +193,10 @@ const BaseResourceListContent = ({ sortBy, sortOrder, debounceResult, + nodeListingFilters: { + isNodeListing, + searchParams, + }, origin: new URL(window.__BASE_URL__, window.location.href).origin, }, }) @@ -207,6 +235,10 @@ const BaseResourceListContent = ({ }, [nodeType]) useEffect(() => { + if (!isOpen) { + return + } + if (!resourceList) { setFilteredResourceList(null) return @@ -214,7 +246,7 @@ const BaseResourceListContent = ({ handleFilterChanges(searchText) setResourceListOffset(0) - }, [resourceList, sortBy, sortOrder]) + }, [resourceList, sortBy, sortOrder, location.search]) const getHandleCheckedForId = (resourceData: K8sResourceDetailDataType) => () => { const id = Number(resourceData.id) @@ -270,9 +302,7 @@ const BaseResourceListContent = ({ if (bulkOperationModalState === 'restart') { return selections?.map((selection) => ({ - id: selection.id, name: selection.name as string, - namespace: (selection.namespace as string) ?? ALL_NAMESPACE_OPTION.value, operation: async (signal: AbortSignal = null) => { const payload = { clusterId: Number(clusterId), @@ -290,10 +320,21 @@ const BaseResourceListContent = ({ } return selections.map((selection) => ({ - id: selection.id, name: selection.name as string, - namespace: (selection.namespace as string) ?? ALL_NAMESPACE_OPTION.value, operation: async (signal: AbortSignal, shouldForceDelete: boolean) => { + if (isNodeListing) { + const nodeDeletePayload = { + clusterId: Number(clusterId), + name: String(selection.name), + version: String(selection.version), + kind: String(selection.kind), + } + + await deleteNodeCapacity(nodeDeletePayload, signal) + + return + } + const resourceDeletePayload: ResourceListPayloadType = { clusterId: Number(clusterId), k8sRequest: { @@ -330,6 +371,7 @@ const BaseResourceListContent = ({ setFilteredResourceList(resourceList?.data ?? null) setResourceListOffset(0) setSelectedNamespace(ALL_NAMESPACE_OPTION) + setLastTimeStringSinceClearAllFilters(new Date().toISOString()) } const getStatusClass = (status: string) => { @@ -366,7 +408,7 @@ const BaseResourceListContent = ({ className="scrollable-resource-list__row fw-4 cn-9 fs-13 dc__border-bottom-n1 hover-class h-44 dc__gap-16 dc__visible-hover dc__hover-n50" style={{ gridTemplateColumns }} > - {resourceList?.headers.map((columnName) => + {headers.map((columnName) => columnName === 'name' ? (
- {shouldShowRedirectionAndActions && ( - Promise} - selectedResource={{ - ...selectedResource, - ...(shouldOverrideSelectedResourceKind && { - gvk: { - Group: gvkFromRawData.Group ?? selectedResource.gvk.Group, - Kind: gvkFromRawData.Kind ?? selectedResource.gvk.Kind, - Version: gvkFromRawData.Version ?? selectedResource.gvk.Version, - } as GVKType, - }), - }} - handleResourceClick={handleResourceClick} - hideDeleteResource={hideDeleteResource} - /> - )} + {shouldShowRedirectionAndActions && + (!isNodeListing ? ( + Promise} + selectedResource={{ + ...selectedResource, + ...(shouldOverrideSelectedResourceKind && { + gvk: { + Group: gvkFromRawData.Group ?? selectedResource.gvk.Group, + Kind: gvkFromRawData.Kind ?? selectedResource.gvk.Kind, + Version: gvkFromRawData.Version ?? selectedResource.gvk.Version, + } as GVKType, + }), + }} + handleResourceClick={handleResourceClick} + hideDeleteResource={hideDeleteResource} + /> + ) : ( + Promise} + addTab={addTab} + nodeData={resourceData} + /> + ))}
) : (
- {resourceList?.headers.map((columnName, index) => ( + {headers.map((columnName, index) => (
{!hideBulkSelection && index === 0 && ( @@ -584,22 +639,33 @@ const BaseResourceListContent = ({ }`} ref={parentRef} > - + {isNodeListing ? ( + + ) : ( + + )} {renderContent()} {children} {!hideBulkSelection && ( diff --git a/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx b/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx new file mode 100644 index 0000000000..52f85d5182 --- /dev/null +++ b/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx @@ -0,0 +1,191 @@ +/* + * 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 React, { useState, useMemo, useRef } from 'react' +import ReactSelect, { components, ValueContainerProps, MenuListProps } from 'react-select' +import { Option } from '@devtron-labs/devtron-fe-common-lib' +import { ReactComponent as Setting } from '@Icons/ic-nav-gear.svg' +import { containerImageSelectStyles } from '../../CIPipelineN/ciPipeline.utils' +import { OPTIONAL_NODE_LIST_HEADERS } from '../Constants' +import { ColumnFilterContextType, ColumnSelectorType } from '../Types' +import { saveAppliedColumnsInLocalStorage } from './utils' + +const ColumnFilterContext = React.createContext(null) + +export function useColumnFilterContext() { + const context = React.useContext(ColumnFilterContext) + + if (!context) { + throw new Error(`cannot be rendered outside the component`) + } + + return context +} + +const ValueContainer = (props: ValueContainerProps) => { + const { getValue, selectProps, children } = props + const { length } = getValue() + + return ( + + {length > 0 ? ( + <> + {!selectProps.menuIsOpen && ( + <> + + Columns + + )} + {React.cloneElement(children[1])} + + ) : ( + children + )} + + ) +} + +const MenuList = (props: MenuListProps) => { + const { selectedColumns, setVisibleColumns, setIsMenuOpen, selectRef } = useColumnFilterContext() + + const { children } = props + + const handleApplySelectedColumns = (): void => { + setIsMenuOpen(false) + + const newVisibleColumns = selectedColumns.map((option) => option.value) + + if (typeof Storage !== 'undefined') { + saveAppliedColumnsInLocalStorage(newVisibleColumns) + } + + selectRef.current?.blur() + + setVisibleColumns(newVisibleColumns) + } + + return ( + + {children} + +
+ +
+
+ ) +} + +const ColumnSelector = ({ setVisibleColumns, visibleColumns }: ColumnSelectorType) => { + const columnOptions = useMemo( + () => + OPTIONAL_NODE_LIST_HEADERS.map((header) => ({ + value: header, + label: header, + })), + [], + ) + + const [isMenuOpen, setIsMenuOpen] = useState(false) + const [selectedColumns, setSelectedColumns] = useState( + visibleColumns.map((column) => ({ value: column, label: column })), + ) + + const selectRef: ColumnFilterContextType['selectRef'] = useRef() + + const handleMenuOpen = () => { + setIsMenuOpen(true) + } + + const handleMenuClose = () => { + setIsMenuOpen(false) + + selectRef.current?.blur() + } + + const columnFilterProviderValue: ColumnFilterContextType = useMemo( + () => ({ + isMenuOpen, + setIsMenuOpen, + selectedColumns, + setSelectedColumns, + selectRef, + setVisibleColumns, + }), + [isMenuOpen, selectedColumns], + ) + + return ( + + ({ + ...base, + zIndex: 6, + }), + menuList: (base) => ({ + ...base, + borderRadius: '4px', + paddingTop: 0, + paddingBottom: 0, + }), + option: (base, state) => ({ + ...base, + padding: '10px 0', + backgroundColor: state.isFocused ? 'var(--N100) !important' : 'var(--N0) !important', + color: 'var(--N900)', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + cursor: 'pointer', + }), + dropdownIndicator: (base, state) => ({ + ...base, + color: 'var(--N400)', + transition: 'all .2s ease', + transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : 'rotate(0deg)', + padding: '0 8px', + }), + }} + /> + + ) +} + +export default ColumnSelector diff --git a/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx b/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx index 691334dd70..145025d757 100644 --- a/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx @@ -25,11 +25,12 @@ import { import { useMemo, useRef, useState } from 'react' import { useParams, useLocation } from 'react-router-dom' import { getPodRestartRBACPayload } from '@Components/v2/appDetails/k8Resource/nodeDetail/nodeDetail.api' +import { getClusterCapacity } from '@Components/ClusterNodes/clusterNodes.service' import { importComponentFromFELibrary } from '../../common/helpers/Helpers' import { ALL_NAMESPACE_OPTION, SIDEBAR_KEYS } from '../Constants' -import { getResourceListPayload } from '../ResourceBrowser.service' +import { getNodeList, getResourceListPayload } from '../ResourceBrowser.service' import { K8SResourceListType, URLParams } from '../Types' -import { sortEventListData, removeDefaultForStorageClass } from '../Utils' +import { sortEventListData, removeDefaultForStorageClass, parseNodeList } from '../Utils' import BaseResourceList from './BaseResourceList' const PodRestart = importComponentFromFELibrary('PodRestart') @@ -68,9 +69,15 @@ export const K8SResourceList = ({ const [resourceListLoader, _resourceList, _resourceListDataError, reloadResourceListData] = useAsync( () => - abortPreviousRequests( - () => - getK8sResourceList( + abortPreviousRequests(async () => { + try { + if (selectedResource.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind) { + const response = await getNodeList(clusterId, abortControllerRef.current.signal) + + return parseNodeList(response) + } + + return await getK8sResourceList( getResourceListPayload( clusterId, selectedNamespace.value.toLowerCase(), @@ -78,18 +85,35 @@ export const K8SResourceList = ({ filters, ), abortControllerRef.current.signal, - ).catch((err) => { - if (!getIsRequestAborted(err)) { - showError(err) - throw err - } - }), - abortControllerRef, - ), + ) + } catch (err) { + if (!getIsRequestAborted(err)) { + showError(err) + throw err + } + + return null + } + }, abortControllerRef), [selectedResource, clusterId, selectedNamespace, filters], - selectedResource && selectedResource.gvk.Kind !== SIDEBAR_KEYS.nodeGVK.Kind, ) + const [, nodeK8sVersions] = useAsync(async () => { + if (selectedResource.gvk.Kind !== SIDEBAR_KEYS.nodeGVK.Kind) { + return [] + } + + try { + const { + result: { nodeK8sVersions: versions }, + } = await getClusterCapacity(clusterId) + + return versions + } catch { + return [] + } + }, [selectedResource, clusterId]) + const resourceListDataError = getIsRequestAborted(_resourceListDataError) ? null : _resourceListDataError const resourceList = useMemo(() => { @@ -130,9 +154,11 @@ export const K8SResourceList = ({ nodeType={nodeType} group={group} addTab={addTab} + hideBulkSelection={!getFilterOptionsFromSearchParams} // NOTE: hideBulkSelection if fe-lib not linked setWidgetEventDetails={setWidgetEventDetails} lowercaseKindToResourceGroupMap={lowercaseKindToResourceGroupMap} handleResourceClick={handleResourceClick} + nodeK8sVersions={nodeK8sVersions ?? []} > {PodRestart && } diff --git a/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx b/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx index 470d7938a4..b7fcda3d61 100644 --- a/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx +++ b/src/components/ResourceBrowser/ResourceList/K8SResourceTabComponent.tsx @@ -16,26 +16,18 @@ import { useRef } from 'react' import { useParams } from 'react-router-dom' -import { - useAsync, - abortPreviousRequests, - BulkSelectionProvider, - SelectAllDialogStatus, -} from '@devtron-labs/devtron-fe-common-lib' +import { useAsync, abortPreviousRequests } from '@devtron-labs/devtron-fe-common-lib' import { K8SResourceTabComponentProps, URLParams } from '../Types' import { getResourceGroupList } from '../ResourceBrowser.service' -import { SIDEBAR_KEYS } from '../Constants' import Sidebar from './Sidebar' import { K8SResourceList } from './K8SResourceList' import ConnectingToClusterState from './ConnectingToClusterState' -import NodeDetailsList from '../../ClusterNodes/NodeDetailsList' const K8SResourceTabComponent = ({ selectedResource, setSelectedResource, selectedCluster, renderRefreshBar, - isSuperAdmin, addTab, isOpen, showStaleDataWarning, @@ -83,37 +75,19 @@ const K8SResourceTabComponent = ({ updateK8sResourceTab={updateK8sResourceTab} updateK8sResourceTabLastSyncMoment={updateK8sResourceTabLastSyncMoment} /> - {/* NOTE: if we directly use nodeType for this check - * component will mount/dismount on every tab change */} - {selectedResource?.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind ? ( - SelectAllDialogStatus.CLOSED} - > - - - ) : ( - - )} +
) } diff --git a/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx similarity index 75% rename from src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx rename to src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx index dfddb32a98..2f902ea548 100644 --- a/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx +++ b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx @@ -14,9 +14,18 @@ * limitations under the License. */ -import React, { useState } from 'react' -import { useHistory, useRouteMatch } from 'react-router-dom' -import { PopupMenu, TOAST_ACCESS_DENIED, ToastManager, ToastVariantType } from '@devtron-labs/devtron-fe-common-lib' +import { useState } from 'react' +import { useHistory, useLocation, useRouteMatch } from 'react-router-dom' +import { + noop, + PopupMenu, + TOAST_ACCESS_DENIED, + ToastManager, + ToastVariantType, + useMainContext, +} from '@devtron-labs/devtron-fe-common-lib' +import { AppDetailsTabs } from '@Components/v2/appDetails/appDetails.store' +import { TaintType } from '@Components/ClusterNodes/types' import { ReactComponent as TerminalIcon } from '../../../assets/icons/ic-terminal-fill.svg' import { ReactComponent as CordonIcon } from '../../../assets/icons/ic-cordon.svg' import { ReactComponent as UncordonIcon } from '../../../assets/icons/ic-play-medium.svg' @@ -25,34 +34,34 @@ import { ReactComponent as EditTaintsIcon } from '../../../assets/icons/ic-spray import { ReactComponent as EditFileIcon } from '../../../assets/icons/ic-edit-lines.svg' import { ReactComponent as DeleteIcon } from '../../../assets/icons/ic-delete-interactive.svg' import { ReactComponent as MenuDots } from '../../../assets/icons/appstatus/ic-menu-dots.svg' -import { NodeActionsMenuProps } from '../types' -import CordonNodeModal from './CordonNodeModal' -import DrainNodeModal from './DrainNodeModal' -import DeleteNodeModal from './DeleteNodeModal' -import { CLUSTER_NODE_ACTIONS_LABELS } from '../constants' -import EditTaintsModal from './EditTaintsModal' -import { K8S_EMPTY_GROUP } from '../../ResourceBrowser/Constants' +import { NodeActionsMenuProps } from '../Types' +import CordonNodeModal from '../../ClusterNodes/NodeActions/CordonNodeModal' +import DrainNodeModal from '../../ClusterNodes/NodeActions/DrainNodeModal' +import DeleteNodeModal from '../../ClusterNodes/NodeActions/DeleteNodeModal' +import { CLUSTER_NODE_ACTIONS_LABELS } from '../../ClusterNodes/constants' +import EditTaintsModal from '../../ClusterNodes/NodeActions/EditTaintsModal' +import { K8S_EMPTY_GROUP } from '../Constants' // TODO: This should be commoned out with ResourceBrowserActionMenu to have consistent styling -export default function NodeActionsMenu({ - nodeData, - openTerminal, - getNodeListData, - isSuperAdmin, - addTab, -}: NodeActionsMenuProps) { +const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuProps) => { const history = useHistory() const { url } = useRouteMatch() + const location = useLocation() + const [showCordonNodeDialog, setCordonNodeDialog] = useState(false) const [showDrainNodeDialog, setDrainNodeDialog] = useState(false) const [showDeleteNodeDialog, setDeleteNodeDialog] = useState(false) const [showEditTaintNodeDialog, setEditTaintNodeDialog] = useState(false) + const { name, version, kind } = nodeData as Record + + const { isSuperAdmin } = useMainContext() + const isAuthorized = (): boolean => { if (!isSuperAdmin) { ToastManager.showToast({ variant: ToastVariantType.notAuthorized, - description: TOAST_ACCESS_DENIED.SUBTITLE + description: TOAST_ACCESS_DENIED.SUBTITLE, }) return false } @@ -61,14 +70,21 @@ export default function NodeActionsMenu({ const handleOpenTerminalAction = () => { if (isAuthorized()) { - openTerminal(nodeData) + const queryParams = new URLSearchParams(location.search) + queryParams.set('node', name) + const url = location.pathname + history.push( + `${url.split('/').slice(0, -2).join('/')}/${AppDetailsTabs.terminal}/${K8S_EMPTY_GROUP}?${queryParams.toString()}`, + ) } } const handleEditYamlAction = () => { if (isAuthorized()) { const _url = `${url.split('/').slice(0, -2).join('/')}/node/${K8S_EMPTY_GROUP}/${nodeData.name}?tab=yaml` - addTab(K8S_EMPTY_GROUP, 'node', nodeData.name, _url).then(() => history.push(_url)) + addTab(K8S_EMPTY_GROUP, 'node', name, _url) + .then(() => history.push(_url)) + .catch(noop) } } @@ -179,38 +195,30 @@ export default function NodeActionsMenu({ {showCordonNodeDialog && ( )} {showDrainNodeDialog && ( - + )} {showDeleteNodeDialog && ( - + )} {showEditTaintNodeDialog && ( )} ) } + +export default NodeActionsMenu diff --git a/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx new file mode 100644 index 0000000000..7bee0923dd --- /dev/null +++ b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx @@ -0,0 +1,305 @@ +/* + * 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 { useState, useEffect, KeyboardEvent, ChangeEvent, useMemo, useRef, RefCallback } from 'react' +import { useLocation, useHistory } from 'react-router-dom' +import { ParsedQuery, parse as parseQueryString, stringify as stringifyQueryString } from 'query-string' +import { OptionType, SelectPicker, useRegisterShortcut } from '@devtron-labs/devtron-fe-common-lib' +import { ReactComponent as ICSearch } from '@Icons/ic-search.svg' +import { ReactComponent as ICClear } from '@Icons/ic-error.svg' +import ColumnSelector from './ColumnSelector' +import { NODE_SEARCH_KEYS, NodeListSearchFilterType } from '../Types' +import { ShortcutKeyBadge } from '../../common/formFields/Widgets/Widgets' +import { + DEFAULT_NODE_K8S_VERSION, + NODE_K8S_VERSION_FILTER_KEY, + NODE_SEARCH_KEY_OPTIONS, + NODE_SEARCH_KEY_PLACEHOLDER, +} from '../Constants' + +const NodeListSearchFilter = ({ + nodeK8sVersions, + visibleColumns, + setVisibleColumns, + isOpen, + searchParams, +}: NodeListSearchFilterType) => { + const selectedSearchTextType: NODE_SEARCH_KEYS | '' = Object.values(NODE_SEARCH_KEYS).reduce((type, key) => { + if (searchParams[key]) { + return key + } + + return type + }, '') + + const selectedK8sNodeVersion = searchParams[NODE_K8S_VERSION_FILTER_KEY] ?? '' + + const [isSearchKeySelectorOpen, setIsSearchKeySelectorOpen] = useState(false) + const [searchTextType, setSearchTextType] = useState(selectedSearchTextType) + const [searchInputText, setSearchInputText] = useState( + selectedSearchTextType ? searchParams[selectedSearchTextType] : '', + ) + + const searchInputRef = useRef(null) + + const handleSearchInputMount: RefCallback = (node) => { + if (node) { + searchInputRef.current = node + + node.focus() + } + } + + const location = useLocation() + const { push } = useHistory() + + const nodeK8sVersionOptions = useMemo( + () => [ + DEFAULT_NODE_K8S_VERSION, + ...(nodeK8sVersions?.map((version) => ({ + label: `K8s version: ${version}`, + value: version, + })) || []), + ], + [nodeK8sVersions], + ) + + const selectedK8sVersionOption = useMemo( + () => + nodeK8sVersionOptions.find((option) => option.value === selectedK8sNodeVersion) ?? DEFAULT_NODE_K8S_VERSION, + [nodeK8sVersionOptions, selectedK8sNodeVersion], + ) + + const handleFocusInput = () => { + setIsSearchKeySelectorOpen(true) + searchInputRef.current?.focus() + } + + const handleBlurInput = () => { + setIsSearchKeySelectorOpen(false) + searchInputRef.current?.blur() + } + + const { registerShortcut, unregisterShortcut } = useRegisterShortcut() + + useEffect(() => { + if (registerShortcut && isOpen) { + registerShortcut({ keys: ['R'], callback: handleFocusInput }) + registerShortcut({ keys: ['Escape'], callback: handleBlurInput }) + } + + return (): void => { + unregisterShortcut(['R']) + unregisterShortcut(['Escape']) + } + }, [isOpen]) + + const handleQueryParamsUpdate = (callback: (queryObject: ParsedQuery) => ParsedQuery) => { + if (!callback) { + return + } + + const queryObject = parseQueryString(location.search) + + const finalQueryString = stringifyQueryString(callback(queryObject)) + + push(`?${finalQueryString}`) + } + + const handleQueryParamsSearch = (searchString: string) => { + handleQueryParamsUpdate((queryObject) => { + const finalQueryObject = structuredClone(queryObject) + + Object.values(NODE_SEARCH_KEYS).forEach((key) => { + if (key === searchTextType) { + finalQueryObject[key] = searchString + } else { + delete finalQueryObject[key] + } + }) + + return finalQueryObject + }) + } + + const handleSearchTextChange = (event: ChangeEvent): void => { + setSearchInputText(event.target.value) + } + + const handleClearTextFilters = (): void => { + setSearchInputText('') + + setSearchTextType('') + + handleQueryParamsUpdate((queryObject) => { + const finalQueryObject = structuredClone(queryObject) + + Object.values(NODE_SEARCH_KEYS).forEach((key) => { + delete finalQueryObject[key] + }) + + return finalQueryObject + }) + } + + const handleKeyDownOnSearchInput = (event: KeyboardEvent): void => { + const { key } = event + + if (key === 'Enter') { + handleQueryParamsSearch(searchInputText) + + setIsSearchKeySelectorOpen(false) + } + + if (key === 'Backspace' && searchInputText.length === 0 && searchTextType) { + setSearchTextType('') + + setIsSearchKeySelectorOpen(false) + } + + if (key === 'Escape') { + event.currentTarget.blur() + } + } + + const handleToggleIsSearchKeySelectorOpen = () => { + setIsSearchKeySelectorOpen((prev) => !prev) + } + + const getSelectSearchKeyTypeHandler = (key: NODE_SEARCH_KEYS) => () => { + setSearchTextType(key) + setSearchInputText('') + setIsSearchKeySelectorOpen(false) + } + + const handleApplyNodeK8sVersion = (option: OptionType) => { + handleQueryParamsUpdate((queryObject) => { + const finalQueryObject = structuredClone(queryObject) + + if (option.value === DEFAULT_NODE_K8S_VERSION.value) { + delete finalQueryObject[NODE_K8S_VERSION_FILTER_KEY] + } else { + finalQueryObject[NODE_K8S_VERSION_FILTER_KEY] = option.value + } + + return finalQueryObject + }) + } + + const handleOpenSearchKeySelectorMenu = () => { + setIsSearchKeySelectorOpen(true) + } + + const renderTextFilter = (): JSX.Element => { + const placeholderText = NODE_SEARCH_KEY_PLACEHOLDER[searchTextType] + + return ( +
+
+ + + {searchTextType ? ( + <> + + {NODE_SEARCH_KEY_OPTIONS.find((option) => option.value === searchTextType).label} +  : + + + + + ) : ( + Search nodes by name, labels or node group + )} + + {!searchTextType && ( + + )} +
+ + {isSearchKeySelectorOpen && ( + <> +
+ + {!searchTextType && ( +
+
Search by
+ + {NODE_SEARCH_KEY_OPTIONS.map((option) => ( +
+ {option.label} +
+ ))} +
+ )} + + )} + + {(searchTextType || searchInputText) && ( + + )} +
+ ) + } + + return ( +
+ {renderTextFilter()} + + + +
+ + +
+ ) +} + +export default NodeListSearchFilter diff --git a/src/components/ResourceBrowser/ResourceList/types.ts b/src/components/ResourceBrowser/ResourceList/types.ts index 564b022dcc..8427e776a2 100644 --- a/src/components/ResourceBrowser/ResourceList/types.ts +++ b/src/components/ResourceBrowser/ResourceList/types.ts @@ -45,6 +45,7 @@ export interface BaseResourceListProps * @default false */ shouldOverrideSelectedResourceKind?: boolean + nodeK8sVersions?: string[] } export interface ClusterUpgradeCompatibilityInfoProps diff --git a/src/components/ResourceBrowser/ResourceList/utils.tsx b/src/components/ResourceBrowser/ResourceList/utils.tsx index 724878c74b..a662a49fe1 100644 --- a/src/components/ResourceBrowser/ResourceList/utils.tsx +++ b/src/components/ResourceBrowser/ResourceList/utils.tsx @@ -1,5 +1,41 @@ -import { TARGET_K8S_VERSION_SEARCH_KEY } from '../Constants' +import { noop } from '@devtron-labs/devtron-fe-common-lib' +import { + NODE_LIST_HEADERS, + TARGET_K8S_VERSION_SEARCH_KEY, + LOCAL_STORAGE_EXISTS, + LOCAL_STORAGE_KEY_FOR_APPLIED_COLUMNS, +} from '../Constants' export const parseSearchParams = (searchParams: URLSearchParams) => ({ targetK8sVersion: searchParams.get(TARGET_K8S_VERSION_SEARCH_KEY), }) + +export const getAppliedColumnsFromLocalStorage = () => { + if (!LOCAL_STORAGE_EXISTS) { + return [...NODE_LIST_HEADERS] + } + + try { + const appliedColumns = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY_FOR_APPLIED_COLUMNS)) + + if (!Array.isArray(appliedColumns)) { + throw new Error('') + } + + return appliedColumns + } catch { + return [...NODE_LIST_HEADERS] + } +} + +export const saveAppliedColumnsInLocalStorage = (appliedColumns: string[]) => { + if (!LOCAL_STORAGE_EXISTS || !Array.isArray(appliedColumns)) { + return + } + + try { + localStorage.setItem(LOCAL_STORAGE_KEY_FOR_APPLIED_COLUMNS, JSON.stringify(appliedColumns)) + } catch { + noop() + } +} diff --git a/src/components/ResourceBrowser/Types.ts b/src/components/ResourceBrowser/Types.ts index ea63bb1b2d..9b2ffa3d48 100644 --- a/src/components/ResourceBrowser/Types.ts +++ b/src/components/ResourceBrowser/Types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import React from 'react' +import React, { RefObject } from 'react' import { K8SObjectBaseType, ResponseType, @@ -26,8 +26,9 @@ import { K8sResourceDetailType, K8sResourceDetailDataType, } from '@devtron-labs/devtron-fe-common-lib' +import { SelectInstance } from 'react-select' import { LogSearchTermType, SelectedResourceType } from '../v2/appDetails/appDetails.type' -import { ClusterDetail } from '../ClusterNodes/types' +import { ClusterDetail, ResourceDetail } from '../ClusterNodes/types' import { useTabs } from '../common/DynamicTabs' export interface K8SObjectType extends K8SObjectBaseType { @@ -285,3 +286,49 @@ export interface GetTabsBasedOnRoleParamsType { */ isMonitoringDashBoardSelected?: boolean } + +export interface NodeRowDetail { + name: string + status: string + roles: string[] + errors: Record[] + k8sVersion: string + podCount: number + taintCount: number + cpu: ResourceDetail + memory: ResourceDetail + age: string +} + +export interface NodeListResponse extends ResponseType { + result?: NodeRowDetail[] +} + +export interface NodeListSearchFilterType extends Pick { + nodeK8sVersions: string[] + visibleColumns: string[] + setVisibleColumns: React.Dispatch> + searchParams: Record +} + +export enum NODE_SEARCH_KEYS { + NAME = 'name', + LABEL = 'label', + NODE_GROUP = 'nodeGroup', +} + +export interface ColumnSelectorType extends Pick {} + +export interface ColumnFilterContextType extends Pick { + selectedColumns: OptionType[] + setSelectedColumns: React.Dispatch> + isMenuOpen: boolean + setIsMenuOpen: React.Dispatch> + selectRef: RefObject +} + +export interface NodeActionsMenuProps { + addTab: ReturnType['addTab'] + nodeData: K8sResourceDetailDataType + getNodeListData: () => void +} diff --git a/src/components/ResourceBrowser/Utils.tsx b/src/components/ResourceBrowser/Utils.tsx index 17766466cd..0b266be5a9 100644 --- a/src/components/ResourceBrowser/Utils.tsx +++ b/src/components/ResourceBrowser/Utils.tsx @@ -28,13 +28,20 @@ import moment from 'moment' import { URLS, LAST_SEEN } from '../../config' import { eventAgeComparator, importComponentFromFELibrary, processK8SObjects } from '../common' import { AppDetailsTabs, AppDetailsTabsIdPrefix } from '../v2/appDetails/appDetails.store' -import { JUMP_TO_KIND_SHORT_NAMES, K8S_EMPTY_GROUP, ORDERED_AGGREGATORS, SIDEBAR_KEYS } from './Constants' +import { + JUMP_TO_KIND_SHORT_NAMES, + K8S_EMPTY_GROUP, + NODE_LIST_HEADERS, + ORDERED_AGGREGATORS, + SIDEBAR_KEYS, +} from './Constants' import { GetTabsBasedOnRoleParamsType, K8SObjectChildMapType, K8SObjectMapType, K8SObjectType, K8sObjectOptionType, + NodeListResponse, } from './Types' import TerminalIcon from '../../assets/icons/ic-terminal-fill.svg' import K8ResourceIcon from '../../assets/icons/ic-object.svg' @@ -383,3 +390,42 @@ export const renderResourceValue = (value: string) => { return isDateValue ? moment(value).format(DATE_TIME_FORMAT_STRING) : value } + +const flattenObject = (ob: object): object => { + const toReturn = {} + + Object.keys(ob).forEach((i) => { + const currentElement = ob[i] + + if (typeof currentElement === 'object' && currentElement !== null && !Array.isArray(currentElement)) { + const flatObject = flattenObject(currentElement) + + Object.keys(flatObject).forEach((x) => { + toReturn[`${i}.${x}`] = flatObject[x] + }) + } else { + toReturn[i] = currentElement + } + }) + + return toReturn +} + +export const parseNodeList = (response: NodeListResponse) => ({ + result: { + headers: [...NODE_LIST_HEADERS] as string[], + data: response.result.map((data) => { + const _flattenNodeData = flattenObject(data) + const meta: Record = {} + + if (data.errors) { + meta.errorCount = String(Object.keys(data.errors).length || '') + } + + meta.taintCount = + Object.hasOwn(data, 'taints') && 'taints' in data ? String(Object.keys(data.taints).length || '') : '' + + return { ..._flattenNodeData, ...meta } + }), + }, +}) diff --git a/src/config/searchWorker.ts b/src/config/searchWorker.ts index 1c9860f06b..a01407ad40 100644 --- a/src/config/searchWorker.ts +++ b/src/config/searchWorker.ts @@ -21,6 +21,43 @@ export default () => { DESC = 'DESC', } + // NOTE!: this is a copy from ResourceBrowser/Types + // imports don't get bundled with the service worker + enum NODE_SEARCH_KEYS { + NAME = 'name', + LABEL = 'label', + NODE_GROUP = 'nodeGroup', + } + + // NOTE!: this is a copy from ResourceBrowser/Constants + // imports don't get bundled with the service worker + const NODE_SEARCH_KEYS_TO_OBJECT_KEYS = { + [NODE_SEARCH_KEYS.LABEL]: 'labels', + [NODE_SEARCH_KEYS.NAME]: 'name', + [NODE_SEARCH_KEYS.NODE_GROUP]: 'nodeGroup', + } + + const NODE_LIST_HEADERS_TO_KEY_MAP = { + name: 'name', + status: 'status', + roles: 'roles', + errors: 'errorCount', + 'k8s version': 'k8sVersion', + 'node group': 'nodeGroup', + pods: 'podCount', + taints: 'taintCount', + 'cpu usage (%)': 'cpu.usagePercentage', + 'cpu usage (absolute)': 'cpu.usage', + 'cpu allocatable': 'cpu.allocatable', + 'mem usage (%)': 'memory.usagePercentage', + 'mem usage (absolute)': 'memory.usageInBytes', + 'mem allocatable': 'memory.allocatable', + age: 'age', + unschedulable: 'unschedulable', + } + + const NODE_K8S_VERSION_KEY = 'k8sVersion' + const stringComparatorBySortOrder = (a: string, b: string, sortOrder: SortingOrder = SortingOrder.ASC) => sortOrder === SortingOrder.ASC ? a.localeCompare(b) : b.localeCompare(a) @@ -68,6 +105,14 @@ export default () => { ? k8sStyledAgeToSeconds(b) - k8sStyledAgeToSeconds(a) : k8sStyledAgeToSeconds(a) - k8sStyledAgeToSeconds(b) + const versionComparatorBySortOrder = (a: string, b: string, orderBy = SortingOrder.ASC) => { + if (orderBy === SortingOrder.DESC) { + return a?.localeCompare(b, undefined, { numeric: true }) ?? 1 + } + + return b?.localeCompare(a, undefined, { numeric: true }) ?? 1 + } + const propertyComparatorMap = { age: durationComparator, duration: durationComparator, @@ -76,6 +121,13 @@ export default () => { cpu: numberInStringComparator, memory: numberInStringComparator, window: durationComparator, + errors: numberInStringComparator, + 'k8s version': versionComparatorBySortOrder, + taints: numberInStringComparator, + 'cpu usage (%)': numberInStringComparator, + 'cpu allocatable': numberInStringComparator, + 'mem usage (%)': numberInStringComparator, + 'mem allocatable': numberInStringComparator, } /** @@ -85,10 +137,10 @@ export default () => { * @returns A sorting function. */ const dynamicSort = - (property: string, sortOrder: SortingOrder) => + (property: string, sortOrder: SortingOrder, isNodeListing: boolean) => (a: Record, b: Record) => { - const valueA = a[property] - const valueB = b[property] + const valueA = isNodeListing ? a[NODE_LIST_HEADERS_TO_KEY_MAP[property]] : a[property] + const valueB = isNodeListing ? b[NODE_LIST_HEADERS_TO_KEY_MAP[property]] : b[property] // Special cases handling where the property is not in sortable format. if (Object.keys(propertyComparatorMap).includes(property)) { @@ -130,16 +182,67 @@ export default () => { list, sortBy, sortOrder, + nodeListingFilters: { isNodeListing, searchParams }, }: { searchText: string list: unknown[] sortBy: string sortOrder: SortingOrder + nodeListingFilters: { + isNodeListing: boolean + searchParams: Record + } }) => { const searchTextLowerCased = searchText.trim().toLowerCase() let filteredList = [...list] - if (searchTextLowerCased !== '' && list?.length) { + if (isNodeListing) { + filteredList = list.filter((item: Record) => { + const isK8sVersionFilterAppliedAndMatchFound = + !searchParams[NODE_K8S_VERSION_KEY] || + item[NODE_K8S_VERSION_KEY] === searchParams[NODE_K8S_VERSION_KEY] + + const doesAnyNodeSearchKeyExists = Object.values(NODE_SEARCH_KEYS).some((key) => + Object.hasOwn(searchParams, key), + ) + + const doesItemHaveAnyMatchingSearchKey = Object.values(NODE_SEARCH_KEYS).reduce( + (isFound, searchKey) => { + if (!searchParams[searchKey]) { + return isFound + } + + const searchTextFromSearchKey = searchParams[searchKey] + + return !!searchTextFromSearchKey?.split(',').some((text) => { + const trimmedText = text.trim() + const objectKey = NODE_SEARCH_KEYS_TO_OBJECT_KEYS[searchKey] + + // NOTE: if corresponding value in data is anything other than primitives like string, or number + // handle it appropriately likewise + if (searchKey === NODE_SEARCH_KEYS.LABEL) { + const [searchKeyFromLabelText, searchValueFromLabelText] = trimmedText.split('=') + + return ( + !!item[objectKey]?.some( + ({ key, value }) => + key === searchKeyFromLabelText && value === searchValueFromLabelText, + ) && isFound + ) + } + + return String(item[objectKey] ?? '').includes(trimmedText) && isFound + }) + }, + true, + ) + + return ( + isK8sVersionFilterAppliedAndMatchFound && + (!doesAnyNodeSearchKeyExists || doesItemHaveAnyMatchingSearchKey) + ) + }) + } else if (searchTextLowerCased !== '' && list?.length) { filteredList = list.filter((item) => Object.entries(item).some( ([key, value]) => key !== 'id' && String(value).toLowerCase().includes(searchTextLowerCased), @@ -148,7 +251,7 @@ export default () => { } if (sortBy && sortOrder) { - filteredList.sort(dynamicSort(sortBy, sortOrder)) + filteredList.sort(dynamicSort(sortBy, sortOrder, isNodeListing)) } self.postMessage(filteredList) From 8bd324b0e20ecaf7aff9ef507e4d65af8feac51b Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 20 Nov 2024 11:32:03 +0530 Subject: [PATCH 09/52] chore: revert code --- .../Details/TriggerView/BulkCITrigger.tsx | 9 +++ .../details/triggerView/BranchRegexModal.tsx | 62 ++++++++++--------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx b/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx index e6ad119639..c2ac7c6224 100644 --- a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx +++ b/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx @@ -418,8 +418,17 @@ const BulkCITrigger = ({ handleRegexInputValue={handleRegexInputValueChange} regexValue={regexValue} onCloseBranchRegexModal={hideBranchEditModal} + hideHeaderFooter savingRegexValue={isLoading} /> +
+ + +
) } diff --git a/src/components/app/details/triggerView/BranchRegexModal.tsx b/src/components/app/details/triggerView/BranchRegexModal.tsx index cc90a78b50..df6535bd12 100644 --- a/src/components/app/details/triggerView/BranchRegexModal.tsx +++ b/src/components/app/details/triggerView/BranchRegexModal.tsx @@ -42,6 +42,7 @@ const BranchRegexModal = ({ handleRegexInputValue, regexValue, onCloseBranchRegexModal, + hideHeaderFooter, savingRegexValue, }: BranchRegexModalProps) => { const getBranchRegexName = (gitMaterialId: number) => { @@ -54,35 +55,40 @@ const BranchRegexModal = ({ return ciMaterial?.source?.regex || '' } - const renderBranchRegexMaterialHeader = () => ( -
-
- {isChangeBranchClicked && ( -
+
-
- ) + ) + } const renderRegexInfo = () => (
From 5a134adb4b34dfda1f99684d72a13af26111c17e Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 20 Nov 2024 11:35:19 +0530 Subject: [PATCH 10/52] chore: code formatting --- .../ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx b/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx index c2ac7c6224..03c5daefba 100644 --- a/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx +++ b/src/components/ApplicationGroup/Details/TriggerView/BulkCITrigger.tsx @@ -421,7 +421,7 @@ const BulkCITrigger = ({ hideHeaderFooter savingRegexValue={isLoading} /> -
+
From 0a3d19e970450fd5337ea06b6c8052d2daef776f Mon Sep 17 00:00:00 2001 From: Eshank Vaish <48060426+eshankvaish@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:16:09 +0530 Subject: [PATCH 11/52] chore: bump common lib --- package.json | 2 +- yarn.lock | 110 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2a67d22d53..68cf9fa545 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.0.9", + "@devtron-labs/devtron-fe-common-lib": "1.0.9-beta-2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index 99ad6ad0a8..a104db945d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9.tgz#0bff1f08ba59da0e1adbbad744e0b3fd8ab4d950" - integrity sha512-UtuG5iANoAoURcyuExq07dV4y/CyCai8RHZsh6PqBAjnCh0EKlmrc5JERWG0kwej+RYkmIDI98taYqgztpSSQg== +"@devtron-labs/devtron-fe-common-lib@1.0.9-beta-2": + version "1.0.9-beta-2" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9-beta-2.tgz#acf8ddf8169ab0f706431c28787f54603391cfb4" + integrity sha512-MQCxYF5p6rCahDdLqrTZR49mH2pwHR5iSAgMgzWXQTJH1St9P/YYGvr2g3V8KpHqK3+mPG5CXrvMCrage+47Tg== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" @@ -985,6 +985,7 @@ fast-json-patch "^3.1.1" jsonpath-plus "^10.0.0" react-dates "^21.8.0" + react-diff-viewer-continued "^3.4.0" react-monaco-editor "^0.54.0" sass "^1.69.7" tslib "2.7.0" @@ -1006,6 +1007,23 @@ source-map "^0.5.7" stylis "4.2.0" +"@emotion/babel-plugin@^11.13.5": + version "11.13.5" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0" + integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/serialize" "^1.3.3" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + "@emotion/cache@^11.11.0", "@emotion/cache@^11.4.0": version "11.11.0" resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz" @@ -1017,16 +1035,48 @@ "@emotion/weak-memoize" "^0.3.1" stylis "4.2.0" +"@emotion/cache@^11.13.5": + version "11.13.5" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.5.tgz#e78dad0489e1ed7572507ba8ed9d2130529e4266" + integrity sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g== + dependencies: + "@emotion/memoize" "^0.9.0" + "@emotion/sheet" "^1.4.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + stylis "4.2.0" + +"@emotion/css@^11.11.2": + version "11.13.5" + resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.13.5.tgz#db2d3be6780293640c082848e728a50544b9dfa4" + integrity sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w== + dependencies: + "@emotion/babel-plugin" "^11.13.5" + "@emotion/cache" "^11.13.5" + "@emotion/serialize" "^1.3.3" + "@emotion/sheet" "^1.4.0" + "@emotion/utils" "^1.4.2" + "@emotion/hash@^0.9.1": version "0.9.1" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== +"@emotion/hash@^0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" + integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== + "@emotion/memoize@^0.8.1": version "0.8.1" resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz" integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== +"@emotion/memoize@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" + integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== + "@emotion/react@^11.8.1": version "11.11.4" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz" @@ -1052,11 +1102,32 @@ "@emotion/utils" "^1.2.1" csstype "^3.0.2" +"@emotion/serialize@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8" + integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA== + dependencies: + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/unitless" "^0.10.0" + "@emotion/utils" "^1.4.2" + csstype "^3.0.2" + "@emotion/sheet@^1.2.2": version "1.2.2" resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== +"@emotion/sheet@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" + integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== + +"@emotion/unitless@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" + integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== + "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz" @@ -1072,11 +1143,21 @@ resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz" integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== +"@emotion/utils@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52" + integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA== + "@emotion/weak-memoize@^0.3.1": version "0.3.1" resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== +"@emotion/weak-memoize@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" + integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== + "@esbuild-plugins/node-globals-polyfill@0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz" @@ -3442,6 +3523,11 @@ ci-info@^3.2.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +classnames@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -3878,6 +3964,11 @@ diff@^4.0.1: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -6341,6 +6432,17 @@ react-dates@^21.8.0: react-with-styles "^4.1.0" react-with-styles-interface-css "^6.0.0" +react-diff-viewer-continued@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/react-diff-viewer-continued/-/react-diff-viewer-continued-3.4.0.tgz#0501ffb2b5ab740f88b9ae5f18771aa90d3803c2" + integrity sha512-kMZmUyb3Pv5L9vUtCfIGYsdOHs8mUojblGy1U1Sm0D7FhAOEsH9QhnngEIRo5hXWIPNGupNRJls1TJ6Eqx84eg== + dependencies: + "@emotion/css" "^11.11.2" + classnames "^2.3.2" + diff "^5.1.0" + memoize-one "^6.0.0" + prop-types "^15.8.1" + react-docgen-typescript@^2.2.2: version "2.2.2" resolved "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz" From 79dee60b307b4da88e3d17e59badbdd068269cad Mon Sep 17 00:00:00 2001 From: Eshank Vaish <48060426+eshankvaish@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:47:06 +0530 Subject: [PATCH 12/52] feat: update color for diff view --- src/components/app/details/cdDetails/cdDetail.scss | 8 -------- src/components/ciConfig/CIConfig.scss | 8 -------- src/css/base.scss | 8 ++++++++ src/css/colorPalette.scss | 3 --- src/css/triggerView.scss | 8 -------- 5 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/components/app/details/cdDetails/cdDetail.scss b/src/components/app/details/cdDetails/cdDetail.scss index 76a503a681..eec7b74da6 100644 --- a/src/components/app/details/cdDetails/cdDetail.scss +++ b/src/components/app/details/cdDetails/cdDetail.scss @@ -86,14 +86,6 @@ color: var(--N900) !important; } -.code-editor-green-diff { - background: #eaf1dd; -} - -.code-editor-red-diff { - background: #ffd4d1; -} - .left-50 { left: 50px; } diff --git a/src/components/ciConfig/CIConfig.scss b/src/components/ciConfig/CIConfig.scss index ab5a66c69f..e8e5823b53 100644 --- a/src/components/ciConfig/CIConfig.scss +++ b/src/components/ciConfig/CIConfig.scss @@ -211,14 +211,6 @@ background: white; overflow-wrap: break-word; table-layout: fixed; - - .code-editor-green-diff { - background: #eaf1dd; - } - - .code-editor-red-diff { - background: #ffd4d1; - } } } } diff --git a/src/css/base.scss b/src/css/base.scss index f46b5056e8..6868dd562e 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -5227,3 +5227,11 @@ textarea::placeholder { .dc__open-sans { font-family: 'Open Sans'; } + +.code-editor-green-diff { + background: var(--G100); +} + +.code-editor-red-diff { + background: var(--R100); +} diff --git a/src/css/colorPalette.scss b/src/css/colorPalette.scss index 7d874cf3d5..8120471045 100644 --- a/src/css/colorPalette.scss +++ b/src/css/colorPalette.scss @@ -75,9 +75,6 @@ --window-bg: #f2f4f7; --white: #fff; --gray: #1e23601a; - --code-editor-green: #eaf1dd; - --code-editor-red: #ffd4d1; - --code-diff-dot: #f37171; --tippy-card-black-bg: #2d3452; --terminal-bg: #0b0f22; --inprogress-orange: #ff7e5b; diff --git a/src/css/triggerView.scss b/src/css/triggerView.scss index 96ba6c874e..6c1eef73bd 100644 --- a/src/css/triggerView.scss +++ b/src/css/triggerView.scss @@ -692,11 +692,3 @@ .trigger-modal__config-diff-status svg.config-diff-found-icon path:nth-of-type(2) { fill: white; } - -.code-editor-green-diff { - background: var(--code-editor-green); -} - -.code-editor-red-diff { - background: var(--code-editor-red); -} From aa82331c284ad615e81449e5d7d4c996f93f6050 Mon Sep 17 00:00:00 2001 From: Eshank Vaish <48060426+eshankvaish@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:47:59 +0530 Subject: [PATCH 13/52] chore: bump common --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 68cf9fa545..e9c8594fdd 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.0.9-beta-2", + "@devtron-labs/devtron-fe-common-lib": "1.0.9-beta-3", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index a104db945d..f684149da2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.0.9-beta-2": - version "1.0.9-beta-2" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9-beta-2.tgz#acf8ddf8169ab0f706431c28787f54603391cfb4" - integrity sha512-MQCxYF5p6rCahDdLqrTZR49mH2pwHR5iSAgMgzWXQTJH1St9P/YYGvr2g3V8KpHqK3+mPG5CXrvMCrage+47Tg== +"@devtron-labs/devtron-fe-common-lib@1.0.9-beta-3": + version "1.0.9-beta-3" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9-beta-3.tgz#f2c7eba7808d9a4ad980800b479e5bc47f543a4a" + integrity sha512-CcG80qnxpbvb4NDrTRn/Y10lnVii/gJ//uC8fIi2jGM/U32ea+pUqEM7+g3TMPGtXAukBtTFtKsYwst8ZHyoFA== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" From e3237b4a5458cc5188225b3db2345dcf361dd770 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Wed, 20 Nov 2024 16:18:54 +0530 Subject: [PATCH 14/52] fix: check for old appliedColumns format --- src/components/ResourceBrowser/ResourceList/utils.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ResourceBrowser/ResourceList/utils.tsx b/src/components/ResourceBrowser/ResourceList/utils.tsx index adf46b9fdf..7b9f9d849f 100644 --- a/src/components/ResourceBrowser/ResourceList/utils.tsx +++ b/src/components/ResourceBrowser/ResourceList/utils.tsx @@ -19,8 +19,8 @@ export const getAppliedColumnsFromLocalStorage = () => { try { const appliedColumns = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY_FOR_APPLIED_COLUMNS)) - if (!Array.isArray(appliedColumns)) { - throw new Error('') + if (!Array.isArray(appliedColumns) || !appliedColumns.every((column) => typeof column === 'string')) { + throw new Error() } return appliedColumns From 1a5e4425137b8a92b5b81d3b279ce1c1aeb03047 Mon Sep 17 00:00:00 2001 From: Eshank Vaish <48060426+eshankvaish@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:37:57 +0530 Subject: [PATCH 15/52] chore: bump common lib --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e9c8594fdd..b82672ef6e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.0.9-beta-3", + "@devtron-labs/devtron-fe-common-lib": "1.0.9-beta-4", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index f684149da2..ec7a6c7cf3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.0.9-beta-3": - version "1.0.9-beta-3" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9-beta-3.tgz#f2c7eba7808d9a4ad980800b479e5bc47f543a4a" - integrity sha512-CcG80qnxpbvb4NDrTRn/Y10lnVii/gJ//uC8fIi2jGM/U32ea+pUqEM7+g3TMPGtXAukBtTFtKsYwst8ZHyoFA== +"@devtron-labs/devtron-fe-common-lib@1.0.9-beta-4": + version "1.0.9-beta-4" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9-beta-4.tgz#d8cba9e4f536fb87c1c1099c349fd2b948a42b35" + integrity sha512-NNrCKAeKWZ07VWDv4pmCxkigw3a1qFmJquLfc5D3Xsy4xGG0dNOmXmowIMT633j1mJizYlHHgwp0vpyZlodOYw== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" From 60ffc9a2558ef580c3b3a2cc925312519dd7fafb Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Wed, 20 Nov 2024 18:51:43 +0530 Subject: [PATCH 16/52] feat: remove markdown and marked from dashboard --- package.json | 4 +- src/components/bulkEdits/BulkEdits.tsx | 2 +- src/components/charts/AdvancedConfig.tsx | 2 +- .../DiscoverChartDetails.tsx | 102 +--- .../common/Description/GenericDescription.tsx | 3 +- .../environmentStatus/NotesDrawer.tsx | 5 +- .../devtronStackManager/AboutDevtronView.tsx | 3 +- .../DevtronStackManager.component.tsx | 2 +- .../ChartValuesView.component.tsx | 2 +- vite.config.mts | 2 +- yarn.lock | 465 +++++++++--------- 11 files changed, 238 insertions(+), 354 deletions(-) diff --git a/package.json b/package.json index 2a67d22d53..aa8aca1432 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.0.9", + "@devtron-labs/devtron-fe-common-lib": "1.0.11-beta-1", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", @@ -14,7 +14,6 @@ "@sentry/tracing": "7.50.0", "@tippyjs/react": "4.2.6", "@typeform/embed-react": "2.20.0", - "@types/marked": "4.0.8", "@vitejs/plugin-react": "4.3.1", "command-line-parser": "^0.2.10", "compute-histogram": "^0.9.11", @@ -23,7 +22,6 @@ "fast-json-patch": "^3.1.1", "flexsearch": "^0.6.32", "jsonpath-plus": "^10.0.0", - "marked": "4.3.0", "moment": "^2.29.4", "monaco-editor": "0.44.0", "monaco-yaml": "5.1.1", diff --git a/src/components/bulkEdits/BulkEdits.tsx b/src/components/bulkEdits/BulkEdits.tsx index 2bd3be02d9..c27afd494f 100644 --- a/src/components/bulkEdits/BulkEdits.tsx +++ b/src/components/bulkEdits/BulkEdits.tsx @@ -27,6 +27,7 @@ import { ComponentSizeType, ToastManager, ToastVariantType, + MarkDown, } from '@devtron-labs/devtron-fe-common-lib' import { DOCUMENTATION, SERVER_MODE, ViewType } from '../../config' import { @@ -40,7 +41,6 @@ import { ReactComponent as Close } from '../../assets/icons/ic-close.svg' import { ReactComponent as PlayButton } from '../../assets/icons/ic-play.svg' import { updateBulkList, getSeeExample, updateImpactedObjectsList } from './bulkedits.service' import './bulkEdit.scss' -import { MarkDown } from '../charts/discoverChartDetail/DiscoverChartDetails' import '../charts/discoverChartDetail/DiscoverChartDetails.scss' import '../charts/modal/DeployChart.scss' import EAEmptyState, { EAEmptyStateType } from '../common/eaEmptyState/EAEmptyState' diff --git a/src/components/charts/AdvancedConfig.tsx b/src/components/charts/AdvancedConfig.tsx index 3adac1ea44..eef0c54cb3 100644 --- a/src/components/charts/AdvancedConfig.tsx +++ b/src/components/charts/AdvancedConfig.tsx @@ -22,12 +22,12 @@ import { useAsync, CustomInput, CodeEditor, + MarkDown, } from '@devtron-labs/devtron-fe-common-lib' import { useHistory } from 'react-router-dom' import { Select, mapByKey, useKeyDown, Info, Pencil } from '../common' import { getEnvironmentListMin } from '../../services/service' import { ChartGroupEntry, AdvancedConfigHelpers, ChartValuesNativeType, ChartVersionType } from './charts.types' -import { MarkDown } from './discoverChartDetail/DiscoverChartDetails' import { getReadme, getChartValues } from './charts.service' import { ValuesYamlConfirmDialog } from './dialogs/ValuesYamlConfirmDialog' import { ReactComponent as LockIcon } from '../../assets/icons/ic-locked.svg' diff --git a/src/components/charts/discoverChartDetail/DiscoverChartDetails.tsx b/src/components/charts/discoverChartDetail/DiscoverChartDetails.tsx index 41f42f2215..aeb21ab09d 100644 --- a/src/components/charts/discoverChartDetail/DiscoverChartDetails.tsx +++ b/src/components/charts/discoverChartDetail/DiscoverChartDetails.tsx @@ -14,8 +14,8 @@ * limitations under the License. */ -import React, { useState, useEffect, useRef } from 'react' -import { Route, Switch, useRouteMatch, useLocation, useParams, useHistory } from 'react-router-dom' +import React, { useState, useEffect } from 'react' +import { Route, Switch, useRouteMatch, useParams, useHistory } from 'react-router-dom' import { showError, Progressing, @@ -26,9 +26,8 @@ import { versionComparatorBySortOrder, ToastManager, ToastVariantType, + MarkDown, } from '@devtron-labs/devtron-fe-common-lib' -import { marked } from 'marked' -import DOMPurify from 'dompurify' import { List } from '../../common' import { URLS } from '../../../config' import { getChartVersionsMin, getChartVersionDetails, getChartValuesCategorizedListParsed } from '../charts.service' @@ -47,8 +46,6 @@ import { ChartInstalledConfig, ChartKind } from '../../v2/values/chartValuesDiff import { ChartValuesType } from '../charts.types' const DiscoverDetailsContext = React.createContext(null) -const uncheckedCheckboxInputElement = `` -const checkedCheckboxInputElement = `` export function useDiscoverDetailsContext() { const context = React.useContext(DiscoverDetailsContext) if (!context) { @@ -405,97 +402,4 @@ const ReadmeRowHorizontal = ({ readme = null, version = '', ...props }) => { ) } -function isReadmeInputCheckbox(text: string) { - if (text.includes(uncheckedCheckboxInputElement) || text.includes(checkedCheckboxInputElement)) { - return true - } - return false -} - -/** - * - * @deprecated function is used in common component - */ -export const MarkDown = ({ markdown = '', className = '', breaks = false, disableEscapedText = false, ...props }) => { - const { hash } = useLocation() - const renderer = new marked.Renderer() - const mdeRef = useRef(null) - - renderer.listitem = function (text: string) { - if (isReadmeInputCheckbox(text)) { - text = text - .replace( - uncheckedCheckboxInputElement, - '', - ) - .replace( - checkedCheckboxInputElement, - '', - ) - return `
  • ${text}
  • ` - } - return `
  • ${text}
  • ` - } - - renderer.image = function (href: string, title: string, text: string) { - return `${text}` - } - - renderer.table = function (header, body) { - return ` -
    - - ${header} - ${body} -
    -
    - ` - } - - renderer.heading = function (text, level) { - const escapedText = disableEscapedText ? '' : text.toLowerCase().replace(/[^\w]+/g, '-') - - return ` - - - - ${text} - - ` - } - - marked.setOptions({ - renderer, - gfm: true, - smartLists: true, - ...(breaks && { breaks: true }), - }) - - useEffect(() => { - getHeight() - }, [markdown]) - - const getHeight = () => { - const editorHeight = mdeRef.current?.clientHeight - const minHeight = 320 - const showExpandableViewIcon = editorHeight > minHeight - if (typeof props.setExpandableIcon === 'function') { - props.setExpandableIcon(showExpandableViewIcon) - } - } - - function createMarkup() { - return { __html: DOMPurify.sanitize(marked(markdown), { USE_PROFILES: { html: true } }) } - } - return ( -
    - ) -} - export default DiscoverChartDetails diff --git a/src/components/common/Description/GenericDescription.tsx b/src/components/common/Description/GenericDescription.tsx index 3c33f04f1a..ee5fb069d4 100644 --- a/src/components/common/Description/GenericDescription.tsx +++ b/src/components/common/Description/GenericDescription.tsx @@ -20,7 +20,7 @@ import Tippy from '@tippyjs/react' import moment from 'moment' import { patchApplicationNote, patchClusterNote } from '../../ClusterNodes/clusterNodes.service' import 'react-mde/lib/styles/css/react-mde-all.css' -import { showError, TOAST_ACCESS_DENIED, ToastManager, ToastVariantType } from '@devtron-labs/devtron-fe-common-lib' +import { MarkDown, showError, TOAST_ACCESS_DENIED, ToastManager, ToastVariantType } from '@devtron-labs/devtron-fe-common-lib' import { MDEditorSelectedTabType } from '../../ClusterNodes/types' import { ReactComponent as HeaderIcon } from '../../../assets/icons/mdeditor/ic-header.svg' import { ReactComponent as BoldIcon } from '../../../assets/icons/mdeditor/ic-bold.svg' @@ -47,7 +47,6 @@ import { MARKDOWN_EDITOR_COMMAND_TITLE, } from '../../ClusterNodes/constants' import './GenericDescription.scss' -import { MarkDown } from '../../charts/discoverChartDetail/DiscoverChartDetails' import { AppMetaInfo } from '../../app/types' export default function GenericDescription({ diff --git a/src/components/v2/appDetails/sourceInfo/environmentStatus/NotesDrawer.tsx b/src/components/v2/appDetails/sourceInfo/environmentStatus/NotesDrawer.tsx index e27cb4689e..da59144915 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/NotesDrawer.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/NotesDrawer.tsx @@ -14,12 +14,11 @@ * limitations under the License. */ -import React, { useEffect, useRef } from 'react' +import { useEffect, useRef } from 'react' import { ReactComponent as Close } from '../../../assets/icons/ic-close.svg' -import { MarkDown } from '../../../../charts/discoverChartDetail/DiscoverChartDetails' import './environmentStatus.scss' import { NotesDrawerType } from './notesDrawer.type' -import { Drawer } from '@devtron-labs/devtron-fe-common-lib' +import { Drawer, MarkDown } from '@devtron-labs/devtron-fe-common-lib' const NotesDrawer = ({ notes, close }: NotesDrawerType) => { const appNotesRef = useRef(null) diff --git a/src/components/v2/devtronStackManager/AboutDevtronView.tsx b/src/components/v2/devtronStackManager/AboutDevtronView.tsx index e33dd5bdda..39c9e4b2f9 100644 --- a/src/components/v2/devtronStackManager/AboutDevtronView.tsx +++ b/src/components/v2/devtronStackManager/AboutDevtronView.tsx @@ -16,12 +16,11 @@ import React, { useEffect } from 'react' import AboutDevtron from '../../../assets/img/about-devtron@2x.png' -import { MarkDown } from '../../charts/discoverChartDetail/DiscoverChartDetails' import { InstallationWrapper } from './DevtronStackManager.component' import { AboutDevtronViewType, InstallationType } from './DevtronStackManager.type' import './AboutDevtronView.scss' import { URLS } from '../../../config' -import { TabGroup } from '@devtron-labs/devtron-fe-common-lib' +import { MarkDown, TabGroup } from '@devtron-labs/devtron-fe-common-lib' const AboutDevtronView = ({ parentRef, diff --git a/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx b/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx index 934c127681..682aaf9f7d 100644 --- a/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx +++ b/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx @@ -32,6 +32,7 @@ import { ToastManager, ToastVariantType, TOAST_ACCESS_DENIED, + MarkDown, } from '@devtron-labs/devtron-fe-common-lib' import Tippy from '@tippyjs/react' import { @@ -75,7 +76,6 @@ import { PENDING_DEPENDENCY_MESSAGE, handleEnableAction, } from './DevtronStackManager.utils' -import { MarkDown } from '../../charts/discoverChartDetail/DiscoverChartDetails' import './devtronStackManager.component.scss' import trivy from '../../../assets/icons/ic-clair-to-trivy.svg' import clair from '../../../assets/icons/ic-trivy-to-clair.svg' diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx index 06acb69809..18fc5cdd77 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx @@ -31,6 +31,7 @@ import { SelectPicker, SelectPickerOptionType, SelectPickerProps, + MarkDown, } from '@devtron-labs/devtron-fe-common-lib' import Tippy from '@tippyjs/react' import { ReactComponent as Error } from '../../../../assets/icons/ic-warning.svg' @@ -58,7 +59,6 @@ import { ValueNameInputType, gitOpsDrawerType, } from './ChartValuesView.type' -import { MarkDown } from '../../../charts/discoverChartDetail/DiscoverChartDetails' import { DELETE_CHART_APP_DESCRIPTION_LINES, DELETE_PRESET_VALUE_DESCRIPTION_LINES, diff --git a/vite.config.mts b/vite.config.mts index 6b08970ec8..6028a619a7 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -29,7 +29,7 @@ import { VitePWA } from 'vite-plugin-pwa' import tsconfigPaths from 'vite-tsconfig-paths' const WRONG_CODE = `import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";` -const TARGET_URL = 'https://preview.devtron.ai/' +const TARGET_URL = 'https://qa-ent.devtron.info/' function reactVirtualized(): PluginOption { return { diff --git a/yarn.lock b/yarn.lock index 99ad6ad0a8..bb0dcef5c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,7 +4,7 @@ "@adobe/css-tools@^4.0.1", "@adobe/css-tools@^4.4.0": version "4.4.0" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" + resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz" integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== "@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": @@ -161,9 +161,9 @@ "@babel/types" "^7.24.7" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.25.7" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz" - integrity sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw== + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz" + integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== "@babel/helper-remap-async-to-generator@^7.24.7", "@babel/helper-remap-async-to-generator@^7.25.0": version "7.25.0" @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9.tgz#0bff1f08ba59da0e1adbbad744e0b3fd8ab4d950" - integrity sha512-UtuG5iANoAoURcyuExq07dV4y/CyCai8RHZsh6PqBAjnCh0EKlmrc5JERWG0kwej+RYkmIDI98taYqgztpSSQg== +"@devtron-labs/devtron-fe-common-lib@1.0.11-beta-1": + version "1.0.11-beta-1" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.11-beta-1.tgz#009bc7d83c9a547de6039d8054d031e9f1e9f1d1" + integrity sha512-D52Scs2Cp0T+kLDjVuqdsw26GcJnjEB1lf3qioAq/Vf2s+gJjW9a/puTTER7ouzScPsgaMIO8EPywGa6fsgO9g== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" @@ -1129,7 +1129,7 @@ "@esbuild/darwin-arm64@0.24.0": version "0.24.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz#2d0d9414f2acbffd2d86e98253914fca603a53dd" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz" integrity sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw== "@esbuild/darwin-x64@0.21.5": @@ -1416,7 +1416,7 @@ "@joshwooding/vite-plugin-react-docgen-typescript@0.3.0": version "0.3.0" - resolved "https://registry.yarnpkg.com/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.3.0.tgz#67599fca260c2eafdaf234a944f9d471e6d53b08" + resolved "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.3.0.tgz" integrity sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA== dependencies: glob "^7.2.0" @@ -1614,95 +1614,95 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz#3e7eda4c0c1de6d2415343002d742ff95e38dca7" - integrity sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA== - -"@rollup/rollup-android-arm64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz#04f679231acf7284f1f8a1f7250d0e0944865ba8" - integrity sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg== - -"@rollup/rollup-darwin-arm64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz#ecea723041621747d0772af93b54752edf26467a" - integrity sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg== - -"@rollup/rollup-darwin-x64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz#28e6e0687092f31e20982fc104779d48c643fc21" - integrity sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA== - -"@rollup/rollup-freebsd-arm64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz#99e9173b8aef3d1ef086983da70413988206e530" - integrity sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g== - -"@rollup/rollup-freebsd-x64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz#f3a1ef941f8d3c6b2b036484c69a7b2d3d9ebbd7" - integrity sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw== - -"@rollup/rollup-linux-arm-gnueabihf@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz#9ba6adcc33f26f2a0c6ee658f0bbda4de8da2f75" - integrity sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA== - -"@rollup/rollup-linux-arm-musleabihf@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz#62f2426fa9016ec884f4fa779d7b62d5ba02a41a" - integrity sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ== - -"@rollup/rollup-linux-arm64-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz#f98ec111a231d35e0c6d3404e3d80f67f9d5b9f8" - integrity sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A== - -"@rollup/rollup-linux-arm64-musl@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz#4b36ffb8359f959f2c29afd187603c53368b6723" - integrity sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw== - -"@rollup/rollup-linux-powerpc64le-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz#52f4b39e6783505d168a745b79d86474fde71680" - integrity sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA== - -"@rollup/rollup-linux-riscv64-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz#49195be7e6a7d68d482b12461e2ea914e31ff977" - integrity sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA== - -"@rollup/rollup-linux-s390x-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz#4b8d50a205eac7b46cdcb9c50d4a6ae5994c02e0" - integrity sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ== - -"@rollup/rollup-linux-x64-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz#dfcceebc5ccac7fc2db19471996026258c81b55f" - integrity sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig== - -"@rollup/rollup-linux-x64-musl@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz#192f78bad8429711d63a31dc0a7d3312e2df850e" - integrity sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ== - -"@rollup/rollup-win32-arm64-msvc@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz#f4ec076579634f780b4e5896ae7f59f3e38e0c60" - integrity sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww== - -"@rollup/rollup-win32-ia32-msvc@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz#5458eab1929827e4f805cefb90bd09ecf7eeed2b" - integrity sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg== - -"@rollup/rollup-win32-x64-msvc@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz#93415e7e707e4b156d77c5950b983b58f4bc33f3" - integrity sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg== +"@rollup/rollup-android-arm-eabi@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz#ab2c78c43e4397fba9a80ea93907de7a144f3149" + integrity sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ== + +"@rollup/rollup-android-arm64@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz#de840660ab65cf73bd6d4bc62d38acd9fc94cd6c" + integrity sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw== + +"@rollup/rollup-darwin-arm64@4.27.3": + version "4.27.3" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz" + integrity sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA== + +"@rollup/rollup-darwin-x64@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz#56dab9e4cac0ad97741740ea1ac7b6a576e20e59" + integrity sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg== + +"@rollup/rollup-freebsd-arm64@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz#bcb4112cb7e68a12d148b03cbc21dde43772f4bc" + integrity sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw== + +"@rollup/rollup-freebsd-x64@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz#c7cd9f69aa43847b37d819f12c2ad6337ec245fa" + integrity sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA== + +"@rollup/rollup-linux-arm-gnueabihf@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz#3692b22987a6195c8490bbf6357800e0c183ee38" + integrity sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q== + +"@rollup/rollup-linux-arm-musleabihf@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz#f920f24e571f26bbcdb882267086942fdb2474bf" + integrity sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg== + +"@rollup/rollup-linux-arm64-gnu@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz#2046553e91d8ca73359a2a3bb471826fbbdcc9a3" + integrity sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ== + +"@rollup/rollup-linux-arm64-musl@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz#8a3f05dbae753102ae10a9bc2168c7b6bbeea5da" + integrity sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g== + +"@rollup/rollup-linux-powerpc64le-gnu@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz#d281d9c762f9e4f1aa7909a313f7acbe78aced32" + integrity sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw== + +"@rollup/rollup-linux-riscv64-gnu@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz#fa84b3f81826cee0de9e90f9954f3e55c3cc6c97" + integrity sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A== + +"@rollup/rollup-linux-s390x-gnu@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz#6b9c04d84593836f942ceb4dd90644633d5fe871" + integrity sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA== + +"@rollup/rollup-linux-x64-gnu@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz#f13effcdcd1cc14b26427e6bec8c6c9e4de3773e" + integrity sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA== + +"@rollup/rollup-linux-x64-musl@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz#6547bc0069f2d788e6cf0f33363b951181f4cca5" + integrity sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ== + +"@rollup/rollup-win32-arm64-msvc@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz#3f2db9347c5df5e6627a7e12d937cea527d63526" + integrity sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw== + +"@rollup/rollup-win32-ia32-msvc@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz#54fcf9a13a98d3f0e4be6a4b6e28b9dca676502f" + integrity sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w== + +"@rollup/rollup-win32-x64-msvc@4.27.3": + version "4.27.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz#3721f601f973059bfeeb572992cf0dfc94ab2970" + integrity sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg== "@sentry-internal/feedback@7.119.1": version "7.119.1" @@ -1901,7 +1901,7 @@ "@storybook/addon-a11y@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-8.4.2.tgz#e827b69ae57dec2b6eeac9835ae125613c32ee34" + resolved "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.4.2.tgz" integrity sha512-v6Tl+qr3Eslf06qmt2hq1ticYi7oRLIFosePQUOlW1+cgdIbV+r1IxsZ7creCDWX4kIMTbUFhbET9LTYGHem1A== dependencies: "@storybook/addon-highlight" "8.4.2" @@ -1909,7 +1909,7 @@ "@storybook/addon-actions@8.4.2", "@storybook/addon-actions@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.4.2.tgz#3aead1b324ff78144f004f22aa6784b1c1d8a13b" + resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.2.tgz" integrity sha512-+hA200XN5aeA4T3jq8IifQq6Y+9FyNQ0Q+blM1L0Tl7WLzBc7B1kHQnKvhSj5pvMSBWc/Q/kY7Ev5t9gdOu13g== dependencies: "@storybook/global" "^5.0.0" @@ -1920,7 +1920,7 @@ "@storybook/addon-backgrounds@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.2.tgz#56adb5772df3b9c0625aaca63ac3d867c952680c" + resolved "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.2.tgz" integrity sha512-s4uag5VKuk8q2MSnuNS7Sv+v1/mykzGPXe/zZRW2ammtkdHp8Uy78eQS2G0aiG02chXCX+qQgWMyy5QItDcTFQ== dependencies: "@storybook/global" "^5.0.0" @@ -1929,7 +1929,7 @@ "@storybook/addon-controls@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-8.4.2.tgz#441320611e5f3aff7bdc5c740e0925b9728bd137" + resolved "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.2.tgz" integrity sha512-raCbHEj1xl4F3wKH6IdfEXNRaxKpY4QGhjSTE8Pte5iJSVhKG86taLqqRr+4dC7H1/LVMPU1XCGV4mkgDGtyxQ== dependencies: "@storybook/global" "^5.0.0" @@ -1938,7 +1938,7 @@ "@storybook/addon-docs@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-8.4.2.tgz#43533b43bbaa8662bfcc69c343444a703d434a69" + resolved "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.2.tgz" integrity sha512-jIpykha7hv2Inlrq31ZoYg2QhuCuvcO+Q+uvhT45RDTB+2US/fg3rJINKlw2Djq8RPPOXvty5W0yvE6CrWKhnQ== dependencies: "@mdx-js/react" "^3.0.0" @@ -1951,7 +1951,7 @@ "@storybook/addon-essentials@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-8.4.2.tgz#c633cb7eee48e2c6c5dbdc7cadebdf8191adf78c" + resolved "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.2.tgz" integrity sha512-+/vfPrXM/GWU3Kbrg92PepwAZr7lOeulTTYF4THK0CL3DfUUlkGNpBPLP5PtjCuIkVrTCjXiIEdVWk47d5m2+w== dependencies: "@storybook/addon-actions" "8.4.2" @@ -1967,14 +1967,14 @@ "@storybook/addon-highlight@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-8.4.2.tgz#77fae7df40e178d33ff8e0bcf34282768d34dc5a" + resolved "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.2.tgz" integrity sha512-vTtwp7nyJ09SXrsMnH+pukCjHjRMjQXgHZHxvbrv09uoH8ldQMv9B7u+X+9Wcy/jYSKFz/ng7pWo4b4a2oXHkg== dependencies: "@storybook/global" "^5.0.0" "@storybook/addon-interactions@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-8.4.2.tgz#1808b6961ec5422347bcc2fe10ba15dfc921e620" + resolved "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.2.tgz" integrity sha512-+/NTENTApeOcONgFNQ6Olbk0GH3pTDG3w0eh00slCB+2agD1BcVKg8SSlHQV0lQF1cK3vWL/X3jeaxdFLYOjjg== dependencies: "@storybook/global" "^5.0.0" @@ -1985,7 +1985,7 @@ "@storybook/addon-links@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-8.4.2.tgz#facbfe343579ba76e391cd261862eeb29b44b5ab" + resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.4.2.tgz" integrity sha512-8nncReA/drR2cyAcUz484FIv+MXbyCQxYrA6yfWHthZfGu+vMIETvhh+eP4OpluVnxySoQ+hCVK/V8G2jcyAZg== dependencies: "@storybook/csf" "^0.1.11" @@ -1994,7 +1994,7 @@ "@storybook/addon-measure@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-8.4.2.tgz#43b24d3246502e34b3f26780e380f143bd057fac" + resolved "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.2.tgz" integrity sha512-z+j6xQwcUBSpgzl1XDU+xU4YYgLraLMljECW7NvRNyJ/PYixvol8R3wtzWbr+CBpxmvbXjEJCPlF+EjF9/mBWQ== dependencies: "@storybook/global" "^5.0.0" @@ -2002,7 +2002,7 @@ "@storybook/addon-outline@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-8.4.2.tgz#cbf0f8fbb5c9a0a0a00a7ffdc67a823eeef05def" + resolved "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.2.tgz" integrity sha512-oTMlPEyT4CBqzcQbfemoJzJ6yzeRAmvrAx9ssaBcnQQRsKxo0D2Ri/Jmm6SNcR0yBHxYRkvIH+2phLw8aiflCQ== dependencies: "@storybook/global" "^5.0.0" @@ -2010,19 +2010,19 @@ "@storybook/addon-toolbars@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-8.4.2.tgz#0662c884ebc5c09b369644fc0f8ee3dca92ab1e9" + resolved "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.2.tgz" integrity sha512-DidzW/NQS224niMJIjcJI2ls83emqygUcS9GYNGgdc5Xwro/TPgGYOXP2qnXgYUxXQTHbrxmIbHdEehxC7CcYQ== "@storybook/addon-viewport@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-8.4.2.tgz#cc7014c9a64046de574334767936ee361e6f28c0" + resolved "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.2.tgz" integrity sha512-qVQ2UaxCNsUSFHnAAAizNPIJ/QwfMg7p5bBdpYROTZXJe+bxVp0rFzZmQgHZ3/sn+lzE4ItM4QEfxkfQUWi1ag== dependencies: memoizerific "^1.11.3" "@storybook/blocks@8.4.2", "@storybook/blocks@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-8.4.2.tgz#69f4458e4aeab1265ae6a304052c5239a0cb82da" + resolved "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.2.tgz" integrity sha512-yAAvmOWaD8gIrepOxCh/RxQqd/1xZIwd/V+gsvAhW/thawN+SpI+zK63gmcqAPLX84hJ3Dh5pegRk0SoHNuDVA== dependencies: "@storybook/csf" "^0.1.11" @@ -2031,7 +2031,7 @@ "@storybook/builder-vite@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/builder-vite/-/builder-vite-8.4.2.tgz#50b0830d0981198508bc5b80411ea834c91c11f6" + resolved "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.4.2.tgz" integrity sha512-dO5FB5yH1C6tr/kBHn1frvGwp8Pt0D1apgXWkJ5ITWEUfh6WwOqX2fqsWsqaNwE7gP0qn0XgwCIEkI/4Mj55SA== dependencies: "@storybook/csf-plugin" "8.4.2" @@ -2040,12 +2040,12 @@ "@storybook/components@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.4.2.tgz#e9e7d5dfaef3e36a2654c6bfbd79aa5a4f307a20" + resolved "https://registry.npmjs.org/@storybook/components/-/components-8.4.2.tgz" integrity sha512-+W59oF7D73LAxLNmCfFrfs98cH9pyNHK9HlJoO5/lKbK4IdWhhOoqUR/AJ3ueksoLuetFat4DxyE8SN1H4Bvrg== "@storybook/core@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-8.4.2.tgz#1e591fc6efef30e4e4fde4f266ca0cc9e756e516" + resolved "https://registry.npmjs.org/@storybook/core/-/core-8.4.2.tgz" integrity sha512-hF8GWoUZTjwwuV5j4OLhMHZtZQL/NYcVUBReC2Ba06c8PkFIKqKZwATr1zKd301gQ5Qwcn9WgmZxJTMgdKQtOg== dependencies: "@storybook/csf" "^0.1.11" @@ -2062,7 +2062,7 @@ "@storybook/csf-plugin@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-8.4.2.tgz#3d098179c6ece8f4a053fdb258981cc6b467a1cb" + resolved "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.2.tgz" integrity sha512-1f0t6W5xbC1sSAHHs3uXYPIQs2NXAEtIGqn6X9i3xbbub6hDS8PF8BIm7dOjQ8dZOPp7d9ltR64V5CoLlsOigA== dependencies: unplugin "^1.3.1" @@ -2076,7 +2076,7 @@ "@storybook/csf@^0.1.11": version "0.1.11" - resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.1.11.tgz#ad685a4fe564a47a6b73571c2e7c07b526f4f71b" + resolved "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.11.tgz" integrity sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg== dependencies: type-fest "^2.19.0" @@ -2088,12 +2088,12 @@ "@storybook/icons@^1.2.12": version "1.2.12" - resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-1.2.12.tgz#3e4c939113b67df7ab17b78f805dbb57f4acf0db" + resolved "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.12.tgz" integrity sha512-UxgyK5W3/UV4VrI3dl6ajGfHM4aOqMAkFLWe2KibeQudLf6NJpDrDMSHwZj+3iKC4jFU7dkKbbtH2h/al4sW3Q== "@storybook/instrumenter@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-8.4.2.tgz#6599abf2b698328659eb0cd5568d553f8d649776" + resolved "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.2.tgz" integrity sha512-gPYCZ/0O6gRLI3zmenu2N6QtKzxDZFdT2xf4RWcNUSZyp28RZkRCIgKFMt3fTmvE0yMzAjQyRSkBdrONjQ44HA== dependencies: "@storybook/global" "^5.0.0" @@ -2101,22 +2101,22 @@ "@storybook/manager-api@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.4.2.tgz#6bf972accfa6339034b50a7338654ad433aac6d1" + resolved "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.2.tgz" integrity sha512-rhPc4cgQDKDH8NUyRh/ZaJW7QIhR/PO5MNX4xc+vz71sM2nO7ONA/FrgLtCuu4SULdwilEPvGefYvLK0dE+Caw== "@storybook/preview-api@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.2.tgz#77640e16c8662b9aa3a9dd4ec1b7362b9b4f6b3f" + resolved "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.2.tgz" integrity sha512-5X/xvIvDPaWJKUBCo5zVeBbbjkhnwcI2KPkuOgrHVRRhuQ5WqD0RYxVtOOFNyQXme7g0nNl5RFNgvT7qv9qGeg== "@storybook/react-dom-shim@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-8.4.2.tgz#cefc4b2cb7d3f632492867a3d5edbf568418c66a" + resolved "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.2.tgz" integrity sha512-FZVTM1f34FpGnf6e3MDIKkz05gmn8H9wEccvQAgr8pEFe8VWfrpVWeUrmatSAfgrCMNXYC1avDend8UX6IM8Fg== "@storybook/react-vite@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/react-vite/-/react-vite-8.4.2.tgz#ddc9c52d9f629db896910768be8ec48c16ab616a" + resolved "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-8.4.2.tgz" integrity sha512-OoXaW/V1AqLggMyniRcnuwmqQ1/OtSn38t31lePX4nDDeJhbGT3ZPldRrwvsLb0EaD3N27uoL+QbAOgsYJIhwA== dependencies: "@joshwooding/vite-plugin-react-docgen-typescript" "0.3.0" @@ -2131,7 +2131,7 @@ "@storybook/react@8.4.2", "@storybook/react@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.4.2.tgz#a8d61902e0b72e99e96dfde4251eb0ce79249905" + resolved "https://registry.npmjs.org/@storybook/react/-/react-8.4.2.tgz" integrity sha512-rO5/aVKBVhIKENcL7G8ud4QKC5OyWBPCkJIvY6XUHIuhErJy9/4pP+sZ85jypVwx5kq+EqCPF8AEOWjIxB/4/Q== dependencies: "@storybook/components" "8.4.2" @@ -2143,7 +2143,7 @@ "@storybook/test@8.4.2", "@storybook/test@^8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/test/-/test-8.4.2.tgz#aab7b4c6848b961f7550fa98157fca3bf2ac817c" + resolved "https://registry.npmjs.org/@storybook/test/-/test-8.4.2.tgz" integrity sha512-MipTdboStv0hsqF2Sw8TZgP0YnxCcDYwxkTOd4hmRzev/7Brtvpi4pqjqh8k98ZCvhrCPAPVIoX5drk+oi3YUA== dependencies: "@storybook/csf" "^0.1.11" @@ -2157,7 +2157,7 @@ "@storybook/theming@8.4.2": version "8.4.2" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.4.2.tgz#0e385869a225040e326cfba301b6cdccd31dcb21" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.2.tgz" integrity sha512-9j4fnu5LcV+qSs1rdwf61Bt14lms0T1LOZkHxGNcS1c1oH+cPS+sxECh2lxtni+mvOAHUlBs9pKhVZzRPdWpvg== "@surma/rollup-plugin-off-main-thread@^2.2.3": @@ -2255,7 +2255,7 @@ "@testing-library/dom@10.4.0": version "10.4.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz" integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== dependencies: "@babel/code-frame" "^7.10.4" @@ -2283,7 +2283,7 @@ "@testing-library/jest-dom@6.5.0": version "6.5.0" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz#50484da3f80fb222a853479f618a9ce5c47bfe54" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz" integrity sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA== dependencies: "@adobe/css-tools" "^4.4.0" @@ -2474,16 +2474,11 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== -"@types/estree@1.0.6": +"@types/estree@1.0.6", "@types/estree@^1.0.0": version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/estree@^1.0.0": - version "1.0.5" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== - "@types/glob@^7.1.3": version "7.2.0" resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" @@ -2534,11 +2529,6 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/marked@4.0.8": - version "4.0.8" - resolved "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz" - integrity sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw== - "@types/mdx@^2.0.0": version "2.0.13" resolved "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz" @@ -2858,7 +2848,7 @@ "@vitest/pretty-format@2.1.4": version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.4.tgz#fc31993bdc1ef5a6c1a4aa6844e7ba55658a4f9f" + resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz" integrity sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww== dependencies: tinyrainbow "^1.2.0" @@ -2906,7 +2896,7 @@ "@vitest/utils@^2.1.1": version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.4.tgz#6d67ac966647a21ce8bc497472ce230de3b64537" + resolved "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz" integrity sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg== dependencies: "@vitest/pretty-format" "2.1.4" @@ -2939,7 +2929,7 @@ agent-base@6: aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -2994,7 +2984,7 @@ ajv@^8.0.0, ajv@^8.12.0, ajv@^8.6.0: ansi-escapes@^4.3.0: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" @@ -3204,7 +3194,7 @@ ast-types@^0.16.1: astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async@^3.2.3: @@ -3276,7 +3266,7 @@ balanced-match@^1.0.0: better-opn@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817" + resolved "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz" integrity sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ== dependencies: open "^8.0.4" @@ -3444,19 +3434,19 @@ ci-info@^3.2.0: clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-truncate@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: slice-ansi "^3.0.0" @@ -3464,7 +3454,7 @@ cli-truncate@^2.1.0: cli-truncate@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz" integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== dependencies: slice-ansi "^5.0.0" @@ -3506,7 +3496,7 @@ color-name@~1.1.4: colorette@^2.0.16: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== command-line-parser@^0.2.10: @@ -3526,7 +3516,7 @@ commander@^4.0.0: commander@^9.3.0: version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== common-tags@^1.8.0: @@ -3776,7 +3766,7 @@ debug@^3.2.7: debug@^4.3.6: version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: ms "^2.1.3" @@ -3846,7 +3836,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: define-lazy-prop@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: @@ -3936,7 +3926,7 @@ dompurify@^3.0.2: eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== ejs@^3.1.10, ejs@^3.1.6: @@ -3953,7 +3943,7 @@ electron-to-chromium@^1.5.41: emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: @@ -4142,7 +4132,7 @@ esbuild-register@^3.5.0: "esbuild@^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0": version "0.24.0" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.0.tgz#f2d470596885fcb2e91c21eb3da3b3c89c0b55e7" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz" integrity sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ== optionalDependencies: "@esbuild/aix-ppc64" "0.24.0" @@ -4501,7 +4491,7 @@ eventsource@^2.0.2: execa@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -4516,7 +4506,7 @@ execa@^5.1.1: execa@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz" integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== dependencies: cross-spawn "^7.0.3" @@ -4732,7 +4722,7 @@ get-own-enumerable-property-symbols@^3.0.0: get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-stream@^8.0.1: @@ -4931,7 +4921,7 @@ https-proxy-agent@^5.0.0: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== human-signals@^5.0.0: @@ -5099,7 +5089,7 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extglob@^2.1.1: @@ -5116,7 +5106,7 @@ is-finalizationregistry@^1.0.2: is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^4.0.0: @@ -5258,7 +5248,7 @@ is-weakset@^2.0.3: is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" @@ -5383,7 +5373,7 @@ js-yaml@^4.1.0: jsdoc-type-pratt-parser@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz#ff6b4a3f339c34a6c188cbf50a16087858d22113" + resolved "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz" integrity sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg== jsdom-worker@^0.2.1: @@ -5546,7 +5536,7 @@ lie@3.1.1: lilconfig@2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz" integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== lines-and-columns@^1.1.6: @@ -5556,7 +5546,7 @@ lines-and-columns@^1.1.6: lint-staged@12.5.0: version "12.5.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.5.0.tgz#d6925747480ae0e380d13988522f9dd8ef9126e3" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-12.5.0.tgz" integrity sha512-BKLUjWDsKquV/JuIcoQW4MSAI3ggwEImF1+sB4zaKvyVx1wBk3FsG7UK9bpnmBTN1pm7EH2BBcMwINJzCRv12g== dependencies: cli-truncate "^3.1.0" @@ -5576,7 +5566,7 @@ lint-staged@12.5.0: listr2@^4.0.5: version "4.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" + resolved "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz" integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== dependencies: cli-truncate "^2.1.0" @@ -5643,7 +5633,7 @@ lodash@^4.1.1, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4 log-update@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: ansi-escapes "^4.3.0" @@ -5667,7 +5657,7 @@ loupe@^3.1.0, loupe@^3.1.1: loupe@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" + resolved "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== lru-cache@^5.1.1: @@ -5718,11 +5708,6 @@ markdown-to-jsx@^7.4.1: resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.4.7.tgz" integrity sha512-0+ls1IQZdU6cwM1yu0ZjjiVWYtkbExSyUIFU2ZeDIFuZM1W42Mh4OlJ4nb4apX4H8smxDHRdFaoIVJGwfv5hkg== -marked@4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz" - integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== - memoize-one@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz" @@ -5755,7 +5740,7 @@ micromatch@^4.0.4, micromatch@^4.0.5: mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-fn@^4.0.0: @@ -5870,7 +5855,7 @@ ms@2.1.2, ms@^2.1.1: ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== nanoid@^3.3.7: @@ -5902,7 +5887,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" @@ -5921,7 +5906,7 @@ object-assign@^4.1.1: object-inspect@^1.12.2: version "1.13.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz" integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== object-inspect@^1.13.1: @@ -6007,7 +5992,7 @@ once@^1.3.0: onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" @@ -6021,7 +6006,7 @@ onetime@^6.0.0: open@^8.0.4: version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== dependencies: define-lazy-prop "^2.0.0" @@ -6056,7 +6041,7 @@ p-locate@^5.0.0: p-map@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" @@ -6152,7 +6137,7 @@ picomatch@^4.0.2: pidtree@^0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.5.0.tgz#ad5fbc1de78b8a5f99d6fbdd4f6e4eee21d1aca1" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz" integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== playwright-core@1.45.0: @@ -6761,7 +6746,7 @@ resolve@^2.0.0-next.5: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -6774,7 +6759,7 @@ reusify@^1.0.4: rfdc@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== rimraf@^3.0.2: @@ -6808,30 +6793,30 @@ rollup-pluginutils@^2.8.1: estree-walker "^0.6.1" rollup@^2.43.1, rollup@^4.20.0, rollup@^4.22.4: - version "4.25.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.25.0.tgz#74dff4b5c2777dfc490f9711393925da50171787" - integrity sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg== + version "4.27.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.27.3.tgz#078ecb20830c1de1f5486607f3e2f490269fb98a" + integrity sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ== dependencies: "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.25.0" - "@rollup/rollup-android-arm64" "4.25.0" - "@rollup/rollup-darwin-arm64" "4.25.0" - "@rollup/rollup-darwin-x64" "4.25.0" - "@rollup/rollup-freebsd-arm64" "4.25.0" - "@rollup/rollup-freebsd-x64" "4.25.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.25.0" - "@rollup/rollup-linux-arm-musleabihf" "4.25.0" - "@rollup/rollup-linux-arm64-gnu" "4.25.0" - "@rollup/rollup-linux-arm64-musl" "4.25.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.25.0" - "@rollup/rollup-linux-riscv64-gnu" "4.25.0" - "@rollup/rollup-linux-s390x-gnu" "4.25.0" - "@rollup/rollup-linux-x64-gnu" "4.25.0" - "@rollup/rollup-linux-x64-musl" "4.25.0" - "@rollup/rollup-win32-arm64-msvc" "4.25.0" - "@rollup/rollup-win32-ia32-msvc" "4.25.0" - "@rollup/rollup-win32-x64-msvc" "4.25.0" + "@rollup/rollup-android-arm-eabi" "4.27.3" + "@rollup/rollup-android-arm64" "4.27.3" + "@rollup/rollup-darwin-arm64" "4.27.3" + "@rollup/rollup-darwin-x64" "4.27.3" + "@rollup/rollup-freebsd-arm64" "4.27.3" + "@rollup/rollup-freebsd-x64" "4.27.3" + "@rollup/rollup-linux-arm-gnueabihf" "4.27.3" + "@rollup/rollup-linux-arm-musleabihf" "4.27.3" + "@rollup/rollup-linux-arm64-gnu" "4.27.3" + "@rollup/rollup-linux-arm64-musl" "4.27.3" + "@rollup/rollup-linux-powerpc64le-gnu" "4.27.3" + "@rollup/rollup-linux-riscv64-gnu" "4.27.3" + "@rollup/rollup-linux-s390x-gnu" "4.27.3" + "@rollup/rollup-linux-x64-gnu" "4.27.3" + "@rollup/rollup-linux-x64-musl" "4.27.3" + "@rollup/rollup-win32-arm64-msvc" "4.27.3" + "@rollup/rollup-win32-ia32-msvc" "4.27.3" + "@rollup/rollup-win32-x64-msvc" "4.27.3" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -6966,7 +6951,7 @@ siginfo@^2.0.0: signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.1.0: @@ -6981,7 +6966,7 @@ slash@^3.0.0: slice-ansi@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== dependencies: ansi-styles "^4.0.0" @@ -6990,7 +6975,7 @@ slice-ansi@^3.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -7080,7 +7065,7 @@ stop-iteration-iterator@^1.0.0: storybook@^8.4.2: version "8.4.2" - resolved "https://registry.yarnpkg.com/storybook/-/storybook-8.4.2.tgz#02e71cf32db25af713b3681b1b52be1403b478dd" + resolved "https://registry.npmjs.org/storybook/-/storybook-8.4.2.tgz" integrity sha512-GMCgyAulmLNrkUtDkCpFO4SB77YrpiIxq6e5tzaQdXEuaDu1mdNwOuP3VG7nE2FzxmqDvagSgriM68YW9iFaZA== dependencies: "@storybook/core" "8.4.2" @@ -7092,12 +7077,12 @@ strict-uri-encode@^2.0.0: string-argv@^0.3.1: version "0.3.2" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -7106,7 +7091,7 @@ string-width@^4.1.0, string-width@^4.2.0: string-width@^5.0.0: version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" @@ -7185,7 +7170,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: strip-ansi@^7.0.1: version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: ansi-regex "^6.0.1" @@ -7202,7 +7187,7 @@ strip-comments@^2.0.1: strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-final-newline@^3.0.0: @@ -7250,7 +7235,7 @@ supports-color@^7.1.0: supports-color@^9.2.2: version "9.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz" integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== supports-preserve-symlinks-flag@^1.0.0: @@ -7308,7 +7293,7 @@ text-table@^0.2.0: through@^2.3.8: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tiny-invariant@^1.0.2, tiny-invariant@^1.3.1, tiny-invariant@^1.3.3: @@ -7494,7 +7479,7 @@ type-fest@^0.20.2: type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^2.19.0: @@ -7644,7 +7629,7 @@ use-isomorphic-layout-effect@^1.1.2: util@^0.12.5: version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz" integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== dependencies: inherits "^2.0.3" @@ -7748,7 +7733,7 @@ vite-node@2.0.5: vite-plugin-pwa@0.20.5: version "0.20.5" - resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.20.5.tgz#437dca4a9bff650dc9c84ea3d7d3ac230b5985e0" + resolved "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.20.5.tgz" integrity sha512-aweuI/6G6n4C5Inn0vwHumElU/UEpNuO+9iZzwPZGTCH87TeZ6YFMrEY6ZUBQdIHHlhTsbMDryFARcSuOdsz9Q== dependencies: debug "^4.3.6" @@ -7786,7 +7771,7 @@ vite-tsconfig-paths@5.0.1: vite@5.4.11: version "5.4.11" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" + resolved "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz" integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== dependencies: esbuild "^0.21.3" @@ -7977,7 +7962,7 @@ word-wrap@^1.2.5: workbox-background-sync@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.3.0.tgz#b6340731a8d5b42b9e75a8a87c8806928e6e6303" + resolved "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.3.0.tgz" integrity sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg== dependencies: idb "^7.0.1" @@ -7985,14 +7970,14 @@ workbox-background-sync@7.3.0: workbox-broadcast-update@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-7.3.0.tgz#bff86b91795c4b9fa46a758d1a7a151828623280" + resolved "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.3.0.tgz" integrity sha512-T9/F5VEdJVhwmrIAE+E/kq5at2OY6+OXXgOWQevnubal6sO92Gjo24v6dCVwQiclAF5NS3hlmsifRrpQzZCdUA== dependencies: workbox-core "7.3.0" workbox-build@^7.1.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-7.3.0.tgz#ab688f3241b32862236aeeb62b240195f1fe4b62" + resolved "https://registry.npmjs.org/workbox-build/-/workbox-build-7.3.0.tgz" integrity sha512-JGL6vZTPlxnlqZRhR/K/msqg3wKP+m0wfEUVosK7gsYzSgeIxvZLi1ViJJzVL7CEeI8r7rGFV973RiEqkP3lWQ== dependencies: "@apideck/better-ajv-errors" "^0.3.1" @@ -8035,7 +8020,7 @@ workbox-build@^7.1.0: workbox-cacheable-response@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-7.3.0.tgz#557b0f5fdfceb22fe243e3f19807c76a0ae646e3" + resolved "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.3.0.tgz" integrity sha512-eAFERIg6J2LuyELhLlmeRcJFa5e16Mj8kL2yCDbhWE+HUun9skRQrGIFVUagqWj4DMaaPSMWfAolM7XZZxNmxA== dependencies: workbox-core "7.3.0" @@ -8047,12 +8032,12 @@ workbox-core@7.1.0: workbox-core@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.3.0.tgz#f24fb92041a0b7482fe2dd856544aaa9fa105248" + resolved "https://registry.npmjs.org/workbox-core/-/workbox-core-7.3.0.tgz" integrity sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw== workbox-expiration@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-7.3.0.tgz#2c1ee1fdada34aa7e7474f706d5429c914bd10d2" + resolved "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.3.0.tgz" integrity sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ== dependencies: idb "^7.0.1" @@ -8060,7 +8045,7 @@ workbox-expiration@7.3.0: workbox-google-analytics@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-7.3.0.tgz#3c4d4956c0a9800dfb587d82ec8bc0f9cf963791" + resolved "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.3.0.tgz" integrity sha512-ii/tSfFdhjLHZ2BrYgFNTrb/yk04pw2hasgbM70jpZfLk0vdJAXgaiMAWsoE+wfJDNWoZmBYY0hMVI0v5wWDbg== dependencies: workbox-background-sync "7.3.0" @@ -8077,7 +8062,7 @@ workbox-navigation-preload@7.1.0: workbox-navigation-preload@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-7.3.0.tgz#9d54693b9179d5175e66af5ef9a92d1b7cf3e605" + resolved "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.3.0.tgz" integrity sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg== dependencies: workbox-core "7.3.0" @@ -8093,7 +8078,7 @@ workbox-precaching@7.1.0: workbox-precaching@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.3.0.tgz#a84663d69efdb334f25c04dba0a72ed3391c4da8" + resolved "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.3.0.tgz" integrity sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw== dependencies: workbox-core "7.3.0" @@ -8102,14 +8087,14 @@ workbox-precaching@7.3.0: workbox-range-requests@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-7.3.0.tgz#1b3d5c235a0ff5271418c3a7183281dc131ccd0d" + resolved "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.3.0.tgz" integrity sha512-EyFmM1KpDzzAouNF3+EWa15yDEenwxoeXu9bgxOEYnFfCxns7eAxA9WSSaVd8kujFFt3eIbShNqa4hLQNFvmVQ== dependencies: workbox-core "7.3.0" workbox-recipes@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-7.3.0.tgz#fa407101e8ce52850dfba8e17a5afccb733a3942" + resolved "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.3.0.tgz" integrity sha512-BJro/MpuW35I/zjZQBcoxsctgeB+kyb2JAP5EB3EYzePg8wDGoQuUdyYQS+CheTb+GhqJeWmVs3QxLI8EBP1sg== dependencies: workbox-cacheable-response "7.3.0" @@ -8128,7 +8113,7 @@ workbox-routing@7.1.0: workbox-routing@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.3.0.tgz#fc86296bc1155c112ee2c16b3180853586c30208" + resolved "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.3.0.tgz" integrity sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A== dependencies: workbox-core "7.3.0" @@ -8142,14 +8127,14 @@ workbox-strategies@7.1.0: workbox-strategies@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.3.0.tgz#bb1530f205806895aacdea3639e6cf6bfb3a6cb0" + resolved "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.3.0.tgz" integrity sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg== dependencies: workbox-core "7.3.0" workbox-streams@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-7.3.0.tgz#a4c0ae51b66121a2aa6f89229e237aca6dc27eb5" + resolved "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.3.0.tgz" integrity sha512-SZnXucyg8x2Y61VGtDjKPO5EgPUG5NDn/v86WYHX+9ZqvAsGOytP0Jxp1bl663YUuMoXSAtsGLL+byHzEuMRpw== dependencies: workbox-core "7.3.0" @@ -8157,7 +8142,7 @@ workbox-streams@7.3.0: workbox-sw@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-7.3.0.tgz#39215017e868d7cfe6835b2961f55369d89b3e73" + resolved "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.3.0.tgz" integrity sha512-aCUyoAZU9IZtH05mn0ACUpyHzPs0lMeJimAYkQkBsOWiqaJLgusfDCR+yllkPkFRxWpZKF8vSvgHYeG7LwhlmA== workbox-window@7.1.0, workbox-window@^7.1.0: @@ -8170,7 +8155,7 @@ workbox-window@7.1.0, workbox-window@^7.1.0: workbox-window@7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-7.3.0.tgz#e71bb0b4d880d2295c96bf1ccadb6cea0df51c07" + resolved "https://registry.npmjs.org/workbox-window/-/workbox-window-7.3.0.tgz" integrity sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA== dependencies: "@types/trusted-types" "^2.0.2" @@ -8186,7 +8171,7 @@ worker-loader@3.0.8: wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -8195,7 +8180,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" From 3f3083568eff4f750bc7efc348043a9484af2feb Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Wed, 20 Nov 2024 18:54:56 +0530 Subject: [PATCH 17/52] chore: revert target url --- vite.config.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.config.mts b/vite.config.mts index 6028a619a7..6b08970ec8 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -29,7 +29,7 @@ import { VitePWA } from 'vite-plugin-pwa' import tsconfigPaths from 'vite-tsconfig-paths' const WRONG_CODE = `import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";` -const TARGET_URL = 'https://qa-ent.devtron.info/' +const TARGET_URL = 'https://preview.devtron.ai/' function reactVirtualized(): PluginOption { return { From b2d10743e23371faae60e1979678541a40337078 Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Thu, 21 Nov 2024 00:17:21 +0530 Subject: [PATCH 18/52] feat: helm charts sidebar ui fixes --- .../chartValuesDiff/ChartRepoSelector.tsx | 2 +- .../ChartValuesView.component.tsx | 62 ++-- .../chartValuesDiff/ChartValuesView.scss | 15 + .../chartValuesDiff/ChartValuesView.tsx | 347 +++++++++--------- 4 files changed, 221 insertions(+), 205 deletions(-) diff --git a/src/components/v2/values/chartValuesDiff/ChartRepoSelector.tsx b/src/components/v2/values/chartValuesDiff/ChartRepoSelector.tsx index fbc62e56b7..57ccecb98f 100644 --- a/src/components/v2/values/chartValuesDiff/ChartRepoSelector.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartRepoSelector.tsx @@ -147,7 +147,7 @@ export const ChartRepoSelector = ({ return ( (isExternal || isUpdate) && ( -
    +
    Helm Chart diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx index 18fc5cdd77..eda5513243 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx @@ -86,7 +86,6 @@ export const ChartEnvironmentSelector = ({ isVirtualEnvironmentOnSelector, isVirtualEnvironment, }: ChartEnvironmentSelectorType): JSX.Element => { - const renderVirtualEnvironmentInfoText = (): JSX.Element => { if (isVirtualEnvironmentOnSelector && VirtualEnvSelectionInfoText) { return @@ -127,7 +126,7 @@ export const ChartEnvironmentSelector = ({ )}
    ) : ( -
    +
    ) : ( -
    -
    - -

    - Cannot be changed after deployment -

    - -
    +
    + How do you want to deploy? + + This cannot be changed after deployment
    ) } @@ -452,7 +446,7 @@ export const ChartProjectSelector = ({ invalidProject, }: ChartProjectSelectorType): JSX.Element => { return ( -
    +
    { - const selectOptions = chartVersionsData.map(chartVersion => ({ + const selectOptions = chartVersionsData.map((chartVersion) => ({ value: chartVersion.id, label: chartVersion.version, })) @@ -483,7 +477,7 @@ export const ChartVersionSelector = ({ ) return ( -
    +
    label="Chart Version" inputId="chart-values-selector" @@ -565,11 +559,17 @@ export const ChartValuesSelector = ({ const handleChange: SelectPickerProps['onChange'] = (selectedOption) => handleChartValuesSelection(selectedOption.value) - const selectedOption = selectOptions.flatMap(groupedOption => groupedOption.options).find(option => getOptionValue(option) === getOptionValue({ - // Setting label null since the getOptionValue is not consuming it - label: null, - value: chartValues - })) + const selectedOption = selectOptions + .flatMap((groupedOption) => groupedOption.options) + .find( + (option) => + getOptionValue(option) === + getOptionValue({ + // Setting label null since the getOptionValue is not consuming it + label: null, + value: chartValues, + }), + ) return ( @@ -623,14 +623,13 @@ export const ChartVersionValuesSelector = ({ export const ActiveReadmeColumn = ({ fetchingReadMe, activeReadMe }: ActiveReadmeColumnProps) => { return (
    -
    +
    Readme
    - {fetchingReadMe ? ( - - ) : ( - - )} + {fetchingReadMe ? : }
    ) } @@ -728,6 +727,7 @@ export const AppNameInput = ({ data-testid="app-name-input" isRequiredField error={invalidAppName && (invalidAppNameMessage || REQUIRED_FIELD_MSG)} + rootClassName="h-32" />
    ) diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.scss b/src/components/v2/values/chartValuesDiff/ChartValuesView.scss index 73ec0c7744..7cbf31f636 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.scss +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.scss @@ -346,3 +346,18 @@ .manifest-repo-link { white-space: nowrap; } + +.chart-values-deployment-radio { + .form__radio-group{ + border: none; + } + + .form__radio-item { + border-right: none; + padding: 4px; + } + + .form__radio-item-content{ + padding: 0px; + } +} \ No newline at end of file diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx index 1098566573..fdbb8a9678 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.tsx @@ -1626,193 +1626,194 @@ const ChartValuesView = ({ > {renderValuesTabsContainer()}
    -
    - {isCreateValueView && ( - - )} - {isDeployChartView && ( - - )} - - {!isDeployChartView && !isCreateValueView && ( -
    -
    - Project +
    +
    + {isCreateValueView && ( + + )} + {isDeployChartView && ( + + )} + {!isDeployChartView && !isCreateValueView && ( +
    +
    + Project +
    +
    + {appMetaInfo?.projectName ? appMetaInfo.projectName : 'unassigned'} + +
    -
    - {appMetaInfo?.projectName ? appMetaInfo.projectName : 'unassigned'} - +
    -
    - )} - - {!isDeployChartView && showUpdateAppModal && !isCreateValueView && ( -
    - -
    - )} - - {isDeployChartView && ( - - )} - {(isDeployChartView || - (!isDeployChartView && (isExternalApp || commonState.selectedEnvironment))) && ( - - )} - {!window._env_.HIDE_GITOPS_OR_HELM_OPTION && showDeploymentTools && ( - - )} - {allowedCustomBool && showDeploymentTools && ( - - )} - {/** - * ChartRepoSelector will be displayed only when, - * - It's not a deploy chart view - * - It's not an external app values view - * - It's an external app which is, - * i. Already linked to a chart repo - * ii. Not already linked but connect to repo action is performed (showRepoSelector is set to true) - */} - {!isDeployChartView && - (!isExternalApp || commonState.installedAppInfo || commonState.showRepoSelector) && ( - )} - - {!isDeployChartView && - isExternalApp && - !commonState.installedAppInfo && - !commonState.showRepoSelector && ( - )} - {renderGeneratedDownloadManifest()} - {(!isExternalApp || - commonState.installedAppInfo || - commonState.repoChartValue?.chartRepoName) && ( - _chartVersion.id === commonState.selectedVersion, - )} - chartValuesList={chartValuesList} - chartValues={commonState.chartValues} - redirectToChartValues={redirectToChartValues} - handleChartValuesSelection={handleChartValuesSelection} - hideVersionFromLabel={ - isExternalApp && - !commonState.installedAppInfo && - commonState.chartValues.kind === ChartKind.EXISTING - } - hideCreateNewOption={isCreateValueView} - /> - )} - {window._env_.ENABLE_RESOURCE_SCAN_V2 && - !isExternalApp && - (isDeployChartView || isUpdateAppView) && - ToggleSecurityScan && ( - )} - {!isDeployChartView && - chartValueId !== '0' && - !( - deployedAppDetail && - checkIfDevtronOperatorHelmRelease( - deployedAppDetail[2], - deployedAppDetail[1], - deployedAppDetail[0], - ) - ) && ( - +
    +
    + {/** + * ChartRepoSelector will be displayed only when, + * - It's not a deploy chart view + * - It's not an external app values view + * - It's an external app which is, + * i. Already linked to a chart repo + * ii. Not already linked but connect to repo action is performed (showRepoSelector is set to true) + */} + {!isDeployChartView && + (!isExternalApp || commonState.installedAppInfo || commonState.showRepoSelector) && ( + + )} + {!isDeployChartView && + isExternalApp && + !commonState.installedAppInfo && + !commonState.showRepoSelector && ( + + )} + {renderGeneratedDownloadManifest()} + {(!isExternalApp || + commonState.installedAppInfo || + commonState.repoChartValue?.chartRepoName) && ( + _chartVersion.id === commonState.selectedVersion, + )} + chartValuesList={chartValuesList} + chartValues={commonState.chartValues} + redirectToChartValues={redirectToChartValues} + handleChartValuesSelection={handleChartValuesSelection} + hideVersionFromLabel={ + isExternalApp && + !commonState.installedAppInfo && + commonState.chartValues.kind === ChartKind.EXISTING + } + hideCreateNewOption={isCreateValueView} /> )} +
    + {window._env_.ENABLE_RESOURCE_SCAN_V2 && + !isExternalApp && + (isDeployChartView || isUpdateAppView) && + ToggleSecurityScan && ( + + )} + {!isDeployChartView && + chartValueId !== '0' && + !( + deployedAppDetail && + checkIfDevtronOperatorHelmRelease( + deployedAppDetail[2], + deployedAppDetail[1], + deployedAppDetail[0], + ) + ) && ( + + )}
    {commonState.openReadMe && ( Date: Thu, 21 Nov 2024 11:24:36 +0530 Subject: [PATCH 19/52] chore: document body replaced with ref --- .../appDetails/sourceInfo/environment.type.ts | 2 ++ .../environmentStatus/ChartUsedCard.tsx | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/v2/appDetails/sourceInfo/environment.type.ts b/src/components/v2/appDetails/sourceInfo/environment.type.ts index daccdcf3f8..340b0ef663 100644 --- a/src/components/v2/appDetails/sourceInfo/environment.type.ts +++ b/src/components/v2/appDetails/sourceInfo/environment.type.ts @@ -16,6 +16,7 @@ import { AppEnvironment as BaseAppEnvironmentType } from '@devtron-labs/devtron-fe-common-lib' import { TooltipProps } from '@devtron-labs/devtron-fe-common-lib/dist/Common/Tooltip/types' +import React from 'react' import { DeploymentStatusDetailsBreakdownDataType } from '../../../app/details/appDetails/appDetails.type' import { HelmReleaseStatus } from '../../../external-apps/ExternalAppService' import { AppDetails } from '../appDetails.type' @@ -58,4 +59,5 @@ export interface HelmAppConfigApplyStatusCardType { export interface ChartToolTipType extends Pick { children: TooltipProps['children'] isDeprecated: boolean + chartRef: React.RefObject } diff --git a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx index d2988fa789..77ad58c32e 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx @@ -16,6 +16,7 @@ import { Link } from 'react-router-dom' import Tippy from '@tippyjs/react' +import { useRef } from 'react' import { ReactComponent as QuestionIcon } from '../../../assets/icons/ic-question.svg' import { ReactComponent as File } from '../../../../../assets/icons/ic-file.svg' import { ReactComponent as DefaultChart } from '../../../../../assets/icons/ic-default-chart.svg' @@ -24,26 +25,32 @@ import { ChartToolTipType, ChartUsedCardType } from '../environment.type' import LoadingCard from '../../../../app/details/appDetails/LoadingCard' import { getUsedChartContent } from '../utils' -const ChartToolTip = ({ children, isDeprecated, onClickUpgrade }: ChartToolTipType) => ( +const ChartToolTip = ({ children, isDeprecated, onClickUpgrade, chartRef }: ChartToolTipType) => ( document.body} // To fix the issue of tippy not showing the content outside the container + appendTo={() => chartRef.current} // To fix the issue of tippy not showing the content outside the container > {children} ) const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading, onClickUpgrade }: ChartUsedCardType) => { + const chartRef = useRef(null) + if (cardLoading) { return } return ( -
    +
    @@ -53,7 +60,11 @@ const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading, onCli > Chart {appDetails.deprecated ? 'deprecated' : 'used'}
    - +
    From 0a675224952d5786add1275008c76bb0e0a7cd0f Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Thu, 21 Nov 2024 11:35:31 +0530 Subject: [PATCH 20/52] fix: add error icon for errors column & colors in status column --- .../ResourceList/BaseResourceList.tsx | 37 ++++++++++++++++--- src/css/base.scss | 2 + 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index 519cb9d081..bd0de1b667 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -33,6 +33,7 @@ import searchWorker from '@Config/searchWorker' import { URLS } from '@Config/routes' import { deleteNodeCapacity } from '@Components/ClusterNodes/clusterNodes.service' import NodeActionsMenu from '@Components/ResourceBrowser/ResourceList/NodeActionsMenu' +import { ReactComponent as ICError } from '@Icons/ic-error.svg' import ResourceListEmptyState from './ResourceListEmptyState' import { ALL_NAMESPACE_OPTION, @@ -377,8 +378,11 @@ const BaseResourceListContent = ({ const getStatusClass = (status: string) => { let statusPostfix = status?.toLowerCase() - if (statusPostfix && (statusPostfix.includes(':') || statusPostfix.includes('/'))) { - statusPostfix = statusPostfix.replace(':', '__').replace('/', '__') + if ( + statusPostfix && + (statusPostfix.includes(':') || statusPostfix.includes('/') || statusPostfix.includes(' ')) + ) { + statusPostfix = statusPostfix.replace(':', '__').replace('/', '__').replace(' ', '__') } return `f-${statusPostfix}` @@ -400,6 +404,8 @@ const BaseResourceListContent = ({ const gvkFromRawData = lowercaseKindToResourceGroupMap[lowercaseKind]?.gvk ?? ({} as GVKType) // Redirection and actions are not possible for Events since the required data for the same is not available const shouldShowRedirectionAndActions = lowercaseKind !== Nodes.Event.toLowerCase() + const isNodeUnschedulable = isNodeListing && !!resourceData.unschedulable + const isNodeListingAndNodeHasErrors = isNodeListing && !!resourceData[NODE_LIST_HEADERS_TO_KEY_MAP.errors] return (
    - + {columnName === 'errors' && isNodeListingAndNodeHasErrors && ( + + )} + + {columnName === 'status' && isNodeUnschedulable && ( + <> + + + SchedulingDisabled + + + )} {columnName === 'restarts' && Number(resourceData.restarts) !== 0 && diff --git a/src/css/base.scss b/src/css/base.scss index f46b5056e8..92ba37123a 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -819,6 +819,7 @@ button.anchor { &.f-completed, &.f-bound, &.f-active, + &.f-ready, &.f-created, &.f-scalingreplicasetdown { color: var(--G500); @@ -846,6 +847,7 @@ button.anchor { &.f-crashloopbackoff, &.f-init__crashloopbackoff, &.f-deleted, + &.f-not__ready, &.f-evicted { color: var(--R500); } From e81f3008bc7cea7df05056549d2e7f05ba90e807 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Thu, 21 Nov 2024 13:37:18 +0530 Subject: [PATCH 21/52] refactor: use select picker in column selector & minor css improvements --- .../CIPipelineN/ciPipeline.utils.tsx | 34 --- .../ResourceList/BaseResourceList.tsx | 16 +- .../ResourceList/ColumnSelector.tsx | 194 +++++------------- .../ResourceBrowser/ResourceList/utils.tsx | 8 +- src/components/ResourceBrowser/Types.ts | 11 +- src/css/base.scss | 1 + 6 files changed, 73 insertions(+), 191 deletions(-) diff --git a/src/components/CIPipelineN/ciPipeline.utils.tsx b/src/components/CIPipelineN/ciPipeline.utils.tsx index 5ef1096964..fec2996a05 100644 --- a/src/components/CIPipelineN/ciPipeline.utils.tsx +++ b/src/components/CIPipelineN/ciPipeline.utils.tsx @@ -172,40 +172,6 @@ export const outputFormatSelectStyle = { }), } -export const containerImageSelectStyles = { - ...baseSelectStyles, - control: (base, state) => ({ - ...base, - border: '1px solid var(--N200)', - boxShadow: 'none', - minHeight: 'auto', - borderRadius: '4px', - height: '32px', - fontSize: '12px', - pointerEvents: 'auto', - cursor: state.isDisabled ? 'not-allowed' : 'pointer', - }), - valueContainer: (base, state) => ({ - ...base, - color: 'var(--N900)', - background: 'var(--N50) !important', - padding: '0px 10px', - display: 'flex', - height: '30px', - borderTopLeftRadius: '4px', - borderBottomLeftRadius: '4px', - fontSize: '12px', - width: '100px', - whiteSpace: 'nowrap', - }), - indicatorsContainer: (base, state) => ({ - ...base, - background: 'var(--N50) !important', - borderTopRightRadius: '4px', - borderBottomRightRadius: '4px', - }), -} - export const CiPipelineSourceTypeBaseOptions = [ { label: 'Branch Fixed', diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index bd0de1b667..e6d279b6c8 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -33,12 +33,14 @@ import searchWorker from '@Config/searchWorker' import { URLS } from '@Config/routes' import { deleteNodeCapacity } from '@Components/ClusterNodes/clusterNodes.service' import NodeActionsMenu from '@Components/ResourceBrowser/ResourceList/NodeActionsMenu' -import { ReactComponent as ICError } from '@Icons/ic-error.svg' +import { ReactComponent as ICErrorExclamation } from '@Icons/ic-error-exclamation.svg' import ResourceListEmptyState from './ResourceListEmptyState' import { ALL_NAMESPACE_OPTION, DEFAULT_K8SLIST_PAGE_SIZE, K8S_EMPTY_GROUP, + MANDATORY_NODE_LIST_HEADERS, + NODE_LIST_HEADERS, NODE_LIST_HEADERS_TO_KEY_MAP, RESOURCE_EMPTY_PAGE_STATE, RESOURCE_LIST_EMPTY_STATE, @@ -129,7 +131,13 @@ const BaseResourceListContent = ({ const visibleColumnsSet = new Set(visibleColumns) - return resourceList?.headers.filter((header) => visibleColumnsSet.has(header)) ?? [] + return ( + resourceList?.headers.filter( + (header) => + MANDATORY_NODE_LIST_HEADERS.includes(header as (typeof NODE_LIST_HEADERS)[number]) || + visibleColumnsSet.has(header), + ) ?? [] + ) }, [resourceList, visibleColumns, isNodeListing]) const { gridTemplateColumns, handleResize } = useResizableTableConfig({ @@ -385,7 +393,7 @@ const BaseResourceListContent = ({ statusPostfix = statusPostfix.replace(':', '__').replace('/', '__').replace(' ', '__') } - return `f-${statusPostfix}` + return `f-${statusPostfix} ${isNodeListing ? 'dc__capitalize' : ''}` } const handleResourceClick = (e) => onResourceClick(e, shouldOverrideSelectedResourceKind) @@ -503,7 +511,7 @@ const BaseResourceListContent = ({ wrap={getRenderNodeButton(resourceData, columnName, handleNodeClick)} > {columnName === 'errors' && isNodeListingAndNodeHasErrors && ( - + )} (null) - -export function useColumnFilterContext() { - const context = React.useContext(ColumnFilterContext) - - if (!context) { - throw new Error(`cannot be rendered outside the component`) - } - - return context -} - -const ValueContainer = (props: ValueContainerProps) => { - const { getValue, selectProps, children } = props - const { length } = getValue() - - return ( - - {length > 0 ? ( - <> - {!selectProps.menuIsOpen && ( - <> - - Columns - - )} - {React.cloneElement(children[1])} - - ) : ( - children - )} - - ) -} - -const MenuList = (props: MenuListProps) => { - const { selectedColumns, setVisibleColumns, setIsMenuOpen, selectRef } = useColumnFilterContext() - - const { children } = props - - const handleApplySelectedColumns = (): void => { - setIsMenuOpen(false) - - const newVisibleColumns = selectedColumns.map((option) => option.value) - - if (typeof Storage !== 'undefined') { - saveAppliedColumnsInLocalStorage(newVisibleColumns) - } - - selectRef.current?.blur() - - setVisibleColumns(newVisibleColumns) - } - - return ( - - {children} - -
    - -
    -
    - ) -} - const ColumnSelector = ({ setVisibleColumns, visibleColumns }: ColumnSelectorType) => { const columnOptions = useMemo( () => @@ -101,14 +33,16 @@ const ColumnSelector = ({ setVisibleColumns, visibleColumns }: ColumnSelectorTyp ) const [isMenuOpen, setIsMenuOpen] = useState(false) - const [selectedColumns, setSelectedColumns] = useState( + const [selectedColumns, setSelectedColumns] = useState>>( visibleColumns.map((column) => ({ value: column, label: column })), ) - const selectRef: ColumnFilterContextType['selectRef'] = useRef() + const selectRef = useRef, true>>(null) const handleMenuOpen = () => { setIsMenuOpen(true) + + selectRef.current?.focus() } const handleMenuClose = () => { @@ -117,74 +51,54 @@ const ColumnSelector = ({ setVisibleColumns, visibleColumns }: ColumnSelectorTyp selectRef.current?.blur() } - const columnFilterProviderValue: ColumnFilterContextType = useMemo( - () => ({ - isMenuOpen, - setIsMenuOpen, - selectedColumns, - setSelectedColumns, - selectRef, - setVisibleColumns, - }), - [isMenuOpen, selectedColumns], + const handleApplySelectedColumns = (): void => { + setIsMenuOpen(false) + + const newVisibleColumns = selectedColumns.map((option) => option.value) + + if (typeof Storage !== 'undefined') { + saveAppliedColumnsInLocalStorage(newVisibleColumns) + } + + selectRef.current?.blur() + + setVisibleColumns(newVisibleColumns) + } + + const renderMenuListFooter = () => ( +
    +
    ) return ( - - ({ - ...base, - zIndex: 6, - }), - menuList: (base) => ({ - ...base, - borderRadius: '4px', - paddingTop: 0, - paddingBottom: 0, - }), - option: (base, state) => ({ - ...base, - padding: '10px 0', - backgroundColor: state.isFocused ? 'var(--N100) !important' : 'var(--N0) !important', - color: 'var(--N900)', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - cursor: 'pointer', - }), - dropdownIndicator: (base, state) => ({ - ...base, - color: 'var(--N400)', - transition: 'all .2s ease', - transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : 'rotate(0deg)', - padding: '0 8px', - }), - }} - /> - + } + isSearchable + menuIsOpen={isMenuOpen} + onMenuOpen={handleMenuOpen} + onMenuClose={handleMenuClose} + isMulti + onChange={setSelectedColumns} + placeholder="Column" + options={columnOptions} + value={selectedColumns} + renderMenuListFooter={renderMenuListFooter} + isClearable={false} + /> ) } diff --git a/src/components/ResourceBrowser/ResourceList/utils.tsx b/src/components/ResourceBrowser/ResourceList/utils.tsx index 7b9f9d849f..cb2a508ffb 100644 --- a/src/components/ResourceBrowser/ResourceList/utils.tsx +++ b/src/components/ResourceBrowser/ResourceList/utils.tsx @@ -1,9 +1,9 @@ import { noop } from '@devtron-labs/devtron-fe-common-lib' import { - NODE_LIST_HEADERS, TARGET_K8S_VERSION_SEARCH_KEY, LOCAL_STORAGE_EXISTS, LOCAL_STORAGE_KEY_FOR_APPLIED_COLUMNS, + OPTIONAL_NODE_LIST_HEADERS, } from '../Constants' import { ResourceListUrlFiltersType } from './types' @@ -13,7 +13,8 @@ export const parseSearchParams = (searchParams: URLSearchParams) => ({ export const getAppliedColumnsFromLocalStorage = () => { if (!LOCAL_STORAGE_EXISTS) { - return [...NODE_LIST_HEADERS] + // NOTE: show all headers by default + return [...OPTIONAL_NODE_LIST_HEADERS] } try { @@ -25,7 +26,8 @@ export const getAppliedColumnsFromLocalStorage = () => { return appliedColumns } catch { - return [...NODE_LIST_HEADERS] + // NOTE: show all headers by default + return [...OPTIONAL_NODE_LIST_HEADERS] } } diff --git a/src/components/ResourceBrowser/Types.ts b/src/components/ResourceBrowser/Types.ts index 038d6c6f10..7de7a62872 100644 --- a/src/components/ResourceBrowser/Types.ts +++ b/src/components/ResourceBrowser/Types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { RefObject } from 'react' +import React from 'react' import { K8SObjectBaseType, ResponseType, @@ -26,7 +26,6 @@ import { K8sResourceDetailType, K8sResourceDetailDataType, } from '@devtron-labs/devtron-fe-common-lib' -import { SelectInstance } from 'react-select' import { LogSearchTermType, SelectedResourceType } from '../v2/appDetails/appDetails.type' import { ClusterDetail, ResourceDetail, ClusterListType } from '../ClusterNodes/types' import { useTabs } from '../common/DynamicTabs' @@ -319,14 +318,6 @@ export enum NODE_SEARCH_KEYS { export interface ColumnSelectorType extends Pick {} -export interface ColumnFilterContextType extends Pick { - selectedColumns: OptionType[] - setSelectedColumns: React.Dispatch> - isMenuOpen: boolean - setIsMenuOpen: React.Dispatch> - selectRef: RefObject -} - export interface NodeActionsMenuProps { addTab: ReturnType['addTab'] nodeData: K8sResourceDetailDataType diff --git a/src/css/base.scss b/src/css/base.scss index 92ba37123a..22a8008381 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -817,6 +817,7 @@ button.anchor { &.f-sync.ok, &.f-running, &.f-completed, + &.f-complete, &.f-bound, &.f-active, &.f-ready, From f90fbba809788ec7e1a4f2b70c59a8e3837629ce Mon Sep 17 00:00:00 2001 From: Eshank Vaish <48060426+eshankvaish@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:48:49 +0530 Subject: [PATCH 22/52] chore: version bump --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 48a1dcb13f..0a797f13e8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.1.0", + "@devtron-labs/devtron-fe-common-lib": "1.1.1", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index 9db84736a1..e8eaf7aaed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.0.tgz#2a4bcc8ddabf911771ef554409480dd32a9c485f" - integrity sha512-J/5B0g1angmHgg0pSW/zSLxjcVC8je0lGSPxvrXeAKZl0l1WyS2nauKDtCdP3UbVYrhIQIyU1kbsBfKqpDxaTw== +"@devtron-labs/devtron-fe-common-lib@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.1.tgz#e2da32013d357ec76c8e17ce26a6b286c9027cd5" + integrity sha512-BAjP+B19CnxRAHVZ4oZHH0TpMT624YbA4Ft8vgvd4RM6KRoXO7qDNTUkqJL/kV5mLBRq/NiPiMoLrkQW8SgQtQ== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" From ee173918f521bf506845fbda1059cdffcdde6754 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Thu, 21 Nov 2024 18:29:44 +0530 Subject: [PATCH 23/52] fix: review comments --- .eslintignore | 3 - .../ResourceBrowser.service.tsx | 48 ++++++++++++-- .../ResourceList/BaseResourceList.tsx | 15 +++-- .../ResourceList/ColumnSelector.tsx | 2 +- .../ResourceList/K8SResourceList.tsx | 62 +++--------------- .../ResourceList/NodeActionsMenu.tsx | 63 +++++++++++-------- .../ResourceList/NodeListSearchFilter.tsx | 44 +++++++------ .../ResourceBrowser/ResourceList/types.ts | 1 - src/components/ResourceBrowser/Types.ts | 14 +++-- src/components/ResourceBrowser/Utils.tsx | 29 ++++++--- src/config/searchWorker.ts | 41 ++++-------- 11 files changed, 171 insertions(+), 151 deletions(-) diff --git a/.eslintignore b/.eslintignore index c86d7dc00e..18b5e6d16c 100755 --- a/.eslintignore +++ b/.eslintignore @@ -66,7 +66,6 @@ src/components/ClusterNodes/ClusterEvents.tsx src/components/ClusterNodes/ClusterManifest.tsx src/components/ClusterNodes/ClusterNodeEmptyStates.tsx src/components/ClusterNodes/ClusterOverview.tsx -src/components/ClusterNodes/ColumnSelector.tsx src/components/ClusterNodes/NodeActions/CordonNodeModal.tsx src/components/ClusterNodes/NodeActions/DeleteNodeModal.tsx src/components/ClusterNodes/NodeActions/DrainNodeModal.tsx @@ -74,8 +73,6 @@ src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx src/components/ClusterNodes/NodeActions/validationRules.ts src/components/ClusterNodes/NodeDetails.tsx -src/components/ClusterNodes/NodeDetailsList.tsx -src/components/ClusterNodes/NodeListSearchFilter.tsx src/components/ClusterNodes/__tests__/ClusterManifest.test.tsx src/components/ClusterNodes/__tests__/NodeList.test.tsx src/components/ClusterNodes/constants.ts diff --git a/src/components/ResourceBrowser/ResourceBrowser.service.tsx b/src/components/ResourceBrowser/ResourceBrowser.service.tsx index 09b787e010..1e8f76f861 100644 --- a/src/components/ResourceBrowser/ResourceBrowser.service.tsx +++ b/src/components/ResourceBrowser/ResourceBrowser.service.tsx @@ -21,6 +21,10 @@ import { ResponseType, ApiResourceGroupType, convertJSONPointerToJSONPath, + getK8sResourceList, + getIsRequestAborted, + showError, + getUrlWithSearchParams, } from '@devtron-labs/devtron-fe-common-lib' import { getManifestResource, @@ -31,8 +35,16 @@ import { JSONPath } from 'jsonpath-plus' import { SelectedResourceType } from '@Components/v2/appDetails/appDetails.type' import { Routes } from '../../config' import { ClusterListResponse } from '../../services/service.types' -import { CreateResourcePayload, CreateResourceResponse, NodeListResponse, ResourceListPayloadType } from './Types' -import { ALL_NAMESPACE_OPTION } from './Constants' +import { + CreateResourcePayload, + CreateResourceResponse, + GetResourceDataType, + NodeRowDetail, + ResourceListPayloadType, + URLParams, +} from './Types' +import { ALL_NAMESPACE_OPTION, SIDEBAR_KEYS } from './Constants' +import { parseNodeList } from './Utils' export const getClusterList = (): Promise => get(Routes.CLUSTER_LIST_PERMISSION) @@ -141,5 +153,33 @@ export const restartWorkload = async (resource: SelectedResourceType, signal: Ab await updateManifestResourceHelmApps(null, '', '', JSON.stringify(manifest), true, resource, signal) } -export const getNodeList = (clusterId: string, signal?: AbortSignal): Promise => - get(`${Routes.NODE_LIST}?clusterId=${clusterId}`, { signal }) +export const getNodeList = (clusterId: string, signal?: AbortSignal): Promise> => + get(getUrlWithSearchParams(Routes.NODE_LIST, { clusterId: Number(clusterId) }), { signal }) + +export const getResourceData = async ({ + selectedResource, + selectedNamespace, + clusterId, + filters, + abortControllerRef, +}: GetResourceDataType) => { + try { + if (selectedResource.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind) { + const response = await getNodeList(clusterId, abortControllerRef.current.signal) + + return parseNodeList(response) + } + + return await getK8sResourceList( + getResourceListPayload(clusterId, selectedNamespace.value.toLowerCase(), selectedResource, filters), + abortControllerRef.current.signal, + ) + } catch (err) { + if (!getIsRequestAborted(err)) { + showError(err) + throw err + } + + return null + } +} diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index 519cb9d081..23949a2ec3 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -38,7 +38,9 @@ import { ALL_NAMESPACE_OPTION, DEFAULT_K8SLIST_PAGE_SIZE, K8S_EMPTY_GROUP, + NODE_K8S_VERSION_FILTER_KEY, NODE_LIST_HEADERS_TO_KEY_MAP, + NODE_SEARCH_KEYS_TO_OBJECT_KEYS, RESOURCE_EMPTY_PAGE_STATE, RESOURCE_LIST_EMPTY_STATE, RESOURCE_PAGE_SIZE_OPTIONS, @@ -88,7 +90,6 @@ const BaseResourceListContent = ({ setWidgetEventDetails, lowercaseKindToResourceGroupMap, handleResourceClick: onResourceClick, - nodeK8sVersions, }: BaseResourceListProps) => { const [filteredResourceList, setFilteredResourceList] = useState(null) const [pageSize, setPageSize] = useState(DEFAULT_K8SLIST_PAGE_SIZE) @@ -122,13 +123,15 @@ const BaseResourceListContent = ({ } = useBulkSelection>() const headers = useMemo(() => { + const list = resourceList?.headers ?? [] + if (!isNodeListing) { - return resourceList?.headers ?? [] + return list } const visibleColumnsSet = new Set(visibleColumns) - return resourceList?.headers.filter((header) => visibleColumnsSet.has(header)) ?? [] + return list.filter((header) => visibleColumnsSet.has(header)) ?? [] }, [resourceList, visibleColumns, isNodeListing]) const { gridTemplateColumns, handleResize } = useResizableTableConfig({ @@ -196,6 +199,11 @@ const BaseResourceListContent = ({ nodeListingFilters: { isNodeListing, searchParams, + // NOTE!: these constants need to be passed to searchWorker since we cannot import + // stuff inside web worker + NODE_LIST_HEADERS_TO_KEY_MAP: structuredClone(NODE_LIST_HEADERS_TO_KEY_MAP), + NODE_SEARCH_KEYS_TO_OBJECT_KEYS: structuredClone(NODE_SEARCH_KEYS_TO_OBJECT_KEYS), + NODE_K8S_VERSION_KEY: structuredClone(NODE_K8S_VERSION_FILTER_KEY), }, origin: new URL(window.__BASE_URL__, window.location.href).origin, }, @@ -642,7 +650,6 @@ const BaseResourceListContent = ({ {isNodeListing ? ( - abortPreviousRequests(async () => { - try { - if (selectedResource.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind) { - const response = await getNodeList(clusterId, abortControllerRef.current.signal) - - return parseNodeList(response) - } - - return await getK8sResourceList( - getResourceListPayload( - clusterId, - selectedNamespace.value.toLowerCase(), - selectedResource, - filters, - ), - abortControllerRef.current.signal, - ) - } catch (err) { - if (!getIsRequestAborted(err)) { - showError(err) - throw err - } - - return null - } - }, abortControllerRef), + abortPreviousRequests( + async () => + getResourceData({ selectedResource, selectedNamespace, clusterId, filters, abortControllerRef }), + abortControllerRef, + ), [selectedResource, clusterId, selectedNamespace, filters], ) - const [, nodeK8sVersions] = useAsync(async () => { - if (selectedResource.gvk.Kind !== SIDEBAR_KEYS.nodeGVK.Kind) { - return [] - } - - try { - const { - result: { nodeK8sVersions: versions }, - } = await getClusterCapacity(clusterId) - - return versions - } catch { - return [] - } - }, [selectedResource, clusterId]) - const resourceListDataError = getIsRequestAborted(_resourceListDataError) ? null : _resourceListDataError const resourceList = useMemo(() => { @@ -158,7 +113,6 @@ export const K8SResourceList = ({ setWidgetEventDetails={setWidgetEventDetails} lowercaseKindToResourceGroupMap={lowercaseKindToResourceGroupMap} handleResourceClick={handleResourceClick} - nodeK8sVersions={nodeK8sVersions ?? []} > {PodRestart && } diff --git a/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx index dee905ddd3..a6a3a67b31 100644 --- a/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx +++ b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx @@ -139,6 +139,43 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP } } + const renderModal = () => { + if (showCordonNodeDialog) { + return ( + + ) + } + + if (showDrainNodeDialog) { + return + } + + if (showDeleteNodeDialog) { + return + } + + if (showEditTaintNodeDialog) { + return ( + + ) + } + + return null + } + return ( <> @@ -192,31 +229,7 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP
    - {showCordonNodeDialog && ( - - )} - {showDrainNodeDialog && ( - - )} - {showDeleteNodeDialog && ( - - )} - {showEditTaintNodeDialog && ( - - )} + {renderModal()} ) } diff --git a/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx index 7bee0923dd..17ea3006f9 100644 --- a/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx +++ b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx @@ -15,13 +15,14 @@ */ import { useState, useEffect, KeyboardEvent, ChangeEvent, useMemo, useRef, RefCallback } from 'react' -import { useLocation, useHistory } from 'react-router-dom' +import { useLocation, useHistory, useParams } from 'react-router-dom' import { ParsedQuery, parse as parseQueryString, stringify as stringifyQueryString } from 'query-string' -import { OptionType, SelectPicker, useRegisterShortcut } from '@devtron-labs/devtron-fe-common-lib' +import { OptionType, SelectPicker, useAsync, useRegisterShortcut } from '@devtron-labs/devtron-fe-common-lib' import { ReactComponent as ICSearch } from '@Icons/ic-search.svg' import { ReactComponent as ICClear } from '@Icons/ic-error.svg' +import { getClusterCapacity } from '@Components/ClusterNodes/clusterNodes.service' import ColumnSelector from './ColumnSelector' -import { NODE_SEARCH_KEYS, NodeListSearchFilterType } from '../Types' +import { NODE_SEARCH_KEYS, NodeListSearchFilterType, URLParams } from '../Types' import { ShortcutKeyBadge } from '../../common/formFields/Widgets/Widgets' import { DEFAULT_NODE_K8S_VERSION, @@ -31,12 +32,13 @@ import { } from '../Constants' const NodeListSearchFilter = ({ - nodeK8sVersions, visibleColumns, setVisibleColumns, isOpen, searchParams, }: NodeListSearchFilterType) => { + const { clusterId } = useParams() + const selectedSearchTextType: NODE_SEARCH_KEYS | '' = Object.values(NODE_SEARCH_KEYS).reduce((type, key) => { if (searchParams[key]) { return key @@ -66,20 +68,25 @@ const NodeListSearchFilter = ({ const location = useLocation() const { push } = useHistory() - const nodeK8sVersionOptions = useMemo( - () => [ - DEFAULT_NODE_K8S_VERSION, - ...(nodeK8sVersions?.map((version) => ({ - label: `K8s version: ${version}`, - value: version, - })) || []), - ], - [nodeK8sVersions], - ) + const [nodeK8sVersionsLoading, nodeK8sVersionOptions, nodeK8sVersionsError, refetchNodeK8sVersions] = + useAsync(async () => { + const { + result: { nodeK8sVersions: versions }, + } = await getClusterCapacity(clusterId) + + return [ + DEFAULT_NODE_K8S_VERSION, + ...(versions?.map((version) => ({ + label: `K8s version: ${version}`, + value: version, + })) || []), + ] + }, [clusterId]) const selectedK8sVersionOption = useMemo( () => - nodeK8sVersionOptions.find((option) => option.value === selectedK8sNodeVersion) ?? DEFAULT_NODE_K8S_VERSION, + nodeK8sVersionOptions?.find((option) => option.value === selectedK8sNodeVersion) ?? + DEFAULT_NODE_K8S_VERSION, [nodeK8sVersionOptions, selectedK8sNodeVersion], ) @@ -283,9 +290,10 @@ const NodeListSearchFilter = ({ diff --git a/src/components/ResourceBrowser/ResourceList/types.ts b/src/components/ResourceBrowser/ResourceList/types.ts index 8427e776a2..564b022dcc 100644 --- a/src/components/ResourceBrowser/ResourceList/types.ts +++ b/src/components/ResourceBrowser/ResourceList/types.ts @@ -45,7 +45,6 @@ export interface BaseResourceListProps * @default false */ shouldOverrideSelectedResourceKind?: boolean - nodeK8sVersions?: string[] } export interface ClusterUpgradeCompatibilityInfoProps diff --git a/src/components/ResourceBrowser/Types.ts b/src/components/ResourceBrowser/Types.ts index 038d6c6f10..5c63a1191d 100644 --- a/src/components/ResourceBrowser/Types.ts +++ b/src/components/ResourceBrowser/Types.ts @@ -30,6 +30,7 @@ import { SelectInstance } from 'react-select' import { LogSearchTermType, SelectedResourceType } from '../v2/appDetails/appDetails.type' import { ClusterDetail, ResourceDetail, ClusterListType } from '../ClusterNodes/types' import { useTabs } from '../common/DynamicTabs' +import { BaseResourceListProps } from './ResourceList/types' export interface K8SObjectType extends K8SObjectBaseType { child: ApiResourceGroupType[] @@ -300,12 +301,7 @@ export interface NodeRowDetail { age: string } -export interface NodeListResponse extends ResponseType { - result?: NodeRowDetail[] -} - export interface NodeListSearchFilterType extends Pick { - nodeK8sVersions: string[] visibleColumns: string[] setVisibleColumns: React.Dispatch> searchParams: Record @@ -332,3 +328,11 @@ export interface NodeActionsMenuProps { nodeData: K8sResourceDetailDataType getNodeListData: () => void } + +export interface GetResourceDataType { + selectedResource: ApiResourceGroupType + selectedNamespace: BaseResourceListProps['selectedNamespace'] + clusterId: string + filters: Record + abortControllerRef: RefObject +} diff --git a/src/components/ResourceBrowser/Utils.tsx b/src/components/ResourceBrowser/Utils.tsx index b426cdbef6..26ec47ea0c 100644 --- a/src/components/ResourceBrowser/Utils.tsx +++ b/src/components/ResourceBrowser/Utils.tsx @@ -23,6 +23,8 @@ import { GVKType, InitTabType, K8sResourceDetailDataType, + K8sResourceDetailType, + ResponseType, } from '@devtron-labs/devtron-fe-common-lib' import moment from 'moment' import { URLS, LAST_SEEN } from '../../config' @@ -43,7 +45,7 @@ import { K8SObjectMapType, K8SObjectType, K8sObjectOptionType, - NodeListResponse, + NodeRowDetail, } from './Types' import TerminalIcon from '../../assets/icons/ic-terminal-fill.svg' import K8ResourceIcon from '../../assets/icons/ic-object.svg' @@ -385,27 +387,38 @@ export const renderResourceValue = (value: string) => { return isDateValue ? moment(value).format(DATE_TIME_FORMAT_STRING) : value } -const flattenObject = (ob: object): object => { +/** + * Provided a js object we will return a flattened object such that the nested + * keys are all direct children created by joining each level using (.) + * + * Ex: given object = { x: 'a', y: { a: 'b' } } returns { x: 'a', 'y.a': b } + * + * @param ob any js object + * @returns object without any nesting; nested keys will be + */ +const flattenObject = (ob: object): Record => { const toReturn = {} - Object.keys(ob).forEach((i) => { - const currentElement = ob[i] + Object.entries(ob).forEach(([key, value]) => { + const currentElement = value if (typeof currentElement === 'object' && currentElement !== null && !Array.isArray(currentElement)) { const flatObject = flattenObject(currentElement) - Object.keys(flatObject).forEach((x) => { - toReturn[`${i}.${x}`] = flatObject[x] + Object.entries(flatObject).forEach(([flatObjectKey, flatObjectValue]) => { + toReturn[`${key}.${flatObjectKey}`] = flatObjectValue }) } else { - toReturn[i] = currentElement + toReturn[key] = currentElement } }) return toReturn } -export const parseNodeList = (response: NodeListResponse) => ({ +// NOTE: Please understand the big comment on @flattenObject to understand this +export const parseNodeList = (response: ResponseType): ResponseType => ({ + ...response, result: { headers: [...NODE_LIST_HEADERS] as string[], data: response.result.map((data) => { diff --git a/src/config/searchWorker.ts b/src/config/searchWorker.ts index a01407ad40..99fa7391d2 100644 --- a/src/config/searchWorker.ts +++ b/src/config/searchWorker.ts @@ -29,34 +29,12 @@ export default () => { NODE_GROUP = 'nodeGroup', } - // NOTE!: this is a copy from ResourceBrowser/Constants - // imports don't get bundled with the service worker - const NODE_SEARCH_KEYS_TO_OBJECT_KEYS = { - [NODE_SEARCH_KEYS.LABEL]: 'labels', - [NODE_SEARCH_KEYS.NAME]: 'name', - [NODE_SEARCH_KEYS.NODE_GROUP]: 'nodeGroup', - } - - const NODE_LIST_HEADERS_TO_KEY_MAP = { - name: 'name', - status: 'status', - roles: 'roles', - errors: 'errorCount', - 'k8s version': 'k8sVersion', - 'node group': 'nodeGroup', - pods: 'podCount', - taints: 'taintCount', - 'cpu usage (%)': 'cpu.usagePercentage', - 'cpu usage (absolute)': 'cpu.usage', - 'cpu allocatable': 'cpu.allocatable', - 'mem usage (%)': 'memory.usagePercentage', - 'mem usage (absolute)': 'memory.usageInBytes', - 'mem allocatable': 'memory.allocatable', - age: 'age', - unschedulable: 'unschedulable', - } - - const NODE_K8S_VERSION_KEY = 'k8sVersion' + /** + * These constants are being passed to WebWorker since we can't import from other files + */ + let NODE_SEARCH_KEYS_TO_OBJECT_KEYS = null + let NODE_LIST_HEADERS_TO_KEY_MAP = null + let NODE_K8S_VERSION_KEY = null const stringComparatorBySortOrder = (a: string, b: string, sortOrder: SortingOrder = SortingOrder.ASC) => sortOrder === SortingOrder.ASC ? a.localeCompare(b) : b.localeCompare(a) @@ -128,6 +106,7 @@ export default () => { 'cpu allocatable': numberInStringComparator, 'mem usage (%)': numberInStringComparator, 'mem allocatable': numberInStringComparator, + 'cpu usage (absolute)': numberInStringComparator, } /** @@ -266,6 +245,12 @@ export default () => { return } + if (e.data.payload?.nodeListingFilters) { + NODE_LIST_HEADERS_TO_KEY_MAP = e.data.payload.nodeListingFilters.NODE_LIST_HEADERS_TO_KEY_MAP + NODE_SEARCH_KEYS_TO_OBJECT_KEYS = e.data.payload.nodeListingFilters.NODE_SEARCH_KEYS_TO_OBJECT_KEYS + NODE_K8S_VERSION_KEY = e.data.payload.nodeListingFilters.NODE_K8S_VERSION_KEY + } + switch (e.data.type) { case 'start': if (e.data.payload.debounceResult) { From 2e3c6b808f1ff22af8af82f0e5f345253cabf9b3 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Thu, 21 Nov 2024 19:30:21 +0530 Subject: [PATCH 24/52] fix: sonarlint issues --- .../ResourceList/NodeActionsMenu.tsx | 77 ++++++++++------ .../ResourceList/NodeListSearchFilter.tsx | 23 +++-- src/config/searchWorker.ts | 87 +++++++++---------- 3 files changed, 108 insertions(+), 79 deletions(-) diff --git a/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx index a6a3a67b31..8634373e8d 100644 --- a/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx +++ b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx @@ -48,10 +48,10 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP const { url } = useRouteMatch() const location = useLocation() - const [showCordonNodeDialog, setCordonNodeDialog] = useState(false) - const [showDrainNodeDialog, setDrainNodeDialog] = useState(false) - const [showDeleteNodeDialog, setDeleteNodeDialog] = useState(false) - const [showEditTaintNodeDialog, setEditTaintNodeDialog] = useState(false) + const [showCordonNodeDialog, setShowCordonNodeDialog] = useState(false) + const [showDrainNodeDialog, setShowDrainNodeDialog] = useState(false) + const [showDeleteNodeDialog, setShowDeleteNodeDialog] = useState(false) + const [showEditTaintNodeDialog, setShowEditTaintNodeDialog] = useState(false) const { name, version, kind } = nodeData as Record @@ -89,12 +89,12 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP const showCordonNodeModal = (): void => { if (isAuthorized()) { - setCordonNodeDialog(true) + setShowCordonNodeDialog(true) } } const hideCordonNodeModal = (refreshData?: boolean): void => { - setCordonNodeDialog(false) + setShowCordonNodeDialog(false) if (refreshData) { getNodeListData() } @@ -102,12 +102,12 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP const showDrainNodeModal = (): void => { if (isAuthorized()) { - setDrainNodeDialog(true) + setShowDrainNodeDialog(true) } } const hideDrainNodeModal = (refreshData?: boolean): void => { - setDrainNodeDialog(false) + setShowDrainNodeDialog(false) if (refreshData) { getNodeListData() } @@ -115,12 +115,12 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP const showDeleteNodeModal = (): void => { if (isAuthorized()) { - setDeleteNodeDialog(true) + setShowDeleteNodeDialog(true) } } const hideDeleteNodeModal = (refreshData?: boolean): void => { - setDeleteNodeDialog(false) + setShowDeleteNodeDialog(false) if (refreshData) { getNodeListData() } @@ -128,12 +128,12 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP const showEditTaintsModal = (): void => { if (isAuthorized()) { - setEditTaintNodeDialog(true) + setShowEditTaintNodeDialog(true) } } const hideEditTaintsModal = (refreshData?: boolean): void => { - setEditTaintNodeDialog(false) + setShowEditTaintNodeDialog(false) if (refreshData) { getNodeListData() } @@ -176,6 +176,8 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP return null } + const menuListItemButtonClassName = 'flex left h-36 cursor pl-12 pr-12 dc__hover-n50 dc__transparent w-100' + return ( <> @@ -184,14 +186,21 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP
    - {CLUSTER_NODE_ACTIONS_LABELS.terminal} - - + + + +
    diff --git a/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx index 17ea3006f9..f332bd87d1 100644 --- a/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx +++ b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx @@ -215,9 +215,11 @@ const NodeListSearchFilter = ({ return (
    -
    @@ -246,24 +248,31 @@ const NodeListSearchFilter = ({ {!searchTextType && ( )} -
    + {isSearchKeySelectorOpen && ( <> -
    + ))}
    )} diff --git a/src/config/searchWorker.ts b/src/config/searchWorker.ts index 99fa7391d2..f3d98a8ef9 100644 --- a/src/config/searchWorker.ts +++ b/src/config/searchWorker.ts @@ -156,6 +156,47 @@ export default () => { } } + const isItemASearchMatchForNodeListing = (item: Record, searchParams: Record) => { + const isK8sVersionFilterAppliedAndMatchFound = + !searchParams[NODE_K8S_VERSION_KEY] || item[NODE_K8S_VERSION_KEY] === searchParams[NODE_K8S_VERSION_KEY] + + const doesAnyNodeSearchKeyExists = Object.values(NODE_SEARCH_KEYS).some((key) => + Object.hasOwn(searchParams, key), + ) + + const doesItemHaveAnyMatchingSearchKey = + !doesAnyNodeSearchKeyExists || + Object.values(NODE_SEARCH_KEYS).reduce((isFound, searchKey) => { + if (!searchParams[searchKey]) { + return isFound + } + + const searchTextFromSearchKey = searchParams[searchKey] + + return !!searchTextFromSearchKey?.split(',').some((text) => { + const trimmedText = text.trim() + const objectKey = NODE_SEARCH_KEYS_TO_OBJECT_KEYS[searchKey] + + // NOTE: if corresponding value in data is anything other than primitives like string, or number + // handle it appropriately likewise + if (searchKey === NODE_SEARCH_KEYS.LABEL) { + const [searchKeyFromLabelText, searchValueFromLabelText] = trimmedText.split('=') + + return ( + !!item[objectKey]?.some( + ({ key, value }) => + key === searchKeyFromLabelText && value === searchValueFromLabelText, + ) && isFound + ) + } + + return String(item[objectKey] ?? '').includes(trimmedText) && isFound + }) + }, true) + + return isK8sVersionFilterAppliedAndMatchFound && doesItemHaveAnyMatchingSearchKey + } + const getFilteredList = ({ searchText, list, @@ -176,51 +217,7 @@ export default () => { let filteredList = [...list] if (isNodeListing) { - filteredList = list.filter((item: Record) => { - const isK8sVersionFilterAppliedAndMatchFound = - !searchParams[NODE_K8S_VERSION_KEY] || - item[NODE_K8S_VERSION_KEY] === searchParams[NODE_K8S_VERSION_KEY] - - const doesAnyNodeSearchKeyExists = Object.values(NODE_SEARCH_KEYS).some((key) => - Object.hasOwn(searchParams, key), - ) - - const doesItemHaveAnyMatchingSearchKey = Object.values(NODE_SEARCH_KEYS).reduce( - (isFound, searchKey) => { - if (!searchParams[searchKey]) { - return isFound - } - - const searchTextFromSearchKey = searchParams[searchKey] - - return !!searchTextFromSearchKey?.split(',').some((text) => { - const trimmedText = text.trim() - const objectKey = NODE_SEARCH_KEYS_TO_OBJECT_KEYS[searchKey] - - // NOTE: if corresponding value in data is anything other than primitives like string, or number - // handle it appropriately likewise - if (searchKey === NODE_SEARCH_KEYS.LABEL) { - const [searchKeyFromLabelText, searchValueFromLabelText] = trimmedText.split('=') - - return ( - !!item[objectKey]?.some( - ({ key, value }) => - key === searchKeyFromLabelText && value === searchValueFromLabelText, - ) && isFound - ) - } - - return String(item[objectKey] ?? '').includes(trimmedText) && isFound - }) - }, - true, - ) - - return ( - isK8sVersionFilterAppliedAndMatchFound && - (!doesAnyNodeSearchKeyExists || doesItemHaveAnyMatchingSearchKey) - ) - }) + filteredList = list.filter((item) => isItemASearchMatchForNodeListing(item, searchParams)) } else if (searchTextLowerCased !== '' && list?.length) { filteredList = list.filter((item) => Object.entries(item).some( From 3bcbd946ff860d90bd26ec2310bc5db2e8124985 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Thu, 21 Nov 2024 20:04:29 +0530 Subject: [PATCH 25/52] chore: minor code refactorings suggested in review --- .../ResourceBrowser/ResourceList/BaseResourceList.tsx | 4 ++-- src/components/ResourceBrowser/Utils.tsx | 8 +++----- src/config/searchWorker.ts | 4 ++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index 23949a2ec3..a46d5b0a48 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -156,11 +156,11 @@ const BaseResourceListContent = ({ const initialSortKey = useMemo(() => { // NOTE: if isEventList don't initiate sort since we already sort it; therefore return empty initialSortKey if (resourceList && !isEventList) { - const isNameSpaceColumnPresent = resourceList.headers.some((header) => header === 'namespace') + const isNameSpaceColumnPresent = headers.some((header) => header === 'namespace') return isNameSpaceColumnPresent ? 'namespace' : 'name' } return '' - }, [resourceList, isEventList]) + }, [headers, isEventList]) // SORTING HOOK const { sortBy, sortOrder, handleSorting, clearFilters } = useStateFilters({ initialSortKey }) diff --git a/src/components/ResourceBrowser/Utils.tsx b/src/components/ResourceBrowser/Utils.tsx index 26ec47ea0c..a65a6b5479 100644 --- a/src/components/ResourceBrowser/Utils.tsx +++ b/src/components/ResourceBrowser/Utils.tsx @@ -400,16 +400,14 @@ const flattenObject = (ob: object): Record => { const toReturn = {} Object.entries(ob).forEach(([key, value]) => { - const currentElement = value - - if (typeof currentElement === 'object' && currentElement !== null && !Array.isArray(currentElement)) { - const flatObject = flattenObject(currentElement) + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + const flatObject = flattenObject(value) Object.entries(flatObject).forEach(([flatObjectKey, flatObjectValue]) => { toReturn[`${key}.${flatObjectKey}`] = flatObjectValue }) } else { - toReturn[key] = currentElement + toReturn[key] = value } }) diff --git a/src/config/searchWorker.ts b/src/config/searchWorker.ts index f3d98a8ef9..920424ade9 100644 --- a/src/config/searchWorker.ts +++ b/src/config/searchWorker.ts @@ -160,6 +160,10 @@ export default () => { const isK8sVersionFilterAppliedAndMatchFound = !searchParams[NODE_K8S_VERSION_KEY] || item[NODE_K8S_VERSION_KEY] === searchParams[NODE_K8S_VERSION_KEY] + if (!isK8sVersionFilterAppliedAndMatchFound) { + return false + } + const doesAnyNodeSearchKeyExists = Object.values(NODE_SEARCH_KEYS).some((key) => Object.hasOwn(searchParams, key), ) From bdccc5066137921e362ebf56b7f0a54681a18fdf Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Fri, 22 Nov 2024 15:05:40 +0530 Subject: [PATCH 26/52] chore: update common-lib verion --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 863c7e4dd0..e27b46cc80 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.0.9-beta-1", + "@devtron-labs/devtron-fe-common-lib": "1.1.2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index f0fa43a1a6..4abf9d959d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.0.9-beta-1": - version "1.0.9-beta-1" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.0.9-beta-1.tgz#7fd08a9684d6c776aab53bdd169d37e52a21b05f" - integrity sha512-2ZCIOGw/SR1uBBuYFZAJPVk2A8R1zu0Km3Y1FIanGJC42ECe60zt4DpAXghVKykFy5G9fAsyMDJL0WjGUNlqCA== +"@devtron-labs/devtron-fe-common-lib@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.2.tgz#94009c7e7744740d37c5dfdf5a6209ec0268fe29" + integrity sha512-DGfFFRdpTfpK93HI6XifAxcUc32ByBqeTahkKkEev1XjtCSQGDMRfaob2JIgY3keUiN7JGm9cOJdPTFPVqPXgQ== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" From 3eca66bda87c31c0331190b4c6d271709356e677 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Fri, 22 Nov 2024 15:09:57 +0530 Subject: [PATCH 27/52] chore: remove unneeded checks & send abortControllerRef to fetch --- .../ResourceBrowser/ResourceBrowser.service.tsx | 12 +++++++++--- .../ResourceBrowser/ResourceList/ColumnSelector.tsx | 4 +--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/ResourceBrowser/ResourceBrowser.service.tsx b/src/components/ResourceBrowser/ResourceBrowser.service.tsx index 1e8f76f861..4376958247 100644 --- a/src/components/ResourceBrowser/ResourceBrowser.service.tsx +++ b/src/components/ResourceBrowser/ResourceBrowser.service.tsx @@ -33,6 +33,7 @@ import { import { applyOperation, escapePathComponent } from 'fast-json-patch' import { JSONPath } from 'jsonpath-plus' import { SelectedResourceType } from '@Components/v2/appDetails/appDetails.type' +import { RefObject } from 'react' import { Routes } from '../../config' import { ClusterListResponse } from '../../services/service.types' import { @@ -153,8 +154,13 @@ export const restartWorkload = async (resource: SelectedResourceType, signal: Ab await updateManifestResourceHelmApps(null, '', '', JSON.stringify(manifest), true, resource, signal) } -export const getNodeList = (clusterId: string, signal?: AbortSignal): Promise> => - get(getUrlWithSearchParams(Routes.NODE_LIST, { clusterId: Number(clusterId) }), { signal }) +export const getNodeList = ( + clusterId: string, + abortControllerRef: RefObject, +): Promise> => + get(getUrlWithSearchParams(Routes.NODE_LIST, { clusterId: Number(clusterId) }), { + abortControllerRef, + }) export const getResourceData = async ({ selectedResource, @@ -165,7 +171,7 @@ export const getResourceData = async ({ }: GetResourceDataType) => { try { if (selectedResource.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind) { - const response = await getNodeList(clusterId, abortControllerRef.current.signal) + const response = await getNodeList(clusterId, abortControllerRef) return parseNodeList(response) } diff --git a/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx b/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx index 5ce8c77d54..ed0a64d034 100644 --- a/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx +++ b/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx @@ -56,9 +56,7 @@ const ColumnSelector = ({ setVisibleColumns, visibleColumns }: ColumnSelectorTyp const newVisibleColumns = selectedColumns.map((option) => option.value) - if (typeof Storage !== 'undefined') { - saveAppliedColumnsInLocalStorage(newVisibleColumns) - } + saveAppliedColumnsInLocalStorage(newVisibleColumns) selectRef.current?.blur() From 29700fe1ba2ec146bd69199bc455a105fce99044 Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Fri, 22 Nov 2024 12:48:15 +0530 Subject: [PATCH 28/52] fix: dont check for namespaced in kind selector of user permission --- .../K8sObjectPermissions/K8sListItemCard.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx b/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx index a94728ab54..3960401f3e 100644 --- a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx +++ b/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx @@ -30,6 +30,7 @@ import { GVKType, getK8sResourceList, EntityTypes, + ApiResourceGroupType, } from '@devtron-labs/devtron-fe-common-lib' import CreatableSelect from 'react-select/creatable' import Tippy from '@tippyjs/react' @@ -85,7 +86,7 @@ const K8sListItemCard = ({ const { showStatus, userStatus } = usePermissionConfiguration() const [clusterOptions, setClusterOptions] = useState([]) const [processedData, setProcessedData] = useState>() - const [allInKindMapping, setAllInKindMapping] = useState([]) + const allInKindMapping = { label: 'All kind', value: SELECT_ALL_VALUE } const [ { isClusterListLoading, isNamespaceListLoading, isApiGroupListLoading, isResourceListLoading }, setLoadingState, @@ -174,16 +175,19 @@ const K8sListItemCard = ({ } } - const createKindData = (selected, _allKindMapping, _k8SObjectMap = null) => { + const createKindData = (selected, _k8SObjectMap = null) => { const kind = [] let selectedGvk: GVKType + const isAllNamespaceSelected = k8sPermission.namespace.some((option) => option.value === SELECT_ALL_VALUE) if (_k8SObjectMap ?? processedData) { if (selected.value === SELECT_ALL_VALUE) { // eslint-disable-next-line no-restricted-syntax for (const value of (_k8SObjectMap ?? processedData).values()) { // eslint-disable-next-line no-loop-func - value?.child.forEach((ele: { gvk: GVKType }) => { - kind.push({ value: ele.gvk.Kind, label: ele.gvk.Kind, gvk: ele.gvk }) + value?.child.forEach((ele: ApiResourceGroupType) => { + if (isAllNamespaceSelected || ele.namespaced) { + kind.push({ label: ele.gvk.Kind, value: ele.gvk.Kind, gvk: ele.gvk }) + } if (!selectedGvk && ele.gvk.Kind === k8sPermission.kind?.value) { selectedGvk = ele.gvk } @@ -191,8 +195,9 @@ const K8sListItemCard = ({ } } else { const data = (_k8SObjectMap ?? processedData).get(selected.value === 'k8sempty' ? '' : selected.value) + data?.child?.forEach((ele) => { - if (ele.namespaced) { + if (isAllNamespaceSelected || ele.namespaced) { kind.push({ label: ele.gvk.Kind, value: ele.gvk.Kind, gvk: ele.gvk }) } if (!selectedGvk && ele.gvk.Kind === k8sPermission.kind?.value) { @@ -201,12 +206,12 @@ const K8sListItemCard = ({ }) } } else { - _allKindMapping = [{ label: 'All kind', value: SELECT_ALL_VALUE }] + kind.push(allInKindMapping[0]) } setKindMapping((prevMapping) => ({ ...prevMapping, - [k8sPermission.key]: [..._allKindMapping, ...kind.sort(sortOptionsByLabel)], + [k8sPermission.key]: [...kind.sort(sortOptionsByLabel)], })) if (k8sPermission?.resource) { if (k8sPermission.kind.value !== SELECT_ALL_VALUE && k8sPermission.kind.value !== 'Event') { @@ -238,15 +243,12 @@ const K8sListItemCard = ({ const _processedNamespacedGvk = processK8SObjects(namespacedGvkList, '', true) const _allApiGroupMapping = [] - const _allKindMapping = [] if (resourceGroupList.allowedAll) { _allApiGroupMapping.push( { label: 'All API groups', value: SELECT_ALL_VALUE }, { label: 'K8s core groups (eg. service, pod, etc.)', value: 'k8sempty' }, ) - _allKindMapping.push({ label: 'All kind', value: SELECT_ALL_VALUE }) } - setAllInKindMapping(_allKindMapping) setApiGroupMapping((prevMapping) => ({ ...prevMapping, [k8sPermission.key]: [..._allApiGroupMapping, ..._k8SObjectList.sort(sortOptionsByLabel)], @@ -255,7 +257,6 @@ const K8sListItemCard = ({ if (k8sPermission?.kind) { createKindData( k8sPermission.group, - _allKindMapping, k8sPermission?.namespace.some((el) => el.value === SELECT_ALL_VALUE) ? _k8SObjectMap : _processedNamespacedGvk.k8SObjectMap, @@ -336,7 +337,7 @@ const K8sListItemCard = ({ const onApiGroupSelect = (selected) => { if (selected.value !== k8sPermission?.group?.value) { handleK8sPermission(K8sPermissionActionType.onApiGroupChange, index, selected) - createKindData(selected, allInKindMapping) + createKindData(selected) } } From e3b4333e084c086e54f0d8171d45808c30fb6c5e Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Mon, 25 Nov 2024 13:53:14 +0530 Subject: [PATCH 29/52] feat: remove super admin checks for terminal, node details, etc --- .../K8sObjectPermissions/K8sListItemCard.tsx | 15 +++-- .../ClusterNodes/ClusterOverview.tsx | 7 ++- .../ClusterNodes/ClusterSelectionList.tsx | 3 +- src/components/ClusterNodes/NodeDetails.tsx | 39 +++--------- src/components/ClusterNodes/types.ts | 2 - .../ResourceBrowser/ResourceBrowser.tsx | 10 +--- .../ResourceList/AdminTerminal.tsx | 6 +- .../ResourceList/BaseResourceList.tsx | 2 +- .../ResourceList/NodeActionsMenu.tsx | 60 +++++-------------- .../ResourceList/ResourceList.tsx | 27 ++------- src/components/ResourceBrowser/Types.ts | 4 -- src/components/ResourceBrowser/Utils.tsx | 33 ++++------ 12 files changed, 60 insertions(+), 148 deletions(-) diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx b/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx index 3960401f3e..9f68bd3ee8 100644 --- a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx +++ b/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx @@ -86,7 +86,7 @@ const K8sListItemCard = ({ const { showStatus, userStatus } = usePermissionConfiguration() const [clusterOptions, setClusterOptions] = useState([]) const [processedData, setProcessedData] = useState>() - const allInKindMapping = { label: 'All kind', value: SELECT_ALL_VALUE } + const [allInKindMapping, setAllInKindMapping] = useState([]) const [ { isClusterListLoading, isNamespaceListLoading, isApiGroupListLoading, isResourceListLoading }, setLoadingState, @@ -175,7 +175,7 @@ const K8sListItemCard = ({ } } - const createKindData = (selected, _k8SObjectMap = null) => { + const createKindData = (selected, _allKindMapping, _k8SObjectMap = null) => { const kind = [] let selectedGvk: GVKType const isAllNamespaceSelected = k8sPermission.namespace.some((option) => option.value === SELECT_ALL_VALUE) @@ -195,7 +195,6 @@ const K8sListItemCard = ({ } } else { const data = (_k8SObjectMap ?? processedData).get(selected.value === 'k8sempty' ? '' : selected.value) - data?.child?.forEach((ele) => { if (isAllNamespaceSelected || ele.namespaced) { kind.push({ label: ele.gvk.Kind, value: ele.gvk.Kind, gvk: ele.gvk }) @@ -206,12 +205,12 @@ const K8sListItemCard = ({ }) } } else { - kind.push(allInKindMapping[0]) + _allKindMapping = [{ label: 'All kind', value: SELECT_ALL_VALUE }] } setKindMapping((prevMapping) => ({ ...prevMapping, - [k8sPermission.key]: [...kind.sort(sortOptionsByLabel)], + [k8sPermission.key]: [..._allKindMapping, ...kind.sort(sortOptionsByLabel)], })) if (k8sPermission?.resource) { if (k8sPermission.kind.value !== SELECT_ALL_VALUE && k8sPermission.kind.value !== 'Event') { @@ -243,12 +242,15 @@ const K8sListItemCard = ({ const _processedNamespacedGvk = processK8SObjects(namespacedGvkList, '', true) const _allApiGroupMapping = [] + const _allKindMapping = [] if (resourceGroupList.allowedAll) { _allApiGroupMapping.push( { label: 'All API groups', value: SELECT_ALL_VALUE }, { label: 'K8s core groups (eg. service, pod, etc.)', value: 'k8sempty' }, ) + _allKindMapping.push({ label: 'All kind', value: SELECT_ALL_VALUE }) } + setAllInKindMapping(_allKindMapping) setApiGroupMapping((prevMapping) => ({ ...prevMapping, [k8sPermission.key]: [..._allApiGroupMapping, ..._k8SObjectList.sort(sortOptionsByLabel)], @@ -257,6 +259,7 @@ const K8sListItemCard = ({ if (k8sPermission?.kind) { createKindData( k8sPermission.group, + _allKindMapping, k8sPermission?.namespace.some((el) => el.value === SELECT_ALL_VALUE) ? _k8SObjectMap : _processedNamespacedGvk.k8SObjectMap, @@ -337,7 +340,7 @@ const K8sListItemCard = ({ const onApiGroupSelect = (selected) => { if (selected.value !== k8sPermission?.group?.value) { handleK8sPermission(K8sPermissionActionType.onApiGroupChange, index, selected) - createKindData(selected) + createKindData(selected, allInKindMapping) } } diff --git a/src/components/ClusterNodes/ClusterOverview.tsx b/src/components/ClusterNodes/ClusterOverview.tsx index b820b52049..9edd1bab77 100644 --- a/src/components/ClusterNodes/ClusterOverview.tsx +++ b/src/components/ClusterNodes/ClusterOverview.tsx @@ -24,6 +24,7 @@ import { EditableTextArea, ResourceKindType, getUrlWithSearchParams, + showError, } from '@devtron-labs/devtron-fe-common-lib' import { ClusterErrorType, @@ -89,7 +90,7 @@ const tippyForMetricsApi = () => { ) } -function ClusterOverview({ isSuperAdmin, selectedCluster, addTab }: ClusterOverviewProps) { +function ClusterOverview({ selectedCluster, addTab }: ClusterOverviewProps) { const { clusterId, namespace } = useParams<{ clusterId: string namespace: string @@ -132,7 +133,7 @@ function ClusterOverview({ isSuperAdmin, selectedCluster, addTab }: ClusterOverv }) } } catch (error) { - setErrorCode(error['code']) + showError(error) throw error } } @@ -512,7 +513,7 @@ function ClusterOverview({ isSuperAdmin, selectedCluster, addTab }: ClusterOverv ({ const ClusterSelectionList: React.FC = ({ clusterOptions, - isSuperAdmin, clusterListLoader, initialLoading, refreshData, @@ -190,7 +189,7 @@ const ClusterSelectionList: React.FC = ({ {/* NOTE: visible-hover plays with display prop; therefore need to set display: flex on a new div */}
    - {!!clusterData.nodeCount && !clusterListLoader && isSuperAdmin && ( + {!!clusterData.nodeCount && !clusterListLoader && (
    { +const NodeDetails = ({ addTab, lowercaseKindToResourceGroupMap, updateTabUrl }: ClusterListType) => { const { clusterId, node } = useParams<{ clusterId: string; nodeType: string; node: string }>() const [loader, setLoader] = useState(true) const [apiInProgress, setApiInProgress] = useState(false) @@ -799,12 +799,10 @@ const NodeDetails = ({ isSuperAdmin, addTab, lowercaseKindToResourceGroupMap, up return (
    - {isSuperAdmin && ( - - - {NODE_DETAILS_TABS.debug} - - )} + + + {NODE_DETAILS_TABS.debug} + | {renderTabControls()}
    @@ -996,21 +994,8 @@ const NodeDetails = ({ isSuperAdmin, addTab, lowercaseKindToResourceGroupMap, up return renderSummary() } - const isAuthorized = (): boolean => { - if (!isSuperAdmin) { - ToastManager.showToast({ - variant: ToastVariantType.notAuthorized, - description: TOAST_ACCESS_DENIED.SUBTITLE, - }) - return false - } - return true - } - const showCordonNodeModal = (): void => { - if (isAuthorized()) { - setCordonNodeDialog(true) - } + setCordonNodeDialog(true) } const hideCordonNodeModal = (refreshData?: boolean): void => { @@ -1021,9 +1006,7 @@ const NodeDetails = ({ isSuperAdmin, addTab, lowercaseKindToResourceGroupMap, up } const showDrainNodeModal = (): void => { - if (isAuthorized()) { - setDrainNodeDialog(true) - } + setDrainNodeDialog(true) } const hideDrainNodeModal = (refreshData?: boolean): void => { @@ -1034,9 +1017,7 @@ const NodeDetails = ({ isSuperAdmin, addTab, lowercaseKindToResourceGroupMap, up } const showDeleteNodeModal = (): void => { - if (isAuthorized()) { - setDeleteNodeDialog(true) - } + setDeleteNodeDialog(true) } const hideDeleteNodeModal = (refreshData?: boolean): void => { @@ -1047,9 +1028,7 @@ const NodeDetails = ({ isSuperAdmin, addTab, lowercaseKindToResourceGroupMap, up } const showEditTaintsModal = (): void => { - if (isAuthorized()) { - setShowEditTaints(true) - } + setShowEditTaints(true) } const hideEditTaintsModal = (refreshData?: boolean): void => { diff --git a/src/components/ClusterNodes/types.ts b/src/components/ClusterNodes/types.ts index 49b5ed8863..cba282716a 100644 --- a/src/components/ClusterNodes/types.ts +++ b/src/components/ClusterNodes/types.ts @@ -161,7 +161,6 @@ export interface ColumnMetadataType { } export interface ClusterListType extends Pick { - isSuperAdmin: boolean addTab?: ReturnType['addTab'] updateTabUrl: (params: Omit) => void } @@ -344,7 +343,6 @@ export interface ClusterErrorType { filterText: string[] } export interface ClusterOverviewProps { - isSuperAdmin: boolean selectedCluster: ClusterOptionType addTab: ReturnType['addTab'] } diff --git a/src/components/ResourceBrowser/ResourceBrowser.tsx b/src/components/ResourceBrowser/ResourceBrowser.tsx index 19f7079aa9..50a14bfd9e 100644 --- a/src/components/ResourceBrowser/ResourceBrowser.tsx +++ b/src/components/ResourceBrowser/ResourceBrowser.tsx @@ -17,7 +17,6 @@ import React, { useEffect, useMemo, useRef } from 'react' import { showError, - getUserRole, DevtronProgressing, useAsync, PageHeader, @@ -44,11 +43,7 @@ const ResourceBrowser: React.FC = () => { return null } }) - const [initialLoading, data, error] = useAsync(() => - Promise.all([getClusterListMin(), window._env_?.K8S_CLIENT ? null : getUserRole()]), - ) - /* transpose the data */ - const [clusterListMinData = null, userRoleData = null] = data || [] + const [initialLoading, clusterListMinData, error] = useAsync(() => getClusterListMin()) useEffect( () => () => { @@ -67,8 +62,6 @@ const ResourceBrowser: React.FC = () => { [detailClusterList, clusterListMinData], ) - const isSuperAdmin = userRoleData?.result.superAdmin || false - const renderContent = () => { if (error) { return @@ -77,7 +70,6 @@ const ResourceBrowser: React.FC = () => { return ( = ({ isSuperAdmin, updateTerminalTabUrl }: AdminTerminalProps) => { +const AdminTerminal: React.FC = ({ updateTerminalTabUrl }: AdminTerminalProps) => { const { clusterId } = useParams() const [loading, data, error] = useAsync( @@ -65,7 +65,7 @@ const AdminTerminal: React.FC = ({ isSuperAdmin, updateTermi const errCode = error?.code || 403 return (
    - {isSuperAdmin ? : } +
    ) } diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index 075f3924f7..027d47ff21 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -261,7 +261,7 @@ const BaseResourceListContent = ({ handleFilterChanges(searchText) setResourceListOffset(0) - }, [resourceList, sortBy, sortOrder, location.search]) + }, [resourceList, sortBy, sortOrder, location.search, isOpen]) const getHandleCheckedForId = (resourceData: K8sResourceDetailDataType) => () => { const { id } = resourceData as Record<'id', string> diff --git a/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx index 8634373e8d..bcd1215e11 100644 --- a/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx +++ b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx @@ -16,14 +16,7 @@ import { useState } from 'react' import { useHistory, useLocation, useRouteMatch } from 'react-router-dom' -import { - noop, - PopupMenu, - TOAST_ACCESS_DENIED, - ToastManager, - ToastVariantType, - useMainContext, -} from '@devtron-labs/devtron-fe-common-lib' +import { noop, PopupMenu } from '@devtron-labs/devtron-fe-common-lib' import { AppDetailsTabs } from '@Components/v2/appDetails/appDetails.store' import { TaintType } from '@Components/ClusterNodes/types' import { ReactComponent as TerminalIcon } from '../../../assets/icons/ic-terminal-fill.svg' @@ -55,42 +48,23 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP const { name, version, kind } = nodeData as Record - const { isSuperAdmin } = useMainContext() - - const isAuthorized = (): boolean => { - if (!isSuperAdmin) { - ToastManager.showToast({ - variant: ToastVariantType.notAuthorized, - description: TOAST_ACCESS_DENIED.SUBTITLE, - }) - return false - } - return true - } - const handleOpenTerminalAction = () => { - if (isAuthorized()) { - const queryParams = new URLSearchParams(location.search) - queryParams.set('node', name) - history.push( - `${location.pathname.split('/').slice(0, -2).join('/')}/${AppDetailsTabs.terminal}/${K8S_EMPTY_GROUP}?${queryParams.toString()}`, - ) - } + const queryParams = new URLSearchParams(location.search) + queryParams.set('node', name) + history.push( + `${location.pathname.split('/').slice(0, -2).join('/')}/${AppDetailsTabs.terminal}/${K8S_EMPTY_GROUP}?${queryParams.toString()}`, + ) } const handleEditYamlAction = () => { - if (isAuthorized()) { - const _url = `${url.split('/').slice(0, -2).join('/')}/node/${K8S_EMPTY_GROUP}/${nodeData.name}?tab=yaml` - addTab({ idPrefix: K8S_EMPTY_GROUP, kind: 'node', name, url: _url }) - .then(() => history.push(_url)) - .catch(noop) - } + const _url = `${url.split('/').slice(0, -2).join('/')}/node/${K8S_EMPTY_GROUP}/${nodeData.name}?tab=yaml` + addTab({ idPrefix: K8S_EMPTY_GROUP, kind: 'node', name, url: _url }) + .then(() => history.push(_url)) + .catch(noop) } const showCordonNodeModal = (): void => { - if (isAuthorized()) { - setShowCordonNodeDialog(true) - } + setShowCordonNodeDialog(true) } const hideCordonNodeModal = (refreshData?: boolean): void => { @@ -101,9 +75,7 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP } const showDrainNodeModal = (): void => { - if (isAuthorized()) { - setShowDrainNodeDialog(true) - } + setShowDrainNodeDialog(true) } const hideDrainNodeModal = (refreshData?: boolean): void => { @@ -114,9 +86,7 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP } const showDeleteNodeModal = (): void => { - if (isAuthorized()) { - setShowDeleteNodeDialog(true) - } + setShowDeleteNodeDialog(true) } const hideDeleteNodeModal = (refreshData?: boolean): void => { @@ -127,9 +97,7 @@ const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuP } const showEditTaintsModal = (): void => { - if (isAuthorized()) { - setShowEditTaintNodeDialog(true) - } + setShowEditTaintNodeDialog(true) } const hideEditTaintsModal = (refreshData?: boolean): void => { diff --git a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx b/src/components/ResourceBrowser/ResourceList/ResourceList.tsx index c265a2fdc6..0a45cad98d 100644 --- a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/ResourceList.tsx @@ -17,7 +17,6 @@ import { useState, useEffect, useMemo, useRef } from 'react' import { useHistory, useParams, useRouteMatch, useLocation } from 'react-router-dom' import { - getUserRole, BreadCrumb, useBreadcrumb, ErrorScreenManager, @@ -98,11 +97,7 @@ const ResourceList = () => { const [rawGVKLoader, k8SObjectMapRaw] = useAsync(() => getResourceGroupListRaw(clusterId), [clusterId]) - const [loading, data, error] = useAsync(() => - Promise.all([getClusterListMin(), window._env_.K8S_CLIENT ? null : getUserRole()]), - ) - - const [clusterListData = null, userRole = null] = data || [] + const [loading, clusterListData, error] = useAsync(() => getClusterListMin()) const clusterList = clusterListData?.result || null @@ -144,8 +139,6 @@ const ResourceList = () => { [k8SObjectMapRaw], ) - const isSuperAdmin = !!userRole?.result.superAdmin - const isOverviewNodeType = nodeType === SIDEBAR_KEYS.overviewGVK.Kind.toLowerCase() const isMonitoringNodeType = nodeType === SIDEBAR_KEYS.monitoringGVK.Kind.toLowerCase() const isTerminalNodeType = nodeType === AppDetailsTabs.terminal @@ -200,25 +193,19 @@ const ResourceList = () => { }) const initTabsBasedOnRole = (reInit: boolean) => { - /* NOTE: selectedCluster is not in useEffect dep list since it arrives with isSuperAdmin (Promise.all) */ const _tabs = getTabsBasedOnRole({ selectedCluster, namespace, - isSuperAdmin, /* NOTE: if node is available in url but no associated dynamicTab we create a dynamicTab */ dynamicTabData: (node || isUpgradeClusterNodeType) && getDynamicTabData(), isTerminalSelected: isTerminalNodeType, isOverviewSelected: isOverviewNodeType, isMonitoringDashBoardSelected: isMonitoringNodeType, }) - initTabs( - _tabs, - reInit, - !isSuperAdmin ? [getTabId(ResourceBrowserTabsId.terminal, AppDetailsTabs.terminal, '')] : null, - ) + initTabs(_tabs, reInit) } - useEffect(() => initTabsBasedOnRole(false), [isSuperAdmin]) + useEffect(() => initTabsBasedOnRole(false), []) useEffectAfterMount(() => initTabsBasedOnRole(true), [clusterId]) useEffectAfterMount(() => { @@ -413,7 +400,6 @@ const ResourceList = () => { return nodeType.toLowerCase() === SIDEBAR_KEYS.nodeGVK.Kind.toLowerCase() ? ( { } const fixedTabComponents = [ - , + , { getTabById(ResourceBrowserTabsId.k8s_Resources)?.lastSyncMoment?.toString(), refreshData, )} - isSuperAdmin={isSuperAdmin} isOpen={!!getTabById(ResourceBrowserTabsId.k8s_Resources)?.isSelected} showStaleDataWarning={isDataStale} updateK8sResourceTab={getUpdateTabUrlForId(getTabById(ResourceBrowserTabsId.k8s_Resources)?.id)} @@ -458,8 +443,8 @@ const ResourceList = () => { lowercaseKindToResourceGroupMap={lowercaseKindToResourceGroupMap} />, ...(MonitoringDashboard ? [] : []), - ...(isSuperAdmin && getTabById(ResourceBrowserTabsId.terminal)?.isAlive - ? [] + ...(getTabById(ResourceBrowserTabsId.terminal)?.isAlive + ? [] : []), ] diff --git a/src/components/ResourceBrowser/Types.ts b/src/components/ResourceBrowser/Types.ts index e3addd935e..10a87c6329 100644 --- a/src/components/ResourceBrowser/Types.ts +++ b/src/components/ResourceBrowser/Types.ts @@ -94,7 +94,6 @@ export interface ResourceDetailsPropType extends LogSearchTermType { export interface ClusterSelectionType { clusterOptions: ClusterDetail[] - isSuperAdmin: boolean clusterListLoader: boolean initialLoading: boolean refreshData: () => void @@ -221,7 +220,6 @@ export interface K8SResourceTabComponentProps 'setWidgetEventDetails' | 'handleResourceClick' | 'clusterName' | 'lowercaseKindToResourceGroupMap' > { selectedCluster: ClusterOptionType - isSuperAdmin: boolean renderRefreshBar: () => JSX.Element addTab: ReturnType['addTab'] showStaleDataWarning: boolean @@ -230,7 +228,6 @@ export interface K8SResourceTabComponentProps } export interface AdminTerminalProps { - isSuperAdmin: boolean updateTerminalTabUrl: (queryParams: string) => void } @@ -271,7 +268,6 @@ export interface RBSidebarKeysType { export interface GetTabsBasedOnRoleParamsType { selectedCluster: ClusterOptionType namespace: string - isSuperAdmin: boolean dynamicTabData: InitTabType /** * @default false diff --git a/src/components/ResourceBrowser/Utils.tsx b/src/components/ResourceBrowser/Utils.tsx index a65a6b5479..305e620f38 100644 --- a/src/components/ResourceBrowser/Utils.tsx +++ b/src/components/ResourceBrowser/Utils.tsx @@ -289,7 +289,6 @@ export const getURLBasedOnSidebarGVK = (kind: GVKType['Kind'], clusterId: string export const getTabsBasedOnRole = ({ selectedCluster, namespace, - isSuperAdmin, dynamicTabData, isTerminalSelected = false, isOverviewSelected = false, @@ -311,11 +310,7 @@ export const getTabsBasedOnRole = ({ id: ResourceBrowserTabsId.k8s_Resources, name: AppDetailsTabs.k8s_Resources, url: getURLBasedOnSidebarGVK(SIDEBAR_KEYS.nodeGVK.Kind, clusterId, namespace), - isSelected: - (!isSuperAdmin || !isTerminalSelected) && - !dynamicTabData && - !isOverviewSelected && - !isMonitoringDashBoardSelected, + isSelected: !isTerminalSelected && !dynamicTabData && !isOverviewSelected && !isMonitoringDashBoardSelected, type: 'fixed', iconPath: K8ResourceIcon, showNameOnSelect: false, @@ -331,21 +326,17 @@ export const getTabsBasedOnRole = ({ ), ] : []), - ...(isSuperAdmin - ? [ - { - id: ResourceBrowserTabsId.terminal, - name: AppDetailsTabs.terminal, - url: `${URLS.RESOURCE_BROWSER}/${clusterId}/${namespace}/${AppDetailsTabs.terminal}/${K8S_EMPTY_GROUP}`, - isSelected: isTerminalSelected, - type: 'fixed', - iconPath: TerminalIcon, - showNameOnSelect: true, - isAlive: isTerminalSelected, - dynamicTitle: `${AppDetailsTabs.terminal} '${selectedCluster.label}'`, - }, - ] - : []), + { + id: ResourceBrowserTabsId.terminal, + name: AppDetailsTabs.terminal, + url: `${URLS.RESOURCE_BROWSER}/${clusterId}/${namespace}/${AppDetailsTabs.terminal}/${K8S_EMPTY_GROUP}`, + isSelected: isTerminalSelected, + type: 'fixed', + iconPath: TerminalIcon, + showNameOnSelect: true, + isAlive: isTerminalSelected, + dynamicTitle: `${AppDetailsTabs.terminal} '${selectedCluster.label}'`, + }, ...(dynamicTabData ? [dynamicTabData] : []), ] From 6c5180fca960d09a76f89fa1d7d402cbb1031f3e Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Mon, 25 Nov 2024 15:55:57 +0530 Subject: [PATCH 30/52] chore: format file --- .../v2/values/chartValuesDiff/ChartValuesView.component.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx index eda5513243..c2e8777004 100644 --- a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx +++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx @@ -194,7 +194,7 @@ export const DeploymentAppSelector = ({
    ) : (
    - How do you want to deploy? + How do you want to deploy? - This cannot be changed after deployment + This cannot be changed after deployment
    ) } From b73d67b17cb192ac5072c9cac4c3154813dddbd7 Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Mon, 25 Nov 2024 16:01:47 +0530 Subject: [PATCH 31/52] chore: version bump --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index faed5cc693..3455457918 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.1.3", + "@devtron-labs/devtron-fe-common-lib": "1.1.3-beta-3", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index 6cff5a50f9..60899dfdb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,10 +974,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.3.tgz#863c98c16eb6e13a4bd996a11f1b8baf6a745129" - integrity sha512-wg7QJ6pENHTFA5kNOHFac0/60uheJ+SWVppJufXcgThZgaWvMXOnpOl0waV1BXpTwSpGVx2STW3WW1uBPcrYVQ== +"@devtron-labs/devtron-fe-common-lib@1.1.3-beta-3": + version "1.1.3-beta-3" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.3-beta-3.tgz#c7b93c1c8853f9202c3b1bff057ab1d4b7f86e90" + integrity sha512-/eKzJPMEFR9Uj3Wj6viW0UQWkEolwdp8q3VPfJQmkjpC24MIVddqXuN5FxHbHvCiv4/NOBwZi5Aakzlll+7usQ== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" From 642226dafa068c95a265cfa0861e1a7af7f7008f Mon Sep 17 00:00:00 2001 From: arunjaindev Date: Mon, 25 Nov 2024 16:05:54 +0530 Subject: [PATCH 32/52] chore: remove import of tooltip props --- src/components/v2/appDetails/sourceInfo/environment.type.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/v2/appDetails/sourceInfo/environment.type.ts b/src/components/v2/appDetails/sourceInfo/environment.type.ts index 340b0ef663..3b05bf57a7 100644 --- a/src/components/v2/appDetails/sourceInfo/environment.type.ts +++ b/src/components/v2/appDetails/sourceInfo/environment.type.ts @@ -15,7 +15,6 @@ */ import { AppEnvironment as BaseAppEnvironmentType } from '@devtron-labs/devtron-fe-common-lib' -import { TooltipProps } from '@devtron-labs/devtron-fe-common-lib/dist/Common/Tooltip/types' import React from 'react' import { DeploymentStatusDetailsBreakdownDataType } from '../../../app/details/appDetails/appDetails.type' import { HelmReleaseStatus } from '../../../external-apps/ExternalAppService' @@ -57,7 +56,7 @@ export interface HelmAppConfigApplyStatusCardType { } export interface ChartToolTipType extends Pick { - children: TooltipProps['children'] + children: React.ReactElement isDeprecated: boolean chartRef: React.RefObject } From afc83a878cee8af7460215db5c3b97143795274b Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Mon, 25 Nov 2024 16:07:43 +0530 Subject: [PATCH 33/52] feat: hide node option from sidebar if no permission for it --- package.json | 2 +- src/components/ResourceBrowser/Constants.ts | 3 +- .../ResourceList/BaseResourceList.tsx | 6 +- .../ResourceList/K8SResourceList.tsx | 18 +- .../ResourceList/ResourceList.tsx | 8 +- .../ResourceBrowser/ResourceList/Sidebar.tsx | 41 +- src/components/ResourceBrowser/Utils.tsx | 27 +- .../app/details/appDetails/utils.tsx | 2 + src/components/app/types.ts | 2 + src/components/common/helpers/Helpers.tsx | 6 + yarn.lock | 455 +++++++++--------- 11 files changed, 292 insertions(+), 278 deletions(-) diff --git a/package.json b/package.json index 46190716ea..773d0138fe 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.1.3", + "@devtron-labs/devtron-fe-common-lib": "1.1.3-beta-2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/ResourceBrowser/Constants.ts b/src/components/ResourceBrowser/Constants.ts index 5a343b5b93..a22b078ad4 100644 --- a/src/components/ResourceBrowser/Constants.ts +++ b/src/components/ResourceBrowser/Constants.ts @@ -114,6 +114,7 @@ export const NAMESPACE_NOT_APPLICABLE_TEXT = 'Namespace is not applicable for th export const CLUSTER_NOT_REACHABLE = 'Cluster is not reachable' export const ORDERED_AGGREGATORS: AggregationKeysType[] = [ + AggregationKeys.Nodes, AggregationKeys.Events, AggregationKeys.Namespaces, AggregationKeys.Workloads, @@ -222,7 +223,7 @@ export const UPGRADE_CLUSTER_CONSTANTS = { export const JUMP_TO_KIND_SHORT_NAMES: Record = { events: null, - nodes: ['no'], // NOTE: hardcoding cuz backend doesn't send nodeGVK + nodes: null, namespaces: null, } diff --git a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx index 027d47ff21..5d1300beff 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -115,7 +115,7 @@ const BaseResourceListContent = ({ const { searchParams } = useSearchString() - const isNodeListing = selectedResource.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind + const isNodeListing = selectedResource?.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind const { selectedIdentifiers: bulkSelectionState, @@ -579,7 +579,7 @@ const BaseResourceListContent = ({ } const renderContent = () => { - if (!resourceListError && (isLoading || !resourceList || !filteredResourceList)) { + if (!resourceListError && (isLoading || !resourceList || !filteredResourceList || !selectedResource)) { return } @@ -690,7 +690,7 @@ const BaseResourceListContent = ({ /> ) : ( - abortPreviousRequests( - async () => - getResourceData({ selectedResource, selectedNamespace, clusterId, filters, abortControllerRef }), - abortControllerRef, - ), + abortPreviousRequests(async () => { + if (selectedResource) { + return getResourceData({ + selectedResource, + selectedNamespace, + clusterId, + filters, + abortControllerRef, + }) + } + + return null + }, abortControllerRef), [selectedResource, clusterId, selectedNamespace, filters], ) diff --git a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx b/src/components/ResourceBrowser/ResourceList/ResourceList.tsx index 0a45cad98d..622ac3174a 100644 --- a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/ResourceList.tsx @@ -88,11 +88,7 @@ const ResourceList = () => { const [logSearchTerms, setLogSearchTerms] = useState>() const [widgetEventDetails, setWidgetEventDetails] = useState(null) const [isDataStale, setIsDataStale] = useState(false) - const [selectedResource, setSelectedResource] = useState({ - gvk: SIDEBAR_KEYS.nodeGVK, - namespaced: false, - isGrouped: false, - }) + const [selectedResource, setSelectedResource] = useState(null) const { targetK8sVersion } = useUrlFilters({ parseSearchParams }) const [rawGVKLoader, k8SObjectMapRaw] = useAsync(() => getResourceGroupListRaw(clusterId), [clusterId]) @@ -348,7 +344,7 @@ const ResourceList = () => { const _group: string = (shouldOverrideSelectedResourceKind ? lowercaseKindToResourceGroupMap[lowercaseKindFromResource]?.gvk?.Group?.toLowerCase() - : selectedResource?.gvk.Group.toLowerCase()) || K8S_EMPTY_GROUP + : selectedResource.gvk.Group.toLowerCase()) || K8S_EMPTY_GROUP const _namespace = currentNamespace ?? ALL_NAMESPACE_OPTION.value let resourceParam: string diff --git a/src/components/ResourceBrowser/ResourceList/Sidebar.tsx b/src/components/ResourceBrowser/ResourceList/Sidebar.tsx index ada9246fd1..5c2992c514 100644 --- a/src/components/ResourceBrowser/ResourceList/Sidebar.tsx +++ b/src/components/ResourceBrowser/ResourceList/Sidebar.tsx @@ -151,15 +151,13 @@ const Sidebar = ({ useEffect(() => { /* NOTE: this effect accommodates for user navigating through browser history (push) */ - if (!isOpen || nodeType === selectedResource.gvk.Kind.toLowerCase()) { + if (!isOpen || nodeType === selectedResource?.gvk.Kind.toLowerCase() || !k8sObjectOptionsList?.length) { return } /* NOTE: match will never be null; due to node fallback */ const match = k8sObjectOptionsList.find((option) => option.dataset.kind.toLowerCase() === nodeType) || - k8sObjectOptionsList.find( - (option) => option.dataset.kind.toLowerCase() === SIDEBAR_KEYS.nodeGVK.Kind.toLowerCase(), - ) + k8sObjectOptionsList[0] /* NOTE: if nodeType doesn't match the selectedResource kind, set it accordingly */ selectNode( { @@ -169,9 +167,9 @@ const Sidebar = ({ }, match.groupName, /* NOTE: if we push here the history will be lost */ - false, + !selectedResource, ) - }, [nodeType]) + }, [nodeType, k8sObjectOptionsList]) const selectedChildRef: React.Ref = (node) => { /** @@ -342,17 +340,19 @@ const Sidebar = ({
    - - {list?.size && list.get(AggregationKeys.Events) && ( + {!!list?.size && !!list.get(AggregationKeys.Nodes) && ( + + )} + {!!list?.size && !!list.get(AggregationKeys.Events) && ( )} - {list?.size && list.get(AggregationKeys.Namespaces) && ( + {!!list?.size && !!list.get(AggregationKeys.Namespaces) && ( )}
    - {list?.size && + {!!list?.size && [...list.values()].map((k8sObject) => k8sObject.name === AggregationKeys.Events || - k8sObject.name === AggregationKeys.Namespaces ? null : ( + k8sObject.name === AggregationKeys.Namespaces || + k8sObject.name === AggregationKeys.Nodes ? null : (