"use client";
import {
ActionBlock,
AddActionPanel,
AlertRuleSourceFields,
AlertRuleTriggerFields
} from "@app/components/alert-rule-editor/AlertRuleFields";
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
import { SettingsContainer } from "@app/components/Settings";
import { SwitchInput } from "@app/components/SwitchInput";
import { Button } from "@app/components/ui/button";
import { Card, CardContent } from "@app/components/ui/card";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage
} from "@app/components/ui/form";
import { Input } from "@app/components/ui/input";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { toast } from "@app/hooks/useToast";
import {
buildFormSchema,
defaultFormValues,
formValuesToApiPayload,
type AlertRuleFormValues
} from "@app/lib/alertRuleForm";
import { createApiClient, formatAxiosError } from "@app/lib/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
import type { CreateAlertRuleResponse } from "@server/routers/alertRule/types";
import type { AxiosResponse } from "axios";
import { Cog, Flag, Zap, ZapIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { useActionState, useMemo, useTransition, type ReactNode } from "react";
import { useFieldArray, useForm, type Resolver } from "react-hook-form";
import { Badge } from "../ui/badge";
const FORM_ID = "alert-rule-form";
type StepAccent = {
labelClass: string;
icon: typeof Flag;
};
type AlertRuleGraphEditorProps = {
orgId: string;
alertRuleId?: number;
initialValues: AlertRuleFormValues;
isNew: boolean;
disabled?: boolean;
};
function VerticalRuleStep({
stepNumber,
isLast,
title,
accent,
children
}: {
stepNumber: number;
isLast: boolean;
title: string;
accent: StepAccent;
children: ReactNode;
}) {
const Icon = accent.icon;
return (
{stepNumber}
{!isLast && (
)}
);
}
export default function AlertRuleGraphEditor({
orgId,
alertRuleId,
initialValues,
isNew,
disabled = false
}: AlertRuleGraphEditorProps) {
const t = useTranslations();
const router = useRouter();
const api = createApiClient(useEnvContext());
const schema = useMemo(() => buildFormSchema(t), [t]);
const form = useForm({
resolver: zodResolver(schema) as Resolver,
defaultValues: initialValues ?? defaultFormValues()
});
const { fields, append, remove, update } = useFieldArray({
control: form.control,
name: "actions"
});
const saveAlert = async () => {
const isValid = await form.trigger();
if (!isValid) {
const values = form.getValues();
if (values.actions.length === 0) {
toast({
variant: "warning",
title: t("alertingNoActionsTitle"),
description: t("alertingNoActionsSaveDescription")
});
}
return;
}
const values = form.getValues();
try {
const payload = formValuesToApiPayload(values);
if (isNew) {
const res = await api.put<
AxiosResponse
>(`/org/${orgId}/alert-rule`, payload);
toast({
title: t("alertingRuleSaved"),
description: t("alertingRuleSavedCreatedDescription")
});
router.replace(
`/${orgId}/settings/alerting/${res.data.data.alertRuleId}`
);
} else {
await api.post(
`/org/${orgId}/alert-rule/${alertRuleId}`,
payload
);
toast({
title: t("alertingRuleSaved"),
description: t("alertingRuleSavedUpdatedDescription")
});
}
} catch (e) {
toast({
title: t("error"),
description: formatAxiosError(e),
variant: "destructive"
});
}
};
const testAlert = async () => {
const isValid = await form.trigger("actions");
const values = form.getValues();
if (!isValid) {
if (values.actions.length === 0) {
toast({
variant: "warning",
title: t("alertingNoActionsTitle"),
description: t("alertingNoActionsTestDescription")
});
}
return;
}
try {
const payload = formValuesToApiPayload(values);
await api.post(`/org/${orgId}/test-alert-rule`, payload);
toast({
title: t("alertingTestAlertSent"),
description: t("alertingTestAlertSentDescription")
});
} catch (e) {
toast({
title: t("error"),
description: formatAxiosError(e),
variant: "destructive"
});
}
};
const [, formAction, isSaving] = useActionState(saveAlert, null);
const [isTestingAlert, startTransition] = useTransition();
return (
);
}