From 2d5d7bbf039f515e069839c95fb35bce64e16d21 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Thu, 10 Aug 2023 16:03:43 -0400 Subject: [PATCH] Update cost models settings layout per mocks https://issues.redhat.com/browse/COST-3307 --- locales/data.json | 10 +++- locales/translations.json | 3 +- src/__mocks__/react-intl.ts | 5 +- src/locales/messages.ts | 13 +++-- .../costModels/costModelsDetails/table.tsx | 2 +- .../costModels/components/readOnlyTooltip.tsx | 17 ++++--- .../costModels/costModel/distribution.tsx | 2 +- .../settings/costModels/costModel/header.tsx | 47 ++++++++++++------- .../settings/costModels/costModel/markup.tsx | 2 +- .../costModels/costModel/sourcesTable.tsx | 46 +++++++----------- .../costModels/costModelsDetails/table.tsx | 42 ++++++++--------- 11 files changed, 100 insertions(+), 89 deletions(-) diff --git a/locales/data.json b/locales/data.json index b55647f28..c96ab9eae 100644 --- a/locales/data.json +++ b/locales/data.json @@ -1376,10 +1376,10 @@ "value": "Should not exceed 100 characters" } ], - "costModelsLastChange": [ + "costModelsLastUpdated": [ { "type": 0, - "value": "Last change" + "value": "Last updated" } ], "costModelsPopover": [ @@ -11230,6 +11230,12 @@ "value": "Failed to get RBAC information" } ], + "readOnly": [ + { + "type": 0, + "value": "Read only" + } + ], "readOnlyPermissions": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 1cd9b4e76..f8ea59e3a 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -109,7 +109,7 @@ "costModelsFilterPlaceholder": "Filter by name...", "costModelsFilterTagKey": "Filter by tag key", "costModelsInfoTooLong": "Should not exceed 100 characters", - "costModelsLastChange": "Last change", + "costModelsLastUpdated": "Last updated", "costModelsPopover": "A cost model allows you to associate a price to metrics provided by your sources to charge for utilization of resources. {learnMore}", "costModelsPopoverAriaLabel": "Cost model info popover", "costModelsPopoverButtonAriaLabel": "Opens a dialog with cost model info", @@ -484,6 +484,7 @@ "rawCostTitle": "Raw cost", "rbacErrorDesc": "There was a problem receiving user permissions. Refreshing this page may fix it. If it does not, please contact your admin.", "rbacErrorTitle": "Failed to get RBAC information", + "readOnly": "Read only", "readOnlyPermissions": "You have read only permissions", "recommended": "Recommended", "redHatStatusUrl": "https://status.redhat.com", diff --git a/src/__mocks__/react-intl.ts b/src/__mocks__/react-intl.ts index d3a40d2f6..f1dcaa03b 100644 --- a/src/__mocks__/react-intl.ts +++ b/src/__mocks__/react-intl.ts @@ -3,12 +3,12 @@ const mockedReactIntl = jest.genMockFromModule('react-intl') as any; const intl = { formatDate: jest.fn(() => ''), // Dates won't match snapshots during PR builds formatDateTimeRange: jest.fn(() => ''), // Dates won't match snapshots during PR builds - formatMessage: ({ defaultMessage }, params?) => { + formatMessage: jest.fn(({ defaultMessage }, params?) => { if (!params) { return defaultMessage; } return defaultMessage + JSON.stringify(params); - }, + }), formatNumber: jest.fn(v => v), }; @@ -16,5 +16,6 @@ mockedReactIntl.createIntl = () => intl; mockedReactIntl.defineMessages = jest.fn(v => v); mockedReactIntl.injectIntl = jest.fn(v => v); // mockedReactIntl.injectIntl = Component => props => ; +mockedReactIntl.useIntl = () => intl; module.exports = mockedReactIntl; diff --git a/src/locales/messages.ts b/src/locales/messages.ts index dfff44b26..a28ff4257 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -610,10 +610,10 @@ export default defineMessages({ description: 'Should not exceed 100 characters', id: 'costModelsInfoTooLong', }, - costModelsLastChange: { - defaultMessage: 'Last change', - description: 'Last change', - id: 'costModelsLastChange', + costModelsLastUpdated: { + defaultMessage: 'Last updated', + description: 'Last updated', + id: 'costModelsLastUpdated', }, costModelsPopover: { defaultMessage: @@ -3014,6 +3014,11 @@ export default defineMessages({ description: 'RBAC error title', id: 'rbacErrorTitle', }, + readOnly: { + defaultMessage: 'Read only', + description: 'Read only', + id: 'readOnly', + }, readOnlyPermissions: { defaultMessage: 'You have read only permissions', description: 'You have read only permissions', diff --git a/src/routes/costModels/costModelsDetails/table.tsx b/src/routes/costModels/costModelsDetails/table.tsx index 57580027b..0f019291a 100644 --- a/src/routes/costModels/costModelsDetails/table.tsx +++ b/src/routes/costModels/costModelsDetails/table.tsx @@ -76,7 +76,7 @@ class CostModelsTableBase extends React.Component = ({ - children, - tooltip = 'You have read only permissions', - isDisabled, -}) => { +const ReadOnlyTooltip: React.FC = ({ children, tooltip, isDisabled }) => { + const intl = useIntl(); + const content = tooltip ? tooltip : intl.formatMessage(messages.readOnlyPermissions); + return isDisabled ? ( - {tooltip}}> -
{children}
+ {content}}> +
{children}
) : ( children ); }; + +export { ReadOnlyTooltip }; diff --git a/src/routes/settings/costModels/costModel/distribution.tsx b/src/routes/settings/costModels/costModel/distribution.tsx index 7fffbc91f..7a9abbba7 100644 --- a/src/routes/settings/costModels/costModel/distribution.tsx +++ b/src/routes/settings/costModels/costModel/distribution.tsx @@ -51,9 +51,9 @@ const DistributionCardBase: React.FC = ({ diff --git a/src/routes/settings/costModels/costModel/header.tsx b/src/routes/settings/costModels/costModel/header.tsx index 46bce0555..c29097b2e 100644 --- a/src/routes/settings/costModels/costModel/header.tsx +++ b/src/routes/settings/costModels/costModel/header.tsx @@ -23,7 +23,7 @@ import type { CostModel } from 'api/costModels'; import messages from 'locales/messages'; import React from 'react'; import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; +import { useIntl } from 'react-intl'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import { routes } from 'routes'; @@ -54,7 +54,6 @@ const Header: React.FC = ({ current, deleteCostModel, deleteError, - intl, isDeleteProcessing, isDialogOpen, isWritePermission, @@ -65,6 +64,18 @@ const Header: React.FC = ({ tabIndex, }) => { const [dropdownIsOpen, setDropdownIsOpen] = React.useState(false); + const intl = useIntl(); + + const dateTime: any = intl.formatDate(current.updated_timestamp, { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + }); return ( <> @@ -161,6 +172,10 @@ const Header: React.FC = ({ + + {intl.formatMessage(messages.costModelsLastUpdated)} + + {dateTime} {intl.formatMessage(messages.currency)} {intl.formatMessage(messages.currencyOptions, { units: current.currency || 'USD' })} @@ -209,19 +224,17 @@ const Header: React.FC = ({ ); }; -export default injectIntl( - withRouter( - connect( - createMapStateToProps(state => ({ - isDialogOpen: costModelsSelectors.isDialogOpen(state)('costmodel'), - isDeleteProcessing: costModelsSelectors.deleteProcessing(state), - deleteError: costModelsSelectors.deleteError(state), - isWritePermission: rbacSelectors.isCostModelWritePermission(state), - })), - { - setDialogOpen: costModelsActions.setCostModelDialog, - deleteCostModel: costModelsActions.deleteCostModel, - } - )(Header) - ) +export default withRouter( + connect( + createMapStateToProps(state => ({ + isDialogOpen: costModelsSelectors.isDialogOpen(state)('costmodel'), + isDeleteProcessing: costModelsSelectors.deleteProcessing(state), + deleteError: costModelsSelectors.deleteError(state), + isWritePermission: rbacSelectors.isCostModelWritePermission(state), + })), + { + setDialogOpen: costModelsActions.setCostModelDialog, + deleteCostModel: costModelsActions.deleteCostModel, + } + )(Header) ); diff --git a/src/routes/settings/costModels/costModel/markup.tsx b/src/routes/settings/costModels/costModel/markup.tsx index 76a25e927..846de7951 100644 --- a/src/routes/settings/costModels/costModel/markup.tsx +++ b/src/routes/settings/costModels/costModel/markup.tsx @@ -56,9 +56,9 @@ const MarkupCardBase: React.FC = ({ diff --git a/src/routes/settings/costModels/costModel/sourcesTable.tsx b/src/routes/settings/costModels/costModel/sourcesTable.tsx index e8563ccb5..a1eca12fe 100644 --- a/src/routes/settings/costModels/costModel/sourcesTable.tsx +++ b/src/routes/settings/costModels/costModel/sourcesTable.tsx @@ -1,11 +1,14 @@ -import type { IAction, IRow } from '@patternfly/react-table'; -import { ActionsColumn, TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import { Button, ButtonVariant } from '@patternfly/react-core'; +import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; +import type { IRow } from '@patternfly/react-table'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { TableGridBreakpoint } from '@patternfly/react-table'; import messages from 'locales/messages'; import React from 'react'; import type { WrappedComponentProps } from 'react-intl'; import { injectIntl } from 'react-intl'; import { connect } from 'react-redux'; +import { ReadOnlyTooltip } from 'routes/settings/costModels/components/readOnlyTooltip'; import { createMapStateToProps } from 'store/common'; import { costModelsSelectors } from 'store/costModels'; import { rbacSelectors } from 'store/rbac'; @@ -22,26 +25,6 @@ interface SourcesTableStateProps { type SourcesTableProps = SourcesTableOwnProps & SourcesTableStateProps & WrappedComponentProps; const SourcesTable: React.FC = ({ canWrite, costModels, intl, showDeleteDialog }) => { - const getActions = (): IAction[] => { - if (canWrite) { - return [ - { - title: intl.formatMessage(messages.costModelsSourceDelete), - onClick: (_evt, rowIndex: number) => showDeleteDialog(rowIndex), - }, - ]; - } - return [ - { - style: { pointerEvents: 'auto' }, - tooltip: intl.formatMessage(messages.readOnlyPermissions), - isDisabled: true, - title: intl.formatMessage(messages.costModelsSourceDelete), - }, - ]; - }; - - const actions = getActions(); const rows: (IRow | string[])[] = costModels.length > 0 ? costModels[0].sources.map(source => [source.name]) : []; return ( @@ -61,14 +44,17 @@ const SourcesTable: React.FC = ({ canWrite, costModels, intl, {r} - { - return { - ...a, - onClick: () => a.onClick(null, rowIndex, r, null), - }; - })} - > + + + ))} diff --git a/src/routes/settings/costModels/costModelsDetails/table.tsx b/src/routes/settings/costModels/costModelsDetails/table.tsx index d2da5f676..569584384 100644 --- a/src/routes/settings/costModels/costModelsDetails/table.tsx +++ b/src/routes/settings/costModels/costModelsDetails/table.tsx @@ -1,5 +1,7 @@ -import type { ICell, IRowData, ThProps } from '@patternfly/react-table'; -import { ActionsColumn, TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import { Button, ButtonVariant } from '@patternfly/react-core'; +import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; +import type { ICell, ThProps } from '@patternfly/react-table'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { sortable, TableGridBreakpoint } from '@patternfly/react-table'; import type { CostModel } from 'api/costModels'; import { intl as defaultIntl } from 'components/i18n'; @@ -9,6 +11,7 @@ import type { WrappedComponentProps } from 'react-intl'; import { injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import type { Dispatch } from 'redux'; +import { ReadOnlyTooltip } from 'routes/settings/costModels/components/readOnlyTooltip'; import { createMapStateToProps } from 'store/common'; import { costModelsActions, costModelsSelectors } from 'store/costModels'; import { rbacSelectors } from 'store/rbac'; @@ -16,7 +19,7 @@ import type { RouterComponentProps } from 'utils/router'; import { withRouter } from 'utils/router'; import type { CostModelsQuery } from './utils/query'; -import { createActions, createOnSort, getRowsByStateName } from './utils/table'; +import { createOnSort, getRowsByStateName } from './utils/table'; interface CostModelsTableOwnProps { actionResolver?: any; @@ -75,7 +78,7 @@ class CostModelsTableBase extends React.Component { - openDeleteDialog(rowData.data); - }, - } as any, - ]); - const onSort = createOnSort(cells, query, router); const getSortParams = (columnIndex: number): ThProps['sort'] => ({ sortBy: { @@ -137,16 +130,19 @@ class CostModelsTableBase extends React.Component ))} - {!r.heightAuto && ( + {!r.heightAuto && stateName === 'success' && ( - { - return { - ...a, - onClick: _evt => a.onClick(_evt, rowIndex, r, null), - }; - })} - /> + + + )}