diff --git a/package-lock.json b/package-lock.json index 297fadd1c..3f402a30e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.0", + "version": "1.14.0-pre-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.0", + "version": "1.14.0-pre-0", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 198fe2859..733ca4fa2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.0", + "version": "1.14.0-pre-0", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Assets/IconV2/ic-app-template.svg b/src/Assets/IconV2/ic-app-template.svg new file mode 100644 index 000000000..70a64cf7a --- /dev/null +++ b/src/Assets/IconV2/ic-app-template.svg @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/src/Assets/IconV2/ic-devtron-app.svg b/src/Assets/IconV2/ic-devtron-app.svg new file mode 100644 index 000000000..adc694a17 --- /dev/null +++ b/src/Assets/IconV2/ic-devtron-app.svg @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/src/Assets/IconV2/ic-devtron-job.svg b/src/Assets/IconV2/ic-devtron-job.svg new file mode 100644 index 000000000..d5a2bc1bd --- /dev/null +++ b/src/Assets/IconV2/ic-devtron-job.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Assets/Img/empty-create.png b/src/Assets/Img/empty-create.png new file mode 100755 index 000000000..705f13dd7 Binary files /dev/null and b/src/Assets/Img/empty-create.png differ diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 49ce89f06..3243f76c2 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -22,16 +22,6 @@ export const DEVTRON_HOME_PAGE = 'https://devtron.ai/' export const DOCUMENTATION_VERSION = '/v/v0.7' export const DISCORD_LINK = 'https://discord.devtron.ai/' export const DEFAULT_JSON_SCHEMA_URI = 'https://json-schema.org/draft/2020-12/schema' -export const DOCUMENTATION = { - APP_METRICS: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/app-details/app-metrics`, - APP_TAGS: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/create-application#tags`, - APP_OVERVIEW_TAGS: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/overview#manage-tags`, - BLOB_STORAGE: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/getting-started/install/installation-configuration#configuration-of-blob-storage`, - GLOBAL_CONFIG_BUILD_INFRA: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/global-configurations/build-infra`, - ENTERPRISE_LICENSE: `${DOCUMENTATION_HOME_PAGE}/enterprise-license`, - KUBE_CONFIG: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/resource-browser#running-kubectl-commands-locally`, - TENANT_INSTALLATION: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/software-distribution-hub/tenants`, -} export const PATTERNS = { STRING: /^[a-zA-Z0-9_]+$/, diff --git a/src/Common/CustomTagSelector/PropagateTagInfo.tsx b/src/Common/CustomTagSelector/PropagateTagInfo.tsx index e15f2d119..d09e6739b 100644 --- a/src/Common/CustomTagSelector/PropagateTagInfo.tsx +++ b/src/Common/CustomTagSelector/PropagateTagInfo.tsx @@ -14,12 +14,10 @@ * limitations under the License. */ -import React from 'react' import { ReactComponent as InjectTag } from '../../Assets/Icon/inject-tag.svg' import { ReactComponent as ICHelpOutline } from '../../Assets/Icon/ic-help-outline.svg' import { TippyCustomized } from '../TippyCustomized' import { TippyTheme } from '../Types' -import { DOCUMENTATION } from '../Constants' export default function PropagateTagInfo({ isCreateApp }: { isCreateApp: boolean }) { const additionalInfo = () => ( @@ -47,7 +45,7 @@ export default function PropagateTagInfo({ isCreateApp }: { isCreateApp: boolean showCloseButton trigger="click" interactive - documentationLink={isCreateApp ? DOCUMENTATION.APP_TAGS : DOCUMENTATION.APP_OVERVIEW_TAGS} + documentationLink={isCreateApp ? "APP_TAGS" : "APP_OVERVIEW_TAGS"} documentationLinkText="View Documentation" >
diff --git a/src/Common/TippyCustomized.tsx b/src/Common/TippyCustomized.tsx index fbfaa4ec9..32ec5bbb3 100644 --- a/src/Common/TippyCustomized.tsx +++ b/src/Common/TippyCustomized.tsx @@ -19,15 +19,15 @@ import Tippy from '@tippyjs/react' import { ReactComponent as CloseIcon } from '../Assets/Icon/ic-cross.svg' import { ReactComponent as Help } from '../Assets/Icon/ic-help.svg' import { ReactComponent as ICHelpOutline } from '../Assets/Icon/ic-help-outline.svg' -import { ReactComponent as ICOpenInNew } from '../Assets/Icon/ic-open-in-new.svg' import 'tippy.js/animations/shift-toward-subtle.css' import 'tippy.js/animations/shift-toward.css' import { TippyCustomizedProps, TippyTheme } from './Types' import { not, stopPropagation } from './Helper' +import { DocLink } from '../Shared/DocLink' // This component will handle some of the new tippy designs and interactions // So this can be updated to support further for new features or interactions -export const TippyCustomized = (props: TippyCustomizedProps) => { +export const TippyCustomized = (props: TippyCustomizedProps) => { const tippyRef = useRef(null) const [showHeadingInfo, setShowHeadingInfo] = useState(false) const isWhiteTheme = props.theme === TippyTheme.white @@ -79,6 +79,8 @@ export const TippyCustomized = (props: TippyCustomizedProps) => { additionalContent, documentationLink, documentationLinkText, + isEnterprise, + isExternalLink, } = props return ( <> @@ -156,17 +158,16 @@ export const TippyCustomized = (props: TippyCustomizedProps) => { )} {additionalContent} {documentationLink && ( -
- + - {documentationLinkText || 'Learn more'} - - + isEnterprise={isEnterprise} + isExternalLink={isExternalLink} + docLinkKey={documentationLink} + />
)} diff --git a/src/Common/Types.ts b/src/Common/Types.ts index fcbb95b57..2a5597eff 100644 --- a/src/Common/Types.ts +++ b/src/Common/Types.ts @@ -33,6 +33,7 @@ import { ACTION_STATE, DEPLOYMENT_WINDOW_TYPE, DockerConfigOverrideType, + DOCUMENTATION, RefVariableType, SortingOrder, TaskErrorObj, @@ -119,52 +120,60 @@ export interface CheckboxProps { children?: ReactNode } -export interface TippyCustomizedProps extends Pick { - theme: TippyTheme - visible?: boolean - heading?: ReactNode | string - headingInfo?: ReactNode | string - noHeadingBorder?: boolean - infoTextHeading?: string - hideHeading?: boolean - placement?: TippyProps['placement'] - className?: string - Icon?: React.FunctionComponent> - iconPath?: string - iconClass?: string - iconSize?: number // E.g. 16, 20, etc.. Currently, there are around 12 sizes supported. Check `icons.css` or `base.scss` for supported sizes or add new size (class names starts with `icon-dim-`). - onImageLoadError?: (e) => void - onClose?: () => void - infoText?: React.ReactNode - showCloseButton?: boolean - arrow?: boolean - interactive?: boolean - showOnCreate?: boolean - trigger?: string - animation?: string - duration?: number - additionalContent?: ReactNode - documentationLink?: string - documentationLinkText?: string - children: React.ReactElement - disableClose?: boolean -} - -export interface InfoIconTippyProps +export type TippyWithBaseDocLinkTypes = { + isExternalLink?: T + isEnterprise?: boolean + documentationLink?: T extends true ? string : keyof typeof DOCUMENTATION +} + +export type TippyCustomizedProps = Pick & + TippyWithBaseDocLinkTypes & { + theme: TippyTheme + visible?: boolean + heading?: ReactNode | string + headingInfo?: ReactNode | string + noHeadingBorder?: boolean + infoTextHeading?: string + hideHeading?: boolean + placement?: TippyProps['placement'] + className?: string + Icon?: React.FunctionComponent> + iconPath?: string + iconClass?: string + iconSize?: number // E.g. 16, 20, etc.. Currently, there are around 12 sizes supported. Check `icons.css` or `base.scss` for supported sizes or add new size (class names starts with `icon-dim-`). + onImageLoadError?: (e) => void + onClose?: () => void + infoText?: React.ReactNode + showCloseButton?: boolean + arrow?: boolean + interactive?: boolean + showOnCreate?: boolean + trigger?: string + animation?: string + duration?: number + additionalContent?: ReactNode + documentationLinkText?: string + children: React.ReactElement + disableClose?: boolean + } + +export interface InfoIconTippyProps extends Pick< - TippyCustomizedProps, + TippyCustomizedProps, | 'heading' | 'infoText' | 'iconClass' - | 'documentationLink' | 'documentationLinkText' | 'additionalContent' | 'placement' | 'Icon' | 'headingInfo' + | 'documentationLink' + | 'isEnterprise' + | 'isExternalLink' > { dataTestid?: string - children?: TippyCustomizedProps['children'] + children?: TippyCustomizedProps['children'] iconClassName?: string buttonPadding?: string } diff --git a/src/Common/index.ts b/src/Common/index.ts index dc619f806..95b97700f 100644 --- a/src/Common/index.ts +++ b/src/Common/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export * from '../Shared/DocLink' export * from './AddCDButton' export * from './API' export { BreadCrumb, useBreadcrumb } from './BreadCrumb/BreadCrumb' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx index 4ca82f852..5ffea8c0e 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx @@ -16,7 +16,6 @@ import { ReactComponent as ICInfoFilledOverride } from '@Icons/ic-info-filled-override.svg' import { Checkbox } from '@Common/Checkbox' -import { DOCUMENTATION } from '@Common/Constants' import { Progressing } from '@Common/Progressing' import { Tooltip } from '@Common/Tooltip' import { CHECKBOX_VALUE } from '@Common/Types' @@ -96,7 +95,7 @@ const DTApplicationMetricsFormField = ({ diff --git a/src/Shared/Components/AppStatusModal/AppStatusBody.tsx b/src/Shared/Components/AppStatusModal/AppStatusBody.tsx index 7c8c2e2dd..b50a568b4 100644 --- a/src/Shared/Components/AppStatusModal/AppStatusBody.tsx +++ b/src/Shared/Components/AppStatusModal/AppStatusBody.tsx @@ -19,7 +19,7 @@ import { getAppStatusMessageFromAppDetails } from './utils' const InfoCardItem = ({ heading, value, isLast = false, alignCenter = false }: InfoCardItemProps) => (

{heading}

diff --git a/src/Shared/Components/CICDHistory/Artifacts.tsx b/src/Shared/Components/CICDHistory/Artifacts.tsx index 9e4f2beda..1975273eb 100644 --- a/src/Shared/Components/CICDHistory/Artifacts.tsx +++ b/src/Shared/Components/CICDHistory/Artifacts.tsx @@ -24,17 +24,11 @@ import folder from '@Icons/ic-folder.svg' import { ReactComponent as ICHelpOutline } from '@Icons/ic-help.svg' import { ReactComponent as MechanicalOperation } from '@Icons/ic-mechanical-operation.svg' import noartifact from '@Images/no-artifact.webp' +import { DocLink } from '@Shared/DocLink' import { getIsApprovalPolicyConfigured } from '@Shared/Helpers' import { useDownload } from '@Shared/Hooks' -import { - ClipboardButton, - DOCUMENTATION, - extractImage, - GenericEmptyState, - ImageTagsContainer, - useGetUserRoles, -} from '../../../Common' +import { ClipboardButton, extractImage, GenericEmptyState, ImageTagsContainer, useGetUserRoles } from '../../../Common' import { EMPTY_STATE_STATUS } from '../../constants' import { TargetPlatformBadgeList } from '../TargetPlatforms' import { TERMINAL_STATUS_MAP } from './constants' @@ -274,14 +268,11 @@ const Artifacts = ({ {EMPTY_STATE_STATUS.ARTIFACTS_EMPTY_STATE_TEXTS.StoreFiles} - - {EMPTY_STATE_STATUS.ARTIFACTS_EMPTY_STATE_TEXTS.ConfigureBlobStorage} - +
diff --git a/src/Shared/Components/CICDHistory/LogsRenderer.tsx b/src/Shared/Components/CICDHistory/LogsRenderer.tsx index 5f82948ff..e0f9266e2 100644 --- a/src/Shared/Components/CICDHistory/LogsRenderer.tsx +++ b/src/Shared/Components/CICDHistory/LogsRenderer.tsx @@ -23,6 +23,7 @@ import { ReactComponent as ICArrow } from '@Icons/ic-caret-down.svg' import { ReactComponent as ICCollapseAll } from '@Icons/ic-collapse-all.svg' import { ReactComponent as ICExpandAll } from '@Icons/ic-expand-all.svg' import { ANSI_UP_REGEX, ComponentSizeType } from '@Shared/constants' +import { DocLink } from '@Shared/DocLink' import { escapeRegExp, sanitizeTargetPlatforms } from '@Shared/Helpers' import { AppThemeType, getComponentSpecificThemeClass } from '@Shared/Providers' @@ -30,7 +31,6 @@ import { ReactComponent as OpenInNew } from '../../../Assets/Icon/ic-arrow-out.s import { ReactComponent as HelpIcon } from '../../../Assets/Icon/ic-help.svg' import { ReactComponent as Info } from '../../../Assets/Icon/ic-info-filled.svg' import { - DOCUMENTATION, Host, Progressing, ROUTES, @@ -82,14 +82,8 @@ const renderBlobNotConfigured = (): JSX.Element => (
Want to store logs to view later? - - Configure blob storage - + +
diff --git a/src/Shared/Components/CodeEditor/CodeEditor.tsx b/src/Shared/Components/CodeEditor/CodeEditor.tsx index 22838f114..abd9cd3b0 100644 --- a/src/Shared/Components/CodeEditor/CodeEditor.tsx +++ b/src/Shared/Components/CodeEditor/CodeEditor.tsx @@ -33,7 +33,7 @@ import { } from '@uiw/react-codemirror' import { DEFAULT_JSON_SCHEMA_URI, MODES } from '@Common/Constants' -import { cleanKubeManifest } from '@Common/Helper' +import { cleanKubeManifest, noop } from '@Common/Helper' import { getUniqueId } from '@Shared/Helpers' import { AppThemeType, useTheme } from '@Shared/Providers' @@ -43,8 +43,8 @@ import { getCodeEditorTheme } from './CodeEditor.theme' import { CodeEditorRenderer } from './CodeEditorRenderer' import { blurOnEscape, - openSearchPanel, - openSearchPanelWithReplace, + getOpenSearchPanel, + getOpenSearchPanelWithReplace, replaceAll, showReplaceFieldState, } from './Commands' @@ -89,6 +89,7 @@ const CodeEditor = ({ onFocus, autoFocus, disableSearch = false, + onOpenSearchPanel = noop, }: CodeEditorProps) => { // HOOKS const { appTheme } = useTheme() @@ -203,9 +204,11 @@ const CodeEditor = ({ themeExtension, keymap.of([ ...vscodeKeymap.filter(({ key }) => key !== 'Mod-Alt-Enter' && key !== 'Mod-Enter' && key !== 'Mod-f'), - ...(!disableSearch ? [{ key: 'Mod-f', run: openSearchPanel, scope: 'editor search-panel' }] : []), + ...(!disableSearch + ? [{ key: 'Mod-f', run: getOpenSearchPanel(onOpenSearchPanel), scope: 'editor search-panel' }] + : []), { key: 'Mod-Enter', run: replaceAll, scope: 'editor search-panel' }, - { key: 'Mod-Alt-f', run: openSearchPanelWithReplace, scope: 'editor search-panel' }, + { key: 'Mod-Alt-f', run: getOpenSearchPanelWithReplace(onOpenSearchPanel), scope: 'editor search-panel' }, { key: 'Escape', run: blurOnEscape, stopPropagation: true }, ]), indentationMarkers(), diff --git a/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts b/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts index d1257618b..94d0c9397 100644 --- a/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts +++ b/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts @@ -37,19 +37,22 @@ export const getShowReplaceField = (state: EditorState) => { return curState || false } -export const openSearchPanel: Command = (view: EditorView) => { - view.dispatch({ - effects: [setShowReplaceField.of(searchPanelOpen(view.state) ? getShowReplaceField(view.state) : false)], - }) - cmOpenSearchPanel(view) - return true -} +export const getOpenSearchPanel: (onOpenSearchPanel: () => void) => Command = + (onOpenSearchPanel: () => void) => (view: EditorView) => { + view.dispatch({ + effects: [setShowReplaceField.of(searchPanelOpen(view.state) ? getShowReplaceField(view.state) : false)], + }) + cmOpenSearchPanel(view) + onOpenSearchPanel() + return true + } -export const openSearchPanelWithReplace: Command = (view: EditorView) => { - openSearchPanel(view) - view.dispatch({ effects: [setShowReplaceField.of(!view.state.readOnly && true)] }) - return true -} +export const getOpenSearchPanelWithReplace: (onOpenSearchPanel: () => void) => Command = + (onOpenSearchPanel: () => void) => (view: EditorView) => { + getOpenSearchPanel(onOpenSearchPanel)(view) + view.dispatch({ effects: [setShowReplaceField.of(!view.state.readOnly && true)] }) + return true + } export const replaceAll: Command = (view: EditorView) => { const isReplaceEnabled = getShowReplaceField(view.state) diff --git a/src/Shared/Components/CodeEditor/types.ts b/src/Shared/Components/CodeEditor/types.ts index 1ffb86fd1..6eef70edc 100644 --- a/src/Shared/Components/CodeEditor/types.ts +++ b/src/Shared/Components/CodeEditor/types.ts @@ -78,6 +78,7 @@ export type CodeEditorProps = { disableSearch?: boolean diffView?: DiffView theme?: AppThemeType + onOpenSearchPanel?: () => void } & CodeEditorPropsBasedOnDiffView export interface GetCodeEditorHeightReturnType { diff --git a/src/Shared/Components/DeploymentStatusBreakdown/constants.ts b/src/Shared/Components/DeploymentStatusBreakdown/constants.ts index 3b961cb7c..c9c1ceba3 100644 --- a/src/Shared/Components/DeploymentStatusBreakdown/constants.ts +++ b/src/Shared/Components/DeploymentStatusBreakdown/constants.ts @@ -41,6 +41,10 @@ export const WFR_STATUS_DTO_TO_DEPLOYMENT_STATUS_MAP: Readonly< [WorkflowRunnerStatusDTO.PROGRESSING]: DEPLOYMENT_STATUS.INPROGRESS, [WorkflowRunnerStatusDTO.QUEUED]: DEPLOYMENT_STATUS.QUEUED, + + [WorkflowRunnerStatusDTO.UNKNOWN]: DEPLOYMENT_STATUS.UNKNOWN, + [WorkflowRunnerStatusDTO.SUSPENDED]: DEPLOYMENT_STATUS.UNKNOWN, + [WorkflowRunnerStatusDTO.MISSING]: DEPLOYMENT_STATUS.UNKNOWN, } export const PROGRESSING_DEPLOYMENT_STATUS: Readonly<(typeof DEPLOYMENT_STATUS)[keyof typeof DEPLOYMENT_STATUS][]> = [ diff --git a/src/Shared/Components/DeploymentStatusBreakdown/types.ts b/src/Shared/Components/DeploymentStatusBreakdown/types.ts index 6c0cc185e..3ff56bf80 100644 --- a/src/Shared/Components/DeploymentStatusBreakdown/types.ts +++ b/src/Shared/Components/DeploymentStatusBreakdown/types.ts @@ -15,9 +15,12 @@ export enum WorkflowRunnerStatusDTO { STARTING = 'Starting', QUEUED = 'Queued', INITIATING = 'Initiating', - // Not found on BE but added for Backward compatibility + // Coming in specific cases of helm apps HEALTHY = 'Healthy', DEGRADED = 'Degraded', + MISSING = 'Missing', + UNKNOWN = 'Unknown', + SUSPENDED = 'Suspended', } export interface ProcessUnableToFetchOrTimedOutStatusType { diff --git a/src/Shared/Components/Error/ErrorBar.tsx b/src/Shared/Components/Error/ErrorBar.tsx index 1cf0b7bc1..ee5c2781f 100644 --- a/src/Shared/Components/Error/ErrorBar.tsx +++ b/src/Shared/Components/Error/ErrorBar.tsx @@ -17,7 +17,7 @@ import { NavLink } from 'react-router-dom' import { ReactComponent as ErrorInfo } from '../../../Assets/Icon/ic-errorInfo.svg' -import { URLS } from '../../../Common' +import { DISCORD_LINK, URLS } from '../../../Common' import { AppType } from '../../types' import { ErrorBarType } from './types' import { getIsImagePullBackOff, renderErrorHeaderMessage } from './utils' @@ -90,7 +90,7 @@ const ErrorBar = ({ appDetails, useParentMargin = true }: ErrorBarType) => {
Facing issues? >) => { const renderImage = () => { if (!SVGImage) { @@ -64,15 +64,14 @@ const FeatureDescriptionModalContent = ({ className={`flex right w-100 dc__align-right dc__border-top-n1 px-20 py-16 ${docLink ? 'dc__content-space' : 'right'}`} > {docLink.length > 0 && ( - - {BUTTON_TEXT.VIEW_DOCUMENTATION} - - + )}
diff --git a/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx b/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx index 88fa2f878..f36c2d2b4 100644 --- a/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx +++ b/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx @@ -38,6 +38,7 @@ const FeatureTitleWithInfo = ({ additionalContent, showInfoIcon = false, tabsConfig, + isEnterprise, }: DescriptorProps) => { const [showFeatureDescriptionModal, setShowFeatureDescriptionModal] = useState(false) const onClickInfoIcon = () => { @@ -63,6 +64,7 @@ const FeatureTitleWithInfo = ({ documentationLink={docLink} documentationLinkText={docLinkText} dataTestid="info-tippy-button" + isEnterprise={isEnterprise} /> ) @@ -95,6 +97,7 @@ const FeatureTitleWithInfo = ({ { renderDescriptionContent?: () => ReactNode - docLink?: string + docLink?: DocLinkProps['docLinkKey'] imageVariant?: ImageType SVGImage?: React.FunctionComponent> imageStyles?: React.CSSProperties @@ -53,6 +55,7 @@ export type FeatureDescriptionModalProps = { title: string closeModalText?: string closeModal?: () => void + isEnterprise?: boolean } & ( | (BaseFeatureDescriptionModalProps & { tabsConfig?: never @@ -81,4 +84,5 @@ export type DescriptorProps = ( * @default false */ showInfoIcon?: boolean + isEnterprise?: boolean } diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx new file mode 100644 index 000000000..c8dd2704e --- /dev/null +++ b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024. Devtron Inc. + */ + +import { useMemo } from 'react' + +import emptyList from '@Images/empty-create.png' +import ErrorScreenManager from '@Common/ErrorScreenManager' +import { GenericEmptyState, GenericFilterEmptyState } from '@Common/index' + +import GenericInfoCard from './GenericInfoCard.component' +import { GenericInfoListSkeleton } from './GenericInfoListSkeleton' +import { GenericInfoCardListingProps } from './types' + +export const GenericInfoCardListing = ({ + isLoading, + error, + list, + searchKey, + reloadList, + borderVariant, + handleClearFilters, + emptyStateConfig, +}: GenericInfoCardListingProps) => { + const filteredList = useMemo(() => { + const sanitizedList = list || [] + if (!searchKey || error) { + return sanitizedList + } + + const loweredSearchKey = searchKey.toLowerCase() + return sanitizedList.filter(({ title }) => title.toLowerCase().includes(loweredSearchKey)) + }, [searchKey, list, error]) + + if (isLoading) { + return + } + + if (error) { + return + } + + if (filteredList.length === 0) { + if (searchKey) { + return + } + + return ( + + ) + } + + return ( + <> + {filteredList.map(({ id, title, description, author, Icon, onClick, linkProps }) => ( + + ))} + + ) +} diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx new file mode 100644 index 000000000..f78f83668 --- /dev/null +++ b/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx @@ -0,0 +1,10 @@ +import GenericInfoCard from './GenericInfoCard.component' +import { GenericInfoListSkeletonProps } from './types' + +export const GenericInfoListSkeleton = ({ borderVariant }: GenericInfoListSkeletonProps) => ( + <> + + + + +) diff --git a/src/Shared/Components/GenericInfoCard/index.ts b/src/Shared/Components/GenericInfoCard/index.ts index 763c46d7d..bca53f3fd 100644 --- a/src/Shared/Components/GenericInfoCard/index.ts +++ b/src/Shared/Components/GenericInfoCard/index.ts @@ -15,4 +15,6 @@ */ export { default as GenericInfoCard } from './GenericInfoCard.component' -export { GenericInfoCardBorderVariant, type GenericInfoCardProps } from './types' +export * from './GenericInfoCardListing' +export { GenericInfoListSkeleton } from './GenericInfoListSkeleton' +export { GenericInfoCardBorderVariant, type GenericInfoCardListingProps, type GenericInfoCardProps } from './types' diff --git a/src/Shared/Components/GenericInfoCard/types.ts b/src/Shared/Components/GenericInfoCard/types.ts index 3050292f6..6c90f881f 100644 --- a/src/Shared/Components/GenericInfoCard/types.ts +++ b/src/Shared/Components/GenericInfoCard/types.ts @@ -17,6 +17,11 @@ import { MouseEventHandler, ReactElement } from 'react' import { LinkProps } from 'react-router-dom' +import { GenericFilterEmptyStateProps } from '@Common/EmptyState/types' +import { GenericEmptyStateType } from '@Common/Types' + +import { APIResponseHandlerProps } from '../APIResponseHandler' + type BaseGenericInfoCardProps = { title: string description: string @@ -46,3 +51,17 @@ export type GenericInfoCardProps = { borderVariant: GenericInfoCardBorderVariant isLoading?: boolean } & BaseGenericInfoCardProps) ) + +export interface GenericInfoCardListingProps + extends Pick, + Pick { + list: (Pick & + Record<'id', string>)[] + emptyStateConfig: Pick + searchKey?: string + reloadList: () => void + error?: APIResponseHandlerProps['error'] + isLoading?: boolean +} + +export interface GenericInfoListSkeletonProps extends Partial> {} diff --git a/src/Shared/Components/Header/PageHeader.tsx b/src/Shared/Components/Header/PageHeader.tsx index e30dfdedb..52fd08531 100644 --- a/src/Shared/Components/Header/PageHeader.tsx +++ b/src/Shared/Components/Header/PageHeader.tsx @@ -48,6 +48,7 @@ const PageHeader = ({ onClose, markAsBeta, tippyProps, + isEnterprise, }: PageHeaderType) => { const { loginCount, setLoginCount, showGettingStartedCard, setShowGettingStartedCard, licenseData } = useMainContext() @@ -154,6 +155,8 @@ const PageHeader = ({ interactive arrow onClose={handleCloseSwitchThemeLocationTippyChange} + isEnterprise={isEnterprise} + documentationLink={tippyRedirectLink} >