fix(ui): don't close check editor overlay when saving check fails
parent
e925b0ad72
commit
3dadb2b4ce
|
@ -126,53 +126,43 @@ export const saveCheckFromTimeMachine = () => async (
|
||||||
dispatch: Dispatch<any>,
|
dispatch: Dispatch<any>,
|
||||||
getState: GetState
|
getState: GetState
|
||||||
) => {
|
) => {
|
||||||
try {
|
const state = getState()
|
||||||
const state = getState()
|
const {
|
||||||
const {
|
orgs: {
|
||||||
orgs: {
|
org: {id: orgID},
|
||||||
org: {id: orgID},
|
},
|
||||||
},
|
} = state
|
||||||
} = state
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
draftQueries,
|
draftQueries,
|
||||||
alerting: {check},
|
alerting: {check},
|
||||||
} = getActiveTimeMachine(state)
|
} = getActiveTimeMachine(state)
|
||||||
|
|
||||||
const checkWithOrg = {...check, query: draftQueries[0], orgID} as Check
|
const checkWithOrg = {...check, query: draftQueries[0], orgID} as Check
|
||||||
|
|
||||||
const resp = check.id
|
const resp = check.id
|
||||||
? await api.patchCheck({checkID: check.id, data: checkWithOrg})
|
? await api.patchCheck({checkID: check.id, data: checkWithOrg})
|
||||||
: await api.postCheck({data: checkWithOrg})
|
: await api.postCheck({data: checkWithOrg})
|
||||||
|
|
||||||
if (resp.status === 201 || resp.status === 200) {
|
if (resp.status === 201 || resp.status === 200) {
|
||||||
dispatch(setCheck(resp.data))
|
dispatch(setCheck(resp.data))
|
||||||
} else {
|
} else {
|
||||||
throw new Error(resp.data.message)
|
throw new Error(resp.data.message)
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
dispatch(notify(copy.createCheckFailed(e.message)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateCheck = (check: Partial<Check>) => async (
|
export const updateCheck = (check: Partial<Check>) => async (
|
||||||
dispatch: Dispatch<Action | NotificationAction>
|
dispatch: Dispatch<Action | NotificationAction>
|
||||||
) => {
|
) => {
|
||||||
try {
|
const resp = await api.patchCheck({checkID: check.id, data: check as Check})
|
||||||
const resp = await api.patchCheck({checkID: check.id, data: check as Check})
|
|
||||||
|
|
||||||
if (resp.status === 200) {
|
|
||||||
dispatch(setCheck(resp.data))
|
|
||||||
} else {
|
|
||||||
throw new Error(resp.data.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
dispatch(setCheck(resp.data))
|
dispatch(setCheck(resp.data))
|
||||||
} catch (e) {
|
} else {
|
||||||
console.error(e)
|
throw new Error(resp.data.message)
|
||||||
dispatch(notify(copy.updateCheckFailed(e.message)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispatch(setCheck(resp.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteCheck = (checkID: string) => async (
|
export const deleteCheck = (checkID: string) => async (
|
||||||
|
|
|
@ -25,6 +25,8 @@ import {
|
||||||
} from 'src/alerting/actions/checks'
|
} from 'src/alerting/actions/checks'
|
||||||
import {createLabel as createLabelAsync} from 'src/labels/actions'
|
import {createLabel as createLabelAsync} from 'src/labels/actions'
|
||||||
import {viewableLabels} from 'src/labels/selectors'
|
import {viewableLabels} from 'src/labels/selectors'
|
||||||
|
import {notify} from 'src/shared/actions/notifications'
|
||||||
|
import {updateCheckFailed} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {Check, Label, AppState, AlertHistoryType} from 'src/types'
|
import {Check, Label, AppState, AlertHistoryType} from 'src/types'
|
||||||
|
@ -36,6 +38,7 @@ interface DispatchProps {
|
||||||
onRemoveCheckLabel: typeof deleteCheckLabel
|
onRemoveCheckLabel: typeof deleteCheckLabel
|
||||||
onCreateLabel: typeof createLabelAsync
|
onCreateLabel: typeof createLabelAsync
|
||||||
onCloneCheck: typeof cloneCheck
|
onCloneCheck: typeof cloneCheck
|
||||||
|
onNotify: typeof notify
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
|
@ -53,6 +56,7 @@ const CheckCard: FunctionComponent<Props> = ({
|
||||||
onAddCheckLabel,
|
onAddCheckLabel,
|
||||||
onCreateLabel,
|
onCreateLabel,
|
||||||
onCloneCheck,
|
onCloneCheck,
|
||||||
|
onNotify,
|
||||||
check,
|
check,
|
||||||
updateCheck,
|
updateCheck,
|
||||||
deleteCheck,
|
deleteCheck,
|
||||||
|
@ -60,12 +64,20 @@ const CheckCard: FunctionComponent<Props> = ({
|
||||||
labels,
|
labels,
|
||||||
router,
|
router,
|
||||||
}) => {
|
}) => {
|
||||||
const onUpdateName = (name: string) => {
|
const onUpdateName = async (name: string) => {
|
||||||
updateCheck({id: check.id, name})
|
try {
|
||||||
|
await updateCheck({id: check.id, name})
|
||||||
|
} catch (e) {
|
||||||
|
onNotify(updateCheckFailed(e.message))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateDescription = (description: string) => {
|
const onUpdateDescription = async (description: string) => {
|
||||||
updateCheck({id: check.id, description})
|
try {
|
||||||
|
await updateCheck({id: check.id, description})
|
||||||
|
} catch (e) {
|
||||||
|
onNotify(updateCheckFailed(e.message))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDelete = () => {
|
const onDelete = () => {
|
||||||
|
@ -76,10 +88,14 @@ const CheckCard: FunctionComponent<Props> = ({
|
||||||
onCloneCheck(check)
|
onCloneCheck(check)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onToggle = () => {
|
const onToggle = async () => {
|
||||||
const status = check.status === 'active' ? 'inactive' : 'active'
|
const status = check.status === 'active' ? 'inactive' : 'active'
|
||||||
|
|
||||||
updateCheck({id: check.id, status})
|
try {
|
||||||
|
await updateCheck({id: check.id, status})
|
||||||
|
} catch (e) {
|
||||||
|
onNotify(updateCheckFailed(e.message))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onEdit = () => {
|
const onEdit = () => {
|
||||||
|
@ -168,6 +184,7 @@ const mdtp: DispatchProps = {
|
||||||
onCreateLabel: createLabelAsync,
|
onCreateLabel: createLabelAsync,
|
||||||
onRemoveCheckLabel: deleteCheckLabel,
|
onRemoveCheckLabel: deleteCheckLabel,
|
||||||
onCloneCheck: cloneCheck,
|
onCloneCheck: cloneCheck,
|
||||||
|
onNotify: notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mstp = ({labels}: AppState): StateProps => {
|
const mstp = ({labels}: AppState): StateProps => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Libraries
|
// Libraries
|
||||||
import React, {FC, MouseEvent} from 'react'
|
import React, {useState, FC, MouseEvent} from 'react'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import RenamablePageTitle from 'src/pageLayout/components/RenamablePageTitle'
|
import RenamablePageTitle from 'src/pageLayout/components/RenamablePageTitle'
|
||||||
|
@ -7,6 +7,7 @@ import {
|
||||||
SquareButton,
|
SquareButton,
|
||||||
ComponentColor,
|
ComponentColor,
|
||||||
ComponentSize,
|
ComponentSize,
|
||||||
|
ComponentStatus,
|
||||||
IconFont,
|
IconFont,
|
||||||
Page,
|
Page,
|
||||||
} from '@influxdata/clockface'
|
} from '@influxdata/clockface'
|
||||||
|
@ -15,26 +16,45 @@ import CheckAlertingButton from 'src/alerting/components/CheckAlertingButton'
|
||||||
// Constants
|
// Constants
|
||||||
import {DEFAULT_CHECK_NAME, CHECK_NAME_MAX_LENGTH} from 'src/alerting/constants'
|
import {DEFAULT_CHECK_NAME, CHECK_NAME_MAX_LENGTH} from 'src/alerting/constants'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {RemoteDataState} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string
|
name: string
|
||||||
onSetName: (name: string) => void
|
onSetName: (name: string) => void
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
onSave: () => void
|
onSave: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveButtonClass = 'veo-header--save-cell-button'
|
const saveButtonClass = 'veo-header--save-cell-button'
|
||||||
|
|
||||||
const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
||||||
const handleClickOutsideTitle = (e: MouseEvent<HTMLElement>) => {
|
const [saveStatus, setSaveStatus] = useState(RemoteDataState.NotStarted)
|
||||||
const target = e.target as HTMLButtonElement
|
|
||||||
|
|
||||||
if (!target.className.includes(saveButtonClass)) {
|
const handleSave = async () => {
|
||||||
|
if (saveStatus === RemoteDataState.Loading) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave()
|
try {
|
||||||
|
setSaveStatus(RemoteDataState.Loading)
|
||||||
|
await onSave()
|
||||||
|
} catch {
|
||||||
|
setSaveStatus(RemoteDataState.NotStarted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleClickOutsideTitle = (e: MouseEvent<HTMLElement>) => {
|
||||||
|
if ((e.target as Element).classList.contains(saveButtonClass)) {
|
||||||
|
handleSave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveButtonStatus =
|
||||||
|
saveStatus === RemoteDataState.Loading
|
||||||
|
? ComponentStatus.Loading
|
||||||
|
: ComponentStatus.Default
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="veo-header">
|
<div className="veo-header">
|
||||||
<Page.Header fullWidth={true}>
|
<Page.Header fullWidth={true}>
|
||||||
|
@ -59,6 +79,7 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
||||||
icon={IconFont.Checkmark}
|
icon={IconFont.Checkmark}
|
||||||
color={ComponentColor.Success}
|
color={ComponentColor.Success}
|
||||||
size={ComponentSize.Small}
|
size={ComponentSize.Small}
|
||||||
|
status={saveButtonStatus}
|
||||||
onClick={onSave}
|
onClick={onSave}
|
||||||
testID="save-cell--button"
|
testID="save-cell--button"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -19,6 +19,8 @@ import {
|
||||||
updateTimeMachineCheck,
|
updateTimeMachineCheck,
|
||||||
} from 'src/timeMachine/actions'
|
} from 'src/timeMachine/actions'
|
||||||
import {executeQueries} from 'src/timeMachine/actions/queries'
|
import {executeQueries} from 'src/timeMachine/actions/queries'
|
||||||
|
import {notify} from 'src/shared/actions/notifications'
|
||||||
|
import {updateCheckFailed} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {
|
import {
|
||||||
|
@ -37,6 +39,7 @@ interface DispatchProps {
|
||||||
onSetActiveTimeMachine: typeof setActiveTimeMachine
|
onSetActiveTimeMachine: typeof setActiveTimeMachine
|
||||||
onSetTimeMachineCheck: typeof setTimeMachineCheck
|
onSetTimeMachineCheck: typeof setTimeMachineCheck
|
||||||
onExecuteQueries: typeof executeQueries
|
onExecuteQueries: typeof executeQueries
|
||||||
|
onNotify: typeof notify
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
|
@ -55,6 +58,7 @@ const EditCheckEditorOverlay: FunctionComponent<Props> = ({
|
||||||
onGetCheckForTimeMachine,
|
onGetCheckForTimeMachine,
|
||||||
onUpdateTimeMachineCheck,
|
onUpdateTimeMachineCheck,
|
||||||
onSetTimeMachineCheck,
|
onSetTimeMachineCheck,
|
||||||
|
onNotify,
|
||||||
activeTimeMachineID,
|
activeTimeMachineID,
|
||||||
checkStatus,
|
checkStatus,
|
||||||
router,
|
router,
|
||||||
|
@ -80,10 +84,15 @@ const EditCheckEditorOverlay: FunctionComponent<Props> = ({
|
||||||
onSetTimeMachineCheck(RemoteDataState.NotStarted, null)
|
onSetTimeMachineCheck(RemoteDataState.NotStarted, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = async () => {
|
||||||
// todo: update view when check has own view
|
// todo: update view when check has own view
|
||||||
onUpdateCheck({...check, query})
|
try {
|
||||||
handleClose()
|
await onUpdateCheck({...check, query})
|
||||||
|
handleClose()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
onNotify(updateCheckFailed(e.message))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let loadingStatus = RemoteDataState.Loading
|
let loadingStatus = RemoteDataState.Loading
|
||||||
|
@ -144,6 +153,7 @@ const mdtp: DispatchProps = {
|
||||||
onSetActiveTimeMachine: setActiveTimeMachine,
|
onSetActiveTimeMachine: setActiveTimeMachine,
|
||||||
onGetCheckForTimeMachine: getCheckForTimeMachine,
|
onGetCheckForTimeMachine: getCheckForTimeMachine,
|
||||||
onExecuteQueries: executeQueries,
|
onExecuteQueries: executeQueries,
|
||||||
|
onNotify: notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect<StateProps, DispatchProps, {}>(
|
export default connect<StateProps, DispatchProps, {}>(
|
||||||
|
|
|
@ -9,15 +9,14 @@ import CheckEOHeader from 'src/alerting/components/CheckEOHeader'
|
||||||
import TimeMachine from 'src/timeMachine/components/TimeMachine'
|
import TimeMachine from 'src/timeMachine/components/TimeMachine'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
import {
|
import {saveCheckFromTimeMachine} from 'src/alerting/actions/checks'
|
||||||
updateCheck,
|
|
||||||
saveCheckFromTimeMachine,
|
|
||||||
} from 'src/alerting/actions/checks'
|
|
||||||
import {
|
import {
|
||||||
setActiveTimeMachine,
|
setActiveTimeMachine,
|
||||||
updateTimeMachineCheck,
|
updateTimeMachineCheck,
|
||||||
setTimeMachineCheck,
|
setTimeMachineCheck,
|
||||||
} from 'src/timeMachine/actions'
|
} from 'src/timeMachine/actions'
|
||||||
|
import {createCheckFailed} from 'src/shared/copy/notifications'
|
||||||
|
import {notify} from 'src/shared/actions/notifications'
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import {createView} from 'src/shared/utils/view'
|
import {createView} from 'src/shared/utils/view'
|
||||||
|
@ -28,11 +27,11 @@ import {Check, AppState, RemoteDataState, CheckViewProperties} from 'src/types'
|
||||||
import {DEFAULT_THRESHOLD_CHECK} from 'src/alerting/constants'
|
import {DEFAULT_THRESHOLD_CHECK} from 'src/alerting/constants'
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
updateCheck: typeof updateCheck
|
|
||||||
setTimeMachineCheck: typeof setTimeMachineCheck
|
setTimeMachineCheck: typeof setTimeMachineCheck
|
||||||
updateTimeMachineCheck: typeof updateTimeMachineCheck
|
updateTimeMachineCheck: typeof updateTimeMachineCheck
|
||||||
onSetActiveTimeMachine: typeof setActiveTimeMachine
|
onSetActiveTimeMachine: typeof setActiveTimeMachine
|
||||||
saveCheckFromTimeMachine: typeof saveCheckFromTimeMachine
|
saveCheckFromTimeMachine: typeof saveCheckFromTimeMachine
|
||||||
|
notify: typeof notify
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
|
@ -51,6 +50,7 @@ const NewCheckOverlay: FunctionComponent<Props> = ({
|
||||||
router,
|
router,
|
||||||
checkStatus,
|
checkStatus,
|
||||||
check,
|
check,
|
||||||
|
notify,
|
||||||
}) => {
|
}) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const view = createView<CheckViewProperties>('check')
|
const view = createView<CheckViewProperties>('check')
|
||||||
|
@ -72,12 +72,17 @@ const NewCheckOverlay: FunctionComponent<Props> = ({
|
||||||
router.push(`/orgs/${params.orgID}/alerting`)
|
router.push(`/orgs/${params.orgID}/alerting`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = async () => {
|
||||||
// todo: when check has own view
|
// todo: when check has own view
|
||||||
// save view as view
|
// save view as view
|
||||||
// put view.id on check.viewID
|
// put view.id on check.viewID
|
||||||
saveCheckFromTimeMachine()
|
try {
|
||||||
handleClose()
|
await saveCheckFromTimeMachine()
|
||||||
|
handleClose()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
notify(createCheckFailed(e.message))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -112,11 +117,11 @@ const mstp = (state: AppState): StateProps => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mdtp: DispatchProps = {
|
const mdtp: DispatchProps = {
|
||||||
updateCheck: updateCheck,
|
|
||||||
setTimeMachineCheck: setTimeMachineCheck,
|
setTimeMachineCheck: setTimeMachineCheck,
|
||||||
updateTimeMachineCheck: updateTimeMachineCheck,
|
updateTimeMachineCheck: updateTimeMachineCheck,
|
||||||
onSetActiveTimeMachine: setActiveTimeMachine,
|
onSetActiveTimeMachine: setActiveTimeMachine,
|
||||||
saveCheckFromTimeMachine: saveCheckFromTimeMachine,
|
saveCheckFromTimeMachine: saveCheckFromTimeMachine,
|
||||||
|
notify: notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect<StateProps, DispatchProps, {}>(
|
export default connect<StateProps, DispatchProps, {}>(
|
||||||
|
|
Loading…
Reference in New Issue