Update codebase
This commit is contained in:
+108
-36
@@ -881,9 +881,10 @@ footer{background:var(--midnight-900);color:#8A99C2;padding:64px 0 32px;border-t
|
||||
<div class="field" style="margin-bottom:0">
|
||||
<label data-i18n="calc.plan"></label>
|
||||
<select id="plan">
|
||||
<option value="349:0.025:0.04">علامة — $349/mo · 2.5%</option>
|
||||
<option value="169:0.035:0.05">انطلاقة — $169/mo · 3.5%</option>
|
||||
<option value="599:0.02:0.03">أسطول+ — $599/mo · 2%</option>
|
||||
<option value="169:0.035:0.05:startup">انطلاقة — $169/mo · 3.5%</option>
|
||||
<option value="349:0.025:0.04:brand" selected>علامة — $349/mo · 2.5%</option>
|
||||
<option value="599:0.02:0.03:fleet">أسطول+ — $599/mo · 2%</option>
|
||||
<option value="879:0.01:0.02:sov">سيادة — $879/mo · 1%</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1666,39 +1667,76 @@ function fmt(n){return "$"+Math.round(n).toLocaleString("en-US");}
|
||||
function calc(){
|
||||
const trips=+$("#trips").value;
|
||||
const fare=+$("#fare").value||0;
|
||||
const [,rate]=$("#cur").value.split(":");
|
||||
const [base,gmvRate,minRate]=$("#plan").value.split(":").map(Number);
|
||||
const usd=fare*(+rate);
|
||||
const monthlyTrips=trips*30;
|
||||
const gmv=monthlyTrips*usd;
|
||||
// GMV fee: only the Brand plan (2.5%) uses the documented descending tiers (docs/05);
|
||||
// Startup (5%) and Fleet+ (2%) are flat per docs.
|
||||
const [curCode, rate]=$("#cur").value.split(":");
|
||||
const [baseVal, gmvVal, minVal, pKey]=$("#plan").value.split(":");
|
||||
const base = +baseVal, minRate = +minVal;
|
||||
const usd = fare * (+rate);
|
||||
const monthlyTrips = trips * 30;
|
||||
const gmv = monthlyTrips * usd;
|
||||
|
||||
// Calculate GMV fee with progressive tiers based on plan
|
||||
let gmvFee;
|
||||
if(gmvRate===0.025){
|
||||
if(gmv<=50000)gmvFee=gmv*0.025;
|
||||
else if(gmv<=200000)gmvFee=50000*0.025+(gmv-50000)*0.02;
|
||||
else gmvFee=50000*0.025+150000*0.02+(gmv-200000)*0.015;
|
||||
}else{
|
||||
gmvFee=gmv*gmvRate;
|
||||
if(pKey === "brand"){
|
||||
if(gmv <= 50000) gmvFee = gmv * 0.025;
|
||||
else if(gmv <= 200000) gmvFee = 50000 * 0.025 + (gmv - 50000) * 0.02;
|
||||
else gmvFee = 50000 * 0.025 + 150000 * 0.02 + (gmv - 200000) * 0.015;
|
||||
} else if(pKey === "fleet"){
|
||||
if(gmv <= 50000) gmvFee = gmv * 0.02;
|
||||
else if(gmv <= 200000) gmvFee = 50000 * 0.02 + (gmv - 50000) * 0.015;
|
||||
else gmvFee = 50000 * 0.02 + 150000 * 0.015 + (gmv - 200000) * 0.01;
|
||||
} else if(pKey === "startup"){
|
||||
if(gmv <= 50000) gmvFee = gmv * 0.035;
|
||||
else if(gmv <= 200000) gmvFee = 50000 * 0.035 + (gmv - 50000) * 0.03;
|
||||
else gmvFee = 50000 * 0.035 + 150000 * 0.03 + (gmv - 200000) * 0.025;
|
||||
} else {
|
||||
gmvFee = gmv * 0.01;
|
||||
}
|
||||
const minMonthly=monthlyTrips*minRate;
|
||||
const tripz=base+Math.max(minMonthly,gmvFee);
|
||||
const commission=gmv*0.15;
|
||||
const floor=monthlyTrips*0.10;
|
||||
$("#rTripz").textContent=fmt(tripz);
|
||||
$("#rComm").textContent=fmt(commission);
|
||||
$("#rFloor").textContent=fmt(floor);
|
||||
const save=Math.max(commission,floor)-tripz;
|
||||
const bigEl=$("#saveBig");
|
||||
bigEl.textContent=(save>0?fmt(save*12):"$0")+"/yr";
|
||||
const sv=$("#savings");sv.classList.remove("bump");void sv.offsetWidth;sv.classList.add("bump");
|
||||
|
||||
const minMonthly = monthlyTrips * minRate;
|
||||
const tripz = base + Math.max(minMonthly, gmvFee);
|
||||
const commission = gmv * 0.15;
|
||||
const floor = monthlyTrips * 0.10;
|
||||
|
||||
$("#rTripz").textContent = fmt(tripz);
|
||||
$("#rComm").textContent = fmt(commission);
|
||||
$("#rFloor").textContent = fmt(floor);
|
||||
|
||||
const save = Math.max(commission, floor) - tripz;
|
||||
const bigEl = $("#saveBig");
|
||||
bigEl.textContent = (save > 0 ? fmt(save * 12) : "$0") + "/yr";
|
||||
const sv = $("#savings"); sv.classList.remove("bump"); void sv.offsetWidth; sv.classList.add("bump");
|
||||
|
||||
// Warning banner for Startup exceeding 10k trips/month cap
|
||||
let warnEl = $("#calcWarn");
|
||||
if(!warnEl){
|
||||
warnEl = document.createElement("div");
|
||||
warnEl.id = "calcWarn";
|
||||
warnEl.style.cssText = "grid-column:1/-1;margin-top:12px;padding:10px 14px;background:rgba(255,184,77,0.12);border:1px solid rgba(255,184,77,0.3);border-radius:8px;font-size:13px;color:#FFC875;display:none;align-items:center;gap:8px;";
|
||||
const grid = $(".calc-grid");
|
||||
if(grid) grid.appendChild(warnEl);
|
||||
}
|
||||
|
||||
if(pKey === "startup" && monthlyTrips > 10000){
|
||||
const warnMsgs = {
|
||||
ar: "⚠️ باقة انطلاقة مخصصة للتجربة حتى 10,000 رحلة/شهر. للحجم الحالي يُنصح باختيار باقة «علامة» للحصول على تطبيق خاص بتوفير أكبر.",
|
||||
en: "⚠️ Startup plan is for trials up to 10,000 trips/mo. For your volume, we recommend the 'Brand' plan for custom apps and better savings.",
|
||||
fr: "⚠️ Le forfait Startup est limité à 10 000 trajets/mois. Pour votre volume, nous recommandons le forfait 'Marque'.",
|
||||
es: "⚠️ El plan Inicio está limitado a 10,000 viajes/mes. Para su volumen actual, recomendamos el plan 'Marca'."
|
||||
};
|
||||
warnEl.textContent = warnMsgs[LANG] || warnMsgs.ar;
|
||||
warnEl.style.display = "flex";
|
||||
} else if(warnEl) {
|
||||
warnEl.style.display = "none";
|
||||
}
|
||||
|
||||
// slider fill + label
|
||||
const pct=((trips-50)/(5000-50))*100;
|
||||
$("#trips").style.setProperty("--p",pct+"%");
|
||||
$("#tripsVal").textContent=trips.toLocaleString("en-US")+" / day";
|
||||
const pct = ((trips - 50) / (5000 - 50)) * 100;
|
||||
$("#trips").style.setProperty("--p", pct + "%");
|
||||
$("#tripsVal").textContent = trips.toLocaleString("en-US") + " / day";
|
||||
|
||||
// whatsapp prefill
|
||||
const msg=encodeURIComponent(`Tripz — ${I18N[LANG]["calc.r.savecap"]}: ${bigEl.textContent} | ${trips}/day @ ${fare} ${$("#cur").value.split(":")[0]}`);
|
||||
$("#waCalc").href="https://wa.me/962798583052?text="+msg;
|
||||
const msg = encodeURIComponent(`Tripz — ${I18N[LANG]["calc.r.savecap"]}: ${bigEl.textContent} | ${trips}/day @ ${fare} ${curCode}`);
|
||||
$("#waCalc").href = "https://wa.me/962798583052?text=" + msg;
|
||||
}
|
||||
|
||||
/* ============ TABS ============ */
|
||||
@@ -1779,11 +1817,36 @@ function animateTimeline(){
|
||||
فقط بعد موافقة صريحة — مطلوب لزوّار الاتحاد الأوروبي (الموقع بالفرنسية
|
||||
والإسبانية). تعريف الأحداث وربطها بأهداف 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;
|
||||
@@ -1795,11 +1858,16 @@ const Track = (() => {
|
||||
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');
|
||||
local.forEach(([n,p]) => fbq('trackCustom', n, p));
|
||||
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) === "yes"; }
|
||||
function consented(){ return localStorage.getItem(CONSENT_KEY) !== "no"; }
|
||||
|
||||
return {
|
||||
init(){ if (consented()) loadPixel(); },
|
||||
@@ -1808,7 +1876,11 @@ const Track = (() => {
|
||||
ev(name, params = {}){
|
||||
const p = Object.assign({ lang: document.documentElement.lang }, params);
|
||||
local.push([name, p]);
|
||||
if (pixelReady && window.fbq) fbq('trackCustom', 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()
|
||||
|
||||
Reference in New Issue
Block a user