This commit is contained in:
@@ -1129,7 +1129,7 @@ function buildMediumVoltageCrossSectionTableFromNewExcel(args: {
|
||||
export function buildDatasheetModel(args: { product: ProductData; locale: 'en' | 'de' }): DatasheetModel {
|
||||
const labels = getLabels(args.locale);
|
||||
const categoriesLine = (args.product.categories || []).map(c => stripHtml(c.name)).join(' • ');
|
||||
const descriptionText = stripHtml(args.product.shortDescriptionHtml || args.product.descriptionHtml || '');
|
||||
const descriptionText = stripHtml(args.product.applicationHtml || '');
|
||||
const heroSrc = resolveMediaToLocalPath(args.product.featuredImage || args.product.images?.[0] || null);
|
||||
const productUrl = getProductUrl(args.product);
|
||||
|
||||
@@ -1173,22 +1173,71 @@ export function buildDatasheetModel(args: { product: ProductData; locale: 'en' |
|
||||
productUrl,
|
||||
},
|
||||
labels,
|
||||
technicalItems: [
|
||||
...(excelModel.ok ? excelModel.technicalItems : []),
|
||||
...(isMediumVoltageProduct(args.product)
|
||||
? args.locale === 'de'
|
||||
? [
|
||||
{ label: 'Prüfspannung 6/10 kV', value: '21 kV' },
|
||||
{ label: 'Prüfspannung 12/20 kV', value: '42 kV' },
|
||||
{ label: 'Prüfspannung 18/30 kV', value: '63 kV' },
|
||||
]
|
||||
: [
|
||||
{ label: 'Test voltage 6/10 kV', value: '21 kV' },
|
||||
{ label: 'Test voltage 12/20 kV', value: '42 kV' },
|
||||
{ label: 'Test voltage 18/30 kV', value: '63 kV' },
|
||||
]
|
||||
: []),
|
||||
],
|
||||
technicalItems: (() => {
|
||||
if (!isMediumVoltageProduct(args.product)) {
|
||||
return excelModel.ok ? excelModel.technicalItems : [];
|
||||
}
|
||||
|
||||
const pn = normalizeDesignation(args.product.name || '');
|
||||
const isAl = /^NA/.test(pn);
|
||||
const isFL = pn.includes('FL');
|
||||
const isF = !isFL && pn.includes('F');
|
||||
|
||||
const findExcelVal = (labelPart: string) => {
|
||||
const found = excelModel.technicalItems.find(it => it.label.toLowerCase().includes(labelPart.toLowerCase()));
|
||||
return found ? found.value : null;
|
||||
};
|
||||
|
||||
const items: KeyValueItem[] = [];
|
||||
if (args.locale === 'de') {
|
||||
items.push({ label: 'Leitermaterial', value: isAl ? 'Aluminium' : 'Kupfer' });
|
||||
items.push({ label: 'Leiterklasse', value: isAl ? 'Klasse 1' : 'Klasse 2 mehrdrähtig' });
|
||||
items.push({ label: 'Aderisolation', value: 'VPE DIX8' });
|
||||
items.push({ label: 'Feldsteuerung', value: 'innere und äußere Leitschicht aus halbleitendem Kunststoff - 3-fach-extrudiert' });
|
||||
items.push({ label: 'Schirm', value: 'Kupferdrähte + Querleitwendel' });
|
||||
items.push({ label: 'Längswasserdichtigkeit', value: (isF || isFL) ? 'ja, mit Quellvliess' : 'nein' });
|
||||
items.push({ label: 'Querwasserdichtigkeit', value: isFL ? 'ja, Al-Band' : 'nein' });
|
||||
items.push({ label: 'Mantelmaterial', value: 'Polyethylen DMP2' });
|
||||
items.push({ label: 'Mantelfarbe', value: 'schwarz' });
|
||||
items.push({ label: 'Flammwidrigkeit', value: 'nein' });
|
||||
items.push({ label: 'UV-beständig', value: 'ja' });
|
||||
items.push({ label: 'Max. zulässige Leitertemperatur', value: findExcelVal('Leitertemperatur') || '90°C' });
|
||||
items.push({ label: 'Zul. Kabelaußentemperatur, fest verlegt', value: findExcelVal('fest verlegt') || '70°C' });
|
||||
items.push({ label: 'Zul. Kabelaußentemperatur, in Bewegung', value: findExcelVal('in Bewegung') || '-20 °C bis +70 °C' });
|
||||
items.push({ label: 'Maximale Kurzschlußtemperatur', value: findExcelVal('Kurzschlußtemperatur') || '+250 °C' });
|
||||
items.push({ label: 'Min. Biegeradius, fest verlegt', value: findExcelVal('Biegeradius') || '15 facher Durchmesser' });
|
||||
items.push({ label: 'Mindesttemperatur Verlegung', value: findExcelVal('Verlegung') || '-5 °C' });
|
||||
items.push({ label: 'Metermarkierung', value: 'ja' });
|
||||
items.push({ label: 'Teilentladung', value: findExcelVal('Teilentladung') || '2 pC' });
|
||||
items.push({ label: 'Prüfspannung 6/10 kV', value: '21 kV' });
|
||||
items.push({ label: 'Prüfspannung 12/20 kV', value: '42 kV' });
|
||||
items.push({ label: 'Prüfspannung 18/30 kV', value: '63 kV' });
|
||||
} else {
|
||||
items.push({ label: 'Conductor material', value: isAl ? 'Aluminum' : 'Copper' });
|
||||
items.push({ label: 'Conductor class', value: isAl ? 'Class 1' : 'Class 2 stranded' });
|
||||
items.push({ label: 'Core insulation', value: 'XLPE DIX8' });
|
||||
items.push({ label: 'Field control', value: 'inner and outer semiconducting layer made of semiconducting plastic - 3-fold extruded' });
|
||||
items.push({ label: 'Screen', value: 'copper wires + transverse conductive helix' });
|
||||
items.push({ label: 'Longitudinal water tightness', value: (isF || isFL) ? 'yes, with swelling tape' : 'no' });
|
||||
items.push({ label: 'Transverse water tightness', value: isFL ? 'yes, Al-tape' : 'no' });
|
||||
items.push({ label: 'Sheath material', value: 'Polyethylene DMP2' });
|
||||
items.push({ label: 'Sheath color', value: 'black' });
|
||||
items.push({ label: 'Flame retardancy', value: 'no' });
|
||||
items.push({ label: 'UV resistant', value: 'yes' });
|
||||
items.push({ label: 'Max. permissible conductor temperature', value: findExcelVal('conductor temperature') || '90°C' });
|
||||
items.push({ label: 'Permissible cable outer temperature, fixed', value: findExcelVal('fixed') || '70°C' });
|
||||
items.push({ label: 'Permissible cable outer temperature, in motion', value: findExcelVal('in motion') || '-20 °C to +70 °C' });
|
||||
items.push({ label: 'Maximum short-circuit temperature', value: findExcelVal('short-circuit temperature') || '+250 °C' });
|
||||
items.push({ label: 'Min. bending radius, fixed', value: findExcelVal('bending radius') || '15 times diameter' });
|
||||
items.push({ label: 'Minimum laying temperature', value: findExcelVal('laying temperature') || '-5 °C' });
|
||||
items.push({ label: 'Meter marking', value: 'yes' });
|
||||
items.push({ label: 'Partial discharge', value: findExcelVal('Partial discharge') || '2 pC' });
|
||||
items.push({ label: 'Test voltage 6/10 kV', value: '21 kV' });
|
||||
items.push({ label: 'Test voltage 12/20 kV', value: '42 kV' });
|
||||
items.push({ label: 'Test voltage 18/30 kV', value: '63 kV' });
|
||||
}
|
||||
return items;
|
||||
})(),
|
||||
voltageTables,
|
||||
legendItems: crossSectionModel.legendItems || [],
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ export interface ProductData {
|
||||
name: string;
|
||||
shortDescriptionHtml: string;
|
||||
descriptionHtml: string;
|
||||
applicationHtml: string;
|
||||
images: string[];
|
||||
featuredImage: string | null;
|
||||
sku: string;
|
||||
|
||||
@@ -26,56 +26,62 @@ export function DatasheetDocument(props: { model: DatasheetModel; assets: Assets
|
||||
return (
|
||||
<Document>
|
||||
<Page size="A4" style={styles.page}>
|
||||
<Header title={headerTitle} logoDataUrl={assets.logoDataUrl} qrDataUrl={assets.qrDataUrl} />
|
||||
<Footer locale={model.locale} siteUrl={CONFIG.siteUrl} />
|
||||
|
||||
<Text style={styles.h1}>{model.product.name}</Text>
|
||||
{model.product.categoriesLine ? <Text style={styles.subhead}>{model.product.categoriesLine}</Text> : null}
|
||||
|
||||
<View style={styles.heroBox}>
|
||||
{assets.heroDataUrl ? (
|
||||
<Image src={assets.heroDataUrl} style={styles.heroImage} />
|
||||
) : (
|
||||
<Text style={styles.noImage}>{model.labels.noImage}</Text>
|
||||
)}
|
||||
<View style={styles.hero}>
|
||||
<Header title={headerTitle} logoDataUrl={assets.logoDataUrl} qrDataUrl={assets.qrDataUrl} isHero={true} />
|
||||
|
||||
<View style={styles.productRow}>
|
||||
<View style={styles.productInfoCol}>
|
||||
<View style={styles.productHero}>
|
||||
{model.product.categoriesLine ? <Text style={styles.productMeta}>{model.product.categoriesLine}</Text> : null}
|
||||
<Text style={styles.productName}>{model.product.name}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.productImageCol}>
|
||||
{assets.heroDataUrl ? (
|
||||
<Image src={assets.heroDataUrl} style={styles.heroImage} />
|
||||
) : (
|
||||
<Text style={styles.noImage}>{model.labels.noImage}</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{model.product.descriptionText ? (
|
||||
<Section title={model.labels.description} minPresenceAhead={24}>
|
||||
<Text style={styles.body}>{model.product.descriptionText}</Text>
|
||||
</Section>
|
||||
) : null}
|
||||
<Footer locale={model.locale} siteUrl={CONFIG.siteUrl} />
|
||||
|
||||
{model.technicalItems.length ? (
|
||||
<Section title={model.labels.technicalData} minPresenceAhead={24}>
|
||||
<KeyValueGrid items={model.technicalItems} />
|
||||
</Section>
|
||||
) : null}
|
||||
<View style={styles.content}>
|
||||
{model.product.descriptionText ? (
|
||||
<Section title={model.labels.description} minPresenceAhead={24}>
|
||||
<Text style={styles.body}>{model.product.descriptionText}</Text>
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
{model.technicalItems.length ? (
|
||||
<Section title={model.labels.technicalData} minPresenceAhead={24}>
|
||||
<KeyValueGrid items={model.technicalItems} />
|
||||
</Section>
|
||||
) : null}
|
||||
</View>
|
||||
</Page>
|
||||
|
||||
{/*
|
||||
Render all voltage sections in a single flow so React-PDF can paginate naturally.
|
||||
This avoids hard page breaks that waste remaining whitespace at the bottom of a page.
|
||||
Each table section has break={false} to prevent breaking within individual tables,
|
||||
but the overall flow allows tables to move to the next page if needed.
|
||||
*/}
|
||||
<Page size="A4" style={styles.page}>
|
||||
<Header title={headerTitle} logoDataUrl={assets.logoDataUrl} qrDataUrl={assets.qrDataUrl} />
|
||||
<Footer locale={model.locale} siteUrl={CONFIG.siteUrl} />
|
||||
|
||||
{model.voltageTables.map((t: DatasheetVoltageTable) => (
|
||||
<View key={t.voltageLabel} style={{ marginBottom: 14 }} break={false} minPresenceAhead={24}>
|
||||
<Text style={styles.sectionTitle}>{`${model.labels.crossSection} — ${t.voltageLabel}`}</Text>
|
||||
<View style={styles.content}>
|
||||
{model.voltageTables.map((t: DatasheetVoltageTable) => (
|
||||
<View key={t.voltageLabel} style={{ marginBottom: 14 }} break={false} minPresenceAhead={24}>
|
||||
<Text style={styles.sectionTitle}>{`${model.labels.crossSection} — ${t.voltageLabel}`}</Text>
|
||||
<View style={styles.sectionAccent} />
|
||||
<DenseTable table={{ columns: t.columns, rows: t.rows }} firstColLabel={firstColLabel} />
|
||||
</View>
|
||||
))}
|
||||
|
||||
<DenseTable table={{ columns: t.columns, rows: t.rows }} firstColLabel={firstColLabel} />
|
||||
</View>
|
||||
))}
|
||||
|
||||
{model.legendItems.length ? (
|
||||
<Section title={model.locale === 'de' ? 'ABKÜRZUNGEN' : 'ABBREVIATIONS'} minPresenceAhead={24}>
|
||||
<KeyValueGrid items={model.legendItems} />
|
||||
</Section>
|
||||
) : null}
|
||||
{model.legendItems.length ? (
|
||||
<Section title={model.locale === 'de' ? 'ABKÜRZUNGEN' : 'ABBREVIATIONS'} minPresenceAhead={24}>
|
||||
<KeyValueGrid items={model.legendItems} />
|
||||
</Section>
|
||||
) : null}
|
||||
</View>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
|
||||
@@ -14,9 +14,9 @@ export function Footer(props: { locale: 'en' | 'de'; siteUrl?: string }): React.
|
||||
|
||||
return (
|
||||
<View style={styles.footer} fixed>
|
||||
<Text>{siteUrl}</Text>
|
||||
<Text>{date}</Text>
|
||||
<Text render={({ pageNumber, totalPages }) => `${pageNumber}/${totalPages}`} />
|
||||
<Text style={styles.footerBrand}>KLZ CABLES</Text>
|
||||
<Text style={styles.footerText}>{date}</Text>
|
||||
<Text style={styles.footerText} render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,17 +3,16 @@ import { Image, Text, View } from '@react-pdf/renderer';
|
||||
|
||||
import { styles } from '../styles';
|
||||
|
||||
export function Header(props: { title: string; logoDataUrl?: string | null; qrDataUrl?: string | null }): React.ReactElement {
|
||||
export function Header(props: { title: string; logoDataUrl?: string | null; qrDataUrl?: string | null; isHero?: boolean }): React.ReactElement {
|
||||
const { isHero = false } = props;
|
||||
|
||||
return (
|
||||
<View style={styles.header} fixed>
|
||||
<View style={isHero ? styles.header : [styles.header, { paddingHorizontal: 0, backgroundColor: 'transparent', borderBottomWidth: 0, marginBottom: 24, paddingTop: 40 }]}>
|
||||
<View style={styles.headerLeft}>
|
||||
{props.logoDataUrl ? (
|
||||
<Image src={props.logoDataUrl} style={styles.logo} />
|
||||
) : (
|
||||
<View style={styles.brandFallback}>
|
||||
<Text style={styles.brandFallbackKlz}>KLZ</Text>
|
||||
<Text style={styles.brandFallbackCables}>Cables</Text>
|
||||
</View>
|
||||
<Text style={styles.brandFallback}>KLZ</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.headerRight}>
|
||||
|
||||
@@ -8,37 +8,25 @@ export function KeyValueGrid(props: { items: KeyValueItem[] }): React.ReactEleme
|
||||
const items = (props.items || []).filter(i => i.label && i.value);
|
||||
if (!items.length) return null;
|
||||
|
||||
// 4-column layout: (label, value, label, value)
|
||||
const rows: Array<[KeyValueItem, KeyValueItem | null]> = [];
|
||||
for (let i = 0; i < items.length; i += 2) {
|
||||
rows.push([items[i], items[i + 1] || null]);
|
||||
}
|
||||
|
||||
// 2-column layout: (label, value)
|
||||
return (
|
||||
<View style={styles.kvGrid}>
|
||||
{rows.map(([left, right], rowIndex) => {
|
||||
const isLast = rowIndex === rows.length - 1;
|
||||
const leftValue = left.unit ? `${left.value} ${left.unit}` : left.value;
|
||||
const rightValue = right ? (right.unit ? `${right.value} ${right.unit}` : right.value) : '';
|
||||
{items.map((item, rowIndex) => {
|
||||
const isLast = rowIndex === items.length - 1;
|
||||
const value = item.unit ? `${item.value} ${item.unit}` : item.value;
|
||||
|
||||
return (
|
||||
<View
|
||||
key={`${left.label}-${rowIndex}`}
|
||||
key={`${item.label}-${rowIndex}`}
|
||||
style={[styles.kvRow, rowIndex % 2 === 0 ? styles.kvRowAlt : null, isLast ? styles.kvRowLast : null]}
|
||||
wrap={false}
|
||||
minPresenceAhead={12}
|
||||
>
|
||||
<View style={[styles.kvCell, { width: '18%' }]}>
|
||||
<Text style={styles.kvLabelText}>{left.label}</Text>
|
||||
<View style={[styles.kvCell, { width: '50%' }]}>
|
||||
<Text style={styles.kvLabelText}>{item.label}</Text>
|
||||
</View>
|
||||
<View style={[styles.kvCell, styles.kvMidDivider, { width: '32%' }]}>
|
||||
<Text style={styles.kvValueText}>{leftValue}</Text>
|
||||
</View>
|
||||
<View style={[styles.kvCell, { width: '18%' }]}>
|
||||
<Text style={styles.kvLabelText}>{right?.label || ''}</Text>
|
||||
</View>
|
||||
<View style={[styles.kvCell, { width: '32%' }]}>
|
||||
<Text style={styles.kvValueText}>{rightValue}</Text>
|
||||
<View style={[styles.kvCell, { width: '50%' }]}>
|
||||
<Text style={styles.kvValueText}>{value}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -11,8 +11,9 @@ export function Section(props: {
|
||||
}): React.ReactElement {
|
||||
const boxed = props.boxed ?? true;
|
||||
return (
|
||||
<View style={boxed ? styles.section : styles.sectionPlain} minPresenceAhead={props.minPresenceAhead}>
|
||||
<View style={styles.section} minPresenceAhead={props.minPresenceAhead}>
|
||||
<Text style={styles.sectionTitle}>{props.title}</Text>
|
||||
<View style={styles.sectionAccent} />
|
||||
{props.children}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -5,146 +5,212 @@ import { Font, StyleSheet } from '@react-pdf/renderer';
|
||||
Font.registerHyphenationCallback(word => [word]);
|
||||
|
||||
export const COLORS = {
|
||||
navy: '#0E2A47',
|
||||
mediumGray: '#6B7280',
|
||||
darkGray: '#1F2933',
|
||||
lightGray: '#E6E9ED',
|
||||
almostWhite: '#F8F9FA',
|
||||
headerBg: '#F6F8FB',
|
||||
primary: '#001a4d',
|
||||
primaryDark: '#000d26',
|
||||
accent: '#82ed20',
|
||||
textPrimary: '#111827',
|
||||
textSecondary: '#4b5563',
|
||||
textLight: '#9ca3af',
|
||||
neutral: '#f8f9fa',
|
||||
border: '#e5e7eb',
|
||||
} as const;
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
page: {
|
||||
paddingTop: 54,
|
||||
paddingLeft: 54,
|
||||
paddingRight: 54,
|
||||
paddingBottom: 72,
|
||||
paddingTop: 0,
|
||||
paddingLeft: 30,
|
||||
paddingRight: 30,
|
||||
paddingBottom: 60,
|
||||
fontFamily: 'Helvetica',
|
||||
fontSize: 10,
|
||||
color: COLORS.darkGray,
|
||||
color: COLORS.textPrimary,
|
||||
backgroundColor: '#FFFFFF',
|
||||
},
|
||||
|
||||
// Hero-style header
|
||||
hero: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
paddingTop: 30,
|
||||
paddingBottom: 0,
|
||||
paddingHorizontal: 0,
|
||||
marginBottom: 20,
|
||||
position: 'relative',
|
||||
borderBottomWidth: 0,
|
||||
borderBottomColor: COLORS.border,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: COLORS.headerBg,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: COLORS.lightGray,
|
||||
marginBottom: 16,
|
||||
marginBottom: 24,
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
headerLeft: { flexDirection: 'row', alignItems: 'center', gap: 10 },
|
||||
logo: { width: 110, height: 24, objectFit: 'contain' },
|
||||
brandFallback: { flexDirection: 'row', alignItems: 'baseline', gap: 6 },
|
||||
brandFallbackKlz: { fontSize: 18, fontWeight: 700, color: COLORS.navy },
|
||||
brandFallbackCables: { fontSize: 10, color: COLORS.mediumGray },
|
||||
logo: { width: 100, height: 22, objectFit: 'contain' },
|
||||
brandFallback: { fontSize: 20, fontWeight: 700, color: COLORS.primaryDark, letterSpacing: 1, textTransform: 'uppercase' },
|
||||
headerRight: { flexDirection: 'row', alignItems: 'center', gap: 10 },
|
||||
headerTitle: { fontSize: 9, fontWeight: 700, color: COLORS.navy, letterSpacing: 0.2 },
|
||||
qr: { width: 34, height: 34, objectFit: 'contain' },
|
||||
headerTitle: { fontSize: 9, fontWeight: 700, color: COLORS.primary, letterSpacing: 1.5, textTransform: 'uppercase' },
|
||||
qr: { width: 30, height: 30, objectFit: 'contain' },
|
||||
|
||||
productRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 20,
|
||||
},
|
||||
productInfoCol: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
productImageCol: {
|
||||
flex: 1,
|
||||
height: 120,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.border,
|
||||
backgroundColor: '#FFFFFF',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
|
||||
productHero: {
|
||||
marginTop: 0,
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
productName: {
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
color: COLORS.primaryDark,
|
||||
marginBottom: 0,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
productMeta: {
|
||||
fontSize: 9,
|
||||
color: COLORS.textSecondary,
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 1,
|
||||
marginBottom: 4,
|
||||
},
|
||||
|
||||
content: {
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
left: 54,
|
||||
right: 54,
|
||||
bottom: 36,
|
||||
paddingTop: 10,
|
||||
left: 30,
|
||||
right: 30,
|
||||
bottom: 30,
|
||||
paddingTop: 16,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: COLORS.lightGray,
|
||||
borderTopColor: COLORS.border,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
fontSize: 8,
|
||||
color: COLORS.mediumGray,
|
||||
alignItems: 'center',
|
||||
},
|
||||
footerBrand: { fontSize: 9, fontWeight: 700, color: COLORS.primaryDark, textTransform: 'uppercase', letterSpacing: 1 },
|
||||
footerText: { fontSize: 8, color: COLORS.textLight, fontWeight: 500, textTransform: 'uppercase', letterSpacing: 0.5 },
|
||||
|
||||
h1: { fontSize: 18, fontWeight: 700, color: COLORS.navy, marginBottom: 6 },
|
||||
subhead: { fontSize: 10.5, color: COLORS.mediumGray, marginBottom: 14 },
|
||||
h1: { fontSize: 22, fontWeight: 700, color: COLORS.primaryDark, marginBottom: 8, textTransform: 'uppercase' },
|
||||
subhead: { fontSize: 10, fontWeight: 700, color: COLORS.textSecondary, marginBottom: 16, textTransform: 'uppercase', letterSpacing: 0.5 },
|
||||
|
||||
heroBox: {
|
||||
height: 110,
|
||||
height: 180,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.lightGray,
|
||||
backgroundColor: COLORS.almostWhite,
|
||||
marginBottom: 16,
|
||||
borderColor: COLORS.border,
|
||||
backgroundColor: '#FFFFFF',
|
||||
marginBottom: 24,
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
},
|
||||
heroImage: { width: '100%', height: '100%', objectFit: 'contain' },
|
||||
noImage: { fontSize: 8, color: COLORS.mediumGray, paddingHorizontal: 12 },
|
||||
noImage: { fontSize: 8, color: COLORS.textLight, textAlign: 'center' },
|
||||
|
||||
section: {
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.lightGray,
|
||||
padding: 14,
|
||||
marginBottom: 14,
|
||||
},
|
||||
sectionPlain: {
|
||||
paddingVertical: 2,
|
||||
marginBottom: 12,
|
||||
marginBottom: 10,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 10,
|
||||
fontSize: 14,
|
||||
fontWeight: 700,
|
||||
color: COLORS.navy,
|
||||
color: COLORS.primaryDark,
|
||||
marginBottom: 8,
|
||||
letterSpacing: 0.2,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: -0.2,
|
||||
},
|
||||
body: { fontSize: 10, lineHeight: 1.5, color: COLORS.darkGray },
|
||||
sectionAccent: {
|
||||
width: 30,
|
||||
height: 3,
|
||||
backgroundColor: COLORS.accent,
|
||||
marginBottom: 8,
|
||||
borderRadius: 1.5,
|
||||
},
|
||||
body: { fontSize: 10, lineHeight: 1.6, color: COLORS.textSecondary },
|
||||
|
||||
kvGrid: {
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.lightGray,
|
||||
borderColor: COLORS.border,
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
kvRow: {
|
||||
flexDirection: 'row',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: COLORS.lightGray,
|
||||
borderBottomColor: COLORS.border,
|
||||
},
|
||||
kvRowAlt: { backgroundColor: COLORS.almostWhite },
|
||||
kvRowAlt: { backgroundColor: COLORS.neutral },
|
||||
kvRowLast: { borderBottomWidth: 0 },
|
||||
kvCell: { paddingVertical: 6, paddingHorizontal: 8 },
|
||||
// Visual separator between (label,value) pairs in the 4-col KV grid.
|
||||
// Matches the engineering-table look and improves scanability.
|
||||
kvCell: { paddingVertical: 3, paddingHorizontal: 12 },
|
||||
kvMidDivider: {
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: COLORS.lightGray,
|
||||
borderRightColor: COLORS.border,
|
||||
},
|
||||
kvLabelText: { fontSize: 8.5, fontWeight: 700, color: COLORS.mediumGray },
|
||||
kvValueText: { fontSize: 9.5, color: COLORS.darkGray },
|
||||
kvLabelText: { fontSize: 8, fontWeight: 700, color: COLORS.primaryDark, textTransform: 'uppercase', letterSpacing: 0.3 },
|
||||
kvValueText: { fontSize: 9, color: COLORS.textPrimary, fontWeight: 500 },
|
||||
|
||||
tableWrap: { width: '100%', borderWidth: 1, borderColor: COLORS.lightGray, marginBottom: 14 },
|
||||
tableWrap: {
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.border,
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 16,
|
||||
},
|
||||
tableHeader: {
|
||||
width: '100%',
|
||||
flexDirection: 'row',
|
||||
backgroundColor: '#FFFFFF',
|
||||
backgroundColor: COLORS.neutral,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: COLORS.lightGray,
|
||||
borderBottomColor: COLORS.border,
|
||||
},
|
||||
tableHeaderCell: {
|
||||
paddingVertical: 5,
|
||||
paddingHorizontal: 4,
|
||||
fontSize: 6.6,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 6,
|
||||
fontSize: 7,
|
||||
fontWeight: 700,
|
||||
color: COLORS.navy,
|
||||
color: COLORS.primaryDark,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.2,
|
||||
},
|
||||
tableHeaderCellCfg: {
|
||||
paddingHorizontal: 6,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
tableHeaderCellDivider: {
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: COLORS.lightGray,
|
||||
borderRightColor: COLORS.border,
|
||||
},
|
||||
tableRow: { width: '100%', flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: COLORS.lightGray },
|
||||
tableRowAlt: { backgroundColor: COLORS.almostWhite },
|
||||
tableCell: { paddingVertical: 4, paddingHorizontal: 4, fontSize: 6.6, color: COLORS.darkGray },
|
||||
tableRow: { width: '100%', flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: COLORS.border },
|
||||
tableRowAlt: { backgroundColor: '#FFFFFF' },
|
||||
tableCell: { paddingVertical: 6, paddingHorizontal: 6, fontSize: 7, color: COLORS.textSecondary, fontWeight: 500 },
|
||||
tableCellCfg: {
|
||||
paddingHorizontal: 6,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
tableCellDivider: {
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: COLORS.lightGray,
|
||||
borderRightColor: COLORS.border,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user