audit: Tier 1/3/6/7 batch — PII redaction, mobile safe-area, perf, build hardening
Tier 1.4: error_events.request_body_excerpt sanitizer now redacts GDPR-relevant fields (email, phone, dob, address, fullName, firstName, lastName, postcode, nationalId, etc.) on top of the existing credential list. A 5xx in /api/v1/clients no longer lands full client PII in the super-admin inspector. Tier 3.10: ScanShell <main> now adds pb-[max(1.5rem, env(safe-area- inset-bottom))]. Mobile-pwa audit caught the Save expense button sitting flush against the iPhone 14/15 home indicator in standalone PWA mode. Tier 6.2: dashboard widget-registry now dynamic-imports every recharts-backed chart widget (berth status, lead source, occupancy timeline, pipeline funnel, revenue breakdown, source conversion). ~80-150KB initial-bundle savings when reps have charts disabled. ssr:false because recharts needs window. Tier 6.3: DataTable wraps the assembled columns in useMemo keyed on (columns, hasBulkActions). TanStack docs explicitly warn that rebuilding columns every render resets the table's internal state. Tier 7.1: Added .dockerignore (was missing — 7.6 GB context with .env reachable via COPY . .). Excludes git, env files, node_modules, build artefacts, IDE config, test artefacts, audit docs. Tier 7.4: Dockerfile.dev now runs as the node user (uid 1000) — was root. Working dir moves to /home/node/app. Tier 7.5: docker-compose.prod.yml adds memory limits (2g postgres, 512m redis, 1g crm-app, 1g crm-worker) and json-file log rotation (max-size, max-file) to every service. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
@@ -106,35 +106,44 @@ export function DataTable<TData>({
|
||||
const rowSelectionState = externalSelection ?? internalSelection;
|
||||
const setRowSelection = onRowSelectionChange ?? setInternalSelection;
|
||||
|
||||
const allColumns: ColumnDef<TData, unknown>[] = [];
|
||||
if (bulkActions && bulkActions.length > 0) {
|
||||
allColumns.push({
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
className="translate-y-[2px]"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
className="translate-y-[2px]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
size: 40,
|
||||
});
|
||||
}
|
||||
allColumns.push(...columns);
|
||||
// Memoize the assembled columns array. perf-test-auditor HIGH H2:
|
||||
// TanStack docs explicitly warn that rebuilding `columns` on every
|
||||
// render resets the table's internal state (sort, filter, sizing).
|
||||
// Re-derive only when the source columns or bulkActions presence
|
||||
// actually change.
|
||||
const hasBulkActions = Boolean(bulkActions && bulkActions.length > 0);
|
||||
const allColumns = useMemo<ColumnDef<TData, unknown>[]>(() => {
|
||||
const cols: ColumnDef<TData, unknown>[] = [];
|
||||
if (hasBulkActions) {
|
||||
cols.push({
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
className="translate-y-[2px]"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
className="translate-y-[2px]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
size: 40,
|
||||
});
|
||||
}
|
||||
cols.push(...columns);
|
||||
return cols;
|
||||
}, [columns, hasBulkActions]);
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
|
||||
Reference in New Issue
Block a user