- 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.
This commit is contained in:
44
resources/js/hooks/useAnalytics.ts
Normal file
44
resources/js/hooks/useAnalytics.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useConsent } from '@/contexts/consent';
|
||||
|
||||
type AnalyticsEvent = {
|
||||
category: string;
|
||||
action: string;
|
||||
name?: string;
|
||||
value?: number;
|
||||
};
|
||||
|
||||
export function useAnalytics() {
|
||||
const { hasConsent } = useConsent();
|
||||
|
||||
const trackEvent = useCallback(
|
||||
({ category, action, name, value }: AnalyticsEvent) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasConsent('analytics')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queue = (window._paq = window._paq || []);
|
||||
const payload: (string | number)[] = ['trackEvent', category, action];
|
||||
|
||||
if (typeof name === 'string') {
|
||||
payload.push(name);
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
if (payload.length === 3) {
|
||||
payload.push('');
|
||||
}
|
||||
payload.push(value);
|
||||
}
|
||||
|
||||
queue.push(payload);
|
||||
},
|
||||
[hasConsent],
|
||||
);
|
||||
|
||||
return { trackEvent };
|
||||
}
|
||||
58
resources/js/hooks/useCtaExperiment.ts
Normal file
58
resources/js/hooks/useCtaExperiment.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import { useAnalytics } from '@/hooks/useAnalytics';
|
||||
|
||||
type CtaVariant = 'gradient' | 'neutral';
|
||||
|
||||
const STORAGE_KEY = 'marketing_cta_variant_v1';
|
||||
|
||||
function determineVariant(): CtaVariant {
|
||||
if (typeof window === 'undefined') {
|
||||
return 'gradient';
|
||||
}
|
||||
|
||||
const stored = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (stored === 'gradient' || stored === 'neutral') {
|
||||
return stored;
|
||||
}
|
||||
|
||||
const assigned: CtaVariant = Math.random() < 0.5 ? 'gradient' : 'neutral';
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, assigned);
|
||||
} catch (error) {
|
||||
// localStorage may be unavailable; ignore and fallback to the assigned variant in memory
|
||||
console.warn('Unable to persist CTA variant assignment', error);
|
||||
}
|
||||
return assigned;
|
||||
}
|
||||
|
||||
export function useCtaExperiment(placement: string) {
|
||||
const { trackEvent } = useAnalytics();
|
||||
const [variant] = useState<CtaVariant>(() => determineVariant());
|
||||
|
||||
const trackingLabel = useMemo(() => `${placement}:${variant}`, [placement, variant]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
trackEvent({
|
||||
category: 'marketing_experiment',
|
||||
action: 'cta_impression',
|
||||
name: trackingLabel,
|
||||
});
|
||||
}, [trackEvent, trackingLabel]);
|
||||
|
||||
const trackClick = useCallback(() => {
|
||||
trackEvent({
|
||||
category: 'marketing_experiment',
|
||||
action: 'cta_click',
|
||||
name: trackingLabel,
|
||||
});
|
||||
}, [trackEvent, trackingLabel]);
|
||||
|
||||
return {
|
||||
variant,
|
||||
trackClick,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user