Spaces:
Running
Running
Make the title Lumaxia Wedding Films - Follow Up Deployment
Browse files- index.html +246 -546
index.html
CHANGED
@@ -3,605 +3,305 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<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 |
<style>
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
}
|
14 |
-
.
|
15 |
-
|
16 |
-
|
17 |
}
|
18 |
-
.
|
19 |
-
|
20 |
-
|
21 |
}
|
22 |
-
|
23 |
-
|
24 |
-
width: 6px;
|
25 |
}
|
26 |
-
.
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
-
.
|
30 |
-
|
31 |
-
border-radius: 3px;
|
32 |
}
|
33 |
-
|
34 |
-
|
|
|
35 |
}
|
36 |
</style>
|
37 |
</head>
|
38 |
-
<body class="
|
39 |
-
|
40 |
-
|
41 |
-
<
|
42 |
-
<
|
43 |
-
|
44 |
-
<
|
|
|
|
|
|
|
45 |
</div>
|
46 |
-
<
|
47 |
-
<
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
|
|
|
|
|
|
|
|
53 |
</div>
|
54 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
<
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
</div>
|
63 |
|
64 |
-
<!--
|
65 |
-
<div class="
|
66 |
-
<
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
<label for="clientPhone" class="block text-sm font-medium text-gray-700 mb-1">Phone Number</label>
|
74 |
-
<input type="tel" id="clientPhone" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required>
|
75 |
-
</div>
|
76 |
-
</div>
|
77 |
-
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
78 |
-
<div>
|
79 |
-
<label for="clientService" class="block text-sm font-medium text-gray-700 mb-1">Service</label>
|
80 |
-
<select id="clientService" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500">
|
81 |
-
<option value="Brazilian Wax">Brazilian Wax</option>
|
82 |
-
<option value="Bikini Wax">Bikini Wax</option>
|
83 |
-
<option value="Leg Wax">Leg Wax</option>
|
84 |
-
<option value="Arm Wax">Arm Wax</option>
|
85 |
-
<option value="Underarm Wax">Underarm Wax</option>
|
86 |
-
<option value="Facial Wax">Facial Wax</option>
|
87 |
-
</select>
|
88 |
-
</div>
|
89 |
-
<div>
|
90 |
-
<label for="clientDate" class="block text-sm font-medium text-gray-700 mb-1">Appointment Date</label>
|
91 |
-
<input type="date" id="clientDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required>
|
92 |
-
</div>
|
93 |
-
<div>
|
94 |
-
<label for="clientTime" class="block text-sm font-medium text-gray-700 mb-1">Time</label>
|
95 |
-
<input type="time" id="clientTime" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required>
|
96 |
-
</div>
|
97 |
-
</div>
|
98 |
-
<div class="flex justify-end">
|
99 |
-
<button type="submit" class="px-4 py-2 bg-pink-500 text-white rounded-md hover:bg-pink-600 transition flex items-center">
|
100 |
-
<i class="fas fa-plus mr-2"></i> Add Client
|
101 |
-
</button>
|
102 |
-
</div>
|
103 |
-
</form>
|
104 |
</div>
|
105 |
|
106 |
-
<!--
|
107 |
-
<div class="overflow-
|
108 |
-
<
|
109 |
-
|
110 |
-
|
111 |
-
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
112 |
-
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Phone</th>
|
113 |
-
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Service</th>
|
114 |
-
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Appointment</th>
|
115 |
-
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
116 |
-
</tr>
|
117 |
-
</thead>
|
118 |
-
<tbody id="clientsList" class="bg-white divide-y divide-gray-200">
|
119 |
-
<!-- Clients will be added here dynamically -->
|
120 |
-
</tbody>
|
121 |
-
</table>
|
122 |
-
</div>
|
123 |
-
</div>
|
124 |
-
|
125 |
-
<!-- Calendar Section -->
|
126 |
-
<div id="calendarSection" class="hidden lg:block bg-white rounded-xl shadow-md overflow-hidden">
|
127 |
-
<div class="p-5 border-b border-gray-200">
|
128 |
-
<h2 class="text-xl font-semibold text-gray-800">Appointment Calendar</h2>
|
129 |
-
</div>
|
130 |
-
<div class="p-4">
|
131 |
-
<div class="flex justify-between items-center mb-4">
|
132 |
-
<button id="prevMonth" class="p-2 rounded-full hover:bg-gray-100">
|
133 |
-
<i class="fas fa-chevron-left text-gray-600"></i>
|
134 |
-
</button>
|
135 |
-
<h3 id="currentMonthYear" class="text-lg font-medium text-gray-800">June 2023</h3>
|
136 |
-
<button id="nextMonth" class="p-2 rounded-full hover:bg-gray-100">
|
137 |
-
<i class="fas fa-chevron-right text-gray-600"></i>
|
138 |
-
</button>
|
139 |
</div>
|
140 |
-
<div class="
|
141 |
-
<
|
142 |
-
<div class="text-center text-xs font-medium text-gray-500 py-1">Mon</div>
|
143 |
-
<div class="text-center text-xs font-medium text-gray-500 py-1">Tue</div>
|
144 |
-
<div class="text-center text-xs font-medium text-gray-500 py-1">Wed</div>
|
145 |
-
<div class="text-center text-xs font-medium text-gray-500 py-1">Thu</div>
|
146 |
-
<div class="text-center text-xs font-medium text-gray-500 py-1">Fri</div>
|
147 |
-
<div class="text-center text-xs font-medium text-gray-500 py-1">Sat</div>
|
148 |
</div>
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
</div>
|
152 |
</div>
|
153 |
|
154 |
-
<!--
|
155 |
-
<div class="
|
156 |
-
<
|
157 |
-
<div
|
158 |
-
|
|
|
|
|
|
|
159 |
</div>
|
160 |
</div>
|
161 |
|
162 |
-
<!--
|
163 |
-
<div class="
|
164 |
-
<
|
165 |
-
|
166 |
-
|
167 |
-
|
|
|
|
|
|
|
168 |
</div>
|
169 |
</div>
|
170 |
-
</
|
171 |
-
</
|
172 |
|
173 |
-
<!--
|
174 |
-
<
|
175 |
-
<div class="
|
176 |
-
<div class="flex
|
177 |
-
<
|
178 |
-
|
179 |
-
<i class="fas fa-times"></i>
|
180 |
-
</button>
|
181 |
-
</div>
|
182 |
-
<form id="editClientForm" class="space-y-4">
|
183 |
-
<input type="hidden" id="editClientId">
|
184 |
-
<div>
|
185 |
-
<label for="editClientName" class="block text-sm font-medium text-gray-700 mb-1">Full Name</label>
|
186 |
-
<input type="text" id="editClientName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required>
|
187 |
</div>
|
188 |
-
<div>
|
189 |
-
<
|
190 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
</div>
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
|
|
|
|
202 |
</div>
|
203 |
-
<div class="
|
204 |
-
<
|
205 |
-
|
206 |
-
<input type="date" id="editClientDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required>
|
207 |
-
</div>
|
208 |
-
<div>
|
209 |
-
<label for="editClientTime" class="block text-sm font-medium text-gray-700 mb-1">Time</label>
|
210 |
-
<input type="time" id="editClientTime" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required>
|
211 |
-
</div>
|
212 |
</div>
|
213 |
-
<div class="
|
214 |
-
<
|
215 |
-
|
216 |
-
</button>
|
217 |
-
<button type="submit" class="px-4 py-2 bg-pink-500 text-white rounded-md hover:bg-pink-600 transition">
|
218 |
-
Save Changes
|
219 |
-
</button>
|
220 |
</div>
|
|
|
221 |
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
</div>
|
223 |
-
</
|
224 |
-
|
225 |
-
<script>
|
226 |
-
// Client data storage
|
227 |
-
let clients = JSON.parse(localStorage.getItem('clients')) || [];
|
228 |
-
|
229 |
-
// Current date for calendar
|
230 |
-
let currentDate = new Date();
|
231 |
-
let selectedDate = new Date();
|
232 |
-
|
233 |
-
// DOM Elements
|
234 |
-
const clientsSection = document.getElementById('clientsSection');
|
235 |
-
const calendarSection = document.getElementById('calendarSection');
|
236 |
-
const showClientsBtn = document.getElementById('showClientsBtn');
|
237 |
-
const showCalendarBtn = document.getElementById('showCalendarBtn');
|
238 |
-
const clientForm = document.getElementById('clientForm');
|
239 |
-
const clientsList = document.getElementById('clientsList');
|
240 |
-
const currentMonthYear = document.getElementById('currentMonthYear');
|
241 |
-
const calendarDays = document.getElementById('calendarDays');
|
242 |
-
const prevMonthBtn = document.getElementById('prevMonth');
|
243 |
-
const nextMonthBtn = document.getElementById('nextMonth');
|
244 |
-
const dayAppointments = document.getElementById('dayAppointments');
|
245 |
-
const selectedDayTitle = document.getElementById('selectedDayTitle');
|
246 |
-
const sendRemindersBtn = document.getElementById('sendRemindersBtn');
|
247 |
-
const smsStatus = document.getElementById('smsStatus');
|
248 |
-
const editModal = document.getElementById('editModal');
|
249 |
-
const editClientForm = document.getElementById('editClientForm');
|
250 |
-
const closeEditModal = document.getElementById('closeEditModal');
|
251 |
-
const cancelEdit = document.getElementById('cancelEdit');
|
252 |
-
|
253 |
-
// Initialize the app
|
254 |
-
document.addEventListener('DOMContentLoaded', () => {
|
255 |
-
renderClients();
|
256 |
-
generateCalendar();
|
257 |
-
showTodaysAppointments();
|
258 |
-
|
259 |
-
// Set default date to today in the form
|
260 |
-
const today = new Date().toISOString().split('T')[0];
|
261 |
-
document.getElementById('clientDate').value = today;
|
262 |
-
|
263 |
-
// Set default time to next hour
|
264 |
-
const nextHour = new Date();
|
265 |
-
nextHour.setHours(nextHour.getHours() + 1, 0, 0, 0);
|
266 |
-
const timeString = nextHour.toTimeString().substring(0, 5);
|
267 |
-
document.getElementById('clientTime').value = timeString;
|
268 |
-
});
|
269 |
-
|
270 |
-
// Toggle between clients and calendar on mobile
|
271 |
-
showClientsBtn.addEventListener('click', () => {
|
272 |
-
clientsSection.classList.remove('hidden');
|
273 |
-
calendarSection.classList.add('hidden');
|
274 |
-
showClientsBtn.classList.remove('bg-gray-200', 'text-gray-700');
|
275 |
-
showClientsBtn.classList.add('bg-pink-500', 'text-white');
|
276 |
-
showCalendarBtn.classList.remove('bg-pink-500', 'text-white');
|
277 |
-
showCalendarBtn.classList.add('bg-gray-200', 'text-gray-700');
|
278 |
-
});
|
279 |
-
|
280 |
-
showCalendarBtn.addEventListener('click', () => {
|
281 |
-
clientsSection.classList.add('hidden');
|
282 |
-
calendarSection.classList.remove('hidden');
|
283 |
-
showCalendarBtn.classList.remove('bg-gray-200', 'text-gray-700');
|
284 |
-
showCalendarBtn.classList.add('bg-pink-500', 'text-white');
|
285 |
-
showClientsBtn.classList.remove('bg-pink-500', 'text-white');
|
286 |
-
showClientsBtn.classList.add('bg-gray-200', 'text-gray-700');
|
287 |
-
});
|
288 |
-
|
289 |
-
// Add new client
|
290 |
-
clientForm.addEventListener('submit', (e) => {
|
291 |
-
e.preventDefault();
|
292 |
-
|
293 |
-
const name = document.getElementById('clientName').value;
|
294 |
-
const phone = document.getElementById('clientPhone').value;
|
295 |
-
const service = document.getElementById('clientService').value;
|
296 |
-
const date = document.getElementById('clientDate').value;
|
297 |
-
const time = document.getElementById('clientTime').value;
|
298 |
-
|
299 |
-
const newClient = {
|
300 |
-
id: Date.now().toString(),
|
301 |
-
name,
|
302 |
-
phone,
|
303 |
-
service,
|
304 |
-
date,
|
305 |
-
time,
|
306 |
-
status: 'scheduled'
|
307 |
-
};
|
308 |
-
|
309 |
-
clients.push(newClient);
|
310 |
-
saveClients();
|
311 |
-
renderClients();
|
312 |
-
generateCalendar();
|
313 |
-
showTodaysAppointments();
|
314 |
-
|
315 |
-
// Reset form
|
316 |
-
clientForm.reset();
|
317 |
-
|
318 |
-
// Set default date to today
|
319 |
-
document.getElementById('clientDate').value = new Date().toISOString().split('T')[0];
|
320 |
-
|
321 |
-
// Set default time to next hour
|
322 |
-
const nextHour = new Date();
|
323 |
-
nextHour.setHours(nextHour.getHours() + 1, 0, 0, 0);
|
324 |
-
const timeString = nextHour.toTimeString().substring(0, 5);
|
325 |
-
document.getElementById('clientTime').value = timeString;
|
326 |
-
});
|
327 |
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
editModal.classList.add('hidden');
|
343 |
-
});
|
344 |
|
345 |
-
|
346 |
-
|
347 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
348 |
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
const name = document.getElementById('editClientName').value;
|
355 |
-
const phone = document.getElementById('editClientPhone').value;
|
356 |
-
const service = document.getElementById('editClientService').value;
|
357 |
-
const date = document.getElementById('editClientDate').value;
|
358 |
-
const time = document.getElementById('editClientTime').value;
|
359 |
-
|
360 |
-
const clientIndex = clients.findIndex(client => client.id === id);
|
361 |
-
|
362 |
-
if (clientIndex !== -1) {
|
363 |
-
clients[clientIndex] = {
|
364 |
-
...clients[clientIndex],
|
365 |
-
name,
|
366 |
-
phone,
|
367 |
-
service,
|
368 |
-
date,
|
369 |
-
time
|
370 |
-
};
|
371 |
-
|
372 |
-
saveClients();
|
373 |
-
renderClients();
|
374 |
-
generateCalendar();
|
375 |
-
showTodaysAppointments();
|
376 |
-
editModal.classList.add('hidden');
|
377 |
-
}
|
378 |
});
|
379 |
|
380 |
-
//
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
saveClients();
|
385 |
-
renderClients();
|
386 |
-
generateCalendar();
|
387 |
-
showTodaysAppointments();
|
388 |
-
}
|
389 |
-
}
|
390 |
-
|
391 |
-
// Render clients list
|
392 |
-
function renderClients() {
|
393 |
-
if (clients.length === 0) {
|
394 |
-
clientsList.innerHTML = `
|
395 |
-
<tr>
|
396 |
-
<td colspan="5" class="px-6 py-4 text-center text-gray-500">No clients found. Add your first client above.</td>
|
397 |
-
</tr>
|
398 |
-
`;
|
399 |
-
return;
|
400 |
-
}
|
401 |
-
|
402 |
-
// Sort clients by date and time
|
403 |
-
const sortedClients = [...clients].sort((a, b) => {
|
404 |
-
const dateA = new Date(`${a.date}T${a.time}`);
|
405 |
-
const dateB = new Date(`${b.date}T${b.time}`);
|
406 |
-
return dateA - dateB;
|
407 |
});
|
408 |
-
|
409 |
-
clientsList.innerHTML = sortedClients.map(client => `
|
410 |
-
<tr class="hover:bg-gray-50">
|
411 |
-
<td class="px-6 py-4 whitespace-nowrap">
|
412 |
-
<div class="text-sm font-medium text-gray-900">${client.name}</div>
|
413 |
-
</td>
|
414 |
-
<td class="px-6 py-4 whitespace-nowrap">
|
415 |
-
<div class="text-sm text-gray-500">${formatPhoneNumber(client.phone)}</div>
|
416 |
-
</td>
|
417 |
-
<td class="px-6 py-4 whitespace-nowrap">
|
418 |
-
<div class="text-sm text-gray-500">${client.service}</div>
|
419 |
-
</td>
|
420 |
-
<td class="px-6 py-4 whitespace-nowrap">
|
421 |
-
<div class="text-sm text-gray-500">${formatDate(client.date)} at ${client.time}</div>
|
422 |
-
</td>
|
423 |
-
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
424 |
-
<button onclick="openEditModal(${JSON.stringify(client).replace(/"/g, '"')})" class="text-pink-600 hover:text-pink-900 mr-3">
|
425 |
-
<i class="fas fa-edit"></i>
|
426 |
-
</button>
|
427 |
-
<button onclick="deleteClient('${client.id}')" class="text-red-600 hover:text-red-900">
|
428 |
-
<i class="fas fa-trash-alt"></i>
|
429 |
-
</button>
|
430 |
-
</td>
|
431 |
-
</tr>
|
432 |
-
`).join('');
|
433 |
-
}
|
434 |
|
435 |
-
//
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
currentMonthYear.textContent = new Date(year, month).toLocaleDateString('en-US', {
|
441 |
-
month: 'long',
|
442 |
-
year: 'numeric'
|
443 |
-
});
|
444 |
-
|
445 |
-
const firstDay = new Date(year, month, 1).getDay();
|
446 |
-
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
447 |
-
const daysInPrevMonth = new Date(year, month, 0).getDate();
|
448 |
-
|
449 |
-
calendarDays.innerHTML = '';
|
450 |
-
|
451 |
-
// Previous month's days
|
452 |
-
for (let i = firstDay; i > 0; i--) {
|
453 |
-
const day = daysInPrevMonth - i + 1;
|
454 |
-
calendarDays.innerHTML += `
|
455 |
-
<div class="h-12 flex items-center justify-center text-gray-400 text-sm empty-day">
|
456 |
-
${day}
|
457 |
-
</div>
|
458 |
-
`;
|
459 |
-
}
|
460 |
-
|
461 |
-
// Current month's days
|
462 |
-
for (let i = 1; i <= daysInMonth; i++) {
|
463 |
-
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`;
|
464 |
-
const hasAppointment = clients.some(client => client.date === dateStr);
|
465 |
-
const isToday = isSameDay(new Date(dateStr), new Date());
|
466 |
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
|
471 |
-
|
472 |
-
|
473 |
-
${i}
|
474 |
-
${hasAppointment ? '<span class="absolute bottom-1 w-1 h-1 bg-pink-500 rounded-full"></span>' : ''}
|
475 |
-
</div>
|
476 |
-
`;
|
477 |
-
}
|
478 |
-
|
479 |
-
// Next month's days
|
480 |
-
const daysToAdd = 42 - (firstDay + daysInMonth); // 6 rows x 7 days = 42 cells
|
481 |
-
for (let i = 1; i <= daysToAdd; i++) {
|
482 |
-
calendarDays.innerHTML += `
|
483 |
-
<div class="h-12 flex items-center justify-center text-gray-400 text-sm empty-day">
|
484 |
-
${i}
|
485 |
-
</div>
|
486 |
-
`;
|
487 |
-
}
|
488 |
-
|
489 |
-
// Add click event to days
|
490 |
-
document.querySelectorAll('.calendar-day').forEach(day => {
|
491 |
-
day.addEventListener('click', () => {
|
492 |
-
const dateStr = day.getAttribute('data-date');
|
493 |
-
selectedDate = new Date(dateStr);
|
494 |
-
showDayAppointments(dateStr);
|
495 |
-
});
|
496 |
});
|
497 |
-
}
|
498 |
-
|
499 |
-
// Show today's appointments
|
500 |
-
function showTodaysAppointments() {
|
501 |
-
const today = new Date().toISOString().split('T')[0];
|
502 |
-
selectedDayTitle.textContent = "Today's Appointments";
|
503 |
-
showDayAppointments(today);
|
504 |
-
}
|
505 |
-
|
506 |
-
// Show appointments for a specific day
|
507 |
-
function showDayAppointments(dateStr) {
|
508 |
-
const dayAppointmentsList = clients.filter(client => client.date === dateStr)
|
509 |
-
.sort((a, b) => a.time.localeCompare(b.time));
|
510 |
-
|
511 |
-
if (dayAppointmentsList.length === 0) {
|
512 |
-
dayAppointments.innerHTML = `
|
513 |
-
<div class="text-center text-gray-500 py-4">
|
514 |
-
No appointments scheduled for ${formatDate(dateStr)}
|
515 |
-
</div>
|
516 |
-
`;
|
517 |
-
} else {
|
518 |
-
dayAppointments.innerHTML = dayAppointmentsList.map(client => `
|
519 |
-
<div class="bg-white border border-gray-200 rounded-lg p-3 shadow-sm">
|
520 |
-
<div class="flex justify-between items-start">
|
521 |
-
<div>
|
522 |
-
<h4 class="font-medium text-gray-800">${client.name}</h4>
|
523 |
-
<p class="text-sm text-gray-600">${client.service}</p>
|
524 |
-
</div>
|
525 |
-
<span class="text-sm font-medium">${client.time}</span>
|
526 |
-
</div>
|
527 |
-
<div class="mt-2 flex justify-between items-center">
|
528 |
-
<span class="text-xs text-gray-500">${formatPhoneNumber(client.phone)}</span>
|
529 |
-
<button onclick="deleteClient('${client.id}')" class="text-red-500 hover:text-red-700 text-xs">
|
530 |
-
<i class="fas fa-times mr-1"></i>Cancel
|
531 |
-
</button>
|
532 |
-
</div>
|
533 |
-
</div>
|
534 |
-
`).join('');
|
535 |
-
}
|
536 |
-
|
537 |
-
// Update selected day title if not today
|
538 |
-
if (!isSameDay(new Date(dateStr), new Date())) {
|
539 |
-
selectedDayTitle.textContent = `Appointments for ${formatDate(dateStr)}`;
|
540 |
-
}
|
541 |
-
}
|
542 |
-
|
543 |
-
// Navigate calendar months
|
544 |
-
prevMonthBtn.addEventListener('click', () => {
|
545 |
-
currentDate.setMonth(currentDate.getMonth() - 1);
|
546 |
-
generateCalendar();
|
547 |
});
|
548 |
|
549 |
-
|
550 |
-
|
551 |
-
|
|
|
|
|
|
|
|
|
|
|
552 |
});
|
553 |
|
554 |
-
//
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
if (tomorrowsAppointments.length === 0) {
|
563 |
-
smsStatus.textContent = "No appointments tomorrow to send reminders for.";
|
564 |
-
smsStatus.className = "mt-2 text-sm text-center text-gray-600";
|
565 |
-
smsStatus.classList.remove('hidden');
|
566 |
-
return;
|
567 |
-
}
|
568 |
-
|
569 |
-
// Simulate sending SMS (in a real app, you would integrate with an SMS API)
|
570 |
-
smsStatus.textContent = `Sending ${tomorrowsAppointments.length} reminder(s)...`;
|
571 |
-
smsStatus.className = "mt-2 text-sm text-center text-blue-600";
|
572 |
-
smsStatus.classList.remove('hidden');
|
573 |
-
|
574 |
-
setTimeout(() => {
|
575 |
-
smsStatus.textContent = `Successfully sent ${tomorrowsAppointments.length} reminder(s) for tomorrow's appointments!`;
|
576 |
-
smsStatus.className = "mt-2 text-sm text-center text-green-600";
|
577 |
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
|
|
|
|
583 |
});
|
584 |
-
|
585 |
-
// Helper functions
|
586 |
-
function saveClients() {
|
587 |
-
localStorage.setItem('clients', JSON.stringify(clients));
|
588 |
-
}
|
589 |
-
|
590 |
-
function formatDate(dateStr) {
|
591 |
-
const options = { weekday: 'short', month: 'short', day: 'numeric' };
|
592 |
-
return new Date(dateStr).toLocaleDateString('en-US', options);
|
593 |
-
}
|
594 |
-
|
595 |
-
function formatPhoneNumber(phone) {
|
596 |
-
// Simple formatting for display
|
597 |
-
return phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
|
598 |
-
}
|
599 |
-
|
600 |
-
function isSameDay(date1, date2) {
|
601 |
-
return date1.getFullYear() === date2.getFullYear() &&
|
602 |
-
date1.getMonth() === date2.getMonth() &&
|
603 |
-
date1.getDate() === date2.getDate();
|
604 |
-
}
|
605 |
</script>
|
606 |
<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=Cezarxil/client-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
607 |
</html>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Lumaxia Wedding Films</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 |
<style>
|
10 |
+
/* Custom CSS for elements that need more precise control */
|
11 |
+
.hero-video {
|
12 |
+
position: absolute;
|
13 |
+
top: 0;
|
14 |
+
left: 0;
|
15 |
+
width: 100%;
|
16 |
+
height: 100%;
|
17 |
+
object-fit: cover;
|
18 |
+
z-index: -1;
|
19 |
}
|
20 |
+
.video-thumbnail:hover .play-icon {
|
21 |
+
opacity: 1;
|
22 |
+
transform: scale(1.1);
|
23 |
}
|
24 |
+
.play-icon {
|
25 |
+
transition: all 0.3s ease;
|
26 |
+
opacity: 0.8;
|
27 |
}
|
28 |
+
.nav-link {
|
29 |
+
position: relative;
|
|
|
30 |
}
|
31 |
+
.nav-link:after {
|
32 |
+
content: '';
|
33 |
+
position: absolute;
|
34 |
+
width: 0;
|
35 |
+
height: 1px;
|
36 |
+
bottom: 0;
|
37 |
+
left: 0;
|
38 |
+
background-color: #d1a782;
|
39 |
+
transition: width 0.3s ease;
|
40 |
}
|
41 |
+
.nav-link:hover:after {
|
42 |
+
width: 100%;
|
|
|
43 |
}
|
44 |
+
/* Smooth scrolling */
|
45 |
+
html {
|
46 |
+
scroll-behavior: smooth;
|
47 |
}
|
48 |
</style>
|
49 |
</head>
|
50 |
+
<body class="font-sans text-gray-700 bg-white antialiased">
|
51 |
+
<!-- Navigation -->
|
52 |
+
<nav class="fixed w-full bg-white bg-opacity-90 z-50 shadow-sm">
|
53 |
+
<div class="container mx-auto px-6 py-4 flex justify-between items-center">
|
54 |
+
<a href="#" class="text-2xl font-light text-gray-800">Lumaxia <span class="text-amber-800">Wedding Films</span></a>
|
55 |
+
<div class="hidden md:flex space-x-8">
|
56 |
+
<a href="#home" class="nav-link text-gray-700 hover:text-amber-800">Home</a>
|
57 |
+
<a href="#portfolio" class="nav-link text-gray-700 hover:text-amber-800">Portfolio</a>
|
58 |
+
<a href="#about" class="nav-link text-gray-700 hover:text-amber-800">About</a>
|
59 |
+
<a href="#contact" class="nav-link text-gray-700 hover:text-amber-800">Contact</a>
|
60 |
</div>
|
61 |
+
<button class="md:hidden focus:outline-none" id="menu-toggle">
|
62 |
+
<i class="fas fa-bars text-gray-700 text-xl"></i>
|
63 |
+
</button>
|
64 |
+
</div>
|
65 |
+
<!-- Mobile menu -->
|
66 |
+
<div class="md:hidden hidden bg-white w-full px-6 py-4" id="mobile-menu">
|
67 |
+
<div class="flex flex-col space-y-4">
|
68 |
+
<a href="#home" class="nav-link text-gray-700 hover:text-amber-800">Home</a>
|
69 |
+
<a href="#portfolio" class="nav-link text-gray-700 hover:text-amber-800">Portfolio</a>
|
70 |
+
<a href="#about" class="nav-link text-gray-700 hover:text-amber-800">About</a>
|
71 |
+
<a href="#contact" class="nav-link text-gray-700 hover:text-amber-800">Contact</a>
|
72 |
</div>
|
73 |
+
</div>
|
74 |
+
</nav>
|
75 |
+
|
76 |
+
<!-- Hero Section with Video Background -->
|
77 |
+
<section id="home" class="relative h-screen flex items-center justify-center overflow-hidden">
|
78 |
+
<video autoplay muted loop class="hero-video">
|
79 |
+
<source src="https://assets.mixkit.co/videos/preview/mixkit-wedding-couple-standing-in-a-field-while-the-sun-1210-large.mp4" type="video/mp4">
|
80 |
+
Your browser does not support the video tag.
|
81 |
+
</video>
|
82 |
+
<div class="absolute inset-0 bg-black bg-opacity-30"></div>
|
83 |
+
<div class="text-center px-6 z-10">
|
84 |
+
<h1 class="text-4xl md:text-6xl font-light text-white mb-4">Capturing Timeless Moments</h1>
|
85 |
+
<p class="text-xl text-white mb-8 max-w-2xl mx-auto">Preserving your most precious memories with cinematic elegance</p>
|
86 |
+
<a href="#contact" class="inline-block px-8 py-3 bg-white text-amber-800 font-medium rounded-sm hover:bg-amber-50 transition duration-300">Get in Touch</a>
|
87 |
+
</div>
|
88 |
+
</section>
|
89 |
|
90 |
+
<!-- Portfolio Section -->
|
91 |
+
<section id="portfolio" class="py-20 bg-gray-50">
|
92 |
+
<div class="container mx-auto px-6">
|
93 |
+
<h2 class="text-3xl font-light text-center mb-12 text-gray-800">Wedding Films</h2>
|
94 |
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
95 |
+
<!-- Video 1 -->
|
96 |
+
<div class="video-thumbnail relative group overflow-hidden rounded-sm shadow-md">
|
97 |
+
<img src="https://images.unsplash.com/photo-1519225421980-715cb0215aed?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80" alt="Wedding Film" class="w-full h-64 object-cover transition duration-500 group-hover:scale-105">
|
98 |
+
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center cursor-pointer">
|
99 |
+
<i class="fas fa-play play-icon text-white text-4xl"></i>
|
100 |
+
</div>
|
101 |
+
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black to-transparent">
|
102 |
+
<h3 class="text-white font-light text-xl">Sarah & Michael</h3>
|
103 |
+
</div>
|
104 |
</div>
|
105 |
|
106 |
+
<!-- Video 2 -->
|
107 |
+
<div class="video-thumbnail relative group overflow-hidden rounded-sm shadow-md">
|
108 |
+
<img src="https://images.unsplash.com/photo-1519657337289-077653f724ed?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80" alt="Wedding Film" class="w-full h-64 object-cover transition duration-500 group-hover:scale-105">
|
109 |
+
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center cursor-pointer">
|
110 |
+
<i class="fas fa-play play-icon text-white text-4xl"></i>
|
111 |
+
</div>
|
112 |
+
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black to-transparent">
|
113 |
+
<h3 class="text-white font-light text-xl">Emma & James</h3>
|
114 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
</div>
|
116 |
|
117 |
+
<!-- Video 3 -->
|
118 |
+
<div class="video-thumbnail relative group overflow-hidden rounded-sm shadow-md">
|
119 |
+
<img src="https://images.unsplash.com/photo-1583939003579-730e3918a45a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1587&q=80" alt="Wedding Film" class="w-full h-64 object-cover transition duration-500 group-hover:scale-105">
|
120 |
+
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center cursor-pointer">
|
121 |
+
<i class="fas fa-play play-icon text-white text-4xl"></i>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
</div>
|
123 |
+
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black to-transparent">
|
124 |
+
<h3 class="text-white font-light text-xl">Olivia & Noah</h3>
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
</div>
|
126 |
+
</div>
|
127 |
+
|
128 |
+
<!-- Video 4 -->
|
129 |
+
<div class="video-thumbnail relative group overflow-hidden rounded-sm shadow-md">
|
130 |
+
<img src="https://images.unsplash.com/photo-1519225421980-715cb0215aed?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80" alt="Wedding Film" class="w-full h-64 object-cover transition duration-500 group-hover:scale-105">
|
131 |
+
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center cursor-pointer">
|
132 |
+
<i class="fas fa-play play-icon text-white text-4xl"></i>
|
133 |
+
</div>
|
134 |
+
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black to-transparent">
|
135 |
+
<h3 class="text-white font-light text-xl">Sophia & Liam</h3>
|
136 |
</div>
|
137 |
</div>
|
138 |
|
139 |
+
<!-- Video 5 -->
|
140 |
+
<div class="video-thumbnail relative group overflow-hidden rounded-sm shadow-md">
|
141 |
+
<img src="https://images.unsplash.com/photo-1519657337289-077653f724ed?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80" alt="Wedding Film" class="w-full h-64 object-cover transition duration-500 group-hover:scale-105">
|
142 |
+
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center cursor-pointer">
|
143 |
+
<i class="fas fa-play play-icon text-white text-4xl"></i>
|
144 |
+
</div>
|
145 |
+
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black to-transparent">
|
146 |
+
<h3 class="text-white font-light text-xl">Ava & Lucas</h3>
|
147 |
</div>
|
148 |
</div>
|
149 |
|
150 |
+
<!-- Video 6 -->
|
151 |
+
<div class="video-thumbnail relative group overflow-hidden rounded-sm shadow-md">
|
152 |
+
<img src="https://images.unsplash.com/photo-1583939003579-730e3918a45a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1587&q=80" alt="Wedding Film" class="w-full h-64 object-cover transition duration-500 group-hover:scale-105">
|
153 |
+
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center cursor-pointer">
|
154 |
+
<i class="fas fa-play play-icon text-white text-4xl"></i>
|
155 |
+
</div>
|
156 |
+
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black to-transparent">
|
157 |
+
<h3 class="text-white font-light text-xl">Isabella & Ethan</h3>
|
158 |
+
</div>
|
159 |
</div>
|
160 |
</div>
|
161 |
+
</div>
|
162 |
+
</section>
|
163 |
|
164 |
+
<!-- About Section -->
|
165 |
+
<section id="about" class="py-20 bg-white">
|
166 |
+
<div class="container mx-auto px-6">
|
167 |
+
<div class="flex flex-col md:flex-row items-center">
|
168 |
+
<div class="md:w-1/2 mb-12 md:mb-0 md:pr-12">
|
169 |
+
<img src="https://images.unsplash.com/photo-1551836022-d5d44eef8f86?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80" alt="About Me" class="w-full h-auto rounded-sm shadow-md">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
</div>
|
171 |
+
<div class="md:w-1/2">
|
172 |
+
<h2 class="text-3xl font-light mb-6 text-gray-800">About Me</h2>
|
173 |
+
<p class="text-gray-600 mb-6 leading-relaxed">
|
174 |
+
With over a decade of experience in wedding videography, I specialize in capturing the raw emotions and fleeting moments that make your day truly unique. My approach is unobtrusive yet attentive, allowing me to document your wedding story as it naturally unfolds.
|
175 |
+
</p>
|
176 |
+
<p class="text-gray-600 mb-6 leading-relaxed">
|
177 |
+
I believe that wedding films should be more than just documentation - they should be cinematic experiences that transport you back to the joy, laughter, and tears of your special day. Each film is carefully crafted to reflect the personality and love story of the couple.
|
178 |
+
</p>
|
179 |
+
<p class="text-gray-600 leading-relaxed">
|
180 |
+
When I'm not behind the camera, you can find me hiking with my golden retriever or experimenting with new film techniques. I'm based in California but available for travel worldwide.
|
181 |
+
</p>
|
182 |
</div>
|
183 |
+
</div>
|
184 |
+
</div>
|
185 |
+
</section>
|
186 |
+
|
187 |
+
<!-- Contact Section -->
|
188 |
+
<section id="contact" class="py-20 bg-gray-50">
|
189 |
+
<div class="container mx-auto px-6 max-w-4xl">
|
190 |
+
<h2 class="text-3xl font-light text-center mb-12 text-gray-800">Let's Create Something Beautiful</h2>
|
191 |
+
<form class="bg-white p-8 rounded-sm shadow-md">
|
192 |
+
<div class="mb-6">
|
193 |
+
<label for="name" class="block text-gray-700 mb-2">Your Name</label>
|
194 |
+
<input type="text" id="name" class="w-full px-4 py-2 border border-gray-300 rounded-sm focus:outline-none focus:border-amber-500">
|
195 |
</div>
|
196 |
+
<div class="mb-6">
|
197 |
+
<label for="email" class="block text-gray-700 mb-2">Email Address</label>
|
198 |
+
<input type="email" id="email" class="w-full px-4 py-2 border border-gray-300 rounded-sm focus:outline-none focus:border-amber-500">
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
</div>
|
200 |
+
<div class="mb-6">
|
201 |
+
<label for="message" class="block text-gray-700 mb-2">Your Message</label>
|
202 |
+
<textarea id="message" rows="5" class="w-full px-4 py-2 border border-gray-300 rounded-sm focus:outline-none focus:border-amber-500"></textarea>
|
|
|
|
|
|
|
|
|
203 |
</div>
|
204 |
+
<button type="submit" class="w-full bg-amber-800 text-white py-3 px-4 rounded-sm hover:bg-amber-700 transition duration-300">Send Message</button>
|
205 |
</form>
|
206 |
+
<div class="mt-12 text-center">
|
207 |
+
<p class="text-gray-600 mb-4">Or reach out directly:</p>
|
208 |
+
<div class="flex justify-center space-x-6">
|
209 |
+
<a href="mailto:[email protected]" class="text-gray-700 hover:text-amber-800 transition duration-300">
|
210 |
+
<i class="fas fa-envelope text-xl"></i>
|
211 |
+
</a>
|
212 |
+
<a href="tel:+1234567890" class="text-gray-700 hover:text-amber-800 transition duration-300">
|
213 |
+
<i class="fas fa-phone text-xl"></i>
|
214 |
+
</a>
|
215 |
+
<a href="#" class="text-gray-700 hover:text-amber-800 transition duration-300">
|
216 |
+
<i class="fab fa-instagram text-xl"></i>
|
217 |
+
</a>
|
218 |
+
</div>
|
219 |
+
</div>
|
220 |
</div>
|
221 |
+
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
|
223 |
+
<!-- Footer -->
|
224 |
+
<footer class="bg-gray-800 text-white py-8">
|
225 |
+
<div class="container mx-auto px-6">
|
226 |
+
<div class="flex flex-col md:flex-row justify-between items-center">
|
227 |
+
<div class="mb-4 md:mb-0">
|
228 |
+
<h3 class="text-2xl font-light">Lumaxia <span class="text-amber-400">Wedding Films</span></h3>
|
229 |
+
<p class="text-gray-400 mt-2">Capturing your love story with cinematic elegance</p>
|
230 |
+
</div>
|
231 |
+
<div class="text-gray-400 text-sm">
|
232 |
+
<p>© 2023 Timeless Moments. All rights reserved.</p>
|
233 |
+
</div>
|
234 |
+
</div>
|
235 |
+
</div>
|
236 |
+
</footer>
|
|
|
|
|
237 |
|
238 |
+
<!-- Video Modal -->
|
239 |
+
<div id="video-modal" class="fixed inset-0 bg-black bg-opacity-90 z-50 hidden flex items-center justify-center p-4">
|
240 |
+
<div class="relative w-full max-w-4xl">
|
241 |
+
<button id="close-modal" class="absolute -top-12 right-0 text-white text-2xl focus:outline-none">
|
242 |
+
<i class="fas fa-times"></i>
|
243 |
+
</button>
|
244 |
+
<div class="aspect-w-16 aspect-h-9">
|
245 |
+
<iframe id="modal-video" class="w-full h-full" src="" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>
|
246 |
+
</div>
|
247 |
+
</div>
|
248 |
+
</div>
|
249 |
|
250 |
+
<script>
|
251 |
+
// Mobile menu toggle
|
252 |
+
document.getElementById('menu-toggle').addEventListener('click', function() {
|
253 |
+
const menu = document.getElementById('mobile-menu');
|
254 |
+
menu.classList.toggle('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
});
|
256 |
|
257 |
+
// Close mobile menu when clicking a link
|
258 |
+
document.querySelectorAll('#mobile-menu a').forEach(link => {
|
259 |
+
link.addEventListener('click', () => {
|
260 |
+
document.getElementById('mobile-menu').classList.add('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
});
|
262 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
|
264 |
+
// Video thumbnail click handler
|
265 |
+
document.querySelectorAll('.video-thumbnail').forEach((thumbnail, index) => {
|
266 |
+
thumbnail.addEventListener('click', () => {
|
267 |
+
const modal = document.getElementById('video-modal');
|
268 |
+
const videoFrame = document.getElementById('modal-video');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
|
270 |
+
// In a real implementation, you would use the actual video URL
|
271 |
+
// This is just a placeholder for demonstration
|
272 |
+
videoFrame.src = "https://www.youtube.com/embed/dQw4w9WgXcQ?autoplay=1";
|
273 |
|
274 |
+
modal.classList.remove('hidden');
|
275 |
+
document.body.style.overflow = 'hidden';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
});
|
278 |
|
279 |
+
// Close modal
|
280 |
+
document.getElementById('close-modal').addEventListener('click', () => {
|
281 |
+
const modal = document.getElementById('video-modal');
|
282 |
+
const videoFrame = document.getElementById('modal-video');
|
283 |
+
|
284 |
+
videoFrame.src = "";
|
285 |
+
modal.classList.add('hidden');
|
286 |
+
document.body.style.overflow = 'auto';
|
287 |
});
|
288 |
|
289 |
+
// Smooth scrolling for all links
|
290 |
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
291 |
+
anchor.addEventListener('click', function (e) {
|
292 |
+
e.preventDefault();
|
293 |
+
|
294 |
+
const targetId = this.getAttribute('href');
|
295 |
+
const targetElement = document.querySelector(targetId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
296 |
|
297 |
+
if (targetElement) {
|
298 |
+
window.scrollTo({
|
299 |
+
top: targetElement.offsetTop - 80,
|
300 |
+
behavior: 'smooth'
|
301 |
+
});
|
302 |
+
}
|
303 |
+
});
|
304 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
305 |
</script>
|
306 |
<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=Cezarxil/client-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
307 |
</html>
|