change(ui) - alert no content message and other changes

This commit is contained in:
Shekar Siri 2022-08-11 12:10:07 +02:00
parent c1bbeb61c0
commit 28e1492eb8
6 changed files with 524 additions and 522 deletions

View file

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React, { useEffect } from 'react';
import { Button, Form, Input, SegmentSelection, Checkbox, Message, Link, Icon } from 'UI';
import { alertMetrics as metrics } from 'App/constants';
import { alertConditions as conditions } from 'App/constants';
@ -9,333 +9,322 @@ import DropdownChips from './DropdownChips';
import { validateEmail } from 'App/validate';
import cn from 'classnames';
import { fetchTriggerOptions } from 'Duck/alerts';
import Select from 'Shared/Select'
import Select from 'Shared/Select';
const thresholdOptions = [
{ label: '15 minutes', value: 15 },
{ label: '30 minutes', value: 30 },
{ label: '1 hour', value: 60 },
{ label: '2 hours', value: 120 },
{ label: '4 hours', value: 240 },
{ label: '1 day', value: 1440 },
{ label: '15 minutes', value: 15 },
{ label: '30 minutes', value: 30 },
{ label: '1 hour', value: 60 },
{ label: '2 hours', value: 120 },
{ label: '4 hours', value: 240 },
{ label: '1 day', value: 1440 },
];
const changeOptions = [
{ label: 'change', value: 'change' },
{ label: '% change', value: 'percent' },
{ label: 'change', value: 'change' },
{ label: '% change', value: 'percent' },
];
const Circle = ({ text }) => (
<div className="circle mr-4 w-6 h-6 rounded-full bg-gray-light flex items-center justify-center">{text}</div>
)
const Circle = ({ text }) => <div className="circle mr-4 w-6 h-6 rounded-full bg-gray-light flex items-center justify-center">{text}</div>;
const Section = ({ index, title, description, content }) => (
<div className="w-full">
<div className="flex items-start">
<Circle text={index} />
<div>
<span className="font-medium">{title}</span>
{ description && <div className="text-sm color-gray-medium">{description}</div>}
</div>
</div>
<div className="w-full">
<div className="flex items-start">
<Circle text={index} />
<div>
<span className="font-medium">{title}</span>
{description && <div className="text-sm color-gray-medium">{description}</div>}
</div>
</div>
<div className="ml-10">
{content}
<div className="ml-10">{content}</div>
</div>
</div>
)
);
const integrationsRoute = client(CLIENT_TABS.INTEGRATIONS);
const AlertForm = props => {
const { instance, slackChannels, webhooks, loading, onDelete, deleting, triggerOptions, metricId, style={ width: '580px', height: '100vh' } } = props;
const write = ({ target: { value, name } }) => props.edit({ [ name ]: value })
const writeOption = (e, { name, value }) => props.edit({ [ name ]: value.value });
const onChangeCheck = ({ target: { checked, name }}) => props.edit({ [ name ]: checked })
// const onChangeOption = ({ checked, name }) => props.edit({ [ name ]: checked })
// const onChangeCheck = (e) => { console.log(e) }
const AlertForm = (props) => {
const {
instance,
slackChannels,
webhooks,
loading,
onDelete,
deleting,
triggerOptions,
metricId,
style = { width: '580px', height: '100vh' },
} = props;
const write = ({ target: { value, name } }) => props.edit({ [name]: value });
const writeOption = (e, { name, value }) => props.edit({ [name]: value.value });
const onChangeCheck = ({ target: { checked, name } }) => props.edit({ [name]: checked });
// const onChangeOption = ({ checked, name }) => props.edit({ [ name ]: checked })
// const onChangeCheck = (e) => { console.log(e) }
useEffect(() => {
props.fetchTriggerOptions();
}, [])
useEffect(() => {
props.fetchTriggerOptions();
}, []);
const writeQueryOption = (e, { name, value }) => {
const { query } = instance;
props.edit({ query: { ...query, [name] : value } });
}
const writeQueryOption = (e, { name, value }) => {
const { query } = instance;
props.edit({ query: { ...query, [name]: value } });
};
const writeQuery = ({ target: { value, name } }) => {
const { query } = instance;
props.edit({ query: { ...query, [name] : value } });
}
const writeQuery = ({ target: { value, name } }) => {
const { query } = instance;
props.edit({ query: { ...query, [name]: value } });
};
const metric = (instance && instance.query.left) ? triggerOptions.find(i => i.value === instance.query.left) : null;
const unit = metric ? metric.unit : '';
const isThreshold = instance.detectionMethod === 'threshold';
const metric = instance && instance.query.left ? triggerOptions.find((i) => i.value === instance.query.left) : null;
const unit = metric ? metric.unit : '';
const isThreshold = instance.detectionMethod === 'threshold';
return (
<Form className={ cn("p-6 pb-10", stl.wrapper)} style={style} onSubmit={() => props.onSubmit(instance)} id="alert-form">
<div className={cn(stl.content, '-mx-6 px-6 pb-12')}>
<input
autoFocus={ true }
className="text-lg border border-gray-light rounded w-full"
name="name"
style={{ fontSize: '18px', padding: '10px', fontWeight: '600'}}
value={ instance && instance.name }
onChange={ write }
placeholder="New Alert"
id="name-field"
/>
<div className="mb-8" />
<Section
index="1"
title={'What kind of alert do you want to set?'}
content={
<div>
<SegmentSelection
primary
name="detectionMethod"
className="my-3"
onSelect={ (e, { name, value }) => props.edit({ [ name ]: value }) }
value={{ value: instance.detectionMethod }}
list={ [
{ name: 'Threshold', value: 'threshold' },
{ name: 'Change', value: 'change' },
]}
/>
<div className="text-sm color-gray-medium">
{isThreshold && 'Eg. Alert me if memory.avg is greater than 500mb over the past 4 hours.'}
{!isThreshold && 'Eg. Alert me if % change of memory.avg is greater than 10% over the past 4 hours compared to the previous 4 hours.'}
</div>
<div className="my-4" />
return (
<Form className={cn('p-6 pb-10', stl.wrapper)} style={style} onSubmit={() => props.onSubmit(instance)} id="alert-form">
<div className={cn(stl.content, '-mx-6 px-6 pb-12')}>
<input
autoFocus={true}
className="text-lg border border-gray-light rounded w-full"
name="name"
style={{ fontSize: '18px', padding: '10px', fontWeight: '600' }}
value={instance && instance.name}
onChange={write}
placeholder="Untiltled Alert"
id="name-field"
/>
<div className="mb-8" />
<Section
index="1"
title={'What kind of alert do you want to set?'}
content={
<div>
<SegmentSelection
primary
name="detectionMethod"
className="my-3"
onSelect={(e, { name, value }) => props.edit({ [name]: value })}
value={{ value: instance.detectionMethod }}
list={[
{ name: 'Threshold', value: 'threshold' },
{ name: 'Change', value: 'change' },
]}
/>
<div className="text-sm color-gray-medium">
{isThreshold && 'Eg. Alert me if memory.avg is greater than 500mb over the past 4 hours.'}
{!isThreshold &&
'Eg. Alert me if % change of memory.avg is greater than 10% over the past 4 hours compared to the previous 4 hours.'}
</div>
<div className="my-4" />
</div>
}
/>
<hr className="my-8" />
<Section
index="2"
title="Condition"
content={
<div>
{!isThreshold && (
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'Trigger when'}</label>
<Select
className="w-4/6"
placeholder="change"
options={changeOptions}
name="change"
defaultValue={instance.change}
onChange={({ value }) => writeOption(null, { name: 'change', value })}
id="change-dropdown"
/>
</div>
)}
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{isThreshold ? 'Trigger when' : 'of'}</label>
<Select
className="w-4/6"
placeholder="Select Metric"
isSearchable={true}
options={triggerOptions}
name="left"
value={triggerOptions.find((i) => i.value === instance.query.left)}
// onChange={ writeQueryOption }
onChange={({ value }) => writeQueryOption(null, { name: 'left', value: value.value })}
/>
</div>
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'is'}</label>
<div className="w-4/6 flex items-center">
<Select
placeholder="Select Condition"
options={conditions}
name="operator"
defaultValue={instance.query.operator}
// onChange={ writeQueryOption }
onChange={({ value }) => writeQueryOption(null, { name: 'operator', value: value.value })}
/>
{unit && (
<>
<Input
className="px-4"
style={{ marginRight: '31px' }}
// label={{ basic: true, content: unit }}
// labelPosition='right'
name="right"
value={instance.query.right}
onChange={writeQuery}
placeholder="E.g. 3"
/>
<span className="ml-2">{'test'}</span>
</>
)}
{!unit && (
<Input
wrapperClassName="ml-2"
// className="pl-4"
name="right"
value={instance.query.right}
onChange={writeQuery}
placeholder="Specify Value"
/>
)}
</div>
</div>
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'over the past'}</label>
<Select
className="w-2/6"
placeholder="Select timeframe"
options={thresholdOptions}
name="currentPeriod"
defaultValue={instance.currentPeriod}
// onChange={ writeOption }
onChange={({ value }) => writeOption(null, { name: 'currentPeriod', value })}
/>
</div>
{!isThreshold && (
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'compared to previous'}</label>
<Select
className="w-2/6"
placeholder="Select timeframe"
options={thresholdOptions}
name="previousPeriod"
defaultValue={instance.previousPeriod}
// onChange={ writeOption }
onChange={({ value }) => writeOption(null, { name: 'previousPeriod', value })}
/>
</div>
)}
</div>
}
/>
<hr className="my-8" />
<Section
index="3"
title="Notify Through"
description="You'll be noticed in app notifications. Additionally opt in to receive alerts on:"
content={
<div className="flex flex-col">
<div className="flex items-center my-4">
<Checkbox
name="slack"
className="mr-8"
type="checkbox"
checked={instance.slack}
onClick={onChangeCheck}
label="Slack"
/>
<Checkbox
name="email"
type="checkbox"
checked={instance.email}
onClick={onChangeCheck}
className="mr-8"
label="Email"
/>
<Checkbox name="webhook" type="checkbox" checked={instance.webhook} onClick={onChangeCheck} label="Webhook" />
</div>
{instance.slack && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'Slack'}</label>
<div className="w-4/6">
<DropdownChips
fluid
selected={instance.slackInput}
options={slackChannels}
placeholder="Select Channel"
onChange={(selected) => props.edit({ slackInput: selected })}
/>
</div>
</div>
)}
{instance.email && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'Email'}</label>
<div className="w-4/6">
<DropdownChips
textFiled
validate={validateEmail}
selected={instance.emailInput}
placeholder="Type and press Enter key"
onChange={(selected) => props.edit({ emailInput: selected })}
/>
</div>
</div>
)}
{instance.webhook && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'Webhook'}</label>
<DropdownChips
fluid
selected={instance.webhookInput}
options={webhooks}
placeholder="Select Webhook"
onChange={(selected) => props.edit({ webhookInput: selected })}
/>
</div>
)}
</div>
}
/>
</div>
}
/>
<hr className="my-8" />
<Section
index="2"
title="Condition"
content={
<div>
{!isThreshold && (
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'Trigger when'}</label>
<Select
className="w-4/6"
placeholder="change"
options={ changeOptions }
name="change"
defaultValue={ instance.change }
onChange={ ({ value }) => writeOption(null , { name: 'change', value }) }
id="change-dropdown"
/>
<div className="flex items-center justify-between absolute bottom-0 left-0 right-0 p-6 border-t z-10 bg-white">
<div className="flex items-center">
<Button loading={loading} variant="primary" type="submit" disabled={loading || !instance.validate()} id="submit-button">
{instance.exists() ? 'Update' : 'Create'}
</Button>
<div className="mx-1" />
<Button onClick={props.onClose}>Cancel</Button>
</div>
)}
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{isThreshold ? 'Trigger when' : 'of'}</label>
<Select
className="w-4/6"
placeholder="Select Metric"
isSearchable={true}
options={ triggerOptions }
name="left"
value={ triggerOptions.find(i => i.value === instance.query.left) }
// onChange={ writeQueryOption }
onChange={ ({ value }) => writeQueryOption(null, { name: 'left', value: value.value }) }
/>
</div>
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'is'}</label>
<div className="w-4/6 flex items-center">
<Select
placeholder="Select Condition"
options={ conditions }
name="operator"
defaultValue={ instance.query.operator }
// onChange={ writeQueryOption }
onChange={ ({ value }) => writeQueryOption(null, { name: 'operator', value: value.value }) }
/>
{ unit && (
<>
<Input
className="px-4"
style={{ marginRight: '31px'}}
// label={{ basic: true, content: unit }}
// labelPosition='right'
name="right"
value={ instance.query.right }
onChange={ writeQuery }
placeholder="E.g. 3"
/>
<span className="ml-2">{'test'}</span>
</>
)}
{ !unit && (
<Input
wrapperClassName="ml-2"
// className="pl-4"
name="right"
value={ instance.query.right }
onChange={ writeQuery }
placeholder="Specify Value"
/>
)}
<div>
{instance.exists() && (
<Button hover variant="text" loading={deleting} type="button" onClick={() => onDelete(instance)} id="trash-button">
<Icon name="trash" color="gray-medium" size="18" />
</Button>
)}
</div>
</div>
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'over the past'}</label>
<Select
className="w-2/6"
placeholder="Select timeframe"
options={ thresholdOptions }
name="currentPeriod"
defaultValue={ instance.currentPeriod }
// onChange={ writeOption }
onChange={ ({ value }) => writeOption(null, { name: 'currentPeriod', value }) }
/>
</div>
{!isThreshold && (
<div className="flex items-center my-3">
<label className="w-2/6 flex-shrink-0 font-normal">{'compared to previous'}</label>
<Select
className="w-2/6"
placeholder="Select timeframe"
options={ thresholdOptions }
name="previousPeriod"
defaultValue={ instance.previousPeriod }
// onChange={ writeOption }
onChange={ ({ value }) => writeOption(null, { name: 'previousPeriod', value }) }
/>
</div>
)}
</div>
}
/>
</Form>
);
};
<hr className="my-8" />
<Section
index="3"
title="Notify Through"
description="You'll be noticed in app notifications. Additionally opt in to receive alerts on:"
content={
<div className="flex flex-col">
<div className="flex items-center my-4">
<Checkbox
name="slack"
className="mr-8"
type="checkbox"
checked={ instance.slack }
onClick={ onChangeCheck }
label="Slack"
/>
<Checkbox
name="email"
type="checkbox"
checked={ instance.email }
onClick={ onChangeCheck }
className="mr-8"
label="Email"
/>
<Checkbox
name="webhook"
type="checkbox"
checked={ instance.webhook }
onClick={ onChangeCheck }
label="Webhook"
/>
</div>
{ instance.slack && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'Slack'}</label>
<div className="w-4/6">
<DropdownChips
fluid
selected={instance.slackInput}
options={slackChannels}
placeholder="Select Channel"
onChange={(selected) => props.edit({ 'slackInput': selected })}
/>
</div>
</div>
)}
{instance.email && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'Email'}</label>
<div className="w-4/6">
<DropdownChips
textFiled
validate={validateEmail}
selected={instance.emailInput}
placeholder="Type and press Enter key"
onChange={(selected) => props.edit({ 'emailInput': selected })}
/>
</div>
</div>
)}
{instance.webhook && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'Webhook'}</label>
<DropdownChips
fluid
selected={instance.webhookInput}
options={webhooks}
placeholder="Select Webhook"
onChange={(selected) => props.edit({ 'webhookInput': selected })}
/>
</div>
)}
</div>
}
/>
</div>
<div className="flex items-center justify-between absolute bottom-0 left-0 right-0 p-6 border-t z-10 bg-white">
<div className="flex items-center">
<Button
loading={loading}
variant="primary"
type="submit"
disabled={loading || !instance.validate()}
id="submit-button"
>
{instance.exists() ? 'Update' : 'Create'}
</Button>
<div className="mx-1" />
<Button onClick={props.onClose}>Cancel</Button>
</div>
<div>
{instance.exists() && (
<Button
hover
variant="text"
loading={deleting}
type="button"
onClick={() => onDelete(instance)}
id="trash-button"
>
<Icon name="trash" color="gray-medium" size="18" />
</Button>
)}
</div>
</div>
</Form>
)
}
export default connect(state => ({
instance: state.getIn(['alerts', 'instance']),
triggerOptions: state.getIn(['alerts', 'triggerOptions']),
loading: state.getIn(['alerts', 'saveRequest', 'loading']),
deleting: state.getIn(['alerts', 'removeRequest', 'loading'])
}), { fetchTriggerOptions })(AlertForm)
export default connect(
(state) => ({
instance: state.getIn(['alerts', 'instance']),
triggerOptions: state.getIn(['alerts', 'triggerOptions']),
loading: state.getIn(['alerts', 'saveRequest', 'loading']),
deleting: state.getIn(['alerts', 'removeRequest', 'loading']),
}),
{ fetchTriggerOptions }
)(AlertForm);

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState } from 'react';
import { SlideModal, IconButton } from 'UI';
import { init, edit, save, remove } from 'Duck/alerts';
import { fetchList as fetchWebhooks } from 'Duck/webhook';
@ -9,93 +9,98 @@ import { EMAIL, SLACK, WEBHOOK } from 'App/constants/schedule';
import { confirm } from 'UI';
interface Props {
showModal?: boolean;
metricId?: number;
onClose?: () => void;
webhooks: any;
fetchWebhooks: Function;
save: Function;
remove: Function;
init: Function;
edit: Function;
showModal?: boolean;
metricId?: number;
onClose?: () => void;
webhooks: any;
fetchWebhooks: Function;
save: Function;
remove: Function;
init: Function;
edit: Function;
}
function AlertFormModal(props: Props) {
const { metricId = null, showModal = false, webhooks } = props;
const [showForm, setShowForm] = useState(false);
const { metricId = null, showModal = false, webhooks } = props;
const [showForm, setShowForm] = useState(false);
useEffect(() => {
props.fetchWebhooks();
}, [])
useEffect(() => {
props.fetchWebhooks();
}, []);
const slackChannels = webhooks.filter(hook => hook.type === SLACK).map(({ webhookId, name }) => ({ value: webhookId, text: name })).toJS();
const hooks = webhooks.filter(hook => hook.type === WEBHOOK).map(({ webhookId, name }) => ({ value: webhookId, text: name })).toJS();
const slackChannels = webhooks
.filter((hook) => hook.type === SLACK)
.map(({ webhookId, name }) => ({ value: webhookId, text: name }))
.toJS();
const hooks = webhooks
.filter((hook) => hook.type === WEBHOOK)
.map(({ webhookId, name }) => ({ value: webhookId, text: name }))
.toJS();
const saveAlert = instance => {
const wasUpdating = instance.exists();
props.save(instance).then(() => {
if (!wasUpdating) {
toggleForm(null, false);
}
if (props.onClose) {
props.onClose();
}
})
}
const saveAlert = (instance) => {
const wasUpdating = instance.exists();
props.save(instance).then(() => {
if (!wasUpdating) {
toggleForm(null, false);
}
if (props.onClose) {
props.onClose();
}
});
};
const onDelete = async (instance) => {
if (await confirm({
header: 'Confirm',
confirmButton: 'Yes, delete',
confirmation: `Are you sure you want to permanently delete this alert?`
})) {
props.remove(instance.alertId).then(() => {
toggleForm(null, false);
});
}
}
const toggleForm = (instance, state) => {
if (instance) {
props.init(instance)
}
return setShowForm(state ? state : !showForm);
}
return (
<SlideModal
title={
<div className="flex items-center">
<span className="mr-3">{ 'Create Alert' }</span>
{/* <IconButton
circle
size="small"
icon="plus"
outline
id="add-button"
onClick={ () => toggleForm({}, true) }
/> */}
</div>
const onDelete = async (instance) => {
if (
await confirm({
header: 'Confirm',
confirmButton: 'Yes, delete',
confirmation: `Are you sure you want to permanently delete this alert?`,
})
) {
props.remove(instance.alertId).then(() => {
toggleForm(null, false);
});
}
isDisplayed={ showModal }
onClose={props.onClose}
size="medium"
content={ showModal &&
<AlertForm
metricId={ metricId }
edit={props.edit}
slackChannels={slackChannels}
webhooks={hooks}
onSubmit={saveAlert}
};
const toggleForm = (instance, state) => {
if (instance) {
props.init(instance);
}
return setShowForm(state ? state : !showForm);
};
return (
<SlideModal
title={
<div className="flex items-center">
<span className="mr-3">{'Create Alert'}</span>
</div>
}
isDisplayed={showModal}
onClose={props.onClose}
onDelete={onDelete}
style={{ width: '580px', height: '100vh - 200px' }}
/>
}
/>
);
size="medium"
content={
showModal && (
<AlertForm
metricId={metricId}
edit={props.edit}
slackChannels={slackChannels}
webhooks={hooks}
onSubmit={saveAlert}
onClose={props.onClose}
onDelete={onDelete}
style={{ width: '580px', height: '100vh - 200px' }}
/>
)
}
/>
);
}
export default connect(state => ({
webhooks: state.getIn(['webhooks', 'list']),
instance: state.getIn(['alerts', 'instance']),
}), { init, edit, save, remove, fetchWebhooks, setShowAlerts })(AlertFormModal)
export default connect(
(state) => ({
webhooks: state.getIn(['webhooks', 'list']),
instance: state.getIn(['alerts', 'instance']),
}),
{ init, edit, save, remove, fetchWebhooks, setShowAlerts }
)(AlertFormModal);

View file

@ -10,95 +10,100 @@ import { setShowAlerts } from 'Duck/dashboard';
import { EMAIL, SLACK, WEBHOOK } from 'App/constants/schedule';
import { confirm } from 'UI';
const Alerts = props => {
const { webhooks, setShowAlerts } = props;
const [showForm, setShowForm] = useState(false);
const Alerts = (props) => {
const { webhooks, setShowAlerts } = props;
const [showForm, setShowForm] = useState(false);
useEffect(() => {
props.fetchWebhooks();
}, [])
useEffect(() => {
props.fetchWebhooks();
}, []);
const slackChannels = webhooks.filter(hook => hook.type === SLACK).map(({ webhookId, name }) => ({ value: webhookId, label: name })).toJS();
const hooks = webhooks.filter(hook => hook.type === WEBHOOK).map(({ webhookId, name }) => ({ value: webhookId, label: name })).toJS();
const slackChannels = webhooks
.filter((hook) => hook.type === SLACK)
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
.toJS();
const hooks = webhooks
.filter((hook) => hook.type === WEBHOOK)
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
.toJS();
const saveAlert = instance => {
const wasUpdating = instance.exists();
props.save(instance).then(() => {
if (!wasUpdating) {
toast.success('New alert saved')
toggleForm(null, false);
} else {
toast.success('Alert updated')
}
})
}
const saveAlert = (instance) => {
const wasUpdating = instance.exists();
props.save(instance).then(() => {
if (!wasUpdating) {
toast.success('New alert saved');
toggleForm(null, false);
} else {
toast.success('Alert updated');
}
});
};
const onDelete = async (instance) => {
if (await confirm({
header: 'Confirm',
confirmButton: 'Yes, delete',
confirmation: `Are you sure you want to permanently delete this alert?`
})) {
props.remove(instance.alertId).then(() => {
toggleForm(null, false);
});
}
}
const onDelete = async (instance) => {
if (
await confirm({
header: 'Confirm',
confirmButton: 'Yes, delete',
confirmation: `Are you sure you want to permanently delete this alert?`,
})
) {
props.remove(instance.alertId).then(() => {
toggleForm(null, false);
});
}
};
const toggleForm = (instance, state) => {
if (instance) {
props.init(instance)
}
return setShowForm(state ? state : !showForm);
}
const toggleForm = (instance, state) => {
if (instance) {
props.init(instance);
}
return setShowForm(state ? state : !showForm);
};
return (
<div>
<SlideModal
title={
<div className="flex items-center">
<span className="mr-3">{ 'Alerts' }</span>
<IconButton
circle
size="small"
icon="plus"
outline
id="add-button"
onClick={ () => toggleForm({}, true) }
return (
<div>
<SlideModal
title={
<div className="flex items-center">
<span className="mr-3">{'Alerts'}</span>
<IconButton circle size="small" icon="plus" outline id="add-button" onClick={() => toggleForm({}, true)} />
</div>
}
isDisplayed={true}
onClose={() => {
toggleForm({}, false);
setShowAlerts(false);
}}
size="small"
content={
<AlertsList
onEdit={(alert) => {
toggleForm(alert, true);
}}
onClickCreate={() => toggleForm({}, true)}
/>
}
detailContent={
showForm && (
<AlertForm
edit={props.edit}
slackChannels={slackChannels}
webhooks={hooks}
onSubmit={saveAlert}
onClose={() => toggleForm({}, false)}
onDelete={onDelete}
/>
)
}
/>
</div>
}
isDisplayed={ true }
onClose={ () => {
toggleForm({}, false);
setShowAlerts(false);
} }
size="small"
content={
<AlertsList
onEdit={alert => {
toggleForm(alert, true)
}}
/>
}
detailContent={
showForm && (
<AlertForm
edit={props.edit}
slackChannels={slackChannels}
webhooks={hooks}
onSubmit={saveAlert}
onClose={ () => toggleForm({}, false) }
onDelete={onDelete}
/>
)
}
/>
</div>
)
}
</div>
);
};
export default connect(state => ({
webhooks: state.getIn(['webhooks', 'list']),
instance: state.getIn(['alerts', 'instance']),
}), { init, edit, save, remove, fetchWebhooks, setShowAlerts })(Alerts)
export default connect(
(state) => ({
webhooks: state.getIn(['webhooks', 'list']),
instance: state.getIn(['alerts', 'instance']),
}),
{ init, edit, save, remove, fetchWebhooks, setShowAlerts }
)(Alerts);

View file

@ -1,55 +1,58 @@
import React, { useEffect, useState } from 'react'
import { Loader, NoContent, Input } from 'UI';
import AlertItem from './AlertItem'
import React, { useEffect, useState } from 'react';
import { Loader, NoContent, Input, Button } from 'UI';
import AlertItem from './AlertItem';
import { fetchList, init } from 'Duck/alerts';
import { connect } from 'react-redux';
import { getRE } from 'App/utils';
const AlertsList = props => {
const { loading, list, instance, onEdit } = props;
const [query, setQuery] = useState('')
useEffect(() => {
props.fetchList()
}, [])
const AlertsList = (props) => {
const { loading, list, instance, onEdit } = props;
const [query, setQuery] = useState('');
const filterRE = getRE(query, 'i');
const _filteredList = list.filter(({ name, query: { left } }) => filterRE.test(name) || filterRE.test(left));
useEffect(() => {
props.fetchList();
}, []);
return (
<div>
<div className="mb-3 w-full px-3">
<Input
name="searchQuery"
placeholder="Search by Name or Metric"
onChange={({ target: { value } }) => setQuery(value)}
/>
</div>
<Loader loading={ loading }>
<NoContent
title="No data available."
size="small"
show={ list.size === 0 }
>
<div className="bg-white">
{_filteredList.map(a => (
<div className="border-b" key={a.key}>
<AlertItem
active={instance.alertId === a.alertId}
alert={a}
onEdit={() => onEdit(a.toData())}
/>
</div>
))}
</div>
</NoContent>
</Loader>
</div>
)
}
const filterRE = getRE(query, 'i');
const _filteredList = list.filter(({ name, query: { left } }) => filterRE.test(name) || filterRE.test(left));
export default connect(state => ({
list: state.getIn(['alerts', 'list']).sort((a, b ) => b.createdAt - a.createdAt),
instance: state.getIn(['alerts', 'instance']),
loading: state.getIn(['alerts', 'loading'])
}), { fetchList, init })(AlertsList)
return (
<div>
<div className="mb-3 w-full px-3">
<Input name="searchQuery" placeholder="Search by Name or Metric" onChange={({ target: { value } }) => setQuery(value)} />
</div>
<Loader loading={loading}>
<NoContent
title="No alerts have been setup yet."
subtext={
<div className="flex flex-col items-center">
<div>Alerts helps your team stay up to date with the activity on your app.</div>
<Button variant="primary" className="mt-4" icon="plus" onClick={props.onClickCreate}>
Create
</Button>
</div>
}
size="small"
show={list.size === 0}
>
<div className="bg-white">
{_filteredList.map((a) => (
<div className="border-b" key={a.key}>
<AlertItem active={instance.alertId === a.alertId} alert={a} onEdit={() => onEdit(a.toData())} />
</div>
))}
</div>
</NoContent>
</Loader>
</div>
);
};
export default connect(
(state) => ({
list: state.getIn(['alerts', 'list']).sort((a, b) => b.createdAt - a.createdAt),
instance: state.getIn(['alerts', 'instance']),
loading: state.getIn(['alerts', 'loading']),
}),
{ fetchList, init }
)(AlertsList);

View file

@ -12,7 +12,7 @@
transition: all 0.2s;
padding: 40px;
&.small {
/* &.small {
& .title {
font-size: 20px !important;
}
@ -20,7 +20,7 @@
& .subtext {
font-size: 16px;
}
}
} */
}
.title {

View file

@ -12,7 +12,7 @@ conditions.forEach(c => { conditionsMap[c.value] = c });
export default Record({
alertId: '',
projectId: undefined,
name: 'New Alert',
name: 'Untitled Alert',
description: '',
active: true,
currentPeriod: 15,