Commit Graph

40 Commits

Author SHA1 Message Date
MVA Global Fret
4b622a7d85 Drop parcel spawn point lower under the fuselage
Parcels were appearing right at the plane's center line, looking like
they came out of a window. Move the spawn offset from y-0.4 to y-1.1
so they emerge from below the belly of the airliner, where a real
cargo bay would be.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 13:57:28 +02:00
MVA Global Fret
cf7f84f354 Drop cargo back below the harness, just touching the parachute base
Previous +0.55·height pushed the cargo up into the canopy itself —
user's annotation showed the cargo belongs immediately under the
strings' end, not inside the parachute. With the wrapper fix from
the previous commit, parachuteBottom now correctly points at the
harness end, so cargo center = parachuteBottom - cargoH/2 + 0.06
puts the cargo's top right at the harness with a tiny overlap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 13:50:55 +02:00
MVA Global Fret
71d2f06920 Move cargo up into the canopy base, not below the strings
User's annotation showed the cargo belongs right at the canopy's
base — where the strings converge — not at the very bottom of the
strings. New position: parachuteBottom + 0.55·parachuteHeight,
i.e. about 55% up the parachute's vertical extent. Cargo also
shrunk to 0.36×0.30×0.36 to better match the small rectangle the
user drew.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 13:46:38 +02:00
MVA Global Fret
25ea448abe Make cargo a child of the parachute, drop the rogue strap meshes
The black rectangles the user circled in the screenshot were the
strap meshes (strapH/strapV, 0.04-thick black boxes) — they were
meant to wrap the cardboard box but ended up rendering as detached
rectangles in some viewing angles. They're gone.

Cargo positioning is now computed from the actual scaled parachute
bbox: parachuteBottom = -(size.y · baseScale)/2, cargo center sits
just inside that line for a slight overlap so the parachute strings
visually terminate on the box top. The cargo is now a child of the
parachute mesh (para.add(cargo)), so any transform applied to the
parachute — scale, rotation, position — carries the box along with
it. To keep the visible box size consistent regardless of the
parachute's baseScale, the cargo's local position and scale are
divided by baseScale.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 13:39:58 +02:00
MVA Global Fret
6b0f8d9afb Switch parcel sprites to a real 3D parachute GLB with bound cargo
Drops the parcel.png sprite (which baked the parachute and the box
into a single image) for a real 3D model: assets/parachute.glb is
the CC-BY 3.0 « Parachute » by Poly by Google, decompressed from
poly.pizza's static.poly.pizza CDN.

The parcel template now stacks four meshes inside one Group so they
move as a unit:
- the loaded parachute (centered + scaled to ~1.6 world units max)
- a 0.46×0.36×0.46 brown box with metal-low MeshStandardMaterial,
  positioned at y = -0.96 — right below the parachute's harness
  point — so the model's strings appear to terminate on it
- two thin black straps wrapping the box (0.04-thick boxes, one
  horizontal one vertical) for visual reinforcement that the cargo
  is tied down

spawnParcel clones the template (deep), per-instance clones every
material so opacity can be modulated independently per parcel, and
adds a slight pendulum sway + slow Y spin. Falling/scale-up/fade
logic adapted from the sprite version. Cleanup disposes the cloned
materials when a parcel exits.

HTML credit comment extended to attribute both the airplane and the
new parachute under CC-BY 3.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 13:32:48 +02:00
MVA Global Fret
3f773380c4 Drop parachuted parcels behind the plane as it crosses
Adds a sprite-based parcel system. The plane spawns a new
parcel-on-parachute (assets/parcel.png, 236 KB transparent) every
~1.4 s while it's visible (progress 0.04-0.92). Each parcel:

- Spawns at the plane's current position with a tiny random offset.
- Falls at 1.4 world units/s, with a small horizontal drift and a
  parachute sway sinusoid for character.
- Scales 0.35 → 2.6 over its lifetime, simulating the perspective
  of falling toward the camera.
- Fades in over 6% of life, fades out over the last 15%.
- Cleaned up (removed from scene + material disposed) when its
  lifetime expires or it drops below y = -10.

Implemented as THREE.Sprite so it always faces the camera, no need
to track per-parcel orientation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:46:27 +02:00
MVA Global Fret
6fcc772bf0 Match trajectory to user-drawn line: high cruise, gentle climb
User sketched a near-horizontal red line in the upper third of the
viewport, slightly higher on the left than on the right. Mapping
that to world coords with the camera at z=22 and 40° vfov:
- right end (entrance): y ≈ +3.5
- left end (exit):      y ≈ +5

So the plane keeps cruising in the upper portion of the frame and
climbs ~1.5 world units across 40 horizontal units — about 2° of
slope. Pitch follows: rotation.z = -0.06 - p·0.01 (3.5–4° nose-up,
matching the slope). Roll softened to 0.04 ± 0.02 since the path is
nearly straight.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:39:42 +02:00
MVA Global Fret
6cf619857a Switch trajectory to descending right→top to left→bottom, nose down
Reroute the plane: it now enters from the upper-right (y=+6) and
exits at the lower-left (y=-10), so the path slopes downward as a
~22° descent (slope = -16/40 in world units). Pitch reversed to
match — rotation.z = +0.32 + p·0.05 (≈18-21° nose-down) so the
plane's body aligns with the descent line. Roll and yaw unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:35:03 +02:00
MVA Global Fret
7cebdf86ed Fix: rotation.z is the actual pitch axis, not rotation.x
The labels in the previous commit were swapped. With the wrapper
rotated -π/2 around Y so the nose points -X, the plane's longitudinal
axis is world X (so rotation.x is roll) and its lateral axis is
world Z (so rotation.z is pitch). Earlier code applied "roll"
(positive 0.18) to .z, which was actually pitching the nose down —
no amount of tweaking rotation.x could compensate, hence the user
seeing the plane go forward+down even after sign flips.

Now:
- rotation.z = -0.30 - p·0.05  (nose up ~17–20°, climb attitude)
- rotation.x = 0.12 + small variation  (subtle roll)
- rotation.y = 0  (no yaw, plane already heading the right way)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:31:51 +02:00
MVA Global Fret
710551082c Flip pitch sign — turns out positive rotation.x was nose-down
After the wrapper's -π/2 yaw, applying positive rotation.x to the
planeHolder rotates the plane around world X with the nose dropping,
not lifting (visible in the user-supplied screenshot). Flipping to
-0.18 (≈10°) puts the nose where the trajectory says it should go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:29:12 +02:00
MVA Global Fret
1aa18d92b8 Soften the plane's pitch to a realistic ~4° nose-up
The +0.20 (≈11°) climb angle from last commit looked cartoonishly
steep. Real airliners climb at maybe 3–5° once they've cleaned up
after takeoff. Cut targetPitch to 0.06 + p·0.02 (3.5° → 4.6°).
Roll/yaw untouched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:27:30 +02:00
MVA Global Fret
390e075371 Plane climbs and exits fully off-screen
py was descending +7→+2, now ascending -1→+12 so the plane gains
altitude across the traversal. px stretched 18→-22 (was 16→-16) so
both endpoints sit clearly outside the visible frustum (camera at
z=22 + 40° vfov gives ~28 world-units of visible width on a 16:9
viewport). Pitch flipped from negative to +0.20 + p·0.10 to read
as nose-up for the climb. Roll/yaw unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:24:03 +02:00
MVA Global Fret
a23ef5edc3 Flip plane heading 180° — nose now leads the right→left motion
The Poly by Google airplane's default orientation in the GLB is the
opposite of what I assumed: at wrapper.rotation.y = +π/2 the nose
ended up pointing into the direction the plane was moving away from
(it looked like it was flying tail-first). Switch to -π/2 so the
nose actually leads the trajectory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:19:48 +02:00
MVA Global Fret
48dbd14541 Plane drifts forward at cruise speed, reverse trajectory right→left
Two adjustments to the intro plane:

1. Autonomous cruise speed. Adds BASE_SPEED (1/28 per second of
   wallclock) to targetProgress on every frame, so the plane crosses
   the screen on its own in ~28 s without any input. Mouse motion
   still adds a boost (one full traversal per ~4500 px of cursor
   travel), which feels like the plane "speeding up" when the user
   interacts. Time delta clamped to 0.1 s so the plane doesn't jump
   forward after the tab returns from background.

2. Reverse direction. Plane now enters from upper-right (x = +16),
   traverses to upper-left (x = -16), nose pointing -X. Wrapper rot.y
   flipped to +π/2; px formula flipped; banking angles inverted so
   the plane still rolls "into the turn" along its new direction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:15:59 +02:00
MVA Global Fret
3829ab9af6 Drive plane progress by mouse movement, not mouse position
Old behaviour: plane.x mapped 1:1 to cursor.x — moving the mouse left
made the plane reverse. New behaviour: every pixel of cursor travel
(any direction) increments a progress counter from 0 to 1, the plane
position is derived from progress, and progress saturates at 1 — so
once the plane has exited stage right, it stays gone.

FULL_DISTANCE = 3500 px of cursor travel for a full traversal. The
existing lerp (0.06) still smooths the rendered position. Background
parallax still uses raw cursor X/Y (independent of plane progress).
deviceorientation handler updated symmetrically — gamma+beta deltas
push progress forward on mobile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 12:11:53 +02:00
MVA Global Fret
ad9ad43487 Use the correct Antananarivo aerial image, lift plane to upper half
Background was inadvertently the wrong Gemini export (an unrelated
airliner-over-mountains photo) — sorted out and replaced with the
intended aerial illustration of Antananarivo (Lake Anosy + Rova
hill + city, 487 KB, 1920px wide).

Also lifted the plane's trajectory: y goes from +7 (offscreen
upper-left) to +2 (still upper half, exiting right) instead of
+5..-2. The plane now stays clearly in the upper third of the
viewport, leaving room for the centered CTA button and the city
detail at the bottom.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 11:39:08 +02:00
MVA Global Fret
eba88207c4 Swap video bg for aerial Antananarivo image, drive plane with the mouse
User-supplied aerial illustration of Antananarivo (Lake Anosy + Rova
hill + surrounding city, 1920px wide JPG, 241 KB) replaces the
parachute-drop video as the static intro backdrop. The video files
are deleted from /videos.

The plane no longer orbits a scroll timeline. Now:
- Page is a single viewport, no scroll, no act labels, no scroll hint.
- Mouse X (0..1) drives plane.position.x from -16 (offscreen left) to
  +16 (offscreen right), with plane.position.y descending from +5 to
  -2 — so the plane enters from the upper-left and exits lower-right.
- Pitch/roll/yaw lerp toward small targets that depend on mouse X, so
  the plane banks naturally as it crosses.
- Background image gets a softer mouse parallax (-16/-10px) via the
  existing --mx/--my CSS vars, now updated from intro-scene.js.
- Three.js cloud spheres are gone; the photo is the entire backdrop.
- ScrollTrigger + the GSAP timeline are removed; the page no longer
  needs gsap at all (the script tag stayed for now in case it comes
  back, but the dependency could be dropped on a future pass).
- CTA button is back to plain visible/centered, no reveal animation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 11:35:11 +02:00
MVA Global Fret
d99e2a5fc1 Replace primitive plane with a real GLTF airliner
Drops the cylinder+box airplane built last commit in favor of a CC-BY
3D commercial airliner from Poly by Google (188 KB GLB, 11.3k tris,
hosted in assets/airplane.glb). Loaded at runtime via three/addons
GLTFLoader; importmap extended to expose the addons subpath.

Bug worth noting: a naive setFromObject + position.sub(center) +
scale.setScalar pipeline leaves the model offset by -center after
scaling because position is in pre-scale units. Fix is to wrap the
model in a Group, apply the centering offset to the inner model,
then scale the outer Group — the whole transform stays consistent.

Attribution added in two places per CC-BY 3.0:
- HTML header comment with creator + source URL + license link
- JS file header in intro-scene.js
Tone-mapping bumped to ACES filmic for a slightly nicer render.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 11:18:53 +02:00
MVA Global Fret
341dca7cb5 Convert intro into a scroll-driven 3D cinematic
Restructure the page so the first 4 viewports of scroll drive a
Three.js scene composited on top of the Antananarivo parachute video.

What's there:

- Three.js (ESM, r158 via importmap) renders a low-poly cargo airliner
  built from primitives: cylinder fuselage, cone nose, sphere cockpit
  (dark glass + emissive), box wings/tail/fin, cylinder engines with
  torus intakes, gold trim band, navy fin with gold logo box. No
  external model file.
- Hemisphere + directional + ambient lights tuned for golden-hour fill.
- 14 cloud spheres scattered around the plane, slowly rotating.
- GSAP + ScrollTrigger drive a single progress value scrubbed against
  scroll position. Inside the rAF loop, the camera arcs from rear-left
  (-0.6 rad) to front-right (+1.1 rad), radius dipping mid-flight, and
  the plane rolls slightly with scroll.
- Three act labels (Paris CDG / Vol cargo / Antananarivo) cross-fade at
  20%/40%-60%/72% scroll positions via a chained gsap timeline.
- Gold CTA button stays opacity:0 + pointer-events:none until the last
  ~10% of scroll, then fades and scales in. Hover transform rebuilt
  without the old mouse-parallax tilt (fights the scroll animation).
- Scroll hint pill (chevron + "Faites défiler") at the bottom of the
  first viewport, fades out on first scroll event.
- prefers-reduced-motion shortcut: scroll stage hidden, CTA visible,
  no animation. Page reverts to a static screen with the video bg.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-05 10:41:24 +02:00
MVA Global Fret
1ddd7b12d8 Add cinematic parallax intro page as new landing
The new index.html is a 3-act scroll-driven storytelling intro:

  Act 1 (Tarmac at sunset)
    Sunset gradient sky, mountain silhouettes, hangar, runway with
    centerline lights. Inline SVG cargo plane (MVA-branded gold tail)
    sits on the ground while box emojis cycle up a loading ramp.

  Act 2 (Take-off at dusk)
    Dusk sky, two parallax cloud layers and distant mountains. The
    plane translates diagonally up-and-right with a slight tilt and
    a glowing contrail, sized down progressively.

  Act 3 (Arrival at night)
    Deep navy night sky with twinkling stars, a glowing moon, and
    the Madagascar coastline silhouette. A small plane fades in
    descending toward the island, then a centered CTA block reveals:
    "Bienvenue à bord" with the gold "Accéder au site" button that
    routes to accueil.html (the real homepage).

Implementation:
- Pure CSS layered scenes; no library
- Scroll progress driven by a single CSS custom property --scroll
  (0→1) updated via rAF, layers transform off it
- Mouse-move parallax on layers via --mx/--my (skipped on touch)
- Inline SVG plane reused across the 3 scenes
- All text is i18n-driven with a new `intro` section in FR/EN/MG

Architecture changes:
- index.html  → new parallax intro
- accueil.html → former index.html content (full homepage)
- All nav/logo/footer links updated index.html → accueil.html across
  the 8 existing pages
- Mobile-nav on accueil.html now also includes Service Commande

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 22:18:02 +02:00
MVA Global Fret
245f28f76d Update copyright year from 2025 to 2026
Footer copyright bumped to 2026 across all 8 HTML pages and in the
three translation strings (FR/EN/MG) in translations.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 21:20:29 +02:00
MVA Global Fret
8031a8bf2e Add asterisk note for oversized parcels in shipping guide
"Dépasser les dimensions autorisées" now has an asterisk pointing to
a small italic note below the dos/donts grid: "Nous contacter pour
tout colis volumineux." (FR/EN/MG).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 21:17:57 +02:00
MVA Global Fret
4a79136525 Add "Service Commande" page (personal shopper service)
New page service-commande.html explaining the order-on-behalf service:
- 10% commission on total order amount
- 50% deposit + 50% on order validation
- Right to request 100% upfront for large orders
- Standard 70,000 Ar/kg shipping fee on top, paid on receipt
- 5-question FAQ
- CTA to contact / Messenger

Trilingual content (FR / EN / MG) added to translations.js with full
serviceCommande section + nav.serviceCommande key.

Menu link inserted right after "Tarifs" in main nav, mobile nav and
footer links across all pages: index, about, tarifs, contact,
guide-envoi, application, cgv, service-commande.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 20:14:16 +02:00
MVA Global Fret
48664683e5 Send welcome-back email to clients already registered
When a client tries to re-register with an existing email:
- The form still shows the on-screen "already registered" message
- HubSpot data is still untouched (no duplicate, ref number preserved)
- NEW: a welcome-back email is now sent via EmailJS reminding them
  of their existing client reference number
- The internal MVA notification (Formspree) is preserved

Requires creating a new EmailJS template (id: template_welcome_back)
with variables: firstname, email, reference_client.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 18:18:22 +02:00
MVA Global Fret
39d3496758 Redesign contact map section with elegant card layout
- Replace the cramped map-container layout with a polished map-card
- Centered section header (h2 + gold-line + subtitle) above the card
- Navy gradient header inside the card with gold location icon
- Address displayed in the header alongside an "open in Google Maps" CTA
- Increased map height to 420px (320px on mobile)
- Soft shadow and gold-tinted border for premium feel
- Added mapSubtitle translation key (FR/EN/MG)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:58:39 +02:00
MVA Global Fret
b602e0d6f2 Fix remaining hardcoded French texts (i18n complete audit)
Critical fixes (user-reported):
- tarifs.html: delivery1Note "Livraison à domicile disponible"
- tarifs.html: delivery2Note "Retrait au bureau Cotisse de votre ville"
- contact.html: cgvLabel checkbox (with embedded CGV link, uses data-i18n-html)

Application page (mockup phone + payment methods section):
- mockupFlightTitle/Sub, mockupTab All/Transit/Delivered, mockupBadgeTransit
- mockupItemName/Ref, mockupStep1/3, mockupOrder1/2
- mockupPaymentTitle, mockupArrived
- paymentTitle, paymentSubtitle, cardLabel

Other pages:
- guide-envoi.html: warningContact callout paragraph
- about.html: channelMessenger/MessengerAction/PhoneMG/PhoneFR/Email labels

All keys added in FR/EN/MG.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:48:19 +02:00
MVA Global Fret
ec77737a9c Translate CGV page to EN/MG + full i18n audit fixes
- CGV page: added trilingual content (FR/EN/MG) using data-lang-block divs
- main.js: added data-lang-block toggle handler in applyLanguage()
- translations.js: added cgv.heroTitle/heroSubtitle for FR/EN/MG
- translations.js: fixed Malagasy (Fandefasana, Sarany), English (from Europe)
- translations.js: added delivery delay precision (depuis le dépôt Paris)
- translations.js: added 50+ missing i18n keys for tarifs/contact/guide/app/home
- All footer CGV links updated with data-i18n attributes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:39:55 +02:00
MVA Global Fret
ae85f2944b fix(i18n): traduire tous les textes codés en dur en français sur l'ensemble du site
Ajout de data-i18n sur tous les éléments non traduits (tarifs, contact, guide,
application, accueil) et ajout des clés correspondantes en FR/EN/MG dans
translations.js : détails tarifaires, cartes livraison, stats accueil, sections
CTA, récapitulatif contact, footer CGV.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:31:40 +02:00
MVA Global Fret
7e43b818d8 fix(mg): remplacer "vidiny/Vidinay" par "sarany/Sarany" partout en malgache
"Sarany" est le terme correct pour désigner les tarifs/prix dans ce contexte.
Corrige : heroTitle tarifs, heroSubtitle tarifs, nav pricing, heroCtaSecondary,
et value3Desc (about).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:20:16 +02:00
MVA Global Fret
f2b80d0e04 fix(en): corriger "between Europe and Madagascar" → "from Europe to Madagascar"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:18:31 +02:00
MVA Global Fret
50e792ef96 fix(mg): remplacer toutes les occurrences de "fandefa" par "fandefasana" en malgache
"Fandefasana" est la forme correcte (nominalisée) en malgache pour désigner
le transport/fret aérien. Corrige le titre hero et 5 autres occurrences dans
les textes descriptifs, sous-titres et footer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:16:06 +02:00
MVA Global Fret
1e2ee85b6e fix: préciser que le délai de 2 semaines est compté depuis l'arrivée au dépôt de Paris
Mise à jour de toutes les mentions du délai de livraison sur l'ensemble du site
(index, tarifs, contact) et dans les 3 langues (FR/EN/MG) pour indiquer clairement
que les 2 semaines sont comptées à compter de l'arrivée du colis au dépôt de Paris.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 17:12:20 +02:00
MVA Global Fret
15e98b9c32 feat: numérotation séquentielle MVA-XXX, texte succès corrigé, message email/spam 2026-05-04 16:28:28 +02:00
MVA Global Fret
eea5717143 fix: update EmailJS service ID to service_aeamo3x (Gmail connected) 2026-05-04 15:52:09 +02:00
MVA Global Fret
168a26ba32 feat: email de bienvenue automatique via EmailJS
- Ajout du SDK EmailJS (CDN) dans contact.html
- Initialisation EmailJS avec la clé publique dans form-handler.js
- Nouvelle fonction sendWelcomeEmail() : envoie au client son prénom,
  numéro de référence et l'adresse de dépôt Paris (AEIC Forwarding, Orly)
- Appel automatique dans showSuccess() après inscription réussie

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 15:15:44 +02:00
MVA Global Fret
7ea43c3b72 feat: activer le proxy Cloudflare pour la détection des doublons
Active WORKER_PROXY_URL pointant vers mva-hubspot-proxy.mvaglobalfret.workers.dev.
Le Worker interroge l'API CRM HubSpot côté serveur (contourne le CORS) et
retourne les données du contact si l'email est déjà enregistré.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 14:37:16 +02:00
MVA Global Fret
1970b10089 Sécurise form-handler : retire token côté client, prépare proxy Worker
- Supprime HUBSPOT_SERVICE_KEY du code JS public (évite l'exposition du token)
- Remplace l'appel CORS-bloqué vers api.hubapi.com par un appel au proxy
  Cloudflare Worker (WORKER_PROXY_URL, vide par défaut = sans blocage)
- Ajoute cloudflare-worker/hubspot-proxy.js : code complet du Worker à
  déployer gratuitement depuis dash.cloudflare.com (sans CLI ni Node.js)
- Quand WORKER_PROXY_URL est renseigné, la détection doublon est active :
  un client existant voit son message "déjà inscrit" sans re-soumission

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 14:13:17 +02:00
MVA Global Fret
fa4b49e1b8 Unicité référence client + détection doublon email
- generateRefNumber() basé sur timestamp : références jamais identiques
- checkExistingContact() : lecture HubSpot via clé de service (read-only)
- Si email déjà connu : affiche message 'déjà client' + référence existante,
  AUCUNE soumission envoyée (référence existante jamais modifiée)
- Notification interne Formspree si tentative double inscription
- Traductions FR/EN/MG pour les nouveaux messages
2026-05-03 16:23:49 +02:00
MVA Global Fret
64f5401112 feat: generate client reference number (MVA-YYYY-XXXX), save to HubSpot reference_client field, display in success message 2026-05-03 11:20:52 +02:00
MVA Global Fret
938efe582f Initial commit — Site MVA Global Fret 2026-05-02 22:23:48 +02:00