Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
12 kB · 327 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328import { sameRelease } from "../installation-metadata.ts";import { Button, LinkButton } from "octane-kumo/components/button";import { LayerCard } from "octane-kumo/components/layer-card";import { InstallationProgress } from "./InstallationProgress.tsx";import { installationMessages, type useInstallationStatus,} from "./installation-status.tsx";
type Status = ReturnType<typeof useInstallationStatus>;const labels = { reserved: "Ready to install", installing: "Installing", ready: "Ready", updating: "Updating", failed: "Needs attention",};export function InstallationStatus({ status, accountId, disabled, accounts,}: { status: Status; accountId: string | null; disabled: boolean; accounts: { id: string; name: string }[];}) { const record = status.selected; const active = record?.status === "installing" || record?.status === "updating"; const matched = !!record && record.accountId === accountId; const blocked = disabled || status.busy || !status.ownerSubject; const error = status.error ?? record?.errorCode; const pending = status.pending; const upToDate = !!record?.installedRelease && sameRelease(record.installedRelease, status.latestRelease); const upgradeAvailable = !!record?.installedRelease && status.upgradeFrom.includes(record.installedRelease.artifactDigest); const accountName = (id: string) => accounts.find((a) => a.id === id)?.name ?? id; return ( <section className="installations control-section" id="installations" aria-label="Your installations" tabIndex={-1} > <div className="section-heading"> <div className="intro"> <h2>Your installations</h2> <p>Installation keeps running when you leave this page.</p> </div> <Button variant="secondary" disabled={status.loading || status.busy} onClick={() => void status.refresh()} > Refresh status </Button> </div> {status.loading && !status.refreshedAt && ( <p role="status">Loading installations…</p> )} {!status.loading && !status.ownerSubject && !status.error && ( <p>Connect Cloudflare to view your installations.</p> )} {status.installations.length > 0 && ( <ul className="installation-list" aria-label="Installations"> {status.installations.map((installation) => ( <li key={installation.installationId} data-selected={ record?.installationId === installation.installationId } > <div> <p className="account-name"> {accountName(installation.accountId)} </p> <p className="installation-id"> Installation {installation.installationId.slice(0, 8)} ·{" "} <span className="installation-badge" data-status={installation.status} > {labels[installation.status]} </span> </p> </div> <Button variant="secondary" disabled={status.busy} aria-label={`View installation ${installation.installationId.slice(0, 8)}`} aria-pressed={ record?.installationId === installation.installationId } onClick={() => status.choose(installation)} > {record?.installationId === installation.installationId ? "Viewing" : "View status"} </Button> </li> ))} </ul> )} {(status.cursor || status.nextCursor) && ( <div className="actions"> {status.cursor && ( <Button variant="secondary" disabled={status.loading || status.busy} onClick={() => void status.refresh(null)} > First page </Button> )} {status.nextCursor && ( <Button variant="secondary" disabled={status.loading || status.busy} onClick={() => void status.refresh(status.nextCursor)} > Next page </Button> )} </div> )} {(record || error || pending) && ( <LayerCard className="installation-card" data-status={record?.status}> {record && ( <> <div className="intro installation-intro"> <div className="intro"> <h3> {record.status === "ready" ? "Flarebot is ready" : labels[record.status]} </h3> <p> Installation {record.installationId.slice(0, 8)} in{" "} {accountName(record.accountId)} </p> {record.installedRelease && ( <p> {record.status === "ready" ? "Installed version" : "Last verified version"}{" "} {record.installedRelease.version} </p> )} </div> {record.installedRelease && record.resources.runtimeOrigin && ( <LinkButton variant="primary" href={`${record.resources.runtimeOrigin}/auth/login`} > Open Flarebot </LinkButton> )} </div> <InstallationProgress installation={record} updating={ !!record.installedRelease && record.status !== "ready" } /> {active && ( <p role="status"> Flarebot is working in your Cloudflare account. You can return later to check progress. </p> )} {record.status === "ready" && ( <div className="installation-summary"> <p> {upToDate ? "Up to date" : upgradeAvailable && status.latestRelease ? `Version ${status.latestRelease.version} is available` : status.latestRelease ? "No compatible upgrade is available." : "Release availability could not be checked."} </p> <a className="open-flarebot" href={`/connect?configureDomain=${record.installationId}`} > Choose your address </a> </div> )} {record.status !== "ready" && record.installedRelease && ( <p> Requested version {record.desiredRelease?.version}. An update may already be serving. Recovery checks this version; it does not restore an older version. </p> )} </> )} {error && <p role="alert">{installationMessages[error]}</p>} {pending && ( <p role="status"> Your request is saved {pending.installationId ? ` for installation ${pending.installationId.slice(0, 8)}` : ` to create an installation in ${accountName(pending.accountId)}`} . Continue the same request to confirm the result without creating another installation. </p> )} {record && !matched && record.status !== "ready" && ( <p> Select {accountName(record.accountId)} above to continue this installation. </p> )} <div className="actions"> {pending && ( <Button disabled={blocked || accountId !== pending.accountId} onClick={() => accountId && void status.continuePending(accountId) } > {status.busy ? "Submitting request…" : "Continue saved request"} </Button> )} {!pending && record?.status === "ready" && upgradeAvailable && status.latestRelease && ( <Button disabled={blocked || !matched} onClick={() => accountId && void status.run(accountId, "upgrade", record) } > Upgrade Flarebot </Button> )} {!pending && record && (record.status === "reserved" || record.status === "failed") && ( <Button disabled={blocked || !matched} onClick={() => accountId && void status.run( accountId, record.errorCode === "recovery_required" ? "recover" : record.installedRelease ? "upgrade" : "start", record, ) } > {status.busy ? "Submitting request…" : record.errorCode === "recovery_required" ? "Recover installation" : record.status === "reserved" ? "Continue installation" : "Retry installation"} </Button> )} </div> {active && (!pending || pending.installationId === record.installationId) && (record.status === "installing" || record.status === "updating") && ( <details className="recovery"> <summary>Installation appears interrupted</summary> <p> If progress has stopped, recovery stops the current attempt, checks existing resources, and continues the same installation. </p> {pending && ( <p> This replaces your saved request with a new recovery attempt for this installation. </p> )} <Button variant="secondary" disabled={blocked || !matched} onClick={() => accountId && void status.recover(accountId, record) } > Recover installation </Button> </details> )} </LayerCard> )} {status.ownerSubject && ( <div className="intro installation-create"> <div className="actions"> <Button disabled={blocked || !accountId || !!pending || active} onClick={() => accountId && void status.run(accountId, "reserve")} > {status.busy ? "Submitting request…" : status.installations.length ? "Install another Flarebot" : "Install Flarebot"} </Button> </div> <p> {accountId ? `Creates a new installation in ${accountName(accountId)}.` : "Select a Cloudflare account above to install Flarebot."} </p> </div> )} </section> );}