{"id":313,"date":"2026-09-02T19:51:44","date_gmt":"2026-09-02T19:51:44","guid":{"rendered":"https:\/\/maroczain.com\/tourisme.maroczain.com\/?page_id=313"},"modified":"2026-09-02T19:51:44","modified_gmt":"2026-09-02T19:51:44","slug":"comparateur-de-vols-alertes-de-prix","status":"publish","type":"page","link":"https:\/\/maroczain.com\/tourisme.maroczain.com\/comparateur-de-vols-alertes-de-prix\/","title":{"rendered":"Comparateur de vols &amp; alertes de prix"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>MZA Flight Comparator Prestige<\/title>\n<style>\n:root{\n  --navy:#061a31;--blue:#0b3564;--gold:#d2aa52;--gold2:#f0d88f;\n  --ink:#182337;--muted:#6c7b91;--line:#dfe6ef;--bg:#f3f6fa;--ok:#21a75b;--danger:#b42318;\n}\n*{box-sizing:border-box}\nhtml,body{margin:0;padding:0;background:var(--bg);font-family:Inter,Segoe UI,Arial,sans-serif;color:var(--ink)}\nbutton,input,select{font:inherit}\n.mza{max-width:1500px;margin:auto;padding:18px}\n.hero{\n  background:linear-gradient(135deg,var(--navy),var(--blue));\n  border-radius:24px;color:#fff;padding:30px;display:flex;justify-content:space-between;gap:25px;align-items:center;\n  box-shadow:0 18px 48px rgba(5,25,50,.2);border:1px solid rgba(210,170,82,.3)\n}\n.kicker{color:#efd78e;font-size:12px;font-weight:900;letter-spacing:2px}\n.hero h1{font-size:clamp(28px,4vw,50px);line-height:1.05;margin:8px 0 10px}\n.hero p{margin:0;color:#dbe7f4;max-width:900px;line-height:1.6}\n.live-badge{white-space:nowrap;border:1px solid rgba(255,255,255,.25);border-radius:999px;padding:11px 14px;font-size:12px;font-weight:900}\n.dot{display:inline-block;width:9px;height:9px;border-radius:50%;background:#9ca3af;margin-right:8px;box-shadow:0 0 0 5px rgba(255,255,255,.06)}\n.live .dot{background:#47d47b;box-shadow:0 0 0 5px rgba(71,212,123,.13)}\n.panel{\n  background:#fff;border:1px solid var(--line);border-radius:20px;padding:18px;margin-top:18px;\n  box-shadow:0 10px 30px rgba(20,45,72,.07)\n}\n.config{display:flex;gap:12px;align-items:end;flex-wrap:wrap}\n.config .grow{flex:1;min-width:270px}\nlabel{font-size:12px;font-weight:850;color:#42536a;display:block;margin-bottom:6px}\ninput,select{\n  width:100%;min-height:48px;border:1px solid #cfd8e5;border-radius:11px;padding:10px 12px;background:#fbfcfe;color:#162235;outline:none\n}\ninput:focus,select:focus{border-color:#8faaca;box-shadow:0 0 0 3px rgba(11,53,100,.08)}\n.search{\n  display:grid;grid-template-columns:repeat(6,minmax(125px,1fr));gap:12px\n}\n.search-btn{\n  grid-column:1\/-1;border:0;border-radius:12px;min-height:52px;cursor:pointer;font-weight:950;\n  background:linear-gradient(135deg,var(--gold2),var(--gold));color:#17233b;font-size:16px\n}\n.status{margin-top:14px;padding:14px 16px;border-radius:12px;background:#fff;border:1px solid var(--line)}\n.status.error{background:#fff3f1;border-color:#f0c3bd;color:var(--danger)}\n.status.ok{background:#f0fbf5;border-color:#bde8cf;color:#167544}\n.summary{display:flex;gap:10px;flex-wrap:wrap;margin-top:15px}\n.pill{background:#fff;border:1px solid var(--line);padding:9px 12px;border-radius:999px;font-size:13px}\n.results{display:grid;gap:14px;margin-top:15px}\n.offer{\n  background:#fff;border:1px solid var(--line);border-radius:18px;padding:18px;\n  display:grid;grid-template-columns:125px 1fr 200px;gap:18px;align-items:center;\n  box-shadow:0 8px 24px rgba(21,46,73,.06)\n}\n.offer.best{border:2px solid var(--gold)}\n.rank{font-size:11px;font-weight:950;letter-spacing:.8px;color:#8c6d27}\n.airline{font-weight:900;color:var(--navy)}\n.route{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-top:5px}\n.route b{font-size:18px}.route span{font-size:12px;color:var(--muted)}.route i{font-style:normal;color:var(--gold);font-size:20px}\n.meta{margin-top:5px;color:#738096;font-size:12px}\n.price{text-align:right}.price strong{font-size:30px;color:var(--navy);margin-right:5px}.price span{font-weight:900;color:#8d6b20}\n.price small,.price em{display:block;color:#7b8798;font-size:11px;font-style:normal}.price em{margin-top:5px}\n.alert-box{background:linear-gradient(180deg,#fff,#fbf7ed);border-color:#e4d09d}\n.alert-box h2{margin:0;color:var(--navy)}\n.alert-grid{display:grid;grid-template-columns:2fr 1fr 1fr auto;gap:12px;align-items:end;margin-top:13px}\n.alert-btn,.save-btn{\n  border:0;border-radius:11px;min-height:48px;padding:0 18px;font-weight:900;cursor:pointer\n}\n.alert-btn{background:var(--navy);color:#fff}.save-btn{background:#e9eef5;color:#17375d}\n.notice{\n  background:#fff8e8;border:1px solid #ead69d;border-radius:14px;padding:14px 16px;margin-top:16px;color:#68562e;font-size:13px;line-height:1.55\n}\n.hidden{display:none!important}\n@media(max-width:1100px){.search{grid-template-columns:repeat(3,1fr)}.offer{grid-template-columns:100px 1fr 160px}.alert-grid{grid-template-columns:1fr 1fr}}\n@media(max-width:720px){.mza{padding:10px}.hero{flex-direction:column;align-items:flex-start}.search{grid-template-columns:1fr 1fr}.offer{grid-template-columns:1fr}.price{text-align:left}.alert-grid{grid-template-columns:1fr}}\n<\/style>\n<\/head>\n<body>\n<div class=\"mza\">\n  <section class=\"hero\">\n    <div>\n      <div class=\"kicker\">MAROC ZAIN \u2022 FLIGHT INTELLIGENCE<\/div>\n      <h1>Comparateur de vols &#038; alertes prix<\/h1>\n      <p>Comparez les offres, suivez les baisses de prix et cr\u00e9ez des alertes. L\u2019interface passe automatiquement en mode LIVE d\u00e8s qu\u2019elle est reli\u00e9e \u00e0 votre moteur serveur.<\/p>\n    <\/div>\n    <div id=\"liveBadge\" class=\"live-badge\"><span class=\"dot\"><\/span><span id=\"modeText\">MODE D\u00c9MO<\/span><\/div>\n  <\/section>\n\n  <section class=\"panel\">\n    <div class=\"config\">\n      <div class=\"grow\">\n        <label>URL du moteur serveur (optionnel)<\/label>\n        <input id=\"apiUrl\" placeholder=\"https:\/\/votre-site.com\/wp-json\/mza\/v1\/flights\">\n      <\/div>\n      <button class=\"save-btn\" id=\"saveApi\">Enregistrer l\u2019URL<\/button>\n    <\/div>\n    <div class=\"notice\">\n      Pour de vrais prix et de vraies alertes, cette page HTML doit appeler un serveur\/API s\u00e9curis\u00e9. Ne mettez jamais une cl\u00e9 Duffel, Amadeus ou autre directement dans ce fichier HTML : elle serait visible par les visiteurs.\n    <\/div>\n  <\/section>\n\n  <section class=\"panel\">\n    <form id=\"searchForm\" class=\"search\">\n      <div><label>D\u00e9part<\/label><input name=\"origin\" maxlength=\"3\" value=\"CMN\" required><\/div>\n      <div><label>Destination<\/label><input name=\"destination\" maxlength=\"3\" value=\"CDG\" required><\/div>\n      <div><label>Aller<\/label><input type=\"date\" name=\"depart_date\" required><\/div>\n      <div><label>Retour<\/label><input type=\"date\" name=\"return_date\"><\/div>\n      <div><label>Adultes<\/label><input type=\"number\" name=\"adults\" min=\"1\" max=\"9\" value=\"1\"><\/div>\n      <div><label>Cabine<\/label>\n        <select name=\"cabin\">\n          <option value=\"economy\">\u00c9conomique<\/option>\n          <option value=\"premium_economy\">Premium \u00e9co<\/option>\n          <option value=\"business\">Affaires<\/option>\n          <option value=\"first\">Premi\u00e8re<\/option>\n        <\/select>\n      <\/div>\n      <button class=\"search-btn\" type=\"submit\">Comparer les vols<\/button>\n    <\/form>\n    <div id=\"status\" class=\"status hidden\"><\/div>\n    <div id=\"summary\" class=\"summary hidden\"><\/div>\n    <div id=\"results\" class=\"results\"><\/div>\n  <\/section>\n\n  <section id=\"alertBox\" class=\"panel alert-box hidden\">\n    <h2>\ud83d\udd14 Cr\u00e9er une alerte de prix<\/h2>\n    <p id=\"alertRoute\"><\/p>\n    <form id=\"alertForm\" class=\"alert-grid\">\n      <div><label>E-mail<\/label><input name=\"email\" type=\"email\" placeholder=\"vous@exemple.com\" required><\/div>\n      <div><label>Prix maximum<\/label><input name=\"target_price\" type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"3000\"><\/div>\n      <div><label>Baisse minimum (%)<\/label><input name=\"drop_percent\" type=\"number\" min=\"1\" max=\"90\" value=\"5\"><\/div>\n      <button class=\"alert-btn\" type=\"submit\">Activer l\u2019alerte<\/button>\n    <\/form>\n  <\/section>\n<\/div>\n\n<script>\nconst $ = s => document.querySelector(s);\nconst apiInput = $(\"#apiUrl\");\nconst badge = $(\"#liveBadge\");\nconst modeText = $(\"#modeText\");\nconst status = $(\"#status\");\nconst results = $(\"#results\");\nconst summary = $(\"#summary\");\nconst alertBox = $(\"#alertBox\");\nconst alertRoute = $(\"#alertRoute\");\nlet lastSearch = null;\n\nfunction setMode(){\n  const u = localStorage.getItem(\"mzaFlightApi\") || \"\";\n  apiInput.value = u;\n  const live = !!u;\n  badge.classList.toggle(\"live\", live);\n  modeText.textContent = live ? \"MOTEUR LIVE CONNECT\u00c9\" : \"MODE D\u00c9MO\";\n}\nsetMode();\n\n$(\"#saveApi\").onclick = () => {\n  const u = apiInput.value.trim();\n  localStorage.setItem(\"mzaFlightApi\", u);\n  setMode();\n  showStatus(u ? \"URL enregistr\u00e9e. Les prochaines recherches utiliseront le moteur serveur.\" : \"URL supprim\u00e9e. Retour au mode d\u00e9mo.\", \"ok\");\n};\n\nfunction showStatus(msg, type=\"\"){\n  status.textContent = msg;\n  status.className = \"status \" + type;\n}\nfunction esc(s){return String(s??\"\").replace(\/[&<>\"']\/g,c=>({\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",\"\\\"\":\"&quot;\",\"'\":\"&#039;\"}[c]))}\nfunction money(n,c){return Number(n).toLocaleString(\"fr-FR\",{minimumFractionDigits:2,maximumFractionDigits:2})+\" \"+c}\nfunction demoOffers(q){\n  const base = q.origin===\"CMN\" && (q.destination===\"CDG\" || q.destination===\"ORY\") ? 2450 : 3190;\n  return [\n    {airline:\"Royal Air Maroc\",flight:\"AT760\",origin:q.origin,destination:q.destination,depart:\"06:30\",arrive:\"10:35\",stops:0,amount:base,currency:\"MAD\"},\n    {airline:\"Air France\",flight:\"AF1597\",origin:q.origin,destination:q.destination,depart:\"11:10\",arrive:\"15:20\",stops:0,amount:base+390,currency:\"MAD\"},\n    {airline:\"Iberia\",flight:\"IB + connexion\",origin:q.origin,destination:q.destination,depart:\"13:40\",arrive:\"20:15\",stops:1,amount:base+120,currency:\"MAD\"}\n  ];\n}\nfunction renderOffers(offers, live){\n  results.innerHTML = offers.map((o,i)=>`\n    <article class=\"offer ${i===0?\"best\":\"\"}\">\n      <div class=\"rank\">${i===0?\"MEILLEUR PRIX\":\"OFFRE \"+(i+1)}<\/div>\n      <div>\n        <div class=\"airline\">${esc(o.airline||o.carrier||\"Compagnie\")}<\/div>\n        <div class=\"route\">\n          <b>${esc(o.origin)}<\/b><span>${esc(o.depart||\"\")}<\/span><i>\u2192<\/i>\n          <b>${esc(o.destination)}<\/b><span>${esc(o.arrive||\"\")}<\/span>\n        <\/div>\n        <div class=\"meta\">${esc(o.flight||\"\")} \u00b7 ${Number(o.stops||0)===0?\"Direct\":esc(o.stops)+\" escale(s)\"}<\/div>\n      <\/div>\n      <div class=\"price\">\n        <small>Total indicatif<\/small>\n        <strong>${Number(o.amount).toLocaleString(\"fr-FR\",{minimumFractionDigits:2,maximumFractionDigits:2})}<\/strong><span>${esc(o.currency||\"\")}<\/span>\n        <em>${live?\"Tarif live \u00e0 revalider avant achat\":\"Donn\u00e9e de d\u00e9monstration\"}<\/em>\n      <\/div>\n    <\/article>`).join(\"\");\n}\n\n$(\"#searchForm\").addEventListener(\"submit\", async e=>{\n  e.preventDefault();\n  const q = Object.fromEntries(new FormData(e.currentTarget).entries());\n  q.origin = q.origin.toUpperCase();\n  q.destination = q.destination.toUpperCase();\n  lastSearch = q;\n  const api = localStorage.getItem(\"mzaFlightApi\") || \"\";\n  results.innerHTML = \"\";\n  summary.classList.add(\"hidden\");\n  alertBox.classList.add(\"hidden\");\n  showStatus(api ? \"Recherche LIVE en cours\u2026\" : \"Mode d\u00e9monstration : g\u00e9n\u00e9ration d\u2019exemples\u2026\");\n\n  try{\n    let offers, live=false;\n    if(api){\n      const r = await fetch(api,{\n        method:\"POST\",\n        headers:{\"Content-Type\":\"application\/json\"},\n        body:JSON.stringify(q)\n      });\n      if(!r.ok) throw new Error(\"Le moteur serveur a retourn\u00e9 HTTP \"+r.status);\n      const j = await r.json();\n      offers = j.offers || j.data?.offers || [];\n      live = true;\n      if(!offers.length) throw new Error(\"Aucune offre retourn\u00e9e par le moteur.\");\n    }else{\n      await new Promise(r=>setTimeout(r,450));\n      offers = demoOffers(q);\n    }\n    offers.sort((a,b)=>Number(a.amount)-Number(b.amount));\n    renderOffers(offers, live);\n    status.classList.add(\"hidden\");\n    summary.innerHTML = `<span class=\"pill\"><b>${offers.length}<\/b> offres<\/span><span class=\"pill\">${live?\"LIVE\":\"D\u00c9MO\"}<\/span><span class=\"pill\">${esc(q.origin)} \u2192 ${esc(q.destination)}<\/span>`;\n    summary.classList.remove(\"hidden\");\n    alertRoute.textContent = `${q.origin} \u2192 ${q.destination} \u2022 d\u00e9part ${q.depart_date}${q.return_date?\" \u2022 retour \"+q.return_date:\"\"}`;\n    alertBox.classList.remove(\"hidden\");\n  }catch(err){\n    showStatus(err.message,\"error\");\n  }\n});\n\n$(\"#alertForm\").addEventListener(\"submit\", async e=>{\n  e.preventDefault();\n  if(!lastSearch){showStatus(\"Lancez d\u2019abord une recherche.\",\"error\");return;}\n  const api = localStorage.getItem(\"mzaFlightApi\") || \"\";\n  const data = {...lastSearch,...Object.fromEntries(new FormData(e.currentTarget).entries())};\n  if(!api){\n    showStatus(\"Alerte non envoy\u00e9e : le fichier est en mode d\u00e9mo. Connectez une URL serveur pour des alertes r\u00e9elles.\",\"error\");\n    return;\n  }\n  try{\n    const endpoint = api.replace(\/\\\/$\/,\"\") + \"\/alert\";\n    const r = await fetch(endpoint,{method:\"POST\",headers:{\"Content-Type\":\"application\/json\"},body:JSON.stringify(data)});\n    if(!r.ok) throw new Error(\"Impossible d\u2019activer l\u2019alerte (HTTP \"+r.status+\")\");\n    showStatus(\"Alerte enregistr\u00e9e sur le serveur.\",\"ok\");\n  }catch(err){showStatus(err.message,\"error\")}\n});\n\n\/\/ Dates par d\u00e9faut\nconst today = new Date();\nconst d1 = new Date(today); d1.setDate(d1.getDate()+14);\nconst d2 = new Date(today); d2.setDate(d2.getDate()+21);\nconst iso=d=>d.toISOString().slice(0,10);\ndocument.querySelector('[name=\"depart_date\"]').value=iso(d1);\ndocument.querySelector('[name=\"return_date\"]').value=iso(d2);\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>MZA Flight Comparator Prestige MAROC ZAIN \u2022 FLIGHT INTELLIGENCE Comparateur de vols &#038; alertes prix Comparez les offres, suivez les baisses de prix et cr\u00e9ez des alertes. L\u2019interface passe automatiquement en mode LIVE d\u00e8s qu\u2019elle est reli\u00e9e \u00e0 votre moteur serveur. MODE D\u00c9MO URL du moteur serveur (optionnel) Enregistrer l\u2019URL Pour de vrais prix et [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-313","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/pages\/313","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/comments?post=313"}],"version-history":[{"count":1,"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/pages\/313\/revisions"}],"predecessor-version":[{"id":315,"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/pages\/313\/revisions\/315"}],"wp:attachment":[{"href":"https:\/\/maroczain.com\/tourisme.maroczain.com\/wp-json\/wp\/v2\/media?parent=313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}