// SPDX-FileCopyrightText: 2026 Progmasoft // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 "use client"; import { useEffect, useState } from "react"; import { callOAuthApi, type ApplicationList } from "@/lib/credentials"; import { format, getCredentialsMessages } from "@/lib/credentials-messages"; import { AccountSignInNotice } from "./AccountSignInNotice"; import type { Locale } from "@/lib/localization"; type ListState = | { kind: "loading" } | { kind: "signedOut" } | { kind: "failed" } | { kind: "ready"; list: ApplicationList }; /** The OAuth 2.1 tokens of the signed-in account, with the New token button. */ export function TokenList({ locale }: { locale: Locale }) { const messages = getCredentialsMessages(locale); const { portal, common, kinds, details } = messages; const [state, setState] = useState({ kind: "loading" }); useEffect(() => { const controller = new AbortController(); void callOAuthApi("/applications", { signal: controller.signal, }).then((result) => { if (result.kind === "signedOut") { setState({ kind: "signedOut" }); } else if (result.kind === "ok") { setState({ kind: "ready", list: result.value }); } else { setState({ kind: "failed" }); } }); return () => controller.abort(); }, []); if (state.kind === "loading") { return

{common.loading}

; } if (state.kind === "signedOut") { return ; } if (state.kind === "failed") { return (
{common.unreachable}
); } const { applications, maximum } = state.list; return ( <>
{applications.length < maximum ? ( {portal.newToken} ) : (

{format(portal.limit, { count: maximum })}

)}
{applications.length === 0 ? (

{portal.empty}

) : (
    {applications.map((application) => (
  • {application.displayName} {kinds[application.kind].title} {application.clientId} {format(details.created, { date: new Date(application.createdAt).toLocaleDateString( locale, ), })}
  • ))}
)}

{portal.endpoints}{" "} /.well-known/openid-configuration

); }