ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
"use client"
// "Delete my account": the person types the account's e-mail address and confirms, from a session
// whose sign-in is recent (the server checks both; this form only says so first). The answer is the
// deletion's state: completed, or erasing while a module's service is unavailable -- in both cases the
// person is signed out everywhere, and the app's browser modules clear what they kept (`onDeleted`).
import { useTransport } from "@conusai-kit/ui"
import { type FormEvent, type ReactElement, useId, useState } from "react"
import type { DeletionView } from "../contract/procedures.js"
export interface AccountDeletionLabels {
readonly heading: string
readonly body: string
/** The services whose data is erased, already named; empty when there are none besides the app. */
readonly services: string
/** Set when the person is the last owner of an organization others belong to: the form is closed. */
readonly lastOwner?: string
/** Set when an organization would be left without members. */
readonly onlyMember?: string
readonly reauth: string
readonly reauthLink: string
readonly notInteractive: string
readonly confirmLabel: string
readonly confirmHint: string
readonly submit: string
readonly busy: string
readonly completed: string
/** With `{modules}` still in it. */
readonly erasing: string
readonly home: string
/** The app's `app.failed` message, with `{detail}` still in it. */
readonly failed: string
readonly sectionNames: Readonly<Record<string, string>>
}
export interface AccountDeletionProps {
readonly labels: AccountDeletionLabels
/** Whether the session may delete now: a person, signed in recently, owning no organization alone. */
readonly interactive: boolean
readonly recentSignIn: boolean
/** Where a person signs in again and comes back to this page. */
readonly reauthHref: string
/** A deletion already under way, when the page was rendered. */
readonly deletion: DeletionView | null
/** Runs once the deletion answered, before the result is shown: the app ends its browser modules' context. */
readonly onDeleted?: (deletion: DeletionView) => Promise<unknown> | unknown
}
type State =
| { readonly kind: "idle" | "busy" }
| { readonly kind: "deleted"; readonly deletion: DeletionView }
| { readonly kind: "failed"; readonly detail: string; readonly code: string }
const codeOf = (error: unknown): string => {
const problem = (error as { problem?: { code?: unknown } } | null)?.problem
return typeof problem?.code === "string" ? problem.code : "unknown"
}
export function AccountDeletionPanel(props: AccountDeletionProps): ReactElement {
const { labels } = props
const { call, describe } = useTransport()
const headingId = useId()
const inputId = useId()
const hintId = useId()
const [state, setState] = useState<State>(
props.deletion ? { kind: "deleted", deletion: props.deletion } : { kind: "idle" },
)
const submit = async (event: FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault()
const confirm = String(new FormData(event.currentTarget).get("confirm") ?? "").trim()
if (confirm.length === 0) return
setState({ kind: "busy" })
try {
const deletion = (await call("account.deletion.request", { confirm })) as DeletionView
// The session is revoked now: what the browser modules kept for it goes too (contract §12).
await Promise.resolve(props.onDeleted?.(deletion)).catch(() => undefined)
setState({ kind: "deleted", deletion })
} catch (error) {
setState({ kind: "failed", detail: describe(error), code: codeOf(error) })
}
}
const reauth = (
<p role="status">
{labels.reauth} <a href={props.reauthHref}>{labels.reauthLink}</a>
</p>
)
if (state.kind === "deleted") {
const pending = state.deletion.pending.map((key) => labels.sectionNames[key] ?? key).join(", ")
return (
<section aria-labelledby={headingId}>
<h2 id={headingId}>{labels.heading}</h2>
<p role="status">
{state.deletion.state === "completed" ? labels.completed : labels.erasing.replace("{modules}", pending)}
</p>
<a href="/">{labels.home}</a>
</section>
)
}
const blocked = !props.interactive || labels.lastOwner !== undefined
return (
<section aria-labelledby={headingId}>
<h2 id={headingId}>{labels.heading}</h2>
<p>{labels.body}</p>
{labels.services ? <p>{labels.services}</p> : null}
{labels.onlyMember ? <p>{labels.onlyMember}</p> : null}
{labels.lastOwner ? <p role="status">{labels.lastOwner}</p> : null}
{!props.interactive ? <p role="status">{labels.notInteractive}</p> : null}
{props.interactive && !props.recentSignIn ? reauth : null}
{blocked ? null : (
<form onSubmit={submit} aria-busy={state.kind === "busy"}>
<div className="kit-field">
<label htmlFor={inputId}>{labels.confirmLabel}</label>
<input
id={inputId}
name="confirm"
type="email"
required
autoComplete="off"
spellCheck={false}
aria-describedby={hintId}
/>
<p id={hintId}>{labels.confirmHint}</p>
</div>
<button type="submit" disabled={state.kind === "busy"}>
{state.kind === "busy" ? labels.busy : labels.submit}
</button>
</form>
)}
{state.kind === "failed" && state.code === "reauthentication_required" ? reauth : null}
{state.kind === "failed" && state.code !== "reauthentication_required" ? (
<p role="alert" className="app-error">
{labels.failed.replace("{detail}", state.detail)}
</p>
) : null}
</section>
)
}