diff --git a/artifacts/tx-os/src/pages/admin.tsx b/artifacts/tx-os/src/pages/admin.tsx index 4b31d66c..fd18f5cc 100644 --- a/artifacts/tx-os/src/pages/admin.tsx +++ b/artifacts/tx-os/src/pages/admin.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useMemo, type ReactNode } from "react"; +import { useState, useEffect, useRef, useMemo, useId, type ReactNode } from "react"; import { useTranslation } from "react-i18next"; import { useLocation } from "wouter"; import { @@ -361,6 +361,118 @@ function DeletionWarningDialog({ ); } +function AdminFormDialog({ + title, + subtitle, + icon, + onCancel, + onSubmit, + submitLabel, + cancelLabel, + submitDisabled, + isPending, + children, + testId, + submitTestId, + maxWidth = "sm", +}: { + title: string; + subtitle?: string; + icon: ReactNode; + onCancel: () => void; + onSubmit: () => void; + submitLabel?: string; + cancelLabel?: string; + submitDisabled?: boolean; + isPending?: boolean; + children: ReactNode; + testId?: string; + submitTestId?: string; + maxWidth?: "sm" | "md" | "lg"; +}) { + const { t } = useTranslation(); + const titleId = useId(); + const subtitleId = useId(); + const widthClass = + maxWidth === "lg" ? "max-w-lg" : maxWidth === "md" ? "max-w-md" : "max-w-sm"; + useEffect(() => { + const handleKey = (e: KeyboardEvent) => { + if (e.key === "Escape" && !isPending) { + e.stopPropagation(); + onCancel(); + } + }; + window.addEventListener("keydown", handleKey); + return () => window.removeEventListener("keydown", handleKey); + }, [isPending, onCancel]); + return ( +
+ {subtitle} +
+ )} +{t("admin.roles.nameHint")}
-{t("admin.roles.nameHint")}