Compare commits
8 Commits
version_1_
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d9b42af4a | |||
|
|
7f89e181ad | ||
| 28de81dcec | |||
|
|
1ddb677ab1 | ||
|
|
47ef186991 | ||
| b5cb235306 | |||
|
|
e367a99a38 | ||
| 1f37230e95 |
@@ -2,11 +2,13 @@ import { Routes, Route } from 'react-router-dom';
|
||||
import Layout from './components/Layout';
|
||||
import HomePage from './pages/HomePage';
|
||||
|
||||
import InventoryPage from "@/pages/InventoryPage";
|
||||
export default function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/inventory" element={<InventoryPage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@@ -27,7 +27,9 @@ export default function Layout() {
|
||||
},
|
||||
{
|
||||
"name": "Contact", "href": "#contact"
|
||||
}
|
||||
},
|
||||
{ name: "Inventory", href: "/inventory" },
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,210 +1,33 @@
|
||||
import AboutTestimonial from '@/components/sections/about/AboutTestimonial';
|
||||
import ContactCta from '@/components/sections/contact/ContactCta';
|
||||
import FaqSimple from '@/components/sections/faq/FaqSimple';
|
||||
import FeaturesRevealCardsBento from '@/components/sections/features/FeaturesRevealCardsBento';
|
||||
import HeroCenteredLogos from '@/components/sections/hero/HeroCenteredLogos';
|
||||
import MetricsSimpleCards from '@/components/sections/metrics/MetricsSimpleCards';
|
||||
import TestimonialTrustCard from '@/components/sections/testimonial/TestimonialTrustCard';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
// AUTO-GENERATED shell by per-section-migrate.
|
||||
// Section bodies live in ./<PageBase>/sections/<X>.tsx. Edit the section
|
||||
// files directly. Non-block content (wrappers, non-inlinable sections) is
|
||||
// preserved inline; extracted section blocks become <XSection/> refs.
|
||||
|
||||
export default function HomePage() {
|
||||
import React from 'react';
|
||||
import HomeSection from './HomePage/sections/Home';
|
||||
import AboutSection from './HomePage/sections/About';
|
||||
import ShopSection from './HomePage/sections/Shop';
|
||||
import MetricsSection from './HomePage/sections/Metrics';
|
||||
import TestimonialSection from './HomePage/sections/Testimonial';
|
||||
import FaqSection from './HomePage/sections/Faq';
|
||||
import ContactSection from './HomePage/sections/Contact';
|
||||
|
||||
export default function HomePage(): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div id="home" data-section="home">
|
||||
<SectionErrorBoundary name="home">
|
||||
<HeroCenteredLogos
|
||||
avatarsSrc={[
|
||||
"http://img.b2bpic.net/free-photo/young-caucasian-model-with-long-dark-hair-poses-camera-with-hand-near-her-face_132075-10061.jpg",
|
||||
"http://img.b2bpic.net/free-photo/portrait-blonde-woman-with-glass-water-standing-yellow_114579-81442.jpg",
|
||||
"http://img.b2bpic.net/free-photo/close-up-woman-holding-globe-fir-tree_23-2148332703.jpg",
|
||||
"http://img.b2bpic.net/free-photo/young-woman-holding-pinecone-with-star-red-wall_114579-55960.jpg",
|
||||
]}
|
||||
avatarText="Loved by 10,000+ fragrance enthusiasts"
|
||||
title="Experience Luxury, Simplified."
|
||||
description="Authentic fragrance decants from world-class perfume houses delivered to your door."
|
||||
primaryButton={{
|
||||
text: "Browse Collection",
|
||||
href: "#shop",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "Learn More",
|
||||
href: "#about",
|
||||
}}
|
||||
names={[
|
||||
"JPG",
|
||||
"Dior",
|
||||
"YSL",
|
||||
"Creed",
|
||||
"Tom Ford",
|
||||
"Armani",
|
||||
]}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/luxury-black-perfume-bottle-with-gold-cap-dark-textured-background_84443-84117.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<>
|
||||
<HomeSection />
|
||||
|
||||
<div id="about" data-section="about">
|
||||
<SectionErrorBoundary name="about">
|
||||
<AboutTestimonial
|
||||
tag="Our Story"
|
||||
quote="We believe everyone should have access to the finest scents in the world, without the commitment of a full bottle. Our decants ensure you find your perfect fragrance signature."
|
||||
author="The FragranceLab Team"
|
||||
role="Curators of Fine Scent"
|
||||
imageSrc="http://img.b2bpic.net/free-photo/laboratory-glassware-assortment-with-red-liquids_23-2149481715.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<AboutSection />
|
||||
|
||||
<div id="shop" data-section="shop">
|
||||
<SectionErrorBoundary name="shop">
|
||||
<FeaturesRevealCardsBento
|
||||
tag="Fragrance Catalog"
|
||||
title="Exquisite Fragrance Collection"
|
||||
description="Select from our curated list of elite scents available in 5ml decants or full bottles."
|
||||
items={[
|
||||
{
|
||||
title: "JPG Le Male Elixir",
|
||||
description: "Intense, honeyed tobacco and vanilla.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/luxury-perfume-fragrance-bottle_116380-80.jpg",
|
||||
},
|
||||
{
|
||||
title: "JPG Ultra Male",
|
||||
description: "The ultimate clubbing fragrance.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/top-view-male-self-care-product_23-2150347093.jpg",
|
||||
},
|
||||
{
|
||||
title: "JPG Le Male Le Parfum",
|
||||
description: "Elegant lavender meets cardamom.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/elegant-vegan-alcohol-arrangement_23-2149337695.jpg",
|
||||
},
|
||||
{
|
||||
title: "Dior Sauvage",
|
||||
description: "Timeless, fresh, and masculine.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/perfume-bottle-natural-background-tree-bark-flowers-stones-top-view-beauty-fashion-perfume-template_166373-1585.jpg",
|
||||
},
|
||||
{
|
||||
title: "Azzaro The Most Wanted",
|
||||
description: "Spicy, sweet, and charismatic.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/top-view-male-self-care-items_23-2150347141.jpg",
|
||||
},
|
||||
{
|
||||
title: "YSL Y EDP",
|
||||
description: "Crisp apple and sage sophistication.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/bottle-serum-based-soil-ingredients_23-2150756823.jpg",
|
||||
},
|
||||
{
|
||||
title: "Creed Aventus",
|
||||
description: "The king of modern niche scents.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/single-glass-bottle-filled-with-clear-liquid-generated-by-ai_188544-19684.jpg",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<ShopSection />
|
||||
|
||||
<div id="metrics" data-section="metrics">
|
||||
<SectionErrorBoundary name="metrics">
|
||||
<MetricsSimpleCards
|
||||
tag="Quality Assured"
|
||||
title="The Decant Difference"
|
||||
description="Why fragrance lovers trust us with their collection building."
|
||||
metrics={[
|
||||
{
|
||||
value: "100%",
|
||||
description: "Authentic Fragrances",
|
||||
},
|
||||
{
|
||||
value: "24h",
|
||||
description: "Swift Processing",
|
||||
},
|
||||
{
|
||||
value: "5ml+",
|
||||
description: "Flexible Sizes",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<MetricsSection />
|
||||
|
||||
<div id="testimonial" data-section="testimonial">
|
||||
<SectionErrorBoundary name="testimonial">
|
||||
<TestimonialTrustCard
|
||||
quote="FragranceLab changed the way I sample. I found my signature scent through their 5ml decants. Highly recommend for any fragrance enthusiast!"
|
||||
rating={5}
|
||||
author="Marcus R., Verified Buyer"
|
||||
avatars={[
|
||||
{
|
||||
name: "Sarah",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/medium-shot-young-woman-with-curly-hair_23-2151317375.jpg",
|
||||
},
|
||||
{
|
||||
name: "John",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/medium-shot-woman-sitting-table_23-2149708133.jpg",
|
||||
},
|
||||
{
|
||||
name: "Emily",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/cheerful-young-caucasian-woman-applies-cream-her-hands-while-sitting-table-light-room-brunette-girl-with-smooth-skin-wears-shirt-wellness-self-care-concept_197531-32266.jpg",
|
||||
},
|
||||
{
|
||||
name: "David",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/person-enjoying-berry-snack-outdoors_52683-107522.jpg",
|
||||
},
|
||||
{
|
||||
name: "Anna",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/attractive-woman-posing-with-dry-leaf-blue-background_197531-28551.jpg",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<TestimonialSection />
|
||||
|
||||
<div id="faq" data-section="faq">
|
||||
<SectionErrorBoundary name="faq">
|
||||
<FaqSimple
|
||||
tag="Support"
|
||||
title="Common Questions"
|
||||
description="Everything you need to know about our decant process and shipping."
|
||||
items={[
|
||||
{
|
||||
question: "Are these authentic perfumes?",
|
||||
answer: "Yes, all our decants are 100% original fragrances sourced from authorized retailers.",
|
||||
},
|
||||
{
|
||||
question: "How do you package the decants?",
|
||||
answer: "We use high-quality glass atomizers with Teflon sealing to prevent evaporation.",
|
||||
},
|
||||
{
|
||||
question: "Do you offer international shipping?",
|
||||
answer: "Yes, we ship globally using secure packaging.",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<FaqSection />
|
||||
|
||||
<div id="contact" data-section="contact">
|
||||
<SectionErrorBoundary name="contact">
|
||||
<ContactCta
|
||||
tag="Ready to Sample?"
|
||||
text="Start your journey today. Browse our full list of fragrances and pick your samples."
|
||||
primaryButton={{
|
||||
text: "Shop Now",
|
||||
href: "#shop",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "Contact Support",
|
||||
href: "mailto:support@fragrancelab.com",
|
||||
}}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<ContactSection />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
24
src/pages/HomePage/sections/About.tsx
Normal file
24
src/pages/HomePage/sections/About.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "about" section.
|
||||
|
||||
import React from 'react';
|
||||
import AboutTestimonial from '@/components/sections/about/AboutTestimonial';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function AboutSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="about" data-section="about">
|
||||
<div className="hidden">
|
||||
<SectionErrorBoundary name="about">
|
||||
<AboutTestimonial
|
||||
tag="Our Story"
|
||||
quote="We believe everyone should have access to the finest scents in the world, without the commitment of a full bottle. Our decants ensure you find your perfect fragrance signature."
|
||||
author="The FragranceLab Team"
|
||||
role="Curators of Fine Scent"
|
||||
imageSrc="http://img.b2bpic.net/free-photo/laboratory-glassware-assortment-with-red-liquids_23-2149481715.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
27
src/pages/HomePage/sections/Contact.tsx
Normal file
27
src/pages/HomePage/sections/Contact.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "contact" section.
|
||||
|
||||
import React from 'react';
|
||||
import ContactCta from '@/components/sections/contact/ContactCta';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function ContactSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="contact" data-section="contact">
|
||||
<SectionErrorBoundary name="contact">
|
||||
<ContactCta
|
||||
tag="Ready to Sample?"
|
||||
text="Start your journey today. Browse our full list of fragrances and pick your samples."
|
||||
primaryButton={{
|
||||
text: "Shop Now",
|
||||
href: "#shop",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "Contact Support",
|
||||
href: "mailto:support@fragrancelab.com",
|
||||
}}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
34
src/pages/HomePage/sections/Faq.tsx
Normal file
34
src/pages/HomePage/sections/Faq.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "faq" section.
|
||||
|
||||
import React from 'react';
|
||||
import FaqSimple from '@/components/sections/faq/FaqSimple';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function FaqSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="faq" data-section="faq">
|
||||
<SectionErrorBoundary name="faq">
|
||||
<FaqSimple
|
||||
tag="Support"
|
||||
title="Common Questions"
|
||||
description="Everything you need to know about our decant process and shipping."
|
||||
items={[
|
||||
{
|
||||
question: "Are these authentic perfumes?",
|
||||
answer: "Yes, all our decants are 100% original fragrances sourced from authorized retailers.",
|
||||
},
|
||||
{
|
||||
question: "How do you package the decants?",
|
||||
answer: "We use high-quality glass atomizers with Teflon sealing to prevent evaporation.",
|
||||
},
|
||||
{
|
||||
question: "Do you offer international shipping?",
|
||||
answer: "Yes, we ship globally using secure packaging.",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
src/pages/HomePage/sections/Home.tsx
Normal file
43
src/pages/HomePage/sections/Home.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "home" section.
|
||||
|
||||
import React from 'react';
|
||||
import HeroCenteredLogos from '@/components/sections/hero/HeroCenteredLogos';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function HomeSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="home" data-section="home">
|
||||
<SectionErrorBoundary name="home">
|
||||
<HeroCenteredLogos
|
||||
avatarsSrc={[
|
||||
"http://img.b2bpic.net/free-photo/young-caucasian-model-with-long-dark-hair-poses-camera-with-hand-near-her-face_132075-10061.jpg",
|
||||
"http://img.b2bpic.net/free-photo/portrait-blonde-woman-with-glass-water-standing-yellow_114579-81442.jpg",
|
||||
"http://img.b2bpic.net/free-photo/close-up-woman-holding-globe-fir-tree_23-2148332703.jpg",
|
||||
"http://img.b2bpic.net/free-photo/young-woman-holding-pinecone-with-star-red-wall_114579-55960.jpg",
|
||||
]}
|
||||
avatarText="Loved by 10,000+ fragrance enthusiasts"
|
||||
title="Experience Luxury, Simplified."
|
||||
description="Authentic fragrance decants from world-class perfume houses delivered to your door."
|
||||
primaryButton={{
|
||||
text: "Browse Collection",
|
||||
href: "#shop",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "Learn More",
|
||||
href: "#about",
|
||||
}}
|
||||
names={[
|
||||
"JPG",
|
||||
"Dior",
|
||||
"YSL",
|
||||
"Creed",
|
||||
"Tom Ford",
|
||||
"Armani",
|
||||
]}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/luxury-black-perfume-bottle-with-gold-cap-dark-textured-background_84443-84117.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
34
src/pages/HomePage/sections/Metrics.tsx
Normal file
34
src/pages/HomePage/sections/Metrics.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "metrics" section.
|
||||
|
||||
import React from 'react';
|
||||
import MetricsSimpleCards from '@/components/sections/metrics/MetricsSimpleCards';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function MetricsSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="metrics" data-section="metrics">
|
||||
<SectionErrorBoundary name="metrics">
|
||||
<MetricsSimpleCards
|
||||
tag="Quality Assured"
|
||||
title="The Decant Difference"
|
||||
description="Why fragrance lovers trust us with their collection building."
|
||||
metrics={[
|
||||
{
|
||||
value: "100%",
|
||||
description: "Authentic Fragrances",
|
||||
},
|
||||
{
|
||||
value: "24h",
|
||||
description: "Swift Processing",
|
||||
},
|
||||
{
|
||||
value: "5ml+",
|
||||
description: "Flexible Sizes",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
196
src/pages/HomePage/sections/Shop.tsx
Normal file
196
src/pages/HomePage/sections/Shop.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import ScrollReveal from "@/components/ui/ScrollReveal";
|
||||
import SelectorButton from "@/components/ui/SelectorButton";
|
||||
import Modal from "@/components/ui/Modal";
|
||||
import Input from "@/components/ui/Input";
|
||||
import Textarea from "@/components/ui/Textarea";
|
||||
import Label from "@/components/ui/Label";
|
||||
import { useState } from "react";
|
||||
import { cls } from "@/lib/utils";
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: "JPG Le Male Elixir",
|
||||
description: "Intense, honeyed tobacco and vanilla.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/luxury-perfume-fragrance-bottle_116380-80.jpg"
|
||||
},
|
||||
{
|
||||
title: "JPG Ultra Male",
|
||||
description: "The ultimate clubbing fragrance.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/top-view-male-self-care-product_23-2150347093.jpg"
|
||||
},
|
||||
{
|
||||
title: "JPG Le Male Le Parfum",
|
||||
description: "Elegant lavender meets cardamom.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/elegant-vegan-alcohol-arrangement_23-2149337695.jpg"
|
||||
},
|
||||
{
|
||||
title: "Dior Sauvage",
|
||||
description: "Timeless, fresh, and masculine.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/perfume-bottle-natural-background-tree-bark-flowers-stones-top-view-beauty-fashion-perfume-template_166373-1585.jpg"
|
||||
},
|
||||
{
|
||||
title: "Azzaro The Most Wanted",
|
||||
description: "Spicy, sweet, and charismatic.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/top-view-male-self-care-items_23-2150347141.jpg"
|
||||
},
|
||||
{
|
||||
title: "YSL Y EDP",
|
||||
description: "Crisp apple and sage sophistication.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/bottle-serum-based-soil-ingredients_23-2150756823.jpg"
|
||||
},
|
||||
{
|
||||
title: "Creed Aventus",
|
||||
description: "The king of modern niche scents.",
|
||||
href: "#",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/single-glass-bottle-filled-with-clear-liquid-generated-by-ai_188544-19684.jpg"
|
||||
}
|
||||
];
|
||||
|
||||
type FeatureItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
href: string;
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
interface FeaturesRevealCardsBentoProps {
|
||||
tag: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryButton?: { text: string; href: string };
|
||||
secondaryButton?: { text: string; href: string };
|
||||
items: [FeatureItem, FeatureItem, FeatureItem, FeatureItem, FeatureItem, FeatureItem, FeatureItem];
|
||||
}
|
||||
|
||||
const ProductCard = ({ item, index, staggerDelays, gridClasses }: any) => {
|
||||
const [volume, setVolume] = useState("5ml");
|
||||
const volumeOptions = [
|
||||
{ value: "5ml", label: "5ml" },
|
||||
{ value: "10ml", label: "10ml" },
|
||||
{ value: "30ml", label: "30ml" },
|
||||
{ value: "full", label: "Full" },
|
||||
];
|
||||
|
||||
return (
|
||||
<ScrollReveal
|
||||
variant="slide-up"
|
||||
delay={staggerDelays[index]}
|
||||
className={cls(
|
||||
"card rounded-lg overflow-hidden flex flex-col group relative",
|
||||
gridClasses[index]
|
||||
)}
|
||||
>
|
||||
<div className="relative w-full aspect-video md:aspect-auto md:h-64 overflow-hidden">
|
||||
<ImageOrVideo
|
||||
imageSrc={item.imageSrc}
|
||||
videoSrc={item.videoSrc}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-6 flex flex-col flex-grow gap-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-accent text-sm">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-auto flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-xs font-medium text-foreground">Select Volume</Label>
|
||||
<SelectorButton
|
||||
options={volumeOptions}
|
||||
activeValue={volume}
|
||||
onValueChange={setVolume}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<Button text={`Add to Cart`} variant="primary" className="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
);
|
||||
};
|
||||
|
||||
const ShopInline = () => {
|
||||
const gridClasses = [
|
||||
"md:col-span-2",
|
||||
"md:col-span-4",
|
||||
"md:col-span-3",
|
||||
"md:col-span-3",
|
||||
"md:col-span-2",
|
||||
"md:col-span-2",
|
||||
"md:col-span-2",
|
||||
];
|
||||
|
||||
const staggerDelays = [
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
0.1,
|
||||
0.2,
|
||||
];
|
||||
|
||||
return (
|
||||
<section aria-label="Features reveal cards bento section" className="py-20">
|
||||
<div className="flex flex-col gap-8 md:gap-10">
|
||||
<div className="flex flex-col items-center w-content-width mx-auto gap-2">
|
||||
<div className="px-3 py-1 mb-1 text-sm card rounded w-fit">
|
||||
<p>{"Fragrance Catalog"}</p>
|
||||
</div>
|
||||
|
||||
<TextAnimation
|
||||
text={"Exquisite Fragrance Collection"}
|
||||
variant="fade-blur"
|
||||
gradientText={true}
|
||||
tag="h2"
|
||||
className="md:max-w-8/10 text-6xl 2xl:text-7xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"Select from our curated list of elite scents available in 5ml decants or full bottles."}
|
||||
variant="fade-blur"
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-center text-balance"
|
||||
/>
|
||||
|
||||
{(undefined || undefined) && (
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="primary"/>}
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="secondary" animationDelay={0.1} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-content-width mx-auto grid grid-cols-1 md:grid-cols-6 gap-3">
|
||||
{items.map((item, index) => (
|
||||
<ProductCard key={item.title} item={item} index={index} staggerDelays={staggerDelays} gridClasses={gridClasses} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default function ShopSection() {
|
||||
return (
|
||||
<div data-webild-section="shop" id="shop">
|
||||
<ShopInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
44
src/pages/HomePage/sections/Testimonial.tsx
Normal file
44
src/pages/HomePage/sections/Testimonial.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "testimonial" section.
|
||||
|
||||
import React from 'react';
|
||||
import TestimonialTrustCard from '@/components/sections/testimonial/TestimonialTrustCard';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function TestimonialSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="testimonial" data-section="testimonial">
|
||||
<div className="hidden">
|
||||
<SectionErrorBoundary name="testimonial">
|
||||
<TestimonialTrustCard
|
||||
quote="FragranceLab changed the way I sample. I found my signature scent through their 5ml decants. Highly recommend for any fragrance enthusiast!"
|
||||
rating={5}
|
||||
author="Marcus R., Verified Buyer"
|
||||
avatars={[
|
||||
{
|
||||
name: "Sarah",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/medium-shot-young-woman-with-curly-hair_23-2151317375.jpg",
|
||||
},
|
||||
{
|
||||
name: "John",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/medium-shot-woman-sitting-table_23-2149708133.jpg",
|
||||
},
|
||||
{
|
||||
name: "Emily",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/cheerful-young-caucasian-woman-applies-cream-her-hands-while-sitting-table-light-room-brunette-girl-with-smooth-skin-wears-shirt-wellness-self-care-concept_197531-32266.jpg",
|
||||
},
|
||||
{
|
||||
name: "David",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/person-enjoying-berry-snack-outdoors_52683-107522.jpg",
|
||||
},
|
||||
{
|
||||
name: "Anna",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/attractive-woman-posing-with-dry-leaf-blue-background_197531-28551.jpg",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
src/pages/InventoryPage.tsx
Normal file
15
src/pages/InventoryPage.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ArrowUpRight, Loader2 } from "lucide-react";
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import GridOrCarousel from "@/components/ui/GridOrCarousel";
|
||||
import ScrollReveal from "@/components/ui/ScrollReveal";
|
||||
import useProducts from "@/hooks/useProducts";
|
||||
|
||||
export default function InventoryPage() {
|
||||
return (
|
||||
<>
|
||||
<div data-webild-section="ProductVariantCards"><section aria-label="Products section" className="py-20"><div className="w-content-width mx-auto flex justify-center"><Loader2 className="size-8 animate-spin text-foreground" strokeWidth={1.5} /></div></section></div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -6,4 +6,5 @@ export interface Route {
|
||||
|
||||
export const routes: Route[] = [
|
||||
{ path: '/', label: 'Home', pageFile: 'HomePage' },
|
||||
{ path: '/inventory', label: 'Inventory', pageFile: 'InventoryPage' },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user