import { useState, useEffect } from "react"; import type { ReactElement } from "react"; import { Layout } from "@/components/layout/Layout"; import { StatusBadge, PrimaryButton, DataTable, Pagination, FilterDropdown, SearchBox, type Column, ActionDropdown, // SecondaryButton, } from "@/components/shared"; import { ViewModuleModal, NewModuleModal, EditModuleModal, WebhookSyncModal, ApikeyReissueModal, } from "@/components/superadmin"; import { Plus, ArrowUpDown, Eye, CloudSync, Edit, Key } from "lucide-react"; import { moduleService } from "@/services/module-service"; import type { Module } from "@/types/module"; // Helper function to format date const formatDate = (dateString: string): string => { const date = new Date(dateString); return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", }); }; // Helper function to get status badge variant const getStatusVariant = ( status: string | null, ): "success" | "failure" | "process" => { if (!status) return "process"; switch (status.toLowerCase()) { case "running": case "active": case "healthy": return "success"; case "stopped": case "failed": case "unhealthy": return "failure"; default: return "process"; } }; const Modules = (): ReactElement => { const [modules, setModules] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [isCreating, setIsCreating] = useState(false); // Pagination state const [currentPage, setCurrentPage] = useState(1); const [limit, setLimit] = useState(5); const [pagination, setPagination] = useState<{ page: number; limit: number; total: number; totalPages: number; hasMore: boolean; }>({ page: 1, limit: 5, total: 0, totalPages: 1, hasMore: false, }); // Filter state const [statusFilter, setStatusFilter] = useState(null); const [orderBy, setOrderBy] = useState(null); // Search state const [search, setSearch] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); // View modal const [viewModalOpen, setViewModalOpen] = useState(false); const [selectedModuleId, setSelectedModuleId] = useState(null); const [editModalOpen, setEditModalOpen] = useState(false); const [selectedModuleForEdit, setSelectedModuleForEdit] = useState(null); const [isUpdating, setIsUpdating] = useState(false); const [webhookModalOpen, setWebhookModalOpen] = useState(false); const [reissueModalOpen, setReissueModalOpen] = useState(false); const fetchModules = async ( page: number, itemsPerPage: number, status: string | null = null, sortBy: string[] | null = null, searchQuery: string | null = null, ): Promise => { try { setIsLoading(true); setError(null); const response = await moduleService.getAll( page, itemsPerPage, status, sortBy, searchQuery, ); if (response.success) { setModules(response.data); setPagination(response.pagination); } else { setError("Failed to load modules"); } } catch (err: any) { setError(err?.response?.data?.error?.message || "Failed to load modules"); } finally { setIsLoading(false); } }; // Handle search debouncing useEffect(() => { const timer = setTimeout(() => { setDebouncedSearch(search); // We only reset to first page if we are actively searching. if (search) setCurrentPage(1); }, 500); return () => clearTimeout(timer); }, [search]); // Fetch modules on mount and when pagination/filters change useEffect(() => { fetchModules(currentPage, limit, statusFilter, orderBy, debouncedSearch); }, [currentPage, limit, statusFilter, orderBy, debouncedSearch]); // View module handler const handleViewModule = (moduleId: string): void => { setSelectedModuleId(moduleId); setViewModalOpen(true); }; // Webhook sync handler const handleOpenWebhookSync = (moduleId: string): void => { setSelectedModuleId(moduleId); setWebhookModalOpen(true); }; // Reissue API key handler const handleOpenReissueApiKey = (moduleId: string): void => { setSelectedModuleId(moduleId); setReissueModalOpen(true); }; // Create module handler const handleCreateModule = async ( data: any, ): Promise<{ api_key: { key: string } }> => { try { setIsCreating(true); const response = await moduleService.create(data); await fetchModules(currentPage, limit, statusFilter, orderBy); return { api_key: response.data.api_key }; } catch (err: any) { throw err; } finally { setIsCreating(false); } }; // Edit module handler const handleEditModule = (module: Module): void => { setSelectedModuleForEdit(module); setEditModalOpen(true); }; // Update module handler const handleUpdateModule = async (data: any): Promise => { if (!selectedModuleForEdit) return; try { setIsUpdating(true); const response = await moduleService.update(selectedModuleForEdit.id, data); await fetchModules(currentPage, limit, statusFilter, orderBy); return response; } catch (err: any) { throw err; } finally { setIsUpdating(false); } }; // Load module for view const loadModule = async (id: string): Promise => { const response = await moduleService.getById(id); return response.data; }; // Define table columns const columns: Column[] = [ { key: "name", label: "Module Name", render: (module) => (
{module.name.substring(0, 2).toUpperCase()}
{module.name} {module.module_id}
), mobileLabel: "Name", }, { key: "description", label: "Description", render: (module) => ( {module.description} ), }, { key: "version", label: "Version", render: (module) => ( {module.version} ), }, { key: "status", label: "Status", render: (module) => ( {module.status || "Unknown"} ), }, { key: "health_status", label: "Health Status", render: (module) => ( {module.health_status || "N/A"} ), }, { key: "runtime_language", label: "Runtime", render: (module) => ( {module.runtime_language || "N/A"} ), }, { key: "created_at", label: "Registered Date", render: (module) => ( {formatDate(module.created_at)} ), mobileLabel: "Registered", }, { key: "actions", label: "Actions", align: "right", render: (module) => (
, label: "View", onClick: () => handleViewModule(module.id), }, { icon: , label: "Edit", onClick: () => handleEditModule(module), }, { icon: , label: "Reissue API Key", onClick: () => handleOpenReissueApiKey(module.id), }, { icon: , label: "Webhook Sync", onClick: () => handleOpenWebhookSync(module.id), }, ]} />
), }, // { // key: "actions", // label: "Actions", // align: "right", // render: (module) => ( //
// // handleOpenWebhookSync(module.id)} // > // WebhookSync // //
// ), // }, ]; // Mobile card renderer const mobileCardRenderer = (module: Module) => (
{module.name.substring(0, 2).toUpperCase()}

{module.name}

{module.module_id}

Status:
{module.status || "Unknown"}
Health:
{module.health_status || "N/A"}
Version:

{module.version}

Runtime:

{module.runtime_language || "N/A"}

Registered:

{formatDate(module.created_at)}

Description:

{module.description}

); return ( {/* Table Container */}
{/* Table Header with Filters */}
{/* Filters */}
{/* Global Search */} {/* Status Filter */} { setStatusFilter(value as string | null); setCurrentPage(1); }} placeholder="All" /> {/* Sort Filter */} { setOrderBy(value as string[] | null); setCurrentPage(1); }} placeholder="Default" showIcon icon={} />
{/* Actions */}
{/* Export Button */} {/* */} {/* New Module Button */} setIsModalOpen(true)} > New Module
{/* Table */} module.id} isLoading={isLoading} error={error} mobileCardRenderer={mobileCardRenderer} emptyMessage="No modules found" /> {/* Pagination */} {pagination.total > 0 && (
setCurrentPage(page)} onLimitChange={(newLimit: number) => { setLimit(newLimit); setCurrentPage(1); }} />
)}
{/* New Module Modal */} setIsModalOpen(false)} onSubmit={handleCreateModule} isLoading={isCreating} /> {/* View Module Modal */} { setViewModalOpen(false); setSelectedModuleId(null); }} moduleId={selectedModuleId} onLoadModule={loadModule} /> {/* Edit Module Modal */} { setEditModalOpen(false); setSelectedModuleForEdit(null); }} module={selectedModuleForEdit} onSubmit={handleUpdateModule} isLoading={isUpdating} /> {/* Webhook Sync Modal */} { setWebhookModalOpen(false); setSelectedModuleId(null); }} moduleId={selectedModuleId} onLoadModule={loadModule} /> {/* Reissue API Key Modal */} { setReissueModalOpen(false); setSelectedModuleId(null); }} moduleId={selectedModuleId} onLoadModule={loadModule} />
); }; export default Modules;