Merge branch 'video' into dev
All checks were successful
Dev CI / Backend — Lint (push) Successful in 10m22s
Dev CI / Frontend — Lint & Type-check (push) Successful in 11m6s
Dev CI / Backend — Unit Tests (push) Successful in 10m8s
Dev CI / Frontend — Unit Tests (push) Successful in 10m37s
Dev CI / Notify Failure (push) Has been skipped
All checks were successful
Dev CI / Backend — Lint (push) Successful in 10m22s
Dev CI / Frontend — Lint & Type-check (push) Successful in 11m6s
Dev CI / Backend — Unit Tests (push) Successful in 10m8s
Dev CI / Frontend — Unit Tests (push) Successful in 10m37s
Dev CI / Notify Failure (push) Has been skipped
This commit is contained in:
commit
a2a06c008b
@ -27,6 +27,7 @@ import {
|
|||||||
import { useTranslations, useLocale } from 'next-intl';
|
import { useTranslations, useLocale } from 'next-intl';
|
||||||
import { useAuth } from '@/lib/context/auth-context';
|
import { useAuth } from '@/lib/context/auth-context';
|
||||||
import { LandingHeader, LandingFooter } from '@/components/layout';
|
import { LandingHeader, LandingFooter } from '@/components/layout';
|
||||||
|
import { DemoVideoModal } from '@/components/DemoVideoModal';
|
||||||
|
|
||||||
function AnimatedCounter({
|
function AnimatedCounter({
|
||||||
end,
|
end,
|
||||||
@ -96,6 +97,7 @@ export default function LandingPage() {
|
|||||||
const isHowInView = useInView(howRef, { once: true, amount: 0.2 });
|
const isHowInView = useInView(howRef, { once: true, amount: 0.2 });
|
||||||
|
|
||||||
const [billingYearly, setBillingYearly] = useState(false);
|
const [billingYearly, setBillingYearly] = useState(false);
|
||||||
|
const [isDemoOpen, setIsDemoOpen] = useState(false);
|
||||||
|
|
||||||
const { scrollYProgress } = useScroll();
|
const { scrollYProgress } = useScroll();
|
||||||
const backgroundY = useTransform(scrollYProgress, [0, 1], ['0%', '50%']);
|
const backgroundY = useTransform(scrollYProgress, [0, 1], ['0%', '50%']);
|
||||||
@ -374,7 +376,6 @@ export default function LandingPage() {
|
|||||||
<LayoutDashboard className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
<LayoutDashboard className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<>
|
|
||||||
<Link
|
<Link
|
||||||
href="/register"
|
href="/register"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -384,16 +385,14 @@ export default function LandingPage() {
|
|||||||
<span>{t('hero.ctaRegister')}</span>
|
<span>{t('hero.ctaRegister')}</span>
|
||||||
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
)}
|
||||||
href="/login"
|
<button
|
||||||
target="_blank"
|
type="button"
|
||||||
rel="noopener noreferrer"
|
onClick={() => setIsDemoOpen(true)}
|
||||||
className="px-6 sm:px-8 py-3 sm:py-4 bg-white text-brand-navy rounded-lg hover:bg-gray-50 transition-all hover:shadow-xl font-semibold text-base sm:text-lg w-full sm:w-auto text-center"
|
className="px-6 sm:px-8 py-3 sm:py-4 bg-white text-brand-navy rounded-lg hover:bg-gray-50 transition-all hover:shadow-xl font-semibold text-base sm:text-lg w-full sm:w-auto text-center"
|
||||||
>
|
>
|
||||||
{t('hero.ctaDemo')}
|
{t('hero.ctaDemo')}
|
||||||
</Link>
|
</button>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
@ -1076,6 +1075,8 @@ export default function LandingPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<LandingFooter />
|
<LandingFooter />
|
||||||
|
|
||||||
|
<DemoVideoModal isOpen={isDemoOpen} onClose={() => setIsDemoOpen(false)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
apps/frontend/public/assets/videos/demo-xpeditis.mp4
Normal file
BIN
apps/frontend/public/assets/videos/demo-xpeditis.mp4
Normal file
Binary file not shown.
BIN
apps/frontend/public/assets/videos/demo-xpeditis.webm
Normal file
BIN
apps/frontend/public/assets/videos/demo-xpeditis.webm
Normal file
Binary file not shown.
74
apps/frontend/src/components/DemoVideoModal.tsx
Normal file
74
apps/frontend/src/components/DemoVideoModal.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useCallback } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
|
||||||
|
interface DemoVideoModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DemoVideoModal({ isOpen, onClose }: DemoVideoModalProps) {
|
||||||
|
const handleKeyDown = useCallback(
|
||||||
|
(e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') onClose();
|
||||||
|
},
|
||||||
|
[onClose]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
};
|
||||||
|
}, [isOpen, handleKeyDown]);
|
||||||
|
|
||||||
|
if (typeof document === 'undefined') return null;
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<AnimatePresence>
|
||||||
|
{isOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.25 }}
|
||||||
|
className="fixed inset-0 z-[100] flex items-center justify-center bg-brand-navy/90 backdrop-blur-sm p-4 sm:p-8"
|
||||||
|
onClick={onClose}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label="Démonstration Xpeditis"
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
transition={{ duration: 0.3, ease: 'easeOut' }}
|
||||||
|
className="relative w-full max-w-5xl"
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
aria-label="Fermer"
|
||||||
|
className="absolute -top-12 right-0 p-2 text-white/80 hover:text-white transition-colors rounded-full hover:bg-white/10"
|
||||||
|
>
|
||||||
|
<X className="w-7 h-7" />
|
||||||
|
</button>
|
||||||
|
<div className="rounded-2xl overflow-hidden shadow-2xl ring-1 ring-white/20 bg-black aspect-video">
|
||||||
|
<video controls autoPlay playsInline className="w-full h-full object-contain">
|
||||||
|
<source src="/assets/videos/demo-xpeditis.mp4" type="video/mp4" />
|
||||||
|
<source src="/assets/videos/demo-xpeditis.webm" type="video/webm" />
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user