refactor(marketing): تفكيك الموقع إلى وحدات + توحيد الباقات الثلاث
- marketing: index.html من 2274 إلى 775 سطراً، والباقي إلى styles/ (tokens·base·components·sections·responsive) و js/ (app·i18n·calculator·analytics) و i18n/ بخمس لغات (ar·en·fr·es·tr) عبر سمة data-i18n. - docs/05 + docs/33 + لوحة السوبر-أدمن: الباقات صارت ثلاثاً (إطلاق · نمو · سيادة) بدل الأربع، وتحديث docs/31 بما يطابقها. - docs/36: توثيق سيرو إكسبرس للتوصيل. - تنظيف استيرادات غير مستعملة في apps/rider و apps/driver. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5675a4d928
commit
75b80bd299
@@ -0,0 +1,132 @@
|
||||
/* ============ MEASUREMENT (consent-gated) ============ ------------------
|
||||
⚠️ ضع معرّف البكسل هنا قبل أول دولار إعلاني. ما دام فارغاً لا يُحمَّل شيء.
|
||||
الأحداث تُسجَّل محلياً دائماً (بلا كوكيز وبلا بيانات شخصية) وتُرسل لميتا
|
||||
فقط بعد موافقة صريحة — مطلوب لزوّار الاتحاد الأوروبي (الموقع بالفرنسية
|
||||
والإسبانية). تعريف الأحداث وربطها بأهداف CAC في docs/32 §5. */
|
||||
const PIXEL_ID = "1360167306316901";
|
||||
const CAPI_TOKEN = "EAAOFgSTx7pUBSGsw30ZBnx40yTbFJSFW3iZCEf0bHqEai6wZAxDsZCD89VsAtWtvytneZAVECx2EH3s4XM9kIN8dcAC8e9gogk4bzAdAZBuaLPtVZC8ZAe66SCu4ptZCZCAPVLSPD0LBU26Ac0QeX0ILf5dh4uFHKrM8ZCtU2ZCjYlMaM9xULe1ou5GUv8SxyYWLugZDZD";
|
||||
const CONSENT_KEY = "tripz_consent";
|
||||
|
||||
const Track = (() => {
|
||||
const local = [];
|
||||
let pixelReady = false;
|
||||
const standardEvents = new Set(["PageView", "Lead", "Contact", "ViewContent", "Schedule"]);
|
||||
|
||||
function sendCAPI(eventName, params = {}){
|
||||
if (!CAPI_TOKEN || !PIXEL_ID) return;
|
||||
try {
|
||||
const payload = {
|
||||
data: [{
|
||||
event_name: eventName,
|
||||
event_time: Math.floor(Date.now() / 1000),
|
||||
event_source_url: location.href,
|
||||
action_source: "website",
|
||||
user_data: {
|
||||
client_user_agent: navigator.userAgent
|
||||
},
|
||||
custom_data: params
|
||||
}]
|
||||
};
|
||||
fetch(`https://graph.facebook.com/v19.0/${PIXEL_ID}/events?access_token=${CAPI_TOKEN}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload)
|
||||
}).catch(()=>{});
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
function loadPixel(){
|
||||
if (pixelReady || !PIXEL_ID) return;
|
||||
pixelReady = true;
|
||||
/* Meta Pixel base */
|
||||
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
|
||||
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}
|
||||
(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', PIXEL_ID);
|
||||
fbq('track', 'PageView');
|
||||
sendCAPI('PageView');
|
||||
local.forEach(([n,p]) => {
|
||||
if (standardEvents.has(n)) fbq('track', n, p);
|
||||
else fbq('trackCustom', n, p);
|
||||
sendCAPI(n, p);
|
||||
});
|
||||
}
|
||||
|
||||
function consented(){ return localStorage.getItem(CONSENT_KEY) !== "no"; }
|
||||
|
||||
return {
|
||||
init(){ if (consented()) loadPixel(); },
|
||||
grant(){ localStorage.setItem(CONSENT_KEY,"yes"); loadPixel(); },
|
||||
deny(){ localStorage.setItem(CONSENT_KEY,"no"); },
|
||||
ev(name, params = {}){
|
||||
const p = Object.assign({ lang: document.documentElement.lang }, params);
|
||||
local.push([name, p]);
|
||||
if (pixelReady && window.fbq) {
|
||||
if (standardEvents.has(name)) fbq('track', name, p);
|
||||
else fbq('trackCustom', name, p);
|
||||
}
|
||||
sendCAPI(name, p);
|
||||
if (location.protocol === "file:") console.debug("[track]", name, p);
|
||||
},
|
||||
seen: new Set()
|
||||
};
|
||||
})();
|
||||
|
||||
/* شريط الموافقة */
|
||||
(function(){
|
||||
const box = document.getElementById("consent");
|
||||
if (!box) return;
|
||||
if (!localStorage.getItem(CONSENT_KEY)) setTimeout(()=>box.classList.add("show"), 1600);
|
||||
document.getElementById("consentYes").onclick = () => { Track.grant(); box.classList.remove("show"); Track.ev("ConsentGranted"); };
|
||||
document.getElementById("consentNo").onclick = () => { Track.deny(); box.classList.remove("show"); };
|
||||
Track.init();
|
||||
})();
|
||||
|
||||
/* أحداث القمع — مطابقة لأهداف CAC في docs/32 */
|
||||
(function(){
|
||||
// عمق التصفّح: أي قسم وصله الزائر
|
||||
const secs = document.querySelectorAll("section[id]");
|
||||
if ("IntersectionObserver" in window){
|
||||
const io = new IntersectionObserver(es => es.forEach(e => {
|
||||
if (e.isIntersecting && !Track.seen.has(e.target.id)){
|
||||
Track.seen.add(e.target.id);
|
||||
Track.ev("SectionView", { section: e.target.id });
|
||||
}
|
||||
}), { threshold: .35 });
|
||||
secs.forEach(s => io.observe(s));
|
||||
}
|
||||
|
||||
// نية شراء: نقر على باقة / وحدة / مواصلات
|
||||
document.querySelectorAll("#pricing .plan").forEach(p => p.addEventListener("click", () => {
|
||||
Track.ev("PlanInterest", { plan: p.querySelector("h3")?.getAttribute("data-i18n") || "?" });
|
||||
}));
|
||||
document.querySelectorAll("#addons .addon-card").forEach(c => c.addEventListener("click", () => {
|
||||
Track.ev("ModuleInterest", { module: c.querySelector("h3")?.getAttribute("data-i18n") || "?" });
|
||||
}));
|
||||
|
||||
// استعمال الحاسبة = أقوى إشارة نية قبل التواصل
|
||||
let calcTimer;
|
||||
["trips","fare","cur","plan"].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
el && el.addEventListener("change", () => {
|
||||
clearTimeout(calcTimer);
|
||||
calcTimer = setTimeout(() => Track.ev("CalculatorUsed", {
|
||||
plan: document.getElementById("plan")?.value,
|
||||
currency: document.getElementById("cur")?.value?.split(":")[0]
|
||||
}), 800);
|
||||
});
|
||||
});
|
||||
|
||||
// التحويل: كل مسارات الاتصال
|
||||
document.querySelectorAll('a[href*="wa.me"]').forEach(a => a.addEventListener("click", () => {
|
||||
Track.ev("Lead", { channel: "whatsapp", where: a.closest("section")?.id || "float" });
|
||||
}));
|
||||
document.querySelectorAll('a[href*="calendly"]').forEach(a => a.addEventListener("click", () => {
|
||||
Track.ev("Lead", { channel: "calendly", where: a.closest("section")?.id || "?" });
|
||||
}));
|
||||
document.querySelectorAll('a[href*="apps.apple.com"],a[href*="play.google.com"]').forEach(a => a.addEventListener("click", () => {
|
||||
Track.ev("DemoAppOpened");
|
||||
}));
|
||||
})();
|
||||
Reference in New Issue
Block a user