mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
feat: landing analytics goals, daily bar chart, referrer tracking, contact persistence
- Add per-landing analytics goals (view/click) with admin editor toggle - Add sticky pay button option for mobile landing pages - Add daily purchases bar chart (created vs paid) to landing stats - Replace single purchase count with created/paid split in stats summary - Add referrer tracking to purchases with hostname display in stats - Add time display to purchase cards alongside date - Pass user timezone to stats API for correct daily grouping - Clamp referrer (500 chars) and subid (255 chars) to backend limits - Persist contact value per-landing-slug in localStorage - Fire buy_success analytics goal on successful delivery - Export USER_TIMEZONE from format utils - Add analytics/stats translations for fa.json and zh.json locales Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -820,15 +820,17 @@ export default function QuickPurchase() {
|
||||
if (config?.discount) setDiscountExpired(false);
|
||||
}, [config?.discount]);
|
||||
|
||||
// Save document.referrer on mount (before SPA navigation loses it)
|
||||
// Save document.referrer on mount (before SPA navigation loses it).
|
||||
// Clamp to 500 chars -- backend `referrer` column is max_length=500 and would
|
||||
// otherwise reject long ad-click referrers (gclid+gbraid+params) with 422.
|
||||
useEffect(() => {
|
||||
if (document.referrer && !sessionStorage.getItem('landing_referrer')) {
|
||||
sessionStorage.setItem('landing_referrer', document.referrer);
|
||||
sessionStorage.setItem('landing_referrer', document.referrer.slice(0, 500));
|
||||
}
|
||||
// Save subid from URL
|
||||
// Save subid from URL (also clamped to backend limit of 255)
|
||||
const urlSubid = new URLSearchParams(window.location.search).get('subid');
|
||||
if (urlSubid) {
|
||||
sessionStorage.setItem('landing_subid', urlSubid);
|
||||
sessionStorage.setItem('landing_subid', urlSubid.slice(0, 255));
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -854,7 +856,14 @@ export default function QuickPurchase() {
|
||||
// Selection state
|
||||
const [selectedTariffId, setSelectedTariffId] = useState<number | null>(null);
|
||||
const [selectedPeriodDays, setSelectedPeriodDays] = useState<number | null>(null);
|
||||
const [contactValue, setContactValue] = useState(() => localStorage.getItem('lp_contact') || '');
|
||||
const contactKey = `lp_contact_${slug ?? ''}`;
|
||||
const [contactValue, setContactValue] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem(contactKey) || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
const [isGift, setIsGift] = useState(false);
|
||||
const [giftRecipient, setGiftRecipient] = useState('');
|
||||
const [giftMessage, setGiftMessage] = useState('');
|
||||
@@ -1157,7 +1166,11 @@ export default function QuickPurchase() {
|
||||
contactValue={contactValue}
|
||||
onContactChange={(v) => {
|
||||
setContactValue(v);
|
||||
localStorage.setItem('lp_contact', v);
|
||||
try {
|
||||
localStorage.setItem(contactKey, v);
|
||||
} catch {
|
||||
/* */
|
||||
}
|
||||
setSubmitError(null);
|
||||
}}
|
||||
isGift={isGift}
|
||||
|
||||
Reference in New Issue
Block a user