Files
pn-new-crm/src/lib/pdf/brand-kit/Header.tsx
Matt 7cc80512da docs(backlog): session wrap — full dependency/refactor roadmap shipped
Closes the 2026-05-12 push through the audit roadmap. Every item from
docs/AUDIT-2026-05-12.md §§34-36 is either shipped, deferred with
rationale, or parked behind a concrete UX/product trigger.

Wins this session (in commit order from 73184c5 onward):
  1. PDF stack overhaul (9 commits + design spec)
  2. react-email migration for all 7 remaining templates
  3. browser-image-compression in scan-shell
  4. @axe-core/playwright smoke a11y gate
  5. ts-pattern + bug-fix in search.service.ts
  6. p-limit on 3 mass-op fan-outs
  7. formatDate helper + 17 unit tests + sample sweep
  8. opt-in react-virtual in DataTable

Also nudges:
  - src/lib/pdf/brand-kit/Header.tsx — eslint-disable on react-pdf
    <Image> for a false-positive jsx-a11y/alt-text warning (PDFs
    don't follow the HTML img alt contract).
  - docs/BACKLOG.md §G — rewritten to reflect what's done + the
    remaining opportunistic work (mostly "migrate as you touch the
    file" callsite sweeps).

Comprehensive audit passing:
  - tsc --noEmit: 0 errors
  - vitest: 1315/1315 passing
  - eslint src/: 0 errors, 16 pre-existing warnings (none new)
  - next build: all routes compile, no broken imports
  - playwright --list: 162 tests across 33 files (incl. the new
    a11y spec)

Branch is shippable; remaining items are opportunistic callsite
sweeps the team can pick up when each file is otherwise being
touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:42:51 +02:00

77 lines
2.2 KiB
TypeScript

import { Image, StyleSheet, Text, View } from '@react-pdf/renderer';
import { PDF_TOKENS } from './tokens';
const styles = StyleSheet.create({
band: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: PDF_TOKENS.colors.headerBand,
paddingHorizontal: PDF_TOKENS.spacing.pagePadding,
paddingVertical: 16,
minHeight: PDF_TOKENS.spacing.headerHeight,
},
logoSlot: {
width: PDF_TOKENS.spacing.logoMaxWidth,
height: PDF_TOKENS.spacing.logoMaxHeight,
justifyContent: 'center',
},
logoImage: {
maxWidth: PDF_TOKENS.spacing.logoMaxWidth,
maxHeight: PDF_TOKENS.spacing.logoMaxHeight,
objectFit: 'contain',
objectPositionX: 0,
},
portNameFallback: {
fontFamily: PDF_TOKENS.fonts.sansBold,
fontSize: PDF_TOKENS.sizes.docTitle,
color: PDF_TOKENS.colors.headerText,
},
rightBlock: {
marginLeft: 'auto',
flexDirection: 'column',
alignItems: 'flex-end',
gap: 4,
},
docTitle: {
fontFamily: PDF_TOKENS.fonts.sansBold,
fontSize: PDF_TOKENS.sizes.docTitle,
color: PDF_TOKENS.colors.headerText,
},
meta: {
fontFamily: PDF_TOKENS.fonts.sans,
fontSize: PDF_TOKENS.sizes.small,
color: PDF_TOKENS.colors.headerText,
opacity: 0.85,
},
});
export interface HeaderProps {
portName: string;
docTitle: string;
meta?: string;
logoBuffer: Buffer | null;
}
export function Header({ portName, docTitle, meta, logoBuffer }: HeaderProps) {
return (
<View style={styles.band} fixed>
<View style={styles.logoSlot}>
{logoBuffer ? (
// react-pdf's <Image> renders into PDF bytes; the jsx-a11y/alt-text
// rule is checking against the HTML <img> contract that doesn't
// apply here (PDFs use the document title for AT, not per-image alt).
// eslint-disable-next-line jsx-a11y/alt-text
<Image src={logoBuffer} style={styles.logoImage} />
) : (
<Text style={styles.portNameFallback}>{portName}</Text>
)}
</View>
<View style={styles.rightBlock}>
<Text style={styles.docTitle}>{docTitle}</Text>
{meta ? <Text style={styles.meta}>{meta}</Text> : null}
</View>
</View>
);
}