/** * Offer visuals (customer logo + website screenshot) shared by the lead page and the * quote modals (fulfillment + LogShip Cloud). * * Persistence on the lead (so it is done ONCE, not again in every offer): * - logo → AD_User.AD_Image_ID (the lead page already prefills `logoDataUrl` from it) * - screenshot → offer_conditions.visuals = { screenshotDataUrl, website, capturedAt } * via PUT /api/leads/:id/visuals. The modals call `saveLeadVisuals` after a capture / upload * when their prefill source is a lead and emit `visuals-saved` so the page updates in place. */ export const useOfferVisuals = () => { /** Reads a PNG/JPEG file and returns a (downscaled, ≤ 600×300) data URL. */ const readLogoFile = (file: File): Promise => new Promise((resolve, reject) => { if (!file || !/^image\/(png|jpe?g)$/i.test(file.type)) { reject(new Error('Bitte eine PNG- oder JPG-Datei wählen')) return } const reader = new FileReader() reader.onerror = () => reject(new Error('Datei konnte nicht gelesen werden')) reader.onload = () => { const img = new Image() img.onload = () => { // Downscale big uploads client-side — the PDF renders it at most ~120 pt wide. const maxW = 600 const maxH = 300 const scale = Math.min(1, maxW / img.width, maxH / img.height) if (scale < 1 || file.size > 400 * 1024) { const canvas = document.createElement('canvas') canvas.width = Math.max(1, Math.round(img.width * scale)) canvas.height = Math.max(1, Math.round(img.height * scale)) canvas.getContext('2d')?.drawImage(img, 0, 0, canvas.width, canvas.height) resolve(canvas.toDataURL(file.type === 'image/png' ? 'image/png' : 'image/jpeg', 0.85)) } else { resolve(String(reader.result)) } } img.onerror = () => reject(new Error('Bitte eine PNG- oder JPG-Datei wählen')) img.src = String(reader.result) } reader.readAsDataURL(file) }) /** Captures the website hero banner server-side (mShots) → JPEG data URL. Throws with a message. */ const captureWebsiteScreenshot = async (url: string): Promise => { const res: any = await $fetch('/api/offers/website-screenshot', { method: 'POST', headers: useRequestHeaders(['cookie']), body: { url: String(url || '').trim() } }) if (Number(res?.status) === 200 && res?.dataUrl) return res.dataUrl throw new Error(res?.message || 'Screenshot konnte nicht erstellt werden') } /** Persists logo and/or screenshot on the lead. `null` clears, `undefined` leaves untouched. */ const saveLeadVisuals = (leadId: any, patch: { logoDataUrl?: string | null; screenshotDataUrl?: string | null; website?: string }) => $fetch(`/api/leads/${leadId}/visuals`, { method: 'PUT', headers: useRequestHeaders(['cookie']), body: patch }) /** host + path without scheme / www / trailing slash — "is this screenshot still for this website?" */ const normalizeWebsite = (u: any): string => String(u || '').trim().toLowerCase() .replace(/^https?:\/\//, '').replace(/^www\./, '').replace(/\/+$/, '') return { readLogoFile, captureWebsiteScreenshot, saveLeadVisuals, normalizeWebsite } }