0.0.9
This commit is contained in:
165
src/components/ui/contact.tsx
Normal file
165
src/components/ui/contact.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import { BuildingOffice2Icon, EnvelopeIcon, PhoneIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
interface Props {
|
||||
phoneLabel: string
|
||||
phoneValue: string
|
||||
emailLabel: string
|
||||
emailValue: string
|
||||
addressLabel: string
|
||||
addressValue: string
|
||||
serviceAreaLabel: string
|
||||
serviceArea: string
|
||||
firstNameLabel?: string
|
||||
lastNameLabel?: string
|
||||
emailFieldLabel?: string
|
||||
phoneFieldLabel?: string
|
||||
messageLabel?: string
|
||||
submitLabel?: string
|
||||
}
|
||||
|
||||
export default function Contact({
|
||||
phoneLabel,
|
||||
phoneValue,
|
||||
emailLabel,
|
||||
emailValue,
|
||||
addressLabel,
|
||||
addressValue,
|
||||
serviceAreaLabel,
|
||||
serviceArea,
|
||||
firstNameLabel = 'Prénom',
|
||||
lastNameLabel = 'Nom',
|
||||
emailFieldLabel = 'Courriel',
|
||||
phoneFieldLabel = 'Téléphone',
|
||||
messageLabel = 'Message',
|
||||
submitLabel = 'Envoyer',
|
||||
}: Props) {
|
||||
const inputClass =
|
||||
'block w-full rounded-md bg-white px-3.5 py-2 text-base text-brand-dark outline-1 -outline-offset-1 outline-brand-200 placeholder:text-brand-400 focus:outline-2 focus:-outline-offset-2 focus:outline-brand-600 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-brand-400 dark:focus:outline-brand-500'
|
||||
const labelClass = 'block text-sm/6 font-semibold text-brand-dark dark:text-white'
|
||||
|
||||
return (
|
||||
<div className="relative isolate bg-white dark:bg-brand-900">
|
||||
<div className="mx-auto grid max-w-7xl grid-cols-1 lg:grid-cols-2">
|
||||
<div className="relative px-6 pt-12 pb-20 lg:static">
|
||||
<div className="mx-auto max-w-xl lg:mx-0 lg:max-w-lg">
|
||||
<div className="absolute inset-y-0 left-0 -z-10 w-full overflow-hidden bg-brand-50 ring-1 ring-brand-900/10 lg:w-1/2 dark:bg-brand-900 dark:ring-white/10">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="absolute inset-0 size-full mask-[radial-gradient(100%_100%_at_top_right,white,transparent)] stroke-brand-200 dark:stroke-white/10"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
x="100%"
|
||||
y={-1}
|
||||
id="83fd4e5a-9d52-42fc-97b6-718e5d7ee527"
|
||||
width={200}
|
||||
height={200}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<path d="M130 200V.5M.5 .5H200" fill="none" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" strokeWidth={0} className="fill-white dark:fill-brand-900" />
|
||||
<svg x="100%" y={-1} className="overflow-visible fill-brand-50 dark:fill-brand-800/20">
|
||||
<path d="M-470.5 0h201v201h-201Z" strokeWidth={0} />
|
||||
</svg>
|
||||
<rect fill="url(#83fd4e5a-9d52-42fc-97b6-718e5d7ee527)" width="100%" height="100%" strokeWidth={0} />
|
||||
</svg>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute top-[calc(100%-13rem)] -left-56 hidden transform-gpu blur-3xl lg:top-[calc(50%-7rem)] lg:left-[max(-14rem,calc(100%-59rem))] dark:block"
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
clipPath:
|
||||
'polygon(74.1% 56.1%, 100% 38.6%, 97.5% 73.3%, 85.5% 100%, 80.7% 98.2%, 72.5% 67.7%, 60.2% 37.8%, 52.4% 32.2%, 47.5% 41.9%, 45.2% 65.8%, 27.5% 23.5%, 0.1% 35.4%, 17.9% 0.1%, 27.6% 23.5%, 76.1% 2.6%, 74.1% 56.1%)',
|
||||
}}
|
||||
className="aspect-1155/678 w-288.75 bg-linear-to-br from-brand-300 to-brand-600 opacity-10 dark:opacity-20"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<dl className="mt-10 space-y-4 text-base/7 text-brand-700 dark:text-brand-100">
|
||||
<div className="flex gap-x-4">
|
||||
<dt className="flex-none">
|
||||
<span className="sr-only">{addressLabel}</span>
|
||||
<BuildingOffice2Icon aria-hidden="true" className="h-7 w-6 text-brand-400" />
|
||||
</dt>
|
||||
<dd>
|
||||
{addressValue}
|
||||
<br />
|
||||
<span className="text-sm text-brand-500">{serviceAreaLabel} {serviceArea}</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex gap-x-4">
|
||||
<dt className="flex-none">
|
||||
<span className="sr-only">{phoneLabel}</span>
|
||||
<PhoneIcon aria-hidden="true" className="h-7 w-6 text-brand-400" />
|
||||
</dt>
|
||||
<dd>
|
||||
<a href={`tel:${phoneValue.replace(/\D/g, '')}`} className="hover:text-brand-dark dark:hover:text-white">
|
||||
{phoneValue}
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex gap-x-4">
|
||||
<dt className="flex-none">
|
||||
<span className="sr-only">{emailLabel}</span>
|
||||
<EnvelopeIcon aria-hidden="true" className="h-7 w-6 text-brand-400" />
|
||||
</dt>
|
||||
<dd>
|
||||
<a href={`mailto:${emailValue}`} className="hover:text-brand-dark dark:hover:text-white">
|
||||
{emailValue}
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<form action="#" method="POST" className="px-6 pt-12 pb-20">
|
||||
<div className="mx-auto max-w-xl lg:mr-0 lg:max-w-lg">
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<label htmlFor="first-name" className={labelClass}>{firstNameLabel}</label>
|
||||
<div className="mt-2.5">
|
||||
<input id="first-name" name="first-name" type="text" autoComplete="given-name" className={inputClass} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="last-name" className={labelClass}>{lastNameLabel}</label>
|
||||
<div className="mt-2.5">
|
||||
<input id="last-name" name="last-name" type="text" autoComplete="family-name" className={inputClass} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="email" className={labelClass}>{emailFieldLabel}</label>
|
||||
<div className="mt-2.5">
|
||||
<input id="email" name="email" type="email" autoComplete="email" className={inputClass} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="phone-number" className={labelClass}>{phoneFieldLabel}</label>
|
||||
<div className="mt-2.5">
|
||||
<input id="phone-number" name="phone-number" type="tel" autoComplete="tel" className={inputClass} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="message" className={labelClass}>{messageLabel}</label>
|
||||
<div className="mt-2.5">
|
||||
<textarea id="message" name="message" rows={4} className={inputClass} defaultValue={''} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="rounded-md bg-brand-600 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-xs hover:bg-brand-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-600 dark:bg-brand-500 dark:hover:bg-brand-400 dark:focus-visible:outline-brand-500"
|
||||
>
|
||||
{submitLabel}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import photo3 from "@/assets/photos/silver-and-white-apartment-interior-2023-11-
|
||||
import photo4 from "@/assets/photos/a-bedroom-with-white-walls-and-a-bed-and-a-2023-11-27-05-25-30-utc.webp";
|
||||
import photo5 from "@/assets/photos/empty-room-with-window-2023-11-27-05-08-12-utc.webp";
|
||||
import logo from "@/assets/cachet-hero-logo.svg";
|
||||
|
||||
export type HeroContent = {
|
||||
pillCta: string;
|
||||
title: string;
|
||||
title2: string;
|
||||
subtitle: string;
|
||||
@@ -35,7 +35,7 @@ const autoplayPlugin = Autoplay({
|
||||
});
|
||||
|
||||
export default function Hero({ content }: { content: HeroContent }) {
|
||||
const { pillCta, title, title2, subtitle, cta, ctaHref } = content;
|
||||
const { subtitle, cta, ctaHref } = content;
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-900">
|
||||
@@ -61,7 +61,7 @@ export default function Hero({ content }: { content: HeroContent }) {
|
||||
|
||||
<div className="relative z-10 mx-auto max-w-7xl px-6 lg:px-8 h-full flex items-center justify-center pt-14">
|
||||
<div className="mx-auto max-w-4xl text-center">
|
||||
<div className="hidden sm:mb-8 sm:flex sm:justify-center">
|
||||
{/* <div className="hidden sm:mb-8 sm:flex sm:justify-center">
|
||||
<div className="flex flex-wrap items-center justify-center gap-2 gap-x-3.5 rounded-full px-4 py-2 text-sm/6 text-gray-300 ring-1 ring-white/20">
|
||||
<span>RBQ {general.rbq}</span>
|
||||
<span className="text-white/30" aria-hidden>
|
||||
@@ -73,7 +73,7 @@ export default function Hero({ content }: { content: HeroContent }) {
|
||||
</span>
|
||||
<span>{pillCta}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* <h1
|
||||
className="text-5xl font-semibold tracking-tight text-balance text-white sm:text-7xl"
|
||||
style={{ fontFamily: "var(--font-display)" }}
|
||||
|
||||
44
src/components/ui/short-hero.tsx
Normal file
44
src/components/ui/short-hero.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
interface ContactHeroContent {
|
||||
imageSrc: string;
|
||||
imageAlt: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
|
||||
export default function ContactHero({ content }: { content: ContactHeroContent }) {
|
||||
const { imageSrc, imageAlt, title, subtitle } = content;
|
||||
return (
|
||||
<div className="relative flex items-center justify-center overflow-hidden bg-brand-600 min-h-[50vh]">
|
||||
{/* Decorative background image and gradient */}
|
||||
<div aria-hidden="true" className="absolute inset-0">
|
||||
<div className="absolute inset-0 mx-auto overflow-hidden">
|
||||
<img
|
||||
alt={imageAlt}
|
||||
src="/images/empty-room-blue-wall-with-moulding-and-parquet-fl-2023-11-27-04-52-31-utc.webp"
|
||||
className="size-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute inset-0" />
|
||||
<div className="absolute inset-0 bg-linear-to-t from-white via-white opacity-40" />
|
||||
</div>
|
||||
|
||||
{/* Callout */}
|
||||
<section
|
||||
aria-labelledby="sale-heading"
|
||||
className="relative mx-auto flex max-w-7xl flex-col items-center justify-center text-center sm:px-6 lg:px-8"
|
||||
>
|
||||
<div className="mx-auto max-w-2xl lg:max-w-none">
|
||||
<h2 id="sale-heading" className="text-4xl font-bold tracking-tight text-white sm:text-5xl lg:text-6xl">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-xl text-xl text-white">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,46 +1,29 @@
|
||||
const featuredTestimonial = {
|
||||
body: 'Integer id nunc sit semper purus. Bibendum at lacus ut arcu blandit montes vitae auctor libero. Hac condimentum dignissim nibh vulputate ut nunc. Amet nibh orci mi venenatis blandit vel et proin. Non hendrerit in vel ac diam.',
|
||||
author: {
|
||||
name: 'Brenna Goyette',
|
||||
handle: 'brennagoyette',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=1024&h=1024&q=80',
|
||||
logoUrl: 'https://tailwindcss.com/plus-assets/img/logos/savvycal-logo-gray-900.svg',
|
||||
},
|
||||
}
|
||||
const testimonials = [
|
||||
[
|
||||
[
|
||||
{
|
||||
iframe: <iframe className="w-full h-full rounded-2xl " src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fjean.christophe.ali.2025%2Fposts%2Fpfbid02qsqq2Rn4vRx1gCG3q5e5M2ByPXH6zMux59fWvotfsQxpuWyUh46TZuwCND9iKpwnl&show_text=true&width=500" width="500" height="194" style={{ border: 'none', overflow: 'hidden' }} scrolling="no" frameBorder={0} allowFullScreen allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe>,
|
||||
key: 'jean-christophe-ali',
|
||||
}
|
||||
],
|
||||
[{
|
||||
iframe: <iframe className="w-full h-full rounded-2xl " src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fcharlotte.lafrance.CL%2Fposts%2Fpfbid026Jw7aer2r84q27kgcZ1PKg8q2sqKgZc6nJspP42CTEAU6ac8FF4Ptj8ap8XGh6Wl&show_text=true&width=500" width="500" height="166" style={{ border: 'none', overflow: 'hidden' }} scrolling="no" frameBorder={0} allowFullScreen allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe>,
|
||||
key: 'charlotte-lafrance',
|
||||
}
|
||||
],
|
||||
],
|
||||
[
|
||||
[{
|
||||
iframe: <iframe className="w-full h-full rounded-2xl " src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fcolin.griffin.165%2Fposts%2Fpfbid0iCA6hoF5X32gUJSBcDUiXP2hxhxzMmEiC5Att3CDDwFckrHa7yxyGiLBmXQhQBUFl&show_text=true&width=500" width="500" height="222" style={{ border: 'none', overflow: 'hidden' }} scrolling="no" frameBorder={0} allowFullScreen allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe> ,
|
||||
key: 'colin-griffin',
|
||||
}
|
||||
],
|
||||
[ {
|
||||
iframe: <iframe className="w-full h-full rounded-2xl" src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fhollowman.la.rock%2Fposts%2Fpfbid0hPR3RpaSLZtZkgpaEzKsNKAF4hUeKbDzkicAZ9jsbCpXKRRsYD63C32rw78uvRoml&show_text=true&width=500" width="500" height="170" style={{ border: 'none', overflow: 'hidden' }} scrolling="no" frameBorder={0} allowFullScreen allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe>,
|
||||
key: 'hollowman-la-rock',
|
||||
}
|
||||
],
|
||||
],
|
||||
]
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
interface TestimonialItem {
|
||||
key: string;
|
||||
src: string;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export default function Grid() {
|
||||
interface Props {
|
||||
sectionLabel: string;
|
||||
title: string;
|
||||
items: TestimonialItem[];
|
||||
}
|
||||
|
||||
const iframeProps = {
|
||||
width: 500,
|
||||
scrolling: 'no',
|
||||
frameBorder: 0,
|
||||
allowFullScreen: true,
|
||||
allow: 'autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share',
|
||||
style: { border: 'none', overflow: 'hidden' } as React.CSSProperties,
|
||||
className: 'w-full rounded-2xl',
|
||||
}
|
||||
|
||||
export default function Grid({ sectionLabel, title, items }: Props) {
|
||||
const [featured, ...rest] = items
|
||||
const groups = [rest.slice(0, 2), rest.slice(2)]
|
||||
|
||||
return (
|
||||
<div className="relative isolate bg-brand-50 pt-24 pb-32 sm:pt-32 dark:bg-brand-50">
|
||||
<div
|
||||
@@ -69,52 +52,31 @@ export default function Grid() {
|
||||
</div>
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<h2 className="text-base/7 font-semibold text-brand-400 dark:text-brand-400">Témoignages</h2>
|
||||
<h2 className="text-base/7 font-semibold text-brand-400 dark:text-brand-400">{sectionLabel}</h2>
|
||||
<p className="mt-2 text-4xl font-semibold tracking-tight text-balance text-brand-600 sm:text-5xl dark:text-white">
|
||||
Ce que nos clients disent de nous
|
||||
{title}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mx-auto mt-16 grid max-w-2xl grid-cols-1 grid-rows-1 gap-8 text-sm/6 text-gray-900 sm:mt-20 sm:grid-cols-2 xl:mx-0 xl:max-w-none xl:grid-flow-col xl:grid-cols-4 dark:text-gray-100">
|
||||
<figure className="rounded-2xl bg-white shadow-lg ring-1 ring-gray-900/5 sm:col-span-2 xl:col-start-2 xl:row-end-1 dark:bg-gray-800/75 dark:shadow-none dark:ring-white/10">
|
||||
<iframe className="w-full h-full rounded-2xl " src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fsylvie.gauthier.73594%2Fposts%2Fpfbid0DHM1833TLKru4cbkTnZq3KLNHrSR8B8rQAgGosGzAbpGMccvLffoNmCLgEo8GUNPl&show_text=true&width=500" width="500" height="208" style={{ border: 'none', overflow: 'hidden' }} scrolling="no" frameBorder={0} allowFullScreen allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe>
|
||||
<iframe {...iframeProps} src={featured.src} height={featured.height} />
|
||||
</figure>
|
||||
{testimonials.map((columnGroup, columnGroupIdx) => (
|
||||
<div key={columnGroupIdx} className="space-y-8 xl:contents xl:space-y-0">
|
||||
{columnGroup.map((column, columnIdx) => (
|
||||
<div
|
||||
key={columnIdx}
|
||||
className={classNames(
|
||||
(columnGroupIdx === 0 && columnIdx === 0) ||
|
||||
(columnGroupIdx === testimonials.length - 1 && columnIdx === columnGroup.length - 1)
|
||||
? 'xl:row-span-2'
|
||||
: 'xl:row-start-1',
|
||||
'space-y-8',
|
||||
)}
|
||||
>
|
||||
{column.map((testimonial) => (
|
||||
<figure
|
||||
key={testimonial.key}
|
||||
className="rounded-2xl bg-white shadow-lg ring-1 ring-gray-900/5 dark:bg-gray-800/75 dark:shadow-none dark:ring-white/10"
|
||||
>
|
||||
{testimonial.iframe}
|
||||
{/* <blockquote className="text-gray-900 dark:text-white">
|
||||
<p>{`“${testimonial.body}”`}</p>
|
||||
</blockquote>
|
||||
<figcaption className="mt-6 flex items-center gap-x-4">
|
||||
<img
|
||||
alt=""
|
||||
src={testimonial.author.imageUrl}
|
||||
className="size-10 rounded-full bg-gray-50 dark:bg-gray-700"
|
||||
/>
|
||||
<div>
|
||||
<div className="font-semibold text-gray-900 dark:text-white">{testimonial.author.name}</div>
|
||||
<div className="text-gray-600 dark:text-gray-400">{`@${testimonial.author.handle}`}</div>
|
||||
</div>
|
||||
</figcaption> */}
|
||||
{groups.map((group, groupIdx) => (
|
||||
<div key={groupIdx} className="space-y-8 xl:contents xl:space-y-0">
|
||||
{group.map((t, colIdx) => {
|
||||
const isFirstCol = groupIdx === 0 && colIdx === 0
|
||||
const isLastCol = groupIdx === groups.length - 1 && colIdx === group.length - 1
|
||||
return (
|
||||
<div
|
||||
key={t.key}
|
||||
className={(isFirstCol || isLastCol) ? 'xl:row-span-2 space-y-8' : 'xl:row-start-1 space-y-8'}
|
||||
>
|
||||
<figure className="rounded-2xl bg-white shadow-lg ring-1 ring-gray-900/5 dark:bg-gray-800/75 dark:shadow-none dark:ring-white/10">
|
||||
<iframe {...iframeProps} src={t.src} height={t.height} />
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user