Skip to content

Commit

Permalink
Merge pull request #3689 from dlabrecq/tag-mapping-empty-state
Browse files Browse the repository at this point in the history
Added empty state
  • Loading branch information
dlabrecq authored Mar 7, 2024
2 parents 62c4275 + 860118a commit 4d9898c
Show file tree
Hide file tree
Showing 21 changed files with 165 additions and 43 deletions.
30 changes: 30 additions & 0 deletions locales/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -10205,6 +10205,36 @@
"value": "There are no export files available"
}
],
"noMappedTags": [
{
"type": 0,
"value": "No mapped tags"
}
],
"noMappedTagsDesc": [
{
"type": 0,
"value": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. "
},
{
"type": 1,
"value": "warning"
},
{
"type": 0,
"value": " Changes will be reflected within 24 hours. "
},
{
"type": 1,
"value": "learnMore"
}
],
"noMappedTagsWarning": [
{
"type": 0,
"value": "Tags must be enabled to be mapped."
}
],
"noOptimizationsDesc": [
{
"type": 0,
Expand Down
3 changes: 3 additions & 0 deletions locales/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@
"noDataStateRefresh": "Refresh this page",
"noDataStateTitle": "Still processing the data",
"noExportsStateTitle": "There are no export files available",
"noMappedTags": "No mapped tags",
"noMappedTagsDesc": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. {warning} Changes will be reflected within 24 hours. {learnMore}",
"noMappedTagsWarning": "Tags must be enabled to be mapped.",
"noOptimizationsDesc": "Resource Optimization is now available in preview for select customers. If your organization wants to participate, tell us through the Feedback button, which is purple and located on the right. Otherwise, there is not enough data available to generate an optimization.",
"noOptimizationsTitle": "No optimizations available",
"noProvidersStateAwsDesc": "Add an Amazon Web Services account to see a total cost breakdown of your spend by accounts, organizational units, services, regions, or tags.",
Expand Down
2 changes: 2 additions & 0 deletions src/api/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const enum SettingsType {
tags = 'tags',
tagsEnable = 'tagsEnable',
tagsDisable = 'tagsDisable',
tagsMappings = 'tagsMappings',
}

export const SettingsTypePaths: Partial<Record<SettingsType, string>> = {
Expand All @@ -52,6 +53,7 @@ export const SettingsTypePaths: Partial<Record<SettingsType, string>> = {
[SettingsType.tags]: 'settings/tags',
[SettingsType.tagsEnable]: 'settings/tags/enable/',
[SettingsType.tagsDisable]: 'settings/tags/disable/',
[SettingsType.tagsMappings]: 'settings/tags/mappings',
};

export function fetchSettings(settingsType: SettingsType, query: string) {
Expand Down
17 changes: 17 additions & 0 deletions src/locales/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,23 @@ export default defineMessages({
description: 'There are no export files available',
id: 'noExportsStateTitle',
},
noMappedTags: {
defaultMessage: 'No mapped tags',
description: 'No mapped tags',
id: 'noMappedTags',
},
noMappedTagsDesc: {
defaultMessage:
'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. {warning} Changes will be reflected within 24 hours. {learnMore}',
description:
'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. {warning} Changes will be reflected within 24 hours. {learnMore}',
id: 'noMappedTagsDesc',
},
noMappedTagsWarning: {
defaultMessage: 'Tags must be enabled to be mapped.',
description: 'Tags must be enabled to be mapped.',
id: 'noMappedTagsWarning',
},
noOptimizationsDesc: {
defaultMessage:
'Resource Optimization is now available in preview for select customers. If your organization wants to participate, tell us through the Feedback button, which is purple and located on the right. Otherwise, there is not enough data available to generate an optimization.',
Expand Down
2 changes: 1 addition & 1 deletion src/routes/components/page/noData/noDataState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class NoDataStateBase extends React.Component<NoDataStateProps, any> {
return (
<EmptyState variant={EmptyStateVariant.lg} className="pf-m-redhat-font">
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.noDataStateTitle)}</>}
titleText={intl.formatMessage(messages.noDataStateTitle)}
icon={<EmptyStateIcon icon={PlusCircleIcon} />}
headingLevel="h5"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class NoOptimizationsStateBase extends React.Component<NoOptimizationsStateProps
return (
<EmptyState variant={EmptyStateVariant.lg} className="pf-m-redhat-font">
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.noOptimizationsTitle)}</>}
titleText={intl.formatMessage(messages.noOptimizationsTitle)}
icon={<EmptyStateIcon icon={OptimizationIcon as any} />}
headingLevel="h1"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class NoProvidersStateBase extends React.Component<NoProvidersStateProps, any> {
return (
<EmptyState variant={EmptyStateVariant.lg} className="pf-m-redhat-font">
<EmptyStateHeader
titleText={<>{intl.formatMessage(titleKey)}</>}
titleText={intl.formatMessage(titleKey)}
icon={<EmptyStateIcon icon={icon ? icon : PlusCircleIcon} />}
headingLevel="h1"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const EmptyFilterStateBase: React.FC<EmptyFilterStateProps> = ({
>
<EmptyState>
{getItem()}
<EmptyStateHeader titleText={<>{intl.formatMessage(title)}</>} headingLevel="h2" />
<EmptyStateHeader titleText={intl.formatMessage(title)} headingLevel="h2" />
<EmptyStateBody>{intl.formatMessage(subTitle)}</EmptyStateBody>
</EmptyState>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/components/state/errorState/errorState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const ErrorStateBase: React.FC<ErrorStateProps> = ({ error, icon = ErrorCircleOI

return (
<EmptyState variant={EmptyStateVariant.lg} className="pf-m-redhat-font">
<EmptyStateHeader titleText={<>{title}</>} icon={<EmptyStateIcon icon={icon} />} headingLevel="h5" />
<EmptyStateHeader titleText={title} icon={<EmptyStateIcon icon={icon} />} headingLevel="h5" />
<EmptyStateBody>{subTitle}</EmptyStateBody>
</EmptyState>
);
Expand Down
2 changes: 1 addition & 1 deletion src/routes/components/state/loadingState/loadingState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const LoadingStateBase: React.FC<LoadingStateProps> = ({
return (
<EmptyState variant={EmptyStateVariant.lg} className="pf-m-redhat-font">
<Spinner size="lg" />
<EmptyStateHeader titleText={<>{heading}</>} headingLevel="h5" />
<EmptyStateHeader titleText={heading} headingLevel="h5" />
<EmptyStateBody>{body}</EmptyStateBody>
</EmptyState>
);
Expand Down
2 changes: 1 addition & 1 deletion src/routes/settings/costModels/components/errorState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const ErrorState: React.FC<ErrorStateProps> = ({ variant, actionButton, t
return (
<EmptyState variant={variant}>
<EmptyStateHeader
titleText={<>{title}</>}
titleText={title}
icon={<EmptyStateIcon icon={ExclamationCircleIcon} color={global_DangerColor_100.value} />}
headingLevel="h4"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/settings/costModels/costModel/costModelInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class CostModelInfo extends React.Component<CostModelInfoProps, CostModelInfoSta
<PageSection>
<EmptyState>
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.costModelsUUIDEmptyState)}</>}
titleText={intl.formatMessage(messages.costModelsUUIDEmptyState)}
icon={<EmptyStateIcon icon={ErrorCircleOIcon} />}
headingLevel="h2"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ class PriceListTable extends React.Component<PriceListTableProps, PriceListTable
<Bullseye>
<EmptyState>
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.priceListEmptyRate)}</>}
titleText={intl.formatMessage(messages.priceListEmptyRate)}
icon={<EmptyStateIcon icon={PlusCircleIcon} />}
headingLevel="h2"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/settings/costModels/costModel/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class TableBase extends React.Component<TableBaseProps, TableBaseState> {
<div style={styles.emptyState}>
<EmptyState>
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.costModelsSourceEmptyStateDesc)}</>}
titleText={intl.formatMessage(messages.costModelsSourceEmptyStateDesc)}
icon={<EmptyStateIcon icon={PlusCircleIcon} />}
headingLevel="h2"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class PriceListTable extends React.Component<PriceListTableProps, PriceListTable
<Bullseye>
<EmptyState>
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.costModelsWizardEmptyStateTitle)}</>}
titleText={intl.formatMessage(messages.costModelsWizardEmptyStateTitle)}
icon={<EmptyStateIcon icon={PlusCircleIcon} />}
headingLevel="h2"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/settings/costModels/costModelWizard/review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const ReviewSuccessBase: React.FC<WrappedComponentProps> = ({ intl }) => (
{({ onClose, name }) => (
<EmptyState>
<EmptyStateHeader
titleText={<>{intl.formatMessage(messages.costModelsWizardReviewStatusTitle)}</>}
titleText={intl.formatMessage(messages.costModelsWizardReviewStatusTitle)}
icon={
<Icon status="success">
<EmptyStateIcon icon={OkIcon} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface EmptyStateBaseProps {
function EmptyStateBase(props: EmptyStateBaseProps): JSX.Element {
return (
<EmptyState className="pf-m-redhat-font">
<EmptyStateHeader titleText={<>{props.title}</>} icon={<EmptyStateIcon icon={props.icon} />} headingLevel="h2" />
<EmptyStateHeader titleText={props.title} icon={<EmptyStateIcon icon={props.icon} />} headingLevel="h2" />
<EmptyStateBody>{props.description}</EmptyStateBody>
<EmptyStateFooter>{props.actions ? props.actions : null}</EmptyStateFooter>
</EmptyState>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export const styles = {
action: {
marginLeft: global_spacer_md.var,
},
emptyStateContainer: {
paddingTop: global_spacer_md.value,
},
pagination: {
backgroundColor: global_BackgroundColor_light_100.value,
paddingBottom: global_spacer_md.value,
Expand Down
50 changes: 22 additions & 28 deletions src/routes/settings/tagLabels/tagMappings/tagMappings.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Unavailable from '@patternfly/react-component-groups/dist/esm/UnavailableContent';
import { Pagination, PaginationVariant } from '@patternfly/react-core';
import type { Query } from 'api/queries/query';
import { getQuery } from 'api/queries/query';
Expand All @@ -10,14 +11,14 @@ import { useIntl } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
import type { AnyAction } from 'redux';
import type { ThunkDispatch } from 'redux-thunk';
import { NotAvailable } from 'routes/components/page/notAvailable';
import { LoadingState } from 'routes/components/state/loadingState';
import * as queryUtils from 'routes/utils/query';
import type { RootState } from 'store';
import { FetchStatus } from 'store/common';
import { settingsActions, settingsSelectors } from 'store/settings';

import { styles } from './tagMappings.styles';
import { TagMappingsEmptyState } from './tagMappingsEmptyState';
import { TagMappingsTable } from './tagMappingsTable';
import { TagMappingsToolbar } from './tagMappingsToolbar';

Expand All @@ -43,7 +44,7 @@ const baseQuery: Query = {
offset: 0,
filter_by: {},
order_by: {
key: 'asc',
parent: 'asc',
},
};

Expand Down Expand Up @@ -100,14 +101,14 @@ const TagMappings: React.FC<MappingsProps> = ({ canWrite }) => {
);
};

const getToolbar = (tags: SettingsData[]) => {
const getToolbar = (mappings: SettingsData[]) => {
const itemsTotal = settings?.meta ? settings.meta.count : 0;

return (
<TagMappingsToolbar
canWrite={canWrite}
isDisabled={tags.length === 0}
itemsPerPage={tags.length}
isDisabled={mappings.length === 0}
itemsPerPage={mappings.length}
itemsTotal={itemsTotal}
onCreateTagMapping={handleOnCreateTagMapping}
onFilterAdded={filter => handleOnFilterAdded(filter)}
Expand Down Expand Up @@ -148,11 +149,12 @@ const TagMappings: React.FC<MappingsProps> = ({ canWrite }) => {
};

if (settingsError) {
return <NotAvailable />;
return <Unavailable />;
}

const tags = getMappings();
const isDisabled = tags.length === 0;
const mappings = getMappings();
const isDisabled = mappings.length === 0;
const hasMappings = mappings.length > 0 && !Object.keys(query.filter_by).length; // no filter applied

return (
<>
Expand All @@ -166,14 +168,18 @@ const TagMappings: React.FC<MappingsProps> = ({ canWrite }) => {
warning: <b>{intl.formatMessage(messages.tagMappingWarning)}</b>,
})}
</div>
{getToolbar(tags)}
{hasMappings && getToolbar(mappings)}
{settingsStatus === FetchStatus.inProgress ? (
<LoadingState />
) : (
) : hasMappings ? (
<>
{getTable()}
<div style={styles.pagination}>{getPagination(isDisabled, true)}</div>
</>
) : (
<div style={styles.emptyStateContainer}>
<TagMappingsEmptyState canWrite={canWrite} onCreateTagMapping={handleOnCreateTagMapping} />
</div>
)}
</>
);
Expand All @@ -191,32 +197,20 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => {
};
const settingsQueryString = getQuery(settingsQuery);
const settings = useSelector((state: RootState) =>
settingsSelectors.selectSettings(state, SettingsType.tags, settingsQueryString)
settingsSelectors.selectSettings(state, SettingsType.tagsMappings, settingsQueryString)
);
const settingsStatus = useSelector((state: RootState) =>
settingsSelectors.selectSettingsStatus(state, SettingsType.tags, settingsQueryString)
settingsSelectors.selectSettingsStatus(state, SettingsType.tagsMappings, settingsQueryString)
);
const settingsError = useSelector((state: RootState) =>
settingsSelectors.selectSettingsError(state, SettingsType.tags, settingsQueryString)
);

const settingsUpdateDisableStatus = useSelector((state: RootState) =>
settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsDisable)
);
const settingsUpdateEnableStatus = useSelector((state: RootState) =>
settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsEnable)
settingsSelectors.selectSettingsError(state, SettingsType.tagsMappings, settingsQueryString)
);

useEffect(() => {
if (
!settingsError &&
settingsStatus !== FetchStatus.inProgress &&
settingsUpdateDisableStatus !== FetchStatus.inProgress &&
settingsUpdateEnableStatus !== FetchStatus.inProgress
) {
dispatch(settingsActions.fetchSettings(SettingsType.tags, settingsQueryString));
if (!settingsError && settingsStatus !== FetchStatus.inProgress) {
dispatch(settingsActions.fetchSettings(SettingsType.tagsMappings, settingsQueryString));
}
}, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]);
}, [query]);

return {
settings,
Expand Down
Loading

0 comments on commit 4d9898c

Please sign in to comment.