From 136f126296af65f50da598d084d1485c0e40437a Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sun, 27 Apr 2025 00:02:20 +0100 Subject: feat: Implement generic rule engine (#1318) * Add schema for the new rule engine * Add rule engine backend logic * Implement the worker logic and event firing * Implement the UI changesfor the rule engine * Ensure that when a referenced list or tag are deleted, the corresponding event/action is * Dont show smart lists in rule engine events * Add privacy validations for attached tag and list ids * Move the rules logic into a models --- .../dashboard/rules/RuleEngineRuleList.tsx | 166 +++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 apps/web/components/dashboard/rules/RuleEngineRuleList.tsx (limited to 'apps/web/components/dashboard/rules/RuleEngineRuleList.tsx') diff --git a/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx b/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx new file mode 100644 index 00000000..206a3550 --- /dev/null +++ b/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx @@ -0,0 +1,166 @@ +import { ActionButton } from "@/components/ui/action-button"; +import ActionConfirmingDialog from "@/components/ui/action-confirming-dialog"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Switch } from "@/components/ui/switch"; +import { toast } from "@/components/ui/use-toast"; +import { useClientConfig } from "@/lib/clientConfig"; +import { Edit, Trash2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; + +import type { RuleEngineRule } from "@karakeep/shared/types/rules"; +import { + useDeleteRule, + useUpdateRule, +} from "@karakeep/shared-react/hooks/rules"; + +export default function RuleList({ + rules, + onEditRule, + onDeleteRule, +}: { + rules: RuleEngineRule[]; + onEditRule: (rule: RuleEngineRule) => void; + onDeleteRule?: (ruleId: string) => void; +}) { + const { t } = useTranslation(); + const { demoMode } = useClientConfig(); + const { mutate: updateRule, isPending: isUpdating } = useUpdateRule({ + onSuccess: () => { + toast({ + description: t("settings.rules.rule_has_been_updated"), + }); + }, + onError: (e) => { + toast({ + description: e.message, + variant: "destructive", + }); + }, + }); + + const { mutate: deleteRule, isPending: isDeleting } = useDeleteRule({ + onSuccess: (_ret, req) => { + toast({ + description: t("settings.rules.rule_has_been_deleted"), + }); + onDeleteRule?.(req.id); + }, + onError: (e) => { + toast({ + description: e.message, + variant: "destructive", + }); + }, + }); + + if (rules.length === 0) { + return ( +
+

+ {t("settings.rules.no_rules_created_yet")} +

+

+ {t("settings.rules.create_your_first_rule")} +

+
+ ); + } + + return ( +
+ {rules.map((rule) => ( + + +
+
+

{rule.name}

+

+ {rule.description} +

+
+ + Trigger: + + {formatEventType(rule.event.type)} + + + + + {rule.actions.length} action + {rule.actions.length !== 1 ? "s" : ""} + +
+
+
+ + updateRule({ + ...rule, + enabled: checked, + }) + } + /> + + ( + { + deleteRule({ id: rule.id }); + setDialogOpen(true); + }} + > + + {t("actions.delete")} + + )} + > + + +
+
+
+
+ ))} +
+ ); +} + +function formatEventType(type: string): string { + switch (type) { + case "bookmarkAdded": + return "Bookmark Added"; + case "tagAdded": + return "Tag Added"; + case "tagRemoved": + return "Tag Removed"; + case "addedToList": + return "Added to List"; + case "removedFromList": + return "Removed from List"; + case "favourited": + return "Favourited"; + case "archived": + return "Archived"; + default: + return type; + } +} -- cgit v1.2.3-70-g09d2