diff options
Diffstat (limited to '')
3 files changed, 77 insertions, 3 deletions
diff --git a/apps/web/components/dashboard/rules/RuleEngineConditionBuilder.tsx b/apps/web/components/dashboard/rules/RuleEngineConditionBuilder.tsx index 8faca013..28bf690d 100644 --- a/apps/web/components/dashboard/rules/RuleEngineConditionBuilder.tsx +++ b/apps/web/components/dashboard/rules/RuleEngineConditionBuilder.tsx @@ -19,6 +19,7 @@ import { ChevronDown, ChevronRight, FileType, + Heading, Link, PlusCircle, Rss, @@ -28,7 +29,10 @@ import { } from "lucide-react"; import { useTranslation } from "react-i18next"; -import type { RuleEngineCondition } from "@karakeep/shared/types/rules"; +import type { + RuleEngineCondition, + RuleEngineEvent, +} from "@karakeep/shared/types/rules"; import { FeedSelector } from "../feeds/FeedSelector"; import { TagAutocomplete } from "../tags/TagAutocomplete"; @@ -36,6 +40,7 @@ import { TagAutocomplete } from "../tags/TagAutocomplete"; interface ConditionBuilderProps { value: RuleEngineCondition; onChange: (condition: RuleEngineCondition) => void; + eventType: RuleEngineEvent["type"]; level?: number; onRemove?: () => void; } @@ -43,6 +48,7 @@ interface ConditionBuilderProps { export function ConditionBuilder({ value, onChange, + eventType, level = 0, onRemove, }: ConditionBuilderProps) { @@ -54,6 +60,15 @@ export function ConditionBuilder({ case "urlContains": onChange({ type: "urlContains", str: "" }); break; + case "urlDoesNotContain": + onChange({ type: "urlDoesNotContain", str: "" }); + break; + case "titleContains": + onChange({ type: "titleContains", str: "" }); + break; + case "titleDoesNotContain": + onChange({ type: "titleDoesNotContain", str: "" }); + break; case "importedFromFeed": onChange({ type: "importedFromFeed", feedId: "" }); break; @@ -88,7 +103,11 @@ export function ConditionBuilder({ const renderConditionIcon = (type: RuleEngineCondition["type"]) => { switch (type) { case "urlContains": + case "urlDoesNotContain": return <Link className="h-4 w-4" />; + case "titleContains": + case "titleDoesNotContain": + return <Heading className="h-4 w-4" />; case "importedFromFeed": return <Rss className="h-4 w-4" />; case "bookmarkTypeIs": @@ -118,6 +137,42 @@ export function ConditionBuilder({ </div> ); + case "urlDoesNotContain": + return ( + <div className="mt-2"> + <Input + value={value.str} + onChange={(e) => onChange({ ...value, str: e.target.value })} + placeholder="URL does not contain..." + className="w-full" + /> + </div> + ); + + case "titleContains": + return ( + <div className="mt-2"> + <Input + value={value.str} + onChange={(e) => onChange({ ...value, str: e.target.value })} + placeholder="Title contains..." + className="w-full" + /> + </div> + ); + + case "titleDoesNotContain": + return ( + <div className="mt-2"> + <Input + value={value.str} + onChange={(e) => onChange({ ...value, str: e.target.value })} + placeholder="Title does not contain..." + className="w-full" + /> + </div> + ); + case "importedFromFeed": return ( <div className="mt-2"> @@ -182,6 +237,7 @@ export function ConditionBuilder({ newConditions[index] = newCondition; onChange({ ...value, conditions: newConditions }); }} + eventType={eventType} level={level + 1} onRemove={() => { const newConditions = [...value.conditions]; @@ -217,6 +273,10 @@ export function ConditionBuilder({ } }; + // Title conditions are hidden for "bookmarkAdded" event because + // titles are not available at bookmark creation time (they're fetched during crawling) + const showTitleConditions = eventType !== "bookmarkAdded"; + const ConditionSelector = () => ( <Select value={value.type} onValueChange={handleTypeChange}> <SelectTrigger className="ml-2 h-8 border-none bg-transparent px-2"> @@ -235,6 +295,19 @@ export function ConditionBuilder({ <SelectItem value="urlContains"> {t("settings.rules.conditions_types.url_contains")} </SelectItem> + <SelectItem value="urlDoesNotContain"> + {t("settings.rules.conditions_types.url_does_not_contain")} + </SelectItem> + {showTitleConditions && ( + <SelectItem value="titleContains"> + {t("settings.rules.conditions_types.title_contains")} + </SelectItem> + )} + {showTitleConditions && ( + <SelectItem value="titleDoesNotContain"> + {t("settings.rules.conditions_types.title_does_not_contain")} + </SelectItem> + )} <SelectItem value="importedFromFeed"> {t("settings.rules.conditions_types.imported_from_feed")} </SelectItem> diff --git a/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx b/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx index da10317a..e4859b4a 100644 --- a/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx +++ b/apps/web/components/dashboard/rules/RuleEngineRuleEditor.tsx @@ -8,8 +8,8 @@ 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 { toast } from "@/components/ui/sonner"; import { Textarea } from "@/components/ui/textarea"; -import { toast } from "@/components/ui/use-toast"; import { Save, X } from "lucide-react"; import { useTranslation } from "react-i18next"; @@ -175,6 +175,7 @@ export function RuleEditor({ rule, onCancel }: RuleEditorProps) { <ConditionBuilder value={editedRule.condition} onChange={handleConditionChange} + eventType={editedRule.event.type} /> </div> diff --git a/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx b/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx index 206a3550..32262b31 100644 --- a/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx +++ b/apps/web/components/dashboard/rules/RuleEngineRuleList.tsx @@ -2,8 +2,8 @@ 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 { toast } from "@/components/ui/sonner"; 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"; |
