mirror of
https://github.com/rjNemo/vf-site
synced 2026-06-06 01:16:38 +00:00
feat(home): add T3 Airbnb CTA in hero (FR/EN)
feat(analytics): add apt prop to all Airbnb CTAs for clearer tracking (FR/EN, rates, detail, thank-you). style(home): increase apartment card image height on desktop (md:h-64).
This commit is contained in:
parent
bb76e40893
commit
d9dcd6fd5f
13 changed files with 2341 additions and 1131 deletions
15
AGENTS.md
15
AGENTS.md
|
|
@ -3,9 +3,12 @@
|
|||
## Project Structure & Modules
|
||||
|
||||
- `src/`: Astro app — `pages/` (locale folders: `fr/`, `en/`), `layouts/`, `styles/`.
|
||||
- `src/i18n/routes.ts`: central route manifest (`hrefFor`, `siblingPath`) for FR/EN slugs.
|
||||
- `public/`: static assets and redirects (`_redirects`, `assets/images`, `webfonts`, etc.).
|
||||
- `docs/spec/website-revamp-spec.md`: product/UX spec and release plan (source of truth).
|
||||
- `src/i18n/routes.ts`: central route manifest (`hrefFor`, `siblingPath`) for FR/EN
|
||||
slugs.
|
||||
- `public/`: static assets and redirects (`_redirects`, `assets/images`, `webfonts`,
|
||||
etc.).
|
||||
- `docs/spec/website-revamp-spec.md`: product/UX spec and release plan (source of
|
||||
truth).
|
||||
- Legacy: `lib/`, `pages/`, `data/` (Python generator) — deprecated; do not modify.
|
||||
|
||||
## Build, Test, and Development
|
||||
|
|
@ -20,7 +23,8 @@
|
|||
- UI: Tailwind CSS v4 (brand tokens: `--color-brand`, `--color-brand-600`).
|
||||
- Icons: `lucide-astro` inline SVGs; use `text-brand` for accent.
|
||||
- JS: vanilla only (no jQuery). Prefer lightweight patterns (e.g., scrollBy carousels).
|
||||
- i18n: prefer `getRelativeLocaleUrl()` from `astro:i18n` for links; use `siblingPath()` for toggles when slugs differ.
|
||||
- i18n: prefer `getRelativeLocaleUrl()` from `astro:i18n` for links; use `siblingPath()`
|
||||
for toggles when slugs differ.
|
||||
- Content slugs differ by locale (e.g., FR `avis` ↔ EN `reviews`) — never hardcode.
|
||||
|
||||
## Testing Guidelines
|
||||
|
|
@ -31,7 +35,8 @@
|
|||
|
||||
## Commit & Pull Requests
|
||||
|
||||
- Conventional Commits (examples): `feat(home): hero CTA`, `fix(i18n): toggle sibling path`.
|
||||
- Conventional Commits (examples): `feat(home): hero CTA`, `fix(i18n): toggle
|
||||
sibling path`.
|
||||
- PRs should include: build passing, screenshots (FR/EN), and spec updates when needed.
|
||||
- Keep focused and small; avoid mixing refactors with features.
|
||||
|
||||
|
|
|
|||
17
README.md
17
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# VillaFleurie Website (Astro + Tailwind)
|
||||
# VillaFleurie Website
|
||||
|
||||
This site is built with Astro and Tailwind, with FR/EN locales. The legacy Python generator remains for reference but is deprecated — new work happens in `src/`.
|
||||
This site is built with Astro and Tailwind, with FR/EN locales.
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
|
@ -12,7 +12,8 @@ This site is built with Astro and Tailwind, with FR/EN locales. The legacy Pytho
|
|||
|
||||
## Structure
|
||||
|
||||
- `src/pages/fr|en/`: localized pages (Home, Apartments, Reviews, Rates, Contact, Policies).
|
||||
- `src/pages/fr|en/`: localized pages (Home, Apartments, Reviews, Rates, Contact,
|
||||
Policies).
|
||||
- `src/layouts/`: shared layout with sticky header, language toggle, footer.
|
||||
- `src/styles/global.css`: Tailwind v4 with brand tokens (`--color-brand`, `--color-brand-600`).
|
||||
- `src/i18n/routes.ts`: route manifest (`siblingPath`, CTA label helper).
|
||||
|
|
@ -22,19 +23,17 @@ This site is built with Astro and Tailwind, with FR/EN locales. The legacy Pytho
|
|||
## i18n & Navigation
|
||||
|
||||
- Folder-based locales with different slugs (e.g., FR `avis` ↔ EN `reviews`).
|
||||
- Always use the route helpers for links and the language toggle; don’t hardcode cross‑locale paths.
|
||||
- Always use the route helpers for links and the language toggle; don’t hardcode
|
||||
cross‑locale paths.
|
||||
|
||||
## Forms & Analytics
|
||||
|
||||
- Netlify Forms on `/fr/contact/` and `/en/contact/`, with localized thank‑you pages.
|
||||
- Plausible events: `booking_request_submitted` (primary), `click_airbnb`, `click_booking`, etc.
|
||||
- Plausible events: `booking_request_submitted` (primary), `click_airbnb`,
|
||||
`click_booking`, etc.
|
||||
|
||||
## Contributing
|
||||
|
||||
- Use Conventional Commits. Include screenshots (FR/EN) for UI changes.
|
||||
- Keep the spec updated when requirements change.
|
||||
- See `AGENTS.md` for contributor guidelines and the current phase plan.
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||
|
|
|
|||
3402
pnpm-lock.yaml
3402
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -20,7 +20,7 @@ const contactHref = getRelativeLocaleUrl('en','/contact/');
|
|||
</ul>
|
||||
<div class="mt-6 flex gap-3">
|
||||
<a href={contactHref} class="inline-flex items-center rounded-lg bg-brand px-4 py-2 text-white hover:bg-brand-600">Send a Request</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2">Book on Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2" onclick="plausible('click_airbnb',{props:{locale:'en',page:'apartment',position:'body',apt:'t2'}})">Book on Airbnb (T2)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const contactHref = getRelativeLocaleUrl('en','/contact/');
|
|||
</ul>
|
||||
<div class="mt-6 flex gap-3">
|
||||
<a href={contactHref} class="inline-flex items-center rounded-lg bg-brand px-4 py-2 text-white hover:bg-brand-600">Send a Request</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2">Book on Airbnb (T3)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2" onclick="plausible('click_airbnb',{props:{locale:'en',page:'apartment',position:'body',apt:'t3'}})">Book on Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ const t3Href = getRelativeLocaleUrl('en','/apartments/t3-azur/');
|
|||
<div class="mt-8 flex flex-wrap gap-3">
|
||||
<a href={contactHref} class="inline-flex items-center rounded-lg bg-brand px-5 py-3 text-white hover:bg-brand-600">Send a Request</a>
|
||||
<a href="https://www.booking.com/hotel/gp/villafleurie.fr.html" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" onclick="plausible('click_booking',{props:{locale:'en',page:'home',position:'hero'}})">Book on Booking.com</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" onclick="plausible('click_airbnb',{props:{locale:'en',page:'home',position:'hero'}})">Book on Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" onclick="plausible('click_airbnb',{props:{locale:'en',page:'home',position:'hero',apt:'t2'}})">Book on Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" onclick="plausible('click_airbnb',{props:{locale:'en',page:'home',position:'hero',apt:'t3'}})">Book on Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -74,7 +75,7 @@ const t3Href = getRelativeLocaleUrl('en','/apartments/t3-azur/');
|
|||
<div class="mt-2 h-1 w-16 bg-brand rounded"></div>
|
||||
<div class="mt-6 grid md:grid-cols-2 gap-6">
|
||||
<article class="rounded-xl overflow-hidden border border-slate-200 bg-white shadow-sm">
|
||||
<img src="/assets/images/villafleurie_t2_salon_2.jpg" alt="T2 Corail" class="h-48 w-full object-cover" />
|
||||
<img src="/assets/images/villafleurie_t2_salon_2.jpg" alt="T2 Corail" class="h-48 md:h-64 w-full object-cover" />
|
||||
<div class="p-4">
|
||||
<h3 class="text-lg font-semibold">T2 Corail</h3>
|
||||
<p class="mt-1 text-sm text-slate-600">45 m² • 2–3 guests • 1 queen + sofa‑bed • €59/night</p>
|
||||
|
|
@ -82,7 +83,7 @@ const t3Href = getRelativeLocaleUrl('en','/apartments/t3-azur/');
|
|||
</div>
|
||||
</article>
|
||||
<article class="rounded-xl overflow-hidden border border-slate-200 bg-white shadow-sm">
|
||||
<img src="/assets/images/villafleurie_t3_salon.jpg" alt="T3 Azur" class="h-48 w-full object-cover" />
|
||||
<img src="/assets/images/villafleurie_t3_salon.jpg" alt="T3 Azur" class="h-48 md:h-64 w-full object-cover" />
|
||||
<div class="p-4">
|
||||
<h3 class="text-lg font-semibold">T3 Azur</h3>
|
||||
<p class="mt-1 text-sm text-slate-600">55 m² • up to 4 guests • 2 queen beds • €79/night</p>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
|
|||
</div>
|
||||
<div class="mt-6 flex gap-3">
|
||||
<a href="https://www.booking.com/hotel/gp/villafleurie.fr.html" target="_blank" rel="noopener" class="underline">Book on Booking.com</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline">Book on Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline">Book on Airbnb (T3)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline" onclick="plausible('click_airbnb',{props:{locale:'en',page:'rates',position:'body',apt:'t2'}})">Book on Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline" onclick="plausible('click_airbnb',{props:{locale:'en',page:'rates',position:'body',apt:'t3'}})">Book on Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
|
|||
<p class="mt-2 text-slate-700">We’ll get back to you shortly. For instant booking:</p>
|
||||
<div class="mt-4 flex gap-3">
|
||||
<a href="https://www.booking.com/hotel/gp/villafleurie.fr.html" target="_blank" rel="noopener" class="underline">Booking.com</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline">Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline">Airbnb (T3)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline" onclick="plausible('click_airbnb',{props:{locale:'en',page:'thank-you',position:'body',apt:'t2'}})">Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline" onclick="plausible('click_airbnb',{props:{locale:'en',page:'thank-you',position:'body',apt:'t3'}})">Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const contactHref = getRelativeLocaleUrl('fr','/contact/');
|
|||
</ul>
|
||||
<div class="mt-6 flex gap-3">
|
||||
<a href={contactHref} class="inline-flex items-center rounded-lg bg-brand px-4 py-2 text-white hover:bg-brand-600">Envoyer une demande</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2">Réserver sur Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'apartment',position:'body',apt:'t2'}})}>Réserver sur Airbnb (T2)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const contactHref = getRelativeLocaleUrl('fr','/contact/');
|
|||
</ul>
|
||||
<div class="mt-6 flex gap-3">
|
||||
<a href={contactHref} class="inline-flex items-center rounded-lg bg-brand px-4 py-2 text-white hover:bg-brand-600">Envoyer une demande</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2">Réserver sur Airbnb (T3)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-slate-200 px-4 py-2" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'apartment',position:'body',apt:'t3'}})}>Réserver sur Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ const t3Href = getRelativeLocaleUrl('fr','/appartements/t3-azur/');
|
|||
<div class="mt-8 flex flex-wrap gap-3">
|
||||
<a href={contactHref} class="inline-flex items-center rounded-lg bg-brand px-5 py-3 text-white hover:bg-brand-600">Envoyer une demande</a>
|
||||
<a href="https://www.booking.com/hotel/gp/villafleurie.fr.html" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" on:click={() => plausible('click_booking', {props:{locale:'fr',page:'home',position:'hero'}})}>Réserver sur Booking.com</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'home',position:'hero'}})}>Réserver sur Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'home',position:'hero',apt:'t2'}})}>Réserver sur Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="inline-flex items-center rounded-lg border border-white/70 bg-white/90 px-5 py-3 text-slate-900 hover:bg-white" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'home',position:'hero',apt:'t3'}})}>Réserver sur Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -75,7 +76,7 @@ const t3Href = getRelativeLocaleUrl('fr','/appartements/t3-azur/');
|
|||
<div class="mt-2 h-1 w-16 bg-brand rounded"></div>
|
||||
<div class="mt-6 grid md:grid-cols-2 gap-6">
|
||||
<article class="rounded-xl overflow-hidden border border-slate-200 bg-white shadow-sm">
|
||||
<img src="/assets/images/villafleurie_t2_salon_2.jpg" alt="T2 Corail" class="h-48 w-full object-cover" />
|
||||
<img src="/assets/images/villafleurie_t2_salon_2.jpg" alt="T2 Corail" class="h-48 md:h-64 w-full object-cover" />
|
||||
<div class="p-4">
|
||||
<h3 class="text-lg font-semibold">T2 Corail</h3>
|
||||
<p class="mt-1 text-sm text-slate-600">45 m² • 2–3 pers. • 1 lit queen + canapé‑lit • 59 €/nuit</p>
|
||||
|
|
@ -83,7 +84,7 @@ const t3Href = getRelativeLocaleUrl('fr','/appartements/t3-azur/');
|
|||
</div>
|
||||
</article>
|
||||
<article class="rounded-xl overflow-hidden border border-slate-200 bg-white shadow-sm">
|
||||
<img src="/assets/images/villafleurie_t3_salon.jpg" alt="T3 Azur" class="h-48 w-full object-cover" />
|
||||
<img src="/assets/images/villafleurie_t3_salon.jpg" alt="T3 Azur" class="h-48 md:h-64 w-full object-cover" />
|
||||
<div class="p-4">
|
||||
<h3 class="text-lg font-semibold">T3 Azur</h3>
|
||||
<p class="mt-1 text-sm text-slate-600">55 m² • jusqu’à 4 pers. • 2 lits queen • 79 €/nuit</p>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
|
|||
<p class="mt-2 text-slate-700">Nous vous répondrons rapidement. Pour une réservation instantanée :</p>
|
||||
<div class="mt-4 flex gap-3">
|
||||
<a href="https://www.booking.com/hotel/gp/villafleurie.fr.html" target="_blank" rel="noopener" class="underline">Booking.com</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline">Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline">Airbnb (T3)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'thank-you',position:'body',apt:'t2'}})}>Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'thank-you',position:'body',apt:'t3'}})}>Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
|
|||
</div>
|
||||
<div class="mt-6 flex gap-3">
|
||||
<a href="https://www.booking.com/hotel/gp/villafleurie.fr.html" target="_blank" rel="noopener" class="underline">Réserver sur Booking.com</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline">Réserver sur Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline">Réserver sur Airbnb (T3)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t2" target="_blank" rel="noopener" class="underline" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'rates',position:'body',apt:'t2'}})}>Réserver sur Airbnb (T2)</a>
|
||||
<a href="https://airbnb.fr/h/villafleurie-t3" target="_blank" rel="noopener" class="underline" on:click={() => plausible('click_airbnb', {props:{locale:'fr',page:'rates',position:'body',apt:'t3'}})}>Réserver sur Airbnb (T3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
|
|
|||
Loading…
Reference in a new issue