fix(audit-wave-9): add mobile cardRender to remaining admin lists
Five DataTable consumers were rendering as horizontally-scrolling desktop tables on mobile because they had no cardRender prop. Now they collapse to a vertical card list below the lg: breakpoint with the same actions inline: - admin/tags/tag-list - admin/roles/role-list - admin/ports/port-list (also: Active/Inactive badge -> StatusPill) - admin/document-templates/template-list (also: Active/Inactive badge -> StatusPill) - admin/custom-fields/custom-fields-manager All five now share the user-list / berth-list pattern: row-card with title, secondary meta, and trailing action buttons; same TanStack table instance powers both the desktop table and the mobile cards. Closes ui/ux H2 + extends M2 (status-pill coverage). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -192,6 +192,66 @@ export function CustomFieldsManager() {
|
||||
data={filteredFields}
|
||||
isLoading={loading}
|
||||
getRowId={(row) => row.id}
|
||||
cardRender={({ original }) => (
|
||||
<div className="rounded-xl border border-border bg-card p-4 shadow-sm">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{original.fieldLabel}
|
||||
</p>
|
||||
<p className="mt-0.5 truncate font-mono text-xs text-muted-foreground">
|
||||
{original.fieldName}
|
||||
</p>
|
||||
<div className="mt-2 flex flex-wrap items-center gap-1.5 text-xs">
|
||||
<Badge variant="secondary">
|
||||
{FIELD_TYPE_LABELS[original.fieldType] ?? original.fieldType}
|
||||
</Badge>
|
||||
{original.isRequired ? (
|
||||
<Badge variant="destructive" className="text-xs">
|
||||
Required
|
||||
</Badge>
|
||||
) : (
|
||||
<span className="text-muted-foreground">Optional</span>
|
||||
)}
|
||||
<span aria-hidden className="text-muted-foreground">
|
||||
·
|
||||
</span>
|
||||
<span className="tabular-nums text-muted-foreground">
|
||||
Order {original.sortOrder}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleEdit(original)}
|
||||
aria-label="Edit field"
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Button>
|
||||
<ConfirmationDialog
|
||||
trigger={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive"
|
||||
disabled={deletingId === original.id}
|
||||
aria-label="Delete field"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
}
|
||||
title="Delete Custom Field"
|
||||
description={getDeleteDescription(original)}
|
||||
confirmLabel="Delete Field"
|
||||
onConfirm={() => handleDelete(original.id)}
|
||||
loading={deletingId === original.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
emptyState={
|
||||
<div className="py-8 text-center">
|
||||
<p className="text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user