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