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/package.json b/package.json index d2f30c28d0..249362b5a8 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-patch-1", + "@devtron-labs/devtron-fe-common-lib": "1.1.6", "@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/App.tsx b/src/App.tsx index ace35a25b0..5e744fd021 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -337,6 +337,7 @@ export default function App() {
+
{import.meta.env.VITE_NODE_ENV === 'production' && window._env_ && window._env_.HOTJAR_ENABLED && } diff --git a/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx b/src/Pages/GlobalConfigurations/Authorization/Shared/components/K8sObjectPermissions/K8sListItemCard.tsx index a94728ab54..9f68bd3ee8 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' @@ -177,13 +178,16 @@ const K8sListItemCard = ({ const createKindData = (selected, _allKindMapping, _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 } @@ -192,7 +196,7 @@ 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) { 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/ClusterNodes/ClusterNodeEmptyStates.tsx b/src/components/ClusterNodes/ClusterNodeEmptyStates.tsx index 69877ad547..43a66e0142 100644 --- a/src/components/ClusterNodes/ClusterNodeEmptyStates.tsx +++ b/src/components/ClusterNodes/ClusterNodeEmptyStates.tsx @@ -15,7 +15,6 @@ */ import { GenericFilterEmptyState } from '@devtron-labs/devtron-fe-common-lib' -import React from 'react' export default function ClusterNodeEmptyState({ title, diff --git a/src/components/ClusterNodes/ClusterOverview.tsx b/src/components/ClusterNodes/ClusterOverview.tsx index b820b52049..193ba8af0f 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,6 @@ 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 { 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/NodeActions/NodeActionsMenu.tsx b/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx deleted file mode 100644 index 74435e5be5..0000000000 --- a/src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx +++ /dev/null @@ -1,218 +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 } from 'react' -import { useHistory, useRouteMatch } from 'react-router-dom' -import { PopupMenu, TOAST_ACCESS_DENIED, ToastManager, ToastVariantType } from '@devtron-labs/devtron-fe-common-lib' -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' -import { ReactComponent as DrainIcon } from '../../../assets/icons/ic-clean-brush.svg' -import { ReactComponent as EditTaintsIcon } from '../../../assets/icons/ic-spraycan.svg' -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' - -// TODO: This should be commoned out with ResourceBrowserActionMenu to have consistent styling -export default function NodeActionsMenu({ - nodeData, - openTerminal, - getNodeListData, - isSuperAdmin, - addTab, -}: NodeActionsMenuProps) { - const history = useHistory() - const { url } = useRouteMatch() - const [showCordonNodeDialog, setCordonNodeDialog] = useState(false) - const [showDrainNodeDialog, setDrainNodeDialog] = useState(false) - const [showDeleteNodeDialog, setDeleteNodeDialog] = useState(false) - const [showEditTaintNodeDialog, setEditTaintNodeDialog] = useState(false) - - const isAuthorized = (): boolean => { - if (!isSuperAdmin) { - ToastManager.showToast({ - variant: ToastVariantType.notAuthorized, - description: TOAST_ACCESS_DENIED.SUBTITLE - }) - return false - } - return true - } - - const handleOpenTerminalAction = () => { - if (isAuthorized()) { - openTerminal(nodeData) - } - } - - 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: nodeData.name, url: _url }).then(() => - history.push(_url), - ) - } - } - - const showCordonNodeModal = (): void => { - if (isAuthorized()) { - setCordonNodeDialog(true) - } - } - - const hideCordonNodeModal = (refreshData?: boolean): void => { - setCordonNodeDialog(false) - if (refreshData) { - getNodeListData() - } - } - - const showDrainNodeModal = (): void => { - if (isAuthorized()) { - setDrainNodeDialog(true) - } - } - - const hideDrainNodeModal = (refreshData?: boolean): void => { - setDrainNodeDialog(false) - if (refreshData) { - getNodeListData() - } - } - - const showDeleteNodeModal = (): void => { - if (isAuthorized()) { - setDeleteNodeDialog(true) - } - } - - const hideDeleteNodeModal = (refreshData?: boolean): void => { - setDeleteNodeDialog(false) - if (refreshData) { - getNodeListData() - } - } - - const showEditTaintsModal = (): void => { - if (isAuthorized()) { - setEditTaintNodeDialog(true) - } - } - - const hideEditTaintsModal = (refreshData?: boolean): void => { - setEditTaintNodeDialog(false) - if (refreshData) { - getNodeListData() - } - } - - return ( - <> - - - - - -
- - - {CLUSTER_NODE_ACTIONS_LABELS.terminal} - - - {nodeData.unschedulable ? ( - <> - - {CLUSTER_NODE_ACTIONS_LABELS.uncordon} - - ) : ( - <> - - {CLUSTER_NODE_ACTIONS_LABELS.cordon} - - )} - - - - {CLUSTER_NODE_ACTIONS_LABELS.drain} - - - - {CLUSTER_NODE_ACTIONS_LABELS.taints} - - - - {CLUSTER_NODE_ACTIONS_LABELS.yaml} - - - - {CLUSTER_NODE_ACTIONS_LABELS.delete} - -
-
-
- {showCordonNodeDialog && ( - - )} - {showDrainNodeDialog && ( - - )} - {showDeleteNodeDialog && ( - - )} - {showEditTaintNodeDialog && ( - - )} - - ) -} diff --git a/src/components/ClusterNodes/NodeDetails.tsx b/src/components/ClusterNodes/NodeDetails.tsx index 3155129e38..d8134b9805 100644 --- a/src/components/ClusterNodes/NodeDetails.tsx +++ b/src/components/ClusterNodes/NodeDetails.tsx @@ -79,7 +79,7 @@ import { unauthorizedInfoText } from '../ResourceBrowser/ResourceList/ClusterSel import './clusterNodes.scss' import ResourceBrowserActionMenu from '../ResourceBrowser/ResourceList/ResourceBrowserActionMenu' -const NodeDetails = ({ isSuperAdmin, addTab, lowercaseKindToResourceGroupMap, updateTabUrl }: ClusterListType) => { +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/NodeDetailsList.tsx b/src/components/ClusterNodes/NodeDetailsList.tsx deleted file mode 100644 index bbb8c51eee..0000000000 --- a/src/components/ClusterNodes/NodeDetailsList.tsx +++ /dev/null @@ -1,763 +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 => ( -
- {RBBulkOperations && 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({ idPrefix: K8S_EMPTY_GROUP, kind: nodeType, name: nodeData[column.value], url }) - history.push(url) - } - } - - const renderNodeList = (nodeData: (typeof filteredFlattenNodeList)[number]): JSX.Element => { - return ( -
- {appliedColumns.map((column) => { - return column.label === 'Node' ? ( -
- {RBBulkOperations && ( - - )} - - - {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 a1173691e6..cba282716a 100644 --- a/src/components/ClusterNodes/types.ts +++ b/src/components/ClusterNodes/types.ts @@ -14,12 +14,10 @@ * 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 { UpdateTabUrlParamsType } from '@Components/common/DynamicTabs/Types' 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' @@ -34,20 +32,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 @@ -103,19 +88,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[] } @@ -132,10 +104,6 @@ export interface ClusterCapacityResponse extends ResponseType { result?: ClusterCapacityType } -export interface NodeListResponse extends ResponseType { - result?: NodeRowDetail[] -} - export interface PodType extends K8sResourceDetailDataType { name: string namespace: string @@ -193,7 +161,6 @@ export interface ColumnMetadataType { } export interface ClusterListType extends Pick { - isSuperAdmin: boolean addTab?: ReturnType['addTab'] updateTabUrl: (params: Omit) => void } @@ -241,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 @@ -386,9 +343,6 @@ export interface ClusterErrorType { filterText: string[] } export interface ClusterOverviewProps { - isSuperAdmin: boolean 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 98c7b5a33b..a22b078ad4 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, @@ -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, } @@ -282,6 +283,86 @@ 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' + export const MONITORING_DASHBOARD_TAB_ID = 'monitoring_dashboard' // Note: can't change the snake case to camel case since that would be breaking change 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..4376958247 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, @@ -29,10 +33,19 @@ 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 { CreateResourcePayload, CreateResourceResponse, 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) @@ -140,3 +153,39 @@ export const restartWorkload = async (resource: SelectedResourceType, signal: Ab await updateManifestResourceHelmApps(null, '', '', JSON.stringify(manifest), true, resource, signal) } + +export const getNodeList = ( + clusterId: string, + abortControllerRef: RefObject, +): Promise> => + get(getUrlWithSearchParams(Routes.NODE_LIST, { clusterId: Number(clusterId) }), { + abortControllerRef, + }) + +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) + + 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/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 37bd1f7f77..b79aab8f01 100644 --- a/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/BaseResourceList.tsx @@ -31,11 +31,19 @@ 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 { 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_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, @@ -50,6 +58,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') @@ -89,6 +99,12 @@ const BaseResourceListContent = ({ 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 +115,8 @@ const BaseResourceListContent = ({ const { searchParams } = useSearchString() + const isNodeListing = selectedResource?.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind + const { selectedIdentifiers: bulkSelectionState, handleBulkSelection, @@ -107,8 +125,24 @@ const BaseResourceListContent = ({ getSelectedIdentifiersCount, } = useBulkSelection>() + const headers = useMemo(() => { + const list = resourceList?.headers ?? [] + + if (!isNodeListing) { + return list + } + + const visibleColumnsSet = new Set(visibleColumns) + + return list.filter( + (header) => + MANDATORY_NODE_LIST_HEADERS.includes(header as (typeof NODE_LIST_HEADERS)[number]) || + 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, @@ -129,11 +163,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 }) @@ -169,6 +203,15 @@ const BaseResourceListContent = ({ sortBy, sortOrder, debounceResult, + 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, }, }) @@ -207,6 +250,10 @@ const BaseResourceListContent = ({ }, [nodeType]) useEffect(() => { + if (!isOpen) { + return + } + if (!resourceList) { setFilteredResourceList(null) return @@ -214,7 +261,7 @@ const BaseResourceListContent = ({ handleFilterChanges(searchText) setResourceListOffset(0) - }, [resourceList, sortBy, sortOrder]) + }, [resourceList, sortBy, sortOrder, location.search, isOpen]) const getHandleCheckedForId = (resourceData: K8sResourceDetailDataType) => () => { const { id } = resourceData as Record<'id', string> @@ -270,9 +317,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 +335,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,16 +386,20 @@ const BaseResourceListContent = ({ setFilteredResourceList(resourceList?.data ?? null) setResourceListOffset(0) setSelectedNamespace(ALL_NAMESPACE_OPTION) + setLastTimeStringSinceClearAllFilters(new Date().toISOString()) } 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}` + return `f-${statusPostfix} ${isNodeListing ? 'dc__capitalize' : ''}` } const handleResourceClick = (e) => onResourceClick(e, shouldOverrideSelectedResourceKind) @@ -358,6 +418,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 (
- {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} + /> + ))}
) : (
- + {columnName === 'errors' && isNodeListingAndNodeHasErrors && ( + + )} + + {columnName === 'status' && isNodeUnschedulable && ( + <> + + + SchedulingDisabled + + + )} {columnName === 'restarts' && Number(resourceData.restarts) !== 0 && @@ -483,7 +579,7 @@ const BaseResourceListContent = ({ } const renderContent = () => { - if (!resourceListError && (isLoading || !resourceList || !filteredResourceList)) { + if (!resourceListError && (isLoading || !resourceList || !filteredResourceList || !selectedResource)) { return } @@ -537,7 +633,7 @@ const BaseResourceListContent = ({ className="scrollable-resource-list__row no-hover-bg h-36 fw-6 cn-7 fs-12 dc__gap-16 dc__zi-2 dc__position-sticky dc__border-bottom dc__uppercase bcn-0 dc__top-0" style={{ gridTemplateColumns }} > - {resourceList?.headers.map((columnName, index) => ( + {headers.map((columnName, index) => (
{!hideBulkSelection && index === 0 && ( @@ -583,22 +679,32 @@ 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..ed0a64d034 --- /dev/null +++ b/src/components/ResourceBrowser/ResourceList/ColumnSelector.tsx @@ -0,0 +1,103 @@ +/* + * 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, useMemo, useRef } from 'react' +import { MultiValue, SelectInstance } from 'react-select' +import { Button, ButtonVariantType, SelectPicker, SelectPickerOptionType } from '@devtron-labs/devtron-fe-common-lib' +import { ReactComponent as ICGears } from '@Icons/ic-nav-gear.svg' +import { OPTIONAL_NODE_LIST_HEADERS } from '../Constants' +import { ColumnSelectorType } from '../Types' +import { saveAppliedColumnsInLocalStorage } from './utils' + +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 = useRef, true>>(null) + + const handleMenuOpen = () => { + setIsMenuOpen(true) + + selectRef.current?.focus() + } + + const handleMenuClose = () => { + setIsMenuOpen(false) + + selectRef.current?.blur() + } + + const handleApplySelectedColumns = (): void => { + setIsMenuOpen(false) + + const newVisibleColumns = selectedColumns.map((option) => option.value) + + saveAppliedColumnsInLocalStorage(newVisibleColumns) + + selectRef.current?.blur() + + setVisibleColumns(newVisibleColumns) + } + + const renderMenuListFooter = () => ( +
+
+ ) + + return ( + } + isSearchable + menuIsOpen={isMenuOpen} + onMenuOpen={handleMenuOpen} + onMenuClose={handleMenuClose} + isMulti + onChange={setSelectedColumns} + placeholder="Column" + options={columnOptions} + value={selectedColumns} + renderMenuListFooter={renderMenuListFooter} + isClearable={false} + /> + ) +} + +export default ColumnSelector diff --git a/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx b/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx index b536390273..e8817e6395 100644 --- a/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx +++ b/src/components/ResourceBrowser/ResourceList/K8SResourceList.tsx @@ -14,20 +14,13 @@ * limitations under the License. */ -import { - useAsync, - abortPreviousRequests, - Nodes, - getIsRequestAborted, - getK8sResourceList, - showError, -} from '@devtron-labs/devtron-fe-common-lib' +import { useAsync, abortPreviousRequests, Nodes, getIsRequestAborted } from '@devtron-labs/devtron-fe-common-lib' import { useMemo, useRef, useState } from 'react' import { useParams, useLocation } from 'react-router-dom' import { getPodRestartRBACPayload } from '@Components/v2/appDetails/k8Resource/nodeDetail/nodeDetail.api' import { importComponentFromFELibrary } from '../../common/helpers/Helpers' import { ALL_NAMESPACE_OPTION, SIDEBAR_KEYS } from '../Constants' -import { getResourceListPayload } from '../ResourceBrowser.service' +import { getResourceData } from '../ResourceBrowser.service' import { K8SResourceListType, URLParams } from '../Types' import { sortEventListData, removeDefaultForStorageClass } from '../Utils' import BaseResourceList from './BaseResourceList' @@ -68,26 +61,20 @@ export const K8SResourceList = ({ const [resourceListLoader, _resourceList, _resourceListDataError, reloadResourceListData] = useAsync( () => - abortPreviousRequests( - () => - getK8sResourceList( - getResourceListPayload( - clusterId, - selectedNamespace.value.toLowerCase(), - selectedResource, - filters, - ), - abortControllerRef.current.signal, - ).catch((err) => { - if (!getIsRequestAborted(err)) { - showError(err) - throw err - } - }), - abortControllerRef, - ), + abortPreviousRequests(async () => { + if (selectedResource) { + return getResourceData({ + selectedResource, + selectedNamespace, + clusterId, + filters, + abortControllerRef, + }) + } + + return null + }, abortControllerRef), [selectedResource, clusterId, selectedNamespace, filters], - selectedResource && selectedResource.gvk.Kind !== SIDEBAR_KEYS.nodeGVK.Kind, ) const resourceListDataError = getIsRequestAborted(_resourceListDataError) ? null : _resourceListDataError @@ -132,10 +119,10 @@ export const K8SResourceList = ({ isOpen={isOpen} renderRefreshBar={renderRefreshBar} updateK8sResourceTab={updateK8sResourceTab} - hideBulkSelection={!getFilterOptionsFromSearchParams} // NOTE: checking for fe-lib linking nodeType={nodeType} group={group} addTab={addTab} + hideBulkSelection={!getFilterOptionsFromSearchParams} // NOTE: hideBulkSelection if fe-lib not linked setWidgetEventDetails={setWidgetEventDetails} lowercaseKindToResourceGroupMap={lowercaseKindToResourceGroupMap} handleResourceClick={handleResourceClick} 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/ResourceBrowser/ResourceList/NodeActionsMenu.tsx b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx new file mode 100644 index 0000000000..bcd1215e11 --- /dev/null +++ b/src/components/ResourceBrowser/ResourceList/NodeActionsMenu.tsx @@ -0,0 +1,228 @@ +/* + * 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 } from 'react' +import { useHistory, useLocation, useRouteMatch } from 'react-router-dom' +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' +import { ReactComponent as CordonIcon } from '../../../assets/icons/ic-cordon.svg' +import { ReactComponent as UncordonIcon } from '../../../assets/icons/ic-play-medium.svg' +import { ReactComponent as DrainIcon } from '../../../assets/icons/ic-clean-brush.svg' +import { ReactComponent as EditTaintsIcon } from '../../../assets/icons/ic-spraycan.svg' +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 '../../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 +const NodeActionsMenu = ({ nodeData, getNodeListData, addTab }: NodeActionsMenuProps) => { + const history = useHistory() + const { url } = useRouteMatch() + const location = useLocation() + + 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 + + const handleOpenTerminalAction = () => { + 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 = () => { + 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 => { + setShowCordonNodeDialog(true) + } + + const hideCordonNodeModal = (refreshData?: boolean): void => { + setShowCordonNodeDialog(false) + if (refreshData) { + getNodeListData() + } + } + + const showDrainNodeModal = (): void => { + setShowDrainNodeDialog(true) + } + + const hideDrainNodeModal = (refreshData?: boolean): void => { + setShowDrainNodeDialog(false) + if (refreshData) { + getNodeListData() + } + } + + const showDeleteNodeModal = (): void => { + setShowDeleteNodeDialog(true) + } + + const hideDeleteNodeModal = (refreshData?: boolean): void => { + setShowDeleteNodeDialog(false) + if (refreshData) { + getNodeListData() + } + } + + const showEditTaintsModal = (): void => { + setShowEditTaintNodeDialog(true) + } + + const hideEditTaintsModal = (refreshData?: boolean): void => { + setShowEditTaintNodeDialog(false) + if (refreshData) { + getNodeListData() + } + } + + const renderModal = () => { + if (showCordonNodeDialog) { + return ( + + ) + } + + if (showDrainNodeDialog) { + return + } + + if (showDeleteNodeDialog) { + return + } + + if (showEditTaintNodeDialog) { + return ( + + ) + } + + return null + } + + const menuListItemButtonClassName = 'flex left h-36 cursor pl-12 pr-12 dc__hover-n50 dc__transparent w-100' + + return ( + <> + + + + + +
+ + + + + + +
+
+
+ {renderModal()} + + ) +} + +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..f332bd87d1 --- /dev/null +++ b/src/components/ResourceBrowser/ResourceList/NodeListSearchFilter.tsx @@ -0,0 +1,322 @@ +/* + * 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, useParams } from 'react-router-dom' +import { ParsedQuery, parse as parseQueryString, stringify as stringifyQueryString } from 'query-string' +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, URLParams } 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 = ({ + 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 + } + + 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 [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, 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 ( +
+ + + {isSearchKeySelectorOpen && ( + <> + + ))} +
+ )} + + )} + + {(searchTextType || searchInputText) && ( + + )} +
+ ) + } + + return ( +
+ {renderTextFilter()} + + + +
+ + +
+ ) +} + +export default NodeListSearchFilter diff --git a/src/components/ResourceBrowser/ResourceList/ResourceList.tsx b/src/components/ResourceBrowser/ResourceList/ResourceList.tsx index 7616609ba3..ca0f4fb0a7 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, @@ -89,20 +88,12 @@ 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]) - 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 +135,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 +189,33 @@ 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), []) + useEffect(() => { + const terminalTab = getTabById(ResourceBrowserTabsId.terminal) + const newLabel = `Terminal '${selectedCluster.label}'` + + // NOTE: we don't have cluster name on mount therefore need + // to update the dynamicTitle once we have fetched the cluster name + if (terminalTab && terminalTab.dynamicTitle !== newLabel) { + updateTabUrl({ + id: terminalTab.id, + url: terminalTab.url, + dynamicTitle: newLabel, + }) + } + }, [getTabById(ResourceBrowserTabsId.terminal)?.dynamicTitle, selectedCluster]) useEffectAfterMount(() => initTabsBasedOnRole(true), [clusterId]) useEffectAfterMount(() => { @@ -361,7 +358,7 @@ const ResourceList = () => { let _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 @@ -416,7 +413,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)} @@ -461,8 +456,8 @@ const ResourceList = () => { lowercaseKindToResourceGroupMap={lowercaseKindToResourceGroupMap} />, ...(MonitoringDashboard ? [] : []), - ...(isSuperAdmin && getTabById(ResourceBrowserTabsId.terminal)?.isAlive - ? [] + ...(getTabById(ResourceBrowserTabsId.terminal)?.isAlive + ? [] : []), ] @@ -471,7 +466,7 @@ const ResourceList = () => { return } - if (loading) { + if (loading || !tabs.length) { return } diff --git a/src/components/ResourceBrowser/ResourceList/Sidebar.tsx b/src/components/ResourceBrowser/ResourceList/Sidebar.tsx index ada9246fd1..d746268ff1 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.find((option) => option.dataset.kind.toLowerCase() === nodeType) ?? + 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 : (
- - - + + Pods for this application will be + + scaled + {hibernateConfirmationModal === 'hibernate' ? ' down to 0 ' : ' up to its original count '} + on {appDetails.environmentName} + + environment. +

+ } + buttonConfig={{ + secondaryButtonConfig: { + disabled: hibernating, + onClick: handleHibernateConfirmationModalClose, + text: 'Cancel', + }, + primaryButtonConfig: { + isLoading: hibernating, + onClick: handleHibernate, + text: getHibernateText(), + }, + }} + showConfirmationModal={!!hibernateConfirmationModal} + handleClose={handleHibernateConfirmationModalClose} + > + Are you sure you want to continue? +
) } @@ -821,7 +814,7 @@ export const Details: React.FC = ({ renderCIListHeader={renderCIListHeader} /> )} - {hibernateConfirmationModal && renderHibernateModal()} + {appDetails && renderHibernateModal()} {rotateModal && renderRestartWorkload()} { = ({ isVirtualEnvironment={isVirtualEnvRef.current} /> } - {isConfigDriftEnabled && ConfigDriftModalRoute && !isVirtualEnvRef.current && } + {isConfigDriftEnabled && ConfigDriftModalRoute && !isVirtualEnvRef.current && ( + + )} ) } 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/appDetails/utils.tsx b/src/components/app/details/appDetails/utils.tsx index bc4a000bd9..1e603ece35 100644 --- a/src/components/app/details/appDetails/utils.tsx +++ b/src/components/app/details/appDetails/utils.tsx @@ -69,6 +69,8 @@ export function getAggregator(nodeType: NodeType, defaultAsOtherResources?: bool return AggregationKeys.Events case Nodes.Namespace: return AggregationKeys.Namespaces + case Nodes.Node: + return AggregationKeys.Nodes default: return defaultAsOtherResources ? AggregationKeys['Other Resources'] : AggregationKeys['Custom Resource'] } 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/app/list-new/AppListFilters.tsx b/src/components/app/list-new/AppListFilters.tsx index 9c57e592b3..75a54cae66 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) => ({ diff --git a/src/components/app/types.ts b/src/components/app/types.ts index 9ca6e15d1e..1719bce688 100644 --- a/src/components/app/types.ts +++ b/src/components/app/types.ts @@ -439,6 +439,7 @@ export enum Nodes { PodDisruptionBudget = 'PodDisruptionBudget', Event = 'Event', Namespace = 'Namespace', + Node = 'Node', Overview = 'Overview', MonitoringDashboard = 'MonitoringDashboard', UpgradeCluster = 'UpgradeCluster', @@ -458,6 +459,7 @@ export enum AggregationKeys { 'Other Resources' = 'Other Resources', Events = 'Events', Namespaces = 'Namespaces', + Nodes = 'Nodes', } export type AggregationKeysType = keyof typeof AggregationKeys 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/cdPipeline/CDPipeline.tsx b/src/components/cdPipeline/CDPipeline.tsx index 9621564097..806919c594 100644 --- a/src/components/cdPipeline/CDPipeline.tsx +++ b/src/components/cdPipeline/CDPipeline.tsx @@ -1411,9 +1411,9 @@ export default function CDPipeline({ reload={reloadAppConfig} /> )} - - {cdPipelineId && showDeleteModal && ( + {cdPipelineId && ( 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: { + text: 'Cancel', + onClick: hideDeleteModal, + disabled: isLoading, + }, + primaryButtonConfig: { + text: 'Delete', + onClick: () => handleDeleteCDNodePipeline(deleteCD, deploymentAppType as DeploymentAppTypes), + isLoading, + }, + }} + customInputConfig={{ + identifier: 'delete-cd-node-input', + confirmationKeyword: deleteTitleName, + }} + showConfirmationModal={showDeleteDialog} + handleClose={hideDeleteModal} /> ) } diff --git a/src/components/cdPipeline/types.ts b/src/components/cdPipeline/types.ts index 77b46e8df7..8c23c4aaa6 100644 --- a/src/components/cdPipeline/types.ts +++ b/src/components/cdPipeline/types.ts @@ -46,6 +46,7 @@ export interface ForceDeleteMessageType { } export interface DeleteCDNodeProps { + showDeleteDialog: boolean deleteDialog: DeleteDialogType setDeleteDialog: React.Dispatch> | ((deleteDialog: DeleteDialogType) => void) clusterName: string 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/charts.service.ts b/src/components/charts/charts.service.ts index d57b557fdd..7b611dee9f 100644 --- a/src/components/charts/charts.service.ts +++ b/src/components/charts/charts.service.ts @@ -72,11 +72,9 @@ export function deleteInstalledChart( deleteAction?: DELETE_ACTION, ) { const baseUrl: string = `app-store/deployment/application/delete/${installedAppId}` - let params: DeleteInstalledChartParamsType = {} + const params: DeleteInstalledChartParamsType = {} if (deleteAction === DELETE_ACTION.FORCE_DELETE) { - params = { - forceDelete: true, - } + params['force'] = true } else if (isGitops) { params['partialDelete'] = true if (deleteAction === DELETE_ACTION.NONCASCADE_DELETE) { diff --git a/src/components/charts/charts.types.tsx b/src/components/charts/charts.types.tsx index b155b975f1..75859e8749 100644 --- a/src/components/charts/charts.types.tsx +++ b/src/components/charts/charts.types.tsx @@ -405,7 +405,7 @@ export interface ChartHeaderFilterProps { } export interface DeleteInstalledChartParamsType { - forceDelete?: true + force?: true partialDelete?: true cascade?: false } 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/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/components/common/Description/GenericDescription.tsx b/src/components/common/Description/GenericDescription.tsx index 3c33f04f1a..7780686ec0 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,13 +47,11 @@ 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({ isClusterTerminal, clusterId, - isSuperAdmin, appId, descriptionId, initialDescriptionText, @@ -66,7 +64,6 @@ export default function GenericDescription({ }: { isClusterTerminal: boolean clusterId?: string - isSuperAdmin: boolean appId?: number descriptionId?: number initialDescriptionText?: string @@ -133,7 +130,7 @@ export default function GenericDescription({ } const isAuthorized = (): boolean => { - if (!isSuperAdmin && isClusterTerminal) { + if (isClusterTerminal) { ToastManager.showToast({ variant: ToastVariantType.notAuthorized, description: TOAST_ACCESS_DENIED.SUBTITLE, diff --git a/src/components/common/DynamicTabs/useTabs.ts b/src/components/common/DynamicTabs/useTabs.ts index 782ad7ccc3..c396fe8d6f 100644 --- a/src/components/common/DynamicTabs/useTabs.ts +++ b/src/components/common/DynamicTabs/useTabs.ts @@ -223,8 +223,6 @@ export function useTabs(persistanceKey: string) { } _tabs[index].isSelected = _initTab.isSelected - /* NOTE: dynamic title might get updated between re-initialization */ - _tabs[index].dynamicTitle = _initTab.dynamicTitle _tabs[index].isAlive = _initTab.isAlive _tabs[index].tippyConfig = _initTab.tippyConfig return false @@ -253,7 +251,7 @@ export function useTabs(persistanceKey: string) { }) localStorage.setItem(TAB_DATA_LOCAL_STORAGE_KEY, stringifyData(_tabs, parsedTabsData)) - return _tabs + return [..._tabs] }) } diff --git a/src/components/common/helpers/Helpers.tsx b/src/components/common/helpers/Helpers.tsx index da4a19b1df..cece1b7e26 100644 --- a/src/components/common/helpers/Helpers.tsx +++ b/src/components/common/helpers/Helpers.tsx @@ -884,6 +884,7 @@ export const processK8SObjects = ( isExpanded: element.gvk.Kind !== SIDEBAR_KEYS.namespaceGVK.Kind && element.gvk.Kind !== SIDEBAR_KEYS.eventGVK.Kind && + element.gvk.Kind !== SIDEBAR_KEYS.nodeGVK.Kind && element.gvk.Kind.toLowerCase() === selectedResourceKind, child: [k8sObject], }) @@ -893,6 +894,7 @@ export const processK8SObjects = ( currentData.isExpanded = element.gvk.Kind !== SIDEBAR_KEYS.namespaceGVK.Kind && element.gvk.Kind !== SIDEBAR_KEYS.eventGVK.Kind && + element.gvk.Kind !== SIDEBAR_KEYS.nodeGVK.Kind && element.gvk.Kind.toLowerCase() === selectedResourceKind } } @@ -904,6 +906,10 @@ export const processK8SObjects = ( JUMP_TO_KIND_SHORT_NAMES.namespaces = shortNames SIDEBAR_KEYS.namespaceGVK = { ...element.gvk } } + if (element.gvk.Kind === SIDEBAR_KEYS.nodeGVK.Kind) { + JUMP_TO_KIND_SHORT_NAMES.node = shortNames + SIDEBAR_KEYS.nodeGVK = { ...element.gvk } + } } for (const [, _k8sObject] of _k8SObjectMap.entries()) { _k8sObject.child.sort((a, b) => a['gvk']['Kind'].localeCompare(b['gvk']['Kind'])) diff --git a/src/components/common/navigation/NavigationRoutes.tsx b/src/components/common/navigation/NavigationRoutes.tsx index acbd8181cb..b5e79193c9 100644 --- a/src/components/common/navigation/NavigationRoutes.tsx +++ b/src/components/common/navigation/NavigationRoutes.tsx @@ -28,6 +28,7 @@ import { URLS as CommonURLS, AppListConstants, MODES, + DEVTRON_BASE_MAIN_ID, } from '@devtron-labs/devtron-fe-common-lib' import { Route, Switch, useRouteMatch, useHistory, useLocation } from 'react-router-dom' import * as Sentry from '@sentry/browser' @@ -380,7 +381,7 @@ export default function NavigationRoutes() { isSuperAdmin, }} > -
    +
    {!_isOnboardingPage && ( void cardLoading: boolean + onClickUpgrade: () => void } export interface HelmAppConfigApplyStatusCardType { releaseStatus: HelmReleaseStatus cardLoading: boolean } + +export interface ChartToolTipType extends Pick { + children: React.ReactElement + 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 5f52439607..77ad58c32e 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/ChartUsedCard.tsx @@ -16,40 +16,63 @@ 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' 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 ChartToolTip = ({ children, isDeprecated, onClickUpgrade, chartRef }: ChartToolTipType) => ( + 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) -const ChartUsedCard = ({ appDetails, notes, onClickShowNotes, cardLoading }: ChartUsedCardType) => { if (cardLoading) { return } return ( -
    +
    -
    - 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..e1dc323126 100644 --- a/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx +++ b/src/components/v2/appDetails/sourceInfo/environmentStatus/EnvironmentStatus.component.tsx @@ -169,27 +169,11 @@ const EnvironmentStatusComponent = ({ notes={notes} onClickShowNotes={onClickShowNotes} cardLoading={cardLoading} + onClickUpgrade={onClickUpgrade} /> ) } - const renderUpgraderChartBlock = () => { - return ( - appDetails?.deprecated && ( -
    -
    - Chart deprecated - -
    -
    Upgrade required
    -
    - Upgrade chart -
    -
    - ) - ) - } - return (
    {loadingDetails ? ( @@ -201,7 +185,6 @@ const EnvironmentStatusComponent = ({ {renderHelmConfigApplyStatusBlock()} {renderLastUpdatedBlock()} {renderChartUsedBlock()} - {renderUpgraderChartBlock()} {isScanV2Enabled && appDetails?.appType === AppType.DEVTRON_HELM_CHART && }
    )} 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/appDetails/sourceInfo/utils.tsx b/src/components/v2/appDetails/sourceInfo/utils.tsx index 791317b7e3..c45a38d01c 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,31 @@ 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
    +
    + )} +
    + {/* Due to missing support of white text, unable to use Button component */} + +
    +) 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/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 06acb69809..74b75380d7 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, @@ -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,15 @@ export const ChartValuesSelector = ({ const handleChange: SelectPickerProps['onChange'] = (selectedOption) => handleChartValuesSelection(selectedOption.value) - const selectedOption = selectOptions.flatMap(groupedOption => groupedOption.options).find(option => getOptionValue(option) === getOptionValue({ + const chartValuesOptionValue = getOptionValue({ // Setting label null since the getOptionValue is not consuming it label: null, - value: chartValues - })) + value: chartValues, + }) + + const selectedOption = selectOptions + .flatMap((groupedOption) => groupedOption.options) + .find((option) => getOptionValue(option) === chartValuesOptionValue) return ( @@ -623,14 +621,13 @@ export const ChartVersionValuesSelector = ({ export const ActiveReadmeColumn = ({ fetchingReadMe, activeReadMe }: ActiveReadmeColumnProps) => { return (
    -
    +
    Readme
    - {fetchingReadMe ? ( - - ) : ( - - )} + {fetchingReadMe ? : }
    ) } @@ -728,6 +725,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 && ( { } renderWrapWithLink = (children: ReactElement) => ( - + {children} ) @@ -306,7 +311,7 @@ export class CDNode extends Component { const currentNodeKey = `${WorkflowNodeType.CD}-${this.props.id.substring(4)}` return (
    { > {this.props.cdNamesList?.length > 0 ? this.renderReadOnlyCard() : this.renderCardContent()} - - {this.state.showDeleteDialog && ( - - )} + + {this.renderDeleteConformationDialog()} ) diff --git a/src/config/searchWorker.ts b/src/config/searchWorker.ts index 1c9860f06b..920424ade9 100644 --- a/src/config/searchWorker.ts +++ b/src/config/searchWorker.ts @@ -21,6 +21,21 @@ 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', + } + + /** + * 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) @@ -68,6 +83,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 +99,14 @@ 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, + 'cpu usage (absolute)': numberInStringComparator, } /** @@ -85,10 +116,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)) { @@ -125,21 +156,73 @@ 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] + + if (!isK8sVersionFilterAppliedAndMatchFound) { + return false + } + + 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, 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) => isItemASearchMatchForNodeListing(item, searchParams)) + } else if (searchTextLowerCased !== '' && list?.length) { filteredList = list.filter((item) => Object.entries(item).some( ([key, value]) => key !== 'id' && String(value).toLowerCase().includes(searchTextLowerCased), @@ -148,7 +231,7 @@ export default () => { } if (sortBy && sortOrder) { - filteredList.sort(dynamicSort(sortBy, sortOrder)) + filteredList.sort(dynamicSort(sortBy, sortOrder, isNodeListing)) } self.postMessage(filteredList) @@ -163,6 +246,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) { diff --git a/src/css/base.scss b/src/css/base.scss index fbe65194a6..725ae3e252 100644 --- a/src/css/base.scss +++ b/src/css/base.scss @@ -818,8 +818,10 @@ button.anchor { &.f-sync.ok, &.f-running, &.f-completed, + &.f-complete, &.f-bound, &.f-active, + &.f-ready, &.f-created, &.f-scalingreplicasetdown { color: var(--G500); @@ -847,6 +849,7 @@ button.anchor { &.f-crashloopbackoff, &.f-init__crashloopbackoff, &.f-deleted, + &.f-not__ready, &.f-evicted { color: var(--R500); } @@ -2467,6 +2470,10 @@ button.anchor { background-color: var(--N100); } +.dc__bs-contain { + background-size: contain !important; +} + .dc__registry-icon, .repository-icon { width: 20px; @@ -4005,6 +4012,10 @@ textarea, margin-bottom: 0px !important; } +.mt-40 { + margin-top: 40px; +} + .mt-120 { margin-top: 120px; } @@ -5238,4 +5249,18 @@ textarea::placeholder { .dc__open-sans { font-family: 'Open Sans'; +} + +.code-editor-green-diff { + background: var(--G100); +} + +.code-editor-red-diff { + background: var(--R100); +} + +.backdrop { + background: rgba(0, 0, 0, 0.75); + z-index: var(--modal-index); + backdrop-filter: blur(1px); } \ No newline at end of file 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); -} diff --git a/yarn.lock b/yarn.lock index 7302c8cf31..f8e583a36d 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,17 +974,20 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.1.0-patch-1": - version "1.1.0-patch-1" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.0-patch-1.tgz#48b2516b5cf198df0c7f3c49d6bdb09da795fd5f" - integrity sha512-mFZjjA7/Gzqo4nUfMD7qc814v8sd5RbFR0SoEh/UZNl+1/YakqfqqZaXF4BOTpdI95uM6UpSOqdjzhxoWUDJ/g== +"@devtron-labs/devtron-fe-common-lib@1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.1.6.tgz#13390eaef23265dfaf2525506fc37c263df3416c" + integrity sha512-t5o8rjIBxaUfL97yhXZHJUZDhiwhRQOgWGqaulD2PqzCei9I8rP+0wlwQbbRHh73HFSc3sxmesItYoHGeQhNJw== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" dayjs "^1.11.13" fast-json-patch "^3.1.1" + framer-motion "^6.5.1" jsonpath-plus "^10.0.0" + marked "^13.0.3" 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 +1009,23 @@ source-map "^0.5.7" stylis "4.2.0" +"@emotion/babel-plugin@^11.13.5": + version "11.13.5" + resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz" + 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 +1037,60 @@ "@emotion/weak-memoize" "^0.3.1" stylis "4.2.0" +"@emotion/cache@^11.13.5": + version "11.13.5" + resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.5.tgz" + 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.npmjs.org/@emotion/css/-/css-11.13.5.tgz" + 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.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz" + integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== + +"@emotion/is-prop-valid@^0.8.2": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + "@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.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz" + 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 +1116,32 @@ "@emotion/utils" "^1.2.1" csstype "^3.0.2" +"@emotion/serialize@^1.3.3": + version "1.3.3" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz" + 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.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz" + integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== + +"@emotion/unitless@^0.10.0": + version "0.10.0" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz" + 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 +1157,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.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz" + 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.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" + 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" @@ -1129,7 +1224,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 +1511,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" @@ -1489,6 +1584,59 @@ dependencies: "@types/mdx" "^2.0.0" +"@motionone/animation@^10.12.0": + version "10.18.0" + resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.18.0.tgz#868d00b447191816d5d5cf24b1cafa144017922b" + integrity sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw== + dependencies: + "@motionone/easing" "^10.18.0" + "@motionone/types" "^10.17.1" + "@motionone/utils" "^10.18.0" + tslib "^2.3.1" + +"@motionone/dom@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.12.0.tgz#ae30827fd53219efca4e1150a5ff2165c28351ed" + integrity sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw== + dependencies: + "@motionone/animation" "^10.12.0" + "@motionone/generators" "^10.12.0" + "@motionone/types" "^10.12.0" + "@motionone/utils" "^10.12.0" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/easing@^10.18.0": + version "10.18.0" + resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.18.0.tgz#7b82f6010dfee3a1bb0ee83abfbaff6edae0c708" + integrity sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg== + dependencies: + "@motionone/utils" "^10.18.0" + tslib "^2.3.1" + +"@motionone/generators@^10.12.0": + version "10.18.0" + resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.18.0.tgz#fe09ab5cfa0fb9a8884097feb7eb60abeb600762" + integrity sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg== + dependencies: + "@motionone/types" "^10.17.1" + "@motionone/utils" "^10.18.0" + tslib "^2.3.1" + +"@motionone/types@^10.12.0", "@motionone/types@^10.17.1": + version "10.17.1" + resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.17.1.tgz#cf487badbbdc9da0c2cb86ffc1e5d11147c6e6fb" + integrity sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A== + +"@motionone/utils@^10.12.0", "@motionone/utils@^10.18.0": + version "10.18.0" + resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.18.0.tgz#a59ff8932ed9009624bca07c56b28ef2bb2f885e" + integrity sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw== + dependencies: + "@motionone/types" "^10.17.1" + hey-listen "^1.0.8" + tslib "^2.3.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -1614,95 +1762,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.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz#e3c9cc13f144ba033df4d2c3130a214dc8e3473e" + integrity sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw== + +"@rollup/rollup-android-arm64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz#0474250fcb5871aca952e249a0c3270fc4310b55" + integrity sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA== + +"@rollup/rollup-darwin-arm64@4.27.4": + version "4.27.4" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz" + integrity sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q== + +"@rollup/rollup-darwin-x64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz#7d87711f641a458868758cbf110fb32eabd6a25a" + integrity sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ== + +"@rollup/rollup-freebsd-arm64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz#662f808d2780e4e91021ac9ee7ed800862bb9a57" + integrity sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw== + +"@rollup/rollup-freebsd-x64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz#71e5a7bcfcbe51d8b65d158675acec1307edea79" + integrity sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA== + +"@rollup/rollup-linux-arm-gnueabihf@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz#08f67fcec61ee18f8b33b3f403a834ab8f3aa75d" + integrity sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w== + +"@rollup/rollup-linux-arm-musleabihf@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz#2e1ad4607f86475b1731556359c6070eb8f4b109" + integrity sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A== + +"@rollup/rollup-linux-arm64-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz#c65d559dcb0d3dabea500cf7b8215959ae6cccf8" + integrity sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg== + +"@rollup/rollup-linux-arm64-musl@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz#6739f7eb33e20466bb88748519c98ce8dee23922" + integrity sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA== + +"@rollup/rollup-linux-powerpc64le-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz#8d9fe9471c256e55278cb1f7b1c977cd8fe6df20" + integrity sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ== + +"@rollup/rollup-linux-riscv64-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz#9a467f7ad5b61c9d66b24e79a3c57cb755d02c35" + integrity sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw== + +"@rollup/rollup-linux-s390x-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz#efaddf22df27b87a267a731fbeb9539e92cd4527" + integrity sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg== + +"@rollup/rollup-linux-x64-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz#a959eccb04b07fd1591d7ff745a6865faa7042cd" + integrity sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q== + +"@rollup/rollup-linux-x64-musl@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz#927764f1da1f2dd50943716dec93796d10cb6e99" + integrity sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw== + +"@rollup/rollup-win32-arm64-msvc@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz#030b6cc607d845da23dced624e47fb45de105840" + integrity sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A== + +"@rollup/rollup-win32-ia32-msvc@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz#3457a3f44a84f51d8097c3606429e01f0d2d0ec2" + integrity sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ== + +"@rollup/rollup-win32-x64-msvc@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz#67d516613c9f2fe42e2d8b78e252d0003179d92c" + integrity sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug== "@sentry-internal/feedback@7.119.1": version "7.119.1" @@ -1901,7 +2049,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 +2057,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 +2068,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 +2077,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 +2086,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 +2099,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 +2115,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 +2133,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 +2142,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 +2150,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 +2158,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 +2179,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 +2188,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 +2210,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 +2224,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 +2236,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 +2249,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 +2279,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 +2291,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 +2305,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 +2403,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 +2431,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 +2622,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 +2677,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" @@ -2575,7 +2713,7 @@ "@types/react-dates@^21.8.6": version "21.8.6" - resolved "https://registry.yarnpkg.com/@types/react-dates/-/react-dates-21.8.6.tgz#ec9314b59e9d8e1ad71ccf021a7634e8afd26135" + resolved "https://registry.npmjs.org/@types/react-dates/-/react-dates-21.8.6.tgz" integrity sha512-fDF322SOXAxstapv0/oruiPx9kY4DiiaEHYAVvXdPfQhi/hdaONsA9dFw5JBNPAWz7Niuwk+UUhxPU98h70TjA== dependencies: "@types/react" "*" @@ -2591,7 +2729,7 @@ "@types/react-outside-click-handler@*": version "1.3.4" - resolved "https://registry.yarnpkg.com/@types/react-outside-click-handler/-/react-outside-click-handler-1.3.4.tgz#999e61057a3a23c6dfc9159b28f96378749d6c42" + resolved "https://registry.npmjs.org/@types/react-outside-click-handler/-/react-outside-click-handler-1.3.4.tgz" integrity sha512-kLuYIa9nWk1n0ywJPbVWqOEIRg0mh3vumriCHbz6LUObJw4rXYx9inDm8G579BtnH8vC0wKfrTq5c2y/K/Xzww== dependencies: "@types/react" "*" @@ -2858,7 +2996,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 +3044,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 +3077,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 +3132,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" @@ -3035,7 +3173,7 @@ ansi-styles@^6.0.0: ansi_up@^5.2.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/ansi_up/-/ansi_up-5.2.1.tgz#9437082c7ad4975c15ec57d30a6b55da295bee36" + resolved "https://registry.npmjs.org/ansi_up/-/ansi_up-5.2.1.tgz" integrity sha512-5bz5T/7FRmlxA37zDXhG6cAwlcZtfnmNLDJra66EEIT3kYlw5aPJdbkJEhm59D6kA4Wi5ict6u6IDYHJaQlH+g== anymatch@~3.1.2: @@ -3204,7 +3342,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 +3414,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" @@ -3442,21 +3580,26 @@ 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.npmjs.org/classnames/-/classnames-2.5.1.tgz" + 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" + 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 +3607,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 +3649,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 +3669,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: @@ -3752,7 +3895,7 @@ data-view-byte-offset@^1.0.0: dayjs@^1.11.13: version "1.11.13" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== dayjs@^1.11.8: @@ -3776,7 +3919,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 +3989,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: @@ -3878,6 +4021,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.npmjs.org/diff/-/diff-5.2.0.tgz" + 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" @@ -3936,7 +4084,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 +4101,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 +4290,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 +4649,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 +4664,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" @@ -4659,6 +4807,27 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +framer-motion@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" + integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw== + dependencies: + "@motionone/dom" "10.12.0" + framesync "6.0.1" + hey-listen "^1.0.8" + popmotion "11.0.3" + style-value-types "5.0.0" + tslib "^2.1.0" + optionalDependencies: + "@emotion/is-prop-valid" "^0.8.2" + +framesync@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20" + integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA== + dependencies: + tslib "^2.1.0" + fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" @@ -4732,7 +4901,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: @@ -4897,6 +5066,11 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + history@^4.9.0: version "4.10.1" resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz" @@ -4931,7 +5105,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 +5273,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 +5290,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 +5432,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 +5557,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 +5720,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 +5730,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 +5750,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 +5817,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 +5841,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,10 +5892,10 @@ 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== +marked@^13.0.3: + version "13.0.3" + resolved "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz" + integrity sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA== memoize-one@^6.0.0: version "6.0.0" @@ -5755,7 +5929,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 +6044,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 +6076,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 +6095,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 +6181,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 +6195,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 +6230,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 +6326,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: @@ -6176,6 +6350,16 @@ polished@^4.2.2: dependencies: "@babel/runtime" "^7.17.8" +popmotion@11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9" + integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA== + dependencies: + framesync "6.0.1" + hey-listen "^1.0.8" + style-value-types "5.0.0" + tslib "^2.1.0" + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz" @@ -6341,6 +6525,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.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-3.4.0.tgz" + 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" @@ -6423,7 +6618,7 @@ react-moment-proptypes@^1.6.0: react-monaco-editor@^0.54.0: version "0.54.0" - resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.54.0.tgz#ec9293249a991b08264be723c1ec0ca3a6d480d8" + resolved "https://registry.npmjs.org/react-monaco-editor/-/react-monaco-editor-0.54.0.tgz" integrity sha512-9JwO69851mfpuhYLHlKbae7omQWJ/2ICE2lbL0VHyNyZR8rCOH7440u+zAtDgiOMpLwmYdY1sEZCdRefywX6GQ== dependencies: prop-types "^15.8.1" @@ -6761,7 +6956,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 +6969,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 +7003,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.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.27.4.tgz#b23e4ef4fe4d0d87f5237dacf63f95a499503897" + integrity sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw== 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.4" + "@rollup/rollup-android-arm64" "4.27.4" + "@rollup/rollup-darwin-arm64" "4.27.4" + "@rollup/rollup-darwin-x64" "4.27.4" + "@rollup/rollup-freebsd-arm64" "4.27.4" + "@rollup/rollup-freebsd-x64" "4.27.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.27.4" + "@rollup/rollup-linux-arm-musleabihf" "4.27.4" + "@rollup/rollup-linux-arm64-gnu" "4.27.4" + "@rollup/rollup-linux-arm64-musl" "4.27.4" + "@rollup/rollup-linux-powerpc64le-gnu" "4.27.4" + "@rollup/rollup-linux-riscv64-gnu" "4.27.4" + "@rollup/rollup-linux-s390x-gnu" "4.27.4" + "@rollup/rollup-linux-x64-gnu" "4.27.4" + "@rollup/rollup-linux-x64-musl" "4.27.4" + "@rollup/rollup-win32-arm64-msvc" "4.27.4" + "@rollup/rollup-win32-ia32-msvc" "4.27.4" + "@rollup/rollup-win32-x64-msvc" "4.27.4" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -6966,7 +7161,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 +7176,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 +7185,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 +7275,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 +7287,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 +7301,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 +7380,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 +7397,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: @@ -7229,6 +7424,14 @@ strip-json-comments@^3.1.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-value-types@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad" + integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA== + dependencies: + hey-listen "^1.0.8" + tslib "^2.1.0" + stylis@4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz" @@ -7250,7 +7453,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 +7511,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: @@ -7450,7 +7653,7 @@ tsconfig-paths@^4.2.0: tslib@2.7.0, tslib@^2.0.1: version "2.7.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== tslib@^1.8.1, tslib@^1.9.3: @@ -7463,6 +7666,11 @@ tslib@^2.1.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.3.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslib@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" @@ -7494,7 +7702,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 +7852,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 +7956,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 +7994,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 +8185,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 +8193,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 +8243,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 +8255,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 +8268,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 +8285,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 +8301,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 +8310,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 +8336,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 +8350,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 +8365,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 +8378,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 +8394,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 +8403,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"