Spaces:
Sleeping
Sleeping
File size: 6,094 Bytes
fff42e3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
'use client'
import { useState } from 'react';
import { cultures } from '@/data/cultures';
import { generateDescription, generateImage } from '@/services/ai';
import { motion, AnimatePresence } from 'framer-motion';
export default function Home() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [showPreview, setShowPreview] = useState(false);
const [culture, setCulture] = useState<{
name: string;
description: string;
imageUrl: string;
} | null>(null);
const generateRandomCulture = async () => {
setLoading(true);
setError(null);
try {
const randomCulture = cultures[Math.floor(Math.random() * cultures.length)];
const [description, imageData] = await Promise.all([
generateDescription(randomCulture),
generateImage(`Indonesian traditional culture ${randomCulture}, professional photography style, high quality, detailed, 4k resolution`)
]);
if (!imageData || !imageData.imageUrl) {
throw new Error('Failed to generate image');
}
setCulture({
name: randomCulture,
description: description || '',
imageUrl: imageData.imageUrl
});
} catch (error) {
console.error('Error:', error);
setError('Failed to generate content. Please try again.');
} finally {
setLoading(false);
}
};
return (
<main className="min-h-screen p-8 bg-gradient-to-br from-orange-100 to-red-100">
<div className="max-w-4xl mx-auto">
<div className="text-center mb-12">
<motion.h1
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="text-5xl font-bold mb-3 text-red-800"
>
Warisan Nusantara
</motion.h1>
<motion.p
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="text-xl text-red-700/80"
>
Menjelajahi Ragam Warisan Budaya Indonesia
</motion.p>
</div>
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
onClick={generateRandomCulture}
disabled={loading}
className="w-full max-w-md mx-auto block px-6 py-3 bg-red-600 text-white rounded-lg shadow-lg hover:bg-red-700 transition-colors disabled:bg-gray-400"
>
{loading ? (
<div className="flex items-center justify-center">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-white mr-2"></div>
Memuat...
</div>
) : (
'Jelajahi Budaya'
)}
</motion.button>
{error && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="mt-4 p-4 bg-red-100 text-red-700 rounded-lg"
>
{error}
</motion.div>
)}
<AnimatePresence>
{culture && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="mt-12 bg-white rounded-xl shadow-xl overflow-hidden"
>
<div className="relative h-[400px] w-full cursor-pointer" onClick={() => setShowPreview(true)}>
{culture.imageUrl && (
<img
src={culture.imageUrl}
alt={culture.name}
className="w-full h-full object-contain"
onError={(e) => {
console.error('Image failed to load');
e.currentTarget.src = '/placeholder-image.jpg';
}}
/>
)}
<div className="absolute inset-0 bg-black bg-opacity-0 hover:bg-opacity-10 transition-all duration-300 flex items-center justify-center">
<span className="text-white opacity-0 hover:opacity-100 transition-opacity duration-300">
Klik untuk memperbesar
</span>
</div>
</div>
<div className="p-6">
<h2 className="text-2xl font-bold mb-4 text-red-800">
{culture.name}
</h2>
<p className="text-gray-700 leading-relaxed whitespace-pre-wrap">
{culture.description}
</p>
</div>
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{showPreview && culture && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4"
onClick={() => setShowPreview(false)}
>
<motion.div
initial={{ scale: 0.9 }}
animate={{ scale: 1 }}
exit={{ scale: 0.9 }}
className="relative max-w-[90vw] max-h-[90vh]"
>
<img
src={culture.imageUrl}
alt={culture.name}
className="max-w-full max-h-[90vh] object-contain"
/>
<button
className="absolute top-4 right-4 text-white bg-black bg-opacity-50 rounded-full p-2 hover:bg-opacity-70"
onClick={() => setShowPreview(false)}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
</main>
);
} |