From 3af737186dfb91d79d8c69a6bc87b5e6cb99a8e3 Mon Sep 17 00:00:00 2001
From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com>
Date: Wed, 13 May 2026 19:11:10 +0000
Subject: [PATCH] Improve admin modal design and accessibility with shared
component
Refactors AdminFormDialog to create a reusable component for admin modals, including aria-labelledby/describedby attributes and Escape key closing functionality.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: fa532550-4873-409b-840a-ac4c474132f3
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/kI0sxlu
Replit-Helium-Checkpoint-Created: true
---
artifacts/tx-os/src/pages/admin.tsx | 467 +++++++++++++++++-----------
1 file changed, 277 insertions(+), 190 deletions(-)
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 (
+
+
+
+
+ {icon}
+
+
+
+ {title}
+
+ {subtitle && (
+
+ {subtitle}
+
+ )}
+
+
+
+
+ {children}
+
+
+
+
+ {cancelLabel ?? t("common.cancel")}
+
+
+ {isPending && (
+
+ )}
+ {submitLabel ?? t("common.save")}
+
+
+
+
+ );
+}
+
// Local-state permission picker used by the "Add app" dialog. The full
// AppPermissionsEditor below talks to /apps/:id/permissions, which can't
// run before the app exists. This picker just collects ids into the form
@@ -1754,41 +1866,37 @@ export default function AdminPage() {
{/* Create User Modal */}
{newUserForm && (
-
-
-
{t("admin.addUser")}
-
- {t("admin.username")}
- setNewUserForm({ ...newUserForm, username: e.target.value })}
- className="bg-white/70 border-slate-200"
- />
-
-
- {t("admin.email")}
- setNewUserForm({ ...newUserForm, email: e.target.value })}
- className="bg-white/70 border-slate-200"
- />
-
-
- {t("admin.password")}
- setNewUserForm({ ...newUserForm, password: e.target.value })}
- className="bg-white/70 border-slate-200"
- />
-
-
- setNewUserForm(null)}>{t("common.cancel")}
- {t("common.save")}
-
+
}
+ onCancel={() => setNewUserForm(null)}
+ onSubmit={handleCreateUser}
+ isPending={createUser.isPending}
+ >
+
+ {t("admin.username")}
+ setNewUserForm({ ...newUserForm, username: e.target.value })}
+ />
-
+
+ {t("admin.email")}
+ setNewUserForm({ ...newUserForm, email: e.target.value })}
+ />
+
+
+ {t("admin.password")}
+ setNewUserForm({ ...newUserForm, password: e.target.value })}
+ />
+
+
)}
@@ -4063,80 +4171,75 @@ function UsersPanel({
{newUserOpen && (
-
-
-
{t("admin.addUser")}
-
- {t("auth.username")}
- setNewUserForm({ ...newUserForm, username: e.target.value })} className="bg-white/70 border-slate-200" />
-
-
- {t("auth.email")}
- setNewUserForm({ ...newUserForm, email: e.target.value })} className="bg-white/70 border-slate-200" />
-
-
- {t("auth.password")}
- setNewUserForm({ ...newUserForm, password: e.target.value })} className="bg-white/70 border-slate-200" />
-
-
-
{t("admin.users.col.groups")}
-
- {(groups ?? []).length === 0 && (
-
{t("admin.groups.empty")}
- )}
- {(groups ?? []).map((g) => {
- const checked = newUserForm.groupIds.includes(g.id);
- return (
-
- {
- setNewUserForm((prev) => ({
- ...prev,
- groupIds: e.target.checked
- ? [...prev.groupIds, g.id]
- : prev.groupIds.filter((id) => id !== g.id),
- }));
- }}
- />
- {g.name}
-
- );
- })}
-
-
-
-
setNewUserOpen(false)}>{t("common.cancel")}
-
{
- createUser.mutate(
- {
- data: {
- username: newUserForm.username,
- email: newUserForm.email,
- password: newUserForm.password,
- ...(newUserForm.groupIds.length > 0 ? { groupIds: newUserForm.groupIds } : {}),
- },
- },
- {
- onSuccess: () => {
- invalidateUsers();
- setNewUserOpen(false);
- setNewUserForm({ username: "", email: "", password: "", groupIds: [] });
- toast({ title: t("common.success") });
- },
- onError: () => toast({ title: t("common.error"), variant: "destructive" }),
- },
- );
- }}
- >
- {t("common.save")}
-
+
}
+ onCancel={() => setNewUserOpen(false)}
+ isPending={createUser.isPending}
+ onSubmit={() => {
+ createUser.mutate(
+ {
+ data: {
+ username: newUserForm.username,
+ email: newUserForm.email,
+ password: newUserForm.password,
+ ...(newUserForm.groupIds.length > 0 ? { groupIds: newUserForm.groupIds } : {}),
+ },
+ },
+ {
+ onSuccess: () => {
+ invalidateUsers();
+ setNewUserOpen(false);
+ setNewUserForm({ username: "", email: "", password: "", groupIds: [] });
+ toast({ title: t("common.success") });
+ },
+ onError: () => toast({ title: t("common.error"), variant: "destructive" }),
+ },
+ );
+ }}
+ >
+
+ {t("auth.username")}
+ setNewUserForm({ ...newUserForm, username: e.target.value })} />
+
+
+ {t("auth.email")}
+ setNewUserForm({ ...newUserForm, email: e.target.value })} />
+
+
+ {t("auth.password")}
+ setNewUserForm({ ...newUserForm, password: e.target.value })} />
+
+
+
{t("admin.users.col.groups")}
+
+ {(groups ?? []).length === 0 && (
+
{t("admin.groups.empty")}
+ )}
+ {(groups ?? []).map((g) => {
+ const checked = newUserForm.groupIds.includes(g.id);
+ return (
+
+ {
+ setNewUserForm((prev) => ({
+ ...prev,
+ groupIds: e.target.checked
+ ? [...prev.groupIds, g.id]
+ : prev.groupIds.filter((id) => id !== g.id),
+ }));
+ }}
+ />
+ {g.name}
+
+ );
+ })}
-
+
)}
{editingUser && (
@@ -4709,42 +4812,36 @@ function GroupsPanel({
{createOpen && (
-
-
-
{t("admin.groups.addGroup")}
-
- {t("admin.groups.name")}
- setCreateForm({ ...createForm, name: e.target.value })} className="bg-white/70 border-slate-200" data-testid="group-name-input" />
-
-
- {t("admin.groups.descriptionAr")}
- setCreateForm({ ...createForm, descriptionAr: e.target.value })} className="bg-white/70 border-slate-200" dir="rtl" />
-
-
- {t("admin.groups.descriptionEn")}
- setCreateForm({ ...createForm, descriptionEn: e.target.value })} className="bg-white/70 border-slate-200" dir="ltr" />
-
-
- setCreateOpen(false)}>{t("common.cancel")}
-
- createGroup.mutate(
- { data: { name: createForm.name.trim(), descriptionAr: createForm.descriptionAr || null, descriptionEn: createForm.descriptionEn || null } },
- {
- onSuccess: () => { invalidate(); setCreateOpen(false); toast({ title: t("common.success") }); },
- onError: () => toast({ title: t("common.error"), variant: "destructive" }),
- },
- )
- }
- data-testid="create-group-submit"
- >
- {t("common.save")}
-
-
+
}
+ onCancel={() => setCreateOpen(false)}
+ submitDisabled={!createForm.name.trim()}
+ isPending={createGroup.isPending}
+ submitTestId="create-group-submit"
+ onSubmit={() =>
+ createGroup.mutate(
+ { data: { name: createForm.name.trim(), descriptionAr: createForm.descriptionAr || null, descriptionEn: createForm.descriptionEn || null } },
+ {
+ onSuccess: () => { invalidate(); setCreateOpen(false); toast({ title: t("common.success") }); },
+ onError: () => toast({ title: t("common.error"), variant: "destructive" }),
+ },
+ )
+ }
+ >
+
+ {t("admin.groups.name")}
+ setCreateForm({ ...createForm, name: e.target.value })} data-testid="group-name-input" />
-
+
+ {t("admin.groups.descriptionAr")}
+ setCreateForm({ ...createForm, descriptionAr: e.target.value })} dir="rtl" />
+
+
+ {t("admin.groups.descriptionEn")}
+ setCreateForm({ ...createForm, descriptionEn: e.target.value })} dir="ltr" />
+
+
)}
{editingGroupId != null && (
@@ -6496,55 +6593,45 @@ function RolesPanel({
{createOpen && (
-
-
-
{t("admin.roles.addRole")}
-
-
{t("admin.roles.name")}
-
setCreateForm({ ...createForm, name: e.target.value })}
- className="bg-white/70 border-slate-200"
- placeholder={t("admin.roles.namePlaceholder")}
- data-testid="role-name-input"
- />
-
{t("admin.roles.nameHint")}
-
-
- {t("admin.roles.descriptionAr")}
- setCreateForm({ ...createForm, descriptionAr: e.target.value })}
- className="bg-white/70 border-slate-200"
- dir="rtl"
- data-testid="role-desc-ar-input"
- />
-
-
- {t("admin.roles.descriptionEn")}
- setCreateForm({ ...createForm, descriptionEn: e.target.value })}
- className="bg-white/70 border-slate-200"
- dir="ltr"
- data-testid="role-desc-en-input"
- />
-
-
- setCreateOpen(false)}>
- {t("common.cancel")}
-
-
- {createRole.isPending ? : t("common.save")}
-
-
+
}
+ onCancel={() => setCreateOpen(false)}
+ onSubmit={handleCreate}
+ submitDisabled={!createForm.name.trim()}
+ isPending={createRole.isPending}
+ testId="create-role-dialog"
+ submitTestId="create-role-submit"
+ >
+
+
{t("admin.roles.name")}
+
setCreateForm({ ...createForm, name: e.target.value })}
+ placeholder={t("admin.roles.namePlaceholder")}
+ data-testid="role-name-input"
+ />
+
{t("admin.roles.nameHint")}
-
+
+ {t("admin.roles.descriptionAr")}
+ setCreateForm({ ...createForm, descriptionAr: e.target.value })}
+ dir="rtl"
+ data-testid="role-desc-ar-input"
+ />
+
+
+ {t("admin.roles.descriptionEn")}
+ setCreateForm({ ...createForm, descriptionEn: e.target.value })}
+ dir="ltr"
+ data-testid="role-desc-en-input"
+ />
+
+
)}
{editingId != null && (