Files
arcadia-admin/app/lib/arcadia/profiles.ts
jules ab116f8465 refactor: rename @crema/arcadia-client → @crema/arcadia-core-client
Disambiguates the Phoenix/auth client lib from lib-arcadia-agents-client.
Dir lib-arcadia-client → lib-arcadia-core-client; alias updated in
tsconfig paths, vite config, app.css @source, imports, CI and docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:31:56 +10:00

80 lines
2.4 KiB
TypeScript

// Arcadia profile API. Backed by /api/v1/profile (current user) — handles
// avatar wiring (avatar_digital_object_id + variant URLs) and the basic
// profile fields. The "profile" here is the per-tenant profile row, not
// the auth account.
import type { ArcadiaClient } from "@crema/arcadia-core-client"
export interface Profile {
id: string
user_id: string
tenant_id: string
avatar_url: string | null
avatar_digital_object_id: string | null
/**
* Variant URLs keyed by size (e.g. "thumbnail", "medium", "original").
* Shape depends on the storage backend; treat as best-effort.
*/
avatar_urls?: Record<string, string> | null
bio?: string | null
phone?: string | null
location?: string | null
timezone?: string | null
inserted_at?: string
updated_at?: string
}
export interface ProfileUpdateInput {
avatar_digital_object_id?: string | null
bio?: string | null
phone?: string | null
location?: string | null
timezone?: string | null
}
export async function getProfile(arcadia: ArcadiaClient): Promise<Profile> {
const res = await arcadia.GET<{ data: Profile } | Profile>("/api/v1/profile")
return "data" in (res as object)
? (res as { data: Profile }).data
: (res as Profile)
}
export async function updateProfile(
arcadia: ArcadiaClient,
input: ProfileUpdateInput,
): Promise<Profile> {
const res = await arcadia.PATCH<{ data: Profile } | Profile>("/api/v1/profile", {
body: { profile: input },
})
return "data" in (res as object)
? (res as { data: Profile }).data
: (res as Profile)
}
/**
* Pick the most appropriate avatar URL from a profile. Backend returns
* `avatar_urls = {small, medium, large, original}` keyed by size. The
* variants are populated async after image processing completes —
* before that, all four are `null` and we fall back to the legacy
* `avatar_url` string column (which is also usually null when uploads
* use the digital_object pipeline).
*
* Returns null when nothing is ready; caller should fall back to
* fetching the raw content as a blob URL.
*/
export function pickAvatarUrl(profile: Profile | null | undefined): string | null {
if (!profile) return null
const variants = profile.avatar_urls
if (variants && typeof variants === "object") {
return (
variants.small ||
variants.medium ||
variants.large ||
variants.original ||
profile.avatar_url ||
null
)
}
return profile.avatar_url ?? null
}