Adds the first admin screen — /tenants — listing tenants from GET /api/v1/admin/tenants with search, status badges, plan, created date, and a per-row menu with suspend / activate / deactivate actions. Hand-typed shapes in app/lib/arcadia/tenants.ts because arcadia's admin endpoints aren't yet covered by /api/openapi (same 'ok'-placeholder issue documented in lib-arcadia-client/scripts/sync-spec.mjs). When the spec gains coverage, switch to arcadia.typed.GET(...) and drop the manual types. Trims the inherited consumer-app sidenav (Resources / Assistant / AI / Library) down to admin-shaped items: Overview, Tenants, Audit log, Settings. The unused route files stay in place; they just aren't linked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
2.7 KiB
TypeScript
94 lines
2.7 KiB
TypeScript
// Arcadia tenants API helpers.
|
|
//
|
|
// Hand-rolled because /api/v1/admin/tenants isn't covered by arcadia's
|
|
// OpenAPI spec (controller hasn't been wired into OpenApiSpex yet — same
|
|
// "ok"-placeholder issue as some other admin endpoints). When the spec
|
|
// gains coverage, switch to `arcadia.typed.GET("/api/v1/admin/tenants", ...)`
|
|
// and drop these manual types.
|
|
|
|
import type { ArcadiaClient } from "@crema/arcadia-client"
|
|
|
|
export type TenantStatus = "active" | "suspended" | "deactivated" | string
|
|
|
|
export interface TenantPlan {
|
|
name: string
|
|
limits: Record<string, unknown>
|
|
}
|
|
|
|
export interface TenantBranding {
|
|
logo_url: string | null
|
|
favicon_url: string | null
|
|
primary_color: string | null
|
|
secondary_color: string | null
|
|
accent_color: string | null
|
|
custom_css: string | null
|
|
settings: Record<string, unknown>
|
|
}
|
|
|
|
export interface TenantSettings {
|
|
timezone?: string
|
|
currency?: string
|
|
[key: string]: unknown
|
|
}
|
|
|
|
export interface TenantLocalization {
|
|
locale: string
|
|
timezone: string
|
|
currency: string
|
|
settings: Record<string, unknown>
|
|
}
|
|
|
|
export interface Tenant {
|
|
id: string
|
|
slug: string
|
|
name: string
|
|
status: TenantStatus
|
|
plan: TenantPlan
|
|
branding: TenantBranding
|
|
settings: TenantSettings
|
|
localization: TenantLocalization
|
|
email_settings: Record<string, unknown>
|
|
notification_settings: Record<string, unknown>
|
|
metadata: Record<string, unknown>
|
|
inserted_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface TenantListParams {
|
|
q?: string
|
|
status?: TenantStatus
|
|
page?: number
|
|
per_page?: number
|
|
}
|
|
|
|
export async function listTenants(
|
|
arcadia: ArcadiaClient,
|
|
params?: TenantListParams,
|
|
): Promise<Tenant[]> {
|
|
const queryParams: Record<string, string | number | boolean | null | undefined> | undefined = params
|
|
? { q: params.q, status: params.status, page: params.page, per_page: params.per_page }
|
|
: undefined
|
|
const res = await arcadia.GET<{ data: Tenant[] }>("/api/v1/admin/tenants", { params: queryParams })
|
|
return res.data
|
|
}
|
|
|
|
export async function getTenant(arcadia: ArcadiaClient, id: string): Promise<Tenant> {
|
|
const res = await arcadia.GET<{ data: Tenant }>(`/api/v1/admin/tenants/${id}`)
|
|
return res.data
|
|
}
|
|
|
|
export async function suspendTenant(arcadia: ArcadiaClient, id: string): Promise<Tenant> {
|
|
const res = await arcadia.POST<{ data: Tenant }>(`/api/v1/admin/tenants/${id}/suspend`)
|
|
return res.data
|
|
}
|
|
|
|
export async function activateTenant(arcadia: ArcadiaClient, id: string): Promise<Tenant> {
|
|
const res = await arcadia.POST<{ data: Tenant }>(`/api/v1/admin/tenants/${id}/activate`)
|
|
return res.data
|
|
}
|
|
|
|
export async function deactivateTenant(arcadia: ArcadiaClient, id: string): Promise<Tenant> {
|
|
const res = await arcadia.POST<{ data: Tenant }>(`/api/v1/admin/tenants/${id}/deactivate`)
|
|
return res.data
|
|
}
|