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.

This commit is contained in:
Yashwin 2026-02-04 16:37:31 +05:30
parent dd71820ac9
commit c656594154
5 changed files with 262 additions and 229 deletions

View File

@ -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,7 +274,7 @@ export const NewModuleModal = ({
<p className="text-sm text-[#ef4444]">{errors.root.message}</p>
</div>
)}
{!apiKey && (
<div className="flex flex-col gap-0">
{/* Basic Information Section */}
<div className="mb-4">
@ -475,6 +475,7 @@ export const NewModuleModal = ({
</div>
</div>
</div>
)}
</form>
</Modal >
);

View File

@ -288,7 +288,7 @@ export const RolesTable = ({ tenantId, showHeader = true, compact = false }: Rol
<>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-[#0f1724]">Roles</h3>
<h3 className="text-lg font-semibold text-[#0f1724]"></h3>
<div className="flex items-center gap-2">
<FilterDropdown
label="Scope"

View File

@ -330,7 +330,7 @@ export const UsersTable = ({ tenantId, showHeader = true, compact = false }: Use
<>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-[#0f1724]">Users</h3>
<h3 className="text-lg font-semibold text-[#0f1724]"></h3>
<div className="flex items-center gap-2">
<FilterDropdown
label="Status"

View File

@ -10,8 +10,6 @@ import {
History,
CreditCard,
Edit,
CheckCircle2,
XCircle,
Settings,
Image as ImageIcon,
} from 'lucide-react';
@ -25,8 +23,10 @@ import {
import { UsersTable, RolesTable } from '@/components/superadmin';
import { tenantService } from '@/services/tenant-service';
import { auditLogService } from '@/services/audit-log-service';
import type { Tenant, AssignedModule } from '@/types/tenant';
import { moduleService } from '@/services/module-service';
import type { Tenant } from '@/types/tenant';
import type { AuditLog } from '@/types/audit-log';
import type { MyModule } from '@/types/module';
import { formatDate } from '@/utils/format-date';
type TabType = 'overview' | 'users' | 'roles' | 'modules' | 'settings' | 'license' | 'audit-logs' | 'billing';
@ -278,10 +278,8 @@ const TenantDetails = (): ReactElement => {
{activeTab === 'roles' && id && (
<RolesTable tenantId={id} compact={true} />
)}
{activeTab === 'modules' && tenant && (
<ModulesTab
modules={tenant.assignedModules || []}
/>
{activeTab === 'modules' && id && (
<ModulesTab tenantId={id} />
)}
{activeTab === 'settings' && tenant && (
<SettingsTab tenant={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<Set<string>>(new Set());
const ModulesTab = ({ tenantId }: ModulesTabProps): ReactElement => {
const [modules, setModules] = useState<MyModule[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(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<void> => {
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');
}
} catch (err: any) {
setError(err?.response?.data?.error?.message || 'Failed to load modules');
} finally {
setIsLoading(false);
}
return next;
});
// TODO: Call API to enable/disable module
};
const columns: Column<AssignedModule>[] = [
useEffect(() => {
if (tenantId) {
fetchModules();
}
}, [tenantId]);
// Launch module handler
const handleLaunchModule = async (moduleId: string): Promise<void> => {
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<MyModule>[] = [
{
key: 'name',
label: 'Module Name',
@ -423,6 +441,13 @@ const ModulesTab = ({ modules }: ModulesTabProps): ReactElement => {
</div>
),
},
{
key: 'module_id',
label: 'Module ID',
render: (module) => (
<span className="text-sm font-normal text-[#6b7280] font-mono">{module.module_id}</span>
),
},
{
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) => (
<button
onClick={() => toggleModule(module.id)}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-xs font-medium transition-colors ${
enabledModules.has(module.id)
? 'bg-green-50 text-green-700 border border-green-200'
: 'bg-gray-50 text-gray-700 border border-gray-200'
}`}
>
{enabledModules.has(module.id) ? (
<CheckCircle2 className="w-4 h-4" />
) : (
<XCircle className="w-4 h-4" />
)}
<span>{enabledModules.has(module.id) ? 'Enabled' : 'Disabled'}</span>
</button>
<span className="text-sm font-normal text-[#6b7280] font-mono truncate max-w-[200px]">
{module.base_url || 'N/A'}
</span>
),
},
{
key: 'created_at',
label: 'Registered',
key: 'assigned_at',
label: 'Assigned At',
render: (module) => (
<span className="text-sm font-normal text-[#6b7280]">{formatDate(module.created_at)}</span>
<span className="text-sm font-normal text-[#6b7280]">{formatDate(module.assigned_at)}</span>
),
},
{
key: 'actions',
label: 'Actions',
align: 'right',
render: (module) => (
<div className="flex justify-end gap-3">
<button
type="button"
onClick={() => handleLaunchModule(module.id)}
className="text-sm text-[#FFC107] hover:text-[#FFC107] font-medium transition-colors cursor-pointer"
>
Lunch
</button>
</div>
),
},
];
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-[#0f1724]">Modules</h3>
</div>
<DataTable
columns={columns}
data={modules}
keyExtractor={(module) => module.id}
isLoading={false}
isLoading={isLoading}
error={error}
emptyMessage="No modules assigned to this tenant"
/>
</div>
@ -593,9 +620,9 @@ const AuditLogsTab = ({
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
{/* <div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-[#0f1724]">Audit Logs</h3>
</div>
</div> */}
<DataTable
columns={columns}
data={auditLogs}

View File

@ -75,8 +75,13 @@ export const moduleService = {
const response = await apiClient.post<LaunchModuleResponse>(url);
return response.data;
},
getMyModules: async (): Promise<MyModulesResponse> => {
const response = await apiClient.get<MyModulesResponse>('/modules/my');
getMyModules: async (tenantId?: string | null): Promise<MyModulesResponse> => {
const params = new URLSearchParams();
if (tenantId) {
params.append('tenant_id', tenantId);
}
const url = `/modules/my${params.toString() ? `?${params.toString()}` : ''}`;
const response = await apiClient.get<MyModulesResponse>(url);
return response.data;
},
};