'use client'; import { useCallback, useEffect, useState } from 'react'; import { useTranslations } from 'next-intl'; import { useAuthStore } from '@/lib/stores/auth-store'; const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'; interface Tenant { id: string; name: string; slug: string; isActive: boolean; createdAt: string; userCount: number; } interface TenantFormData { name: string; slug: string; } /** * Tenant management page -- SUPER_ADMIN only (D-10). */ export default function AdminTenantsPage() { const t = useTranslations('admin.tenants'); const tCommon = useTranslations('common'); const currentUser = useAuthStore((s) => s.user); const [tenants, setTenants] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingTenant, setEditingTenant] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [formData, setFormData] = useState({ name: '', slug: '', }); // Access check: only SUPER_ADMIN const hasAccess = currentUser?.role === 'SUPER_ADMIN'; const fetchTenants = useCallback(async () => { try { const res = await fetch(`${API_URL}/tenants`, { credentials: 'include', }); if (res.ok) { setTenants(await res.json()); } } catch { // silently fail } finally { setLoading(false); } }, []); useEffect(() => { if (hasAccess) { fetchTenants(); } else { setLoading(false); } }, [hasAccess, fetchTenants]); const openCreate = () => { setEditingTenant(null); setFormData({ name: '', slug: '' }); setShowForm(true); }; const openEdit = (tenant: Tenant) => { setEditingTenant(tenant); setFormData({ name: tenant.name, slug: tenant.slug }); setShowForm(true); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const url = editingTenant ? `${API_URL}/tenants/${editingTenant.id}` : `${API_URL}/tenants`; const method = editingTenant ? 'PATCH' : 'POST'; const body: Record = editingTenant ? { name: formData.name } : { name: formData.name, slug: formData.slug }; try { const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(body), }); if (res.ok) { setShowForm(false); fetchTenants(); } } catch { // silently fail } }; const handleToggleActive = async (tenant: Tenant) => { try { await fetch(`${API_URL}/tenants/${tenant.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ isActive: !tenant.isActive }), }); fetchTenants(); } catch { // silently fail } }; const handleDelete = async (id: string) => { try { const res = await fetch(`${API_URL}/tenants/${id}`, { method: 'DELETE', credentials: 'include', }); if (res.ok) { setDeleteConfirm(null); fetchTenants(); } } catch { // silently fail } }; if (!hasAccess) { return (

{tCommon('accessDenied')}

); } return (
{/* Page header */}

{t('title')}

{/* Tenants table */} {loading ? (

{tCommon('loading')}

) : tenants.length === 0 ? (

{t('noTenants')}

) : (
{tenants.map((tenant) => ( ))}
{t('name')} {t('slug')} {t('status')} {t('userCount')} {t('actions')}
{tenant.name} {tenant.slug} {tenant.isActive ? tCommon('active') : tCommon('inactive')} {tenant.userCount}
)} {/* Create/Edit modal */} {showForm && (

{editingTenant ? t('edit') : t('create')}

setFormData({ ...formData, name: e.target.value }) } className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
{!editingTenant && (
setFormData({ ...formData, slug: e.target.value }) } className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm font-mono" placeholder="my-tenant" />
)}
)} {/* Delete confirmation modal */} {deleteConfirm && (

{t('deleteConfirm')}

)}
); }