akbit commited on
Commit
7a3a8b5
·
verified ·
1 Parent(s): 5897dc4

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +962 -483
index.html CHANGED
@@ -3,572 +3,1051 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Lumen Gallery | Premium Photo Experience</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
- <script>
10
- tailwind.config = {
11
- darkMode: 'class',
12
- theme: {
13
- extend: {
14
- colors: {
15
- primary: {
16
- 50: '#f0f9ff',
17
- 100: '#e0f2fe',
18
- 200: '#bae6fd',
19
- 300: '#7dd3fc',
20
- 400: '#38bdf8',
21
- 500: '#0ea5e9',
22
- 600: '#0284c7',
23
- 700: '#0369a1',
24
- 800: '#075985',
25
- 900: '#0c4a6e',
26
- }
27
- },
28
- animation: {
29
- 'fade-in': 'fadeIn 0.3s ease-in-out',
30
- 'flip-in': 'flipIn 0.5s ease-out',
31
- 'zoom-in': 'zoomIn 0.3s ease-out',
32
- },
33
- keyframes: {
34
- fadeIn: {
35
- '0%': { opacity: '0' },
36
- '100%': { opacity: '1' },
37
- },
38
- flipIn: {
39
- '0%': { transform: 'rotateY(90deg)', opacity: '0' },
40
- '100%': { transform: 'rotateY(0)', opacity: '1' },
41
- },
42
- zoomIn: {
43
- '0%': { transform: 'scale(0.95)', opacity: '0' },
44
- '100%': { transform: 'scale(1)', opacity: '1' },
45
- }
46
- }
47
- }
48
- }
49
- }
50
- </script>
51
  <style>
52
- .album-thumbnail {
53
- transition: all 0.3s ease;
54
- transform-style: preserve-3d;
55
  }
56
- .album-thumbnail:hover {
57
- transform: translateY(-5px) scale(1.02);
58
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
59
  }
 
 
60
  .flipbook {
61
  perspective: 2000px;
62
  }
63
- .flipbook-page {
64
  transform-style: preserve-3d;
65
- backface-visibility: hidden;
66
  transition: transform 0.8s;
 
67
  }
68
- .flipbook-page.flipped {
69
  transform: rotateY(-180deg);
70
  }
71
- .flipbook-page-inner {
72
- position: absolute;
73
- width: 100%;
74
- height: 100%;
75
  backface-visibility: hidden;
76
  }
77
- .flipbook-page-back {
78
  transform: rotateY(180deg);
 
79
  }
80
- .modal-overlay {
81
- background-color: rgba(0, 0, 0, 0.85);
82
- backdrop-filter: blur(8px);
83
- }
84
- .image-zoom {
85
- transition: transform 0.3s ease;
86
- }
87
- .image-zoom:hover {
88
- transform: scale(1.03);
89
  }
90
- .login-card {
91
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
92
  }
93
- .private-badge {
94
- position: absolute;
95
- top: 10px;
96
- right: 10px;
97
- z-index: 10;
98
  }
99
  </style>
100
  </head>
101
- <body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 min-h-screen transition-colors duration-300">
102
  <!-- Navigation -->
103
- <nav class="bg-white dark:bg-gray-800 shadow-sm py-4 px-6 sticky top-0 z-50">
104
- <div class="max-w-7xl mx-auto flex justify-between items-center">
105
- <div class="flex items-center space-x-2">
106
- <i class="fas fa-camera-retro text-2xl text-primary-600 dark:text-primary-400"></i>
107
- <span class="text-xl font-bold text-primary-600 dark:text-primary-400">Lumen Gallery</span>
108
- </div>
109
- <div class="flex items-center space-x-6">
110
- <a href="#" class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors">Home</a>
111
- <a href="#" class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors">Public Albums</a>
112
- <a href="#" id="login-link" class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors">Login</a>
113
- <button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors">
114
- <i class="fas fa-moon dark:hidden"></i>
115
- <i class="fas fa-sun hidden dark:block"></i>
116
- </button>
 
 
 
 
117
  </div>
118
  </div>
119
  </nav>
120
 
121
  <!-- Main Content -->
122
- <main class="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
123
- <div class="text-center mb-12">
124
- <h1 class="text-4xl font-bold mb-4">Your Visual Journey</h1>
125
- <p class="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
126
- Explore stunning collections of photography in our premium gallery experience.
127
- </p>
128
- </div>
 
 
129
 
130
- <!-- Album Grid -->
131
- <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8" id="album-grid">
132
- <!-- Albums will be dynamically inserted here -->
133
- </div>
134
- </main>
 
 
 
 
 
 
 
 
 
 
135
 
136
- <!-- Login Modal -->
137
- <div id="login-modal" class="fixed inset-0 z-50 flex items-center justify-center hidden">
138
- <div class="modal-overlay absolute inset-0"></div>
139
- <div class="login-card bg-white dark:bg-gray-800 rounded-xl p-8 relative z-10 w-full max-w-md animate-zoom-in">
140
- <div class="text-center mb-8">
141
- <h2 class="text-2xl font-bold mb-2">Welcome Back</h2>
142
- <p class="text-gray-600 dark:text-gray-400">Sign in to access private albums</p>
143
- </div>
144
- <form id="login-form" class="space-y-6">
145
- <div>
146
- <label for="username" class="block text-sm font-medium mb-1">Username</label>
147
- <input type="text" id="username" class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition">
148
  </div>
149
- <div>
150
- <label for="password" class="block text-sm font-medium mb-1">Password</label>
151
- <input type="password" id="password" class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition">
 
 
 
 
 
 
 
 
 
 
152
  </div>
153
- <div class="flex items-center justify-between">
154
- <div class="flex items-center">
155
- <input type="checkbox" id="remember" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
156
- <label for="remember" class="ml-2 block text-sm">Remember me</label>
 
 
 
 
 
 
 
 
157
  </div>
158
- <a href="#" class="text-sm text-primary-600 dark:text-primary-400 hover:underline">Forgot password?</a>
159
  </div>
160
- <button type="submit" class="w-full bg-primary-600 hover:bg-primary-700 text-white py-2 px-4 rounded-lg transition-colors duration-300">
161
- Sign In
162
- </button>
163
- </form>
164
- <button id="close-login" class="absolute top-4 right-4 p-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
165
- <i class="fas fa-times"></i>
166
- </button>
167
  </div>
168
- </div>
169
-
170
- <!-- Flipbook Viewer -->
171
- <div id="flipbook-viewer" class="fixed inset-0 z-50 flex items-center justify-center hidden">
172
- <div class="modal-overlay absolute inset-0"></div>
173
- <div class="relative z-10 w-full max-w-6xl mx-4">
174
- <button id="close-flipbook" class="absolute -top-12 right-0 p-2 text-white hover:text-primary-400 transition-colors">
175
- <i class="fas fa-times text-2xl"></i>
176
- </button>
177
- <div class="flipbook bg-white dark:bg-gray-800 rounded-xl overflow-hidden shadow-2xl">
178
- <div class="relative h-96 md:h-[32rem]">
179
- <!-- Flipbook pages will be dynamically inserted here -->
180
- </div>
181
- <div class="flex justify-between items-center p-4 bg-gray-100 dark:bg-gray-700">
182
- <button id="prev-page" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
183
- <i class="fas fa-chevron-left text-xl"></i>
184
- </button>
185
- <span id="page-indicator" class="text-sm font-medium">Page 1 of 3</span>
186
- <button id="next-page" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
187
- <i class="fas fa-chevron-right text-xl"></i>
188
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  </div>
190
  </div>
191
  </div>
192
- </div>
193
 
194
- <!-- Image Modal -->
195
- <div id="image-modal" class="fixed inset-0 z-50 flex items-center justify-center hidden">
196
- <div class="modal-overlay absolute inset-0"></div>
197
- <div class="relative z-10 w-full max-w-6xl mx-4">
198
- <div class="flex justify-between items-center mb-4">
199
- <div class="flex space-x-2">
200
- <button id="zoom-out" class="p-2 rounded-full bg-gray-800 bg-opacity-50 text-white hover:bg-opacity-70 transition-colors">
201
- <i class="fas fa-search-minus"></i>
202
- </button>
203
- <button id="download-image" class="p-2 rounded-full bg-gray-800 bg-opacity-50 text-white hover:bg-opacity-70 transition-colors">
204
- <i class="fas fa-download"></i>
205
- </button>
206
- </div>
207
- <button id="close-image" class="p-2 rounded-full bg-gray-800 bg-opacity-50 text-white hover:bg-opacity-70 transition-colors">
208
- <i class="fas fa-times"></i>
209
  </button>
210
- </div>
211
- <div class="bg-white dark:bg-gray-800 rounded-xl overflow-hidden shadow-2xl max-h-[90vh] flex items-center justify-center">
212
- <img id="modal-image" src="" alt="" class="max-w-full max-h-[85vh] object-contain">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  </div>
214
  </div>
215
- </div>
216
-
217
- <!-- Upload Button (for admin) -->
218
- <div id="upload-section" class="fixed bottom-8 right-8 hidden">
219
- <button id="upload-btn" class="p-4 bg-primary-600 hover:bg-primary-700 text-white rounded-full shadow-lg transition-colors duration-300">
220
- <i class="fas fa-plus text-xl"></i>
221
- </button>
222
- </div>
223
 
224
- <script>
225
- // Sample album data
226
- const albums = [
227
- {
228
- id: 1,
229
- title: "Mountain Escapes",
230
- thumbnail: "https://source.unsplash.com/random/600x400/?mountain",
231
- photoCount: 12,
232
- isPublic: true,
233
- photos: [
234
- "https://source.unsplash.com/random/800x600/?mountain,1",
235
- "https://source.unsplash.com/random/800x600/?mountain,2",
236
- "https://source.unsplash.com/random/800x600/?mountain,3",
237
- "https://source.unsplash.com/random/800x600/?mountain,4",
238
- "https://source.unsplash.com/random/800x600/?mountain,5",
239
- "https://source.unsplash.com/random/800x600/?mountain,6"
240
- ]
241
- },
242
- {
243
- id: 2,
244
- title: "Urban Landscapes",
245
- thumbnail: "https://source.unsplash.com/random/600x400/?city",
246
- photoCount: 8,
247
- isPublic: true,
248
- photos: [
249
- "https://source.unsplash.com/random/800x600/?city,1",
250
- "https://source.unsplash.com/random/800x600/?city,2",
251
- "https://source.unsplash.com/random/800x600/?city,3",
252
- "https://source.unsplash.com/random/800x600/?city,4"
253
- ]
254
- },
255
- {
256
- id: 3,
257
- title: "Ocean Wonders",
258
- thumbnail: "https://source.unsplash.com/random/600x400/?ocean",
259
- photoCount: 15,
260
- isPublic: true,
261
- photos: [
262
- "https://source.unsplash.com/random/800x600/?ocean,1",
263
- "https://source.unsplash.com/random/800x600/?ocean,2",
264
- "https://source.unsplash.com/random/800x600/?ocean,3",
265
- "https://source.unsplash.com/random/800x600/?ocean,4",
266
- "https://source.unsplash.com/random/800x600/?ocean,5",
267
- "https://source.unsplash.com/random/800x600/?ocean,6",
268
- "https://source.unsplash.com/random/800x600/?ocean,7"
269
- ]
270
- },
271
- {
272
- id: 4,
273
- title: "Family Memories",
274
- thumbnail: "https://source.unsplash.com/random/600x400/?family",
275
- photoCount: 20,
276
- isPublic: false,
277
- photos: [
278
- "https://source.unsplash.com/random/800x600/?family,1",
279
- "https://source.unsplash.com/random/800x600/?family,2",
280
- "https://source.unsplash.com/random/800x600/?family,3",
281
- "https://source.unsplash.com/random/800x600/?family,4",
282
- "https://source.unsplash.com/random/800x600/?family,5",
283
- "https://source.unsplash.com/random/800x600/?family,6",
284
- "https://source.unsplash.com/random/800x600/?family,7",
285
- "https://source.unsplash.com/random/800x600/?family,8",
286
- "https://source.unsplash.com/random/800x600/?family,9",
287
- "https://source.unsplash.com/random/800x600/?family,10"
288
- ]
289
- },
290
- {
291
- id: 5,
292
- title: "Wildlife Adventures",
293
- thumbnail: "https://source.unsplash.com/random/600x400/?wildlife",
294
- photoCount: 18,
295
- isPublic: true,
296
- photos: [
297
- "https://source.unsplash.com/random/800x600/?wildlife,1",
298
- "https://source.unsplash.com/random/800x600/?wildlife,2",
299
- "https://source.unsplash.com/random/800x600/?wildlife,3",
300
- "https://source.unsplash.com/random/800x600/?wildlife,4",
301
- "https://source.unsplash.com/random/800x600/?wildlife,5",
302
- "https://source.unsplash.com/random/800x600/?wildlife,6",
303
- "https://source.unsplash.com/random/800x600/?wildlife,7",
304
- "https://source.unsplash.com/random/800x600/?wildlife,8",
305
- "https://source.unsplash.com/random/800x600/?wildlife,9"
306
- ]
307
- },
308
- {
309
- id: 6,
310
- title: "Architectural Marvels",
311
- thumbnail: "https://source.unsplash.com/random/600x400/?architecture",
312
- photoCount: 14,
313
- isPublic: true,
314
- photos: [
315
- "https://source.unsplash.com/random/800x600/?architecture,1",
316
- "https://source.unsplash.com/random/800x600/?architecture,2",
317
- "https://source.unsplash.com/random/800x600/?architecture,3",
318
- "https://source.unsplash.com/random/800x600/?architecture,4",
319
- "https://source.unsplash.com/random/800x600/?architecture,5",
320
- "https://source.unsplash.com/random/800x600/?architecture,6",
321
- "https://source.unsplash.com/random/800x600/?architecture,7"
322
- ]
323
- }
324
- ];
325
-
326
- // State variables
327
- let currentUser = null;
328
- let currentAlbum = null;
329
- let currentPage = 0;
330
- let totalPages = 0;
331
- let isLoggedIn = false;
332
-
333
- // DOM elements
334
- const albumGrid = document.getElementById('album-grid');
335
- const loginModal = document.getElementById('login-modal');
336
- const loginLink = document.getElementById('login-link');
337
- const closeLogin = document.getElementById('close-login');
338
- const loginForm = document.getElementById('login-form');
339
- const flipbookViewer = document.getElementById('flipbook-viewer');
340
- const closeFlipbook = document.getElementById('close-flipbook');
341
- const prevPageBtn = document.getElementById('prev-page');
342
- const nextPageBtn = document.getElementById('next-page');
343
- const pageIndicator = document.getElementById('page-indicator');
344
- const imageModal = document.getElementById('image-modal');
345
- const modalImage = document.getElementById('modal-image');
346
- const closeImage = document.getElementById('close-image');
347
- const zoomOutBtn = document.getElementById('zoom-out');
348
- const downloadImageBtn = document.getElementById('download-image');
349
- const themeToggle = document.getElementById('theme-toggle');
350
- const uploadSection = document.getElementById('upload-section');
351
- const uploadBtn = document.getElementById('upload-btn');
352
-
353
- // Initialize the gallery
354
- function initGallery() {
355
- renderAlbums();
356
- setupEventListeners();
357
- checkDarkModePreference();
358
- }
359
 
360
- // Render albums to the grid
361
- function renderAlbums() {
362
- albumGrid.innerHTML = '';
363
-
364
- const filteredAlbums = albums.filter(album => {
365
- return album.isPublic || isLoggedIn;
366
- });
367
-
368
- filteredAlbums.forEach(album => {
369
- const albumElement = document.createElement('div');
370
- albumElement.className = 'album-thumbnail bg-white dark:bg-gray-800 rounded-xl overflow-hidden shadow-lg cursor-pointer relative';
371
- albumElement.innerHTML = `
372
- <div class="relative h-48 overflow-hidden">
373
- <img src="${album.thumbnail}" alt="${album.title}" class="w-full h-full object-cover transition-transform duration-300 hover:scale-105">
374
- ${!album.isPublic ? '<span class="private-badge bg-primary-600 text-white text-xs px-2 py-1 rounded-full">Private</span>' : ''}
375
  </div>
376
- <div class="p-4">
377
- <h3 class="font-semibold text-lg mb-1">${album.title}</h3>
378
- <p class="text-sm text-gray-600 dark:text-gray-400">${album.photoCount} photos</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  </div>
380
- `;
381
- albumElement.addEventListener('click', () => openAlbum(album));
382
- albumGrid.appendChild(albumElement);
383
- });
384
- }
385
 
386
- // Open an album in the flipbook viewer
387
- function openAlbum(album) {
388
- if (!album.isPublic && !isLoggedIn) {
389
- openLoginModal();
390
- return;
391
- }
392
-
393
- currentAlbum = album;
394
- currentPage = 0;
395
- totalPages = Math.ceil(album.photos.length / 2);
396
 
397
- renderFlipbook();
398
- flipbookViewer.classList.remove('hidden');
399
- document.body.style.overflow = 'hidden';
400
- }
401
 
402
- // Render the flipbook pages
403
- function renderFlipbook() {
404
- const flipbookContainer = flipbookViewer.querySelector('.flipbook > div');
405
- flipbookContainer.innerHTML = '';
 
 
 
 
406
 
407
- // Create pages with 2 photos each
408
- for (let i = 0; i < currentAlbum.photos.length; i += 2) {
409
- const page = document.createElement('div');
410
- page.className = 'flipbook-page absolute inset-0 w-full h-full';
411
- page.style.transform = i === 0 ? 'rotateY(0)' : 'rotateY(180deg)';
412
- page.style.display = i === 0 ? 'block' : 'none';
413
-
414
- const front = document.createElement('div');
415
- front.className = 'flipbook-page-inner flex';
416
-
417
- const photo1 = document.createElement('div');
418
- photo1.className = 'w-1/2 p-4 flex items-center justify-center';
419
- photo1.innerHTML = `<img src="${currentAlbum.photos[i]}" alt="Photo ${i+1}" class="max-h-full max-w-full rounded-lg shadow-md cursor-pointer image-zoom">`;
420
- photo1.querySelector('img').addEventListener('click', () => openImageModal(currentAlbum.photos[i]));
421
-
422
- const photo2 = document.createElement('div');
423
- photo2.className = 'w-1/2 p-4 flex items-center justify-center';
424
-
425
- if (i + 1 < currentAlbum.photos.length) {
426
- photo2.innerHTML = `<img src="${currentAlbum.photos[i+1]}" alt="Photo ${i+2}" class="max-h-full max-w-full rounded-lg shadow-md cursor-pointer image-zoom">`;
427
- photo2.querySelector('img').addEventListener('click', () => openImageModal(currentAlbum.photos[i+1]));
428
- } else {
429
- photo2.innerHTML = '<div class="w-full h-full flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded-lg"><p class="text-gray-500 dark:text-gray-400">No photo</p></div>';
430
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
- front.appendChild(photo1);
433
- front.appendChild(photo2);
434
- page.appendChild(front);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
- // For a real flipbook effect, we'd have back sides too, but we'll keep it simple
437
- flipbookContainer.appendChild(page);
438
- }
439
-
440
- updatePageControls();
441
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
- // Update flipbook page controls
444
- function updatePageControls() {
445
- pageIndicator.textContent = `Page ${currentPage + 1} of ${totalPages}`;
446
- prevPageBtn.disabled = currentPage === 0;
447
- nextPageBtn.disabled = currentPage === totalPages - 1;
448
-
449
- // Show/hide pages
450
- const pages = flipbookViewer.querySelectorAll('.flipbook-page');
451
- pages.forEach((page, index) => {
452
- page.style.display = index === currentPage ? 'block' : 'none';
453
- });
454
- }
455
 
456
- // Open image modal
457
- function openImageModal(imageUrl) {
458
- modalImage.src = imageUrl;
459
- modalImage.alt = "Enlarged photo";
460
- imageModal.classList.remove('hidden');
461
- document.body.style.overflow = 'hidden';
 
462
  }
463
 
464
- // Open login modal
465
- function openLoginModal() {
 
 
 
 
 
 
 
 
 
466
  loginModal.classList.remove('hidden');
467
- document.body.style.overflow = 'hidden';
468
- }
469
 
470
- // Close login modal
471
- function closeLoginModal() {
472
  loginModal.classList.add('hidden');
473
- document.body.style.overflow = '';
474
- }
475
 
476
- // Close flipbook viewer
477
- function closeFlipbookViewer() {
478
- flipbookViewer.classList.add('hidden');
479
- document.body.style.overflow = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  }
481
 
482
- // Close image modal
483
- function closeImageModal() {
484
- imageModal.classList.add('hidden');
485
- document.body.style.overflow = '';
486
  }
487
 
488
- // Setup event listeners
489
- function setupEventListeners() {
490
- // Login link
491
- loginLink.addEventListener('click', (e) => {
492
- e.preventDefault();
493
- openLoginModal();
494
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
 
496
- // Close login modal
497
- closeLogin.addEventListener('click', closeLoginModal);
 
 
 
 
 
498
 
499
- // Login form submission
500
- loginForm.addEventListener('submit', (e) => {
501
- e.preventDefault();
502
- const username = document.getElementById('username').value;
503
- const password = document.getElementById('password').value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
 
505
- // Simple authentication (in a real app, this would be a server call)
506
- if (username === 'admin' && password === 'password') {
507
- isLoggedIn = true;
508
- currentUser = { username: 'admin' };
509
- closeLoginModal();
510
- renderAlbums();
511
- uploadSection.classList.remove('hidden');
512
- } else {
513
- alert('Invalid credentials. Try admin/password');
514
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  });
 
 
 
 
 
516
 
517
- // Close flipbook
518
- closeFlipbook.addEventListener('click', closeFlipbookViewer);
 
 
519
 
520
- // Flipbook navigation
521
- prevPageBtn.addEventListener('click', () => {
522
- if (currentPage > 0) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  currentPage--;
524
- updatePageControls();
 
525
  }
526
  });
527
 
528
- nextPageBtn.addEventListener('click', () => {
529
- if (currentPage < totalPages - 1) {
530
  currentPage++;
531
- updatePageControls();
 
532
  }
533
  });
534
 
535
- // Close image modal
536
- closeImage.addEventListener('click', closeImageModal);
537
-
538
- // Zoom out button (placeholder functionality)
539
- zoomOutBtn.addEventListener('click', () => {
540
- modalImage.style.transform = 'scale(1)';
541
- });
542
-
543
- // Download button (placeholder functionality)
544
- downloadImageBtn.addEventListener('click', () => {
545
- alert('Download functionality would be implemented here');
546
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
 
548
- // Theme toggle
549
- themeToggle.addEventListener('click', () => {
550
- document.documentElement.classList.toggle('dark');
551
- localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
552
- });
553
 
554
- // Upload button (placeholder functionality)
555
- uploadBtn.addEventListener('click', () => {
556
- alert('Upload functionality would be implemented here');
557
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  }
559
-
560
- // Check user's dark mode preference
561
- function checkDarkModePreference() {
562
- if (localStorage.getItem('darkMode') === 'true' ||
563
- (!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
564
- document.documentElement.classList.add('dark');
565
  } else {
566
- document.documentElement.classList.remove('dark');
567
  }
568
- }
569
-
570
- // Initialize the gallery when the DOM is loaded
571
- document.addEventListener('DOMContentLoaded', initGallery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  </script>
573
  <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>
574
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Cinematic Photo Gallery</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://accounts.google.com/gsi/client" async defer></script>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  <style>
11
+ @keyframes fadeIn {
12
+ from { opacity: 0; }
13
+ to { opacity: 1; }
14
  }
15
+ @keyframes slideUp {
16
+ from { transform: translateY(20px); opacity: 0; }
17
+ to { transform: translateY(0); opacity: 1; }
18
  }
19
+ .animate-fade-in { animation: fadeIn 0.3s ease-out forwards; }
20
+ .animate-slide-up { animation: slideUp 0.3s ease-out forwards; }
21
  .flipbook {
22
  perspective: 2000px;
23
  }
24
+ .page {
25
  transform-style: preserve-3d;
26
+ transform-origin: left center;
27
  transition: transform 0.8s;
28
+ backface-visibility: hidden;
29
  }
30
+ .page.flipped {
31
  transform: rotateY(-180deg);
32
  }
33
+ .page-content {
 
 
 
34
  backface-visibility: hidden;
35
  }
36
+ .page-back {
37
  transform: rotateY(180deg);
38
+ backface-visibility: hidden;
39
  }
40
+ .blur-bg {
41
+ backdrop-filter: blur(10px);
42
+ background-color: rgba(0, 0, 0, 0.7);
 
 
 
 
 
 
43
  }
44
+ .album-card:hover .album-overlay {
45
+ opacity: 1;
46
  }
47
+ .drag-active {
48
+ border-color: #3b82f6 !important;
49
+ background-color: rgba(59, 130, 246, 0.1) !important;
 
 
50
  }
51
  </style>
52
  </head>
53
+ <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">
54
  <!-- Navigation -->
55
+ <nav class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
56
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
57
+ <div class="flex justify-between h-16">
58
+ <div class="flex items-center">
59
+ <a href="#" class="text-xl font-bold text-indigo-600 dark:text-indigo-400">PhotoGallery</a>
60
+ </div>
61
+ <div class="flex items-center space-x-4">
62
+ <button id="darkModeToggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700">
63
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 hidden dark:block" viewBox="0 0 20 20" fill="currentColor">
64
+ <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" />
65
+ </svg>
66
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 block dark:hidden" viewBox="0 0 20 20" fill="currentColor">
67
+ <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
68
+ </svg>
69
+ </button>
70
+ <button id="loginBtn" class="px-4 py-2 rounded-md bg-indigo-600 text-white hover:bg-indigo-700 transition">Login</button>
71
+ <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>
72
+ </div>
73
  </div>
74
  </div>
75
  </nav>
76
 
77
  <!-- Main Content -->
78
+ <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
79
+ <!-- Homepage -->
80
+ <div id="homepage">
81
+ <div class="flex justify-between items-center mb-8">
82
+ <h1 class="text-3xl font-bold">Photo Albums</h1>
83
+ <div class="flex space-x-2">
84
+ <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>
85
+ </div>
86
+ </div>
87
 
88
+ <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
89
+ <!-- Album Cards -->
90
+ <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">
91
+ <div class="relative aspect-square">
92
+ <img src="https://source.unsplash.com/random/600x600/?nature,1" alt="Nature" class="w-full h-full object-cover">
93
+ <div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
94
+ <span class="text-white font-medium">View Album</span>
95
+ </div>
96
+ </div>
97
+ <div class="p-4">
98
+ <h3 class="font-semibold">Nature</h3>
99
+ <p class="text-sm text-gray-500 dark:text-gray-400">24 photos</p>
100
+ <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>
101
+ </div>
102
+ </div>
103
 
104
+ <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">
105
+ <div class="relative aspect-square">
106
+ <img src="https://source.unsplash.com/random/600x600/?travel,1" alt="Travel" class="w-full h-full object-cover">
107
+ <div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
108
+ <span class="text-white font-medium">View Album</span>
109
+ </div>
110
+ </div>
111
+ <div class="p-4">
112
+ <h3 class="font-semibold">Travel</h3>
113
+ <p class="text-sm text-gray-500 dark:text-gray-400">18 photos</p>
114
+ <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>
115
+ </div>
116
  </div>
117
+
118
+ <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">
119
+ <div class="relative aspect-square">
120
+ <img src="https://source.unsplash.com/random/600x600/?portrait,1" alt="Portraits" class="w-full h-full object-cover">
121
+ <div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
122
+ <span class="text-white font-medium">View Album</span>
123
+ </div>
124
+ </div>
125
+ <div class="p-4">
126
+ <h3 class="font-semibold">Portraits</h3>
127
+ <p class="text-sm text-gray-500 dark:text-gray-400">12 photos</p>
128
+ <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>
129
+ </div>
130
  </div>
131
+
132
+ <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">
133
+ <div class="relative aspect-square">
134
+ <img src="https://source.unsplash.com/random/600x600/?architecture,1" alt="Architecture" class="w-full h-full object-cover">
135
+ <div class="album-overlay absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 transition-opacity">
136
+ <span class="text-white font-medium">View Album</span>
137
+ </div>
138
+ </div>
139
+ <div class="p-4">
140
+ <h3 class="font-semibold">Architecture</h3>
141
+ <p class="text-sm text-gray-500 dark:text-gray-400">32 photos</p>
142
+ <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>
143
  </div>
 
144
  </div>
145
+ </div>
 
 
 
 
 
 
146
  </div>
147
+
148
+ <!-- Album Viewer -->
149
+ <div id="albumViewer" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
150
+ <div class="relative w-full max-w-4xl">
151
+ <button id="closeAlbum" class="absolute -top-12 right-0 text-white hover:text-gray-300">
152
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
153
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
154
+ </svg>
155
+ </button>
156
+
157
+ <div class="flipbook bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden">
158
+ <div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
159
+ <h2 id="albumTitle" class="text-xl font-bold">Album Title</h2>
160
+ <div class="flex space-x-2">
161
+ <span id="albumVisibility" class="px-2 py-1 rounded-full text-xs font-medium">Public</span>
162
+ <span id="albumPhotoCount" class="text-gray-500 dark:text-gray-400 text-sm">24 photos</span>
163
+ </div>
164
+ </div>
165
+
166
+ <div class="relative h-96 md:h-[32rem] overflow-hidden">
167
+ <div id="flipbookPages" class="relative h-full w-full flex">
168
+ <!-- Pages will be inserted here by JavaScript -->
169
+ </div>
170
+
171
+ <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">
172
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
173
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
174
+ </svg>
175
+ </button>
176
+ <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">
177
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
178
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
179
+ </svg>
180
+ </button>
181
+ </div>
182
+
183
+ <div class="flex items-center justify-between p-4 border-t border-gray-200 dark:border-gray-700">
184
+ <span id="currentPage" class="text-sm text-gray-500 dark:text-gray-400">Page 1 of 5</span>
185
+ <div class="flex space-x-2">
186
+ <button id="zoomOutBtn" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
187
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
188
+ <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" />
189
+ </svg>
190
+ </button>
191
+ <button id="downloadBtn" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
192
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
193
+ <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" />
194
+ </svg>
195
+ </button>
196
+ </div>
197
+ </div>
198
  </div>
199
  </div>
200
  </div>
 
201
 
202
+ <!-- Image Modal -->
203
+ <div id="imageModal" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
204
+ <div class="relative max-w-5xl max-h-screen">
205
+ <button id="closeModal" class="absolute -top-12 right-0 text-white hover:text-gray-300">
206
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
207
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
208
+ </svg>
 
 
 
 
 
 
 
 
209
  </button>
210
+
211
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden">
212
+ <div id="modalImageContainer" class="max-h-[80vh] overflow-auto">
213
+ <img id="modalImage" src="" alt="" class="w-full h-auto">
214
+ </div>
215
+
216
+ <div class="flex items-center justify-between p-4 border-t border-gray-200 dark:border-gray-700">
217
+ <h3 id="modalImageTitle" class="font-medium">Image Title</h3>
218
+ <div class="flex space-x-2">
219
+ <button id="modalZoomOut" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
220
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
221
+ <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" />
222
+ </svg>
223
+ </button>
224
+ <button id="modalDownload" class="p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
225
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
226
+ <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" />
227
+ </svg>
228
+ </button>
229
+ </div>
230
+ </div>
231
+ </div>
232
  </div>
233
  </div>
 
 
 
 
 
 
 
 
234
 
235
+ <!-- Login Modal -->
236
+ <div id="loginModal" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
237
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden w-full max-w-md animate-slide-up">
238
+ <div class="p-6">
239
+ <div class="flex justify-between items-center mb-6">
240
+ <h2 class="text-2xl font-bold">Login</h2>
241
+ <button id="closeLoginModal" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
242
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
243
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
244
+ </svg>
245
+ </button>
246
+ </div>
247
+
248
+ <div class="space-y-4">
249
+ <div id="g_id_onload"
250
+ data-client_id="YOUR_GOOGLE_CLIENT_ID"
251
+ data-context="signin"
252
+ data-ux_mode="popup"
253
+ data-callback="handleGoogleSignIn"
254
+ data-auto_prompt="false">
255
+ </div>
256
+ <div class="g_id_signin"
257
+ data-type="standard"
258
+ data-shape="rectangular"
259
+ data-theme="outline"
260
+ data-text="signin_with"
261
+ data-size="large"
262
+ data-logo_alignment="left">
263
+ </div>
264
+
265
+ <div class="flex items-center my-4">
266
+ <div class="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
267
+ <span class="mx-4 text-gray-500 dark:text-gray-400">or</span>
268
+ <div class="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
269
+ </div>
270
+
271
+ <div>
272
+ <label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
273
+ <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">
274
+ </div>
275
+
276
+ <div>
277
+ <label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Password</label>
278
+ <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">
279
+ </div>
280
+
281
+ <div class="flex items-center justify-between">
282
+ <div class="flex items-center">
283
+ <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">
284
+ <label for="remember-me" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Remember me</label>
285
+ </div>
286
+ <a href="#" class="text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300">Forgot password?</a>
287
+ </div>
288
+
289
+ <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">
290
+ Sign in
291
+ </button>
292
+
293
+ <div class="text-center text-sm text-gray-500 dark:text-gray-400">
294
+ 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>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
 
301
+ <!-- Signup Modal -->
302
+ <div id="signupModal" class="hidden fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center p-4">
303
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden w-full max-w-md animate-slide-up">
304
+ <div class="p-6">
305
+ <div class="flex justify-between items-center mb-6">
306
+ <h2 class="text-2xl font-bold">Create Account</h2>
307
+ <button id="closeSignupModal" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
308
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
309
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
310
+ </svg>
311
+ </button>
 
 
 
 
312
  </div>
313
+
314
+ <div class="space-y-4">
315
+ <div>
316
+ <label for="signupName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Full Name</label>
317
+ <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">
318
+ </div>
319
+
320
+ <div>
321
+ <label for="signupEmail" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
322
+ <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">
323
+ </div>
324
+
325
+ <div>
326
+ <label for="signupPassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Password</label>
327
+ <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">
328
+ </div>
329
+
330
+ <div>
331
+ <label for="signupConfirmPassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Confirm Password</label>
332
+ <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">
333
+ </div>
334
+
335
+ <div class="flex items-center">
336
+ <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">
337
+ <label for="terms" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">
338
+ 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>
339
+ </label>
340
+ </div>
341
+
342
+ <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">
343
+ Create Account
344
+ </button>
345
+
346
+ <div class="text-center text-sm text-gray-500 dark:text-gray-400">
347
+ 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>
348
+ </div>
349
  </div>
350
+ </div>
351
+ </div>
352
+ </div>
 
 
353
 
354
+ <!-- Recently View -->
355
+ <div id="recentlyView" class="hidden">
356
+ <div class="flex justify-between items-center mb-8">
357
+ <h1 class="text-3xl font-bold">Recently Viewed</h1>
358
+ <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>
359
+ </div>
 
 
 
 
360
 
361
+ <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
362
+ <!-- Recently viewed photos will be inserted here by JavaScript -->
363
+ </div>
364
+ </div>
365
 
366
+ <!-- Admin Dashboard -->
367
+ <div id="adminDashboard" class="hidden">
368
+ <div class="flex justify-between items-center mb-8">
369
+ <h1 class="text-3xl font-bold">Admin Dashboard</h1>
370
+ <div class="flex space-x-2">
371
+ <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>
372
+ </div>
373
+ </div>
374
 
375
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
376
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
377
+ <h2 class="text-xl font-semibold mb-4">Upload Photos</h2>
378
+ <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">
379
+ <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">
380
+ <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" />
381
+ </svg>
382
+ <p class="mt-2 text-sm text-gray-500 dark:text-gray-400">Drag and drop files here</p>
383
+ <p class="text-xs text-gray-400 dark:text-gray-500">or</p>
384
+ <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>
385
+ <input type="file" id="fileInput" class="hidden" multiple accept="image/*">
386
+ </div>
387
+
388
+ <div id="uploadProgress" class="mt-4 hidden">
389
+ <div class="flex justify-between mb-1">
390
+ <span class="text-sm font-medium">Uploading...</span>
391
+ <span id="uploadPercentage" class="text-sm font-medium">0%</span>
392
+ </div>
393
+ <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
394
+ <div id="progressBar" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div>
395
+ </div>
396
+ </div>
397
+
398
+ <div id="uploadedFiles" class="mt-4 space-y-2 hidden">
399
+ <h3 class="font-medium">Uploaded Files</h3>
400
+ <div id="uploadedFilesList" class="space-y-2">
401
+ <!-- Uploaded files will be listed here -->
402
+ </div>
403
+ </div>
404
+
405
+ <div class="mt-6">
406
+ <label for="albumSelect" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Add to Album</label>
407
+ <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">
408
+ <option value="">Select an album</option>
409
+ <option value="1">Nature</option>
410
+ <option value="2">Travel</option>
411
+ <option value="3">Portraits</option>
412
+ <option value="4">Architecture</option>
413
+ <option value="new">Create New Album</option>
414
+ </select>
415
+
416
+ <div id="newAlbumFields" class="mt-2 hidden">
417
+ <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">
418
+ <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">
419
+ <option value="public">Public</option>
420
+ <option value="family">Family</option>
421
+ <option value="private">Private</option>
422
+ </select>
423
+ </div>
424
+
425
+ <div class="mt-4">
426
+ <label for="photoTitles" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Photo Titles (comma separated)</label>
427
+ <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">
428
+ </div>
429
+
430
+ <div class="mt-4">
431
+ <label for="photoTags" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Tags (comma separated)</label>
432
+ <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">
433
+ </div>
434
+
435
+ <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">
436
+ Save Photos
437
+ </button>
438
+ </div>
439
+ </div>
440
 
441
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
442
+ <h2 class="text-xl font-semibold mb-4">User Management</h2>
443
+
444
+ <div class="overflow-x-auto">
445
+ <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
446
+ <thead class="bg-gray-50 dark:bg-gray-700">
447
+ <tr>
448
+ <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>
449
+ <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>
450
+ <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>
451
+ <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>
452
+ </tr>
453
+ </thead>
454
+ <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
455
+ <tr>
456
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">John Doe</td>
457
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">[email protected]</td>
458
+ <td class="px-6 py-4 whitespace-nowrap">
459
+ <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">
460
+ <option>Admin</option>
461
+ <option selected>Viewer</option>
462
+ </select>
463
+ </td>
464
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
465
+ <button class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">Lock</button>
466
+ </td>
467
+ </tr>
468
+ <tr>
469
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">Jane Smith</td>
470
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">[email protected]</td>
471
+ <td class="px-6 py-4 whitespace-nowrap">
472
+ <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">
473
+ <option selected>Admin</option>
474
+ <option>Viewer</option>
475
+ </select>
476
+ </td>
477
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
478
+ <button class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">Lock</button>
479
+ </td>
480
+ </tr>
481
+ <tr>
482
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">Bob Johnson</td>
483
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">[email protected]</td>
484
+ <td class="px-6 py-4 whitespace-nowrap">
485
+ <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">
486
+ <option>Admin</option>
487
+ <option selected>Viewer</option>
488
+ </select>
489
+ </td>
490
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
491
+ <button class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">Lock</button>
492
+ </td>
493
+ </tr>
494
+ </tbody>
495
+ </table>
496
+ </div>
497
+
498
+ <div class="mt-4">
499
+ <h3 class="font-medium mb-2">Add New User</h3>
500
+ <div class="flex space-x-2">
501
+ <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">
502
+ <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">
503
+ <option>Admin</option>
504
+ <option>Viewer</option>
505
+ </select>
506
+ <button class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Add</button>
507
+ </div>
508
+ </div>
509
+ </div>
510
 
511
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
512
+ <h2 class="text-xl font-semibold mb-4">Album Management</h2>
513
+
514
+ <div class="space-y-4">
515
+ <div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
516
+ <div>
517
+ <h3 class="font-medium">Nature</h3>
518
+ <p class="text-sm text-gray-500 dark:text-gray-400">24 photos</p>
519
+ </div>
520
+ <div class="flex items-center space-x-2">
521
+ <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">
522
+ <option value="public" selected>Public</option>
523
+ <option value="family">Family</option>
524
+ <option value="private">Private</option>
525
+ </select>
526
+ <button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
527
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
528
+ <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" />
529
+ </svg>
530
+ </button>
531
+ </div>
532
+ </div>
533
+
534
+ <div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
535
+ <div>
536
+ <h3 class="font-medium">Travel</h3>
537
+ <p class="text-sm text-gray-500 dark:text-gray-400">18 photos</p>
538
+ </div>
539
+ <div class="flex items-center space-x-2">
540
+ <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">
541
+ <option value="public">Public</option>
542
+ <option value="family" selected>Family</option>
543
+ <option value="private">Private</option>
544
+ </select>
545
+ <button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
546
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
547
+ <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" />
548
+ </svg>
549
+ </button>
550
+ </div>
551
+ </div>
552
+
553
+ <div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
554
+ <div>
555
+ <h3 class="font-medium">Portraits</h3>
556
+ <p class="text-sm text-gray-500 dark:text-gray-400">12 photos</p>
557
+ </div>
558
+ <div class="flex items-center space-x-2">
559
+ <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">
560
+ <option value="public">Public</option>
561
+ <option value="family">Family</option>
562
+ <option value="private" selected>Private</option>
563
+ </select>
564
+ <button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
565
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
566
+ <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" />
567
+ </svg>
568
+ </button>
569
+ </div>
570
+ </div>
571
+
572
+ <div class="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
573
+ <div>
574
+ <h3 class="font-medium">Architecture</h3>
575
+ <p class="text-sm text-gray-500 dark:text-gray-400">32 photos</p>
576
+ </div>
577
+ <div class="flex items-center space-x-2">
578
+ <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">
579
+ <option value="public" selected>Public</option>
580
+ <option value="family">Family</option>
581
+ <option value="private">Private</option>
582
+ </select>
583
+ <button class="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
584
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
585
+ <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" />
586
+ </svg>
587
+ </button>
588
+ </div>
589
+ </div>
590
+ </div>
591
+
592
+ <div class="mt-4">
593
+ <h3 class="font-medium mb-2">Create New Album</h3>
594
+ <div class="flex space-x-2">
595
+ <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">
596
+ <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">
597
+ <option value="public">Public</option>
598
+ <option value="family">Family</option>
599
+ <option value="private">Private</option>
600
+ </select>
601
+ <button class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Create</button>
602
+ </div>
603
+ </div>
604
+ </div>
605
+ </div>
606
+ </div>
607
+ </main>
608
 
609
+ <script>
610
+ // Dark mode toggle
611
+ document.getElementById('darkModeToggle').addEventListener('click', function() {
612
+ document.documentElement.classList.toggle('dark');
613
+ localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
614
+ });
 
 
 
 
 
 
615
 
616
+ // Check for saved dark mode preference
617
+ if (localStorage.getItem('darkMode') === 'true') {
618
+ document.documentElement.classList.add('dark');
619
+ } else if (localStorage.getItem('darkMode') === 'false') {
620
+ document.documentElement.classList.remove('dark');
621
+ } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
622
+ document.documentElement.classList.add('dark');
623
  }
624
 
625
+ // Modal management
626
+ const loginModal = document.getElementById('loginModal');
627
+ const signupModal = document.getElementById('signupModal');
628
+ const albumViewer = document.getElementById('albumViewer');
629
+ const imageModal = document.getElementById('imageModal');
630
+ const homepage = document.getElementById('homepage');
631
+ const recentlyView = document.getElementById('recentlyView');
632
+ const adminDashboard = document.getElementById('adminDashboard');
633
+
634
+ // Login/Signup modal toggles
635
+ document.getElementById('loginBtn').addEventListener('click', () => {
636
  loginModal.classList.remove('hidden');
637
+ loginModal.classList.add('animate-fade-in');
638
+ });
639
 
640
+ document.getElementById('closeLoginModal').addEventListener('click', () => {
 
641
  loginModal.classList.add('hidden');
642
+ });
 
643
 
644
+ document.getElementById('signupLink').addEventListener('click', (e) => {
645
+ e.preventDefault();
646
+ loginModal.classList.add('hidden');
647
+ signupModal.classList.remove('hidden');
648
+ signupModal.classList.add('animate-fade-in');
649
+ });
650
+
651
+ document.getElementById('closeSignupModal').addEventListener('click', () => {
652
+ signupModal.classList.add('hidden');
653
+ });
654
+
655
+ document.getElementById('loginLink').addEventListener('click', (e) => {
656
+ e.preventDefault();
657
+ signupModal.classList.add('hidden');
658
+ loginModal.classList.remove('hidden');
659
+ loginModal.classList.add('animate-fade-in');
660
+ });
661
+
662
+ // Mock login functionality
663
+ document.getElementById('emailLoginBtn').addEventListener('click', () => {
664
+ // In a real app, you would validate credentials here
665
+ localStorage.setItem('isLoggedIn', 'true');
666
+ localStorage.setItem('isAdmin', 'true'); // For demo purposes, always log in as admin
667
+ document.getElementById('loginBtn').classList.add('hidden');
668
+ document.getElementById('adminBtn').classList.remove('hidden');
669
+ loginModal.classList.add('hidden');
670
+ });
671
+
672
+ // Mock signup functionality
673
+ document.getElementById('signupBtn').addEventListener('click', () => {
674
+ // In a real app, you would create a new user account here
675
+ alert('Account created successfully! Please log in.');
676
+ signupModal.classList.add('hidden');
677
+ loginModal.classList.remove('hidden');
678
+ loginModal.classList.add('animate-fade-in');
679
+ });
680
+
681
+ // Google Sign-In callback
682
+ function handleGoogleSignIn(response) {
683
+ console.log('Google sign-in response:', response);
684
+ // In a real app, you would verify the credential and log the user in
685
+ localStorage.setItem('isLoggedIn', 'true');
686
+ localStorage.setItem('isAdmin', 'true'); // For demo purposes, always log in as admin
687
+ document.getElementById('loginBtn').classList.add('hidden');
688
+ document.getElementById('adminBtn').classList.remove('hidden');
689
+ loginModal.classList.add('hidden');
690
  }
691
 
692
+ // Check if user is logged in on page load
693
+ if (localStorage.getItem('isLoggedIn') === 'true') {
694
+ document.getElementById('loginBtn').classList.add('hidden');
695
+ document.getElementById('adminBtn').classList.remove('hidden');
696
  }
697
 
698
+ // Admin dashboard toggle
699
+ document.getElementById('adminBtn').addEventListener('click', () => {
700
+ homepage.classList.add('hidden');
701
+ recentlyView.classList.add('hidden');
702
+ adminDashboard.classList.remove('hidden');
703
+ });
704
+
705
+ // Back to home buttons
706
+ document.getElementById('backToHome').addEventListener('click', () => {
707
+ recentlyView.classList.add('hidden');
708
+ homepage.classList.remove('hidden');
709
+ });
710
+
711
+ document.getElementById('backToHomeAdmin').addEventListener('click', () => {
712
+ adminDashboard.classList.add('hidden');
713
+ homepage.classList.remove('hidden');
714
+ });
715
+
716
+ // Recently view toggle
717
+ document.getElementById('recentlyBtn').addEventListener('click', () => {
718
+ homepage.classList.add('hidden');
719
+ adminDashboard.classList.add('hidden');
720
+ recentlyView.classList.remove('hidden');
721
+
722
+ // In a real app, you would fetch recently viewed photos here
723
+ const recentlyGrid = recentlyView.querySelector('.grid');
724
+ recentlyGrid.innerHTML = '';
725
 
726
+ // Mock recently viewed photos
727
+ const recentPhotos = [
728
+ { id: 1, title: 'Mountain View', url: 'https://source.unsplash.com/random/600x600/?mountain,1', views: 24, tags: ['nature', 'landscape'] },
729
+ { id: 2, title: 'Ocean Sunset', url: 'https://source.unsplash.com/random/600x600/?ocean,1', views: 18, tags: ['nature', 'water'] },
730
+ { id: 3, title: 'City Skyline', url: 'https://source.unsplash.com/random/600x600/?city,1', views: 12, tags: ['urban', 'architecture'] },
731
+ { id: 4, title: 'Forest Path', url: 'https://source.unsplash.com/random/600x600/?forest,1', views: 8, tags: ['nature', 'trees'] }
732
+ ];
733
 
734
+ recentPhotos.forEach(photo => {
735
+ const photoCard = document.createElement('div');
736
+ photoCard.className = 'bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden transition-transform hover:scale-105 cursor-pointer';
737
+ photoCard.innerHTML = `
738
+ <div class="relative aspect-square">
739
+ <img src="${photo.url}" alt="${photo.title}" class="w-full h-full object-cover">
740
+ </div>
741
+ <div class="p-4">
742
+ <h3 class="font-semibold">${photo.title}</h3>
743
+ <div class="flex justify-between items-center mt-2">
744
+ <p class="text-sm text-gray-500 dark:text-gray-400">${photo.views} views</p>
745
+ <div class="flex space-x-1">
746
+ ${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('')}
747
+ </div>
748
+ </div>
749
+ </div>
750
+ `;
751
+ recentlyGrid.appendChild(photoCard);
752
+ });
753
+ });
754
+
755
+ // Album viewer functionality
756
+ const albumCards = document.querySelectorAll('.album-card');
757
+ albumCards.forEach(card => {
758
+ card.addEventListener('click', function() {
759
+ const albumId = this.getAttribute('data-album-id');
760
+ const visibility = this.getAttribute('data-visibility');
761
+
762
+ // Check if album is private and user is not logged in
763
+ if (visibility === 'private' && localStorage.getItem('isLoggedIn') !== 'true') {
764
+ alert('This album is private. Please log in to view.');
765
+ return;
766
+ }
767
 
768
+ // Check if album is family-only and user is not logged in
769
+ if (visibility === 'family' && localStorage.getItem('isLoggedIn') !== 'true') {
770
+ alert('This album is for family members only. Please log in to view.');
771
+ return;
 
 
 
 
 
772
  }
773
+
774
+ // Set album title and visibility
775
+ const albumTitle = this.querySelector('h3').textContent;
776
+ const photoCount = this.querySelector('p').textContent;
777
+
778
+ document.getElementById('albumTitle').textContent = albumTitle;
779
+ document.getElementById('albumPhotoCount').textContent = photoCount;
780
+
781
+ const visibilitySpan = document.getElementById('albumVisibility');
782
+ visibilitySpan.textContent = visibility.charAt(0).toUpperCase() + visibility.slice(1);
783
+ visibilitySpan.className = 'px-2 py-1 rounded-full text-xs font-medium ' +
784
+ (visibility === 'public' ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' :
785
+ visibility === 'family' ? 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200' :
786
+ 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200');
787
+
788
+ // Show album viewer
789
+ homepage.classList.add('hidden');
790
+ albumViewer.classList.remove('hidden');
791
+
792
+ // Load album photos
793
+ loadAlbumPhotos(albumId);
794
  });
795
+ });
796
+
797
+ function loadAlbumPhotos(albumId) {
798
+ const flipbookPages = document.getElementById('flipbookPages');
799
+ flipbookPages.innerHTML = '';
800
 
801
+ // In a real app, you would fetch photos for this album from a database
802
+ // Here we're using mock data
803
+ const photos = [];
804
+ const photoCount = albumId === '1' ? 24 : albumId === '2' ? 18 : albumId === '3' ? 12 : 32;
805
 
806
+ for (let i = 1; i <= photoCount; i++) {
807
+ photos.push({
808
+ id: i,
809
+ title: `Photo ${i}`,
810
+ url: `https://source.unsplash.com/random/800x600/?${albumId === '1' ? 'nature' : albumId === '2' ? 'travel' : albumId === '3' ? 'portrait' : 'architecture'},${i}`,
811
+ tags: ['sample', 'tag']
812
+ });
813
+ }
814
+
815
+ // Create pages with 4 photos each (2 rows of 2)
816
+ let currentPage = 1;
817
+ const photosPerPage = 4;
818
+ const totalPages = Math.ceil(photos.length / photosPerPage);
819
+
820
+ document.getElementById('currentPage').textContent = `Page 1 of ${totalPages}`;
821
+
822
+ // Create first page
823
+ createPage(photos, 0, photosPerPage, currentPage, totalPages);
824
+
825
+ // Navigation buttons
826
+ document.getElementById('prevPage').addEventListener('click', () => {
827
+ if (currentPage > 1) {
828
  currentPage--;
829
+ createPage(photos, (currentPage - 1) * photosPerPage, currentPage * photosPerPage, currentPage, totalPages);
830
+ document.getElementById('currentPage').textContent = `Page ${currentPage} of ${totalPages}`;
831
  }
832
  });
833
 
834
+ document.getElementById('nextPage').addEventListener('click', () => {
835
+ if (currentPage < totalPages) {
836
  currentPage++;
837
+ createPage(photos, (currentPage - 1) * photosPerPage, currentPage * photosPerPage, currentPage, totalPages);
838
+ document.getElementById('currentPage').textContent = `Page ${currentPage} of ${totalPages}`;
839
  }
840
  });
841
 
842
+ function createPage(photos, start, end, currentPage, totalPages) {
843
+ flipbookPages.innerHTML = '';
844
+
845
+ const pageDiv = document.createElement('div');
846
+ pageDiv.className = 'page absolute inset-0 flex flex-col';
847
+
848
+ const row1 = document.createElement('div');
849
+ row1.className = 'flex-1 grid grid-cols-2 gap-2 p-4';
850
+
851
+ const row2 = document.createElement('div');
852
+ row2.className = 'flex-1 grid grid-cols-2 gap-2 p-4 pt-0';
853
+
854
+ for (let i = start; i < Math.min(end, photos.length); i++) {
855
+ const photo = photos[i];
856
+ const photoDiv = document.createElement('div');
857
+ photoDiv.className = 'relative group cursor-pointer';
858
+ photoDiv.innerHTML = `
859
+ <img src="${photo.url}" alt="${photo.title}" class="w-full h-full object-cover rounded-lg">
860
+ <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">
861
+ <span class="text-white font-medium">${photo.title}</span>
862
+ </div>
863
+ `;
864
+
865
+ // Add click event to view photo in modal
866
+ photoDiv.addEventListener('click', () => {
867
+ document.getElementById('modalImage').src = photo.url;
868
+ document.getElementById('modalImage').alt = photo.title;
869
+ document.getElementById('modalImageTitle').textContent = photo.title;
870
+ imageModal.classList.remove('hidden');
871
+
872
+ // Track view in localStorage
873
+ const viewedPhotos = JSON.parse(localStorage.getItem('viewedPhotos') || '[]');
874
+ if (!viewedPhotos.some(p => p.id === photo.id)) {
875
+ viewedPhotos.push({
876
+ id: photo.id,
877
+ title: photo.title,
878
+ url: photo.url,
879
+ viewedAt: new Date().toISOString(),
880
+ tags: photo.tags
881
+ });
882
+ localStorage.setItem('viewedPhotos', JSON.stringify(viewedPhotos));
883
+ }
884
+ });
885
+
886
+ if (i - start < 2) {
887
+ row1.appendChild(photoDiv);
888
+ } else {
889
+ row2.appendChild(photoDiv);
890
+ }
891
+ }
892
+
893
+ pageDiv.appendChild(row1);
894
+ pageDiv.appendChild(row2);
895
+ flipbookPages.appendChild(pageDiv);
896
+
897
+ // Disable/enable navigation buttons
898
+ document.getElementById('prevPage').disabled = currentPage === 1;
899
+ document.getElementById('nextPage').disabled = currentPage === totalPages;
900
+ }
901
+ }
902
+
903
+ // Close album viewer
904
+ document.getElementById('closeAlbum').addEventListener('click', () => {
905
+ albumViewer.classList.add('hidden');
906
+ homepage.classList.remove('hidden');
907
+ });
908
+
909
+ // Image modal functionality
910
+ document.getElementById('closeModal').addEventListener('click', () => {
911
+ imageModal.classList.add('hidden');
912
+ });
913
+
914
+ document.getElementById('modalZoomOut').addEventListener('click', () => {
915
+ const img = document.getElementById('modalImage');
916
+ img.style.transform = img.style.transform === 'scale(0.8)' ? 'scale(1)' : 'scale(0.8)';
917
+ });
918
+
919
+ document.getElementById('modalDownload').addEventListener('click', () => {
920
+ // In a real app, this would trigger a download
921
+ alert('Downloading image...');
922
+ });
923
+
924
+ // Admin photo upload functionality
925
+ const dropZone = document.getElementById('dropZone');
926
+ const fileInput = document.getElementById('fileInput');
927
+ const selectFilesBtn = document.getElementById('selectFilesBtn');
928
+ const uploadProgress = document.getElementById('uploadProgress');
929
+ const progressBar = document.getElementById('progressBar');
930
+ const uploadPercentage = document.getElementById('uploadPercentage');
931
+ const uploadedFiles = document.getElementById('uploadedFiles');
932
+ const uploadedFilesList = document.getElementById('uploadedFilesList');
933
+ const albumSelect = document.getElementById('albumSelect');
934
+ const newAlbumFields = document.getElementById('newAlbumFields');
935
+
936
+ // Drag and drop events
937
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
938
+ dropZone.addEventListener(eventName, preventDefaults, false);
939
+ });
940
+
941
+ function preventDefaults(e) {
942
+ e.preventDefault();
943
+ e.stopPropagation();
944
+ }
945
+
946
+ ['dragenter', 'dragover'].forEach(eventName => {
947
+ dropZone.addEventListener(eventName, highlight, false);
948
+ });
949
+
950
+ ['dragleave', 'drop'].forEach(eventName => {
951
+ dropZone.addEventListener(eventName, unhighlight, false);
952
+ });
953
+
954
+ function highlight() {
955
+ dropZone.classList.add('drag-active');
956
+ }
957
+
958
+ function unhighlight() {
959
+ dropZone.classList.remove('drag-active');
960
+ }
961
+
962
+ // Handle dropped files
963
+ dropZone.addEventListener('drop', handleDrop, false);
964
+
965
+ function handleDrop(e) {
966
+ const dt = e.dataTransfer;
967
+ const files = dt.files;
968
+ handleFiles(files);
969
+ }
970
+
971
+ // Handle selected files
972
+ selectFilesBtn.addEventListener('click', () => {
973
+ fileInput.click();
974
+ });
975
+
976
+ fileInput.addEventListener('change', () => {
977
+ handleFiles(fileInput.files);
978
+ });
979
+
980
+ function handleFiles(files) {
981
+ uploadProgress.classList.remove('hidden');
982
+ uploadedFilesList.innerHTML = '';
983
 
984
+ let progress = 0;
985
+ const totalFiles = files.length;
986
+ let processedFiles = 0;
 
 
987
 
988
+ // Mock upload progress
989
+ const interval = setInterval(() => {
990
+ progress += Math.random() * 10;
991
+ if (progress >= 100) {
992
+ progress = 100;
993
+ clearInterval(interval);
994
+
995
+ // Show uploaded files
996
+ uploadedFiles.classList.remove('hidden');
997
+ Array.from(files).forEach((file, index) => {
998
+ const fileItem = document.createElement('div');
999
+ fileItem.className = 'flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-700 rounded';
1000
+ fileItem.innerHTML = `
1001
+ <div class="flex items-center space-x-2">
1002
+ <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">
1003
+ <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" />
1004
+ </svg>
1005
+ <span class="text-sm">${file.name}</span>
1006
+ </div>
1007
+ <span class="text-xs text-green-600 dark:text-green-400">Uploaded</span>
1008
+ `;
1009
+ uploadedFilesList.appendChild(fileItem);
1010
+ });
1011
+ }
1012
+
1013
+ progressBar.style.width = `${progress}%`;
1014
+ uploadPercentage.textContent = `${Math.round(progress)}%`;
1015
+ }, 200);
1016
  }
1017
+
1018
+ // New album fields toggle
1019
+ albumSelect.addEventListener('change', function() {
1020
+ if (this.value === 'new') {
1021
+ newAlbumFields.classList.remove('hidden');
 
1022
  } else {
1023
+ newAlbumFields.classList.add('hidden');
1024
  }
1025
+ });
1026
+
1027
+ // Save photos button
1028
+ document.getElementById('savePhotosBtn').addEventListener('click', function() {
1029
+ const albumId = albumSelect.value;
1030
+ const albumName = albumId === 'new' ? document.getElementById('newAlbumName').value : albumSelect.options[albumSelect.selectedIndex].text;
1031
+ const albumVisibility = albumId === 'new' ? document.getElementById('newAlbumVisibility').value : '';
1032
+ const photoTitles = document.getElementById('photoTitles').value.split(',');
1033
+ const photoTags = document.getElementById('photoTags').value.split(',');
1034
+
1035
+ // In a real app, you would save this data to a database
1036
+ alert(`Photos saved to ${albumName} (${albumVisibility || 'existing album'})`);
1037
+
1038
+ // Reset form
1039
+ uploadProgress.classList.add('hidden');
1040
+ uploadedFiles.classList.add('hidden');
1041
+ fileInput.value = '';
1042
+ document.getElementById('photoTitles').value = '';
1043
+ document.getElementById('photoTags').value = '';
1044
+ albumSelect.value = '';
1045
+ if (albumId === 'new') {
1046
+ document.getElementById('newAlbumName').value = '';
1047
+ document.getElementById('newAlbumVisibility').value = 'public';
1048
+ newAlbumFields.classList.add('hidden');
1049
+ }
1050
+ });
1051
  </script>
1052
  <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>
1053
  </html>