import { useState, useEffect, type ReactElement } from 'react'; import { PrimaryButton, StatusBadge, ActionDropdown, NewRoleModal, ViewRoleModal, EditRoleModal, DeleteConfirmationModal, DataTable, Pagination, FilterDropdown, type Column, } from '@/components/shared'; import { Plus, Download, ArrowUpDown } from 'lucide-react'; import { roleService } from '@/services/role-service'; import type { Role, CreateRoleRequest, UpdateRoleRequest } from '@/types/role'; import { showToast } from '@/utils/toast'; import { formatDate } from '@/utils/format-date'; // Helper function to get scope badge variant const getScopeVariant = (scope: string): 'success' | 'failure' | 'process' => { switch (scope.toLowerCase()) { case 'platform': return 'success'; case 'tenant': return 'process'; case 'module': return 'failure'; default: return 'success'; } }; interface RolesTableProps { tenantId?: string | null; // If provided, fetch roles for this tenant only showHeader?: boolean; // Show header with title and actions (default: true) compact?: boolean; // Compact mode for tabs (default: false) } export const RolesTable = ({ tenantId, showHeader = true, compact = false }: RolesTableProps): ReactElement => { const [roles, setRoles] = 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 [scopeFilter, setScopeFilter] = useState(null); const [orderBy, setOrderBy] = useState(null); // View, Edit, Delete modals const [viewModalOpen, setViewModalOpen] = useState(false); const [editModalOpen, setEditModalOpen] = useState(false); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [selectedRoleId, setSelectedRoleId] = useState(null); const [selectedRoleName, setSelectedRoleName] = useState(''); const [isUpdating, setIsUpdating] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const fetchRoles = async ( page: number, itemsPerPage: number, scope: string | null = null, sortBy: string[] | null = null ): Promise => { try { setIsLoading(true); setError(null); const response = tenantId ? await roleService.getByTenant(tenantId, page, itemsPerPage, scope, sortBy) : await roleService.getAll(page, itemsPerPage, scope, sortBy); if (response.success) { setRoles(response.data); setPagination(response.pagination); } else { setError('Failed to load roles'); } } catch (err: any) { setError(err?.response?.data?.error?.message || 'Failed to load roles'); } finally { setIsLoading(false); } }; useEffect(() => { fetchRoles(currentPage, limit, scopeFilter, orderBy); }, [currentPage, limit, scopeFilter, orderBy, tenantId]); const handleCreateRole = async (data: CreateRoleRequest): Promise => { try { setIsCreating(true); const response = await roleService.create(data); const message = response.message || `Role created successfully`; const description = response.message ? undefined : `${data.name} has been added`; showToast.success(message, description); setIsModalOpen(false); await fetchRoles(currentPage, limit, scopeFilter, orderBy); } catch (err: any) { throw err; } finally { setIsCreating(false); } }; // View role handler const handleViewRole = (roleId: string): void => { setSelectedRoleId(roleId); setViewModalOpen(true); }; // Edit role handler const handleEditRole = (roleId: string, roleName: string): void => { setSelectedRoleId(roleId); setSelectedRoleName(roleName); setEditModalOpen(true); }; // Update role handler const handleUpdateRole = async (id: string, data: UpdateRoleRequest): Promise => { try { setIsUpdating(true); const response = await roleService.update(id, data); const message = response.message || `Role updated successfully`; const description = response.message ? undefined : `${data.name} has been updated`; showToast.success(message, description); setEditModalOpen(false); setSelectedRoleId(null); setSelectedRoleName(''); await fetchRoles(currentPage, limit, scopeFilter, orderBy); } catch (err: any) { throw err; } finally { setIsUpdating(false); } }; // Delete role handler const handleDeleteRole = (roleId: string, roleName: string): void => { setSelectedRoleId(roleId); setSelectedRoleName(roleName); setDeleteModalOpen(true); }; // Confirm delete handler const handleConfirmDelete = async (): Promise => { if (!selectedRoleId) return; try { setIsDeleting(true); await roleService.delete(selectedRoleId); setDeleteModalOpen(false); setSelectedRoleId(null); setSelectedRoleName(''); await fetchRoles(currentPage, limit, scopeFilter, orderBy); } catch (err: any) { throw err; // Let the modal handle the error display } finally { setIsDeleting(false); } }; // Load role for view/edit const loadRole = async (id: string): Promise => { const response = await roleService.getById(id); return response.data; }; // Table columns const columns: Column[] = [ { key: 'name', label: 'Name', render: (role) => ( {role.name} ), }, { key: 'code', label: 'Code', render: (role) => ( {role.code} ), }, { key: 'scope', label: 'Scope', render: (role) => ( {role.scope} ), }, { key: 'description', label: 'Description', render: (role) => ( {role.description || 'N/A'} ), }, { key: 'is_system', label: 'System Role', render: (role) => ( {role.is_system ? 'Yes' : 'No'} ), }, { key: 'created_at', label: 'Created Date', render: (role) => ( {formatDate(role.created_at)} ), }, { key: 'actions', label: 'Actions', align: 'right', render: (role) => (
handleViewRole(role.id)} onEdit={() => handleEditRole(role.id, role.name)} onDelete={() => handleDeleteRole(role.id, role.name)} />
), }, ]; // Mobile card renderer const mobileCardRenderer = (role: Role) => (

{role.name}

{role.code}

handleViewRole(role.id)} onEdit={() => handleEditRole(role.id, role.name)} onDelete={() => handleDeleteRole(role.id, role.name)} />
Scope:
{role.scope}
Created:

{formatDate(role.created_at)}

{role.description && (
Description:

{role.description}

)}
); if (compact) { // Compact mode for tabs return ( <>

Roles

{ setScopeFilter(Array.isArray(value) ? null : value || null); setCurrentPage(1); }} placeholder="Filter by scope" /> setIsModalOpen(true)} > New Role
role.id} mobileCardRenderer={mobileCardRenderer} emptyMessage="No roles found" isLoading={isLoading} error={error} /> {pagination.totalPages > 1 && ( { setCurrentPage(page); }} onLimitChange={(newLimit: number) => { setLimit(newLimit); setCurrentPage(1); }} /> )}
{/* Modals */} setIsModalOpen(false)} onSubmit={handleCreateRole} isLoading={isCreating} defaultTenantId={tenantId || undefined} /> { setViewModalOpen(false); setSelectedRoleId(null); }} roleId={selectedRoleId} onLoadRole={loadRole} /> { setEditModalOpen(false); setSelectedRoleId(null); setSelectedRoleName(''); }} roleId={selectedRoleId} onLoadRole={loadRole} onSubmit={handleUpdateRole} isLoading={isUpdating} defaultTenantId={tenantId || undefined} /> { setDeleteModalOpen(false); setSelectedRoleId(null); setSelectedRoleName(''); }} onConfirm={handleConfirmDelete} title="Delete Role" message={`Are you sure you want to delete this role`} itemName={selectedRoleName} isLoading={isDeleting} /> ); } // Full mode with header return ( <> {/* Table Container */}
{/* Table Header with Filters */} {showHeader && (
{/* Filters */}
{/* Scope Filter */} { setScopeFilter(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 Role Button */} setIsModalOpen(true)} > New Role
)} {/* Data Table */} role.id} mobileCardRenderer={mobileCardRenderer} emptyMessage="No roles found" isLoading={isLoading} error={error} /> {/* Table Footer with Pagination */} {pagination.total > 0 && ( { setCurrentPage(page); }} onLimitChange={(newLimit: number) => { setLimit(newLimit); setCurrentPage(1); }} /> )}
{/* Modals */} setIsModalOpen(false)} onSubmit={handleCreateRole} isLoading={isCreating} defaultTenantId={tenantId || undefined} /> { setViewModalOpen(false); setSelectedRoleId(null); }} roleId={selectedRoleId} onLoadRole={loadRole} /> { setEditModalOpen(false); setSelectedRoleId(null); setSelectedRoleName(''); }} roleId={selectedRoleId} onLoadRole={loadRole} onSubmit={handleUpdateRole} isLoading={isUpdating} defaultTenantId={tenantId || undefined} /> { setDeleteModalOpen(false); setSelectedRoleId(null); setSelectedRoleName(''); }} onConfirm={handleConfirmDelete} title="Delete Role" message={`Are you sure you want to delete this role`} itemName={selectedRoleName} isLoading={isDeleting} /> ); };