Cezarxil commited on
Commit
ec6c815
·
verified ·
1 Parent(s): 539da66

Make the username: epiliz ; password: culoareafericiri - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +857 -447
index.html CHANGED
@@ -2,535 +2,945 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>Epiliz Estetique - Client Appointments</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 scrollbar */
11
- ::-webkit-scrollbar {
12
- width: 8px;
 
13
  }
14
- ::-webkit-scrollbar-track {
15
- background: #fce4ec;
16
  }
17
- ::-webkit-scrollbar-thumb {
18
- background: #f48fb1;
19
- border-radius: 4px;
20
  }
21
- ::-webkit-scrollbar-thumb:hover {
22
- background: #f06292;
23
  }
24
-
25
- /* Animation for notifications */
26
- @keyframes fadeIn {
27
- from { opacity: 0; transform: translateY(20px); }
28
- to { opacity: 1; transform: translateY(0); }
29
- }
30
-
31
  .fade-in {
32
- animation: fadeIn 0.3s ease-out forwards;
 
 
 
 
33
  }
34
  </style>
35
  </head>
36
- <body class="bg-pink-50 min-h-screen font-sans">
37
- <div class="container mx-auto px-2 py-4">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  <!-- Header -->
39
- <header class="flex flex-col justify-between items-center mb-4">
40
- <div class="flex items-center mb-2">
41
- <div class="bg-pink-200 p-2 rounded-full mr-2">
42
- <i class="fas fa-spa text-pink-600 text-xl"></i>
 
43
  </div>
44
- <h1 class="text-xl font-bold text-pink-800">Epiliz Estetique</h1>
45
- </div>
46
- <div class="flex items-center space-x-2">
47
- <div class="relative">
48
- <button id="notificationBtn" class="bg-pink-200 p-3 rounded-full text-pink-600 hover:bg-pink-300 transition">
49
- <i class="fas fa-bell"></i>
50
- <span id="notificationCount" class="absolute -top-1 -right-1 bg-pink-600 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center hidden">0</span>
51
  </button>
52
- <div id="notificationDropdown" class="hidden absolute right-0 mt-2 w-64 bg-white rounded-md shadow-lg z-10 border border-pink-200">
53
- <div class="p-4 border-b border-pink-100">
54
- <h3 class="font-semibold text-pink-800">Notifications</h3>
55
- </div>
56
- <div id="notificationList" class="max-h-60 overflow-y-auto">
57
- <!-- Notifications will appear here -->
58
- </div>
59
- </div>
60
- </div>
61
- <div class="bg-pink-200 p-3 rounded-full">
62
- <i class="fas fa-user text-pink-600"></i>
63
  </div>
64
  </div>
65
  </header>
66
 
67
  <!-- Main Content -->
68
- <div class="flex flex-col gap-4">
69
- <!-- Appointment Form -->
70
- <div class="bg-white rounded-lg shadow-md p-4 border border-pink-200">
71
- <h2 class="text-xl font-semibold text-pink-800 mb-6">Schedule New Appointment</h2>
72
- <form id="appointmentForm" class="space-y-4">
73
- <div>
74
- <label for="clientName" class="block text-sm font-medium text-pink-700 mb-1">Client Name</label>
75
- <input type="text" id="clientName" required class="w-full px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent">
76
- </div>
77
-
78
- <div>
79
- <label for="clientPhone" class="block text-sm font-medium text-pink-700 mb-1">Phone Number</label>
80
- <input type="tel" id="clientPhone" required class="w-full px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent">
81
- </div>
82
-
83
- <div>
84
- <label for="serviceType" class="block text-sm font-medium text-pink-700 mb-1">Service</label>
85
- <select id="serviceType" required class="w-full px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent">
86
- <option value="" disabled selected>Select a service</option>
87
- <option value="Facial Laser">Facial Laser</option>
88
- <option value="Underarm Laser">Underarm Laser</option>
89
- <option value="Legs Laser">Legs Laser</option>
90
- <option value="Bikini Laser">Bikini Laser</option>
91
- <option value="Back Laser">Back Laser</option>
92
- </select>
 
 
 
 
 
 
93
  </div>
94
-
95
- <div>
96
- <label for="appointmentDate" class="block text-sm font-medium text-pink-700 mb-1">Date</label>
97
- <input type="date" id="appointmentDate" required class="w-full px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent">
 
 
 
 
 
 
 
 
98
  </div>
99
 
100
- <div>
101
- <label for="appointmentTime" class="block text-sm font-medium text-pink-700 mb-1">Time</label>
102
- <select id="appointmentTime" required class="w-full px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent">
103
- <option value="" disabled selected>Select a time</option>
104
- <option value="09:00">09:00 AM</option>
105
- <option value="10:00">10:00 AM</option>
106
- <option value="11:00">11:00 AM</option>
107
- <option value="12:00">12:00 PM</option>
108
- <option value="13:00">01:00 PM</option>
109
- <option value="14:00">02:00 PM</option>
110
- <option value="15:00">03:00 PM</option>
111
- <option value="16:00">04:00 PM</option>
112
- </select>
113
  </div>
114
-
115
- <div>
116
- <label for="notes" class="block text-sm font-medium text-pink-700 mb-1">Notes</label>
117
- <textarea id="notes" rows="3" class="w-full px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent"></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  </div>
119
-
120
- <button type="submit" class="w-full bg-pink-600 hover:bg-pink-700 text-white font-medium py-2 px-4 rounded-md transition duration-300">
121
- Schedule Appointment
122
- </button>
123
- </form>
124
  </div>
125
 
126
- <!-- Appointments List -->
127
- <div class="lg:col-span-2">
128
- <div class="bg-white rounded-xl shadow-md p-6 border border-pink-200">
129
- <div class="flex justify-between items-center mb-6">
130
- <h2 class="text-xl font-semibold text-pink-800">Upcoming Appointments</h2>
131
  <div class="relative">
132
- <input type="text" id="searchInput" placeholder="Search clients..." class="px-4 py-2 border border-pink-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-transparent">
133
- <i class="fas fa-search absolute right-3 top-3 text-pink-400"></i>
 
134
  </div>
 
 
 
135
  </div>
136
-
137
- <div class="overflow-x-auto -mx-2">
138
- <table class="min-w-full divide-y divide-pink-200 text-sm">
139
- <thead class="bg-pink-50">
 
 
140
  <tr>
141
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-pink-700 uppercase tracking-wider">Client</th>
142
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-pink-700 uppercase tracking-wider">Service</th>
143
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-pink-700 uppercase tracking-wider">Date & Time</th>
144
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-pink-700 uppercase tracking-wider">Status</th>
145
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-pink-700 uppercase tracking-wider">Actions</th>
146
  </tr>
147
  </thead>
148
- <tbody id="appointmentsList" class="bg-white divide-y divide-pink-200">
149
- <!-- Appointments will be added here dynamically -->
150
  </tbody>
151
  </table>
152
  </div>
153
-
154
- <div id="noAppointments" class="text-center py-8 text-pink-500">
155
- <i class="fas fa-calendar-alt text-4xl mb-4"></i>
156
- <p class="text-lg">No appointments scheduled yet</p>
157
- </div>
158
  </div>
159
-
160
- <!-- Calendar View -->
161
- <div class="bg-white rounded-lg shadow-md p-4 mt-4 border border-pink-200">
162
- <h2 class="text-xl font-semibold text-pink-800 mb-6">Calendar View</h2>
163
- <div class="grid grid-cols-7 gap-1 mb-2">
164
- <div class="text-center font-medium text-pink-700 py-2">Sun</div>
165
- <div class="text-center font-medium text-pink-700 py-2">Mon</div>
166
- <div class="text-center font-medium text-pink-700 py-2">Tue</div>
167
- <div class="text-center font-medium text-pink-700 py-2">Wed</div>
168
- <div class="text-center font-medium text-pink-700 py-2">Thu</div>
169
- <div class="text-center font-medium text-pink-700 py-2">Fri</div>
170
- <div class="text-center font-medium text-pink-700 py-2">Sat</div>
171
  </div>
172
- <div id="calendarGrid" class="grid grid-cols-7 gap-1">
173
- <!-- Calendar days will be generated here -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  </div>
175
  </div>
176
  </div>
177
- </div>
178
- </div>
179
 
180
- <!-- Success Modal -->
181
- <div id="successModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
182
- <div class="bg-white rounded-lg p-4 w-full mx-2 transform transition-all fade-in">
183
- <div class="text-center">
184
- <div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
185
- <i class="fas fa-check text-green-600"></i>
186
- </div>
187
- <h3 class="text-lg font-medium text-gray-900 mt-3" id="modalTitle">Appointment Scheduled!</h3>
188
- <div class="mt-2">
189
- <p class="text-sm text-gray-500" id="modalMessage">The appointment has been successfully scheduled.</p>
190
- </div>
191
- <div class="mt-4">
192
- <button type="button" id="modalCloseBtn" class="inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-pink-600 border border-transparent rounded-md hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500">
193
- Close
194
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  </div>
196
  </div>
197
- </div>
198
  </div>
199
 
200
  <script>
201
- document.addEventListener('DOMContentLoaded', function() {
202
- // Sample data
203
- let appointments = JSON.parse(localStorage.getItem('epilizAppointments')) || [];
204
- let notifications = JSON.parse(localStorage.getItem('epilizNotifications')) || [];
205
-
206
- // DOM Elements
207
- const appointmentForm = document.getElementById('appointmentForm');
208
- const appointmentsList = document.getElementById('appointmentsList');
209
- const noAppointments = document.getElementById('noAppointments');
210
- const searchInput = document.getElementById('searchInput');
211
- const notificationBtn = document.getElementById('notificationBtn');
212
- const notificationDropdown = document.getElementById('notificationDropdown');
213
- const notificationList = document.getElementById('notificationList');
214
- const notificationCount = document.getElementById('notificationCount');
215
- const successModal = document.getElementById('successModal');
216
- const modalCloseBtn = document.getElementById('modalCloseBtn');
217
- const calendarGrid = document.getElementById('calendarGrid');
218
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  // Initialize the app
220
- renderAppointments();
221
- renderCalendar();
222
- updateNotificationCount();
223
- renderNotifications();
 
 
 
 
 
 
 
 
 
 
224
 
225
- // Form submission
226
- appointmentForm.addEventListener('submit', function(e) {
227
- e.preventDefault();
228
-
229
- const clientName = document.getElementById('clientName').value;
230
- const clientPhone = document.getElementById('clientPhone').value;
231
- const serviceType = document.getElementById('serviceType').value;
232
- const appointmentDate = document.getElementById('appointmentDate').value;
233
- const appointmentTime = document.getElementById('appointmentTime').value;
234
- const notes = document.getElementById('notes').value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
- const newAppointment = {
237
- id: Date.now(),
238
- clientName,
239
- clientPhone,
240
- serviceType,
241
- date: appointmentDate,
242
- time: appointmentTime,
243
- notes,
244
- status: 'Scheduled',
245
- createdAt: new Date().toISOString()
246
- };
247
 
248
- appointments.push(newAppointment);
249
- saveAppointments();
 
 
250
 
251
- // Add notification
252
- const newNotification = {
253
- id: Date.now(),
254
- message: `New appointment scheduled for ${clientName} (${serviceType})`,
255
- date: new Date().toISOString(),
256
- read: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  };
258
 
259
- notifications.unshift(newNotification);
260
- saveNotifications();
261
-
262
- // Reset form
263
- appointmentForm.reset();
264
-
265
- // Update UI
266
- renderAppointments();
267
  renderCalendar();
268
- updateNotificationCount();
269
- renderNotifications();
270
-
271
- // Show success modal
272
- showModal('Appointment Scheduled!', 'The appointment has been successfully scheduled.');
273
- });
 
 
274
 
275
- // Search functionality
276
- searchInput.addEventListener('input', function() {
277
- renderAppointments();
278
- });
279
 
280
- // Notification dropdown
281
- notificationBtn.addEventListener('click', function() {
282
- notificationDropdown.classList.toggle('hidden');
283
-
284
- // Mark notifications as read when dropdown is opened
285
- notifications = notifications.map(notif => ({...notif, read: true}));
286
- saveNotifications();
287
- updateNotificationCount();
288
- renderNotifications();
289
- });
290
 
291
- // Close modal
292
- modalCloseBtn.addEventListener('click', function() {
293
- successModal.classList.add('hidden');
294
- });
295
 
296
- // Close dropdown when clicking outside
297
- document.addEventListener('click', function(e) {
298
- if (!notificationBtn.contains(e.target) && !notificationDropdown.contains(e.target)) {
299
- notificationDropdown.classList.add('hidden');
300
- }
301
- });
302
 
303
- // Functions
304
- function renderAppointments() {
305
- const searchTerm = searchInput.value.toLowerCase();
306
-
307
- const filteredAppointments = appointments.filter(appointment => {
308
- return (
309
- appointment.clientName.toLowerCase().includes(searchTerm) ||
310
- appointment.serviceType.toLowerCase().includes(searchTerm) ||
311
- appointment.clientPhone.includes(searchTerm)
312
- );
313
- });
314
-
315
- if (filteredAppointments.length === 0) {
316
- noAppointments.classList.remove('hidden');
317
- appointmentsList.innerHTML = '';
318
- return;
319
- }
320
-
321
- noAppointments.classList.add('hidden');
322
 
323
- appointmentsList.innerHTML = filteredAppointments
324
- .sort((a, b) => new Date(a.date + 'T' + a.time) - new Date(b.date + 'T' + b.time))
325
- .map(appointment => {
326
- const date = new Date(appointment.date);
327
- const formattedDate = date.toLocaleDateString('en-US', {
328
- weekday: 'short',
329
- month: 'short',
330
- day: 'numeric'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  });
332
 
333
- const statusClass = {
334
- 'Scheduled': 'bg-blue-100 text-blue-800',
335
- 'Completed': 'bg-green-100 text-green-800',
336
- 'Cancelled': 'bg-red-100 text-red-800',
337
- 'No-show': 'bg-yellow-100 text-yellow-800'
338
- }[appointment.status] || 'bg-gray-100 text-gray-800';
339
 
340
- return `
341
- <tr class="hover:bg-pink-50" data-id="${appointment.id}">
342
- <td class="px-2 py-2 whitespace-nowrap">
343
- <div class="flex items-center">
344
- <div class="flex-shrink-0 h-10 w-10 bg-pink-200 rounded-full flex items-center justify-center">
345
- <i class="fas fa-user text-pink-600"></i>
346
- </div>
347
- <div class="ml-4">
348
- <div class="text-sm font-medium text-pink-900">${appointment.clientName}</div>
349
- <div class="text-sm text-pink-500">${appointment.clientPhone}</div>
350
- </div>
351
- </div>
352
- </td>
353
- <td class="px-6 py-4 whitespace-nowrap">
354
- <div class="text-sm text-pink-900">${appointment.serviceType}</div>
355
- </td>
356
- <td class="px-6 py-4 whitespace-nowrap">
357
- <div class="text-sm text-pink-900">${formattedDate}</div>
358
- <div class="text-sm text-pink-500">${appointment.time}</div>
359
- </td>
360
- <td class="px-6 py-4 whitespace-nowrap">
361
- <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">
362
- ${appointment.status}
363
- </span>
364
- </td>
365
- <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
366
- <button onclick="completeAppointment(${appointment.id})" class="text-green-600 hover:text-green-900 mr-3">
367
- <i class="fas fa-check"></i>
368
- </button>
369
- <button onclick="cancelAppointment(${appointment.id})" class="text-red-600 hover:text-red-900">
370
- <i class="fas fa-times"></i>
371
- </button>
372
- </td>
373
- </tr>
374
- `;
375
- })
376
- .join('');
377
  }
 
 
 
 
 
 
378
 
379
- function renderCalendar() {
380
- const today = new Date();
381
- const currentMonth = today.getMonth();
382
- const currentYear = today.getFullYear();
383
-
384
- // Get first day of month and total days in month
385
- const firstDay = new Date(currentYear, currentMonth, 1).getDay();
386
- const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
387
-
388
- // Get appointments for this month
389
- const monthAppointments = appointments.filter(app => {
390
- const appDate = new Date(app.date);
391
- return appDate.getMonth() === currentMonth && appDate.getFullYear() === currentYear;
392
- });
393
-
394
- calendarGrid.innerHTML = '';
395
-
396
- // Add empty cells for days before the first day of the month
397
- for (let i = 0; i < firstDay; i++) {
398
- calendarGrid.innerHTML += `<div class="h-20 border border-pink-100 bg-pink-50 rounded"></div>`;
399
- }
400
 
401
- // Add cells for each day of the month
402
- for (let day = 1; day <= daysInMonth; day++) {
403
- const dateStr = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
404
- const dayAppointments = monthAppointments.filter(app => app.date === dateStr);
405
-
406
- const isToday = day === today.getDate() && currentMonth === today.getMonth();
407
-
408
- calendarGrid.innerHTML += `
409
- <div class="h-12 border border-pink-100 rounded overflow-hidden ${isToday ? 'bg-pink-100' : ''}">
410
- <div class="text-right p-1 text-sm ${isToday ? 'font-bold text-pink-800' : 'text-pink-700'}">${day}</div>
411
- <div class="overflow-y-auto h-12 text-xs px-1">
412
- ${dayAppointments.map(app => `
413
- <div class="mb-1 truncate bg-pink-200 text-pink-800 rounded px-1">
414
- ${app.time} - ${app.clientName.split(' ')[0]}
415
- </div>
416
- `).join('')}
417
  </div>
418
  </div>
419
- `;
420
- }
421
- }
422
-
423
- function renderNotifications() {
424
- if (notifications.length === 0) {
425
- notificationList.innerHTML = `
426
- <div class="p-4 text-center text-pink-500">
427
- <i class="fas fa-bell-slash text-2xl mb-2"></i>
428
- <p>No notifications yet</p>
429
  </div>
430
  `;
431
- return;
432
- }
 
433
 
434
- notificationList.innerHTML = notifications.map(notification => {
435
- const date = new Date(notification.date);
436
- const formattedDate = date.toLocaleString('en-US', {
437
- month: 'short',
438
- day: 'numeric',
439
- hour: '2-digit',
440
- minute: '2-digit'
441
  });
442
-
443
- return `
444
- <div class="p-3 border-b border-pink-100 ${notification.read ? 'bg-white' : 'bg-pink-50'}">
445
- <div class="flex justify-between">
446
- <p class="text-sm ${notification.read ? 'text-pink-700' : 'font-semibold text-pink-900'}">${notification.message}</p>
447
- <span class="text-xs text-pink-500">${formattedDate}</span>
448
- </div>
449
- </div>
450
- `;
451
- }).join('');
452
  }
453
 
454
- function updateNotificationCount() {
455
- const unreadCount = notifications.filter(n => !n.read).length;
456
- notificationCount.textContent = unreadCount;
457
-
458
- if (unreadCount > 0) {
459
- notificationCount.classList.remove('hidden');
460
- } else {
461
- notificationCount.classList.add('hidden');
462
- }
463
- }
464
 
465
- function showModal(title, message) {
466
- document.getElementById('modalTitle').textContent = title;
467
- document.getElementById('modalMessage').textContent = message;
468
- successModal.classList.remove('hidden');
469
- }
470
 
471
- function saveAppointments() {
472
- localStorage.setItem('epilizAppointments', JSON.stringify(appointments));
 
 
 
 
473
  }
474
 
475
- function saveNotifications() {
476
- localStorage.setItem('epilizNotifications', JSON.stringify(notifications));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  }
478
 
479
- // Global functions for buttons in table rows
480
- window.completeAppointment = function(id) {
481
- const appointment = appointments.find(app => app.id === id);
482
- if (appointment) {
483
- appointment.status = 'Completed';
484
- saveAppointments();
485
- renderAppointments();
486
-
487
- // Add notification
488
- const newNotification = {
489
- id: Date.now(),
490
- message: `Appointment completed for ${appointment.clientName}`,
491
- date: new Date().toISOString(),
492
- read: false
493
- };
494
-
495
- notifications.unshift(newNotification);
496
- saveNotifications();
497
- updateNotificationCount();
498
- renderNotifications();
499
-
500
- showModal('Appointment Completed!', `The appointment for ${appointment.clientName} has been marked as completed.`);
501
- }
502
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
 
504
- window.cancelAppointment = function(id) {
505
- const appointment = appointments.find(app => app.id === id);
506
- if (appointment) {
507
- appointment.status = 'Cancelled';
508
- saveAppointments();
509
- renderAppointments();
510
-
511
- // Add notification
512
- const newNotification = {
513
- id: Date.now(),
514
- message: `Appointment cancelled for ${appointment.clientName}`,
515
- date: new Date().toISOString(),
516
- read: false
517
- };
518
-
519
- notifications.unshift(newNotification);
520
- saveNotifications();
521
- updateNotificationCount();
522
- renderNotifications();
523
-
524
- showModal('Appointment Cancelled!', `The appointment for ${appointment.clientName} has been cancelled.`);
525
- }
526
- };
527
 
528
- // Set today's date as default in date picker
529
- const today = new Date();
530
- const formattedDate = today.toISOString().split('T')[0];
531
- document.getElementById('appointmentDate').value = formattedDate;
532
- document.getElementById('appointmentDate').min = formattedDate;
533
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  </script>
535
  <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/awesome-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
536
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Laser Hair Removal Salon Scheduler</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 styles that can't be done with Tailwind */
11
+ .calendar-day:hover {
12
+ background-color: #f3f4f6;
13
+ cursor: pointer;
14
  }
15
+ .calendar-day.has-appointments {
16
+ background-color: #e5e7eb;
17
  }
18
+ .appointment-item:hover {
19
+ background-color: #f3f4f6;
 
20
  }
21
+ #calendar {
22
+ min-height: 500px;
23
  }
 
 
 
 
 
 
 
24
  .fade-in {
25
+ animation: fadeIn 0.3s ease-in-out;
26
+ }
27
+ @keyframes fadeIn {
28
+ from { opacity: 0; }
29
+ to { opacity: 1; }
30
  }
31
  </style>
32
  </head>
33
+ <body class="bg-gray-100 font-sans">
34
+ <!-- Login Screen (shown by default) -->
35
+ <div id="login-screen" class="fixed inset-0 flex items-center justify-center bg-gray-900 bg-opacity-50 z-50">
36
+ <div class="bg-white p-8 rounded-lg shadow-lg w-full max-w-md">
37
+ <div class="text-center mb-6">
38
+ <i class="fas fa-spa text-4xl text-purple-600 mb-2"></i>
39
+ <h1 class="text-2xl font-bold text-gray-800">Laser Elegance Salon</h1>
40
+ <p class="text-gray-600">Staff Login</p>
41
+ </div>
42
+ <form id="login-form" class="space-y-4">
43
+ <div>
44
+ <label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
45
+ <input type="text" id="username" name="username" required
46
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
47
+ </div>
48
+ <div>
49
+ <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
50
+ <input type="password" id="password" name="password" required
51
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
52
+ </div>
53
+ <button type="submit"
54
+ class="w-full bg-purple-600 text-white py-2 px-4 rounded-md hover:bg-purple-700 transition duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2">
55
+ Login
56
+ </button>
57
+ </form>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Main App Container (hidden by default) -->
62
+ <div id="app-container" class="hidden">
63
  <!-- Header -->
64
+ <header class="bg-white shadow-sm">
65
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
66
+ <div class="flex items-center space-x-2">
67
+ <i class="fas fa-spa text-2xl text-purple-600"></i>
68
+ <h1 class="text-xl font-bold text-gray-800">Laser Elegance Scheduler</h1>
69
  </div>
70
+ <div class="flex items-center space-x-4">
71
+ <button id="logout-btn" class="flex items-center text-gray-600 hover:text-purple-600">
72
+ <i class="fas fa-sign-out-alt mr-1"></i>
73
+ <span>Logout</span>
 
 
 
74
  </button>
 
 
 
 
 
 
 
 
 
 
 
75
  </div>
76
  </div>
77
  </header>
78
 
79
  <!-- Main Content -->
80
+ <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
81
+ <!-- Tabs Navigation -->
82
+ <div class="border-b border-gray-200 mb-6">
83
+ <nav class="-mb-px flex space-x-8">
84
+ <button id="calendar-tab" class="tab-button active border-purple-500 text-purple-600 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
85
+ <i class="fas fa-calendar-alt mr-2"></i>Calendar
86
+ </button>
87
+ <button id="appointments-tab" class="tab-button border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
88
+ <i class="fas fa-list mr-2"></i>Appointments
89
+ </button>
90
+ <button id="new-appointment-tab" class="tab-button border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
91
+ <i class="fas fa-plus-circle mr-2"></i>New Appointment
92
+ </button>
93
+ </nav>
94
+ </div>
95
+
96
+ <!-- Calendar Tab Content -->
97
+ <div id="calendar-content" class="tab-content fade-in">
98
+ <div class="flex justify-between items-center mb-6">
99
+ <h2 class="text-xl font-semibold text-gray-800">Appointment Calendar</h2>
100
+ <div class="flex items-center space-x-4">
101
+ <button id="prev-month" class="p-2 rounded-full hover:bg-gray-200">
102
+ <i class="fas fa-chevron-left"></i>
103
+ </button>
104
+ <span id="current-month" class="font-medium">July 2023</span>
105
+ <button id="next-month" class="p-2 rounded-full hover:bg-gray-200">
106
+ <i class="fas fa-chevron-right"></i>
107
+ </button>
108
+ <button id="today-btn" class="px-3 py-1 bg-gray-200 rounded-md text-sm hover:bg-gray-300">
109
+ Today
110
+ </button>
111
  </div>
112
+ </div>
113
+
114
+ <div class="bg-white rounded-lg shadow overflow-hidden">
115
+ <!-- Calendar Header (Days of Week) -->
116
+ <div class="grid grid-cols-7 bg-gray-100 border-b border-gray-200">
117
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Sun</div>
118
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Mon</div>
119
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Tue</div>
120
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Wed</div>
121
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Thu</div>
122
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Fri</div>
123
+ <div class="py-2 text-center font-medium text-sm text-gray-600">Sat</div>
124
  </div>
125
 
126
+ <!-- Calendar Grid -->
127
+ <div id="calendar" class="grid grid-cols-7 grid-rows-5 border border-gray-200 bg-white">
128
+ <!-- Calendar days will be dynamically inserted here -->
 
 
 
 
 
 
 
 
 
 
129
  </div>
130
+ </div>
131
+
132
+ <!-- Day Appointments Modal -->
133
+ <div id="day-appointments-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50 hidden">
134
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md max-h-[80vh] overflow-y-auto">
135
+ <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
136
+ <h3 id="modal-day-title" class="text-lg font-semibold text-gray-800">Appointments for July 15, 2023</h3>
137
+ <button id="close-day-modal" class="text-gray-500 hover:text-gray-700">
138
+ <i class="fas fa-times"></i>
139
+ </button>
140
+ </div>
141
+ <div id="day-appointments-list" class="p-4 space-y-3">
142
+ <!-- Appointments will be inserted here -->
143
+ </div>
144
+ <div class="px-6 py-3 border-t border-gray-200 flex justify-end">
145
+ <button id="add-appointment-for-day" class="px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700">
146
+ Add Appointment
147
+ </button>
148
+ </div>
149
  </div>
150
+ </div>
 
 
 
 
151
  </div>
152
 
153
+ <!-- Appointments List Tab Content -->
154
+ <div id="appointments-content" class="tab-content hidden fade-in">
155
+ <div class="flex justify-between items-center mb-6">
156
+ <h2 class="text-xl font-semibold text-gray-800">Upcoming Appointments</h2>
157
+ <div class="flex items-center space-x-4">
158
  <div class="relative">
159
+ <input type="text" id="search-appointments" placeholder="Search clients..."
160
+ class="pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
161
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
162
  </div>
163
+ <button id="export-csv" class="px-3 py-1 bg-gray-200 rounded-md text-sm hover:bg-gray-300 flex items-center">
164
+ <i class="fas fa-file-export mr-2"></i> Export CSV
165
+ </button>
166
  </div>
167
+ </div>
168
+
169
+ <div class="bg-white rounded-lg shadow overflow-hidden">
170
+ <div class="overflow-x-auto">
171
+ <table class="min-w-full divide-y divide-gray-200">
172
+ <thead class="bg-gray-50">
173
  <tr>
174
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
175
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Service</th>
176
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date & Time</th>
177
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contact</th>
178
+ <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
179
  </tr>
180
  </thead>
181
+ <tbody id="appointments-table-body" class="bg-white divide-y divide-gray-200">
182
+ <!-- Appointments will be inserted here -->
183
  </tbody>
184
  </table>
185
  </div>
 
 
 
 
 
186
  </div>
187
+ </div>
188
+
189
+ <!-- New Appointment Tab Content -->
190
+ <div id="new-appointment-content" class="tab-content hidden fade-in">
191
+ <div class="bg-white rounded-lg shadow overflow-hidden">
192
+ <div class="px-6 py-4 border-b border-gray-200">
193
+ <h2 class="text-xl font-semibold text-gray-800">New Appointment</h2>
 
 
 
 
 
194
  </div>
195
+ <div class="p-6">
196
+ <form id="new-appointment-form" class="space-y-6">
197
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
198
+ <div>
199
+ <label for="client-name" class="block text-sm font-medium text-gray-700 mb-1">Client Full Name *</label>
200
+ <input type="text" id="client-name" name="client-name" required
201
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
202
+ </div>
203
+ <div>
204
+ <label for="client-phone" class="block text-sm font-medium text-gray-700 mb-1">Phone Number *</label>
205
+ <input type="tel" id="client-phone" name="client-phone" required
206
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
207
+ </div>
208
+ <div>
209
+ <label for="client-email" class="block text-sm font-medium text-gray-700 mb-1">Email (Optional)</label>
210
+ <input type="email" id="client-email" name="client-email"
211
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
212
+ </div>
213
+ <div>
214
+ <label for="service-package" class="block text-sm font-medium text-gray-700 mb-1">Service Package *</label>
215
+ <select id="service-package" name="service-package" required
216
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
217
+ <option value="">Select a package</option>
218
+ <option value="Full Body">Full Body</option>
219
+ <option value="Legs Only">Legs Only</option>
220
+ <option value="Underarms">Underarms</option>
221
+ <option value="Bikini Line">Bikini Line</option>
222
+ <option value="Brazilian">Brazilian</option>
223
+ <option value="Face">Face</option>
224
+ <option value="Back">Back</option>
225
+ <option value="Chest">Chest</option>
226
+ </select>
227
+ </div>
228
+ <div>
229
+ <label for="appointment-date" class="block text-sm font-medium text-gray-700 mb-1">Date *</label>
230
+ <input type="date" id="appointment-date" name="appointment-date" required
231
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
232
+ </div>
233
+ <div>
234
+ <label for="appointment-time" class="block text-sm font-medium text-gray-700 mb-1">Time *</label>
235
+ <input type="time" id="appointment-time" name="appointment-time" required
236
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
237
+ </div>
238
+ </div>
239
+ <div>
240
+ <label for="appointment-notes" class="block text-sm font-medium text-gray-700 mb-1">Notes (Optional)</label>
241
+ <textarea id="appointment-notes" name="appointment-notes" rows="3"
242
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"></textarea>
243
+ </div>
244
+ <div class="flex justify-end space-x-4">
245
+ <button type="reset" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">
246
+ Clear
247
+ </button>
248
+ <button type="submit" class="px-6 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700">
249
+ Schedule Appointment
250
+ </button>
251
+ </div>
252
+ </form>
253
  </div>
254
  </div>
255
  </div>
 
 
256
 
257
+ <!-- Edit Appointment Modal -->
258
+ <div id="edit-appointment-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50 hidden">
259
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md max-h-[80vh] overflow-y-auto">
260
+ <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
261
+ <h3 class="text-lg font-semibold text-gray-800">Edit Appointment</h3>
262
+ <button id="close-edit-modal" class="text-gray-500 hover:text-gray-700">
263
+ <i class="fas fa-times"></i>
264
+ </button>
265
+ </div>
266
+ <div class="p-6">
267
+ <form id="edit-appointment-form" class="space-y-6">
268
+ <input type="hidden" id="edit-appointment-id">
269
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
270
+ <div>
271
+ <label for="edit-client-name" class="block text-sm font-medium text-gray-700 mb-1">Client Full Name *</label>
272
+ <input type="text" id="edit-client-name" name="edit-client-name" required
273
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
274
+ </div>
275
+ <div>
276
+ <label for="edit-client-phone" class="block text-sm font-medium text-gray-700 mb-1">Phone Number *</label>
277
+ <input type="tel" id="edit-client-phone" name="edit-client-phone" required
278
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
279
+ </div>
280
+ <div>
281
+ <label for="edit-client-email" class="block text-sm font-medium text-gray-700 mb-1">Email (Optional)</label>
282
+ <input type="email" id="edit-client-email" name="edit-client-email"
283
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
284
+ </div>
285
+ <div>
286
+ <label for="edit-service-package" class="block text-sm font-medium text-gray-700 mb-1">Service Package *</label>
287
+ <select id="edit-service-package" name="edit-service-package" required
288
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
289
+ <option value="">Select a package</option>
290
+ <option value="Full Body">Full Body</option>
291
+ <option value="Legs Only">Legs Only</option>
292
+ <option value="Underarms">Underarms</option>
293
+ <option value="Bikini Line">Bikini Line</option>
294
+ <option value="Brazilian">Brazilian</option>
295
+ <option value="Face">Face</option>
296
+ <option value="Back">Back</option>
297
+ <option value="Chest">Chest</option>
298
+ </select>
299
+ </div>
300
+ <div>
301
+ <label for="edit-appointment-date" class="block text-sm font-medium text-gray-700 mb-1">Date *</label>
302
+ <input type="date" id="edit-appointment-date" name="edit-appointment-date" required
303
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
304
+ </div>
305
+ <div>
306
+ <label for="edit-appointment-time" class="block text-sm font-medium text-gray-700 mb-1">Time *</label>
307
+ <input type="time" id="edit-appointment-time" name="edit-appointment-time" required
308
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
309
+ </div>
310
+ </div>
311
+ <div>
312
+ <label for="edit-appointment-notes" class="block text-sm font-medium text-gray-700 mb-1">Notes (Optional)</label>
313
+ <textarea id="edit-appointment-notes" name="edit-appointment-notes" rows="3"
314
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"></textarea>
315
+ </div>
316
+ <div class="flex justify-end space-x-4">
317
+ <button type="button" id="cancel-edit" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">
318
+ Cancel
319
+ </button>
320
+ <button type="submit" class="px-6 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700">
321
+ Update Appointment
322
+ </button>
323
+ <button type="button" id="delete-appointment" class="px-6 py-2 bg-red-600 text-white rounded-md hover:bg-red-700">
324
+ Delete
325
+ </button>
326
+ </div>
327
+ </form>
328
+ </div>
329
  </div>
330
  </div>
331
+ </main>
332
  </div>
333
 
334
  <script>
335
+ // Mock data for demonstration
336
+ let appointments = [
337
+ {
338
+ id: 1,
339
+ clientName: "Sarah Johnson",
340
+ phone: "555-123-4567",
341
+ email: "sarah.[email protected]",
342
+ service: "Full Body",
343
+ date: "2023-07-15",
344
+ time: "10:00",
345
+ notes: "First session, sensitive skin"
346
+ },
347
+ {
348
+ id: 2,
349
+ clientName: "Michael Chen",
350
+ phone: "555-987-6543",
351
+ email: "michael.[email protected]",
352
+ service: "Back",
353
+ date: "2023-07-15",
354
+ time: "14:30",
355
+ notes: "Follow-up session"
356
+ },
357
+ {
358
+ id: 3,
359
+ clientName: "Emily Rodriguez",
360
+ phone: "555-456-7890",
361
+ email: "",
362
+ service: "Legs Only",
363
+ date: "2023-07-18",
364
+ time: "11:15",
365
+ notes: ""
366
+ },
367
+ {
368
+ id: 4,
369
+ clientName: "David Kim",
370
+ phone: "555-789-0123",
371
+ email: "[email protected]",
372
+ service: "Underarms",
373
+ date: "2023-07-20",
374
+ time: "09:00",
375
+ notes: "Allergic to aloe vera"
376
+ },
377
+ {
378
+ id: 5,
379
+ clientName: "Jessica Williams",
380
+ phone: "555-234-5678",
381
+ email: "[email protected]",
382
+ service: "Brazilian",
383
+ date: "2023-07-22",
384
+ time: "13:45",
385
+ notes: "Prefer cold gel"
386
+ }
387
+ ];
388
+
389
+ // Current user session
390
+ let currentUser = null;
391
+
392
+ // Current month and year for calendar
393
+ let currentDate = new Date();
394
+ let currentMonth = currentDate.getMonth();
395
+ let currentYear = currentDate.getFullYear();
396
+
397
+ // DOM Elements
398
+ const loginScreen = document.getElementById('login-screen');
399
+ const appContainer = document.getElementById('app-container');
400
+ const loginForm = document.getElementById('login-form');
401
+ const logoutBtn = document.getElementById('logout-btn');
402
+
403
+ // Tab elements
404
+ const tabButtons = document.querySelectorAll('.tab-button');
405
+ const tabContents = document.querySelectorAll('.tab-content');
406
+
407
+ // Calendar elements
408
+ const calendar = document.getElementById('calendar');
409
+ const currentMonthDisplay = document.getElementById('current-month');
410
+ const prevMonthBtn = document.getElementById('prev-month');
411
+ const nextMonthBtn = document.getElementById('next-month');
412
+ const todayBtn = document.getElementById('today-btn');
413
+
414
+ // Appointments list elements
415
+ const appointmentsTableBody = document.getElementById('appointments-table-body');
416
+ const searchAppointments = document.getElementById('search-appointments');
417
+ const exportCsvBtn = document.getElementById('export-csv');
418
+
419
+ // New appointment form elements
420
+ const newAppointmentForm = document.getElementById('new-appointment-form');
421
+
422
+ // Modal elements
423
+ const dayAppointmentsModal = document.getElementById('day-appointments-modal');
424
+ const modalDayTitle = document.getElementById('modal-day-title');
425
+ const dayAppointmentsList = document.getElementById('day-appointments-list');
426
+ const closeDayModal = document.getElementById('close-day-modal');
427
+ const addAppointmentForDay = document.getElementById('add-appointment-for-day');
428
+
429
+ const editAppointmentModal = document.getElementById('edit-appointment-modal');
430
+ const editAppointmentForm = document.getElementById('edit-appointment-form');
431
+ const closeEditModal = document.getElementById('close-edit-modal');
432
+ const cancelEdit = document.getElementById('cancel-edit');
433
+ const deleteAppointmentBtn = document.getElementById('delete-appointment');
434
+
435
+ // Event Listeners
436
+ document.addEventListener('DOMContentLoaded', () => {
437
  // Initialize the app
438
+ if (localStorage.getItem('laserSalonUser')) {
439
+ currentUser = JSON.parse(localStorage.getItem('laserSalonUser'));
440
+ loginScreen.classList.add('hidden');
441
+ appContainer.classList.remove('hidden');
442
+ renderCalendar();
443
+ renderAppointmentsList();
444
+ }
445
+ });
446
+
447
+ // Login/Logout
448
+ loginForm.addEventListener('submit', (e) => {
449
+ e.preventDefault();
450
+ const username = document.getElementById('username').value;
451
+ const password = document.getElementById('password').value;
452
 
453
+ // Simple validation (in a real app, this would be a backend call)
454
+ if (username === 'epiliz' && password === 'culoareafericiri') {
455
+ currentUser = { username: 'admin', name: 'Admin User' };
456
+ localStorage.setItem('laserSalonUser', JSON.stringify(currentUser));
457
+ loginScreen.classList.add('hidden');
458
+ appContainer.classList.remove('hidden');
459
+ renderCalendar();
460
+ renderAppointmentsList();
461
+ } else {
462
+ alert('Invalid credentials. Try epiliz/culoareafericiri');
463
+ }
464
+ });
465
+
466
+ logoutBtn.addEventListener('click', () => {
467
+ currentUser = null;
468
+ localStorage.removeItem('laserSalonUser');
469
+ loginScreen.classList.remove('hidden');
470
+ appContainer.classList.add('hidden');
471
+ loginForm.reset();
472
+ });
473
+
474
+ // Tab switching
475
+ tabButtons.forEach(button => {
476
+ button.addEventListener('click', () => {
477
+ // Remove active class from all buttons
478
+ tabButtons.forEach(btn => {
479
+ btn.classList.remove('active', 'border-purple-500', 'text-purple-600');
480
+ btn.classList.add('border-transparent', 'text-gray-500');
481
+ });
482
 
483
+ // Add active class to clicked button
484
+ button.classList.add('active', 'border-purple-500', 'text-purple-600');
485
+ button.classList.remove('border-transparent', 'text-gray-500');
 
 
 
 
 
 
 
 
486
 
487
+ // Hide all tab contents
488
+ tabContents.forEach(content => {
489
+ content.classList.add('hidden');
490
+ });
491
 
492
+ // Show the corresponding tab content
493
+ const tabId = button.id.replace('-tab', '-content');
494
+ document.getElementById(tabId).classList.remove('hidden');
495
+ });
496
+ });
497
+
498
+ // Calendar navigation
499
+ prevMonthBtn.addEventListener('click', () => {
500
+ currentMonth--;
501
+ if (currentMonth < 0) {
502
+ currentMonth = 11;
503
+ currentYear--;
504
+ }
505
+ renderCalendar();
506
+ });
507
+
508
+ nextMonthBtn.addEventListener('click', () => {
509
+ currentMonth++;
510
+ if (currentMonth > 11) {
511
+ currentMonth = 0;
512
+ currentYear++;
513
+ }
514
+ renderCalendar();
515
+ });
516
+
517
+ todayBtn.addEventListener('click', () => {
518
+ currentDate = new Date();
519
+ currentMonth = currentDate.getMonth();
520
+ currentYear = currentDate.getFullYear();
521
+ renderCalendar();
522
+ });
523
+
524
+ // Close modals
525
+ closeDayModal.addEventListener('click', () => {
526
+ dayAppointmentsModal.classList.add('hidden');
527
+ });
528
+
529
+ closeEditModal.addEventListener('click', () => {
530
+ editAppointmentModal.classList.add('hidden');
531
+ });
532
+
533
+ cancelEdit.addEventListener('click', () => {
534
+ editAppointmentModal.classList.add('hidden');
535
+ });
536
+
537
+ // Add appointment for specific day
538
+ addAppointmentForDay.addEventListener('click', () => {
539
+ const day = modalDayTitle.textContent.match(/(\w+ \d{1,2}, \d{4})/)[0];
540
+ const date = new Date(day);
541
+ const formattedDate = formatDateForInput(date);
542
+
543
+ // Switch to new appointment tab
544
+ tabButtons.forEach(btn => btn.classList.remove('active', 'border-purple-500', 'text-purple-600'));
545
+ document.getElementById('new-appointment-tab').classList.add('active', 'border-purple-500', 'text-purple-600');
546
+ tabContents.forEach(content => content.classList.add('hidden'));
547
+ document.getElementById('new-appointment-content').classList.remove('hidden');
548
+
549
+ // Set the date in the form
550
+ document.getElementById('appointment-date').value = formattedDate;
551
+
552
+ // Close the modal
553
+ dayAppointmentsModal.classList.add('hidden');
554
+ });
555
+
556
+ // New appointment form
557
+ newAppointmentForm.addEventListener('submit', (e) => {
558
+ e.preventDefault();
559
+
560
+ const newAppointment = {
561
+ id: appointments.length > 0 ? Math.max(...appointments.map(a => a.id)) + 1 : 1,
562
+ clientName: document.getElementById('client-name').value,
563
+ phone: document.getElementById('client-phone').value,
564
+ email: document.getElementById('client-email').value,
565
+ service: document.getElementById('service-package').value,
566
+ date: document.getElementById('appointment-date').value,
567
+ time: document.getElementById('appointment-time').value,
568
+ notes: document.getElementById('appointment-notes').value
569
+ };
570
+
571
+ appointments.push(newAppointment);
572
+ newAppointmentForm.reset();
573
+
574
+ // Show success message
575
+ alert('Appointment scheduled successfully!');
576
+
577
+ // Update views
578
+ renderCalendar();
579
+ renderAppointmentsList();
580
+
581
+ // Switch to appointments tab
582
+ tabButtons.forEach(btn => btn.classList.remove('active', 'border-purple-500', 'text-purple-600'));
583
+ document.getElementById('appointments-tab').classList.add('active', 'border-purple-500', 'text-purple-600');
584
+ tabContents.forEach(content => content.classList.add('hidden'));
585
+ document.getElementById('appointments-content').classList.remove('hidden');
586
+ });
587
+
588
+ // Search appointments
589
+ searchAppointments.addEventListener('input', () => {
590
+ renderAppointmentsList();
591
+ });
592
+
593
+ // Export to CSV
594
+ exportCsvBtn.addEventListener('click', () => {
595
+ exportToCsv();
596
+ });
597
+
598
+ // Delete appointment
599
+ deleteAppointmentBtn.addEventListener('click', () => {
600
+ const appointmentId = parseInt(document.getElementById('edit-appointment-id').value);
601
+ if (confirm('Are you sure you want to delete this appointment?')) {
602
+ appointments = appointments.filter(a => a.id !== appointmentId);
603
+ editAppointmentModal.classList.add('hidden');
604
+ renderCalendar();
605
+ renderAppointmentsList();
606
+ }
607
+ });
608
+
609
+ // Edit appointment form
610
+ editAppointmentForm.addEventListener('submit', (e) => {
611
+ e.preventDefault();
612
+
613
+ const appointmentId = parseInt(document.getElementById('edit-appointment-id').value);
614
+ const appointmentIndex = appointments.findIndex(a => a.id === appointmentId);
615
+
616
+ if (appointmentIndex !== -1) {
617
+ appointments[appointmentIndex] = {
618
+ id: appointmentId,
619
+ clientName: document.getElementById('edit-client-name').value,
620
+ phone: document.getElementById('edit-client-phone').value,
621
+ email: document.getElementById('edit-client-email').value,
622
+ service: document.getElementById('edit-service-package').value,
623
+ date: document.getElementById('edit-appointment-date').value,
624
+ time: document.getElementById('edit-appointment-time').value,
625
+ notes: document.getElementById('edit-appointment-notes').value
626
  };
627
 
628
+ editAppointmentModal.classList.add('hidden');
 
 
 
 
 
 
 
629
  renderCalendar();
630
+ renderAppointmentsList();
631
+ }
632
+ });
633
+
634
+ // Functions
635
+ function renderCalendar() {
636
+ // Update month/year display
637
+ currentMonthDisplay.textContent = new Date(currentYear, currentMonth).toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
638
 
639
+ // Clear the calendar
640
+ calendar.innerHTML = '';
 
 
641
 
642
+ // Get first day of month and total days in month
643
+ const firstDay = new Date(currentYear, currentMonth, 1);
644
+ const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
 
 
 
 
 
 
 
645
 
646
+ // Get day of week for first day (0 = Sunday, 6 = Saturday)
647
+ const firstDayOfWeek = firstDay.getDay();
 
 
648
 
649
+ // Get days from previous month to display
650
+ const prevMonthDays = new Date(currentYear, currentMonth, 0).getDate();
 
 
 
 
651
 
652
+ // Create calendar grid
653
+ let dayCount = 1;
654
+ let nextMonthDay = 1;
655
+
656
+ for (let i = 0; i < 35; i++) { // 5 rows x 7 days
657
+ const dayElement = document.createElement('div');
658
+ dayElement.className = 'calendar-day p-2 border border-gray-200 h-24 overflow-y-auto';
 
 
 
 
 
 
 
 
 
 
 
 
659
 
660
+ if (i < firstDayOfWeek) {
661
+ // Days from previous month
662
+ const prevDay = prevMonthDays - (firstDayOfWeek - i - 1);
663
+ dayElement.innerHTML = `<div class="text-right text-gray-400">${prevDay}</div>`;
664
+ dayElement.classList.add('text-gray-400');
665
+ } else if (dayCount > daysInMonth) {
666
+ // Days from next month
667
+ dayElement.innerHTML = `<div class="text-right text-gray-400">${nextMonthDay}</div>`;
668
+ dayElement.classList.add('text-gray-400');
669
+ nextMonthDay++;
670
+ } else {
671
+ // Current month days
672
+ dayElement.innerHTML = `<div class="text-right font-medium">${dayCount}</div>`;
673
+
674
+ // Highlight today
675
+ const today = new Date();
676
+ if (dayCount === today.getDate() && currentMonth === today.getMonth() && currentYear === today.getFullYear()) {
677
+ dayElement.classList.add('bg-purple-100');
678
+ }
679
+
680
+ // Check for appointments on this day
681
+ const formattedDate = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(dayCount).padStart(2, '0')}`;
682
+ const dayAppointments = appointments.filter(a => a.date === formattedDate);
683
+
684
+ if (dayAppointments.length > 0) {
685
+ dayElement.classList.add('has-appointments');
686
+
687
+ // Add click event to show appointments for the day
688
+ dayElement.addEventListener('click', () => {
689
+ showDayAppointments(formattedDate, dayCount);
690
  });
691
 
692
+ // Display appointment count
693
+ const appointmentCount = document.createElement('div');
694
+ appointmentCount.className = 'text-xs mt-1 text-purple-600 font-medium';
695
+ appointmentCount.textContent = `${dayAppointments.length} appointment${dayAppointments.length !== 1 ? 's' : ''}`;
696
+ dayElement.appendChild(appointmentCount);
 
697
 
698
+ // Display first 2 appointments (if space allows)
699
+ for (let j = 0; j < Math.min(dayAppointments.length, 2); j++) {
700
+ const appointment = dayAppointments[j];
701
+ const appointmentElement = document.createElement('div');
702
+ appointmentElement.className = 'text-xs mt-1 truncate bg-purple-50 px-1 py-0.5 rounded';
703
+ appointmentElement.textContent = `${appointment.time} - ${appointment.clientName.split(' ')[0]}`;
704
+ appointmentElement.title = `${appointment.time} - ${appointment.clientName}: ${appointment.service}`;
705
+ dayElement.appendChild(appointmentElement);
706
+ }
707
+
708
+ if (dayAppointments.length > 2) {
709
+ const moreElement = document.createElement('div');
710
+ moreElement.className = 'text-xs mt-1 text-gray-500';
711
+ moreElement.textContent = `+${dayAppointments.length - 2} more`;
712
+ dayElement.appendChild(moreElement);
713
+ }
714
+ } else {
715
+ // Add click event to add new appointment
716
+ dayElement.addEventListener('click', () => {
717
+ showDayAppointments(formattedDate, dayCount);
718
+ });
719
+ }
720
+
721
+ dayCount++;
722
+ }
723
+
724
+ calendar.appendChild(dayElement);
 
 
 
 
 
 
 
 
 
 
725
  }
726
+ }
727
+
728
+ function showDayAppointments(date, day) {
729
+ const formattedDate = new Date(date);
730
+ const dayName = formattedDate.toLocaleDateString('en-US', { weekday: 'long' });
731
+ const monthName = formattedDate.toLocaleDateString('en-US', { month: 'long' });
732
 
733
+ modalDayTitle.textContent = `Appointments for ${dayName}, ${monthName} ${day}, ${currentYear}`;
734
+
735
+ // Filter appointments for this day
736
+ const dayAppointments = appointments.filter(a => a.date === date);
737
+
738
+ // Clear previous appointments
739
+ dayAppointmentsList.innerHTML = '';
740
+
741
+ if (dayAppointments.length === 0) {
742
+ dayAppointmentsList.innerHTML = '<p class="text-gray-500 text-center py-4">No appointments scheduled for this day.</p>';
743
+ } else {
744
+ // Sort appointments by time
745
+ dayAppointments.sort((a, b) => a.time.localeCompare(b.time));
 
 
 
 
 
 
 
 
746
 
747
+ // Display each appointment
748
+ dayAppointments.forEach(appointment => {
749
+ const appointmentElement = document.createElement('div');
750
+ appointmentElement.className = 'appointment-item bg-white border border-gray-200 rounded-md p-3';
751
+ appointmentElement.innerHTML = `
752
+ <div class="flex justify-between items-start">
753
+ <div>
754
+ <h4 class="font-medium text-gray-800">${appointment.clientName}</h4>
755
+ <p class="text-sm text-gray-600">${appointment.service} ${appointment.time}</p>
756
+ ${appointment.notes ? `<p class="text-xs text-gray-500 mt-1">${appointment.notes}</p>` : ''}
757
+ </div>
758
+ <div class="flex space-x-2">
759
+ <button class="edit-appointment text-purple-600 hover:text-purple-800" data-id="${appointment.id}">
760
+ <i class="fas fa-edit"></i>
761
+ </button>
 
762
  </div>
763
  </div>
764
+ <div class="mt-2 flex items-center text-xs text-gray-500">
765
+ <i class="fas fa-phone-alt mr-1"></i>
766
+ <span class="mr-3">${appointment.phone}</span>
767
+ ${appointment.email ? `<i class="fas fa-envelope mr-1"></i><span>${appointment.email}</span>` : ''}
 
 
 
 
 
 
768
  </div>
769
  `;
770
+
771
+ dayAppointmentsList.appendChild(appointmentElement);
772
+ });
773
 
774
+ // Add event listeners to edit buttons
775
+ document.querySelectorAll('.edit-appointment').forEach(button => {
776
+ button.addEventListener('click', (e) => {
777
+ const appointmentId = parseInt(button.getAttribute('data-id'));
778
+ showEditAppointmentModal(appointmentId);
779
+ e.stopPropagation();
 
780
  });
781
+ });
 
 
 
 
 
 
 
 
 
782
  }
783
 
784
+ // Show the modal
785
+ dayAppointmentsModal.classList.remove('hidden');
786
+ }
787
+
788
+ function renderAppointmentsList() {
789
+ // Clear previous appointments
790
+ appointmentsTableBody.innerHTML = '';
 
 
 
791
 
792
+ // Filter appointments based on search
793
+ const searchTerm = searchAppointments.value.toLowerCase();
794
+ let filteredAppointments = [...appointments];
 
 
795
 
796
+ if (searchTerm) {
797
+ filteredAppointments = appointments.filter(a =>
798
+ a.clientName.toLowerCase().includes(searchTerm) ||
799
+ a.phone.includes(searchTerm) ||
800
+ (a.email && a.email.toLowerCase().includes(searchTerm))
801
+ );
802
  }
803
 
804
+ // Sort appointments by date and time (soonest first)
805
+ filteredAppointments.sort((a, b) => {
806
+ const dateA = new Date(`${a.date}T${a.time}`);
807
+ const dateB = new Date(`${b.date}T${b.time}`);
808
+ return dateA - dateB;
809
+ });
810
+
811
+ if (filteredAppointments.length === 0) {
812
+ const row = document.createElement('tr');
813
+ row.innerHTML = `
814
+ <td colspan="5" class="px-6 py-4 text-center text-gray-500">
815
+ No appointments found. Try a different search term or create a new appointment.
816
+ </td>
817
+ `;
818
+ appointmentsTableBody.appendChild(row);
819
+ return;
820
  }
821
 
822
+ // Display each appointment
823
+ filteredAppointments.forEach(appointment => {
824
+ const appointmentDate = new Date(`${appointment.date}T${appointment.time}`);
825
+ const formattedDate = appointmentDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
826
+ const formattedTime = appointmentDate.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
827
+
828
+ const row = document.createElement('tr');
829
+ row.className = 'hover:bg-gray-50';
830
+ row.innerHTML = `
831
+ <td class="px-6 py-4 whitespace-nowrap">
832
+ <div class="flex items-center">
833
+ <div>
834
+ <div class="font-medium text-gray-900">${appointment.clientName}</div>
835
+ </div>
836
+ </div>
837
+ </td>
838
+ <td class="px-6 py-4 whitespace-nowrap">
839
+ <div class="text-gray-900">${appointment.service}</div>
840
+ </td>
841
+ <td class="px-6 py-4 whitespace-nowrap">
842
+ <div class="text-gray-900">${formattedDate}</div>
843
+ <div class="text-gray-500">${formattedTime}</div>
844
+ </td>
845
+ <td class="px-6 py-4 whitespace-nowrap">
846
+ <div class="text-gray-900">${appointment.phone}</div>
847
+ ${appointment.email ? `<div class="text-gray-500">${appointment.email}</div>` : ''}
848
+ </td>
849
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
850
+ <button class="edit-appointment text-purple-600 hover:text-purple-900 mr-3" data-id="${appointment.id}">
851
+ <i class="fas fa-edit"></i> Edit
852
+ </button>
853
+ <button class="delete-appointment text-red-600 hover:text-red-900" data-id="${appointment.id}">
854
+ <i class="fas fa-trash-alt"></i> Delete
855
+ </button>
856
+ </td>
857
+ `;
858
+
859
+ appointmentsTableBody.appendChild(row);
860
+ });
861
 
862
+ // Add event listeners to edit and delete buttons
863
+ document.querySelectorAll('.edit-appointment').forEach(button => {
864
+ button.addEventListener('click', () => {
865
+ const appointmentId = parseInt(button.getAttribute('data-id'));
866
+ showEditAppointmentModal(appointmentId);
867
+ });
868
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
 
870
+ document.querySelectorAll('.delete-appointment').forEach(button => {
871
+ button.addEventListener('click', () => {
872
+ const appointmentId = parseInt(button.getAttribute('data-id'));
873
+ if (confirm('Are you sure you want to delete this appointment?')) {
874
+ appointments = appointments.filter(a => a.id !== appointmentId);
875
+ renderCalendar();
876
+ renderAppointmentsList();
877
+ }
878
+ });
879
+ });
880
+ }
881
+
882
+ function showEditAppointmentModal(appointmentId) {
883
+ const appointment = appointments.find(a => a.id === appointmentId);
884
+ if (!appointment) return;
885
+
886
+ // Fill the form with appointment data
887
+ document.getElementById('edit-appointment-id').value = appointment.id;
888
+ document.getElementById('edit-client-name').value = appointment.clientName;
889
+ document.getElementById('edit-client-phone').value = appointment.phone;
890
+ document.getElementById('edit-client-email').value = appointment.email || '';
891
+ document.getElementById('edit-service-package').value = appointment.service;
892
+ document.getElementById('edit-appointment-date').value = appointment.date;
893
+ document.getElementById('edit-appointment-time').value = appointment.time;
894
+ document.getElementById('edit-appointment-notes').value = appointment.notes || '';
895
+
896
+ // Show the modal
897
+ dayAppointmentsModal.classList.add('hidden');
898
+ editAppointmentModal.classList.remove('hidden');
899
+ }
900
+
901
+ function formatDateForInput(date) {
902
+ const year = date.getFullYear();
903
+ const month = String(date.getMonth() + 1).padStart(2, '0');
904
+ const day = String(date.getDate()).padStart(2, '0');
905
+ return `${year}-${month}-${day}`;
906
+ }
907
+
908
+ function exportToCsv() {
909
+ // Sort appointments by date
910
+ const sortedAppointments = [...appointments].sort((a, b) => {
911
+ const dateA = new Date(`${a.date}T${a.time}`);
912
+ const dateB = new Date(`${b.date}T${b.time}`);
913
+ return dateA - dateB;
914
+ });
915
+
916
+ // CSV header
917
+ let csv = 'Client Name,Phone,Email,Service,Date,Time,Notes\n';
918
+
919
+ // Add each appointment
920
+ sortedAppointments.forEach(appointment => {
921
+ const row = [
922
+ `"${appointment.clientName}"`,
923
+ `"${appointment.phone}"`,
924
+ `"${appointment.email || ''}"`,
925
+ `"${appointment.service}"`,
926
+ `"${appointment.date}"`,
927
+ `"${appointment.time}"`,
928
+ `"${appointment.notes || ''}"`
929
+ ];
930
+ csv += row.join(',') + '\n';
931
+ });
932
+
933
+ // Create download link
934
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
935
+ const url = URL.createObjectURL(blob);
936
+ const link = document.createElement('a');
937
+ link.setAttribute('href', url);
938
+ link.setAttribute('download', `laser_salon_appointments_${new Date().toISOString().slice(0, 10)}.csv`);
939
+ link.style.visibility = 'hidden';
940
+ document.body.appendChild(link);
941
+ link.click();
942
+ document.body.removeChild(link);
943
+ }
944
  </script>
945
  <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/awesome-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
946
  </html>