diff --git a/frontend/packages/console-app/src/components/tour/GuidedTourMastheadTrigger.tsx b/frontend/packages/console-app/src/components/tour/GuidedTourMastheadTrigger.tsx index f28946bb4b1..19deade1f26 100644 --- a/frontend/packages/console-app/src/components/tour/GuidedTourMastheadTrigger.tsx +++ b/frontend/packages/console-app/src/components/tour/GuidedTourMastheadTrigger.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { forwardRef, useContext } from 'react'; import { useTranslation } from 'react-i18next'; import { TourActions } from './const'; import { TourContext } from './tour-context'; @@ -7,9 +7,9 @@ type GuidedTourMastheadTriggerProps = { className?: string; }; -const GuidedTourMastheadTrigger: React.FC = React.forwardRef( - ({ className }, ref: React.LegacyRef) => { - const { tourDispatch, tour } = React.useContext(TourContext); +const GuidedTourMastheadTrigger = forwardRef( + ({ className }, ref) => { + const { tourDispatch, tour } = useContext(TourContext); const { t } = useTranslation(); if (!tour) return null; diff --git a/frontend/public/components/RBAC/index.js b/frontend/public/components/RBAC/index.ts similarity index 100% rename from frontend/public/components/RBAC/index.js rename to frontend/public/components/RBAC/index.ts diff --git a/frontend/public/components/app.tsx b/frontend/public/components/app.tsx index 3d08cbc7af5..ce5e964cd0c 100644 --- a/frontend/public/components/app.tsx +++ b/frontend/public/components/app.tsx @@ -13,7 +13,7 @@ import { appInternalFetch } from '../co-fetch'; import { detectFeatures } from '../actions/features'; import { setFlag } from '../actions/flags'; import AppContents from './app-contents'; -import { Masthead } from './masthead'; +import { Masthead } from './masthead/masthead'; import { getBrandingDetails } from './utils/branding'; import { ConsoleNotifier } from './console-notifier'; import { NotificationDrawer } from './notification-drawer'; diff --git a/frontend/public/components/feedback-local.ts b/frontend/public/components/masthead/feedback-local.ts similarity index 98% rename from frontend/public/components/feedback-local.ts rename to frontend/public/components/masthead/feedback-local.ts index 09874e3047f..bda8583b726 100644 --- a/frontend/public/components/feedback-local.ts +++ b/frontend/public/components/masthead/feedback-local.ts @@ -39,6 +39,7 @@ export function useFeedbackLocal(reportBug: ReturnType) problemProcessingRequest: t( 'public~There was a problem processing the request. Try reloading the page. If the problem persists, contact', ), + support: t('public~Support'), redHatSupport: t('public~Red Hat support'), reportABug: t('public~Report a bug'), responseSent: t('public~Response sent'), diff --git a/frontend/public/components/masthead-toolbar.jsx b/frontend/public/components/masthead/masthead-toolbar.tsx similarity index 76% rename from frontend/public/components/masthead-toolbar.jsx rename to frontend/public/components/masthead/masthead-toolbar.tsx index 61d2a2189e4..9456ed15a5a 100644 --- a/frontend/public/components/masthead-toolbar.jsx +++ b/frontend/public/components/masthead/masthead-toolbar.tsx @@ -1,4 +1,4 @@ -import { useContext, useState, useRef, useCallback, createRef, useEffect } from 'react'; +import { Fragment, useContext, useState, useRef, useCallback, createRef, useEffect } from 'react'; import * as _ from 'lodash-es'; import { useSelector, useDispatch } from 'react-redux'; import { useTranslation } from 'react-i18next'; @@ -35,25 +35,26 @@ import { LinkTo } from '@console/shared/src/components/links/LinkTo'; import { CloudShellMastheadButton } from '@console/webterminal-plugin/src/components/cloud-shell/CloudShellMastheadButton'; import { CloudShellMastheadAction } from '@console/webterminal-plugin/src/components/cloud-shell/CloudShellMastheadAction'; import { getUser, useActivePerspective } from '@console/dynamic-plugin-sdk'; -import * as UIActions from '../actions/ui'; -import { flagPending, featureReducerName } from '../reducers/features'; -import { authSvc } from '../module/auth'; -import { getOCMLink, referenceForModel } from '../module/k8s'; -import { Firehose } from './utils'; -import { openshiftHelpBase } from './utils/documentation'; -import { AboutModal } from './about-modal'; -import { clusterVersionReference, getReportBugLink } from '../module/k8s/cluster-settings'; -import redhatLogoImg from '../imgs/logos/redhat.svg'; +import * as UIActions from '../../actions/ui'; +import { flagPending, featureReducerName } from '../../reducers/features'; +import { authSvc } from '../../module/auth'; +import { ClusterVersionKind, ConsoleLinkKind, getOCMLink } from '../../module/k8s'; +import { openshiftHelpBase } from '../utils/documentation'; +import { AboutModal } from '../about-modal'; +import { getReportBugLink } from '../../module/k8s/cluster-settings'; +import redhatLogoImg from '../../imgs/logos/redhat.svg'; import { GuidedTourMastheadTrigger } from '@console/app/src/components/tour'; -import { ConsoleLinkModel } from '../models'; +import { ClusterVersionModel, ConsoleLinkModel } from '../../models'; +import { RootState } from '../../redux'; import { FeedbackModal } from '@patternfly/react-user-feedback'; import '@patternfly/react-user-feedback/dist/esm/Feedback/Feedback.css'; import { useFeedbackLocal } from './feedback-local'; import { action as reduxAction } from 'typesafe-actions'; import feedbackImage from '@patternfly/react-user-feedback/dist/esm/images/rh_feedback.svg'; import darkFeedbackImage from '@patternfly/react-user-feedback/dist/esm/images/rh_feedback-dark.svg'; -import QuickCreate, { QuickCreateImportFromGit, QuickCreateContainerImages } from './QuickCreate'; -import { ThemeContext, THEME_DARK } from './ThemeProvider'; +import QuickCreate, { QuickCreateImportFromGit, QuickCreateContainerImages } from '../QuickCreate'; +import { ThemeContext, THEME_DARK } from '../ThemeProvider'; +import { useK8sWatchResource } from '../utils/k8s-watch-hook'; const LAST_CONSOLE_ACTIVITY_TIMESTAMP_LOCAL_STORAGE_KEY = 'last-console-activity-timestamp'; @@ -72,7 +73,17 @@ const defaultHelpLinks = [ }, ]; -const FeedbackModalLocalized = ({ isOpen, onClose, reportBugLink }) => { +interface FeedbackModalLocalizedProps { + isOpen: boolean; + onClose: () => void; + reportBugLink: ReturnType; +} + +const FeedbackModalLocalized: React.FCC = ({ + isOpen, + onClose, + reportBugLink, +}) => { const feedbackLocales = useFeedbackLocal(reportBugLink); const theme = useContext(ThemeContext); return ( @@ -88,21 +99,54 @@ const FeedbackModalLocalized = ({ isOpen, onClose, reportBugLink }) => { ); }; -const SystemStatusButton = ({ statuspageData }) => { +interface StatusButtonProps { + statusPageData: { + incidents: any[]; + page: { url: string }; + }; +} + +const SystemStatusButton: React.FCC = ({ statusPageData }) => { const { t } = useTranslation(); - return !_.isEmpty(_.get(statuspageData, 'incidents')) ? ( + return !_.isEmpty(_.get(statusPageData, 'incidents')) ? ( } - href={statuspageData.page.url} + href={statusPageData.page.url} /> ) : null; }; -// TODO migrate to TS, break this down into smaller components and hooks -const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { +interface MastheadAction { + label: string; + href?: string; + callback?: (e) => void; + externalLink?: boolean; + image?: React.ReactNode; + component?: React.FC; + dataTest?: string; +} + +interface MastheadSection { + name?: string; + isSection: boolean; + actions: MastheadAction[]; +} + +interface MastheadToolbarContentsProps { + consoleLinks: ConsoleLinkKind[]; + cv: ClusterVersionKind; + isMastheadStacked: boolean; +} + +// TODO break this down into smaller components and hooks +const MastheadToolbarContents: React.FCC = ({ + consoleLinks, + cv, + isMastheadStacked, +}) => { const { t } = useTranslation(); const fireTelemetryEvent = useTelemetry(); const authEnabledFlag = useFlag(FLAGS.AUTH_ENABLED); @@ -117,7 +161,7 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { t('public~Login with this command'), externalLoginCommand, ); - const { clusterID, user, alertCount, canAccessNS } = useSelector((state) => ({ + const { clusterID, user, alertCount, canAccessNS } = useSelector((state: RootState) => ({ clusterID: state.UI.get('clusterID'), user: getUser(state), alertCount: state.observe.getIn(['alertCount']), @@ -127,14 +171,14 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { const [isUserDropdownOpen, setIsUserDropdownOpen] = useState(false); const [isKebabDropdownOpen, setIsKebabDropdownOpen] = useState(false); const [isHelpDropdownOpen, setIsHelpDropdownOpen] = useState(false); - const [statusPageData, setStatusPageData] = useState(null); - const [showAboutModal, setshowAboutModal] = useState(false); + const [statusPageData, setstatusPageData] = useState(null); + const [showAboutModal, setShowAboutModal] = useState(false); const [isFeedbackModalOpen, setIsFeedbackModalOpen] = useState(false); const applicationLauncherMenuRef = useRef(null); const helpMenuRef = useRef(null); const userMenuRef = useRef(null); const kebabMenuRef = useRef(null); - const reportBugLink = cv?.data ? getReportBugLink(cv.data, t) : null; + const reportBugLink = cv ? getReportBugLink(cv) : null; const userInactivityTimeout = useRef(null); const username = user?.username ?? ''; const isKubeAdmin = username === 'kube:admin'; @@ -147,19 +191,22 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { const onFeedbackModal = () => setIsFeedbackModalOpen(true); const onAboutModal = (e) => { e.preventDefault(); - setshowAboutModal(true); + setShowAboutModal(true); }; - const closeAboutModal = () => setshowAboutModal(false); + const closeAboutModal = () => setShowAboutModal(false); - const getAdditionalLinks = (links, type) => + const getAdditionalLinks = ( + links: ConsoleLinkKind[], + type: ConsoleLinkKind['spec']['location'], + ) => _.sortBy( // ACM link is being moved to the perspective switcher, so do not show in application launcher _.filter(links, (link) => link.spec.location === type && link.metadata.name !== ACM_LINK_ID), 'spec.text', ); - const getSectionLauncherItems = (launcherItems, sectionName) => + const getSectionLauncherItems = (launcherItems: ConsoleLinkKind[], sectionName: string) => _.sortBy( _.filter( launcherItems, @@ -168,7 +215,7 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { 'spec.text', ); - const sectionSort = (section) => { + const sectionSort = (section: MastheadSection) => { switch (section.name) { case 'Red Hat Applications': return 0; @@ -189,9 +236,9 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { const isTroubleshootingPanelEnabled = Array.isArray(window.SERVER_FLAGS.consolePlugins) ? window.SERVER_FLAGS.consolePlugins.includes('troubleshooting-panel-console-plugin') : false; - const launcherItems = getAdditionalLinks(consoleLinks?.data, 'ApplicationMenu'); + const launcherItems = getAdditionalLinks(consoleLinks, 'ApplicationMenu'); - const sections = []; + const sections: MastheadSection[] = []; if ( clusterID && window.SERVER_FLAGS.branding !== 'okd' && @@ -280,9 +327,9 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { return sections; }; - const getHelpActions = (additionalHelpActions) => { + const getHelpActions = (additionalHelpActions: MastheadSection) => { const helpActions = []; - const tourRef = createRef(); + const tourRef = createRef(); helpActions.push({ isSection: true, @@ -326,7 +373,7 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { label: t('public~Share Feedback'), callback: (e) => { e.preventDefault(); - onFeedbackModal(reportBugLink); + onFeedbackModal(); }, }, ] @@ -335,18 +382,17 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { }); // Add default help links to start of additional links from operator - additionalHelpActions.actions = defaultHelpLinks - .map((helpLink) => ({ + additionalHelpActions.actions = [ + ...defaultHelpLinks.map((helpLink) => ({ ...helpLink, label: t(`public~${helpLink.label}`), - })) - .concat( - { - label: t('public~About'), - callback: onAboutModal, - }, - ...additionalHelpActions.actions, - ); + })), + { + label: t('public~About'), + callback: onAboutModal, + }, + ...additionalHelpActions.actions, + ]; if (!_.isEmpty(additionalHelpActions.actions)) { helpActions.push(additionalHelpActions); @@ -355,7 +401,7 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { return helpActions; }; - const getAdditionalActions = (links) => { + const getAdditionalActions = (links: ConsoleLinkKind[]): MastheadSection => { const actions = _.map(links, (link) => { return { label: link.spec.text, @@ -370,17 +416,22 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { }; }; - const externalProps = (externalLink) => (externalLink ? { rel: 'noopener noreferrer' } : {}); + const externalProps = (externalLink: boolean) => + externalLink ? { rel: 'noopener noreferrer' } : {}; - const renderApplicationItems = (actions) => + const renderApplicationItems = (actions: (MastheadAction | MastheadSection)[]) => _.map(actions, (action, groupIndex) => { - if (action.isSection) { + if ('isSection' in action && action.isSection) { const list = ( - + {_.map(action.actions, (sectionAction, itemIndex) => { + // Use label + index for key, fallback to index if label missing + const itemKey = sectionAction.label + ? `dropdown-item-${groupIndex}-${sectionAction.label}-${itemIndex}` + : `dropdown-item-${groupIndex}-${itemIndex}`; return ( { ); return ( - <> + {action.name ? ( - + {list} ) : ( - <>{list} + list )} - <>{groupIndex < actions.length - 1 && } - + {Number(groupIndex) < actions.length - 1 && } + ); } - return ( - <> - - - {action.label} - - - {groupIndex < actions.length - 1 && } - - ); + if ('label' in action && action.label) { + return ( + + + + {action.label} + + + {Number(groupIndex) < actions.length - 1 && } + + ); + } + return null; }); - const renderMenu = (mobile) => { + const renderMenu = (mobile: boolean) => { const additionalUserActions = getAdditionalActions( - getAdditionalLinks(consoleLinks?.data, 'UserMenu'), + getAdditionalLinks(consoleLinks, 'UserMenu'), ); const helpActions = getHelpActions( - getAdditionalActions(getAdditionalLinks(consoleLinks?.data, 'HelpMenu')), + getAdditionalActions(getAdditionalLinks(consoleLinks, 'HelpMenu')), ); const launchActions = getLaunchActions(); @@ -445,7 +499,7 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { } const actions = []; - const userActions = [ + const userActions: MastheadAction[] = [ { label: t('public~User Preferences'), component: LinkTo('/user-preferences'), @@ -592,9 +646,9 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { headers: { Accept: 'application/json' }, }) .then((response) => response.json()) - .then((newStatusPageData) => setStatusPageData(newStatusPageData)); + .then((newstatusPageData) => setstatusPageData(newstatusPageData)); } - }, [setStatusPageData]); + }, [setstatusPageData]); const setLastConsoleActivityTimestamp = () => localStorage.setItem(LAST_CONSOLE_ACTIVITY_TIMESTAMP_LOCAL_STORAGE_KEY, Date.now().toString()); @@ -643,7 +697,7 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { gap={{ default: 'gapNone', md: 'gapMd' }} > - + {!_.isEmpty(launchActions) && ( { { > {renderApplicationItems( getHelpActions( - getAdditionalActions(getAdditionalLinks(consoleLinks?.data, 'HelpMenu')), + getAdditionalActions(getAdditionalLinks(consoleLinks, 'HelpMenu')), ), )} @@ -718,11 +774,13 @@ const MastheadToolbarContents = ({ consoleLinks, cv, isMastheadStacked }) => { gap={{ default: 'gapNone' }} visibility={{ default: isMastheadStacked ? 'visible' : 'hidden' }} > - + {alertAccess && alertCount > 0 && ( { ); }; -export const MastheadToolbar = ({ isMastheadStacked }) => { - const clusterVersionFlag = useFlag(FLAGS.CLUSTER_VERSION); +interface MastheadToolbarProps { + isMastheadStacked: boolean; +} + +export const MastheadToolbar: React.FCC = ({ isMastheadStacked }) => { const consoleLinkFlag = useFlag(FLAGS.CONSOLE_LINK); - const resources = []; - if (clusterVersionFlag) { - resources.push({ - kind: clusterVersionReference, - name: 'version', - isList: false, - prop: 'cv', - }); - } - if (consoleLinkFlag) { - resources.push({ - kind: referenceForModel(ConsoleLinkModel), - isList: true, - prop: 'consoleLinks', - }); - } + const clusterVersionFlag = useFlag(FLAGS.CLUSTER_VERSION); + + const [consoleLinks] = useK8sWatchResource( + consoleLinkFlag + ? { + groupVersionKind: { + group: ConsoleLinkModel.apiGroup, + version: ConsoleLinkModel.apiVersion, + kind: ConsoleLinkModel.kind, + }, + isList: true, + } + : {}, + ); + + const [cv] = useK8sWatchResource( + clusterVersionFlag + ? { + groupVersionKind: { + group: ClusterVersionModel.apiGroup, + version: ClusterVersionModel.apiVersion, + kind: ClusterVersionModel.kind, + }, + name: 'version', + isList: false, + } + : {}, + ); return ( - - - + ); }; diff --git a/frontend/public/components/masthead.tsx b/frontend/public/components/masthead/masthead.tsx similarity index 98% rename from frontend/public/components/masthead.tsx rename to frontend/public/components/masthead/masthead.tsx index 2315dac2608..c93173bbe3d 100644 --- a/frontend/public/components/masthead.tsx +++ b/frontend/public/components/masthead/masthead.tsx @@ -18,7 +18,7 @@ import { getBrandingDetails, MASTHEAD_TYPE, useCustomLogoURL, -} from './utils/branding'; +} from '../utils/branding'; type MastheadProps = { isMastheadStacked: boolean; diff --git a/frontend/public/components/modals/confirm-modal.jsx b/frontend/public/components/modals/confirm-modal.tsx similarity index 70% rename from frontend/public/components/modals/confirm-modal.jsx rename to frontend/public/components/modals/confirm-modal.tsx index 85028662908..4035060b87b 100644 --- a/frontend/public/components/modals/confirm-modal.jsx +++ b/frontend/public/components/modals/confirm-modal.tsx @@ -1,10 +1,34 @@ import { Translation } from 'react-i18next'; -import * as PropTypes from 'prop-types'; import { createModalLauncher, ModalTitle, ModalBody, ModalSubmitFooter } from '../factory/modal'; import { PromiseComponent } from '../utils'; -class ConfirmModal extends PromiseComponent { +interface ConfirmModalProps { + btnText: string | React.ReactNode; + btnTextKey: string; + cancel: () => void; + cancelText: string | React.ReactNode; + cancelTextKey: string; + close: () => void; + executeFn: ( + e?: React.FormEvent, + opts?: { supressNotifications: boolean }, + ) => Promise; + message: string | React.ReactNode; + messageKey: string; + title: string | React.ReactNode; + titleKey: string; + submitDanger: boolean; +} + +interface ConfirmModalState { + inProgress: boolean; + errorMessage: string; +} + +class ConfirmModal extends PromiseComponent { + _cancel: () => void; + constructor(props) { super(props); this._submit = this._submit.bind(this); @@ -54,20 +78,6 @@ class ConfirmModal extends PromiseComponent { ); } } -ConfirmModal.propTypes = { - btnText: PropTypes.node, - btnTextKey: PropTypes.string, - cancel: PropTypes.func.isRequired, - cancelText: PropTypes.node, - cancelTextKey: PropTypes.string, - close: PropTypes.func.isRequired, - executeFn: PropTypes.func.isRequired, - message: PropTypes.node, - messageKey: PropTypes.string, - title: PropTypes.node, - titleKey: PropTypes.string, - submitDanger: PropTypes.bool, -}; /** @deprecated use `useWarningModal` instead */ export const confirmModal = createModalLauncher(ConfirmModal); diff --git a/frontend/public/components/persistent-volume.jsx b/frontend/public/components/persistent-volume.tsx similarity index 87% rename from frontend/public/components/persistent-volume.jsx rename to frontend/public/components/persistent-volume.tsx index 933cd1bd34b..b97f9d68ef8 100644 --- a/frontend/public/components/persistent-volume.jsx +++ b/frontend/public/components/persistent-volume.tsx @@ -4,7 +4,15 @@ import { Status } from '@console/shared'; import { useTranslation } from 'react-i18next'; import PaneBody from '@console/shared/src/components/layout/PaneBody'; -import { DetailsPage, ListPage, Table, TableData } from './factory'; +import { + DetailsPage, + DetailsPageProps, + ListPage, + ListPageProps, + Table, + TableData, + TableProps, +} from './factory'; import { Kebab, LabelList, @@ -24,11 +32,12 @@ import { Grid, GridItem, } from '@patternfly/react-core'; +import { PersistentVolumeKind } from '@console/internal/module/k8s'; const { common } = Kebab.factory; const menuActions = [...Kebab.getExtensionsActionsForKind(PersistentVolumeModel), ...common]; -const PVStatus = ({ pv }) => ( +const PVStatus = ({ pv }: { pv: PersistentVolumeKind }) => ( ); @@ -42,9 +51,9 @@ const tableColumnClasses = [ Kebab.columnClass, ]; -const kind = 'PersistentVolume'; +const { kind } = PersistentVolumeModel; -const PVTableRow = ({ obj }) => { +const PVTableRow = ({ obj }: { obj: PersistentVolumeKind }) => { const { t } = useTranslation(); return ( <> @@ -89,16 +98,17 @@ const PVTableRow = ({ obj }) => { ); }; -const Details = ({ obj: pv }) => { +const Details = ({ obj: pv }: { obj: PersistentVolumeKind }) => { const { t } = useTranslation(); - const storageClassName = _.get(pv, 'spec.storageClassName'); - const pvcName = _.get(pv, 'spec.claimRef.name'); - const namespace = _.get(pv, 'spec.claimRef.namespace'); - const storage = _.get(pv, 'spec.capacity.storage'); - const accessModes = _.get(pv, 'spec.accessModes'); - const volumeMode = _.get(pv, 'spec.volumeMode'); - const reclaimPolicy = _.get(pv, 'spec.persistentVolumeReclaimPolicy'); - const nfsExport = _.get(pv, 'spec.csi.volumeAttributes.share'); + const storageClassName = pv.spec?.storageClassName; + const pvcName = pv.spec?.claimRef?.name; + const namespace = pv.spec?.claimRef?.namespace; + const storage = pv.spec?.capacity?.storage; + const accessModes = pv.spec?.accessModes; + const volumeMode = pv.spec?.volumeMode; + const reclaimPolicy = pv.spec?.persistentVolumeReclaimPolicy; + const nfsExport = pv.spec?.csi?.volumeAttributes?.share; + return ( @@ -168,7 +178,7 @@ const Details = ({ obj: pv }) => { ); }; -export const PersistentVolumesList = (props) => { +export const PersistentVolumesList = (props: Partial) => { const { t } = useTranslation(); const PVTableHeader = () => { return [ @@ -225,10 +235,10 @@ export const PersistentVolumesList = (props) => { ); }; -export const PersistentVolumesPage = (props) => ( +export const PersistentVolumesPage = (props: ListPageProps) => ( ); -export const PersistentVolumesDetailsPage = (props) => ( +export const PersistentVolumesDetailsPage = (props: DetailsPageProps) => ( { +interface PrometheusTableRowProps { + obj: K8sResourceKind; +} + +const PrometheusTableRow: React.FCC = ({ obj: instance }) => { const { metadata, spec } = instance; return ( <> @@ -55,7 +59,7 @@ const PrometheusTableRow = ({ obj: instance }) => { ); }; -const PrometheusInstancesList = (props) => { +const PrometheusInstancesList = (props: Partial) => { const { t } = useTranslation(); const PrometheusTableHeader = () => { @@ -108,7 +112,7 @@ const PrometheusInstancesList = (props) => { ); }; -export const PrometheusInstancesPage = (props) => ( +export const PrometheusInstancesPage = (props: Partial>) => ( { +export const inject = (children: React.ReactNode, props: object) => { const safeProps = _.omit(props, ['children']); return Children.map(children, (c) => { - if (!_.isObject(c)) { + if (!_.isObject(c) || !isValidElement(c)) { return c; } return cloneElement(c, safeProps); }); }; -const lastKind = new Set(); +const lastKind = new Set(); /** * @deprecated Use `modelFor` or `connectToModel`. * Provides a synchronous way to acquire a statically-defined Kubernetes model. * NOTE: This will not work for CRDs defined at runtime, use `connectToModels` instead. */ -export const kindObj = (kind) => { +export const kindObj = (kind: K8sResourceKindReference): K8sModel => { if (kindForReference(kind) === kind && !lastKind.has(kind)) { // eslint-disable-next-line no-console console.warn( @@ -33,5 +33,8 @@ export const kindObj = (kind) => { // eslint-disable-next-line no-console console.warn('kindObj: no model for kind', kind); } - return model || {}; + + // as this is a deprecated function, future refactors should instead remove + // usage of this function entirely + return (model || {}) as K8sModel; }; diff --git a/frontend/public/locales/en/public.json b/frontend/public/locales/en/public.json index 7412e77e4c9..8d5586931a9 100644 --- a/frontend/public/locales/en/public.json +++ b/frontend/public/locales/en/public.json @@ -599,35 +599,6 @@ "Row select": "Row select", "Actions": "Actions", "Export as CSV": "Export as CSV", - "Back": "Back", - "Bug Reported": "Bug Reported", - "Close": "Close", - "Describe the bug you encountered. For urgent issues, open a support case instead.": "Describe the bug you encountered. For urgent issues, open a support case instead.", - "Describe the bug you encountered. Include where it is located and what action caused it. If this issue is urgent or blocking your workflow,": "Describe the bug you encountered. Include where it is located and what action caused it. If this issue is urgent or blocking your workflow,", - "Enter your feedback": "Enter your feedback", - "Feedback": "Feedback", - "Feedback Sent": "Feedback Sent", - "Help us improve Red Hat OpenShift.": "Help us improve Red Hat OpenShift.", - "What has your experience been like so far?": "What has your experience been like so far?", - "Join mailing list": "Join mailing list", - "By participating in feedback sessions, usability tests, and interviews with our": "By participating in feedback sessions, usability tests, and interviews with our", - "Inform the direction of Red Hat": "Inform the direction of Red Hat", - "Learn about opportunities to share your feedback with our User Research Team.": "Learn about opportunities to share your feedback with our User Research Team.", - "There was a problem processing the request. Try reloading the page. If the problem persists, contact": "There was a problem processing the request. Try reloading the page. If the problem persists, contact", - "Red Hat support": "Red Hat support", - "Report a bug": "Report a bug", - "Response sent": "Response sent", - "Yes, I would like to hear about research opportunities": "Yes, I would like to hear about research opportunities", - "Share feedback": "Share feedback", - "Share your feedback with us!": "Share your feedback with us!", - "Something went wrong": "Something went wrong", - "\"Submit feedback": "\"Submit feedback", - "We appreciate your feedback and our team will review your report shortly": "We appreciate your feedback and our team will review your report shortly", - "Tell us about your experience": "Tell us about your experience", - "Thank you, we appreciate your feedback.": "Thank you, we appreciate your feedback.", - "Thank you for your interest in user research. You have been added to our mailing list.": "Thank you for your interest in user research. You have been added to our mailing list.", - "User Research Team": "User Research Team", - "We never share your personal information, and you can opt out at any time.": "We never share your personal information, and you can opt out at any time.", "Label": "Label", "Clear all filters": "Clear all filters", "Filter": "Filter", @@ -804,6 +775,36 @@ "Provider state": "Provider state", "Machine addresses": "Machine addresses", "Create Machine": "Create Machine", + "Back": "Back", + "Bug Reported": "Bug Reported", + "Close": "Close", + "Describe the bug you encountered. For urgent issues, open a support case instead.": "Describe the bug you encountered. For urgent issues, open a support case instead.", + "Describe the bug you encountered. Include where it is located and what action caused it. If this issue is urgent or blocking your workflow,": "Describe the bug you encountered. Include where it is located and what action caused it. If this issue is urgent or blocking your workflow,", + "Enter your feedback": "Enter your feedback", + "Feedback": "Feedback", + "Feedback Sent": "Feedback Sent", + "Help us improve Red Hat OpenShift.": "Help us improve Red Hat OpenShift.", + "What has your experience been like so far?": "What has your experience been like so far?", + "Join mailing list": "Join mailing list", + "By participating in feedback sessions, usability tests, and interviews with our": "By participating in feedback sessions, usability tests, and interviews with our", + "Inform the direction of Red Hat": "Inform the direction of Red Hat", + "Learn about opportunities to share your feedback with our User Research Team.": "Learn about opportunities to share your feedback with our User Research Team.", + "There was a problem processing the request. Try reloading the page. If the problem persists, contact": "There was a problem processing the request. Try reloading the page. If the problem persists, contact", + "Support": "Support", + "Red Hat support": "Red Hat support", + "Report a bug": "Report a bug", + "Response sent": "Response sent", + "Yes, I would like to hear about research opportunities": "Yes, I would like to hear about research opportunities", + "Share feedback": "Share feedback", + "Share your feedback with us!": "Share your feedback with us!", + "Something went wrong": "Something went wrong", + "\"Submit feedback": "\"Submit feedback", + "We appreciate your feedback and our team will review your report shortly": "We appreciate your feedback and our team will review your report shortly", + "Tell us about your experience": "Tell us about your experience", + "Thank you, we appreciate your feedback.": "Thank you, we appreciate your feedback.", + "Thank you for your interest in user research. You have been added to our mailing list.": "Thank you for your interest in user research. You have been added to our mailing list.", + "User Research Team": "User Research Team", + "We never share your personal information, and you can opt out at any time.": "We never share your personal information, and you can opt out at any time.", "Learning Portal": "Learning Portal", "OpenShift Blog": "OpenShift Blog", "System status": "System status", diff --git a/frontend/public/module/k8s/k8s-models.ts b/frontend/public/module/k8s/k8s-models.ts index 04e50f47355..3317db49a44 100644 --- a/frontend/public/module/k8s/k8s-models.ts +++ b/frontend/public/module/k8s/k8s-models.ts @@ -64,7 +64,7 @@ const getK8sModels = () => { * Provides a synchronous way to acquire a statically-defined Kubernetes model. * NOTE: This will not work for CRDs defined at runtime, use `connectToModels` instead. */ -export const modelFor = (ref: K8sResourceKindReference) => { +export const modelFor = (ref: K8sResourceKindReference): K8sModel => { const metadataExtensions = pluginStore .getExtensionsInUse() .filter(isModelMetadata) as LoadedExtension[]; diff --git a/frontend/public/module/k8s/types.ts b/frontend/public/module/k8s/types.ts index 6fc1d390883..575bf14f3eb 100644 --- a/frontend/public/module/k8s/types.ts +++ b/frontend/public/module/k8s/types.ts @@ -1149,6 +1149,52 @@ export type PersistentVolumeClaimKind = K8sResourceCommon & { }; }; +export type PersistentVolumeKind = K8sResourceCommon & { + spec: { + storageClassName?: string; + claimRef?: { + namespace: string; + name: string; + }; + capacity: { storage: string }; + storage: string; + accessModes: string[]; + persistentVolumeReclaimPolicy: string; + volumeMode?: string; + csi?: { + driver: string; + volumeHandle: string; + fsType?: string; + readOnly?: boolean; + volumeAttributes?: { [key: string]: string }; + nodeStageSecretRef?: { name: string; namespace: string }; + nodePublishSecretRef?: { name: string; namespace: string }; + }; + }; + status: { + phase: string; + }; +}; + +export type ConsoleLinkKind = K8sResourceCommon & { + spec: { + applicationMenu?: { + imageURL?: string; + section: string; + }; + href: string; + location: 'ApplicationMenu' | 'HelpMenu' | 'UserMenu' | 'NamespaceDashboard'; + namespaceDashboard?: { + namespaceSelector?: { + matchExpressions?: { key?: string; operator?: string; values?: string[] }[]; + matchLabels?: { [key: string]: string }; + }; + namespaces?: string[]; + }; + text: string; + }; +}; + export type ConsolePluginKind = K8sResourceCommon & { spec: { displayName: string;