From c65659415492b18875c4cdd8731f6e63bea3889e Mon Sep 17 00:00:00 2001 From: Yashwin Date: Wed, 4 Feb 2026 16:37:31 +0530 Subject: [PATCH] Refactor NewModuleModal to comment out health_status field and improve code readability. Update TenantDetails to fetch modules based on tenant ID and adjust ModulesTab to handle module data more effectively. Modify module service to accept tenant ID for fetching modules. Clean up unused code in RolesTable and UsersTable components by removing header text. --- src/components/superadmin/NewModuleModal.tsx | 349 ++++++++++--------- src/components/superadmin/RolesTable.tsx | 2 +- src/components/superadmin/UsersTable.tsx | 2 +- src/pages/superadmin/TenantDetails.tsx | 129 ++++--- src/services/module-service.ts | 9 +- 5 files changed, 262 insertions(+), 229 deletions(-) diff --git a/src/components/superadmin/NewModuleModal.tsx b/src/components/superadmin/NewModuleModal.tsx index d62c3e2..78990a9 100644 --- a/src/components/superadmin/NewModuleModal.tsx +++ b/src/components/superadmin/NewModuleModal.tsx @@ -30,7 +30,7 @@ const newModuleSchema = z.object({ .min(1, 'runtime_language is required') .max(50, 'runtime_language must be at most 50 characters'), framework: z.string().max(50, 'framework must be at most 50 characters').optional().nullable(), - webhookurl: z.string().max(500, "webhookurl must be at most 500 characters").url("Invalid URL format").nullable(), + webhookurl: z.string().max(500, "webhookurl must be at most 500 characters").url("Invalid URL format").nullable(), base_url: z .string() .min(1, 'base_url is required') @@ -49,7 +49,7 @@ const newModuleSchema = z.object({ min_replicas: z.number().int().min(1, 'min_replicas must be at least 1').max(50, 'min_replicas must be at most 50').optional().nullable(), max_replicas: z.number().int().min(1, 'max_replicas must be at least 1').max(50, 'max_replicas must be at most 50').optional().nullable(), last_health_check: z.string().optional().nullable(), - health_status: z.string().max(20, 'health_status must be at most 20 characters').optional().nullable(), + // health_status: z.string().max(20, 'health_status must be at most 20 characters').optional().nullable(), consecutive_failures: z.number().int().optional().nullable(), registered_by: z.string().uuid().optional().nullable(), tenant_id: z.string().uuid().optional().nullable(), @@ -96,7 +96,7 @@ export const NewModuleModal = ({ min_replicas: null, max_replicas: null, last_health_check: null, - health_status: null, + // health_status: null, consecutive_failures: null, registered_by: null, tenant_id: null, @@ -126,7 +126,7 @@ export const NewModuleModal = ({ min_replicas: null, max_replicas: null, last_health_check: null, - health_status: null, + // health_status: null, consecutive_failures: null, registered_by: null, tenant_id: null, @@ -274,52 +274,52 @@ export const NewModuleModal = ({

{errors.root.message}

)} + {!apiKey && ( +
+ {/* Basic Information Section */} +
+

Basic Information

+
+
+
+ +
+
-
- {/* Basic Information Section */} -
-

Basic Information

-
-
-
- + +
-
- -
-
+ - - - {/*
+ {/*
*/} - - {/*
*/} - {/*
+ + {/*
*/} + {/*
*/} +
-
- {/* Runtime Information Section */} -
-

Runtime Information

-
-
+ {/* Runtime Information Section */} +
+

Runtime Information

+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+
+ + {/* URL Configuration Section */} +
+

URL Configuration

+
-
-
+
-
-
- -
-
-
-
-
- {/* URL Configuration Section */} -
-

URL Configuration

-
- + {/* Resource Configuration Section */} +
+

Resource Configuration (Optional)

+
+
+
+ +
+
+ +
+
- -
-
+
+
+ +
+
+ +
+
- {/* Resource Configuration Section */} -
-

Resource Configuration (Optional)

-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- { - if (value === '' || value === null || value === undefined) return null; - const num = Number(value); - return isNaN(num) ? null : num; - }, - })} - /> -
-
- { - if (value === '' || value === null || value === undefined) return null; - const num = Number(value); - return isNaN(num) ? null : num; - }, - })} - /> +
+
+ { + if (value === '' || value === null || value === undefined) return null; + const num = Number(value); + return isNaN(num) ? null : num; + }, + })} + /> +
+
+ { + if (value === '' || value === null || value === undefined) return null; + const num = Number(value); + return isNaN(num) ? null : num; + }, + })} + /> +
-
+ )} ); diff --git a/src/components/superadmin/RolesTable.tsx b/src/components/superadmin/RolesTable.tsx index 467bcba..419779b 100644 --- a/src/components/superadmin/RolesTable.tsx +++ b/src/components/superadmin/RolesTable.tsx @@ -288,7 +288,7 @@ export const RolesTable = ({ tenantId, showHeader = true, compact = false }: Rol <>
-

Roles

+

-

Users

+

{ {activeTab === 'roles' && id && ( )} - {activeTab === 'modules' && tenant && ( - + {activeTab === 'modules' && id && ( + )} {activeTab === 'settings' && tenant && ( @@ -386,31 +384,51 @@ const OverviewTab = ({ tenant, stats }: OverviewTabProps): ReactElement => { // Modules Tab Component interface ModulesTabProps { - modules: AssignedModule[]; + tenantId: string; } -const ModulesTab = ({ modules }: ModulesTabProps): ReactElement => { - const [enabledModules, setEnabledModules] = useState>(new Set()); +const ModulesTab = ({ tenantId }: ModulesTabProps): ReactElement => { + const [modules, setModules] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); - useEffect(() => { - // Initialize enabled modules (assuming all are enabled by default) - setEnabledModules(new Set(modules.map((m) => m.id))); - }, [modules]); - - const toggleModule = (moduleId: string): void => { - setEnabledModules((prev) => { - const next = new Set(prev); - if (next.has(moduleId)) { - next.delete(moduleId); + // Fetch modules for this tenant + const fetchModules = async (): Promise => { + try { + setIsLoading(true); + setError(null); + const response = await moduleService.getMyModules(tenantId); + if (response.success && response.data) { + setModules(response.data); } else { - next.add(moduleId); + setError('Failed to load modules'); } - return next; - }); - // TODO: Call API to enable/disable module + } catch (err: any) { + setError(err?.response?.data?.error?.message || 'Failed to load modules'); + } finally { + setIsLoading(false); + } }; - const columns: Column[] = [ + useEffect(() => { + if (tenantId) { + fetchModules(); + } + }, [tenantId]); + + // Launch module handler + const handleLaunchModule = async (moduleId: string): Promise => { + try { + const response = await moduleService.launch(moduleId, tenantId); + if (response.success && response.data.launch_url) { + window.open(response.data.launch_url, '_blank', 'noopener,noreferrer'); + } + } catch (err: any) { + setError(err?.response?.data?.error?.message || 'Failed to launch module'); + } + }; + + const columns: Column[] = [ { key: 'name', label: 'Module Name', @@ -423,6 +441,13 @@ const ModulesTab = ({ modules }: ModulesTabProps): ReactElement => {
), }, + { + key: 'module_id', + label: 'Module ID', + render: (module) => ( + {module.module_id} + ), + }, { key: 'version', label: 'Version', @@ -448,45 +473,47 @@ const ModulesTab = ({ modules }: ModulesTabProps): ReactElement => { ), }, { - key: 'enabled', - label: 'Enabled', + key: 'base_url', + label: 'Base URL', render: (module) => ( - + + {module.base_url || 'N/A'} + ), }, { - key: 'created_at', - label: 'Registered', + key: 'assigned_at', + label: 'Assigned At', render: (module) => ( - {formatDate(module.created_at)} + {formatDate(module.assigned_at)} + ), + }, + { + key: 'actions', + label: 'Actions', + align: 'right', + render: (module) => ( +
+ +
), }, ]; return (
-
-

Modules

-
module.id} - isLoading={false} + isLoading={isLoading} + error={error} emptyMessage="No modules assigned to this tenant" />
@@ -593,9 +620,9 @@ const AuditLogsTab = ({ return (
-
+ {/*

Audit Logs

-
+
*/} (url); return response.data; }, - getMyModules: async (): Promise => { - const response = await apiClient.get('/modules/my'); + getMyModules: async (tenantId?: string | null): Promise => { + const params = new URLSearchParams(); + if (tenantId) { + params.append('tenant_id', tenantId); + } + const url = `/modules/my${params.toString() ? `?${params.toString()}` : ''}`; + const response = await apiClient.get(url); return response.data; }, };