Files
arcadia-admin/app/lib/arcadia/tenants.ts
jules 4b817b85ff Wire operator Integrations page + capability-gating framework
Completes the arcadia-admin operator surface for the integration registry and
the capability/route-guard framework it depends on.

- Integration registry: route + Data-group nav entry + `platform.integrations`
  capability; the in-app client now delegates to the shared
  `@crema/integration-registry-client` lib (vite alias + tsconfig); the
  operator Integrations page (committed earlier) is now reachable.
- Capability gating: capabilities map + route-guard + jwt helpers + the
  apps/plan/entitlements routes and supporting tenants/session changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:09:24 +10:00

114 lines
3.2 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
}
export interface ProvisionTenantInput {
tenant: { name: string; slug: string }
admin_user: {
email: string
password: string
first_name: string
last_name: string
}
}
export async function provisionTenant(
arcadia: ArcadiaClient,
input: ProvisionTenantInput,
): Promise<Tenant> {
const res = await arcadia.POST<{ data: Tenant }>("/api/v1/admin/tenants/provision", {
body: input,
})
return res.data
}