hooks in config/services.php/.env.example, and updated wizard steps/controllers to store session payloads, attach packages, and surface localized success/error states. - Retooled payment handling for both Stripe and PayPal, adding richer status management in CheckoutController/ PayPalController, fallback flows in the wizard’s PaymentStep.tsx, and fresh feature tests for intent creation, webhooks, and the wizard CTA. - Introduced a consent-aware Matomo analytics stack: new consent context, cookie-banner UI, useAnalytics/ useCtaExperiment hooks, and MatomoTracker component, then instrumented marketing pages (Home, Packages, Checkout) with localized copy and experiment tracking. - Polished package presentation across marketing UIs by centralizing formatting in PresentsPackages, surfacing localized description tables/placeholders, tuning badges/layouts, and syncing guest/marketing translations. - Expanded docs & reference material (docs/prp/*, TODOs, public gallery overview) and added a Playwright smoke test for the hero CTA while reconciling outstanding checklist items.
105 lines
2.7 KiB
TypeScript
105 lines
2.7 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { usePage } from '@inertiajs/react';
|
|
import { useConsent } from '@/contexts/consent';
|
|
|
|
export type MatomoConfig = {
|
|
enabled: boolean;
|
|
url?: string;
|
|
siteId?: string;
|
|
};
|
|
|
|
declare global {
|
|
interface Window {
|
|
_paq?: any[];
|
|
}
|
|
}
|
|
|
|
interface MatomoTrackerProps {
|
|
config: MatomoConfig | undefined;
|
|
}
|
|
|
|
const MatomoTracker: React.FC<MatomoTrackerProps> = ({ config }) => {
|
|
const page = usePage();
|
|
const { hasConsent } = useConsent();
|
|
const analyticsConsent = hasConsent('analytics');
|
|
|
|
useEffect(() => {
|
|
if (!config?.enabled || !config.url || !config.siteId || typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const base = config.url.replace(/\/$/, '');
|
|
const scriptSelector = `script[data-matomo="${base}"]`;
|
|
|
|
if (!analyticsConsent) {
|
|
const existing = document.querySelector<HTMLScriptElement>(scriptSelector);
|
|
existing?.remove();
|
|
if (window._paq) {
|
|
window._paq.length = 0;
|
|
}
|
|
delete (window as any).__matomoInitialized;
|
|
return;
|
|
}
|
|
|
|
window._paq = window._paq || [];
|
|
const { _paq } = window;
|
|
|
|
if (!(window as any).__matomoInitialized) {
|
|
_paq.push(['setTrackerUrl', `${base}/matomo.php`]);
|
|
_paq.push(['setSiteId', config.siteId]);
|
|
_paq.push(['disableCookies']);
|
|
_paq.push(['enableLinkTracking']);
|
|
|
|
if (!document.querySelector(scriptSelector)) {
|
|
const script = document.createElement('script');
|
|
script.async = true;
|
|
script.src = `${base}/matomo.js`;
|
|
script.dataset.matomo = base;
|
|
document.body.appendChild(script);
|
|
}
|
|
|
|
(window as any).__matomoInitialized = true;
|
|
}
|
|
}, [config, analyticsConsent]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
!config?.enabled ||
|
|
!config.url ||
|
|
!config.siteId ||
|
|
typeof window === 'undefined' ||
|
|
!analyticsConsent
|
|
) {
|
|
return;
|
|
}
|
|
|
|
window._paq = window._paq || [];
|
|
const { _paq } = window;
|
|
const currentUrl =
|
|
typeof window !== 'undefined' ? `${window.location.origin}${page.url}` : page.url;
|
|
|
|
_paq.push(['setCustomUrl', currentUrl]);
|
|
if (typeof document !== 'undefined') {
|
|
_paq.push(['setDocumentTitle', document.title]);
|
|
}
|
|
_paq.push(['trackPageView']);
|
|
}, [config, analyticsConsent, page.url]);
|
|
|
|
if (!config?.enabled || !config.url || !config.siteId || !analyticsConsent) {
|
|
return null;
|
|
}
|
|
|
|
const base = config.url.replace(/\/$/, '');
|
|
const noscriptSrc = `${base}/matomo.php?idsite=${encodeURIComponent(config.siteId)}&rec=1`;
|
|
|
|
return (
|
|
<noscript>
|
|
<p>
|
|
<img src={noscriptSrc} style={{ border: 0 }} alt="" />
|
|
</p>
|
|
</noscript>
|
|
);
|
|
};
|
|
|
|
export default MatomoTracker;
|