import { useState, useEffect, type ReactElement } from "react"; import { useSelector } from "react-redux"; import { PrimaryButton, StatusBadge, DataTable, Pagination, FilterDropdown, DeleteConfirmationModal, WorkflowDefinitionModal, WorkflowDefinitionViewModal, type Column, } from "@/components/shared"; import { Plus, GitBranch, Play, Power, Trash2, Copy, Edit, Eye } from "lucide-react"; import { workflowService } from "@/services/workflow-service"; import type { WorkflowDefinition } from "@/types/workflow"; import { showToast } from "@/utils/toast"; import type { RootState } from "@/store/store"; import { formatDate } from "@/utils/format-date"; interface WorkflowDefinitionsTableProps { tenantId?: string | null; // If provided, use this tenantId (Super Admin mode) compact?: boolean; // Compact mode for tabs showHeader?: boolean; entityType?: string; // Filter by entity type } const WorkflowDefinitionsTable = ({ tenantId: tenantId, compact = false, showHeader = true, entityType, }: WorkflowDefinitionsTableProps): ReactElement => { const reduxTenantId = useSelector((state: RootState) => state.auth.tenantId); const effectiveTenantId = tenantId || reduxTenantId || undefined; const [definitions, setDefinitions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Pagination state const [currentPage, setCurrentPage] = useState(1); const [limit, setLimit] = useState(compact ? 10 : 10); const [totalItems, setTotalItems] = useState(0); // Filter state const [statusFilter, setStatusFilter] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(""); // Modal states const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedDefinition, setSelectedDefinition] = useState(null); const [isViewModalOpen, setIsViewModalOpen] = useState(false); const [viewDefinitionId, setViewDefinitionId] = useState(null); const [isActionLoading, setIsActionLoading] = useState(false); const fetchDefinitions = async () => { try { setIsLoading(true); setError(null); const response = await workflowService.listDefinitions({ tenantId: effectiveTenantId, entity_type: entityType, status: statusFilter || undefined, limit, offset: (currentPage - 1) * limit, search: debouncedSearchQuery || undefined, }); if (response.success) { setDefinitions(response.data); setTotalItems(response.pagination?.total || response.data.length); } else { setError("Failed to load workflow definitions"); } } catch (err: any) { setError( err?.response?.data?.error?.message || "Failed to load workflow definitions", ); } finally { setIsLoading(false); } }; // Debouncing search query useEffect(() => { const timer = setTimeout(() => { setDebouncedSearchQuery(searchQuery); }, 500); return () => clearTimeout(timer); }, [searchQuery]); useEffect(() => { setCurrentPage(1); }, [debouncedSearchQuery, statusFilter]); useEffect(() => { fetchDefinitions(); }, [ effectiveTenantId, statusFilter, currentPage, limit, debouncedSearchQuery, ]); const handleDelete = async () => { if (!selectedDefinition) return; try { setIsActionLoading(true); const response = await workflowService.deleteDefinition( selectedDefinition.id, effectiveTenantId, ); if (response.success) { showToast.success("Workflow definition deleted successfully"); setIsDeleteModalOpen(false); fetchDefinitions(); } } catch (err: any) { showToast.error( err?.response?.data?.error?.message || "Failed to delete workflow definition", ); } finally { setIsActionLoading(false); } }; const handleActivate = async (id: string) => { try { setIsActionLoading(true); const response = await workflowService.activateDefinition( id, effectiveTenantId, ); if (response.success) { showToast.success("Workflow definition activated"); fetchDefinitions(); } } catch (err: any) { showToast.error( err?.response?.data?.error?.message || "Failed to activate", ); } finally { setIsActionLoading(false); } }; const handleDeprecate = async (id: string) => { try { setIsActionLoading(true); const response = await workflowService.deprecateDefinition( id, effectiveTenantId, ); if (response.success) { showToast.success("Workflow definition deprecated"); fetchDefinitions(); } } catch (err: any) { showToast.error( err?.response?.data?.error?.message || "Failed to deprecate", ); } finally { setIsActionLoading(false); } }; const handleClone = async (id: string, name: string) => { try { setIsActionLoading(true); const response = await workflowService.cloneDefinition( id, `${name} (Clone)`, effectiveTenantId, ); if (response.success) { showToast.success("Workflow definition cloned"); fetchDefinitions(); } } catch (err: any) { showToast.error(err?.response?.data?.error?.message || "Failed to clone"); } finally { setIsActionLoading(false); } }; const columns: Column[] = [ { key: "name", label: "Workflow Component", render: (wf) => (
{wf.name} {wf.code}
), }, { key: "entity_type", label: "Entity Type", render: (wf) => ( {wf.entity_type} ), }, { key: "version", label: "Version", render: (wf) => ( v{wf.version} ), }, { key: "status", label: "Status", render: (wf) => { let variant: "success" | "failure" | "info" | "process" = "info"; if (wf.status === "active") variant = "success"; if (wf.status === "deprecated") variant = "failure"; if (wf.status === "draft") variant = "process"; return {wf.status}; }, }, { key: "source_module", label: "Module", render: (wf) => ( {wf.source_module} ), }, { key: "created_at", label: "Created Date", render: (wf) => ( {formatDate(wf.created_at)} ), }, { key: "actions", label: "Actions", align: "right", render: (wf) => (
{wf.status === "draft" && ( )} {wf.status === "active" && ( )} {wf.status === "deprecated" && ( )}
), }, ]; return (
{showHeader && (
setSearchQuery(e.target.value)} />
setStatusFilter( value ? (Array.isArray(value) ? value[0] : value) : null, ) } />
{ setSelectedDefinition(null); setIsModalOpen(true); }} > New Workflow
)} wf.id} isLoading={isLoading} error={error} emptyMessage="No workflow definitions found" /> {totalItems > 0 && ( { setLimit(newLimit); setCurrentPage(1); }} /> )} { setIsDeleteModalOpen(false); setSelectedDefinition(null); }} onConfirm={handleDelete} title="Delete Workflow Definition" message="Are you sure you want to delete this workflow definition? This action cannot be undone." itemName={selectedDefinition?.name || ""} isLoading={isActionLoading} /> { setIsModalOpen(false); setSelectedDefinition(null); }} definition={selectedDefinition} tenantId={effectiveTenantId} onSuccess={fetchDefinitions} initialEntityType={entityType} /> { setIsViewModalOpen(false); setViewDefinitionId(null); }} definitionId={viewDefinitionId} tenantId={effectiveTenantId} />
); }; export default WorkflowDefinitionsTable;