import { useId, type ReactNode } from "react"; import type { AsyncState } from "../../application/view-models/async-state.ts"; import type { AppFailure } from "../../contracts/errors.ts"; import { useLocale } from "../i18n/index.ts"; import { errorMessage } from "./error-copy.ts"; import { Button } from "./ui/button.ts"; export function LoadingSurface({ label }: Readonly<{ label?: string }>) { const { message } = useLocale(); const accessibleLabel = label ?? message("async.loading"); return (
); } export type EmptySurfaceProps = Readonly<{ title?: string; description?: string; action?: ReactNode; }>; export function EmptySurface({ title, description, action, }: EmptySurfaceProps) { const { message } = useLocale(); return (

{title ?? message("async.empty")}

{description ?

{description}

: null} {action}
); } export type TerminalErrorSurfaceProps = Readonly<{ userMessageKey: string; action: AppFailure["action"]; onAction?: () => void; }>; export function TerminalErrorSurface({ userMessageKey, action, onAction, }: TerminalErrorSurfaceProps) { const { locale, message } = useLocale(); const messageId = useId(); const actionLabels: Readonly< Record, string> > = Object.freeze({ retry: message("action.retry"), reauth: message("action.reauth"), navigate: message("action.navigateSafe"), "reload-once": message("action.reloadOnce"), "contact-support": message("action.contactSupport"), }); return (

{errorMessage(userMessageKey, locale)}

{action !== "none" && onAction && ( )}
); } export type AsyncSurfaceProps = Readonly<{ state: AsyncState; children?: ReactNode; onAction?: () => void; onRetry?: () => void; onResolveConflict?: () => void; onReconcileUnknownEffect?: ( resolution: "APPLIED" | "NOT_APPLIED", ) => void; }>; export function AsyncSurface({ state, children, onAction, onRetry, onResolveConflict, onReconcileUnknownEffect, }: AsyncSurfaceProps) { const { message } = useLocale(); if (state.base === "initial-loading") return ; if (state.base === "empty") return ; if (state.base === "terminal-error" && state.failure) { return ( ); } return (
{state.indicator ? (
{message( state.indicator === "stale-degraded" ? "async.staleDegraded" : state.indicator === "mutation-effect-unknown" ? "async.mutationEffectUnknown" : state.indicator === "mutation-conflict" ? "async.mutationConflict" : state.indicator === "mutation-pending" ? "async.mutationPending" : "async.refreshing", )} {state.indicator === "stale-degraded" && onRetry ? ( ) : null} {state.indicator === "mutation-conflict" && onResolveConflict ? ( ) : null} {state.indicator === "mutation-effect-unknown" && onReconcileUnknownEffect ? ( <> ) : null}
) : null} {children}
); }