Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cea5ff0e7 | |||
| 87aa54d786 | |||
| d6a6712456 | |||
| bce176f26f | |||
| 275afe7b48 | |||
| b3d4e9c603 | |||
| 1aaf017077 | |||
| 6277f3754b | |||
| 7069e73c83 | |||
| c87f5afa7c | |||
| 31c6c49d87 | |||
| 091b625404 |
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 25 KiB |
109
src/app/page.tsx
109
src/app/page.tsx
@@ -9,30 +9,9 @@ import MetricCardOne from "@/components/sections/metrics/MetricCardOne";
|
||||
import FaqDouble from "@/components/sections/faq/FaqDouble";
|
||||
import ContactFaq from "@/components/sections/contact/ContactFaq";
|
||||
import FooterBaseReveal from "@/components/sections/footer/FooterBaseReveal";
|
||||
import VirtualTryOnUpload from "@/components/virtualTryOn/VirtualTryOnUpload";
|
||||
import VirtualTryOnPreview from "@/components/virtualTryOn/VirtualTryOnPreview";
|
||||
import { Clock, Package, Sparkles, Target, Zap } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function LandingPage() {
|
||||
const [userPhoto, setUserPhoto] = useState<string | null>(null);
|
||||
const [clothesPhoto, setClothesPhoto] = useState<string | null>(null);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
|
||||
const handleUserPhotoUpload = (photo: string) => {
|
||||
setUserPhoto(photo);
|
||||
};
|
||||
|
||||
const handleClothesPhotoUpload = (photo: string) => {
|
||||
setClothesPhoto(photo);
|
||||
};
|
||||
|
||||
const handleStartTryOn = () => {
|
||||
if (userPhoto && clothesPhoto) {
|
||||
setShowPreview(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="directional-hover"
|
||||
@@ -63,18 +42,21 @@ export default function LandingPage() {
|
||||
<div id="hero" data-section="hero">
|
||||
<HeroCarouselLogo
|
||||
logoText="VIRTUAL TRY ON"
|
||||
description="Upload your photo and clothes to instantly see how outfits look on you. No more guessing – visualize your style in seconds with AI-powered virtual fitting."
|
||||
description="Shop with confidence—see exactly how clothes fit on your body before buying. Upload your photo and clothes to instantly visualize outfits with AI-powered virtual fitting."
|
||||
buttons={[
|
||||
{ text: "Start Try-On", href: "tryon" },
|
||||
{ text: "Learn More", href: "about" },
|
||||
]}
|
||||
slides={[
|
||||
{
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/a-modern-clean-interface-showing-a-perso-1773140923936-4fd9a2ad.png", imageAlt: "Virtual try-on demonstration with user wearing tried-on clothes"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/a-modern-clean-interface-showing-a-perso-1773140923936-4fd9a2ad.png", imageAlt: "Virtual try-on demonstration with user wearing tried-on clothes"
|
||||
},
|
||||
{
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/a-fashion-model-in-professional-studio-s-1773140924846-ede690c0.png", imageAlt: "Fashion model trying on different outfits virtually"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/a-fashion-model-in-professional-studio-s-1773140924846-ede690c0.png", imageAlt: "Fashion model trying on different outfits virtually"
|
||||
},
|
||||
{
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/user-interface-showing-real-time-virtual-1773140923840-8d6f0322.png", imageAlt: "User interface showing virtual clothing fitting on person"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/user-interface-showing-real-time-virtual-1773140923840-8d6f0322.png", imageAlt: "User interface showing virtual clothing fitting on person"
|
||||
},
|
||||
]}
|
||||
autoplayDelay={4000}
|
||||
showDimOverlay={true}
|
||||
@@ -87,23 +69,29 @@ export default function LandingPage() {
|
||||
{
|
||||
id: 1,
|
||||
title: "Upload Your Photo", description: "Take a clear full-body photo or upload from your gallery. Our AI analyzes your body measurements and proportions automatically.", phoneOne: {
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-screen-showing-photo-upload-i-1773140923728-8a57dab7.png", imageAlt: "Upload Your Photo - Phone 1"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-screen-showing-photo-upload-i-1773140923728-8a57dab7.png", imageAlt: "Upload Your Photo - Phone 1"
|
||||
},
|
||||
phoneTwo: {
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-screen-showing-successful-pho-1773140924340-d914f40c.png", imageAlt: "Upload Your Photo - Phone 2"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-screen-showing-successful-pho-1773140924340-d914f40c.png", imageAlt: "Upload Your Photo - Phone 2"
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Select Your Clothes", description: "Browse from our extensive catalog or upload images of clothes you own. Choose sizes and colors to customize your selection.", phoneOne: {
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-displaying-clothing-catalog-i-1773140924524-ff3ad0c2.png", imageAlt: "Select Your Clothes - Phone 1"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-displaying-clothing-catalog-i-1773140924524-ff3ad0c2.png", imageAlt: "Select Your Clothes - Phone 1"
|
||||
},
|
||||
phoneTwo: {
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-screen-showing-selected-cloth-1773140924624-dbbb7667.png", imageAlt: "Select Your Clothes - Phone 2"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-screen-showing-selected-cloth-1773140924624-dbbb7667.png", imageAlt: "Select Your Clothes - Phone 2"
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "See How It Looks", description: "Watch in real-time as our AI technology drapes the clothing onto your body. Get instant visual feedback before making purchase decisions.", phoneOne: {
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-showing-real-time-virtual-try-1773140924250-6c935ace.png", imageAlt: "See How It Looks - Phone 1"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-showing-real-time-virtual-try-1773140924250-6c935ace.png", imageAlt: "See How It Looks - Phone 1"
|
||||
},
|
||||
phoneTwo: {
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-interface-showing-side-by-sid-1773140924110-0eadfeb0.jpg", imageAlt: "See How It Looks - Phone 2"},
|
||||
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AkdZCEKcsvSa3zMiBDupiHXQnd/smartphone-interface-showing-side-by-sid-1773140924110-0eadfeb0.jpg", imageAlt: "See How It Looks - Phone 2"
|
||||
},
|
||||
},
|
||||
]}
|
||||
showStepNumbers={true}
|
||||
@@ -131,28 +119,18 @@ export default function LandingPage() {
|
||||
<div id="tryon" data-section="tryon">
|
||||
<div className="w-full py-20 px-4 md:px-8 bg-gradient-to-b from-transparent to-background/50">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{!showPreview ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<VirtualTryOnUpload
|
||||
title="Upload Your Photo"
|
||||
description="Select a clear full-body photo for accurate measurements"
|
||||
onPhotoUpload={handleUserPhotoUpload}
|
||||
uploadType="user"
|
||||
/>
|
||||
<VirtualTryOnUpload
|
||||
title="Upload Clothing Photo"
|
||||
description="Choose the clothes you want to try on"
|
||||
onPhotoUpload={handleClothesPhotoUpload}
|
||||
uploadType="clothes"
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="flex flex-col items-center justify-center p-8 rounded-lg border border-border/30 bg-card">
|
||||
<h3 className="text-2xl font-semibold mb-2">Upload Your Photo</h3>
|
||||
<p className="text-center text-muted-foreground mb-4">Select a clear full-body photo for accurate measurements</p>
|
||||
<button className="px-6 py-2 bg-primary-cta text-primary-cta-foreground rounded-theme hover:opacity-90 transition-opacity">Upload Photo</button>
|
||||
</div>
|
||||
) : (
|
||||
<VirtualTryOnPreview
|
||||
userPhoto={userPhoto}
|
||||
clothesPhoto={clothesPhoto}
|
||||
onBack={() => setShowPreview(false)}
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-col items-center justify-center p-8 rounded-lg border border-border/30 bg-card">
|
||||
<h3 className="text-2xl font-semibold mb-2">Upload Clothing Photo</h3>
|
||||
<p className="text-center text-muted-foreground mb-4">Choose the clothes you want to try on</p>
|
||||
<button className="px-6 py-2 bg-primary-cta text-primary-cta-foreground rounded-theme hover:opacity-90 transition-opacity">Upload Clothes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -187,17 +165,23 @@ export default function LandingPage() {
|
||||
<FaqDouble
|
||||
faqs={[
|
||||
{
|
||||
id: "1", title: "How accurate is the virtual try-on?", content: "Our AI technology uses advanced computer vision and machine learning to achieve 94% accuracy in fit prediction. We analyze 50+ body points and fabric properties to ensure realistic visualization. Results vary based on photo quality and clothing specifications."},
|
||||
id: "1", title: "How accurate is the virtual try-on?", content: "Our AI technology uses advanced computer vision and machine learning to achieve 94% accuracy in fit prediction. We analyze 50+ body points and fabric properties to ensure realistic visualization. Results vary based on photo quality and clothing specifications."
|
||||
},
|
||||
{
|
||||
id: "2", title: "What data do you collect from my photos?", content: "We analyze body measurements and proportions but do not store your original photos permanently. Data is processed securely and deleted after 30 days unless you choose to save your profile. You have full control over your privacy settings."},
|
||||
id: "2", title: "What data do you collect from my photos?", content: "We analyze body measurements and proportions but do not store your original photos permanently. Data is processed securely and deleted after 30 days unless you choose to save your profile. You have full control over your privacy settings."
|
||||
},
|
||||
{
|
||||
id: "3", title: "Can I use this for online shopping?", content: "Yes! Many retailers integrate with our platform. You can see exactly how clothes will fit before purchasing, significantly reducing return rates and improving shopping confidence."},
|
||||
id: "3", title: "Can I use this for online shopping?", content: "Yes! Many retailers integrate with our platform. You can see exactly how clothes will fit before purchasing, significantly reducing return rates and improving shopping confidence."
|
||||
},
|
||||
{
|
||||
id: "4", title: "Which devices are supported?", content: "Our platform works on all modern devices including smartphones, tablets, and desktop computers. We recommend good lighting and a clear full-body view for best results."},
|
||||
id: "4", title: "Which devices are supported?", content: "Our platform works on all modern devices including smartphones, tablets, and desktop computers. We recommend good lighting and a clear full-body view for best results."
|
||||
},
|
||||
{
|
||||
id: "5", title: "Is there a subscription fee?", content: "Basic virtual try-on is completely free. Premium features like advanced body mapping and brand partnerships are available through optional premium tiers starting at $4.99/month."},
|
||||
id: "5", title: "Is there a subscription fee?", content: "Basic virtual try-on is completely free. Premium features like advanced body mapping and brand partnerships are available through optional premium tiers starting at $4.99/month."
|
||||
},
|
||||
{
|
||||
id: "6", title: "How do I get accurate measurements?", content: "Our AI automatically detects body proportions from your photo. For the most accurate results, wear fitted clothing, stand in good lighting, and ensure your entire body is visible in the frame."},
|
||||
id: "6", title: "How do I get accurate measurements?", content: "Our AI automatically detects body proportions from your photo. For the most accurate results, wear fitted clothing, stand in good lighting, and ensure your entire body is visible in the frame."
|
||||
},
|
||||
]}
|
||||
title="Frequently Asked Questions"
|
||||
description="Everything you need to know about virtual try-on technology"
|
||||
@@ -213,11 +197,14 @@ export default function LandingPage() {
|
||||
<ContactFaq
|
||||
faqs={[
|
||||
{
|
||||
id: "1", title: "Do I need to create an account?", content: "No account is required for basic try-ons. Create a free account to save your profile, preferences, and try-on history for a personalized experience."},
|
||||
id: "1", title: "Do I need to create an account?", content: "No account is required for basic try-ons. Create a free account to save your profile, preferences, and try-on history for a personalized experience."
|
||||
},
|
||||
{
|
||||
id: "2", title: "Can I share my virtual try-ons?", content: "Yes! Share try-on results directly with friends via social media, email, or link. Get real-time feedback before making purchase decisions."},
|
||||
id: "2", title: "Can I share my virtual try-ons?", content: "Yes! Share try-on results directly with friends via social media, email, or link. Get real-time feedback before making purchase decisions."
|
||||
},
|
||||
{
|
||||
id: "3", title: "What about different body types?", content: "Our AI is trained on diverse body types and sizes. We support measurements from XS to XXXL and continuously improve accuracy across all body shapes."},
|
||||
id: "3", title: "What about different body types?", content: "Our AI is trained on diverse body types and sizes. We support measurements from XS to XXXL and continuously improve accuracy across all body shapes."
|
||||
},
|
||||
]}
|
||||
ctaTitle="Ready to Transform Your Shopping?"
|
||||
ctaDescription="Start your virtual try-on journey today. Get instant access to our AI-powered fitting technology."
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
--background: #ffffff;
|
||||
--card: #f9f9f9;
|
||||
--foreground: #120a00e6;
|
||||
--primary-cta: #E34400;
|
||||
--foreground: #000f06e6;
|
||||
--primary-cta: #0a7039;
|
||||
--primary-cta-text: #ffffff;
|
||||
--secondary-cta: #f9f9f9;
|
||||
--secondary-cta-text: #120a00e6;
|
||||
--secondary-cta-text: #000f06e6;
|
||||
--accent: #e2e2e2;
|
||||
--background-accent: #E34400;
|
||||
--background-accent: #c4c4c4;
|
||||
|
||||
/* text sizing - set by ThemeProvider */
|
||||
/* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { ArrowLeft, Download, Share2, RotateCcw } from "lucide-react";
|
||||
|
||||
interface VirtualTryOnPreviewProps {
|
||||
userPhoto: string | null;
|
||||
clothesPhoto: string | null;
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
export default function VirtualTryOnPreview({
|
||||
userPhoto,
|
||||
clothesPhoto,
|
||||
onBack,
|
||||
}: VirtualTryOnPreviewProps) {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [isProcessing, setIsProcessing] = useState(true);
|
||||
const [selectedFit, setSelectedFit] = useState<"tight" | "normal" | "loose">("normal");
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate AI processing time
|
||||
const timer = setTimeout(() => {
|
||||
setIsProcessing(false);
|
||||
// Create a simple composite preview
|
||||
if (userPhoto && clothesPhoto) {
|
||||
// In a real implementation, this would use server-side image processing
|
||||
setPreviewImage(userPhoto);
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [userPhoto, clothesPhoto]);
|
||||
|
||||
const handleDownload = () => {
|
||||
if (previewImage) {
|
||||
const link = document.createElement("a");
|
||||
link.href = previewImage;
|
||||
link.download = "virtual-tryon-result.png";
|
||||
link.click();
|
||||
}
|
||||
};
|
||||
|
||||
const handleShare = () => {
|
||||
if (navigator.share && previewImage) {
|
||||
navigator.share({
|
||||
title: "My Virtual Try-On", text: "Check out how I look in these clothes!"});
|
||||
} else {
|
||||
// Fallback: copy to clipboard
|
||||
const url = window.location.href;
|
||||
navigator.clipboard.writeText(url);
|
||||
alert("Link copied to clipboard!");
|
||||
}
|
||||
};
|
||||
|
||||
const handleFitChange = (fit: "tight" | "normal" | "loose") => {
|
||||
setSelectedFit(fit);
|
||||
setIsProcessing(true);
|
||||
setTimeout(() => setIsProcessing(false), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{/* Header */}
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<h2 className="text-2xl md:text-3xl font-semibold text-foreground">Your Virtual Try-On</h2>
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-lg border border-foreground/20 hover:border-foreground/40 hover:bg-foreground/5 transition-all text-foreground text-sm"
|
||||
aria-label="Go back"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
<span>Back</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Main Preview Container */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Preview Image */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="relative w-full aspect-video rounded-xl overflow-hidden border-2 border-foreground/20 bg-gradient-to-br from-foreground/5 to-background/20">
|
||||
{isProcessing ? (
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center gap-4 bg-background/40 backdrop-blur-sm">
|
||||
<div className="w-16 h-16 rounded-full border-4 border-foreground/20 border-t-primary-cta animate-spin"></div>
|
||||
<p className="text-foreground text-center text-sm md:text-base">
|
||||
Processing your virtual try-on with AI technology...
|
||||
</p>
|
||||
</div>
|
||||
) : previewImage ? (
|
||||
<>
|
||||
<img
|
||||
src={previewImage}
|
||||
alt="Virtual try-on preview"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-background/10"></div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Details Below Preview */}
|
||||
<div className="mt-6 grid grid-cols-2 gap-4">
|
||||
<div className="p-4 rounded-lg bg-foreground/5 border border-foreground/10">
|
||||
<p className="text-xs text-foreground/60 uppercase tracking-wide mb-2">Your Photo</p>
|
||||
<div className="w-full aspect-square rounded-lg overflow-hidden border border-foreground/20 bg-foreground/5">
|
||||
{userPhoto && (
|
||||
<img
|
||||
src={userPhoto}
|
||||
alt="Your photo"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-lg bg-foreground/5 border border-foreground/10">
|
||||
<p className="text-xs text-foreground/60 uppercase tracking-wide mb-2">Clothes</p>
|
||||
<div className="w-full aspect-square rounded-lg overflow-hidden border border-foreground/20 bg-foreground/5">
|
||||
{clothesPhoto && (
|
||||
<img
|
||||
src={clothesPhoto}
|
||||
alt="Clothes photo"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Controls Panel */}
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Fit Selector */}
|
||||
<div className="p-6 rounded-xl bg-foreground/5 border border-foreground/10">
|
||||
<h3 className="text-sm font-semibold text-foreground mb-4 uppercase tracking-wide">
|
||||
Fit Preference
|
||||
</h3>
|
||||
<div className="flex flex-col gap-3">
|
||||
{(["tight", "normal", "loose"] as const).map((fit) => (
|
||||
<button
|
||||
key={fit}
|
||||
onClick={() => handleFitChange(fit)}
|
||||
className={`px-4 py-3 rounded-lg transition-all text-sm font-medium capitalize border-2 ${
|
||||
selectedFit === fit
|
||||
? "border-primary-cta bg-primary-cta/10 text-foreground"
|
||||
: "border-foreground/20 bg-background text-foreground/70 hover:border-foreground/40"
|
||||
}`}
|
||||
>
|
||||
{fit}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
className="w-full px-4 py-3 rounded-lg bg-primary-cta text-background font-medium flex items-center justify-center gap-2 hover:opacity-90 transition-opacity"
|
||||
aria-label="Download result"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
<span>Download</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleShare}
|
||||
className="w-full px-4 py-3 rounded-lg bg-foreground/10 border border-foreground/20 text-foreground font-medium flex items-center justify-center gap-2 hover:border-foreground/40 hover:bg-foreground/15 transition-all"
|
||||
aria-label="Share result"
|
||||
>
|
||||
<Share2 className="w-4 h-4" />
|
||||
<span>Share</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="w-full px-4 py-3 rounded-lg bg-foreground/10 border border-foreground/20 text-foreground font-medium flex items-center justify-center gap-2 hover:border-foreground/40 hover:bg-foreground/15 transition-all"
|
||||
aria-label="Try again"
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
<span>Try Again</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Info Box */}
|
||||
<div className="p-4 rounded-lg bg-background-accent/40 border border-foreground/10">
|
||||
<p className="text-xs text-foreground/70 leading-relaxed">
|
||||
<span className="font-semibold text-foreground block mb-2">Accuracy:</span>
|
||||
This preview is powered by advanced AI. The actual fit may vary based on fabric properties and your exact measurements.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef } from "react";
|
||||
import { Upload, X } from "lucide-react";
|
||||
|
||||
interface VirtualTryOnUploadProps {
|
||||
title: string;
|
||||
description: string;
|
||||
onPhotoUpload: (photo: string) => void;
|
||||
uploadType: "user" | "clothes";
|
||||
}
|
||||
|
||||
export default function VirtualTryOnUpload({
|
||||
title,
|
||||
description,
|
||||
onPhotoUpload,
|
||||
uploadType,
|
||||
}: VirtualTryOnUploadProps) {
|
||||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleFileSelect = (file: File) => {
|
||||
if (file && file.type.startsWith("image/")) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const result = e.target?.result as string;
|
||||
setPreview(result);
|
||||
onPhotoUpload(result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragOver = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
const handleDragLeave = () => {
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(false);
|
||||
const files = e.dataTransfer.files;
|
||||
if (files && files[0]) {
|
||||
handleFileSelect(files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = e.target.files;
|
||||
if (files && files[0]) {
|
||||
handleFileSelect(files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemovePhoto = () => {
|
||||
setPreview(null);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-xl md:text-2xl font-semibold mb-2 text-foreground">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-sm md:text-base text-foreground/70">{description}</p>
|
||||
</div>
|
||||
|
||||
{!preview ? (
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
onClick={handleClick}
|
||||
className={`relative w-full aspect-video rounded-xl border-2 border-dashed transition-all cursor-pointer flex flex-col items-center justify-center gap-4 p-8 ${
|
||||
isDragging
|
||||
? "border-primary-cta bg-primary-cta/10"
|
||||
: "border-foreground/20 bg-foreground/5 hover:border-foreground/40 hover:bg-foreground/10"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleFileInputChange}
|
||||
className="hidden"
|
||||
aria-label={`Upload ${uploadType} photo`}
|
||||
/>
|
||||
<div className="text-center">
|
||||
<Upload className="w-12 h-12 md:w-16 md:h-16 mx-auto mb-4 text-primary-cta opacity-70" />
|
||||
<p className="text-base md:text-lg font-medium text-foreground mb-2">
|
||||
{isDragging ? "Drop your photo here" : "Drag & drop or click to upload"}
|
||||
</p>
|
||||
<p className="text-sm text-foreground/60">PNG, JPG, GIF up to 10MB</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative w-full aspect-video rounded-xl overflow-hidden border-2 border-foreground/20 bg-foreground/5">
|
||||
<img
|
||||
src={preview}
|
||||
alt={`${uploadType} photo preview`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<button
|
||||
onClick={handleRemovePhoto}
|
||||
className="absolute top-3 right-3 p-2 bg-background/80 hover:bg-background rounded-lg transition-colors border border-foreground/20 flex items-center justify-center"
|
||||
aria-label="Remove photo"
|
||||
>
|
||||
<X className="w-5 h-5 text-foreground" />
|
||||
</button>
|
||||
<div className="absolute bottom-3 left-3 right-3 flex items-center gap-2">
|
||||
<div className="flex-1 h-1 bg-foreground/20 rounded-full overflow-hidden">
|
||||
<div className="h-full w-1/2 bg-primary-cta rounded-full"></div>
|
||||
</div>
|
||||
<span className="text-xs font-medium text-foreground/70">Processing...</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4 p-4 bg-background-accent/40 rounded-lg border border-foreground/10">
|
||||
<p className="text-xs md:text-sm text-foreground/70">
|
||||
<span className="font-semibold text-foreground">Pro tip:</span> For best results, ensure good
|
||||
lighting, wear fitted clothes, and stand in a neutral pose.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user