1import { Spinner } from "~/components/ui/spinner";
2import type { ServerUpdateState } from "@t3tools/client-runtime/state/server";
3import { CircleAlertIcon, DownloadIcon } from "lucide-react";
4import { useId, useState } from "react";
5
6import { serverUpdateStageLabel } from "../ServerUpdateAction";
7import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
8import { ComposerBanner } from "./ComposerBanner";
9
10export function ComposerServerUpdateIcon({
11 status,
12}: {
13 readonly status: ServerUpdateState["status"];
14}) {
15 if (status === "running") {
16 return <Spinner aria-hidden />;
17 }
18 if (status === "failed") {
19 return <CircleAlertIcon aria-hidden className="text-error" />;
20 }
21 return <DownloadIcon aria-hidden />;
22}
23
24/** One text line, clipped at the end so the error detail never squeezes its title. */
25export function ComposerServerUpdateStatus({
26 state,
27 serverLabel = "server",
28}: {
29 readonly state: Exclude<ServerUpdateState, { status: "idle" }>;
30 readonly serverLabel?: string;
31}) {
32 const [detailsOpen, setDetailsOpen] = useState(false);
33 const triggerId = useId();
34 const title = `${state.status === "failed" ? "Could not update" : "Updating"} ${serverLabel}`;
35 const detail = state.status === "failed" ? state.message : serverUpdateStageLabel(state.stage);
36 return (
37 <span
38 role={state.status === "failed" ? "alert" : "status"}
39 className="min-w-0"
40 data-composer-server-update-status={state.status}
41 >
42 <Tooltip open={detailsOpen} onOpenChange={setDetailsOpen} triggerId={triggerId}>
43 <TooltipTrigger
44 id={triggerId}
45 closeOnClick={false}
46 render={
47 <button
48 type="button"
49 aria-label={`${title}: ${detail}`}
50 className="block max-w-full cursor-help truncate rounded-sm text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
51 onClick={() => setDetailsOpen(true)}
52 >
53 {title}
54 <ComposerBanner.Separator />
55 <span className="font-normal text-muted-foreground">{detail}</span>
56 </button>
57 }
58 />
59 <TooltipPopup side="top">
60 {title}: {detail}
61 </TooltipPopup>
62 </Tooltip>
63 </span>
64 );
65}