Files
pn-new-crm/src/lib/email/tracking-pixel.ts
Matt 221ae5784e chore(autonomous-session): consolidate uncommitted work from prior session
Bundles the prior autonomous-session output that was sitting unstaged:

- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
  never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
  after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
  redirects (ocr to ai, reports to dashboard, invitations to users),
  docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
  flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
  let-reassign), set-state-in-effect disables in CountryFlag and
  UploadForSigning preview-bytes effect, unused 'confirm' destructures in
  interest contract + reservation tabs, unescaped apostrophe in test-template
  card copy
2026-05-23 00:52:59 +02:00

47 lines
1.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Open-tracking pixel injector (Phase 4b). Appends a 1×1 transparent
* image pointing at /api/public/email-pixel/[sendId] to outbound HTML
* emails. The pixel endpoint records the open + cross-posts the event
* to Umami.
*
* Sites that want to opt out of tracking simply don't call this helper.
* The pixel URL is unguessable per-send (UUID), but a `track_opens=false`
* row in `document_sends` makes the endpoint a no-op even if someone
* does guess one.
*
* Privacy: respects EMAIL_REDIRECT_TO (no pixel injected when dev
* redirect is active) so a re-routed message doesn't fire a fake open.
*/
import { env } from '@/lib/env';
interface InjectOptions {
/** Public base URL of the CRM (e.g. https://crm.portnimara.com).
* Required so the pixel link is absolute - relative URLs break in
* email clients. */
appBaseUrl: string;
/** UUID of the row in `document_sends`. */
sendId: string;
}
/**
* Append a 1×1 tracking pixel just before `</body>` (or at the end of the
* document if no `</body>` is present). Returns the HTML unchanged when
* EMAIL_REDIRECT_TO is set so dev-mode re-routing doesn't generate
* misleading open events.
*/
export function injectTrackingPixel(html: string, opts: InjectOptions): string {
if (env.EMAIL_REDIRECT_TO) return html;
const base = opts.appBaseUrl.replace(/\/$/, '');
const pixelUrl = `${base}/api/public/email-pixel/${opts.sendId}`;
const pixelTag =
`<img src="${pixelUrl}" width="1" height="1" alt="" ` +
`style="display:block;border:0;margin:0;padding:0" />`;
if (html.includes('</body>')) {
return html.replace('</body>', `${pixelTag}</body>`);
}
return html + pixelTag;
}