258 lines
12 KiB
Vue
258 lines
12 KiB
Vue
<script setup lang="ts">
|
|
import DatePicker from '@/components/DatePicker.vue';
|
|
import LandingLayout from '@/layouts/app/LandingLayout.vue';
|
|
import { loadStripe } from '@stripe/stripe-js';
|
|
import axios from 'axios';
|
|
import { ref } from 'vue';
|
|
|
|
interface UserForm {
|
|
fullname: string;
|
|
email: string;
|
|
}
|
|
|
|
const userForm = ref<UserForm>({
|
|
fullname: '',
|
|
email: '',
|
|
});
|
|
const selectedDate = ref(new Date());
|
|
|
|
const loading = ref<boolean>(false);
|
|
|
|
const handleAppointment = () => {
|
|
redirectToStipeCheckout();
|
|
};
|
|
|
|
const redirectToStipeCheckout = async () => {
|
|
loading.value = true;
|
|
try {
|
|
const res = await axios.post('/checkout-rendez-vous', {
|
|
userForm: userForm.value,
|
|
selectedDate: selectedDate.value,
|
|
});
|
|
const sessionId = res.data.sessionId;
|
|
const stripe = await loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY!);
|
|
if (stripe) {
|
|
const { error } = await stripe.redirectToCheckout({ sessionId });
|
|
|
|
if (error) {
|
|
console.error('Stripe redirect error:', error.message);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error initiating Stripe checkout:', error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<LandingLayout>
|
|
<!-- Background elements -->
|
|
<div class="pointer-events-none absolute inset-0 overflow-hidden">
|
|
<div class="absolute top-0 left-0 h-1/3 w-1/3 opacity-10">
|
|
<img src="/background/IMG_2230.jpg" alt="Decorative background" class="h-full w-full rounded-full object-cover" />
|
|
</div>
|
|
<div class="absolute right-0 bottom-0 h-1/3 w-1/3 opacity-10">
|
|
<img src="/background/IMG_2245.jpg" alt="Decorative background" class="h-full w-full rounded-full object-cover" />
|
|
</div>
|
|
<div
|
|
v-for="i in 8"
|
|
:key="'particle-' + i"
|
|
class="animate-float absolute rounded-full opacity-20"
|
|
:class="{
|
|
'bg-[var(--c-gold)]': i % 2 === 0,
|
|
'bg-[var(--c-purple)]': i % 2 === 1,
|
|
}"
|
|
:style="{
|
|
width: `${10 + i * 2}px`,
|
|
height: `${10 + i * 2}px`,
|
|
top: `${(i * 12) % 100}%`,
|
|
left: `${(i * 10) % 100}%`,
|
|
animationDelay: `${i * 0.5}s`,
|
|
}"
|
|
></div>
|
|
</div>
|
|
|
|
<main class="relative z-10 flex flex-1 justify-center px-6 py-16 sm:px-8 lg:px-10">
|
|
<div class="layout-content-container flex w-full max-w-5xl flex-col items-center gap-12">
|
|
<!-- Header with decorative elements -->
|
|
<div class="relative text-center">
|
|
<h1 class="relative mb-4 text-4xl font-black text-white md:text-5xl">
|
|
<span class="relative z-10">Réservez votre consultation</span>
|
|
<span
|
|
class="absolute -bottom-2 left-1/4 h-1 w-1/2 bg-gradient-to-r from-transparent via-[var(--c-gold)] to-transparent"
|
|
></span>
|
|
</h1>
|
|
<p class="mx-auto max-w-2xl text-lg text-white/80">
|
|
Choisissez une date et laissez-vous guider vers une consultation transformative
|
|
</p>
|
|
|
|
<!-- Decorative sparkles -->
|
|
<div class="absolute -top-6 -right-6 text-2xl text-[var(--c-gold)] opacity-60">✦</div>
|
|
<div class="absolute -bottom-4 -left-6 text-xl text-[var(--c-purple)] opacity-60">✦</div>
|
|
</div>
|
|
|
|
<div class="flex w-full flex-wrap items-start justify-center gap-10 md:gap-16">
|
|
<!-- Date Picker with decorative frame -->
|
|
<div class="relative">
|
|
<div
|
|
class="absolute -inset-4 rounded-2xl bg-gradient-to-br from-[var(--c-purple)]/20 to-[var(--c-gold)]/10 opacity-0 blur-sm transition-opacity duration-500 group-hover:opacity-100"
|
|
></div>
|
|
<date-picker v-model:selectedDate="selectedDate" />
|
|
</div>
|
|
|
|
<!-- Form Container -->
|
|
<div
|
|
class="relative flex max-w-md min-w-[320px] flex-1 flex-col gap-6 overflow-hidden rounded-2xl border border-[var(--c-purple)]/30 bg-gradient-to-br from-[var(--card)]/90 to-[var(--card)]/80 p-6 shadow-xl ring-1 ring-[var(--c-purple)]/40 backdrop-blur-sm md:p-8"
|
|
>
|
|
<!-- Background glow effects -->
|
|
<div class="pointer-events-none absolute -top-16 -left-16 h-40 w-40 rounded-full bg-[var(--c-purple)]/20 blur-3xl"></div>
|
|
<div class="pointer-events-none absolute -right-16 -bottom-16 h-40 w-40 rounded-full bg-[var(--c-gold)]/15 blur-3xl"></div>
|
|
|
|
<!-- Decorative corner images -->
|
|
<div class="absolute top-4 left-4 h-12 w-12 opacity-20">
|
|
<img src="/background/IMG_2230.jpg" alt="Decorative corner" class="h-full w-full rounded-lg object-cover" />
|
|
</div>
|
|
<div class="absolute right-4 bottom-4 h-12 w-12 opacity-20">
|
|
<img src="/background/IMG_2245.jpg" alt="Decorative corner" class="h-full w-full rounded-lg object-cover" />
|
|
</div>
|
|
|
|
<!-- Form header with icon -->
|
|
<div class="relative z-10 text-center">
|
|
<div
|
|
class="mb-3 inline-flex h-12 w-12 items-center justify-center rounded-full bg-gradient-to-br from-[var(--c-purple)]/30 to-[var(--c-purple)]/20"
|
|
>
|
|
<svg class="h-6 w-6 text-[var(--c-gold)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
|
></path>
|
|
</svg>
|
|
</div>
|
|
<p class="text-lg font-black text-white">Entrez vos informations</p>
|
|
</div>
|
|
|
|
<form class="relative z-10 flex flex-col gap-6" @submit.prevent="handleAppointment">
|
|
<div>
|
|
<label class="mb-2 block flex items-center gap-2 text-sm font-black text-white" for="name">
|
|
<span>Nom complet</span>
|
|
<span class="text-[var(--c-gold)]">✦</span>
|
|
</label>
|
|
<input
|
|
class="w-full rounded-lg border border-[var(--c-purple)]/40 bg-black/40 p-3 text-base text-white placeholder-white/40 ring-0 backdrop-blur-sm transition-colors outline-none focus:border-[var(--c-purple)] focus:ring-0"
|
|
id="name"
|
|
name="name"
|
|
placeholder="Votre nom complet"
|
|
type="text"
|
|
v-model="userForm.fullname"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="mb-2 block flex items-center gap-2 text-sm font-black text-white" for="email">
|
|
<span>Adresse e-mail</span>
|
|
<span class="text-[var(--c-gold)]">✦</span>
|
|
</label>
|
|
<input
|
|
class="w-full rounded-lg border border-[var(--c-purple)]/40 bg-black/40 p-3 text-base text-white placeholder-white/40 ring-0 backdrop-blur-sm transition-colors outline-none focus:border-[var(--c-purple)] focus:ring-0"
|
|
id="email"
|
|
name="email"
|
|
placeholder="Votre adresse e-mail"
|
|
type="email"
|
|
v-model="userForm.email"
|
|
/>
|
|
</div>
|
|
<button
|
|
class="gold-trail-btn group relative mt-2 flex h-14 w-full cursor-pointer items-center justify-center overflow-hidden rounded-full bg-gradient-to-r from-[var(--c-gold)] to-yellow-400 px-8 text-lg font-bold tracking-wide text-[var(--c-purple)] shadow-lg transition-all duration-300 hover:shadow-[var(--c-gold)]/40 disabled:opacity-60"
|
|
type="submit"
|
|
:disabled="loading"
|
|
>
|
|
<span class="relative z-10 flex items-center gap-2">
|
|
<span>{{ loading ? 'Traitement...' : 'Confirmer et Payer' }}</span>
|
|
<svg v-if="!loading" class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
</span>
|
|
<span
|
|
class="absolute inset-0 translate-x-[-100%] -skew-x-12 bg-gradient-to-r from-transparent via-white/30 to-transparent transition-transform duration-700 group-hover:translate-x-[100%]"
|
|
></span>
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Security badge -->
|
|
<div class="relative z-10 mt-4 flex items-center justify-center gap-2 text-xs text-white/60">
|
|
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
|
></path>
|
|
</svg>
|
|
<span>Paiement sécurisé avec Stripe</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</LandingLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* Custom animations */
|
|
@keyframes float {
|
|
0%,
|
|
100% {
|
|
transform: translateY(0) rotate(0deg);
|
|
}
|
|
33% {
|
|
transform: translateY(-5px) rotate(2deg);
|
|
}
|
|
66% {
|
|
transform: translateY(3px) rotate(-2deg);
|
|
}
|
|
}
|
|
|
|
.animate-float {
|
|
animation: float 8s ease-in-out infinite;
|
|
}
|
|
|
|
/* Gold trail effect */
|
|
.gold-trail-btn {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.gold-trail-btn::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(45deg, transparent, rgba(255, 215, 0, 0.2), transparent);
|
|
transform: translateX(-100%);
|
|
transition: transform 0.6s;
|
|
z-index: 1;
|
|
}
|
|
|
|
.gold-trail-btn:hover::before {
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
/* Custom color variables */
|
|
:root {
|
|
--c-purple: #4c1d95;
|
|
--c-gold: rgba(245, 158, 11, 0.9);
|
|
--card: #1e1b4b;
|
|
}
|
|
|
|
/* Smooth transitions */
|
|
* {
|
|
transition-property: color, background-color, transform, opacity, box-shadow;
|
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
</style>
|