import { ArrowLeft, ArrowLeftRight, ExternalLink, Flame, RefreshCw, ScrollText, Settings2, Trash2, Undo2, UserRound } from "lucide-react"; import { useEffect, useState, type ReactNode } from "react"; import { api, errorMessage } from "../api"; import { ActionButton } from "../components/ActionButton"; import { ChannelPicker, UserPicker } from "../components/EntityPicker"; import { Alert, Badge, EmptyRow, LoadingSurface, PageFrame, SectionHead, Summary } from "../components/ui"; import { displayUsername, formatCurrency, formatDate } from "../lib/format"; import type { Navigate } from "../routing"; import type { AccountRow, ChannelRow, CollectibleUsernameDetail, CollectibleUsernameTransferKind } from "../types"; import { UsernameStatus, ownerLabel, priceLabel } from "./CollectibleUsernamesPage"; type RecipientKind = "user" | "channel"; type Tab = "profile" | "actions"; export function CollectibleUsernameDetailPage({ id, navigate }: { id: string; navigate: Navigate }) { const [detail, setDetail] = useState(null); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const [tab, setTab] = useState("profile"); const [recipientKind, setRecipientKind] = useState("user"); const [recipientUser, setRecipientUser] = useState(null); const [recipientChannel, setRecipientChannel] = useState(null); async function load() { setBusy(true); setError(""); try { setDetail(await api.collectibleUsername(id)); } catch (err) { setError(errorMessage(err)); } finally { setBusy(false); } } useEffect(() => { void load(); setTab("profile"); // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]); if (error && !detail) { return {error}; } if (!detail) { return ; } const asset = detail.asset; const transfers = detail.transfers ?? []; const vaultLabel = "Vault"; const hasOwner = Boolean(asset.OwnerPeerType) && asset.OwnerPeerID !== "" && asset.OwnerPeerID !== "0"; const burned = asset.Status === "burned"; function openOwner() { if (!hasOwner) return; navigate(asset.OwnerPeerType === "channel" ? `/channels/${asset.OwnerPeerID}` : `/accounts/${asset.OwnerPeerID}`); } // Peer ids travel as decimal strings to match the backend `,string` tags. function transferPayload(): Record { const payload: Record = { username: asset.Username }; if (recipientKind === "user" && recipientUser) payload.to_user_id = String(recipientUser.ID); if (recipientKind === "channel" && recipientChannel) payload.to_channel_id = String(recipientChannel.ID); return payload; } const tabs: Array<{ key: Tab; label: string; icon: ReactNode }> = [ { key: "profile", label: "Profile & Status", icon: }, { key: "actions", label: "Actions & Management", icon: } ]; return ( } > {error && {error}}
{displayUsername(asset.Username)}
{`Asset #${asset.ID}`}
0 ? "warn" : "neutral"}>{`${asset.TransferCount} transfers`} {asset.Status === "owned" && ( {asset.RegistryActive ? "Active in profile" : "Hidden in profile"} )}
{tabs.map((item) => ( ))}
{tab === "profile" && (
{hasOwner && ( )} {asset.URL && ( {"Open marketplace page"} )}
} />
{transfers.map((row) => ( ))} {transfers.length === 0 && }
{"ID"} {"Event"} {"From"} {"To"} {"Price"} {"Actor"} {"Reason"} {"Time"}
{row.ID} {peerLabel(row.FromPeerType, row.FromPeerID, vaultLabel, row.FromUsername)} {peerLabel(row.ToPeerType, row.ToPeerID, vaultLabel, row.ToUsername)} {row.Amount && row.Amount !== "0" ? formatCurrency(row.Amount, row.Currency) : "-"} {row.Actor || "-"} {row.Reason || "-"} {formatDate(row.CreatedAt) || "-"}
)} {tab === "actions" && (
{burned ? (

{"This username is burned — no further operations are possible."}

) : (
{recipientKind === "user" ? : } } tone="warn" path="/api/actions/transfer-collectible-username" payload={transferPayload} onDone={load} />
} tone="warn" path="/api/actions/revoke-collectible-username" payload={() => ({ username: asset.Username, burn: false })} onDone={load} />
} tone="danger" path="/api/actions/revoke-collectible-username" payload={() => ({ username: asset.Username, burn: true })} onDone={load} />

{"Irreversible: the username is destroyed and can never be issued again."}

} tone="danger" path="/api/actions/delete-collectible-username" payload={() => ({ username: asset.Username })} onDone={() => navigate("/collectible-usernames")} />

{"Erases the asset and its ownership history, and frees the username for a fresh issue. Use this for a username issued by mistake; a burn keeps the history instead."}

)}
)}
); } const usernameKindLabels: Record = { mint: "Mint", transfer: "Transfer", burn: "Burn", revoke: "Revoke" }; function TransferKind({ kind }: { kind: CollectibleUsernameTransferKind }) { const tone = kind === "burn" ? "danger" : kind === "revoke" ? "warn" : kind === "mint" ? "good" : "neutral"; return {usernameKindLabels[kind]}; } function peerLabel(type: string, peerID: string, vaultLabel: string, username = ""): string { if (!type || peerID === "" || peerID === "0") return vaultLabel; const handle = displayUsername(username); return handle ? `${handle} · ${type}:${peerID}` : `${type}:${peerID}`; }