diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..74c9bc999 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,37 @@ +{ + // - Git + "git.autofetch": true, + + // - TypeScript + "typescript.updateImportsOnFileMove.enabled": "always", + + // - Actions on save + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "always" + }, + + // - Lint and format + "editor.formatOnSave": true, + "eslint.format.enable": true, + "eslint.validate": [ + "javascript" + ], + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "stylelint.vscode-stylelint" + }, + + // - Common + "editor.tabSize": 2, + } \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 58e3051d2..f5743222b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -9,7 +9,7 @@ import { Provider } from "react-wrap-balancer"; import Footer from "@/components/layout/footer/footer"; import Script from "next/script"; import { Toaster } from "@/components/ui/toaster"; -import { Figtree } from "next/font/google" +import { Figtree } from "next/font/google"; import { OrganizationSchema } from "@/components/json-ld/organization-schema"; export const figtree = Figtree({ diff --git a/src/components/marketing/landing/testimonial/index.tsx b/src/components/marketing/landing/testimonial/index.tsx new file mode 100644 index 000000000..b7bb84bbe --- /dev/null +++ b/src/components/marketing/landing/testimonial/index.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import Image from "next/image"; + +export interface TestimonialProps { + quote: string; + author: string; + role: string; + company: string; + imageSrc: string; + imageAlt?: string; +} + +const Testimonial: React.FC = ({ + quote, + author, + role, + company, + imageSrc, + imageAlt = `Photo ${author}`, +}) => { + return ( +
+
+ {/* Image - centrée sur mobile, à gauche sur desktop */} +
+ {imageAlt} +
+ + {/* Contenu texte */} +
+

+ “{quote}” +

+

+ {author}, {role} @{" "} + {company} +

+
+
+
+ ); +}; + +export default Testimonial; diff --git a/src/components/marketing/landing/visitor/featured/index.tsx b/src/components/marketing/landing/visitor/featured/index.tsx index d1bc68ab7..49fd30c7e 100644 --- a/src/components/marketing/landing/visitor/featured/index.tsx +++ b/src/components/marketing/landing/visitor/featured/index.tsx @@ -52,13 +52,139 @@ const Featured: React.FC = () => { /> -
+
+ {/* SVG décoratif gauche - visible uniquement desktop */} +
+ + + + + + + + + + + + + + + + + + +
+ + {/* SVG décoratif droite - visible uniquement desktop */} +
+ + + + + + + + + + + + + + + + + + +
+ {"Équipe @@ -79,12 +205,16 @@ const Featured: React.FC = () => { intéressés par les nouvelles technologies et le monde de la création (design, musique, etc.). À partir de là nous avons créé un Studio et une communauté autour de celui-ci, nous adorons - nous retrouver autour d'une tasse de café ☕️ pour - discuter, jouer et surtout créer des projets. + nous retrouver autour d'une tasse de café ☕️ pour discuter, + jouer et surtout créer des projets.

- +
diff --git a/src/components/marketing/landing/visitor/reviews/index.tsx b/src/components/marketing/landing/visitor/reviews/index.tsx new file mode 100644 index 000000000..532f02f4b --- /dev/null +++ b/src/components/marketing/landing/visitor/reviews/index.tsx @@ -0,0 +1,108 @@ +import React from "react"; +import Image from "next/image"; +import Testimonial from "../../testimonial"; + +// Données extraites pour éviter la répétition +const reviewsData = [ + { + id: 1, + text: "Composé de talents variés, allant de développeurs à des designers passionnés. Chacun apporte sa touche unique à nos projets. Ensemble, nous formons un collectif où l'innovation est au cœur de tout ce que nous entreprenons.", + author: "Cécile Pislor", + role: "CEO @ DroitAuCoeurCoaching", + avatar: "/favicon-32x32.png", + }, + { + id: 2, + text: "Composé de talents variés, allant de développeurs à des designers passionnés. Chacun apporte sa touche unique à nos projets. Ensemble, nous formons un collectif où l'innovation est au cœur de tout ce que nous entreprenons.", + author: "Cécile Pislor", + role: "CEO @ DroitAuCoeurCoaching", + avatar: "/favicon-32x32.png", + }, + { + id: 3, + text: "Composé de talents variés, allant de développeurs à des designers passionnés. Chacun apporte sa touche unique à nos projets. Ensemble, nous formons un collectif où l'innovation est au cœur de tout ce que nous entreprenons.", + author: "Cécile Pislor", + role: "CEO @ DroitAuCoeurCoaching", + avatar: "/favicon-32x32.png", + }, +]; + +const Reviews: React.FC = () => { + return ( +
+
+ {/* Titre sticky qui reste derrière les commentaires */} +
+

+ Ce que nos experts disent de nous. +

+

+ On ne les a pas du tout forcés à parler. +

+
+ + {/* Grille des avis qui défile devant le titre */} +
+ {reviewsData.map((review, index) => { + const getTransform = (index: number) => { + switch (index) { + case 0: + return "translate-y-0"; + case 1: + return "sm:translate-y-8 md:translate-y-[30rem]"; // Moins de décalage sur mobile + case 2: + return "sm:translate-y-4 md:translate-y-32"; // Moins de décalage sur mobile + default: + return "translate-y-0"; + } + }; + + return ( +
+
+

+ “{review.text}” +

+
+
+ {`Photo +
+

+ {review.author} +

+

+ {review.role} +

+
+
+
+ ); + })} +
+ + {/* Spacer responsive */} +
+
+ + {/* Témoignage Lucas Bodin */} + +
+ ); +}; + +export default Reviews; diff --git a/src/components/marketing/landing/visitor/services/index.tsx b/src/components/marketing/landing/visitor/services/index.tsx new file mode 100644 index 000000000..95cce3826 --- /dev/null +++ b/src/components/marketing/landing/visitor/services/index.tsx @@ -0,0 +1,133 @@ +"use client"; + +import React, { useEffect, useRef, useState } from "react"; +import Link from "next/link"; + +const colorSets = [ + { + text: "text-black", + button: "bg-black text-white", + }, + { + text: "text-[#652599]", + button: "bg-[#652599] text-white", + }, + { + text: "text-[#23cd82]", + button: "bg-[#23cd82] text-white", + }, + { + text: "text-[#ee2400]", + button: "bg-[#ee2400] text-white", + }, +]; + +const services = [ + { + color: "#652599", + title: "Design UX UI/Branding", + description: + "Chacun de nos projets à son moodboard, et son maquettage UI complet et prototypé. Même leurs identités graphiques sont faites maisons.", + }, + { + color: "#23cd82", + title: "Développement Front & Back End", + description: + "Nous réalisons chacun de nos projets from-scratch avec un développement front (web, app, logiciel) ainsi qu’en profondeur avec le back (api, bots).", + }, + { + color: "#ee2400", + title: "Intégrations Web", + description: + "Selon les besoins de nos projets, nous pouvons les créer ou les modifier via des intégrations comme Wordpress, Shopify, Webflow, etc.", + }, +]; + +const Services: React.FC = () => { + const [colorIndex, setColorIndex] = useState(0); + const [isMobile, setIsMobile] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + setIsMobile(window.innerWidth < 768); + + const handleScroll = () => { + if (!containerRef.current || isMobile) return; + + const containerTop = + containerRef.current.getBoundingClientRect().top + window.scrollY; + const scrollY = window.scrollY; + const earlyOffset = 25; + const relativeY = scrollY - containerTop + earlyOffset; + + const sectionHeight = 300; + let index = Math.floor(relativeY / sectionHeight); + index = Math.max(0, Math.min(index, colorSets.length - 1)); + + setColorIndex(index); + }; + + window.addEventListener("scroll", handleScroll); + handleScroll(); + + return () => window.removeEventListener("scroll", handleScroll); + }, [isMobile]); + + const colors = isMobile ? colorSets[0] : colorSets[colorIndex]; + + return ( +
+ {/* Bloc gauche */} +
+

+ Un studio qui combine
+ les savoirs-faire créatifs +

+

+ Nous sommes experts dans nos domaines :
+ développement web, design d'interface, audits ... +

+ + + +
+ + {/* Bloc droite */} +
+

+ Nos projets allient nos compétences afin qu’ils soient resplendissants + sur le web. +

+ + {services.map((service, index) => ( +
+
+

{service.title}

+

{service.description}

+
+ ))} +
+
+ ); +}; + +export default Services; diff --git a/src/components/marketing/landing/visitor/tonightpass/index.tsx b/src/components/marketing/landing/visitor/tonightpass/index.tsx new file mode 100644 index 000000000..bce7d7edb --- /dev/null +++ b/src/components/marketing/landing/visitor/tonightpass/index.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; + +const Tonightpass: React.FC = () => { + return ( +
+
+ + + +
+

+ Tonight Pass,
la billetterie 2.0 +

+

+ Réservez votre entrée, entrez rapidement,
+ restez en contact avec les gens. +

+ + En quelques clics depuis votre canapé 🛋️ + + + + +
+ {" "} + {/* mb-4 au lieu de mb-8, padding mobile */} + Showcase Tonight Pass +
+
+ ); +}; + +export default Tonightpass; diff --git a/src/screens/marketing/landing/visitor/index.tsx b/src/screens/marketing/landing/visitor/index.tsx index 29fca6e55..2a98a3e7e 100644 --- a/src/screens/marketing/landing/visitor/index.tsx +++ b/src/screens/marketing/landing/visitor/index.tsx @@ -1,14 +1,20 @@ import Featured from "@/components/marketing/landing/visitor/featured"; import Projects from "@/components/marketing/landing/visitor/projects"; import Team from "@/components/marketing/landing/visitor/team"; +import Tonightpass from "@/components/marketing/landing/visitor/tonightpass"; +import Services from "@/components/marketing/landing/visitor/services"; import type { NextPage } from "next"; import React from "react"; +import Reviews from "@/components/marketing/landing/visitor/reviews"; const VisitorLanding: NextPage = () => { return ( <> + + + ); diff --git a/tailwind.config.ts b/tailwind.config.ts index 45cdf1039..898876389 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -78,3 +78,5 @@ export default { }, plugins: [tailwindcssAnimatePlugin], } satisfies Config; + +