feat(ui): Enhance resource creation experience when limits are reached (#19103)

* feat(ui): add AssetLimitOverlay

* feat(ui): enable create bucket button

* feat(ui): enable create dashboard and task buttons

* feat(ui): add reusable AssetLimitButton

* feat(ui): change alerts limit experience

* feat(ui): update changelog

* feat(ui): address review comments
pull/19153/head
Daniel Campbell 2020-07-30 09:14:21 -07:00 committed by GitHub
parent 48814abab1
commit bc4c19dfd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 375 additions and 125 deletions

View File

@ -3,20 +3,21 @@
### Breaking
1. [19066](https://github.com/influxdata/influxdb/pull/19066): Drop deprecated /packages route tree
1. [19116](https://github.com/influxdata/influxdb/pull/19116): Support more types for template envRef default value and require explicit default values
1. [19116](https://github.com/influxdata/influxdb/pull/19116): Support more types for template envRef default value and require explicit default values
### Features
1. [19075](https://github.com/influxdata/influxdb/pull/19075): Add resource links to a stack's resources from public HTTP API list/read calls
1. [19103](https://github.com/influxdata/influxdb/pull/19103): Enhance resource creation experience when limits are reached
### Bug Fixes
1. [19043](https://github.com/influxdata/influxdb/pull/19043): Enforce all influx CLI flag args are valid
## v2.0.0-beta.15 [2020-07-23]
### Breaking
1. [19004](https://github.com/influxdata/influxdb/pull/19004): Removed the `migrate` command from the `influxd` binary.
1. [18921](https://github.com/influxdata/influxdb/pull/18921): Restricted UI variable names to not clash with Flux reserved words
@ -41,7 +42,6 @@
1. [18989](https://github.com/influxdata/influxdb/pull/18989): Stopped fetching tags in the advanced builder
1. [19044](https://github.com/influxdata/influxdb/pull/19044): Graph customization: X and Y axis properly accept values
## v2.0.0-beta.14 [2020-07-08]
### Features

View File

@ -3,8 +3,8 @@ import React, {FC, ReactChild, useState} from 'react'
import {connect} from 'react-redux'
// Types
import {AppState, ResourceType} from 'src/types'
import {LimitStatus} from 'src/cloud/actions/limits'
import {AppState, ResourceType, ColumnTypes} from 'src/types'
import {LimitStatus, MonitoringLimits} from 'src/cloud/actions/limits'
// Components
import {
@ -20,14 +20,17 @@ import {
ComponentColor,
} from '@influxdata/clockface'
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
import AssetLimitButton from 'src/cloud/components/AssetLimitButton'
// Utils
import {extractMonitoringLimitStatus} from 'src/cloud/utils/limits'
import {
extractChecksLimits,
extractRulesLimits,
extractEndpointsLimits,
} from 'src/cloud/utils/limits'
type ColumnTypes =
| ResourceType.NotificationRules
| ResourceType.NotificationEndpoints
| ResourceType.Checks
// Constants
import {CLOUD} from 'src/shared/constants'
interface OwnProps {
type: ColumnTypes
@ -38,7 +41,7 @@ interface OwnProps {
}
interface StateProps {
limitStatus: LimitStatus
limitStatus: MonitoringLimits
}
const AlertsColumnHeader: FC<OwnProps & StateProps> = ({
@ -53,6 +56,20 @@ const AlertsColumnHeader: FC<OwnProps & StateProps> = ({
const formattedTitle = title.toLowerCase().replace(' ', '-')
const panelClassName = `alerting-index--column alerting-index--${formattedTitle}`
const resourceName = title.substr(0, title.length - 1)
const isLimitExceeded =
CLOUD &&
limitStatus[type] === LimitStatus.EXCEEDED &&
type !== ResourceType.Checks
const assetLimitButton = (
<AssetLimitButton
color={ComponentColor.Secondary}
buttonText="Create"
resourceName={resourceName}
/>
)
return (
<Panel
@ -70,7 +87,7 @@ const AlertsColumnHeader: FC<OwnProps & StateProps> = ({
tooltipContents={questionMarkTooltipContents}
/>
</FlexBox>
{createButton}
{isLimitExceeded ? assetLimitButton : createButton}
</Panel.Header>
<div className="alerting-index--search">
<Input
@ -88,7 +105,10 @@ const AlertsColumnHeader: FC<OwnProps & StateProps> = ({
>
<div className="alerting-index--list">
{children(searchTerm)}
<AssetLimitAlert resourceName={title} limitStatus={limitStatus} />
<AssetLimitAlert
resourceName={title}
limitStatus={limitStatus[type]}
/>
</div>
</DapperScrollbars>
</div>
@ -98,7 +118,11 @@ const AlertsColumnHeader: FC<OwnProps & StateProps> = ({
const mstp = ({cloud: {limits}}: AppState) => {
return {
limitStatus: extractMonitoringLimitStatus(limits),
limitStatus: {
[ResourceType.Checks]: extractChecksLimits(limits),
[ResourceType.NotificationRules]: extractRulesLimits(limits),
[ResourceType.NotificationEndpoints]: extractEndpointsLimits(limits),
},
}
}

View File

@ -3,12 +3,8 @@ import React, {FC, useEffect} from 'react'
import {connect, ConnectedProps, useDispatch} from 'react-redux'
// Components
import {
Button,
IconFont,
ComponentColor,
ComponentStatus,
} from '@influxdata/clockface'
import {Button, IconFont, ComponentColor} from '@influxdata/clockface'
import AssetLimitButton from 'src/cloud/components/AssetLimitButton'
// Actions
import {checkBucketLimits, LimitStatus} from 'src/cloud/actions/limits'
@ -20,10 +16,12 @@ import {extractBucketLimits} from 'src/cloud/utils/limits'
// Types
import {AppState} from 'src/types'
type ReduxProps = ConnectedProps<typeof connector>
type Props = ReduxProps
// Constants
import {CLOUD} from 'src/shared/constants'
const CreateBucketButton: FC<Props> = ({
type ReduxProps = ConnectedProps<typeof connector>
const CreateBucketButton: FC<ReduxProps> = ({
limitStatus,
onShowOverlay,
onDismissOverlay,
@ -34,33 +32,22 @@ const CreateBucketButton: FC<Props> = ({
dispatch(checkBucketLimits())
}, [dispatch])
const limitExceeded = limitStatus === LimitStatus.EXCEEDED
const text = 'Create Bucket'
let titleText = 'Click to create a bucket'
let buttonStatus = ComponentStatus.Default
if (limitExceeded) {
titleText = 'This account has the maximum number of buckets allowed'
buttonStatus = ComponentStatus.Disabled
const handleItemClick = (): void => {
onShowOverlay('create-bucket', null, onDismissOverlay)
}
const handleItemClick = (): void => {
if (limitExceeded) {
return
}
onShowOverlay('create-bucket', null, onDismissOverlay)
if (CLOUD && limitStatus === LimitStatus.EXCEEDED) {
return <AssetLimitButton resourceName="Bucket" />
}
return (
<Button
icon={IconFont.Plus}
color={ComponentColor.Primary}
text={text}
titleText={titleText}
text="Create Bucket"
titleText="Click to create a bucket"
onClick={handleItemClick}
testID="Create Bucket"
status={buttonStatus}
/>
)
}

View File

@ -23,6 +23,9 @@ import {
ResourceType,
} from 'src/types'
// Utils
import {extractChecksLimits} from 'src/cloud/utils/limits'
type ReduxProps = ConnectedProps<typeof connector>
type Props = ReduxProps & RouteComponentProps<{orgID: string}>
@ -34,6 +37,7 @@ const ChecksColumn: FunctionComponent<Props> = ({
},
rules,
endpoints,
limitStatus,
}) => {
const handleCreateThreshold = () => {
history.push(`/orgs/${orgID}/alerting/checks/new-threshold`)
@ -66,6 +70,7 @@ const ChecksColumn: FunctionComponent<Props> = ({
const createButton = (
<CreateCheckDropdown
limitStatus={limitStatus}
onCreateThreshold={handleCreateThreshold}
onCreateDeadman={handleCreateDeadman}
/>
@ -92,6 +97,10 @@ const ChecksColumn: FunctionComponent<Props> = ({
}
const mstp = (state: AppState) => {
const {
cloud: {limits},
} = state
const checks = getAll<Check>(state, ResourceType.Checks)
const endpoints = getAll<NotificationEndpoint>(
@ -108,6 +117,7 @@ const mstp = (state: AppState) => {
checks: sortChecksByName(checks),
rules: sortRulesByName(rules),
endpoints: sortEndpointsByName(endpoints),
limitStatus: extractChecksLimits(limits),
}
}

View File

@ -1,22 +1,41 @@
// Libraries
import React, {FunctionComponent, MouseEvent} from 'react'
import {connect, ConnectedProps} from 'react-redux'
// Components
import {Dropdown, ComponentColor, IconFont} from '@influxdata/clockface'
// Actions
import {showOverlay, dismissOverlay} from 'src/overlays/actions/overlays'
// Types
import {CheckType} from 'src/types'
import {LimitStatus} from 'src/cloud/actions/limits'
interface Props {
// Constants
import {CLOUD} from 'src/shared/constants'
interface OwnProps {
onCreateThreshold: () => void
onCreateDeadman: () => void
limitStatus: LimitStatus
}
const CreateCheckDropdown: FunctionComponent<Props> = ({
type ReduxProps = ConnectedProps<typeof connector>
const CreateCheckDropdown: FunctionComponent<OwnProps & ReduxProps> = ({
onCreateThreshold,
onCreateDeadman,
onDismissOverlay,
onShowOverlay,
limitStatus,
}) => {
const handleItemClick = (type: CheckType): void => {
if (CLOUD && limitStatus === LimitStatus.EXCEEDED) {
onShowOverlay('asset-limit', {asset: 'Checks'}, onDismissOverlay)
return
}
if (type === 'threshold') {
onCreateThreshold()
}
@ -69,4 +88,11 @@ const CreateCheckDropdown: FunctionComponent<Props> = ({
)
}
export default CreateCheckDropdown
const mdtp = {
onShowOverlay: showOverlay,
onDismissOverlay: dismissOverlay,
}
const connector = connect(null, mdtp)
export default connector(CreateCheckDropdown)

View File

@ -17,6 +17,7 @@ import {
Limits,
RemoteDataState,
ResourceType,
ColumnTypes,
} from 'src/types'
// Selectors
@ -36,6 +37,10 @@ export enum LimitStatus {
EXCEEDED = 'exceeded',
}
export type MonitoringLimits = {
[type in ColumnTypes]: LimitStatus
}
export enum ActionTypes {
SetLimits = 'SET_LIMITS',
SetLimitsStatus = 'SET_LIMITS_STATUS',

View File

@ -0,0 +1,48 @@
// Libraries
import React, {FC} from 'react'
import {connect, ConnectedProps} from 'react-redux'
// Components
import {Button, ComponentColor, IconFont} from '@influxdata/clockface'
// Actions
import {showOverlay, dismissOverlay} from 'src/overlays/actions/overlays'
interface OwnProps {
color?: ComponentColor
resourceName: string
buttonText?: string
}
type ReduxProps = ConnectedProps<typeof connector>
const AssetLimitButton: FC<OwnProps & ReduxProps> = ({
color = ComponentColor.Primary,
buttonText,
resourceName,
onShowOverlay,
onDismissOverlay,
}) => {
const handleClick = () => {
onShowOverlay('asset-limit', {asset: `${resourceName}s`}, onDismissOverlay)
}
return (
<Button
icon={IconFont.Plus}
text={buttonText || `Create ${resourceName}`}
color={color}
titleText={`Click to create ${resourceName}`}
onClick={handleClick}
testID={`create-${resourceName.toLowerCase()}`}
/>
)
}
const mdtp = {
onShowOverlay: showOverlay,
onDismissOverlay: dismissOverlay,
}
const connector = connect(null, mdtp)
export default connector(AssetLimitButton)

View File

@ -0,0 +1,71 @@
// Libraries
import React, {FC} from 'react'
import {connect} from 'react-redux'
import {get} from 'lodash'
// Components
import {
OverlayContainer,
Overlay,
ComponentSize,
GradientBox,
Gradients,
InfluxColors,
} from '@influxdata/clockface'
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
// Types
import {AppState} from 'src/types'
interface OwnProps {
onClose: () => void
}
interface StateProps {
assetName: string
}
const AssetLimitOverlay: FC<OwnProps & StateProps> = ({assetName, onClose}) => {
return (
<OverlayContainer
maxWidth={600}
testID="asset-limit-overlay"
className="asset-limit-overlay"
>
<GradientBox
borderGradient={Gradients.MiyazakiSky}
borderColor={InfluxColors.Raven}
>
<div className="asset-limit-overlay--contents">
<Overlay.Header
title={`Need More ${assetName}?`}
wrapText={true}
onDismiss={onClose}
/>
<Overlay.Body>
<div className="asset-limit-overlay--graphic-container">
<div className="asset-limit-overlay--graphic">
<div className="asset-limit-overlay--graphic-element" />
</div>
</div>
<p>
Youve reached the maximum allotted {assetName} on your current
plan. Upgrade to Pay as you go to create more {assetName}.
</p>
</Overlay.Body>
<Overlay.Footer>
<CloudUpgradeButton size={ComponentSize.Large} />
</Overlay.Footer>
</div>
</GradientBox>
</OverlayContainer>
)
}
const mstp = (state: AppState) => {
return {
assetName: get(state, 'overlays.params.asset', ''),
}
}
export default connect(mstp)(AssetLimitOverlay)

View File

@ -34,10 +34,16 @@ export const extractTaskMax = (limits: LimitsState): number => {
return get(limits, 'tasks.maxAllowed') || Infinity
}
export const extractChecksLimits = (limits: LimitsState): LimitStatus => {
return get(limits, 'checks.limitStatus')
}
export const extractChecksMax = (limits: LimitsState): number => {
return get(limits, 'checks.maxAllowed') || Infinity
}
export const extractRulesLimits = (limits: LimitsState): LimitStatus => {
return get(limits, 'rules.limitStatus')
}
export const extractRulesMax = (limits: LimitsState): number => {
return get(limits, 'rules.maxAllowed') || Infinity
}
@ -45,6 +51,9 @@ export const extractBlockedRules = (limits: LimitsState): string[] => {
return get(limits, 'rules.blocked') || []
}
export const extractEndpointsLimits = (limits: LimitsState): LimitStatus => {
return get(limits, 'endpoints.limitStatus')
}
export const extractEndpointsMax = (limits: LimitsState): number => {
return get(limits, 'endpoints.maxAllowed') || Infinity
}
@ -52,22 +61,6 @@ export const extractBlockedEndpoints = (limits: LimitsState): string[] => {
return get(limits, 'endpoints.blocked') || []
}
export const extractMonitoringLimitStatus = (
limits: LimitsState
): LimitStatus => {
const statuses = [
get(limits, 'rules.limitStatus'),
get(limits, 'endpoints.limitStatus'),
get(limits, 'checks.limitStatus'),
]
if (statuses.includes(LimitStatus.EXCEEDED)) {
return LimitStatus.EXCEEDED
}
return LimitStatus.OK
}
export const extractLimitedMonitoringResources = (
limits: LimitsState
): string => {

View File

@ -29,8 +29,7 @@ import {setDashboardSort} from 'src/dashboards/actions/creators'
// Types
import {AppState, ResourceType} from 'src/types'
import {LimitStatus} from 'src/cloud/actions/limits'
import {ComponentStatus, Sort} from '@influxdata/clockface'
import {Sort} from '@influxdata/clockface'
import {SortTypes} from 'src/shared/utils/sort'
import {DashboardSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
@ -52,7 +51,7 @@ class DashboardIndex extends PureComponent<Props, State> {
}
public render() {
const {createDashboard, sortOptions} = this.props
const {createDashboard, sortOptions, limitStatus} = this.props
const {searchTerm} = this.state
return (
@ -87,7 +86,7 @@ class DashboardIndex extends PureComponent<Props, State> {
onSelectTemplate={this.summonImportFromTemplateOverlay}
resourceName="Dashboard"
canImportFromTemplate={true}
status={this.addResourceStatus}
limitStatus={limitStatus}
/>
</Page.ControlBarRight>
</Page.ControlBar>
@ -156,14 +155,6 @@ class DashboardIndex extends PureComponent<Props, State> {
} = this.props
history.push(`/orgs/${orgID}/dashboards-list/import/template`)
}
private get addResourceStatus(): ComponentStatus {
const {limitStatus} = this.props
if (limitStatus === LimitStatus.EXCEEDED) {
return ComponentStatus.Disabled
}
return ComponentStatus.Default
}
}
const mstp = (state: AppState) => {

View File

@ -14,6 +14,7 @@ import TelegrafConfigOverlay from 'src/telegrafs/components/TelegrafConfigOverla
import TelegrafOutputOverlay from 'src/telegrafs/components/TelegrafOutputOverlay'
import OrgSwitcherOverlay from 'src/pageLayout/components/OrgSwitcherOverlay'
import CreateBucketOverlay from 'src/buckets/components/CreateBucketOverlay'
import AssetLimitOverlay from 'src/cloud/components/AssetLimitOverlay'
// Actions
import {dismissOverlay} from 'src/overlays/actions/overlays'
@ -57,6 +58,9 @@ const OverlayController: FunctionComponent<OverlayControllerProps> = props => {
case 'create-bucket':
activeOverlay = <CreateBucketOverlay onClose={closer} />
break
case 'asset-limit':
activeOverlay = <AssetLimitOverlay onClose={closer} />
break
default:
visibility = false
}

View File

@ -13,6 +13,7 @@ export type OverlayID =
| 'telegraf-output'
| 'switch-organizations'
| 'create-bucket'
| 'asset-limit'
export interface OverlayParams {
[key: string]: string

View File

@ -1,5 +1,6 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect, ConnectedProps} from 'react-redux'
import _ from 'lodash'
// Components
@ -11,11 +12,21 @@ import {
ComponentStatus,
} from '@influxdata/clockface'
// Actions
import {showOverlay, dismissOverlay} from 'src/overlays/actions/overlays'
// Types
import {LimitStatus} from 'src/cloud/actions/limits'
// Constants
import {CLOUD} from 'src/shared/constants'
interface OwnProps {
onSelectNew: () => void
onSelectImport: () => void
onSelectTemplate?: () => void
resourceName: string
limitStatus?: LimitStatus
}
interface DefaultProps {
@ -24,9 +35,11 @@ interface DefaultProps {
titleText: string
}
type Props = OwnProps & DefaultProps
type ReduxProps = ConnectedProps<typeof connector>
export default class AddResourceDropdown extends PureComponent<Props> {
type Props = OwnProps & DefaultProps & ReduxProps
class AddResourceDropdown extends PureComponent<Props> {
public static defaultProps: DefaultProps = {
canImportFromTemplate: false,
status: ComponentStatus.Default,
@ -121,8 +134,23 @@ export default class AddResourceDropdown extends PureComponent<Props> {
return `From a Template`
}
private handleLimit = (): void => {
const {resourceName, onShowOverlay, onDismissOverlay} = this.props
onShowOverlay('asset-limit', {asset: `${resourceName}s`}, onDismissOverlay)
}
private handleSelect = (selection: string): void => {
const {onSelectNew, onSelectImport, onSelectTemplate} = this.props
const {
onSelectNew,
onSelectImport,
onSelectTemplate,
limitStatus = LimitStatus.OK,
} = this.props
if (CLOUD && limitStatus === LimitStatus.EXCEEDED) {
this.handleLimit()
return
}
if (selection === this.newOption) {
onSelectNew()
@ -135,3 +163,12 @@ export default class AddResourceDropdown extends PureComponent<Props> {
}
}
}
const mdtp = {
onShowOverlay: showOverlay,
onDismissOverlay: dismissOverlay,
}
const connector = connect(null, mdtp)
export default connector(AddResourceDropdown)

View File

@ -30,10 +30,12 @@ interface StateProps {
interface OwnProps {
className?: string
buttonText?: string
size?: ComponentSize
}
const CloudUpgradeButton: FC<StateProps & OwnProps> = ({
inView,
size = ComponentSize.Small,
className,
buttonText = 'Upgrade Now',
}) => {
@ -47,7 +49,7 @@ const CloudUpgradeButton: FC<StateProps & OwnProps> = ({
<LinkButton
className={cloudUpgradeButtonClass}
color={ComponentColor.Success}
size={ComponentSize.Small}
size={size}
shape={ButtonShape.Default}
href={`${CLOUD_URL}${CLOUD_CHECKOUT_PATH}`}
target="_self"

View File

@ -1,67 +1,119 @@
.load-data--asset-alert {
margin-bottom: $cf-marg-d;
margin-bottom: $cf-marg-d;
}
.cf-page-control-bar {
+ .rate-alert {
margin: 0 $cf-marg-c $cf-marg-c;
}
+ .rate-alert {
margin: 0 $cf-marg-c $cf-marg-c;
}
}
.rate-alert--content {
display: inline-flex;
flex-direction: column;
font-weight: 500;
.rate-alert--button {
margin-top: $cf-marg-b;
}
display: inline-flex;
flex-direction: column;
font-weight: 500;
.rate-alert--button {
margin-top: $cf-marg-b;
}
}
.cf-page--title {
flex-shrink: 0;
flex-shrink: 0;
}
.asset-alert {
height: 100%;
background-position: bottom -20px left -20px;
background-size: contain;
background-repeat: no-repeat;
background-image: url('../../assets/images/dashboard-empty--dark.svg');
height: 100%;
background-position: bottom -20px left -20px;
background-size: contain;
background-repeat: no-repeat;
background-image: url('~assets/images/dashboard-empty--dark.svg');
}
.dashboards--asset-alert {
.asset-alert--contents {
position: absolute;
bottom: 0;
}
.asset-alert--contents {
position: absolute;
bottom: 0;
}
}
a.upgrade-payg--button {
@include gradient-diag-up($c-pool, $c-rainforest);
@include gradient-diag-up($c-pool, $c-rainforest);
}
.asset-limit-overlay--contents {
background-color: $g1-raven;
border-radius: $cf-radius;
p {
text-align: center;
}
.cf-overlay--title {
word-break: break-word;
}
}
.asset-limit-overlay--graphic-container {
width: 100%;
display: inline-flex;
justify-content: center;
}
.asset-limit-overlay--graphic {
display: inline-flex;
position: relative;
width: 100%;
max-width: 720px;
}
.asset-limit-overlay--graphic:after {
content: '';
display: inline-block;
padding-bottom: 57.3170731707317%;
}
.asset-limit-overlay--graphic-element {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-position: center center;
background-size: contain;
background-repeat: no-repeat;
background-image: url('~assets/images/dashboard-empty--dark.svg');
}
@media screen and (min-width: $cf-nav-menu--breakpoint) {
.rate-alert {
margin-left: $cf-marg-d;
}
.rate-alert {
margin-left: $cf-marg-d;
}
.rate-alert--content {
flex-direction: row;
width: 100%;
height: 100%;
align-items: center;
justify-content: space-between;
.rate-alert--content {
flex-direction: row;
width: 100%;
height: 100%;
align-items: center;
justify-content: space-between;
.rate-alert--button {
margin-top: 0;
margin-left: $cf-marg-c;
}
.rate-alert--button {
margin-top: 0;
margin-left: $cf-marg-c;
}
}
.cf-page-control-bar {
+ .rate-alert {
margin: 0 $cf-marg-d $cf-marg-c;
}
.cf-page-control-bar {
+ .rate-alert {
margin: 0 $cf-marg-d $cf-marg-c;
}
}
}
.asset-limit-overlay--graphic {
width: 60%;
}
.asset-limit-overlay--contents {
.cf-overlay--header__wrap {
align-items: center;
}
}
}

View File

@ -6,7 +6,6 @@ import {
InputLabel,
SlideToggle,
ComponentSize,
ComponentStatus,
Page,
Sort,
FlexBox,
@ -57,6 +56,7 @@ export default class TasksHeader extends PureComponent<Props> {
sortType,
sortDirection,
onSort,
limitStatus,
} = this.props
return (
@ -98,19 +98,11 @@ export default class TasksHeader extends PureComponent<Props> {
onSelectImport={onImportTask}
onSelectTemplate={onImportFromTemplate}
resourceName="Task"
status={this.addResourceStatus}
limitStatus={limitStatus}
/>
</Page.ControlBarRight>
</Page.ControlBar>
</>
)
}
private get addResourceStatus(): ComponentStatus {
const {limitStatus} = this.props
if (limitStatus === LimitStatus.EXCEEDED) {
return ComponentStatus.Disabled
}
return ComponentStatus.Default
}
}

View File

@ -20,7 +20,9 @@ import {
CheckBase as GenCheckBase,
NotificationEndpointBase as GenEndpointBase,
} from 'src/client'
import {RemoteDataState} from 'src/types'
import {ResourceType} from './resources'
type Omit<T, U> = Pick<T, Exclude<keyof T, U>>
type Overwrite<T, U> = Omit<T, keyof U> & U
@ -30,6 +32,11 @@ interface WithClientID<T> {
value: T
}
export type ColumnTypes =
| ResourceType.NotificationRules
| ResourceType.NotificationEndpoints
| ResourceType.Checks
/* Endpoints */
type EndpointOverrides = {
status: RemoteDataState