mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
Violation reports: replace fining stamp with bold red header line
The diagonal semi-transparent "RECOMMENDED FOR FINING" stamp looked poor over the report content. Remove it (drawRecommendedForFiningStamp + both call sites) and instead print a bold red "RECOMMENDED FOR FINING" line just beneath the navy header bar on both the Summary and Timeline reports, shifting the owner block down to make room. Non-fining reports are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,50 +11,20 @@ import { formatPhotoTimestamp, getPhotoDateFromUrl } from './exifUtils';
|
|||||||
// Safe yield helper to prevent main thread blocking and ensure it resolves
|
// Safe yield helper to prevent main thread blocking and ensure it resolves
|
||||||
const safeYieldToMain = () => new Promise(resolve => setTimeout(resolve, 0));
|
const safeYieldToMain = () => new Promise(resolve => setTimeout(resolve, 0));
|
||||||
|
|
||||||
// Draws a diagonal red "RECOMMENDED FOR FINING" stamp across the current page
|
// Draws a clean, bold red "RECOMMENDED FOR FINING" line just beneath the navy
|
||||||
const drawRecommendedForFiningStamp = (doc) => {
|
// header bar (replaces the old diagonal stamp). Returns the y of the owner
|
||||||
|
// block so callers leave room for the banner only when it's shown.
|
||||||
|
const FINING_HEADER_COLOR = '#c0392b';
|
||||||
|
const drawRecommendedForFiningHeader = (doc, margin) => {
|
||||||
try {
|
try {
|
||||||
const pageWidth = doc.internal.pageSize.getWidth();
|
|
||||||
// Place stamp near the top-right of the page so it doesn't cover
|
|
||||||
// photo evidence in the middle/bottom of the page.
|
|
||||||
const stampW = Math.min(pageWidth - 160, 300);
|
|
||||||
const stampH = 56;
|
|
||||||
const cx = pageWidth - (stampW / 2) - 40;
|
|
||||||
const cy = 110;
|
|
||||||
|
|
||||||
doc.saveGraphicsState && doc.saveGraphicsState();
|
|
||||||
// Lighter opacity so underlying content remains fully readable
|
|
||||||
if (doc.setGState && doc.GState) {
|
|
||||||
try { doc.setGState(new doc.GState({ opacity: 0.55 })); } catch (e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate around center (jsPDF rotates around the x,y origin)
|
|
||||||
const angle = -14;
|
|
||||||
const rad = (angle * Math.PI) / 180;
|
|
||||||
const cos = Math.cos(rad);
|
|
||||||
const sin = Math.sin(rad);
|
|
||||||
|
|
||||||
// Compute rotated rect corner so the rect is centered at (cx, cy)
|
|
||||||
const rx = cx - (stampW / 2) * cos + (stampH / 2) * sin;
|
|
||||||
const ry = cy - (stampW / 2) * sin - (stampH / 2) * cos;
|
|
||||||
|
|
||||||
doc.setDrawColor(200, 0, 0);
|
|
||||||
doc.setTextColor(200, 0, 0);
|
|
||||||
doc.setLineWidth(2.5);
|
|
||||||
doc.roundedRect(rx, ry, stampW, stampH, 10, 10, 'S', angle);
|
|
||||||
|
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(20);
|
doc.setFontSize(12);
|
||||||
// Text position: center of stamp, jsPDF text rotates around the text origin
|
doc.setTextColor(FINING_HEADER_COLOR);
|
||||||
doc.text('RECOMMENDED FOR FINING', cx, cy + 7, { align: 'center', angle: -angle });
|
doc.text('RECOMMENDED FOR FINING', margin, 75);
|
||||||
|
// Reset to the standard header text colour for the owner block that follows.
|
||||||
doc.restoreGraphicsState && doc.restoreGraphicsState();
|
doc.setTextColor('#2c3e50');
|
||||||
// Reset state
|
|
||||||
doc.setTextColor(0, 0, 0);
|
|
||||||
doc.setDrawColor(0, 0, 0);
|
|
||||||
doc.setLineWidth(1);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Failed to draw recommended-for-fining stamp', e);
|
console.warn('Failed to draw recommended-for-fining header', e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -332,6 +302,12 @@ export const generateViolationStatusPDF = async (violations, settings = {}, onPr
|
|||||||
doc.text(`Generated: ${formatDateSafe(new Date())}`, pageWidth - margin, 45, { align: 'right' });
|
doc.text(`Generated: ${formatDateSafe(new Date())}`, pageWidth - margin, 45, { align: 'right' });
|
||||||
currentY = 80;
|
currentY = 80;
|
||||||
|
|
||||||
|
// Bold red "RECOMMENDED FOR FINING" header line (replaces the old stamp).
|
||||||
|
if (isRecommendedForFining(vData.status)) {
|
||||||
|
drawRecommendedForFiningHeader(doc, margin);
|
||||||
|
currentY = 96;
|
||||||
|
}
|
||||||
|
|
||||||
// Owner/Address
|
// Owner/Address
|
||||||
doc.setFont(fonts.subHeader.name, fonts.subHeader.style); doc.setFontSize(14); doc.setTextColor(colors.text);
|
doc.setFont(fonts.subHeader.name, fonts.subHeader.style); doc.setFontSize(14); doc.setTextColor(colors.text);
|
||||||
doc.text(vData.owner || 'Unknown Owner', margin, currentY);
|
doc.text(vData.owner || 'Unknown Owner', margin, currentY);
|
||||||
@@ -491,10 +467,6 @@ export const generateViolationStatusPDF = async (violations, settings = {}, onPr
|
|||||||
doc.setFont(fonts.footer.name, fonts.footer.style); doc.setFontSize(fonts.footer.size); doc.setTextColor(colors.subHeader);
|
doc.setFont(fonts.footer.name, fonts.footer.style); doc.setFontSize(fonts.footer.size); doc.setTextColor(colors.subHeader);
|
||||||
doc.text(pageStr, pageWidth / 2, pageHeight - 15, { align: 'center' });
|
doc.text(pageStr, pageWidth / 2, pageHeight - 15, { align: 'center' });
|
||||||
|
|
||||||
if (isRecommendedForFining(vData.status)) {
|
|
||||||
drawRecommendedForFiningStamp(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
if (errorCount <= 5) {
|
if (errorCount <= 5) {
|
||||||
@@ -562,6 +534,12 @@ export const generateViolationTimelinePDF = async (violations, settings = {}, on
|
|||||||
doc.text(`Generated: ${formatDateSafe(new Date())}`, pageWidth - margin, 45, { align: 'right' });
|
doc.text(`Generated: ${formatDateSafe(new Date())}`, pageWidth - margin, 45, { align: 'right' });
|
||||||
currentY = 80;
|
currentY = 80;
|
||||||
|
|
||||||
|
// Bold red "RECOMMENDED FOR FINING" header line (replaces the old stamp).
|
||||||
|
if (isRecommendedForFining(vData.status)) {
|
||||||
|
drawRecommendedForFiningHeader(doc, margin);
|
||||||
|
currentY = 96;
|
||||||
|
}
|
||||||
|
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(14);
|
doc.setFontSize(14);
|
||||||
doc.setTextColor('#2c3e50');
|
doc.setTextColor('#2c3e50');
|
||||||
@@ -590,10 +568,6 @@ export const generateViolationTimelinePDF = async (violations, settings = {}, on
|
|||||||
doc.setTextColor('#34495e');
|
doc.setTextColor('#34495e');
|
||||||
doc.text(pageStr, pageWidth / 2, pageHeight - 15, { align: 'center' });
|
doc.text(pageStr, pageWidth / 2, pageHeight - 15, { align: 'center' });
|
||||||
|
|
||||||
if (isRecommendedForFining(vData.status)) {
|
|
||||||
drawRecommendedForFiningStamp(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
if (errorCount <= 5) console.error(`Error processing timeline page ${i}:`, e);
|
if (errorCount <= 5) console.error(`Error processing timeline page ${i}:`, e);
|
||||||
|
|||||||
Reference in New Issue
Block a user