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:
@@ -11,22 +11,52 @@
|
||||
*/
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import { ActiveDealsTile } from './active-deals-tile';
|
||||
import { ActivityFeed } from './activity-feed';
|
||||
import { BerthStatusChart } from './berth-status-chart';
|
||||
import { HotDealsCard } from './hot-deals-card';
|
||||
import { LeadSourceChart } from './lead-source-chart';
|
||||
import { OccupancyTimelineChart } from './occupancy-timeline-chart';
|
||||
import { PipelineFunnelChart } from './pipeline-funnel-chart';
|
||||
import { PipelineValueTile } from './pipeline-value-tile';
|
||||
import { RevenueBreakdownChart } from './revenue-breakdown-chart';
|
||||
import { SourceConversionChart } from './source-conversion-chart';
|
||||
import { WebsiteGlanceTile } from './website-glance-tile';
|
||||
import { MyRemindersRail } from './my-reminders-rail';
|
||||
import { AlertRail } from '@/components/alerts/alert-rail';
|
||||
import type { DateRange } from '@/lib/analytics/range';
|
||||
|
||||
// Recharts-backed widgets are dynamic-imported so the recharts bundle
|
||||
// (~80-150KB) doesn't ship on every dashboard load when the rep has
|
||||
// disabled charts. perf-test-auditor HIGH H3 caught the static import.
|
||||
// Each one gets a placeholder loading state matching its grid slot.
|
||||
const ChartFallback = () => (
|
||||
<div className="rounded-lg border bg-muted/30 p-8 text-center text-sm text-muted-foreground">
|
||||
Loading chart…
|
||||
</div>
|
||||
);
|
||||
const BerthStatusChart = dynamic(
|
||||
() => import('./berth-status-chart').then((m) => ({ default: m.BerthStatusChart })),
|
||||
{ loading: ChartFallback, ssr: false },
|
||||
);
|
||||
const LeadSourceChart = dynamic(
|
||||
() => import('./lead-source-chart').then((m) => ({ default: m.LeadSourceChart })),
|
||||
{ loading: ChartFallback, ssr: false },
|
||||
);
|
||||
const OccupancyTimelineChart = dynamic(
|
||||
() =>
|
||||
import('./occupancy-timeline-chart').then((m) => ({ default: m.OccupancyTimelineChart })),
|
||||
{ loading: ChartFallback, ssr: false },
|
||||
);
|
||||
const PipelineFunnelChart = dynamic(
|
||||
() => import('./pipeline-funnel-chart').then((m) => ({ default: m.PipelineFunnelChart })),
|
||||
{ loading: ChartFallback, ssr: false },
|
||||
);
|
||||
const RevenueBreakdownChart = dynamic(
|
||||
() => import('./revenue-breakdown-chart').then((m) => ({ default: m.RevenueBreakdownChart })),
|
||||
{ loading: ChartFallback, ssr: false },
|
||||
);
|
||||
const SourceConversionChart = dynamic(
|
||||
() => import('./source-conversion-chart').then((m) => ({ default: m.SourceConversionChart })),
|
||||
{ loading: ChartFallback, ssr: false },
|
||||
);
|
||||
|
||||
/**
|
||||
* Where a widget lives on the dashboard. The shell renders three
|
||||
* separate auto-fit regions so charts and rails don't compete for the
|
||||
|
||||
Reference in New Issue
Block a user