|
|
import React, { useState } from 'react'; |
|
|
import { motion } from 'framer-motion'; |
|
|
import { Upload, Image as ImageIcon, Loader } from 'lucide-react'; |
|
|
import axios from 'axios'; |
|
|
|
|
|
const InteriorStyleTransformer = () => { |
|
|
const [selectedImage, setSelectedImage] = useState(null); |
|
|
const [styleInput, setStyleInput] = useState(''); |
|
|
const [generatedImage, setGeneratedImage] = useState(null); |
|
|
const [isLoading, setIsLoading] = useState(false); |
|
|
const [error, setError] = useState(null); |
|
|
|
|
|
|
|
|
const handleImageUpload = (event) => { |
|
|
const file = event.target.files[0]; |
|
|
if (file) { |
|
|
setSelectedImage(URL.createObjectURL(file)); |
|
|
setGeneratedImage(null); |
|
|
setError(null); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const handleStyleInput = (event) => { |
|
|
setStyleInput(event.target.value); |
|
|
}; |
|
|
|
|
|
|
|
|
const handleSubmit = async () => { |
|
|
if (!selectedImage || !styleInput) { |
|
|
setError('Please upload an image and specify a design style.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
setIsLoading(true); |
|
|
setError(null); |
|
|
|
|
|
try { |
|
|
|
|
|
const formData = new FormData(); |
|
|
const fileInput = document.querySelector('input[type="file"]'); |
|
|
formData.append('image', fileInput.files[0]); |
|
|
formData.append('style', styleInput); |
|
|
|
|
|
|
|
|
const response = await axios.post('https://api.example.com/transform-interior', formData, { |
|
|
headers: { 'Content-Type': 'multipart/form-data' }, |
|
|
}); |
|
|
|
|
|
|
|
|
setGeneratedImage(response.data.imageUrl); |
|
|
} catch (err) { |
|
|
setError('Failed to process the image. Please try again.'); |
|
|
console.error(err); |
|
|
} finally { |
|
|
setIsLoading(false); |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<section className="py-20 bg-[#0B1118]"> |
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
|
|
<motion.div |
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
whileInView={{ opacity: 1, y: 0 }} |
|
|
transition={{ duration: 0.8 }} |
|
|
className="text-center mb-12" |
|
|
> |
|
|
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-white"> |
|
|
Transform Your Interior Design |
|
|
</h2> |
|
|
<p className="text-xl text-gray-400"> |
|
|
Upload a photo of your space and specify your desired interior design style. |
|
|
</p> |
|
|
</motion.div> |
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8"> |
|
|
{/* Image Upload Section */} |
|
|
<motion.div |
|
|
initial={{ opacity: 0, scale: 0.9 }} |
|
|
whileInView={{ opacity: 1, scale: 1 }} |
|
|
transition={{ duration: 0.5 }} |
|
|
className="bg-gray-800/30 rounded-lg p-6 border border-gray-700 hover:border-blue-500/50" |
|
|
> |
|
|
<h3 className="text-xl font-semibold mb-4 text-white">Upload Your Room Photo</h3> |
|
|
<div className="flex flex-col items-center"> |
|
|
<label className="w-full flex flex-col items-center px-4 py-6 bg-gray-900/50 rounded-lg border-2 border-dashed border-gray-600 cursor-pointer hover:border-blue-500"> |
|
|
<Upload className="h-12 w-12 text-blue-500 mb-4" /> |
|
|
<span className="text-gray-400"> |
|
|
{selectedImage ? 'Change Image' : 'Upload an Image'} |
|
|
</span> |
|
|
<input |
|
|
type="file" |
|
|
accept="image/*" |
|
|
className="hidden" |
|
|
onChange={handleImageUpload} |
|
|
/> |
|
|
</label> |
|
|
{selectedImage && ( |
|
|
<img |
|
|
src={selectedImage} |
|
|
alt="Uploaded room" |
|
|
className="mt-4 max-h-64 w-full object-contain rounded-lg" |
|
|
/> |
|
|
)} |
|
|
</div> |
|
|
</motion.div> |
|
|
|
|
|
{/* Style Input and Generated Image Section */} |
|
|
<motion.div |
|
|
initial={{ opacity: 0, scale: 0.9 }} |
|
|
whileInView={{ opacity: 1, scale: 1 }} |
|
|
transition={{ duration: 0.5 }} |
|
|
className="bg-gray-800/30 rounded-lg p-6 border border-gray-700 hover:border-blue-500/50" |
|
|
> |
|
|
<h3 className="text-xl font-semibold mb-4 text-white">Specify Design Style</h3> |
|
|
<input |
|
|
type="text" |
|
|
placeholder="e.g., Modern, Scandinavian, Minimalist" |
|
|
value={styleInput} |
|
|
onChange={handleStyleInput} |
|
|
className="w-full px-4 py-2 bg-gray-900 text-white rounded-lg border border-gray-600 focus:outline-none focus:border-blue-500 mb-4" |
|
|
/> |
|
|
<motion.button |
|
|
whileHover={{ scale: 1.05 }} |
|
|
whileTap={{ scale: 0.95 }} |
|
|
onClick={handleSubmit} |
|
|
disabled={isLoading} |
|
|
className="w-full px-4 py-2 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 disabled:bg-gray-600" |
|
|
> |
|
|
{isLoading ? ( |
|
|
<div className="flex items-center justify-center"> |
|
|
<Loader className="h-6 w-6 animate-spin mr-2" /> |
|
|
Processing... |
|
|
</div> |
|
|
) : ( |
|
|
'Generate Design' |
|
|
)} |
|
|
</motion.button> |
|
|
{error && <p className="text-red-500 mt-4">{error}</p>} |
|
|
{generatedImage && ( |
|
|
<div className="mt-4"> |
|
|
<h3 className="text-lg font-semibold text-white mb-2">Generated Design</h3> |
|
|
<img |
|
|
src={generatedImage} |
|
|
alt="Generated interior design" |
|
|
className="max-h-64 w-full object-contain rounded-lg" |
|
|
/> |
|
|
</div> |
|
|
)} |
|
|
</motion.div> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default InteriorStyleTransformer; |