lumemgallery / index.html
akbit's picture
Add 1 files
7a3a8b5 verified
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cinematic Photo Gallery</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://accounts.google.com/gsi/client" async defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.animate-fade-in { animation: fadeIn 0.3s ease-out forwards; }
.animate-slide-up { animation: slideUp 0.3s ease-out forwards; }
.flipbook {
perspective: 2000px;
}
.page {
transform-style: preserve-3d;
transform-origin: left center;
transition: transform 0.8s;
backface-visibility: hidden;
}
.page.flipped {
transform: rotateY(-180deg);
}
.page-content {
backface-visibility: hidden;
}
.page-back {
transform: rotateY(180deg);
backface-visibility: hidden;
}
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(0, 0, 0, 0.7);
}
.album-card:hover .album-overlay {
opacity: 1;
}
.drag-active {
border-color: #3b82f6 !important;
background-color: rgba(59, 130, 246, 0.1) !important;
}
</style>
</head>
<body class="font-inter bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen transition-colors duration-300">
<!-- Navigation -->
<nav class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<a href="#" class="text-xl font-bold text-indigo-600 dark:text-indigo-400">PhotoGallery</a>
</div>
<div class="flex items-center space-x-4">
<button id="darkModeToggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 hidden dark:block" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 block dark:hidden" viewBox="0 0 20 20" fill="currentColor">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>
</button>
<button id="loginBtn" class="px-4 py-2 rounded-md bg-indigo-600 text-white hover:bg-indigo-700 transition">Login</button>
<button id="adminBtn" class="px-4 py-2 rounded-md bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition hidden">Admin</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Homepage -->
<div id="homepage">
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold">Photo Albums</h1>
<div class="flex space-x-2">
<button id="recentlyBtn" class="px-4 py-2 rounded-md bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition">Recently</button>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<!-- Album Cards -->
<div class="album-card bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden transition-transform hover:scale-105 cursor-pointer relative" data-album-id="1" data-visibility="public">
<div class="relative aspect-square">
<img src="https://source.unsplash.com/random/600x600/?nature,1" alt="Nature" class="w-full h-full object-cover">
<div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
<span class="text-white font-medium">View Album</span>
</div>
</div>
<div class="p-4">
<h3 class="font-semibold">Nature</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">24 photos</p>
<span class="absolute top-2 right-2 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 text-xs px-2 py-1 rounded-full">Public</span>
</div>
</div>
<div class="album-card bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden transition-transform hover:scale-105 cursor-pointer relative" data-album-id="2" data-visibility="family">
<div class="relative aspect-square">
<img src="https://source.unsplash.com/random/600x600/?travel,1" alt="Travel" class="w-full h-full object-cover">
<div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
<span class="text-white font-medium">View Album</span>
</div>
</div>
<div class="p-4">
<h3 class="font-semibold">Travel</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">18 photos</p>
<span class="absolute top-2 right-2 bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 text-xs px-2 py-1 rounded-full">Family</span>
</div>
</div>
<div class="album-card bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden transition-transform hover:scale-105 cursor-pointer relative" data-album-id="3" data-visibility="private">
<div class="relative aspect-square">
<img src="https://source.unsplash.com/random/600x600/?portrait,1" alt="Portraits" class="w-full h-full object-cover">
<div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
<span class="text-white font-medium">View Album</span>
</div>
</div>
<div class="p-4">
<h3 class="font-semibold">Portraits</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">12 photos</p>
<span class="absolute top-2 right-2 bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 text-xs px-2 py-1 rounded-full">Private</span>
</div>
</div>
<div class="album-card bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden transition-transform hover:scale-105 cursor-pointer relative" data-album-id="4" data-visibility="public">
<div class="relative aspect-square">
<img src="https://source.unsplash.com/random/600x600/?architecture,1" alt="Architecture" class="w-full h-full object-cover">
<div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
<span class="text-white font-medium">View Album</span>
</div>
</div>
<div class="p-4">
<h3 class="font-semibold">Architecture</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">32 photos</p>
<span class="absolute top-2 right-2 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 text-xs px-2 py-1 rounded-full">Public</span>
</div>
</div>
</div>
</div>
<!-- Album Viewer -->
<div id="albumViewer" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
<div class="relative w-full max-w-4xl">
<button id="closeAlbum" class="absolute -top-12 right-0 text-white hover:text-gray-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<div class="flipbook bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden">
<div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
<h2 id="albumTitle" class="text-xl font-bold">Album Title</h2>
<div class="flex space-x-2">
<span id="albumVisibility" class="px-2 py-1 rounded-full text-xs font-medium">Public</span>
<span id="albumPhotoCount" class="text-gray-500 dark:text-gray-400 text-sm">24 photos</span>
</div>
</div>
<div class="relative h-96 md:h-[32rem] overflow-hidden">
<div id="flipbookPages" class="relative h-full w-full flex">
<!-- Pages will be inserted here by JavaScript -->
</div>
<button id="prevPage" class="absolute left-4 top-1/2 -translate-y-1/2 bg-black bg-opacity-50 text-white rounded-full p-2 hover:bg-opacity-70 disabled:opacity-30 disabled:cursor-not-allowed">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</button>
<button id="nextPage" class="absolute right-4 top-1/2 -translate-y-1/2 bg-black bg-opacity-50 text-white rounded-full p-2 hover:bg-opacity-70 disabled:opacity-30 disabled:cursor-not-allowed">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
<div class="flex items-center justify-between p-4 border-t border-gray-200 dark:border-gray-700">
<span id="currentPage" class="text-sm text-gray-500 dark:text-gray-400">Page 1 of 5</span>
<div class="flex space-x-2">
<button id="zoomOutBtn" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7" />
</svg>
</button>
<button id="downloadBtn" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Image Modal -->
<div id="imageModal" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
<div class="relative max-w-5xl max-h-screen">
<button id="closeModal" class="absolute -top-12 right-0 text-white hover:text-gray-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden">
<div id="modalImageContainer" class="max-h-[80vh] overflow-auto">
<img id="modalImage" src="" alt="" class="w-full h-auto">
</div>
<div class="flex items-center justify-between p-4 border-t border-gray-200 dark:border-gray-700">
<h3 id="modalImageTitle" class="font-medium">Image Title</h3>
<div class="flex space-x-2">
<button id="modalZoomOut" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7" />
</svg>
</button>
<button id="modalDownload" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Login Modal -->
<div id="loginModal" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden w-full max-w-md animate-slide-up">
<div class="p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">Login</h2>
<button id="closeLoginModal" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="space-y-4">
<div id="g_id_onload"
data-client_id="YOUR_GOOGLE_CLIENT_ID"
data-context="signin"
data-ux_mode="popup"
data-callback="handleGoogleSignIn"
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-shape="rectangular"
data-theme="outline"
data-text="signin_with"
data-size="large"
data-logo_alignment="left">
</div>
<div class="flex items-center my-4">
<div class="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
<span class="mx-4 text-gray-500 dark:text-gray-400">or</span>
<div class="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
<input type="email" id="email" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Password</label>
<input type="password" id="password" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">
<input id="remember-me" name="remember-me" type="checkbox" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 dark:border-gray-600 rounded dark:bg-gray-700">
<label for="remember-me" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Remember me</label>
</div>
<a href="#" class="text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300">Forgot password?</a>
</div>
<button id="emailLoginBtn" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Sign in
</button>
<div class="text-center text-sm text-gray-500 dark:text-gray-400">
Don't have an account? <a href="#" id="signupLink" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300">Sign up</a>
</div>
</div>
</div>
</div>
</div>
<!-- Signup Modal -->
<div id="signupModal" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden w-full max-w-md animate-slide-up">
<div class="p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">Create Account</h2>
<button id="closeSignupModal" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="space-y-4">
<div>
<label for="signupName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Full Name</label>
<input type="text" id="signupName" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="signupEmail" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
<input type="email" id="signupEmail" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="signupPassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Password</label>
<input type="password" id="signupPassword" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="signupConfirmPassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Confirm Password</label>
<input type="password" id="signupConfirmPassword" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div class="flex items-center">
<input id="terms" name="terms" type="checkbox" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 dark:border-gray-600 rounded dark:bg-gray-700">
<label for="terms" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">
I agree to the <a href="#" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300">Terms and Conditions</a>
</label>
</div>
<button id="signupBtn" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Create Account
</button>
<div class="text-center text-sm text-gray-500 dark:text-gray-400">
Already have an account? <a href="#" id="loginLink" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300">Log in</a>
</div>
</div>
</div>
</div>
</div>
<!-- Recently View -->
<div id="recentlyView" class="hidden">
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold">Recently Viewed</h1>
<button id="backToHome" class="px-4 py-2 rounded-md bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition">Back to Albums</button>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<!-- Recently viewed photos will be inserted here by JavaScript -->
</div>
</div>
<!-- Admin Dashboard -->
<div id="adminDashboard" class="hidden">
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold">Admin Dashboard</h1>
<div class="flex space-x-2">
<button id="backToHomeAdmin" class="px-4 py-2 rounded-md bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition">Back to Albums</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold mb-4">Upload Photos</h2>
<div id="dropZone" class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-8 text-center cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">Drag and drop files here</p>
<p class="text-xs text-gray-400 dark:text-gray-500">or</p>
<button id="selectFilesBtn" class="mt-2 px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Select Files</button>
<input type="file" id="fileInput" class="hidden" multiple accept="image/*">
</div>
<div id="uploadProgress" class="mt-4 hidden">
<div class="flex justify-between mb-1">
<span class="text-sm font-medium">Uploading...</span>
<span id="uploadPercentage" class="text-sm font-medium">0%</span>
</div>
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
<div id="progressBar" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
</div>
<div id="uploadedFiles" class="mt-4 space-y-2 hidden">
<h3 class="font-medium">Uploaded Files</h3>
<div id="uploadedFilesList" class="space-y-2">
<!-- Uploaded files will be listed here -->
</div>
</div>
<div class="mt-6">
<label for="albumSelect" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Add to Album</label>
<select id="albumSelect" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="">Select an album</option>
<option value="1">Nature</option>
<option value="2">Travel</option>
<option value="3">Portraits</option>
<option value="4">Architecture</option>
<option value="new">Create New Album</option>
</select>
<div id="newAlbumFields" class="mt-2 hidden">
<input type="text" id="newAlbumName" placeholder="Album Name" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white mb-2">
<select id="newAlbumVisibility" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="public">Public</option>
<option value="family">Family</option>
<option value="private">Private</option>
</select>
</div>
<div class="mt-4">
<label for="photoTitles" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Photo Titles (comma separated)</label>
<input type="text" id="photoTitles" placeholder="Title 1, Title 2, Title 3" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<div class="mt-4">
<label for="photoTags" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Tags (comma separated)</label>
<input type="text" id="photoTags" placeholder="nature, outdoor, summer" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
</div>
<button id="savePhotosBtn" class="mt-4 w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Save Photos
</button>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold mb-4">User Management</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Email</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Role</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">John Doe</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">john@example.com</td>
<td class="px-6 py-4 whitespace-nowrap">
<select class="text-sm border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option>Admin</option>
<option selected>Viewer</option>
</select>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<button class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">Lock</button>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">Jane Smith</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">jane@example.com</td>
<td class="px-6 py-4 whitespace-nowrap">
<select class="text-sm border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option selected>Admin</option>
<option>Viewer</option>
</select>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<button class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">Lock</button>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">Bob Johnson</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">bob@example.com</td>
<td class="px-6 py-4 whitespace-nowrap">
<select class="text-sm border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option>Admin</option>
<option selected>Viewer</option>
</select>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<button class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">Lock</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="mt-4">
<h3 class="font-medium mb-2">Add New User</h3>
<div class="flex space-x-2">
<input type="email" placeholder="Email" class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<select class="text-sm border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option>Admin</option>
<option>Viewer</option>
</select>
<button class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Add</button>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold mb-4">Album Management</h2>
<div class="space-y-4">
<div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div>
<h3 class="font-medium">Nature</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">24 photos</p>
</div>
<div class="flex items-center space-x-2">
<select class="text-xs border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="public" selected>Public</option>
<option value="family">Family</option>
<option value="private">Private</option>
</select>
<button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div>
<h3 class="font-medium">Travel</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">18 photos</p>
</div>
<div class="flex items-center space-x-2">
<select class="text-xs border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="public">Public</option>
<option value="family" selected>Family</option>
<option value="private">Private</option>
</select>
<button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div>
<h3 class="font-medium">Portraits</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">12 photos</p>
</div>
<div class="flex items-center space-x-2">
<select class="text-xs border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="public">Public</option>
<option value="family">Family</option>
<option value="private" selected>Private</option>
</select>
<button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div>
<h3 class="font-medium">Architecture</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">32 photos</p>
</div>
<div class="flex items-center space-x-2">
<select class="text-xs border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="public" selected>Public</option>
<option value="family">Family</option>
<option value="private">Private</option>
</select>
<button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</div>
</div>
<div class="mt-4">
<h3 class="font-medium mb-2">Create New Album</h3>
<div class="flex space-x-2">
<input type="text" placeholder="Album Name" class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<select class="text-sm border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white">
<option value="public">Public</option>
<option value="family">Family</option>
<option value="private">Private</option>
</select>
<button class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Create</button>
</div>
</div>
</div>
</div>
</div>
</main>
<script>
// Dark mode toggle
document.getElementById('darkModeToggle').addEventListener('click', function() {
document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
});
// Check for saved dark mode preference
if (localStorage.getItem('darkMode') === 'true') {
document.documentElement.classList.add('dark');
} else if (localStorage.getItem('darkMode') === 'false') {
document.documentElement.classList.remove('dark');
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
}
// Modal management
const loginModal = document.getElementById('loginModal');
const signupModal = document.getElementById('signupModal');
const albumViewer = document.getElementById('albumViewer');
const imageModal = document.getElementById('imageModal');
const homepage = document.getElementById('homepage');
const recentlyView = document.getElementById('recentlyView');
const adminDashboard = document.getElementById('adminDashboard');
// Login/Signup modal toggles
document.getElementById('loginBtn').addEventListener('click', () => {
loginModal.classList.remove('hidden');
loginModal.classList.add('animate-fade-in');
});
document.getElementById('closeLoginModal').addEventListener('click', () => {
loginModal.classList.add('hidden');
});
document.getElementById('signupLink').addEventListener('click', (e) => {
e.preventDefault();
loginModal.classList.add('hidden');
signupModal.classList.remove('hidden');
signupModal.classList.add('animate-fade-in');
});
document.getElementById('closeSignupModal').addEventListener('click', () => {
signupModal.classList.add('hidden');
});
document.getElementById('loginLink').addEventListener('click', (e) => {
e.preventDefault();
signupModal.classList.add('hidden');
loginModal.classList.remove('hidden');
loginModal.classList.add('animate-fade-in');
});
// Mock login functionality
document.getElementById('emailLoginBtn').addEventListener('click', () => {
// In a real app, you would validate credentials here
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('isAdmin', 'true'); // For demo purposes, always log in as admin
document.getElementById('loginBtn').classList.add('hidden');
document.getElementById('adminBtn').classList.remove('hidden');
loginModal.classList.add('hidden');
});
// Mock signup functionality
document.getElementById('signupBtn').addEventListener('click', () => {
// In a real app, you would create a new user account here
alert('Account created successfully! Please log in.');
signupModal.classList.add('hidden');
loginModal.classList.remove('hidden');
loginModal.classList.add('animate-fade-in');
});
// Google Sign-In callback
function handleGoogleSignIn(response) {
console.log('Google sign-in response:', response);
// In a real app, you would verify the credential and log the user in
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('isAdmin', 'true'); // For demo purposes, always log in as admin
document.getElementById('loginBtn').classList.add('hidden');
document.getElementById('adminBtn').classList.remove('hidden');
loginModal.classList.add('hidden');
}
// Check if user is logged in on page load
if (localStorage.getItem('isLoggedIn') === 'true') {
document.getElementById('loginBtn').classList.add('hidden');
document.getElementById('adminBtn').classList.remove('hidden');
}
// Admin dashboard toggle
document.getElementById('adminBtn').addEventListener('click', () => {
homepage.classList.add('hidden');
recentlyView.classList.add('hidden');
adminDashboard.classList.remove('hidden');
});
// Back to home buttons
document.getElementById('backToHome').addEventListener('click', () => {
recentlyView.classList.add('hidden');
homepage.classList.remove('hidden');
});
document.getElementById('backToHomeAdmin').addEventListener('click', () => {
adminDashboard.classList.add('hidden');
homepage.classList.remove('hidden');
});
// Recently view toggle
document.getElementById('recentlyBtn').addEventListener('click', () => {
homepage.classList.add('hidden');
adminDashboard.classList.add('hidden');
recentlyView.classList.remove('hidden');
// In a real app, you would fetch recently viewed photos here
const recentlyGrid = recentlyView.querySelector('.grid');
recentlyGrid.innerHTML = '';
// Mock recently viewed photos
const recentPhotos = [
{ id: 1, title: 'Mountain View', url: 'https://source.unsplash.com/random/600x600/?mountain,1', views: 24, tags: ['nature', 'landscape'] },
{ id: 2, title: 'Ocean Sunset', url: 'https://source.unsplash.com/random/600x600/?ocean,1', views: 18, tags: ['nature', 'water'] },
{ id: 3, title: 'City Skyline', url: 'https://source.unsplash.com/random/600x600/?city,1', views: 12, tags: ['urban', 'architecture'] },
{ id: 4, title: 'Forest Path', url: 'https://source.unsplash.com/random/600x600/?forest,1', views: 8, tags: ['nature', 'trees'] }
];
recentPhotos.forEach(photo => {
const photoCard = document.createElement('div');
photoCard.className = 'bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden transition-transform hover:scale-105 cursor-pointer';
photoCard.innerHTML = `
<div class="relative aspect-square">
<img src="${photo.url}" alt="${photo.title}" class="w-full h-full object-cover">
</div>
<div class="p-4">
<h3 class="font-semibold">${photo.title}</h3>
<div class="flex justify-between items-center mt-2">
<p class="text-sm text-gray-500 dark:text-gray-400">${photo.views} views</p>
<div class="flex space-x-1">
${photo.tags.map(tag => `<span class="text-xs bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded-full">${tag}</span>`).join('')}
</div>
</div>
</div>
`;
recentlyGrid.appendChild(photoCard);
});
});
// Album viewer functionality
const albumCards = document.querySelectorAll('.album-card');
albumCards.forEach(card => {
card.addEventListener('click', function() {
const albumId = this.getAttribute('data-album-id');
const visibility = this.getAttribute('data-visibility');
// Check if album is private and user is not logged in
if (visibility === 'private' && localStorage.getItem('isLoggedIn') !== 'true') {
alert('This album is private. Please log in to view.');
return;
}
// Check if album is family-only and user is not logged in
if (visibility === 'family' && localStorage.getItem('isLoggedIn') !== 'true') {
alert('This album is for family members only. Please log in to view.');
return;
}
// Set album title and visibility
const albumTitle = this.querySelector('h3').textContent;
const photoCount = this.querySelector('p').textContent;
document.getElementById('albumTitle').textContent = albumTitle;
document.getElementById('albumPhotoCount').textContent = photoCount;
const visibilitySpan = document.getElementById('albumVisibility');
visibilitySpan.textContent = visibility.charAt(0).toUpperCase() + visibility.slice(1);
visibilitySpan.className = 'px-2 py-1 rounded-full text-xs font-medium ' +
(visibility === 'public' ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' :
visibility === 'family' ? 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200' :
'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200');
// Show album viewer
homepage.classList.add('hidden');
albumViewer.classList.remove('hidden');
// Load album photos
loadAlbumPhotos(albumId);
});
});
function loadAlbumPhotos(albumId) {
const flipbookPages = document.getElementById('flipbookPages');
flipbookPages.innerHTML = '';
// In a real app, you would fetch photos for this album from a database
// Here we're using mock data
const photos = [];
const photoCount = albumId === '1' ? 24 : albumId === '2' ? 18 : albumId === '3' ? 12 : 32;
for (let i = 1; i <= photoCount; i++) {
photos.push({
id: i,
title: `Photo ${i}`,
url: `https://source.unsplash.com/random/800x600/?${albumId === '1' ? 'nature' : albumId === '2' ? 'travel' : albumId === '3' ? 'portrait' : 'architecture'},${i}`,
tags: ['sample', 'tag']
});
}
// Create pages with 4 photos each (2 rows of 2)
let currentPage = 1;
const photosPerPage = 4;
const totalPages = Math.ceil(photos.length / photosPerPage);
document.getElementById('currentPage').textContent = `Page 1 of ${totalPages}`;
// Create first page
createPage(photos, 0, photosPerPage, currentPage, totalPages);
// Navigation buttons
document.getElementById('prevPage').addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
createPage(photos, (currentPage - 1) * photosPerPage, currentPage * photosPerPage, currentPage, totalPages);
document.getElementById('currentPage').textContent = `Page ${currentPage} of ${totalPages}`;
}
});
document.getElementById('nextPage').addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage++;
createPage(photos, (currentPage - 1) * photosPerPage, currentPage * photosPerPage, currentPage, totalPages);
document.getElementById('currentPage').textContent = `Page ${currentPage} of ${totalPages}`;
}
});
function createPage(photos, start, end, currentPage, totalPages) {
flipbookPages.innerHTML = '';
const pageDiv = document.createElement('div');
pageDiv.className = 'page absolute inset-0 flex flex-col';
const row1 = document.createElement('div');
row1.className = 'flex-1 grid grid-cols-2 gap-2 p-4';
const row2 = document.createElement('div');
row2.className = 'flex-1 grid grid-cols-2 gap-2 p-4 pt-0';
for (let i = start; i < Math.min(end, photos.length); i++) {
const photo = photos[i];
const photoDiv = document.createElement('div');
photoDiv.className = 'relative group cursor-pointer';
photoDiv.innerHTML = `
<img src="${photo.url}" alt="${photo.title}" class="w-full h-full object-cover rounded-lg">
<div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-all duration-300 rounded-lg flex items-center justify-center opacity-0 group-hover:opacity-100">
<span class="text-white font-medium">${photo.title}</span>
</div>
`;
// Add click event to view photo in modal
photoDiv.addEventListener('click', () => {
document.getElementById('modalImage').src = photo.url;
document.getElementById('modalImage').alt = photo.title;
document.getElementById('modalImageTitle').textContent = photo.title;
imageModal.classList.remove('hidden');
// Track view in localStorage
const viewedPhotos = JSON.parse(localStorage.getItem('viewedPhotos') || '[]');
if (!viewedPhotos.some(p => p.id === photo.id)) {
viewedPhotos.push({
id: photo.id,
title: photo.title,
url: photo.url,
viewedAt: new Date().toISOString(),
tags: photo.tags
});
localStorage.setItem('viewedPhotos', JSON.stringify(viewedPhotos));
}
});
if (i - start < 2) {
row1.appendChild(photoDiv);
} else {
row2.appendChild(photoDiv);
}
}
pageDiv.appendChild(row1);
pageDiv.appendChild(row2);
flipbookPages.appendChild(pageDiv);
// Disable/enable navigation buttons
document.getElementById('prevPage').disabled = currentPage === 1;
document.getElementById('nextPage').disabled = currentPage === totalPages;
}
}
// Close album viewer
document.getElementById('closeAlbum').addEventListener('click', () => {
albumViewer.classList.add('hidden');
homepage.classList.remove('hidden');
});
// Image modal functionality
document.getElementById('closeModal').addEventListener('click', () => {
imageModal.classList.add('hidden');
});
document.getElementById('modalZoomOut').addEventListener('click', () => {
const img = document.getElementById('modalImage');
img.style.transform = img.style.transform === 'scale(0.8)' ? 'scale(1)' : 'scale(0.8)';
});
document.getElementById('modalDownload').addEventListener('click', () => {
// In a real app, this would trigger a download
alert('Downloading image...');
});
// Admin photo upload functionality
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const selectFilesBtn = document.getElementById('selectFilesBtn');
const uploadProgress = document.getElementById('uploadProgress');
const progressBar = document.getElementById('progressBar');
const uploadPercentage = document.getElementById('uploadPercentage');
const uploadedFiles = document.getElementById('uploadedFiles');
const uploadedFilesList = document.getElementById('uploadedFilesList');
const albumSelect = document.getElementById('albumSelect');
const newAlbumFields = document.getElementById('newAlbumFields');
// Drag and drop events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropZone.classList.add('drag-active');
}
function unhighlight() {
dropZone.classList.remove('drag-active');
}
// Handle dropped files
dropZone.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
// Handle selected files
selectFilesBtn.addEventListener('click', () => {
fileInput.click();
});
fileInput.addEventListener('change', () => {
handleFiles(fileInput.files);
});
function handleFiles(files) {
uploadProgress.classList.remove('hidden');
uploadedFilesList.innerHTML = '';
let progress = 0;
const totalFiles = files.length;
let processedFiles = 0;
// Mock upload progress
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
// Show uploaded files
uploadedFiles.classList.remove('hidden');
Array.from(files).forEach((file, index) => {
const fileItem = document.createElement('div');
fileItem.className = 'flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-700 rounded';
fileItem.innerHTML = `
<div class="flex items-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 dark:text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span class="text-sm">${file.name}</span>
</div>
<span class="text-xs text-green-600 dark:text-green-400">Uploaded</span>
`;
uploadedFilesList.appendChild(fileItem);
});
}
progressBar.style.width = `${progress}%`;
uploadPercentage.textContent = `${Math.round(progress)}%`;
}, 200);
}
// New album fields toggle
albumSelect.addEventListener('change', function() {
if (this.value === 'new') {
newAlbumFields.classList.remove('hidden');
} else {
newAlbumFields.classList.add('hidden');
}
});
// Save photos button
document.getElementById('savePhotosBtn').addEventListener('click', function() {
const albumId = albumSelect.value;
const albumName = albumId === 'new' ? document.getElementById('newAlbumName').value : albumSelect.options[albumSelect.selectedIndex].text;
const albumVisibility = albumId === 'new' ? document.getElementById('newAlbumVisibility').value : '';
const photoTitles = document.getElementById('photoTitles').value.split(',');
const photoTags = document.getElementById('photoTags').value.split(',');
// In a real app, you would save this data to a database
alert(`Photos saved to ${albumName} (${albumVisibility || 'existing album'})`);
// Reset form
uploadProgress.classList.add('hidden');
uploadedFiles.classList.add('hidden');
fileInput.value = '';
document.getElementById('photoTitles').value = '';
document.getElementById('photoTags').value = '';
albumSelect.value = '';
if (albumId === 'new') {
document.getElementById('newAlbumName').value = '';
document.getElementById('newAlbumVisibility').value = 'public';
newAlbumFields.classList.add('hidden');
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=akbit/lumemgallery" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>