diff --git a/public/locales/en-US.json b/public/locales/en-US.json index 05700570b..1a3d8a3e3 100644 --- a/public/locales/en-US.json +++ b/public/locales/en-US.json @@ -12,6 +12,8 @@ "cardHeading": "CPU usage", "cardHeading_cores": "CPU core usage", "cardHeading_sockets": "CPU socket usage", + "cardHeading_OpenShift Container Platform": "Fixed capacity", + "cardHeading_OpenShift-metrics": "Flexible subscriptions", "cardHeading_OpenShift-dedicated-metrics": "Flexible subscriptions", "cardHeading_RHEL": "CPU socket usage", "cardHeading_Satellite": "CPU socket usage", @@ -83,7 +85,12 @@ "header_measurementType": "Type", "header_inventoryId": "UUID", "header_sockets": "Subscribed sockets", + "header_sockets_OpenShift Container Platform": "Sockets", + "header_sockets_OpenShift-metrics": "Sockets", + "header_sockets_OpenShift-dedicated-metrics": "Sockets", "header_lastSeen": "Last seen", + "header_lastSeen_OpenShift Container Platform": "Last check-in", + "header_lastSeen_OpenShift-metrics": "Last check-in", "header_lastSeen_OpenShift-dedicated-metrics": "Last check-in", "header_productName": "Product", "header_serviceLevel": "Service level", @@ -152,8 +159,8 @@ "curiosity-view": { "title": "{{appName}}", "subtitle": "Monitor your usage based on your subscription terms. <0>Learn more about {{appName}} reporting", - "title_OpenShift Container Platform": "Red Hat OpenShift", - "subtitle_OpenShift Container Platform": "Monitor your Red Hat OpenShift usage by socket or core usage, based on your subscription terms. <0>Learn more about {{appName}} reporting", + "title_OpenShift Container Platform": "OpenShift Container Platform", + "subtitle_OpenShift Container Platform": "Monitor your OpenShift Container Platform usage for both fixed and flexible capacity subscriptions. <0>Learn more about {{appName}} reporting", "title_OpenShift-dedicated-metrics": "OpenShift Dedicated", "subtitle_OpenShift-dedicated-metrics": "Monitor your OpenShift Dedicated usage for flexible capacity subscriptions. <0>Learn more about {{appName}} reporting", "title_RHEL": "Red Hat Enterprise Linux", diff --git a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap index 73888da4b..5a25d4d65 100644 --- a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap +++ b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap @@ -149,13 +149,9 @@ Array [ Object { "file": "./src/components/openshiftView/openshiftView.js", "keys": Array [ - Object { - "key": "curiosity-view.title", - "match": "t(\`curiosity-view.title\`, { appName: helpers.UI_DISPLAY_NAME, context: productLabel })", - }, Object { "key": "curiosity-graph.cardHeading", - "match": "t('curiosity-graph.cardHeading', { context: uomFilter })", + "match": "t('curiosity-graph.cardHeading', { context: productId })", }, Object { "key": "curiosity-inventory.tab", @@ -165,6 +161,10 @@ Array [ "key": "curiosity-inventory.tab", "match": "t('curiosity-inventory.tab', { context: 'subscriptions' })", }, + Object { + "key": "curiosity-view.title", + "match": "t(\`curiosity-view.title\`, { appName: helpers.UI_DISPLAY_NAME, context: productLabel })", + }, Object { "key": "curiosity-inventory.header", "match": "translate('curiosity-inventory.header', { context: 'guestsDisplayName' })", @@ -174,12 +174,16 @@ Array [ "match": "translate('curiosity-inventory.label', { context: 'numberOfGuests', count: numberOfGuests.value }, [ ])", }, Object { - "key": "curiosity-inventory.measurementType", - "match": "translate('curiosity-inventory.measurementType', { context: measurementType.value })", + "key": "curiosity-inventory.header", + "match": "translate('curiosity-inventory.header', { context: 'sockets_OpenShift Container Platform' })", }, Object { - "key": "curiosity-inventory.cloudProvider", - "match": "translate('curiosity-inventory.cloudProvider', { context: cloudProvider.value })", + "key": "curiosity-inventory.header", + "match": "translate('curiosity-inventory.header', { context: 'cores_OpenShift Container Platform' })", + }, + Object { + "key": "curiosity-inventory.label", + "match": "translate('curiosity-inventory.label', { context: 'numberOfGuests', count: numberOfGuests.value }, [ ])", }, ], }, @@ -559,6 +563,10 @@ Array [ "file": "./src/components/openshiftView/openshiftView.js", "key": "curiosity-inventory.label", }, + Object { + "file": "./src/components/openshiftView/openshiftView.js", + "key": "curiosity-inventory.label", + }, Object { "file": "./src/components/productView/productView.js", "key": "curiosity-inventory.tab", diff --git a/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap b/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap index 95ed35ab0..8770eb37f 100644 --- a/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap +++ b/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap @@ -4,279 +4,446 @@ exports[`OpenshiftView Component should have a fallback title: title 1`] = ` - t(curiosity-view.title, {"appName":"Subscription Watch","context":"test label"}) + t(curiosity-view.title, {"appName":"Subscription Watch","context":"OpenShift Container Platform"}) - - - - - - - - - - + + - - - - - - + - - + + + + + + + + + + + + + - - - - + + + + + + + + + + `; exports[`OpenshiftView Component should have default props that set product configuration: filteredGuestsData results 1`] = ` Object { "cells": Array [ - Object { - "title": "lorem", - }, - Object { - "title": "lorem inventory id", - }, - Object { - "title": , - }, + "lorem", + "lorem inventory id", + "lorem subscription id", + "lorem date obj", + "hello world", ], "columnHeaders": Array [ - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"guestsDisplayName\\"})", - "transforms": Array [], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"inventoryId\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", - "transforms": Array [ - [Function], - ], - }, + "t(curiosity-inventory.header, {\\"context\\":\\"displayName\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"inventoryId\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"subscriptionManagerId\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"loremIpsum\\"})", ], "data": Object { "displayName": Object { @@ -306,43 +473,18 @@ Object { exports[`OpenshiftView Component should have default props that set product configuration: filteredGuestsData results, authorized 1`] = ` Object { "cells": Array [ - Object { - "title": , - }, - Object { - "title": "lorem inventory id", - }, - Object { - "title": , - }, + "lorem", + "lorem inventory id", + "lorem subscription id", + "lorem date obj", + "hello world", ], "columnHeaders": Array [ - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"guestsDisplayName\\"})", - "transforms": Array [], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"inventoryId\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", - "transforms": Array [ - [Function], - ], - }, + "t(curiosity-inventory.header, {\\"context\\":\\"displayName\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"inventoryId\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"subscriptionManagerId\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"loremIpsum\\"})", ], "data": Object { "displayName": Object { @@ -372,61 +514,26 @@ Object { exports[`OpenshiftView Component should have default props that set product configuration: filteredInventoryData results 1`] = ` Object { "cells": Array [ - Object { - "title": - lorem - - t(curiosity-inventory.label, {"context":"numberOfGuests","count":3}, [object Object]) - , - }, - Object { - "title": - t(curiosity-inventory.measurementType, {"context":null}) - - - , - }, - Object { - "title": 10, - }, - Object { - "title": 12, - }, - Object { - "title": , - }, + "lorem", + "lorem inventory id", + "ipsum", + null, + 3, + 10, + 12, + "lorem date obj", + "hello world", ], "columnHeaders": Array [ - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"displayName\\"})", - "transforms": Array [], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"measurementType\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"sockets\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"cores\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", - "transforms": Array [ - [Function], - ], - }, + "t(curiosity-inventory.header, {\\"context\\":\\"displayName\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"inventoryId\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"hardwareType\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"measurementType\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"numberOfGuests\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"sockets\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"cores\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"loremIpsum\\"})", ], "data": Object { "cores": Object { @@ -472,69 +579,26 @@ Object { exports[`OpenshiftView Component should have default props that set product configuration: filteredInventoryData results, authorized 1`] = ` Object { "cells": Array [ - Object { - "title": - - - t(curiosity-inventory.label, {"context":"numberOfGuests","count":3}, [object Object]) - , - }, - Object { - "title": - t(curiosity-inventory.measurementType, {"context":null}) - - - , - }, - Object { - "title": 10, - }, - Object { - "title": 12, - }, - Object { - "title": , - }, + "lorem", + "lorem inventory id", + "ipsum", + null, + 3, + 10, + 12, + "lorem date obj", + "hello world", ], "columnHeaders": Array [ - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"displayName\\"})", - "transforms": Array [], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"measurementType\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"sockets\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"cores\\"})", - "transforms": Array [ - [Function], - ], - }, - Object { - "title": "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", - "transforms": Array [ - [Function], - ], - }, + "t(curiosity-inventory.header, {\\"context\\":\\"displayName\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"inventoryId\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"hardwareType\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"measurementType\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"numberOfGuests\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"sockets\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"cores\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"lastSeen\\"})", + "t(curiosity-inventory.header, {\\"context\\":\\"loremIpsum\\"})", ], "data": Object { "cores": Object { @@ -579,83 +643,10 @@ Object { exports[`OpenshiftView Component should have default props that set product configuration: initial configuration 1`] = ` Object { - "initialGraphFilters": Array [ - Object { - "color": "#06c", - "fill": "#8bc1f7", - "id": "cores", - "isOptional": true, - "stroke": "#06c", - }, - Object { - "color": "#06c", - "fill": "#8bc1f7", - "id": "sockets", - "isOptional": true, - "stroke": "#06c", - }, - Object { - "id": "thresholdSockets", - "isOptional": true, - }, - Object { - "id": "thresholdCores", - "isOptional": true, - }, - ], - "initialGuestsFilters": Array [ - Object { - "cell": [Function], - "header": "t(curiosity-inventory.header, {\\"context\\":\\"guestsDisplayName\\"})", - "id": "displayName", - }, - Object { - "cellWidth": 40, - "id": "inventoryId", - }, - Object { - "cell": [Function], - "cellWidth": 15, - "id": "lastSeen", - }, - ], - "initialInventoryFilters": Array [ - Object { - "cell": [Function], - "id": "displayName", - "isSortable": true, - }, - Object { - "cell": [Function], - "cellWidth": 20, - "id": "measurementType", - "isSortable": true, - }, - Object { - "cellWidth": 15, - "id": "sockets", - "isOptional": true, - "isSortable": true, - "isWrappable": true, - }, - Object { - "cellWidth": 15, - "id": "cores", - "isOptional": true, - "isSortable": true, - "isWrappable": true, - }, - Object { - "cell": [Function], - "cellWidth": 15, - "id": "lastSeen", - "isSortable": true, - "isWrappable": true, - }, - ], - "query": Object { - "uom": "cores", - }, + "initialGraphFilters": undefined, + "initialGuestsFilters": undefined, + "initialInventoryFilters": undefined, + "query": Object {}, } `; @@ -663,243 +654,427 @@ exports[`OpenshiftView Component should render a non-connected component: non-co - t(curiosity-view.title, {"appName":"Subscription Watch","context":"test label"}) + t(curiosity-view.title, {"appName":"Subscription Watch","context":"OpenShift Container Platform"}) - - - - - - - - - - + + - - - - - - + - - + + + + + + + + + + + + + - - - - + + + + + + + + + + `; diff --git a/src/components/openshiftView/openshiftView.js b/src/components/openshiftView/openshiftView.js index 28f5c1855..74c22ed42 100644 --- a/src/components/openshiftView/openshiftView.js +++ b/src/components/openshiftView/openshiftView.js @@ -7,8 +7,9 @@ import { import { Button, Label as PfLabel } from '@patternfly/react-core'; import { DateFormat } from '@redhat-cloud-services/frontend-components/components/DateFormat'; import moment from 'moment'; -import { PageLayout, PageHeader, PageMessages, PageSection, PageToolbar } from '../pageLayout/pageLayout'; +import { PageLayout, PageColumns, PageHeader, PageSection, PageToolbar } from '../pageLayout/pageLayout'; import { + RHSM_API_PATH_ID_TYPES, RHSM_API_QUERY_SORT_DIRECTION_TYPES as SORT_DIRECTION_TYPES, RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES, RHSM_API_QUERY_TYPES, @@ -20,11 +21,11 @@ import { apiQueries, connect, reduxSelectors } from '../../redux'; import GraphCard from '../graphCard/graphCard'; import { ToolbarFieldUom } from '../toolbar/toolbarFieldUom'; import { ToolbarFieldGranularity } from '../toolbar/toolbarFieldGranularity'; +import { ToolbarFieldRangedMonthly } from '../toolbar/toolbarFieldRangedMonthly'; import Toolbar from '../toolbar/toolbar'; import InventoryList from '../inventoryList/inventoryList'; import InventorySubscriptions from '../inventorySubscriptions/inventorySubscriptions'; import InventoryTabs, { InventoryTab } from '../inventoryTabs/inventoryTabs'; -import BannerMessages from '../bannerMessages/bannerMessages'; import { helpers, dateHelpers } from '../../common'; import { translate } from '../i18n/i18n'; @@ -32,34 +33,35 @@ import { translate } from '../i18n/i18n'; * An OpenShift encompassing view. * * @augments React.Component - * @fires onSelect */ class OpenshiftView extends React.Component { componentDidMount() {} /** - * Render an OpenShift view. + * Render a product column * + * @param {object} config + * @param {string} uomValue * @returns {Node} */ - render() { + renderProduct(config, uomValue) { + const { t } = this.props; const { - initialGuestsFilters, - initialToolbarFilters, - initialInventorySettings, - initialGraphFilters, - initialInventoryFilters, + productContextFilterUom, + query = {}, + graphTallyQuery = {}, + inventoryHostsQuery = {}, + inventorySubscriptionsQuery = {}, + initialGraphFilters = [], + initialGuestsFilters = [], + initialInventoryFilters = [], + initialInventorySettings = {}, initialSubscriptionsInventoryFilters, - initialOption, - query, - graphTallyQuery, - inventoryHostsQuery, - inventorySubscriptionsQuery, - routeDetail, - t, + initialToolbarFilters, + productLabel, + productId, viewId - } = this.props; - const { pathParameter: productId, productParameter: productLabel } = routeDetail; + } = config; const { graphTallyQuery: initialGraphTallyQuery, inventoryHostsQuery: initialInventoryHostsQuery, @@ -67,29 +69,33 @@ class OpenshiftView extends React.Component { toolbarQuery } = apiQueries.parseRhsmQuery(query, { graphTallyQuery, inventoryHostsQuery, inventorySubscriptionsQuery }); - const uomFilter = query[RHSM_API_QUERY_TYPES.UOM] || initialOption; - const filter = ({ id, isOptional }) => { - if (!isOptional) { - return true; - } - return new RegExp(uomFilter, 'i').test(id); - }; + let graphFilters = initialGraphFilters; + let inventoryFilters = initialInventoryFilters; + let subscriptionsInventoryFilters = initialSubscriptionsInventoryFilters; + let uomFilter; + + if (productContextFilterUom) { + uomFilter = uomValue || query[RHSM_API_QUERY_TYPES.UOM]; - const graphFilters = initialGraphFilters.filter(filter); - const inventoryFilters = initialInventoryFilters.filter(filter); - const subscriptionsInventoryFilters = initialSubscriptionsInventoryFilters.filter(filter); + const filter = ({ id, isOptional }) => { + if (!isOptional) { + return true; + } + return new RegExp(uomFilter, 'i').test(id); + }; + + graphFilters = initialGraphFilters.filter(filter); + inventoryFilters = initialInventoryFilters.filter(filter); + subscriptionsInventoryFilters = initialSubscriptionsInventoryFilters.filter(filter); + } return ( - - - {t(`curiosity-view.title`, { appName: helpers.UI_DISPLAY_NAME, context: productLabel })} - - - - - - - + + {initialToolbarFilters && ( + + + + )} - - + {productId === RHSM_API_PATH_ID_TYPES.OPENSHIFT && uomFilter && ( + + )} + {productId === RHSM_API_PATH_ID_TYPES.OPENSHIFT && ( + + )} + {productId === RHSM_API_PATH_ID_TYPES.OPENSHIFT_METRICS && } @@ -111,7 +122,7 @@ class OpenshiftView extends React.Component { title={t('curiosity-inventory.tab', { context: 'hosts' })} > - {!helpers.UI_DISABLED_TABLE_SUBSCRIPTIONS && ( + {!helpers.UI_DISABLED_TABLE_SUBSCRIPTIONS && initialSubscriptionsInventoryFilters && ( + + ); + } + + /** + * Render an OpenShift view. + * + * @returns {Node} + */ + render() { + const { productConfig, productLabel, query, t } = this.props; + const uomValue = query[RHSM_API_QUERY_TYPES.UOM]; + + return ( + + + {t(`curiosity-view.title`, { appName: helpers.UI_DISPLAY_NAME, context: productLabel })} + + {productConfig.map(config => this.renderProduct(config, uomValue))} ); } } +/** + * FixMe: the viewId is used by viewSelectors.js, remove it! + * The viewId is used to help fire a render on UOM update, + * this is a hack until context and hooks can replace it. + */ /** * Prop types. * - * @type {{initialOption: string, inventorySubscriptionsQuery: object, query: object, - * initialSubscriptionsInventoryFilters: Array, initialInventorySettings: object, initialToolbarFilters: Array, - * t: Function, graphTallyQuery: object, inventoryHostsQuery: object, initialGraphFilters: Array, - * routeDetail: object, initialGuestsFilters: Array, initialInventoryFilters: Array, viewId: string}} + * @type {{productLabel: string, t: Function, routeDetail: object, productConfig: Array}} */ OpenshiftView.propTypes = { - query: PropTypes.object, - graphTallyQuery: PropTypes.shape({ - [RHSM_API_QUERY_TYPES.GRANULARITY]: PropTypes.oneOf([...Object.values(GRANULARITY_TYPES)]), - [RHSM_API_QUERY_TYPES.START_DATE]: PropTypes.string, - [RHSM_API_QUERY_TYPES.END_DATE]: PropTypes.string - }), - inventoryHostsQuery: PropTypes.shape({ - [RHSM_API_QUERY_TYPES.LIMIT]: PropTypes.number, - [RHSM_API_QUERY_TYPES.OFFSET]: PropTypes.number, - [RHSM_API_QUERY_TYPES.SORT]: PropTypes.oneOf([...Object.values(RHSM_API_QUERY_SORT_TYPES)]), - [RHSM_API_QUERY_TYPES.DIRECTION]: PropTypes.oneOf([...Object.values(SORT_DIRECTION_TYPES)]) - }), - inventorySubscriptionsQuery: PropTypes.shape({ - [RHSM_API_QUERY_TYPES.LIMIT]: PropTypes.number, - [RHSM_API_QUERY_TYPES.OFFSET]: PropTypes.number, - [RHSM_API_QUERY_TYPES.SORT]: PropTypes.oneOf([...Object.values(RHSM_API_QUERY_SUBSCRIPTIONS_SORT_TYPES)]), - [RHSM_API_QUERY_TYPES.DIRECTION]: PropTypes.oneOf([...Object.values(SORT_DIRECTION_TYPES)]) - }), - initialOption: PropTypes.oneOf(Object.values(RHSM_API_QUERY_UOM_TYPES)), - initialGraphFilters: PropTypes.array, - initialGuestsFilters: PropTypes.array, - initialInventoryFilters: PropTypes.array, - initialInventorySettings: PropTypes.shape({ - hasGuests: PropTypes.func - }), - initialSubscriptionsInventoryFilters: PropTypes.array, - initialToolbarFilters: PropTypes.array, + productConfig: PropTypes.arrayOf( + PropTypes.shape({ + productContextFilterUom: PropTypes.bool, + query: PropTypes.object, + graphTallyQuery: PropTypes.shape({ + [RHSM_API_QUERY_TYPES.GRANULARITY]: PropTypes.oneOf([...Object.values(GRANULARITY_TYPES)]), + [RHSM_API_QUERY_TYPES.START_DATE]: PropTypes.string, + [RHSM_API_QUERY_TYPES.END_DATE]: PropTypes.string + }), + inventoryHostsQuery: PropTypes.shape({ + [RHSM_API_QUERY_TYPES.LIMIT]: PropTypes.number, + [RHSM_API_QUERY_TYPES.OFFSET]: PropTypes.number, + [RHSM_API_QUERY_TYPES.SORT]: PropTypes.oneOf([...Object.values(RHSM_API_QUERY_SORT_TYPES)]), + [RHSM_API_QUERY_TYPES.DIRECTION]: PropTypes.oneOf([...Object.values(SORT_DIRECTION_TYPES)]) + }), + inventorySubscriptionsQuery: PropTypes.shape({ + [RHSM_API_QUERY_TYPES.LIMIT]: PropTypes.number, + [RHSM_API_QUERY_TYPES.OFFSET]: PropTypes.number, + [RHSM_API_QUERY_TYPES.SORT]: PropTypes.oneOf([...Object.values(RHSM_API_QUERY_SUBSCRIPTIONS_SORT_TYPES)]), + [RHSM_API_QUERY_TYPES.DIRECTION]: PropTypes.oneOf([...Object.values(SORT_DIRECTION_TYPES)]) + }), + initialOption: PropTypes.oneOf(Object.values(RHSM_API_QUERY_UOM_TYPES)), + initialGraphFilters: PropTypes.array, + initialGuestsFilters: PropTypes.array, + initialInventoryFilters: PropTypes.array, + initialInventorySettings: PropTypes.shape({ + hasGuests: PropTypes.func + }), + initialSubscriptionsInventoryFilters: PropTypes.array, + initialToolbarFilters: PropTypes.array, + productLabel: PropTypes.string, + productId: PropTypes.string, + viewId: PropTypes.string + }) + ), + productLabel: PropTypes.string, routeDetail: PropTypes.shape({ pathParameter: PropTypes.string, productParameter: PropTypes.string, viewParameter: PropTypes.string }).isRequired, - t: PropTypes.func, - viewId: PropTypes.string + query: PropTypes.object, + viewId: PropTypes.string, // eslint-disable-line + t: PropTypes.func }; +/** + * FixMe: the viewId is used by viewSelectors.js, remove it! + * The viewId is used to help fire a render on UOM update, + * this is a hack until context and hooks can replace it. + */ /** * Default props. * - * @type {{initialOption: string, inventorySubscriptionsQuery: object, query: object, - * initialSubscriptionsInventoryFilters: Array, initialInventorySettings: object, initialToolbarFilters: Array, - * t: translate, graphTallyQuery: object, inventoryHostsQuery: object, - * initialGraphFilters: Array, initialGuestsFilters: Array, initialInventoryFilters: Array, viewId: string}} + * @type {{productLabel: string, t: Function, productConfig: Array}} */ OpenshiftView.defaultProps = { - query: { - [RHSM_API_QUERY_TYPES.UOM]: RHSM_API_QUERY_UOM_TYPES.CORES - }, - graphTallyQuery: { - [RHSM_API_QUERY_TYPES.GRANULARITY]: GRANULARITY_TYPES.DAILY, - [RHSM_API_QUERY_TYPES.START_DATE]: dateHelpers.getRangedDateTime(GRANULARITY_TYPES.DAILY).startDate.toISOString(), - [RHSM_API_QUERY_TYPES.END_DATE]: dateHelpers.getRangedDateTime(GRANULARITY_TYPES.DAILY).endDate.toISOString() - }, - inventoryHostsQuery: { - [RHSM_API_QUERY_TYPES.SORT]: RHSM_API_QUERY_SORT_TYPES.LAST_SEEN, - [RHSM_API_QUERY_TYPES.DIRECTION]: SORT_DIRECTION_TYPES.ASCENDING, - [RHSM_API_QUERY_TYPES.LIMIT]: 100, - [RHSM_API_QUERY_TYPES.OFFSET]: 0 - }, - inventorySubscriptionsQuery: { - [RHSM_API_QUERY_TYPES.SORT]: RHSM_API_QUERY_SUBSCRIPTIONS_SORT_TYPES.UPCOMING_EVENT_DATE, - [RHSM_API_QUERY_TYPES.DIRECTION]: SORT_DIRECTION_TYPES.ASCENDING, - [RHSM_API_QUERY_TYPES.LIMIT]: 100, - [RHSM_API_QUERY_TYPES.OFFSET]: 0 - }, - initialOption: RHSM_API_QUERY_UOM_TYPES.CORES, - initialGraphFilters: [ - { - id: 'cores', - isOptional: true, - fill: chartColorBlueLight.value, - stroke: chartColorBlueDark.value, - color: chartColorBlueDark.value - }, + productConfig: [ { - id: 'sockets', - isOptional: true, - fill: chartColorBlueLight.value, - stroke: chartColorBlueDark.value, - color: chartColorBlueDark.value - }, - { id: 'thresholdSockets', isOptional: true }, - { id: 'thresholdCores', isOptional: true } - ], - initialGuestsFilters: [ - { - id: 'displayName', - header: translate('curiosity-inventory.header', { context: 'guestsDisplayName' }), - cell: (data, session) => { - const { displayName, inventoryId } = data; - const { inventory: authorized } = session?.authorized || {}; + productContextFilterUom: true, + query: { + [RHSM_API_QUERY_TYPES.UOM]: RHSM_API_QUERY_UOM_TYPES.CORES + }, + graphTallyQuery: { + [RHSM_API_QUERY_TYPES.GRANULARITY]: GRANULARITY_TYPES.DAILY, + [RHSM_API_QUERY_TYPES.START_DATE]: dateHelpers + .getRangedDateTime(GRANULARITY_TYPES.DAILY) + .startDate.toISOString(), + [RHSM_API_QUERY_TYPES.END_DATE]: dateHelpers.getRangedDateTime(GRANULARITY_TYPES.DAILY).endDate.toISOString() + }, + inventoryHostsQuery: { + [RHSM_API_QUERY_TYPES.SORT]: RHSM_API_QUERY_SORT_TYPES.LAST_SEEN, + [RHSM_API_QUERY_TYPES.DIRECTION]: SORT_DIRECTION_TYPES.ASCENDING, + [RHSM_API_QUERY_TYPES.LIMIT]: 100, + [RHSM_API_QUERY_TYPES.OFFSET]: 0 + }, + inventorySubscriptionsQuery: { + [RHSM_API_QUERY_TYPES.SORT]: RHSM_API_QUERY_SUBSCRIPTIONS_SORT_TYPES.UPCOMING_EVENT_DATE, + [RHSM_API_QUERY_TYPES.DIRECTION]: SORT_DIRECTION_TYPES.ASCENDING, + [RHSM_API_QUERY_TYPES.LIMIT]: 100, + [RHSM_API_QUERY_TYPES.OFFSET]: 0 + }, + initialOption: RHSM_API_QUERY_UOM_TYPES.CORES, + initialGraphFilters: [ + { + id: 'cores', + isOptional: true, + fill: chartColorBlueLight.value, + stroke: chartColorBlueDark.value, + color: chartColorBlueDark.value + }, + { + id: 'sockets', + isOptional: true, + fill: chartColorBlueLight.value, + stroke: chartColorBlueDark.value, + color: chartColorBlueDark.value + }, + { id: 'thresholdSockets', isOptional: true }, + { id: 'thresholdCores', isOptional: true } + ], + initialGuestsFilters: [ + { + id: 'displayName', + header: translate('curiosity-inventory.header', { context: 'guestsDisplayName' }), + cell: (data, session) => { + const { displayName, inventoryId } = data; + const { inventory: authorized } = session?.authorized || {}; - if (!inventoryId?.value) { - return displayName?.value; - } + if (!inventoryId?.value) { + return displayName?.value; + } - if (!authorized) { - return displayName?.value || inventoryId?.value; + if (!authorized) { + return displayName?.value || inventoryId?.value; + } + + return ( + + ); + } + }, + { + id: 'inventoryId', + cellWidth: 40 + }, + { + id: 'lastSeen', + cell: data => (data?.lastSeen?.value && ) || '', + cellWidth: 15 } + ], + initialInventoryFilters: [ + { + id: 'displayName', + cell: (data, session) => { + const { displayName = {}, inventoryId = {}, numberOfGuests = {} } = data; + const { inventory: authorized } = session?.authorized || {}; - return ( - - ); - } - }, - { - id: 'inventoryId', - cellWidth: 40 - }, - { - id: 'lastSeen', - cell: data => (data?.lastSeen?.value && ) || '', - cellWidth: 15 - } - ], - initialInventoryFilters: [ - { - id: 'displayName', - cell: (data, session) => { - const { displayName = {}, inventoryId = {}, numberOfGuests = {} } = data; - const { inventory: authorized } = session?.authorized || {}; + if (!inventoryId.value) { + return displayName.value; + } - if (!inventoryId.value) { - return displayName.value; - } + let updatedDisplayName = displayName.value || inventoryId.value; - let updatedDisplayName = displayName.value || inventoryId.value; + if (authorized) { + updatedDisplayName = ( + + ); + } - if (authorized) { - updatedDisplayName = ( - - ); + return ( + + {updatedDisplayName}{' '} + {(numberOfGuests.value && + translate('curiosity-inventory.label', { context: 'numberOfGuests', count: numberOfGuests.value }, [ + + ])) || + ''} + + ); + }, + isSortable: true + }, + { + id: 'sockets', + header: translate('curiosity-inventory.header', { context: 'sockets_OpenShift Container Platform' }), + isOptional: true, + isSortable: true, + isWrappable: true, + cellWidth: 15 + }, + { + id: 'cores', + header: translate('curiosity-inventory.header', { context: 'cores_OpenShift Container Platform' }), + isOptional: true, + isSortable: true, + isWrappable: true, + cellWidth: 15 + }, + { + id: 'lastSeen', + cell: data => (data?.lastSeen?.value && ) || '', + isSortable: true, + isWrappable: true, + cellWidth: 15 } - - return ( - - {updatedDisplayName}{' '} - {(numberOfGuests.value && - translate('curiosity-inventory.label', { context: 'numberOfGuests', count: numberOfGuests.value }, [ - - ])) || - ''} - - ); - }, - isSortable: true + ], + initialInventorySettings: {}, + initialSubscriptionsInventoryFilters: [ + { + id: 'productName', + isSortable: true + }, + { + id: 'serviceLevel', + isSortable: true, + isWrappable: true, + cellWidth: 15 + }, + { + id: 'upcomingEventDate', + cell: data => + (data?.upcomingEventDate?.value && moment.utc(data?.upcomingEventDate?.value).format('YYYY-DD-MM')) || '', + isSortable: true, + isWrappable: true, + cellWidth: 15 + } + ], + initialToolbarFilters: [ + { + id: RHSM_API_QUERY_TYPES.SLA + } + ], + productLabel: 'OpenShift', + productId: RHSM_API_PATH_ID_TYPES.OPENSHIFT, + viewId: 'viewOpenShift' }, { - id: 'measurementType', - cell: data => { - const { cloudProvider = {}, measurementType = {} } = data; - return ( - - {translate('curiosity-inventory.measurementType', { context: measurementType.value })}{' '} - {(cloudProvider.value && ( - - {translate('curiosity-inventory.cloudProvider', { context: cloudProvider.value })} - - )) || - ''} - - ); + query: {}, + graphTallyQuery: { + [RHSM_API_QUERY_TYPES.GRANULARITY]: GRANULARITY_TYPES.DAILY, + [RHSM_API_QUERY_TYPES.START_DATE]: dateHelpers + .getRangedDateTime(GRANULARITY_TYPES.DAILY) + .startDate.toISOString(), + [RHSM_API_QUERY_TYPES.END_DATE]: dateHelpers.getRangedDateTime(GRANULARITY_TYPES.DAILY).endDate.toISOString() }, - isSortable: true, - cellWidth: 20 - }, - { - id: 'sockets', - isOptional: true, - isSortable: true, - isWrappable: true, - cellWidth: 15 - }, - { - id: 'cores', - isOptional: true, - isSortable: true, - isWrappable: true, - cellWidth: 15 - }, - { - id: 'lastSeen', - cell: data => (data?.lastSeen?.value && ) || '', - isSortable: true, - isWrappable: true, - cellWidth: 15 - } - ], - initialInventorySettings: {}, - initialSubscriptionsInventoryFilters: [ - { - id: 'productName', - isSortable: true - }, - { - id: 'serviceLevel', - isSortable: true, - isWrappable: true, - cellWidth: 15 - }, - { - id: 'upcomingEventDate', - cell: data => - (data?.upcomingEventDate?.value && moment.utc(data?.upcomingEventDate?.value).format('YYYY-DD-MM')) || '', - isSortable: true, - isWrappable: true, - cellWidth: 15 - } - ], - initialToolbarFilters: [ - { - id: RHSM_API_QUERY_TYPES.SLA + inventoryHostsQuery: { + [RHSM_API_QUERY_TYPES.SORT]: RHSM_API_QUERY_SORT_TYPES.LAST_SEEN, + [RHSM_API_QUERY_TYPES.DIRECTION]: SORT_DIRECTION_TYPES.ASCENDING, + [RHSM_API_QUERY_TYPES.LIMIT]: 100, + [RHSM_API_QUERY_TYPES.OFFSET]: 0 + }, + initialGraphFilters: [ + { + id: 'coreHours', + fill: chartColorBlueLight.value, + stroke: chartColorBlueDark.value, + color: chartColorBlueDark.value + } + ], + initialInventoryFilters: [ + { + id: 'displayName', + cell: data => { + const { displayName = {}, inventoryId = {}, numberOfGuests = {} } = data; + + if (!inventoryId.value) { + return displayName.value; + } + + const updatedDisplayName = displayName.value || inventoryId.value; + + return ( + + {updatedDisplayName}{' '} + {(numberOfGuests.value && + translate('curiosity-inventory.label', { context: 'numberOfGuests', count: numberOfGuests.value }, [ + + ])) || + ''} + + ); + }, + isSortable: true + }, + { + id: 'coreHours', + isSortable: true, + isWrappable: true, + cellWidth: 15 + }, + { + id: 'lastSeen', + cell: data => (data?.lastSeen?.value && ) || '', + isSortable: true, + isWrappable: true, + cellWidth: 15 + } + ], + initialToolbarFilters: undefined, + productLabel: 'OpenShift Metric', + productId: RHSM_API_PATH_ID_TYPES.OPENSHIFT_METRICS, + viewId: 'viewOpenShiftMetric' } ], - t: translate, - viewId: 'viewOpenShift' + productLabel: RHSM_API_PATH_ID_TYPES.OPENSHIFT, + query: {}, + viewId: 'viewOpenShift', // eslint-disable-line + t: translate }; /** diff --git a/src/components/productView/__tests__/__snapshots__/productView.test.js.snap b/src/components/productView/__tests__/__snapshots__/productView.test.js.snap index e656a7b30..c793d5bcf 100644 --- a/src/components/productView/__tests__/__snapshots__/productView.test.js.snap +++ b/src/components/productView/__tests__/__snapshots__/productView.test.js.snap @@ -48,7 +48,7 @@ exports[`ProductView Component should allow custom toolbar components: custom to title="t(curiosity-inventory.tab, {\\"context\\":\\"hosts\\"})" > `; diff --git a/src/components/productView/__tests__/__snapshots__/productViewSatellite.test.js.snap b/src/components/productView/__tests__/__snapshots__/productViewSatellite.test.js.snap index bb30fffd6..0838cfb31 100644 --- a/src/components/productView/__tests__/__snapshots__/productViewSatellite.test.js.snap +++ b/src/components/productView/__tests__/__snapshots__/productViewSatellite.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ProductViewSatellite Component should render a non-connected component: non-connected 1`] = ` - `; diff --git a/src/components/productView/productView.js b/src/components/productView/productView.js index f2c68410e..7b971d90c 100644 --- a/src/components/productView/productView.js +++ b/src/components/productView/productView.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { PageLayout, PageHeader, PageSection, PageToolbar, PageMessages } from '../pageLayout/pageLayout'; -import { apiQueries, connect, reduxSelectors } from '../../redux'; +import { apiQueries } from '../../redux'; import { ConnectedGraphCard, GraphCard } from '../graphCard/graphCard'; import { ConnectedToolbar, Toolbar } from '../toolbar/toolbar'; import { ConnectedInventoryList, InventoryList } from '../inventoryList/inventoryList'; @@ -115,7 +115,7 @@ const ProductView = ({ productConfig, routeDetail, t, toolbarGraph, toolbarProdu { const { [RHSM_API_QUERY_TYPES.START_DATE]: startDate } = productConfig.graphTallyQuery; return ( - ( - + ); /** diff --git a/src/components/productView/productViewSatellite.js b/src/components/productView/productViewSatellite.js index d2e65a8ca..2d15406b3 100644 --- a/src/components/productView/productViewSatellite.js +++ b/src/components/productView/productViewSatellite.js @@ -18,7 +18,7 @@ import { RHSM_API_QUERY_SORT_TYPES, RHSM_API_QUERY_SUBSCRIPTIONS_SORT_TYPES } from '../../types/rhsmApiTypes'; -import { ConnectedProductView, ProductView } from './productView'; +import { ProductView } from './productView'; import { translate } from '../i18n/i18n'; import { dateHelpers, helpers } from '../../common'; @@ -31,7 +31,7 @@ import { dateHelpers, helpers } from '../../common'; * @returns {Node} */ const ProductViewSatellite = ({ productConfig, routeDetail }) => ( - + ); /** diff --git a/src/redux/selectors/__tests__/__snapshots__/viewSelectors.test.js.snap b/src/redux/selectors/__tests__/__snapshots__/viewSelectors.test.js.snap index 52320ed16..fb3100af4 100644 --- a/src/redux/selectors/__tests__/__snapshots__/viewSelectors.test.js.snap +++ b/src/redux/selectors/__tests__/__snapshots__/viewSelectors.test.js.snap @@ -2,10 +2,6 @@ exports[`ViewSelectors should pass minimal data on missing a reducer response: missing reducer error 1`] = ` Object { - "graphTallyQuery": Object {}, - "inventoryGuestsQuery": Object {}, - "inventoryHostsQuery": Object {}, - "inventorySubscriptionsQuery": Object {}, "query": Object {}, } `; diff --git a/src/redux/selectors/viewSelectors.js b/src/redux/selectors/viewSelectors.js index a2bdc3ff6..3d8cf391f 100644 --- a/src/redux/selectors/viewSelectors.js +++ b/src/redux/selectors/viewSelectors.js @@ -1,25 +1,9 @@ -import { createSelectorCreator, defaultMemoize } from 'reselect'; -import _isEqual from 'lodash/isEqual'; +import { createSelector } from 'reselect'; /** - * Create a custom "are objects equal" selector. - * - * @private - * @type {Function}} - */ -const createDeepEqualSelector = createSelectorCreator(defaultMemoize, _isEqual); - -/** - * ToDo: as part of future potential upgrades consider moving reduxHelpers.setApiQuery into statePropsFilter - * The side effect is multiple queries being dumped into the views, meaning more "configuration to handle". - * Leaving it as a single query passed from this selector and handling the "setApiQuery" in "redux/common/apiQueries" - * is a first step. - * i.e. - * queries: { - * query: ..., - * graph: reduxHelpers.setApiQuery(..., RHSM_API_QUERY_SET_REPORT_CAPACITY_TYPES), - * inventory: reduxHelpers.setApiQuery(..., RHSM_API_QUERY_SET_INVENTORY_TYPES) - * } + * FixMe: Remove this selector, switch fully over to context and hooks for products! + * This selector is currently used as a hack by the openshiftView.js to fire a component + * render for UOM selection. This needs to be fixed! */ /** * Return a combined state, props object. @@ -34,45 +18,23 @@ const statePropsFilter = (state = {}, props, defaultProps = {}) => ({ query: { ...defaultProps.query, ...state.view?.query?.[defaultProps.viewId] - }, - graphTallyQuery: { - ...defaultProps.graphTallyQuery, - ...state.view?.graphTallyQuery?.[defaultProps.viewId] - }, - inventoryGuestsQuery: { - ...defaultProps.inventoryGuestsQuery, - ...state.view?.inventoryGuestsQuery?.[defaultProps.viewId] - }, - inventoryHostsQuery: { - ...defaultProps.inventoryHostsQuery, - ...state.view?.inventoryHostsQuery?.[defaultProps.viewId] - }, - inventorySubscriptionsQuery: { - ...defaultProps.inventorySubscriptionsQuery, - ...state.view?.inventorySubscriptionsQuery?.[defaultProps.viewId] } }); /** * Create selector, transform combined state, props into a consumable API param/query object. * - * @type {{query: object, graphTallyQuery: object, inventoryGuestsQuery: object, inventoryHostsQuery: object, - * inventorySubscriptionsQuery: object}} + * @type {{query: object}} */ -const selector = createDeepEqualSelector([statePropsFilter], view => ({ - query: { ...view.query }, - graphTallyQuery: { ...view.graphTallyQuery }, - inventoryGuestsQuery: { ...view.inventoryGuestsQuery }, - inventoryHostsQuery: { ...view.inventoryHostsQuery }, - inventorySubscriptionsQuery: { ...view.inventorySubscriptionsQuery } +const selector = createSelector([statePropsFilter], view => ({ + query: { ...view.query } })); /** * Expose selector instance. For scenarios where a selector is reused across component instances. * * @param {object} defaultProps - * @returns {{query: object, graphTallyQuery: object, inventoryGuestsQuery: object, inventoryHostsQuery: object, - * inventorySubscriptionsQuery: object}} + * @returns {{query: object}} */ const makeSelector = defaultProps => (state, props) => ({ ...selector(state, props, defaultProps)