Skip to content

Commit

Permalink
feat(alerts): Alert goal lines + product intro (#25391)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
anirudhpillai and github-actions[bot] authored Oct 7, 2024
1 parent f529a78 commit 380abf1
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 17 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export function AlertDeletionWarning(): JSX.Element | null {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { shouldShowAlertDeletionWarning } = useValues(
insightAlertsLogic({
insightShortId: insight.short_id,
insightId: insight.id as number,
insightLogicProps: insightProps,
})
Expand Down
8 changes: 2 additions & 6 deletions frontend/src/lib/components/Alerts/alertsLogic.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { afterMount, connect, kea, path, selectors } from 'kea'
import { afterMount, kea, path, selectors } from 'kea'
import { loaders } from 'kea-loaders'
import api from 'lib/api'

import { AlertState } from '~/queries/schema'

import { alertLogic, AlertLogicProps } from './alertLogic'
import { AlertLogicProps } from './alertLogic'
import type { alertsLogicType } from './alertsLogicType'
import { AlertType } from './types'

Expand All @@ -13,10 +13,6 @@ export interface AlertsLogicProps extends AlertLogicProps {}
export const alertsLogic = kea<alertsLogicType>([
path(['lib', 'components', 'Alerts', 'alertsLogic']),

connect((props: AlertsLogicProps) => ({
values: [alertLogic(props), ['alert', 'alertLoading']],
})),

loaders({
alerts: {
__default: [] as AlertType[],
Expand Down
34 changes: 31 additions & 3 deletions frontend/src/lib/components/Alerts/insightAlertsLogic.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { actions, afterMount, connect, kea, key, listeners, path, props, reducers } from 'kea'
import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
import { loaders } from 'kea-loaders'
import api from 'lib/api'
import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic'

import { GoalLine } from '~/queries/schema'
import { getBreakdown, isInsightVizNode, isTrendsQuery } from '~/queries/utils'
import { InsightLogicProps, InsightShortId } from '~/types'
import { InsightLogicProps } from '~/types'

import type { insightAlertsLogicType } from './insightAlertsLogicType'
import { AlertType } from './types'

export interface InsightAlertsLogicProps {
insightId: number
insightShortId: InsightShortId
insightLogicProps: InsightLogicProps
}

Expand Down Expand Up @@ -60,6 +60,34 @@ export const insightAlertsLogic = kea<insightAlertsLogicType>([
],
}),

selectors({
alertThresholdLines: [
(s) => [s.alerts],
(alerts: AlertType[]): GoalLine[] =>
alerts.flatMap((alert) => {
const thresholds = []

const absoluteThreshold = alert.threshold.configuration.absoluteThreshold

if (absoluteThreshold?.upper !== undefined) {
thresholds.push({
label: `${alert.name} Upper Threshold`,
value: absoluteThreshold?.upper,
})
}

if (absoluteThreshold?.lower !== undefined) {
thresholds.push({
label: `${alert.name} Lower Threshold`,
value: absoluteThreshold?.lower,
})
}

return thresholds
}),
],
}),

listeners(({ actions, values }) => ({
deleteAlert: async ({ alertId }) => {
await api.alerts.delete(alertId)
Expand Down
26 changes: 24 additions & 2 deletions frontend/src/lib/components/Alerts/views/Alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { IconCheck } from '@posthog/icons'
import { Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { router } from 'kea-router'
import { DetectiveHog } from 'lib/components/hedgehogs'
import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
import { createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink'
import { urls } from 'scenes/urls'

import { ProductKey } from '~/types'

import { AlertState } from '../../../../queries/schema'
import { alertLogic } from '../alertLogic'
import { alertsLogic } from '../alertsLogic'
Expand All @@ -23,11 +27,10 @@ export function Alerts({ alertId }: AlertsProps): JSX.Element {
const { push } = useActions(router)
const logic = alertsLogic()
const { loadAlerts } = useActions(logic)
const { alertsSortedByState, alertsLoading } = useValues(logic)

const { alert } = useValues(alertLogic({ alertId }))

const { alertsSortedByState, alertsLoading } = useValues(logic)

const columns: LemonTableColumns<AlertType> = [
{
key: 'id',
Expand Down Expand Up @@ -101,6 +104,25 @@ export function Alerts({ alertId }: AlertsProps): JSX.Element {
// TODO: add info here to sign up for alerts early access
return (
<>
{alertsSortedByState.length === 0 && !alertsLoading && (
<ProductIntroduction
productName="Alerts"
productKey={ProductKey.ALERTS}
thingName="alert"
description="Alerts enable you to monitor your insight and notify you when certain conditions are met. Please note that alerts are in alpha and may not be fully reliable."
// TODO: update docs link when ready
// docsURL="https://posthog.com/docs/data/annotations"
isEmpty={alertsSortedByState.length === 0 && !alertsLoading}
customHog={DetectiveHog}
actionElementOverride={
<span className="italic">
To get started, visit a trends insight, expand options in the header and click 'Manage
Alerts'
</span>
}
/>
)}

{alert && (
<EditAlertModal
onClose={() => push(urls.alerts())}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { pluralize } from 'lib/utils'
import { urls } from 'scenes/urls'

import { AlertState } from '~/queries/schema'
import { InsightShortId } from '~/types'

import { insightAlertsLogic, InsightAlertsLogicProps } from '../insightAlertsLogic'
import { AlertType } from '../types'
Expand Down Expand Up @@ -58,6 +59,7 @@ export function AlertListItem({ alert, onClick }: AlertListItemProps): JSX.Eleme

interface ManageAlertsModalProps extends InsightAlertsLogicProps {
isOpen: boolean
insightShortId: InsightShortId
onClose?: () => void
}

Expand All @@ -77,7 +79,7 @@ export function ManageAlertsModal(props: ManageAlertsModalProps): JSX.Element {
<LemonModal.Content>
<div className="mb-4">
With alerts, PostHog will monitor your insight and notify you when certain conditions are met. We do
not evaluate alerts in real-time, but rather on a schedule of once every hour. Please note that
not evaluate alerts in real-time, but rather on a schedule (hourly, daily...). Please note that
alerts are in alpha and may not be fully reliable.
</div>
{alerts.length ? (
Expand Down
1 change: 0 additions & 1 deletion frontend/src/scenes/insights/InsightPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export function InsightPageHeader({ insightLogicProps }: { insightLogicProps: In
insightAlertsLogic({
insightLogicProps,
insightId: insight.id as number,
insightShortId: insight.short_id as InsightShortId,
})
)

Expand Down
31 changes: 29 additions & 2 deletions frontend/src/scenes/insights/views/LineGraph/LineGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'chartjs-adapter-dayjs-3'

import { LegendOptions } from 'chart.js'
import { DeepPartial } from 'chart.js/dist/types/utils'
import annotationPlugin, { AnnotationPluginOptions } from 'chartjs-plugin-annotation'
import ChartDataLabels from 'chartjs-plugin-datalabels'
import ChartjsPluginStacked100, { ExtendedChartData } from 'chartjs-plugin-stacked100'
import clsx from 'clsx'
Expand Down Expand Up @@ -40,7 +41,7 @@ import { ErrorBoundary } from '~/layout/ErrorBoundary'
import { themeLogic } from '~/layout/navigation-3000/themeLogic'
import { hexToRGBA, lightenDarkenColor } from '~/lib/utils'
import { groupsModel } from '~/models/groupsModel'
import { TrendsFilter } from '~/queries/schema'
import { GoalLine, TrendsFilter } from '~/queries/schema'
import { GraphDataset, GraphPoint, GraphPointPayload, GraphType } from '~/types'

let tooltipRoot: Root
Expand Down Expand Up @@ -238,6 +239,7 @@ export interface LineGraphProps {
hideYAxis?: boolean
legend?: DeepPartial<LegendOptions<ChartType>>
yAxisScaleType?: string | null
alertLines?: GoalLine[]
}

export const LineGraph = (props: LineGraphProps): JSX.Element => {
Expand Down Expand Up @@ -277,6 +279,7 @@ export function LineGraph_({
hideYAxis,
legend = { display: false },
yAxisScaleType,
alertLines = [],
}: LineGraphProps): JSX.Element {
let datasets = _datasets

Expand Down Expand Up @@ -394,6 +397,19 @@ export function LineGraph_({
}
}

const annotations = alertLines.reduce((acc, { value }, idx) => {
acc[`${idx}`] = {
type: 'line',
yMin: value,
yMax: value,
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1,
borderDash: [5, 8],
}

return acc
}, {} as AnnotationPluginOptions['annotations'])

datasets = datasets.map(processDataset)

const seriesNonZeroMax = Math.max(...datasets.flatMap((d) => d.data).filter((n) => !!n && n !== LOG_ZERO))
Expand Down Expand Up @@ -463,6 +479,7 @@ export function LineGraph_({
borderColor: 'white',
},
legend: legend,
annotation: { annotations },
tooltip: {
...tooltipOptions,
external({ tooltip }: { chart: Chart; tooltip: TooltipModel<ChartType> }) {
Expand Down Expand Up @@ -767,6 +784,7 @@ export function LineGraph_({
options.indexAxis = 'y'
}
Chart.register(ChartjsPluginStacked100)
Chart.register(annotationPlugin)
const newChart = new Chart(canvasRef.current?.getContext('2d') as ChartItem, {
type: (isBar ? GraphType.Bar : type) as ChartType,
data: { labels, datasets },
Expand All @@ -775,7 +793,16 @@ export function LineGraph_({
})
setMyLineChart(newChart)
return () => newChart.destroy()
}, [datasets, hiddenLegendIndexes, isDarkModeOn, trendsFilter, formula, showValuesOnSeries, showPercentStackView])
}, [
datasets,
hiddenLegendIndexes,
isDarkModeOn,
trendsFilter,
formula,
showValuesOnSeries,
showPercentStackView,
alertLines,
])

return (
<div className={clsx('LineGraph w-full grow relative overflow-hidden')} data-attr={dataAttr}>
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/scenes/trends/viz/ActionsLineGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ChartType, defaults, LegendOptions } from 'chart.js'
import { DeepPartial } from 'chart.js/dist/types/utils'
import { useValues } from 'kea'
import { Chart } from 'lib/Chart'
import { insightAlertsLogic } from 'lib/components/Alerts/insightAlertsLogic'
import { DateDisplay } from 'lib/components/DateDisplay'
import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo'
import { capitalizeFirstLetter, isMultiSeriesFormula } from 'lib/utils'
Expand All @@ -20,7 +21,8 @@ export function ActionsLineGraph({
showPersonsModal = true,
context,
}: ChartParams): JSX.Element | null {
const { insightProps } = useValues(insightLogic)
const { insightProps, insight } = useValues(insightLogic)

const {
indexedResults,
labelGroupType,
Expand All @@ -41,6 +43,10 @@ export function ActionsLineGraph({
yAxisScaleType,
} = useValues(trendsDataLogic(insightProps))

const { alertThresholdLines } = useValues(
insightAlertsLogic({ insightId: insight.id!, insightLogicProps: insightProps })
)

const labels =
(indexedResults.length === 2 &&
indexedResults.every((x) => x.compare) &&
Expand Down Expand Up @@ -106,6 +112,7 @@ export function ActionsLineGraph({
isArea={display === ChartDisplayType.ActionsAreaGraph}
incompletenessOffsetFromEnd={incompletenessOffsetFromEnd}
legend={legend}
alertLines={alertThresholdLines}
onClick={
!showPersonsModal || isMultiSeriesFormula(formula) || isDataWarehouseSeries
? undefined
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ type AvailableFeatureUnion = `${AvailableFeature}`
export enum ProductKey {
COHORTS = 'cohorts',
ACTIONS = 'actions',
ALERTS = 'alerts',
EXPERIMENTS = 'experiments',
FEATURE_FLAGS = 'feature_flags',
ANNOTATIONS = 'annotations',
Expand Down

0 comments on commit 380abf1

Please sign in to comment.