Files
fotospiel-app/resources/js/components/analytics/MatomoTracker.tsx
Codex Agent a949c8d3af - Wired the checkout wizard for Google “comfort login”: added Socialite controller + dependency, new Google env
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.
2025-10-19 11:41:03 +02:00

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;