import React from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Separator } from "@/components/ui/separator"; import { useToast } from "@/hooks/use-toast"; import { queryClient, apiRequest } from "@/lib/queryClient"; import DashboardLayout from "@/layouts/dashboard-layout"; import { PlusCircle, RefreshCw, Trash2, Edit, Play, Clock, Settings } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { ScrollArea } from "@/components/ui/scroll-area"; import { format } from "date-fns"; import { Skeleton } from "@/components/ui/skeleton"; import DynamicGroupRuleModal from "@/components/modals/dynamic-group-rule-modal"; interface DynamicGroupRule { id: number; name: string; description: string | null; targetGroup: string; enabled: boolean; schedule: string; createdAt: string; updatedAt: string; lastRun: string | null; lastRunStatus: string | null; variablePattern: string | null; connections: { id: number; name: string; }[]; } // Helper functions are defined inside the component export default function DynamicGroupsPage() { const { toast } = useToast(); const [activeTab, setActiveTab] = React.useState("active"); const [modalOpen, setModalOpen] = React.useState(false); const [selectedRuleId, setSelectedRuleId] = React.useState(undefined); const { data: dynamicGroupRules, isLoading } = useQuery({ queryKey: ["/api/dynamic-group-rules"], retry: false, }); const runNowMutation = useMutation({ mutationFn: async (id: number) => { const response = await apiRequest("POST", `/api/dynamic-group-rules/${id}/run`); return response.json(); }, onSuccess: () => { toast({ title: "Success", description: "Rule execution started", variant: "default", }); queryClient.invalidateQueries({ queryKey: ["/api/dynamic-group-rules"] }); }, onError: (error: Error) => { toast({ title: "Error", description: `Failed to execute rule: ${error.message}`, variant: "destructive", }); }, }); const toggleRuleMutation = useMutation({ mutationFn: async ({ id, enabled }: { id: number; enabled: boolean }) => { const response = await apiRequest("PATCH", `/api/dynamic-group-rules/${id}`, { enabled }); return response.json(); }, onSuccess: () => { toast({ title: "Success", description: "Rule status updated", variant: "default", }); queryClient.invalidateQueries({ queryKey: ["/api/dynamic-group-rules"] }); }, onError: (error: Error) => { toast({ title: "Error", description: `Failed to update rule: ${error.message}`, variant: "destructive", }); }, }); const deleteRuleMutation = useMutation({ mutationFn: async (id: number) => { await apiRequest("DELETE", `/api/dynamic-group-rules/${id}`); }, onSuccess: () => { toast({ title: "Success", description: "Rule deleted successfully", variant: "default", }); queryClient.invalidateQueries({ queryKey: ["/api/dynamic-group-rules"] }); }, onError: (error: Error) => { toast({ title: "Error", description: `Failed to delete rule: ${error.message}`, variant: "destructive", }); }, }); const handleDeleteRule = (id: number) => { if (window.confirm("Are you sure you want to delete this rule? This action cannot be undone.")) { deleteRuleMutation.mutate(id); } }; const handleRunNow = (id: number) => { runNowMutation.mutate(id); }; const handleToggleRule = (id: number, currentStatus: boolean) => { toggleRuleMutation.mutate({ id, enabled: !currentStatus }); }; const handleEditRule = (id: number) => { setSelectedRuleId(id); setModalOpen(true); }; const handleCreateRule = () => { setSelectedRuleId(undefined); setModalOpen(true); }; // Filter the rules based on the active tab const filteredRules = React.useMemo(() => { if (!dynamicGroupRules) return []; return activeTab === "active" ? dynamicGroupRules.filter((rule: DynamicGroupRule) => rule.enabled) : dynamicGroupRules.filter((rule: DynamicGroupRule) => !rule.enabled); }, [dynamicGroupRules, activeTab]); const formatLastRunTime = (lastRun: string | null) => { if (!lastRun) return "Never"; return format(new Date(lastRun), "MMM d, yyyy 'at' h:mm a"); }; const getStatusBadge = (status: string | null) => { if (!status) return null; let variant: "default" | "destructive" | "outline" | "secondary" | null = null; switch (status.toLowerCase()) { case "success": variant = "default"; break; case "failed": variant = "destructive"; break; case "running": variant = "secondary"; break; default: variant = "outline"; } return {status}; }; return (
Active Rules Disabled Rules
{/* Rule Editor Modal */} { queryClient.invalidateQueries({ queryKey: ["/api/dynamic-group-rules"] }); }} />
); } interface RulesListProps { rules: DynamicGroupRule[]; isLoading: boolean; onDelete: (id: number) => void; onRunNow: (id: number) => void; onToggle: (id: number, currentStatus: boolean) => void; onEdit: (id: number) => void; onCreateNew: () => void; } function RulesList({ rules, isLoading, onDelete, onRunNow, onToggle, onEdit, onCreateNew }: RulesListProps) { if (isLoading) { return (
{[1, 2, 3].map((i) => (
))}
); } if (!rules.length) { return (

No rules found. Create a new dynamic group rule to get started.

); } return (
{rules.map((rule) => (
{rule.name} {rule.description || "No description provided"}
{rule.enabled ? "Disable Rule" : "Enable Rule"} Run Now Edit Rule Delete Rule

Target Group

{rule.targetGroup}

Schedule

{rule.schedule}

Last Run

{formatLastRunTime(rule.lastRun)}

{rule.lastRunStatus && getStatusBadge(rule.lastRunStatus)}

Connected to

{rule.connections.map((connection) => ( {connection.name} ))} {!rule.connections.length && (

No connections

)}
{rule.variablePattern && (

Variable Pattern

{rule.variablePattern}

)}
))}
); }