Skip to content

Commit 5b6df25

Browse files
committed
Tables consistency
1 parent 0853f6a commit 5b6df25

9 files changed

Lines changed: 209 additions & 326 deletions

File tree

frontend/src/components/pages/security/roles/role-detail-page.tsx

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { setPageHeader } from '../../../../state/ui-state';
2727
import { Button } from '../../../redpanda-ui/components/button';
2828
import { Combobox } from '../../../redpanda-ui/components/combobox';
2929
import { ListLayout, ListLayoutContent, ListLayoutFilters } from '../../../redpanda-ui/components/list-layout';
30+
import { Table, TableBody, TableCell, TableRow } from '../../../redpanda-ui/components/table';
3031
import { parsePrincipal } from '../shared/acl-model';
3132
import { AclsCard } from '../shared/acls-card';
3233

@@ -132,29 +133,30 @@ const RoleDetailPage = () => {
132133
) : allMembers.length === 0 ? (
133134
<p className="text-muted-foreground text-sm">No principals assigned to this role.</p>
134135
) : (
135-
<div className="rounded-md border">
136-
{allMembers.map((member) => {
137-
const parsed = parsePrincipal(member.principal);
138-
const displayName = parsed.name || member.principal;
139-
return (
140-
<div
141-
className="flex items-center justify-between gap-2 border-b px-3 py-2 text-sm last:border-b-0 hover:bg-muted/30"
142-
key={member.principal}
143-
>
144-
<span className="font-mono">{displayName}</span>
145-
<Button
146-
data-testid={`remove-${parsed.type.toLowerCase()}-${displayName}-button`}
147-
disabled={deletingPrincipal === member.principal || isSubmitting}
148-
onClick={() => handleRemoveMember(member.principal)}
149-
size="icon-sm"
150-
variant="destructive-ghost"
151-
>
152-
<Trash2 className="h-4 w-4" />
153-
</Button>
154-
</div>
155-
);
156-
})}
157-
</div>
136+
<Table>
137+
<TableBody>
138+
{allMembers.map((member) => {
139+
const parsed = parsePrincipal(member.principal);
140+
const displayName = parsed.name || member.principal;
141+
return (
142+
<TableRow key={member.principal}>
143+
<TableCell className="font-mono">{displayName}</TableCell>
144+
<TableCell align="right">
145+
<Button
146+
data-testid={`remove-${parsed.type.toLowerCase()}-${displayName}-button`}
147+
disabled={deletingPrincipal === member.principal || isSubmitting}
148+
onClick={() => handleRemoveMember(member.principal)}
149+
size="icon-sm"
150+
variant="destructive-ghost"
151+
>
152+
<Trash2 className="h-4 w-4" />
153+
</Button>
154+
</TableCell>
155+
</TableRow>
156+
);
157+
})}
158+
</TableBody>
159+
</Table>
158160
)}
159161
</ListLayoutContent>
160162
</ListLayout>

frontend/src/components/pages/security/roles/role-update-page.tsx

Lines changed: 0 additions & 139 deletions
This file was deleted.

frontend/src/components/pages/security/shared/acls-card.tsx

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
DialogTitle,
4242
} from '../../../redpanda-ui/components/dialog';
4343
import { ListLayout, ListLayoutContent, ListLayoutFilters } from '../../../redpanda-ui/components/list-layout';
44+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../../../redpanda-ui/components/table';
4445
import { AddAclDialog } from '../users/add-acl-dialog';
4546

4647
const RESOURCE_TYPE_LABELS: Record<string, string> = {
@@ -206,34 +207,39 @@ export const AclsCard = ({ acls, principal }: AclsCardProps) => {
206207
{rows.length === 0 ? (
207208
<p>No ACLs assigned.</p>
208209
) : (
209-
<div className="rounded-md border">
210-
<div className="grid grid-cols-[32px_1fr_2fr_1fr_1fr_1fr] items-center gap-2 border-b px-3 py-2 font-medium text-muted-foreground text-xs uppercase tracking-wide">
211-
<Checkbox
212-
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
213-
onCheckedChange={toggleAll}
214-
/>
215-
<span>Type</span>
216-
<span>Resource</span>
217-
<span>Operation</span>
218-
<span>Permission</span>
219-
<span>Host</span>
220-
</div>
221-
{rows.map((row) => (
222-
<div
223-
className="grid grid-cols-[32px_1fr_2fr_1fr_1fr_1fr] items-center gap-2 border-b px-3 py-2 text-sm last:border-b-0 hover:bg-muted/30"
224-
key={row.id}
225-
>
226-
<Checkbox checked={selected.has(row.id)} onCheckedChange={() => toggleRow(row.id)} />
227-
<span className="text-muted-foreground">{row.resourceType}</span>
228-
<span className="font-mono">{row.resourceName}</span>
229-
<span>{row.operation}</span>
230-
<span className={row.permissionType === 'Allow' ? 'text-green-600' : 'text-red-600'}>
231-
{row.permissionType}
232-
</span>
233-
<span className="text-muted-foreground">{row.host}</span>
234-
</div>
235-
))}
236-
</div>
210+
<Table>
211+
<TableHeader>
212+
<TableRow>
213+
<TableHead>
214+
<Checkbox
215+
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
216+
onCheckedChange={toggleAll}
217+
/>
218+
</TableHead>
219+
<TableHead>Type</TableHead>
220+
<TableHead>Resource</TableHead>
221+
<TableHead>Operation</TableHead>
222+
<TableHead>Permission</TableHead>
223+
<TableHead>Host</TableHead>
224+
</TableRow>
225+
</TableHeader>
226+
<TableBody>
227+
{rows.map((row) => (
228+
<TableRow key={row.id}>
229+
<TableCell>
230+
<Checkbox checked={selected.has(row.id)} onCheckedChange={() => toggleRow(row.id)} />
231+
</TableCell>
232+
<TableCell className="text-muted-foreground">{row.resourceType}</TableCell>
233+
<TableCell className="font-mono">{row.resourceName}</TableCell>
234+
<TableCell>{row.operation}</TableCell>
235+
<TableCell className={row.permissionType === 'Allow' ? 'text-green-600' : 'text-red-600'}>
236+
{row.permissionType}
237+
</TableCell>
238+
<TableCell className="text-muted-foreground">{row.host}</TableCell>
239+
</TableRow>
240+
))}
241+
</TableBody>
242+
</Table>
237243
)}
238244
</ListLayoutContent>
239245
</ListLayout>
@@ -249,22 +255,26 @@ export const AclsCard = ({ acls, principal }: AclsCardProps) => {
249255
</DialogDescription>
250256
</DialogHeader>
251257

252-
<div className="rounded-md border text-sm">
253-
<div className="grid grid-cols-[1fr_1fr_1fr_1fr] gap-2 border-b px-3 py-2 font-medium text-muted-foreground text-xs uppercase tracking-wide">
254-
<span>Resource Type</span>
255-
<span>Resource Name</span>
256-
<span>Operation</span>
257-
<span>Permission</span>
258-
</div>
259-
{GRANT_ALL_RESOURCES.map((r) => (
260-
<div className="grid grid-cols-[1fr_1fr_1fr_1fr] gap-2 border-b px-3 py-2 last:border-b-0" key={r.type}>
261-
<span className="text-muted-foreground">{r.label}</span>
262-
<span className="font-mono">{r.name}</span>
263-
<span>All</span>
264-
<span className="text-green-600">Allow</span>
265-
</div>
266-
))}
267-
</div>
258+
<Table>
259+
<TableHeader>
260+
<TableRow>
261+
<TableHead>Resource Type</TableHead>
262+
<TableHead>Resource Name</TableHead>
263+
<TableHead>Operation</TableHead>
264+
<TableHead>Permission</TableHead>
265+
</TableRow>
266+
</TableHeader>
267+
<TableBody>
268+
{GRANT_ALL_RESOURCES.map((r) => (
269+
<TableRow key={r.type}>
270+
<TableCell className="text-muted-foreground">{r.label}</TableCell>
271+
<TableCell className="font-mono">{r.name}</TableCell>
272+
<TableCell>All</TableCell>
273+
<TableCell className="text-green-600">Allow</TableCell>
274+
</TableRow>
275+
))}
276+
</TableBody>
277+
</Table>
268278

269279
<DialogFooter>
270280
<Button onClick={() => setGrantAllOpen(false)} type="button" variant="outline">

frontend/src/components/pages/security/shared/delete-role-confirm-modal.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@ export const DeleteRoleConfirmModal: FC<{
2828
roleName: string;
2929
numberOfPrincipals: number;
3030
onConfirm: () => Promise<void> | void;
31-
buttonEl: React.ReactElement;
32-
}> = ({ roleName, numberOfPrincipals, onConfirm, buttonEl }) => {
33-
const [open, setOpen] = useState(false);
31+
buttonEl?: React.ReactElement;
32+
open?: boolean;
33+
onOpenChange?: (open: boolean) => void;
34+
}> = ({ roleName, numberOfPrincipals, onConfirm, buttonEl, open: openProp, onOpenChange: onOpenChangeProp }) => {
35+
const [internalOpen, setInternalOpen] = useState(false);
3436
const [confirmText, setConfirmText] = useState('');
3537

38+
const isControlled = openProp !== undefined;
39+
const open = isControlled ? openProp : internalOpen;
40+
3641
const handleOpenChange = (o: boolean) => {
37-
setOpen(o);
42+
if (!isControlled) setInternalOpen(o);
43+
onOpenChangeProp?.(o);
3844
if (!o) setConfirmText('');
3945
};
4046

@@ -45,7 +51,7 @@ export const DeleteRoleConfirmModal: FC<{
4551

4652
return (
4753
<Dialog onOpenChange={handleOpenChange} open={open}>
48-
<DialogTrigger asChild>{buttonEl}</DialogTrigger>
54+
{buttonEl && <DialogTrigger asChild>{buttonEl}</DialogTrigger>}
4955
<DialogContent variant="destructive">
5056
<DialogHeader>
5157
<DialogTitle>Delete role {roleName}</DialogTitle>

0 commit comments

Comments
 (0)