feat(B3): add tenant management UI (CRUD + tenant selector)

- frontend/src/api/tenants.ts: Tenant CRUD API client (getTenants, getTenant, createTenant, updateTenant, deleteTenant)
- frontend/src/pages/Tenants.tsx: Admin page with table, create/edit modals, delete confirm, and cross-tenant selector persisted in localStorage
- App.tsx: /tenants route (AdminRoute-guarded)
- Layout.tsx: Tenants sidebar link (admin-only, Building2 icon)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 16:13:26 +01:00
parent 995339959e
commit 82bf46725b
4 changed files with 514 additions and 1 deletions
+46
View File
@@ -0,0 +1,46 @@
import api from './client'
export interface Tenant {
id: string
name: string
slug: string
is_active: boolean
user_count?: number
created_at: string
}
export interface TenantCreate {
name: string
slug: string
is_active?: boolean
}
export interface TenantUpdate {
name?: string
slug?: string
is_active?: boolean
}
export async function getTenants(): Promise<Tenant[]> {
const res = await api.get<Tenant[]>('/tenants')
return res.data
}
export async function getTenant(id: string): Promise<Tenant> {
const res = await api.get<Tenant>(`/tenants/${id}`)
return res.data
}
export async function createTenant(data: TenantCreate): Promise<Tenant> {
const res = await api.post<Tenant>('/tenants', data)
return res.data
}
export async function updateTenant(id: string, data: TenantUpdate): Promise<Tenant> {
const res = await api.patch<Tenant>(`/tenants/${id}`, data)
return res.data
}
export async function deleteTenant(id: string): Promise<void> {
await api.delete(`/tenants/${id}`)
}