aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx')
-rw-r--r--apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx203
1 files changed, 203 insertions, 0 deletions
diff --git a/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx b/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx
new file mode 100644
index 00000000..da10317a
--- /dev/null
+++ b/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx
@@ -0,0 +1,203 @@
+import type React from "react";
+import { useEffect, useState } from "react";
+import { ActionBuilder } from "@/components/dashboard/rules/RuleEngineActionBuilder";
+import { ConditionBuilder } from "@/components/dashboard/rules/RuleEngineConditionBuilder";
+import { EventSelector } from "@/components/dashboard/rules/RuleEngineEventSelector";
+import { ActionButton } from "@/components/ui/action-button";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Textarea } from "@/components/ui/textarea";
+import { toast } from "@/components/ui/use-toast";
+import { Save, X } from "lucide-react";
+import { useTranslation } from "react-i18next";
+
+import type {
+ RuleEngineAction,
+ RuleEngineCondition,
+ RuleEngineEvent,
+ RuleEngineRule,
+} from "@karakeep/shared/types/rules";
+import {
+ useCreateRule,
+ useUpdateRule,
+} from "@karakeep/shared-react/hooks/rules";
+
+interface RuleEditorProps {
+ rule: Omit<RuleEngineRule, "id"> & { id: string | null };
+ onCancel: () => void;
+}
+
+export function RuleEditor({ rule, onCancel }: RuleEditorProps) {
+ const { t } = useTranslation();
+ const { mutate: createRule, isPending: isCreating } = useCreateRule({
+ onSuccess: () => {
+ toast({
+ description: t("settings.rules.rule_has_been_created"),
+ });
+ onCancel();
+ },
+ onError: (e) => {
+ if (e.data?.code == "BAD_REQUEST") {
+ if (e.data.zodError) {
+ toast({
+ variant: "destructive",
+ description: Object.values(e.data.zodError.fieldErrors)
+ .flat()
+ .join("\n"),
+ });
+ } else {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ }
+ } else {
+ toast({
+ variant: "destructive",
+ title: t("common.something_went_wrong"),
+ });
+ }
+ },
+ });
+ const { mutate: updateRule, isPending: isUpdating } = useUpdateRule({
+ onSuccess: () => {
+ toast({
+ description: t("settings.rules.rule_has_been_updated"),
+ });
+ onCancel();
+ },
+ onError: (e) => {
+ if (e.data?.code == "BAD_REQUEST") {
+ if (e.data.zodError) {
+ toast({
+ variant: "destructive",
+ description: Object.values(e.data.zodError.fieldErrors)
+ .flat()
+ .join("\n"),
+ });
+ } else {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ }
+ } else {
+ toast({
+ variant: "destructive",
+ title: t("common.something_went_wrong"),
+ });
+ }
+ },
+ });
+
+ const [editedRule, setEditedRule] = useState<typeof rule>({ ...rule });
+
+ useEffect(() => {
+ setEditedRule({ ...rule });
+ }, [rule]);
+
+ const handleEventChange = (event: RuleEngineEvent) => {
+ setEditedRule({ ...editedRule, event });
+ };
+
+ const handleConditionChange = (condition: RuleEngineCondition) => {
+ setEditedRule({ ...editedRule, condition });
+ };
+
+ const handleActionsChange = (actions: RuleEngineAction[]) => {
+ setEditedRule({ ...editedRule, actions });
+ };
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ const rule = editedRule;
+ if (rule.id) {
+ updateRule({
+ ...rule,
+ id: rule.id,
+ });
+ } else {
+ createRule(rule);
+ }
+ };
+
+ return (
+ <form onSubmit={handleSubmit}>
+ <Card>
+ <CardHeader>
+ <CardTitle>
+ {rule.id
+ ? t("settings.rules.edit_rule")
+ : t("settings.rules.ceate_rule")}
+ </CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-6">
+ <div className="space-y-4">
+ <div>
+ <Label htmlFor="name">{t("settings.rules.rule_name")}</Label>
+ <Input
+ id="name"
+ value={editedRule.name}
+ onChange={(e) =>
+ setEditedRule({ ...editedRule, name: e.target.value })
+ }
+ placeholder={t("settings.rules.enter_rule_name")}
+ required
+ />
+ </div>
+
+ <div>
+ <Label htmlFor="description">Description</Label>
+ <Textarea
+ id="description"
+ value={editedRule.description ?? ""}
+ onChange={(e) =>
+ setEditedRule({ ...editedRule, description: e.target.value })
+ }
+ placeholder={t("settings.rules.describe_what_this_rule_does")}
+ rows={2}
+ />
+ </div>
+ </div>
+
+ <div className="space-y-2">
+ <Label>{t("settings.rules.whenever")}</Label>
+ <EventSelector
+ value={editedRule.event}
+ onChange={handleEventChange}
+ />
+ </div>
+
+ <div className="space-y-2">
+ <Label>{t("settings.rules.if")}</Label>
+ <ConditionBuilder
+ value={editedRule.condition}
+ onChange={handleConditionChange}
+ />
+ </div>
+
+ <div className="space-y-2">
+ <Label>{t("common.actions")}</Label>
+ <ActionBuilder
+ value={editedRule.actions}
+ onChange={handleActionsChange}
+ />
+ </div>
+
+ <div className="flex justify-end space-x-2 pt-4">
+ <Button type="button" variant="outline" onClick={onCancel}>
+ <X className="mr-2 h-4 w-4" />
+ {t("actions.cancel")}
+ </Button>
+ <ActionButton loading={isCreating || isUpdating} type="submit">
+ <Save className="mr-2 h-4 w-4" />
+ {t("settings.rules.save_rule")}
+ </ActionButton>
+ </div>
+ </CardContent>
+ </Card>
+ </form>
+ );
+}