Compare commits

2 Commits

Author SHA1 Message Date
Riyadh d8fcdc4662 Unify edit-role dialog with AdminFormDialog (#621)
The "edit role" dialog in admin.tsx was hand-rolled as a bespoke
<div role-less wrapper> with its own close button, sticky footer and
full-width split buttons. That made it look different from every
other admin dialog (add role, add/edit group, add user), and on
iPad the action buttons floated mid-dialog because the content
exceeded max-h-[92vh]. The custom wrapper also lacked role="dialog",
so the new `:has(:focus)` CSS keyboard rule (index.css:492) never
applied to it.

Replaced the wrapper with AdminFormDialog (maxWidth="lg", KeyRound
icon, same title key). All inner content — name/desc fields, rename
warning, permissions list, removal impact, RolePermissionHistory,
RecentActivityForTarget — moved verbatim as children. Removed the
custom sticky footer; AdminFormDialog provides the unified footer
with cancel/save buttons. Preserved testIds `edit-role-dialog` and
`edit-role-submit`. Disabled logic translated 1:1 into
submitDisabled + isPending props (note: `impactError` coerced to
boolean via `!!` to satisfy the prop type).

No behavior change: same handlers (closeEditDialog, handleEdit),
same loading spinner, same disabled conditions. Bonus: the keyboard
override CSS now applies because AdminFormDialog already sets
role="dialog".

`tsc --noEmit` clean.
2026-05-21 08:57:08 +00:00
Riyadh 53be7431cb Transitioned from Plan to Build mode 2026-05-21 08:56:06 +00:00
+16 -37
View File
@@ -6867,20 +6867,21 @@ function RolesPanel({
)}
{editingId != null && (
<div className="fixed inset-0 bg-slate-900/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div
className="relative bg-white rounded-2xl sm:rounded-3xl shadow-2xl ring-1 ring-slate-200 p-5 sm:p-6 w-full max-w-[min(100%,560px)] sm:max-w-xl md:max-w-2xl space-y-4 max-h-[92vh] overflow-y-auto"
data-testid="edit-role-dialog"
<AdminFormDialog
title={t("admin.roles.editRole")}
icon={<KeyRound size={20} />}
maxWidth="lg"
onCancel={closeEditDialog}
onSubmit={handleEdit}
submitDisabled={
!editForm.name.trim() ||
(!editingIsSystem && hasRemovals && (impactLoading || !!impactError))
}
isPending={updateRole.isPending || replaceRolePermissions.isPending}
testId="edit-role-dialog"
submitTestId="edit-role-submit"
>
<button
type="button"
onClick={closeEditDialog}
aria-label={t("common.cancel")}
className="absolute top-3 end-3 inline-flex items-center justify-center w-9 h-9 rounded-full text-slate-500 hover:text-slate-900 hover:bg-slate-100"
>
<X size={18} />
</button>
<h2 className="text-lg sm:text-xl font-semibold text-foreground pe-10">{t("admin.roles.editRole")}</h2>
<>
<div className="space-y-1">
<Label>{t("admin.roles.name")}</Label>
<Input
@@ -7079,30 +7080,8 @@ function RolesPanel({
onOpenAuditLogForTarget("role", editingPermissionsId);
}}
/>
<div className="flex gap-2 pt-3 sticky bottom-0 bg-white -mx-5 sm:-mx-6 px-5 sm:px-6 -mb-5 sm:-mb-6 pb-5 sm:pb-6 border-t border-slate-100">
<Button variant="outline" className="flex-1 min-h-11 rounded-xl text-base" onClick={closeEditDialog}>
{t("common.cancel")}
</Button>
<Button
className="flex-1 min-h-11 rounded-xl text-base"
disabled={
!editForm.name.trim() ||
updateRole.isPending ||
replaceRolePermissions.isPending ||
(!editingIsSystem && hasRemovals && (impactLoading || impactError))
}
onClick={handleEdit}
data-testid="edit-role-submit"
>
{updateRole.isPending || replaceRolePermissions.isPending ? (
<Loader2 size={14} className="animate-spin" />
) : (
t("common.save")
)}
</Button>
</div>
</div>
</div>
</>
</AdminFormDialog>
)}
{confirmRemoval && permissionsImpact && permissionsImpact.totalAffectedUsers > 0 && (