import { X } from "lucide-react";
import { type ReactNode, useEffect, useId, useRef } from "react";
interface ConfirmDialogProps {
open: boolean;
title: string;
description: ReactNode;
confirmLabel: string;
danger?: boolean;
busy?: boolean;
confirmDisabled?: boolean;
onConfirm: () => void;
onCancel: () => void;
children?: ReactNode;
}
export function ConfirmDialog({ open, title, description, confirmLabel, danger, busy, confirmDisabled, onConfirm, onCancel, children }: ConfirmDialogProps) {
const titleId = useId();
const descriptionId = useId();
const panelRef = useDialogLifecycle(open, onCancel, !busy);
if (!open) {
return null;
}
return (
!busy && onCancel()}>
event.stopPropagation()}
>
{title}
{description}
{children}
);
}
interface ManagementDialogProps {
open: boolean;
title: string;
description?: ReactNode;
wide?: boolean;
onClose: () => void;
children: ReactNode;
}
export function ManagementDialog({ open, title, description, wide, onClose, children }: ManagementDialogProps) {
const titleId = useId();
const descriptionId = useId();
const panelRef = useDialogLifecycle(open, onClose, true);
if (!open) {
return null;
}
return (
event.stopPropagation()}
>
{title}
{description &&
{description}
}
{children}
);
}
function useDialogLifecycle(open: boolean, onClose: () => void, canClose: boolean) {
const panelRef = useRef(null);
const onCloseRef = useRef(onClose);
const canCloseRef = useRef(canClose);
useEffect(() => {
onCloseRef.current = onClose;
canCloseRef.current = canClose;
}, [canClose, onClose]);
useEffect(() => {
if (!open) {
return;
}
const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
const panel = panelRef.current;
const focusTarget =
panel?.querySelector("input:not([disabled]), select:not([disabled]), textarea:not([disabled])") ??
panel?.querySelector("button:not([disabled])");
focusTarget?.focus();
function handleKeyDown(event: KeyboardEvent) {
if (event.key === "Escape" && canCloseRef.current) {
event.preventDefault();
onCloseRef.current();
}
}
document.addEventListener("keydown", handleKeyDown);
const previousBodyOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", handleKeyDown);
document.body.style.overflow = previousBodyOverflow;
previousFocus?.focus();
};
}, [open]);
return panelRef;
}
interface UsageMeterProps {
label: string;
percent?: number;
pending?: boolean;
}
export function UsageMeter({ label, percent, pending = false }: UsageMeterProps) {
const known = typeof percent === "number" && Number.isFinite(percent);
const clamped = known ? Math.max(0, Math.min(100, percent)) : 0;
const rounded = Math.round(clamped);
const state = known ? (clamped >= 85 ? "critical" : clamped >= 70 ? "watch" : "normal") : pending ? "pending" : "missing";
const stateLabel = known ? (clamped >= 85 ? "警戒" : clamped >= 70 ? "偏高" : "正常") : pending ? "读取中" : "未上报";
const trackText = pending ? "等待指标" : "等待 Run";
return (
{label}{stateLabel}
{known ? `${rounded}%` : stateLabel}
{known ? : {trackText}}
);
}
interface DiffViewProps {
lines: Array<{ kind: "same" | "added" | "removed"; text: string }>;
}
export function DiffView({ lines }: DiffViewProps) {
return (
{lines.map((line, index) => (
{line.kind === "added" ? "+" : line.kind === "removed" ? "-" : " "}
{line.text || " "}
))}
);
}