fix(pdf): fix missing logos and product images in datasheets, update pipeline
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Successful in 2m20s
Build & Deploy / 🏗️ Build (push) Successful in 3m56s
Build & Deploy / 🚀 Deploy (push) Successful in 22s
Build & Deploy / 🧪 Post-Deploy Verification (push) Failing after 4m30s
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-03-08 01:55:52 +01:00
parent 7583540de2
commit d575e5924a
58 changed files with 67 additions and 25 deletions

View File

@@ -197,6 +197,7 @@ interface ProductData {
applicationHtml?: string;
images?: string[];
featuredImage?: string | null;
logoDataUrl?: string | null;
categories?: Array<{ name: string }>;
attributes?: Array<{ name: string; options: string[] }>;
}
@@ -204,7 +205,7 @@ interface ProductData {
export interface PDFDatasheetProps {
product: ProductData;
locale: 'en' | 'de';
logoUrl?: string;
logoDataUrl?: string | null;
technicalItems?: KeyValueItem[];
voltageTables?: DatasheetVoltageTable[];
legendItems?: KeyValueItem[];
@@ -485,8 +486,15 @@ export const PDFDatasheet: React.FC<PDFDatasheetProps> = ({
<Page size="A4" style={styles.page}>
<View style={styles.hero}>
<View style={styles.header}>
<View>
<Text style={styles.logoText}>KLZ</Text>
<View style={{ width: 80 }}>
{product.logoDataUrl || (product as any).logoDataUrl ? (
<Image
src={product.logoDataUrl || (product as any).logoDataUrl}
style={{ width: '100%', objectFit: 'contain' }}
/>
) : (
<Text style={styles.logoText}>KLZ</Text>
)}
</View>
<Text style={styles.docTitle}>{labels.productDatasheet}</Text>
</View>
@@ -586,7 +594,16 @@ export const PDFDatasheet: React.FC<PDFDatasheetProps> = ({
</View>
<View style={styles.footer} fixed>
<Text style={styles.footerBrand}>KLZ CABLES</Text>
<View style={{ width: 60 }}>
{product.logoDataUrl || (product as any).logoDataUrl ? (
<Image
src={product.logoDataUrl || (product as any).logoDataUrl}
style={{ width: '100%', objectFit: 'contain' }}
/>
) : (
<Text style={styles.footerBrand}>KLZ CABLES</Text>
)}
</View>
<Text style={styles.footerText}>
{new Date().toLocaleDateString(locale === 'en' ? 'en-US' : 'de-DE', {
year: 'numeric',

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -15,6 +15,7 @@ SOURCE_ENV="${1:-}" # local | testing | staging | prod
TARGET_ENV="${2:-}" # testing | staging | prod
SSH_HOST="root@alpha.mintel.me"
LOCAL_MEDIA_DIR="./public/media"
LOCAL_DATASHEETS_DIR="./public/datasheets"
DRY_RUN=""
CHECKSUM=""
@@ -38,6 +39,16 @@ get_media_path() {
esac
}
get_datasheets_path() {
case "$1" in
local) echo "$LOCAL_DATASHEETS_DIR" ;;
testing) echo "/home/deploy/sites/testing.klz-cables.com/public/datasheets" ;;
staging) echo "/home/deploy/sites/staging.klz-cables.com/public/datasheets" ;;
prod|production) echo "/home/deploy/sites/klz-cables.com/public/datasheets" ;;
*) echo "❌ Unknown environment: $1"; exit 1 ;;
esac
}
get_app_container() {
case "$1" in
testing) echo "klz-testing-klz-app-1" ;;
@@ -52,35 +63,39 @@ TGT_PATH=$(get_media_path "$TARGET_ENV")
TGT_CONTAINER=$(get_app_container "$TARGET_ENV")
echo "🚀 Syncing assets: $SOURCE_ENV$TARGET_ENV"
echo "📂 Source: $SRC_PATH"
echo "📂 Target: $TGT_PATH"
# ── Execution ──────────────────────────────────────────────────────────────
if [[ ! -d "$SRC_PATH" ]] && [[ "$SOURCE_ENV" == "local" ]]; then
echo "❌ Source directory does not exist: $SRC_PATH"
exit 1
fi
# ── Media Sync ─────────────────────────────────────────────────────────────
echo "🖼️ Syncing Media..."
if [[ "$SOURCE_ENV" == "local" ]]; then
# Local → Remote
echo "📡 Running rsync..."
rsync -avzi $CHECKSUM --delete --progress $DRY_RUN "$SRC_PATH/" "$SSH_HOST:$TGT_PATH/"
elif [[ "$TARGET_ENV" == "local" ]]; then
# Remote → Local
mkdir -p "$LOCAL_MEDIA_DIR"
echo "📡 Running rsync..."
rsync -avzi $CHECKSUM --delete --progress $DRY_RUN "$SSH_HOST:$SRC_PATH/" "$TGT_PATH/"
else
# Remote → Remote (e.g., testing → staging)
echo "📡 Running remote rsync..."
ssh "$SSH_HOST" "rsync -avzi $CHECKSUM --delete --progress $DRY_RUN $SRC_PATH/ $TGT_PATH/"
fi
# ── Datasheets Sync ────────────────────────────────────────────────────────
echo "📄 Syncing Datasheets..."
SRC_DS_PATH=$(get_datasheets_path "$SOURCE_ENV")
TGT_DS_PATH=$(get_datasheets_path "$TARGET_ENV")
if [[ "$SOURCE_ENV" == "local" ]]; then
ssh "$SSH_HOST" "mkdir -p $TGT_DS_PATH"
rsync -avzi $CHECKSUM --delete --progress $DRY_RUN "$SRC_DS_PATH/" "$SSH_HOST:$TGT_DS_PATH/"
elif [[ "$TARGET_ENV" == "local" ]]; then
mkdir -p "$LOCAL_DATASHEETS_DIR"
rsync -avzi $CHECKSUM --delete --progress $DRY_RUN "$SSH_HOST:$SRC_DS_PATH/" "$TGT_DS_PATH/"
else
ssh "$SSH_HOST" "mkdir -p $TGT_DS_PATH && rsync -avzi $CHECKSUM --delete --progress $DRY_RUN $SRC_DS_PATH/ $TGT_DS_PATH/"
fi
# Fix ownership on remote target if it's not local
if [[ "$TARGET_ENV" != "local" && -z "$DRY_RUN" ]]; then
echo "🔑 Fixing media file permissions on $TARGET_ENV..."
ssh "$SSH_HOST" "docker exec -u 0 $TGT_CONTAINER chown -R 1001:65533 /app/public/media/ 2>/dev/null || true"
echo "🔑 Fixing datasheet permissions..."
ssh "$SSH_HOST" "chown -R 1001:1001 $TGT_DS_PATH 2>/dev/null || true"
fi
echo "✅ Asset sync complete!"

View File

@@ -268,7 +268,10 @@ async function processChunk(
}
// Load assets as Data URLs for React-PDF
const heroDataUrl = await loadImageAsPngDataUrl(model.product.heroSrc);
const [heroDataUrl, logoDataUrl] = await Promise.all([
loadImageAsPngDataUrl(model.product.heroSrc),
loadImageAsPngDataUrl('/logo-black.svg'),
]);
const fileName = generateFileName(product, locale);
const voltageType = (product as any).voltageType || 'other';
@@ -281,14 +284,18 @@ async function processChunk(
// Render using the unified component
const element = (
<PDFDatasheet
product={{
...model.product,
featuredImage: heroDataUrl,
}}
product={
{
...model.product,
featuredImage: heroDataUrl,
logoDataUrl,
} as any
}
locale={locale}
technicalItems={model.technicalItems}
voltageTables={model.voltageTables}
legendItems={model.legendItems}
logoDataUrl={logoDataUrl}
/>
);

View File

@@ -147,7 +147,10 @@ function compactCellForDenseTable(
function resolveMediaToLocalPath(urlOrPath: string | null | undefined): string | null {
if (!urlOrPath) return null;
if (urlOrPath.startsWith('/')) return urlOrPath;
if (urlOrPath.startsWith('/')) {
// Handle Payload API URL prefix: /api/media/file/filename.ext -> /media/filename.ext
return urlOrPath.replace(/^\/api\/media\/file\//, '/media/');
}
if (/^media\//i.test(urlOrPath)) return `/${urlOrPath}`;
const mapped = ASSET_MAP[urlOrPath];
if (mapped) {