| 1 | "use client"; |
| 2 | |
| 3 | import { Trash2 } from "lucide-react"; |
| 4 | import { motion } from "framer-motion"; |
| 5 | import { Switch } from "@/components/ui/switch"; |
| 6 | import { Button } from "@/components/ui/button"; |
| 7 | import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; |
| 8 | import { usePatchWatch, useDeleteWatch } from "@/hooks/useWatchlist"; |
| 9 | import { fadeUpItem } from "@/lib/motion"; |
| 10 | import { cn } from "@/lib/utils"; |
| 11 | import type { Priority, WatchlistRule } from "@/lib/types"; |
| 12 | |
| 13 | const PRIORITY_COLOR: Record<Priority, string> = { |
| 14 | low: "var(--color-fg-tertiary)", |
| 15 | normal: "var(--color-accent-blue)", |
| 16 | high: "var(--color-accent-amber)", |
| 17 | critical: "var(--color-accent-red)", |
| 18 | }; |
| 19 | |
| 20 | export function WatchlistItem({ rule }: { rule: WatchlistRule }) { |
| 21 | const patch = usePatchWatch(); |
| 22 | const del = useDeleteWatch(); |
| 23 | const c = PRIORITY_COLOR[rule.priority]; |
| 24 | |
| 25 | return ( |
| 26 | <motion.li |
| 27 | variants={fadeUpItem} |
| 28 | layout |
| 29 | className={cn( |
| 30 | "flex items-center gap-3 rounded-[var(--radius)] border border-[var(--color-border-subtle)] bg-[var(--color-bg-elevated)] p-3", |
| 31 | !rule.active && "opacity-60", |
| 32 | )} |
| 33 | > |
| 34 | <div className="min-w-0 flex-1"> |
| 35 | <div className="flex items-center gap-2"> |
| 36 | <span className="truncate text-sm font-medium text-[var(--color-fg-primary)]">{rule.label}</span> |
| 37 | <span |
| 38 | className="rounded-full px-1.5 py-0.5 text-[10px] font-medium capitalize" |
| 39 | style={{ color: c, backgroundColor: `color-mix(in oklch, ${c} 14%, transparent)` }} |
| 40 | > |
| 41 | {rule.priority} |
| 42 | </span> |
| 43 | </div> |
| 44 | <p className="mt-0.5 text-xs text-[var(--color-fg-tertiary)]"> |
| 45 | <span className="capitalize">{rule.kind}</span> · {rule.matches_30d} matches in 30d |
| 46 | </p> |
| 47 | </div> |
| 48 | |
| 49 | <Tooltip> |
| 50 | <TooltipTrigger asChild> |
| 51 | <span> |
| 52 | <Switch |
| 53 | checked={rule.active} |
| 54 | onCheckedChange={(active) => patch.mutate({ id: rule.id, active })} |
| 55 | aria-label={rule.active ? "Pause alerts" : "Resume alerts"} |
| 56 | /> |
| 57 | </span> |
| 58 | </TooltipTrigger> |
| 59 | <TooltipContent>{rule.active ? "Alerts on" : "Paused"}</TooltipContent> |
| 60 | </Tooltip> |
| 61 | |
| 62 | <Button |
| 63 | variant="ghost" |
| 64 | size="icon" |
| 65 | aria-label={`Remove ${rule.label}`} |
| 66 | onClick={() => del.mutate(rule.id)} |
| 67 | className="text-[var(--color-fg-tertiary)] hover:text-[var(--color-accent-red)]" |
| 68 | > |
| 69 | <Trash2 className="h-4 w-4" aria-hidden /> |
| 70 | </Button> |
| 71 | </motion.li> |
| 72 | ); |
| 73 | } |