import { useState, useEffect } from 'react'; import type { ReactElement } from 'react'; import { Layout } from '@/components/layout/Layout'; import { PrimaryButton, StatusBadge, ActionDropdown, // NewTenantModal, // Commented out - using wizard instead // ViewTenantModal, // Commented out - using details page instead EditTenantModal, DeleteConfirmationModal, DataTable, Pagination, FilterDropdown, type Column, } from '@/components/shared'; import { Plus, Download, ArrowUpDown } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { tenantService } from '@/services/tenant-service'; import type { Tenant } from '@/types/tenant'; import { showToast } from '@/utils/toast'; // Helper function to get tenant initials const getTenantInitials = (name: string): string => { const words = name.trim().split(/\s+/); if (words.length >= 2) { return `${words[0][0]}${words[1][0]}`.toUpperCase(); } return name.substring(0, 2).toUpperCase(); }; // 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): 'success' | 'failure' | 'process' => { switch (status.toLowerCase()) { case 'active': return 'success'; case 'deleted': return 'failure'; case 'suspended': return 'process'; default: return 'success'; } }; // Helper function to format subscription tier const formatSubscriptionTier = (tier: string | null): string => { if (!tier) return 'N/A'; return tier.charAt(0).toUpperCase() + tier.slice(1); }; const Tenants = (): ReactElement => { const navigate = useNavigate(); const [tenants, setTenants] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // const [isModalOpen, setIsModalOpen] = useState(false); // Commented out - using wizard instead // const [isCreating, setIsCreating] = useState(false); // Commented out - using wizard instead // 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); // View, Edit, Delete modals // const [viewModalOpen, setViewModalOpen] = useState(false); // Commented out - using details page instead const [editModalOpen, setEditModalOpen] = useState(false); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [selectedTenantId, setSelectedTenantId] = useState(null); const [selectedTenantName, setSelectedTenantName] = useState(''); const [isUpdating, setIsUpdating] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const fetchTenants = async ( page: number, itemsPerPage: number, status: string | null = null, sortBy: string[] | null = null ): Promise => { try { setIsLoading(true); setError(null); const response = await tenantService.getAll(page, itemsPerPage, status, sortBy); if (response.success) { setTenants(response.data); setPagination(response.pagination); } else { setError('Failed to load tenants'); } } catch (err: any) { setError(err?.response?.data?.error?.message || 'Failed to load tenants'); } finally { setIsLoading(false); } }; useEffect(() => { fetchTenants(currentPage, limit, statusFilter, orderBy); }, [currentPage, limit, statusFilter, orderBy]); // Commented out - using wizard instead // const handleCreateTenant = async (data: { // name: string; // slug: string; // status: 'active' | 'suspended' | 'deleted'; // settings?: Record | null; // subscription_tier?: string | null; // max_users?: number | null; // max_modules?: number | null; // }): Promise => { // try { // setIsCreating(true); // const response = await tenantService.create(data); // const message = response.message || `Tenant created successfully`; // const description = response.message ? undefined : `${data.name} has been added`; // showToast.success(message, description); // // Close modal and refresh tenant list // setIsModalOpen(false); // await fetchTenants(currentPage, limit, statusFilter, orderBy); // } catch (err: any) { // throw err; // Let the modal handle the error display // } finally { // setIsCreating(false); // } // }; // View tenant handler const handleViewTenant = (tenantId: string): void => { navigate(`/tenants/${tenantId}`); }; // Edit tenant handler const handleEditTenant = (tenantId: string, tenantName: string): void => { setSelectedTenantId(tenantId); setSelectedTenantName(tenantName); setEditModalOpen(true); }; // Update tenant handler const handleUpdateTenant = async ( id: string, data: { name: string; slug: string; status: 'active' | 'suspended' | 'deleted'; settings?: Record | null; subscription_tier?: string | null; max_users?: number | null; max_modules?: number | null; } ): Promise => { try { setIsUpdating(true); const response = await tenantService.update(id, data); const message = response.message || `Tenant updated successfully`; const description = response.message ? undefined : `${data.name} has been updated`; showToast.success(message, description); setEditModalOpen(false); setSelectedTenantId(null); await fetchTenants(currentPage, limit, statusFilter, orderBy); } catch (err: any) { throw err; // Let the modal handle the error display } finally { setIsUpdating(false); } }; // Delete tenant handler const handleDeleteTenant = (tenantId: string, tenantName: string): void => { setSelectedTenantId(tenantId); setSelectedTenantName(tenantName); setDeleteModalOpen(true); }; // Confirm delete handler const handleConfirmDelete = async (): Promise => { if (!selectedTenantId) return; try { setIsDeleting(true); await tenantService.delete(selectedTenantId); setDeleteModalOpen(false); setSelectedTenantId(null); setSelectedTenantName(''); await fetchTenants(currentPage, limit, statusFilter, orderBy); } catch (err: any) { throw err; // Let the modal handle the error display } finally { setIsDeleting(false); } }; // Load tenant for view/edit const loadTenant = async (id: string): Promise => { const response = await tenantService.getById(id); return response.data; }; // Define table columns const columns: Column[] = [ { key: 'name', label: 'Tenant Name', render: (tenant) => (
{getTenantInitials(tenant.name)}
{tenant.name}
), mobileLabel: 'Name', }, { key: 'status', label: 'Status', render: (tenant) => ( {tenant.status} ), }, { key: 'max_users', label: 'Users', render: (tenant) => ( {tenant.max_users ?? 'N/A'} ), }, { key: 'subscription_tier', label: 'Plan', render: (tenant) => ( {formatSubscriptionTier(tenant.subscription_tier)} ), }, { key: 'max_modules', label: 'Modules', render: (tenant) => ( {tenant.max_modules ?? 'N/A'} ), }, { key: 'created_at', label: 'Joined Date', render: (tenant) => ( {formatDate(tenant.created_at)} ), mobileLabel: 'Joined', }, { key: 'actions', label: 'Actions', align: 'right', render: (tenant) => (
handleViewTenant(tenant.id)} onEdit={() => handleEditTenant(tenant.id, tenant.name)} onDelete={() => handleDeleteTenant(tenant.id, tenant.name)} />
), }, ]; // Mobile card renderer const mobileCardRenderer = (tenant: Tenant) => (
{getTenantInitials(tenant.name)}

{tenant.name}

{formatDate(tenant.created_at)}

handleViewTenant(tenant.id)} onEdit={() => handleEditTenant(tenant.id, tenant.name)} onDelete={() => handleDeleteTenant(tenant.id, tenant.name)} />
Status:
{tenant.status}
Plan:

{formatSubscriptionTier(tenant.subscription_tier)}

Users:

{tenant.max_users ?? 'N/A'}

Modules:

{tenant.max_modules ?? 'N/A'}

); return ( {/* Table Container */}
{/* Table Header with Filters */}
{/* Filters */}
{/* Status Filter */} { setStatusFilter(value as string | null); setCurrentPage(1); // Reset to first page when filter changes }} placeholder="All" /> {/* Sort Filter */} { setOrderBy(value as string[] | null); setCurrentPage(1); // Reset to first page when sort changes }} placeholder="Default" showIcon icon={} />
{/* Actions */}
{/* Export Button */} {/* New Tenant Button (Old) - Commented out, using wizard instead */} {/* setIsModalOpen(true)} > New Tenant */} {/* Add Tenant Button (New Wizard) */} navigate('/tenants/create-wizard')} > Add Tenant
{/* Data Table */} tenant.id} mobileCardRenderer={mobileCardRenderer} emptyMessage="No tenants found" isLoading={isLoading} error={error} /> {/* Table Footer with Pagination */} {pagination.total > 0 && ( { setCurrentPage(page); }} onLimitChange={(newLimit: number) => { setLimit(newLimit); setCurrentPage(1); // Reset to first page when limit changes }} /> )}
{/* New Tenant Modal - Commented out, using wizard instead */} {/* setIsModalOpen(false)} onSubmit={handleCreateTenant} isLoading={isCreating} /> */} {/* View Tenant Modal - Commented out, using details page instead */} {/* { setViewModalOpen(false); setSelectedTenantId(null); }} tenantId={selectedTenantId} onLoadTenant={loadTenant} /> */} {/* Edit Tenant Modal */} { setEditModalOpen(false); setSelectedTenantId(null); setSelectedTenantName(''); }} tenantId={selectedTenantId} onLoadTenant={loadTenant} onSubmit={handleUpdateTenant} isLoading={isUpdating} /> {/* Delete Confirmation Modal */} { setDeleteModalOpen(false); setSelectedTenantId(null); setSelectedTenantName(''); }} onConfirm={handleConfirmDelete} title="Delete Tenant" message="Are you sure you want to delete this tenant" itemName={selectedTenantName} isLoading={isDeleting} />
); }; export default Tenants;