* ui: start redesign for live search/list
* ui: remove search field, show filters picker by default for assist
* ui: filter modal wip
* ui: filter modal wip
* ui: finish with omnisearch thing
* ui: start new dashboard redesign
* refining new card section
* ui: some "new dashboard" view improvs, fix icons fill inheritance, add ai button colors
* ui: split up search component (1.22+ tbd?), restrict filter type to own modals
* ui: mimic ant card
* ui: some changes for card creation flow, add series table to CustomMetricLineChart.tsx
* ui: more chart types, add table with filtering out series, start "compare to" thing
* ui: comparison designs
* ui: better granularity support, comparison view for bar chart
* ui: add comparison to more charts, add "metric" chart (BigNumChart.tsx)
* ui: cleanup logs
* ui: fix defualt import, fix sessheader crash, fix condition set ui
* ui: some refactoring and type coverage...
* ui: more refactoring; silence warnings for list renderers
* ui: moveing and renaming filters
* ui: add metricOf selector
* ui: check for metric type
* ui: fix crashes, add widget library table
* ui: change new series btn
* ui: restrict filterselection
* ui: fix timeseries table format
* ui: autoclose autocomplete modal
* ui: some fixes to issue filters default value, display and placeholder consistency
* ui: some dashboard issues with card selection modal and empty states
* ui: comparing for funnels, alternate column view, some refactoring to prepare for customizations...
* Style improvements in omnisearch headers
* Revert "Style improvements in omnisearch headers"
This reverts commit 89e51b0531.
* ui: show health status fetch error
* ui: table, bignum and comp for funnel, add csv export
* Omni-search improvements. (#2823)
Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
* ui: fix bad merge (git hallo?)
* ui: fix filter mapper
* rm husky
* ui: add card floater
* ui: add card floater
* ui: refactor local autocomplete input
* ui: filterout empty options
* UI improvements in New Cards (#2864)
* ui: some minor dashb improvements
* ui: metric type selector for head
* ui: change card type selector, add automapping
* ui: check chart/widget components for crashes
* ui: fix crash with table metrics
* ui: fix crashes related to metric type changes
* ui: filter category for clickmap filt
* ui: fix dash options menu, fix cr/up button
* ui: fix dash list menu propagation
* ui: hide addevent in heatmaps
* ui: fix time mapping for charts
* ui: fix exclusion component for path
* ui: fix series amount for path analysis, rm grid/list selector
* ui: fix icons in list view
* ui: fix for dlt button in widgets
* Various improvements Cards, OmniSearch and Cards Listing (#2881)
* ui: some improvements for cards list view, funnels and general filter display
* ui: longer node width for journey
* Product Analytics UI Improvements. (#2896)
* Various improvements Cards, OmniSearch and Cards Listing
* Improved cards listing page
* Various improvements in product analytics
* Charts UI improvements
---------
Co-authored-by: nick-delirium <nikita@openreplay.com>
* Live se red s2 (#2902)
* Various improvements Cards, OmniSearch and Cards Listing
* Improved cards listing page
* Various improvements in product analytics
* Charts UI improvements
* ui crash
---------
Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
* ui: fix lucide version
* ui: fix custom comparison period
* ui: fix custom comparison period
* ui: handle minor paths on frontend for path/sankey
* ui: assign icon for event types in sankey nodes
* ui: some strings changed
* ui: hide btn control for table view
* Various improvements in graphs, and analytics pages. (#2908)
* Various improvements Cards, OmniSearch and Cards Listing
* Improved cards listing page
* Various improvements in product analytics
* Charts UI improvements
* ui crash
* Chart improvements and layout toggling
* Various improvements
* Tooltips
---------
Co-authored-by: nick-delirium <nikita@openreplay.com>
* ui: fix weekday mapper for x axis on >7d range
* ui: lower default density to 35, fix table card display
* ui: filterMinorPaths -> return input data if nodes arr. is empty
* ui: use default filter for sessions, move around saved search actions, remove tags modal
* ui: fix card creator visibility in grid, fix table exporter visiblility in grid
* ui: fix some proptype warnings
* ui: change new series default expand state
* ui: save comp range in widget details
* ui: move timeseries to apache echarts
* ui: use unique id for window values
* ui: add timestamp for comp tooltip row
* ui: rename var for readability
* ui: fix comparison for 24hr
* Streamlined icons and improved echarts trends (#2920)
* Various improvements Cards, OmniSearch and Cards Listing
* Improved cards listing page
* Various improvements in product analytics
* Charts UI improvements
* ui crash
* Chart improvements and layout toggling
* Various improvements
* Tooltips
* Improved icons in cards listing page
* Update WidgetFormNew.tsx
* Sankey improvements
* Icon and text updates
Text alignment and color changes in x-ray
Icon Mapping with appropriate names and shapes
* Colors and Trend Chart Interaction updates
* ui
---------
Co-authored-by: nick-delirium <nikita@openreplay.com>
* ui: series update observe
* ui: resize chart on window
* ui: move barchart to echarts
* ui: fixing bars under comparison
* ui: fixing horizontal bar tooltip
* ui: rm unused
* ui: keep state in storage
* ui: small fixes for granularity and comparisons
* ui: fix savesearch button, fix comparison period tracking
* ui: fix funnel type selection
* ui: fixing saved search button
* ui: enable error logging, remove immutable reference
* ui: update savedsearch drop
* ui: disable button if no saved
* ui: small ui fixes
* ui: add drill to summary charts, add more options to card category picker
* ui: filter compSeries with table
* ui: swap tag_el operator and value
* ui: fix top countries
* ui: further changes for search/cards
* ui: move focus to session list on line click
* ui: fix issue filter mapper
* ui: fix alert pre-init function, fix metric list options, fix legend placement
* ui: fixes for card library
* ui: work on new sankey chart
* ui: fix metadata prefetch
* ui: moving snakey to echarts
* ui: fix funnel comparison focus
* ui: stale loader
---------
Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
395 lines
18 KiB
JavaScript
395 lines
18 KiB
JavaScript
import React, {useEffect} from 'react';
|
|
import {Form, Input, SegmentSelection, Checkbox, Icon} from 'UI';
|
|
import {alertConditions as conditions} from 'App/constants';
|
|
import stl from './alertForm.module.css';
|
|
import DropdownChips from './DropdownChips';
|
|
import {validateEmail} from 'App/validate';
|
|
import cn from 'classnames';
|
|
import {useStore} from 'App/mstore'
|
|
import {observer} from 'mobx-react-lite'
|
|
import Select from 'Shared/Select';
|
|
import {Button} from "antd";
|
|
|
|
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},
|
|
];
|
|
|
|
const changeOptions = [
|
|
{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 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="ml-10">{content}</div>
|
|
</div>
|
|
);
|
|
|
|
function AlertForm(props) {
|
|
const {
|
|
slackChannels,
|
|
msTeamsChannels,
|
|
webhooks,
|
|
onDelete,
|
|
style = {height: "calc('100vh - 40px')"},
|
|
} = props;
|
|
const {alertsStore, metricStore} = useStore()
|
|
const {
|
|
triggerOptions: allTriggerSeries,
|
|
loading,
|
|
} = alertsStore
|
|
|
|
const triggerOptions = metricStore.instance.series.length > 0 ? allTriggerSeries.filter(s => {
|
|
return metricStore.instance.series.findIndex(ms => ms.seriesId === s.value) !== -1
|
|
}).map(v => {
|
|
const labelArr = v.label.split('.')
|
|
labelArr.shift()
|
|
return {
|
|
...v,
|
|
label: labelArr.join('.')
|
|
}
|
|
}) : allTriggerSeries
|
|
const instance = alertsStore.instance
|
|
const deleting = loading
|
|
|
|
const write = ({target: {value, name}}) => alertsStore.edit({[name]: value});
|
|
const writeOption = (e, {name, value}) => alertsStore.edit({[name]: value.value});
|
|
const onChangeCheck = ({target: {checked, name}}) => alertsStore.edit({[name]: checked});
|
|
|
|
useEffect(() => {
|
|
void alertsStore.fetchTriggerOptions();
|
|
}, []);
|
|
|
|
const writeQueryOption = (e, {name, value}) => {
|
|
const {query} = instance;
|
|
alertsStore.edit({query: {...query, [name]: value}});
|
|
};
|
|
|
|
const writeQuery = ({target: {value, name}}) => {
|
|
const {query} = instance;
|
|
alertsStore.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';
|
|
|
|
return (
|
|
<Form
|
|
className={cn('pb-10', stl.wrapper)}
|
|
style={style}
|
|
onSubmit={() => props.onSubmit(instance)}
|
|
id="alert-form"
|
|
>
|
|
<div className={cn('-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}) => alertsStore.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="msteams"
|
|
className="mr-8"
|
|
type="checkbox"
|
|
checked={instance.msteams}
|
|
onClick={onChangeCheck}
|
|
label="MS Teams"
|
|
/>
|
|
<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) => alertsStore.edit({slackInput: selected})}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{instance.msteams && (
|
|
<div className="flex items-start my-4">
|
|
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'MS Teams'}</label>
|
|
<div className="w-4/6">
|
|
<DropdownChips
|
|
fluid
|
|
selected={instance.msteamsInput}
|
|
options={msTeamsChannels}
|
|
placeholder="Select Channel"
|
|
onChange={(selected) => alertsStore.edit({msteamsInput: 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) => alertsStore.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) => alertsStore.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}
|
|
type="primary"
|
|
htmlType="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
|
|
primary="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 observer(AlertForm);
|