Cezarxil commited on
Commit
3cfe1e2
·
verified ·
1 Parent(s): fd5e9fd

Make the username: Epiliz password: Epilat2110 - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +304 -830
index.html CHANGED
@@ -3,167 +3,101 @@
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">Epiliz Estetique</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">Epiliz Estetique 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">
@@ -171,776 +105,316 @@
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: "[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: "[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>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Epiliz - Laser Hair Removal</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
  .fade-in {
11
  animation: fadeIn 0.3s ease-in-out;
12
  }
13
  @keyframes fadeIn {
14
+ from { opacity: 0; transform: translateY(10px); }
15
+ to { opacity: 1; transform: translateY(0); }
16
+ }
17
+ .sidebar {
18
+ transition: all 0.3s ease;
19
+ }
20
+ @media (max-width: 768px) {
21
+ .sidebar {
22
+ transform: translateX(-100%);
23
+ }
24
+ .sidebar.open {
25
+ transform: translateX(0);
26
+ }
27
  }
28
  </style>
29
  </head>
30
+ <body class="bg-gray-50 font-sans">
31
+ <!-- Login Page -->
32
+ <div id="login-page" class="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-100 to-pink-100">
33
+ <div class="bg-white p-8 rounded-lg shadow-md w-full max-w-md">
34
+ <div class="text-center mb-8">
35
+ <h1 class="text-3xl font-bold text-purple-800">Epiliz</h1>
36
+ <p class="text-gray-600 mt-2">Laser Hair Removal Management</p>
 
37
  </div>
38
+ <form id="login-form" class="space-y-6">
39
  <div>
40
  <label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
41
  <input type="text" id="username" name="username" required
42
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
43
  </div>
44
  <div>
45
  <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
46
  <input type="password" id="password" name="password" required
47
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
48
  </div>
49
+ <div>
50
+ <button type="submit"
51
  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">
52
+ Sign In
53
+ </button>
54
+ </div>
55
  </form>
56
+ <div id="login-error" class="mt-4 text-red-500 text-sm hidden"></div>
57
  </div>
58
  </div>
59
 
60
+ <!-- Main App (hidden initially) -->
61
+ <div id="app-container" class="hidden min-h-screen">
62
+ <!-- Mobile Header -->
63
+ <div class="md:hidden bg-purple-700 text-white p-4 flex justify-between items-center">
64
+ <button id="menu-toggle" class="text-white focus:outline-none">
65
+ <i class="fas fa-bars text-xl"></i>
66
+ </button>
67
+ <h1 class="text-xl font-bold">Epiliz</h1>
68
+ <div class="w-6"></div> <!-- Spacer for alignment -->
69
+ </div>
 
 
 
 
 
 
 
70
 
71
+ <!-- Sidebar -->
72
+ <div id="sidebar" class="sidebar fixed inset-y-0 left-0 w-64 bg-purple-800 text-white transform md:translate-x-0 z-10">
73
+ <div class="p-4 flex items-center border-b border-purple-700">
74
+ <h1 class="text-xl font-bold">Epiliz</h1>
 
 
 
 
 
 
 
 
 
 
 
75
  </div>
76
+ <nav class="mt-6">
77
+ <div class="px-4 py-3 bg-purple-900">
78
+ <span class="text-sm font-semibold">Menu</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  </div>
80
+ <a href="#" id="dashboard-link" class="block px-4 py-3 mt-2 text-white hover:bg-purple-700 transition duration-200">
81
+ <i class="fas fa-calendar-alt mr-3"></i> Appointments
82
+ </a>
83
+ <a href="#" id="add-appointment-link" class="block px-4 py-3 mt-2 text-white hover:bg-purple-700 transition duration-200">
84
+ <i class="fas fa-plus-circle mr-3"></i> Add Appointment
85
+ </a>
86
+ <a href="#" id="logout-link" class="block px-4 py-3 mt-2 text-white hover:bg-purple-700 transition duration-200">
87
+ <i class="fas fa-sign-out-alt mr-3"></i> Logout
88
+ </a>
89
+ </nav>
90
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ <!-- Main Content -->
93
+ <div class="md:ml-64 transition-all duration-300">
94
+ <!-- Dashboard -->
95
+ <div id="dashboard" class="p-6 fade-in">
96
  <div class="flex justify-between items-center mb-6">
97
+ <h2 class="text-2xl font-bold text-gray-800">Appointments</h2>
98
+ <button id="add-appointment-btn" class="bg-purple-600 text-white px-4 py-2 rounded-md hover:bg-purple-700 transition duration-200 md:hidden">
99
+ <i class="fas fa-plus mr-2"></i> Add
100
+ </button>
 
 
 
 
 
 
 
101
  </div>
102
 
103
  <div class="bg-white rounded-lg shadow overflow-hidden">
 
105
  <table class="min-w-full divide-y divide-gray-200">
106
  <thead class="bg-gray-50">
107
  <tr>
108
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
109
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
110
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Treatment Area</th>
111
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contact</th>
112
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
113
  </tr>
114
  </thead>
115
+ <tbody id="appointments-list" class="bg-white divide-y divide-gray-200">
116
+ <!-- Appointments will be loaded here -->
117
  </tbody>
118
  </table>
119
  </div>
120
  </div>
121
+ <div id="no-appointments" class="mt-8 text-center text-gray-500 hidden">
122
+ <i class="fas fa-calendar-times text-4xl mb-4"></i>
123
+ <p class="text-lg">No appointments scheduled</p>
124
+ </div>
125
  </div>
126
 
127
+ <!-- Add Appointment Form -->
128
+ <div id="add-appointment" class="p-6 hidden fade-in">
129
+ <div class="flex items-center mb-6">
130
+ <button id="back-to-dashboard" class="mr-4 text-purple-600 hover:text-purple-800">
131
+ <i class="fas fa-arrow-left text-xl"></i>
132
+ </button>
133
+ <h2 class="text-2xl font-bold text-gray-800">Add New Appointment</h2>
134
+ </div>
135
+
136
+ <div class="bg-white rounded-lg shadow p-6 max-w-2xl mx-auto">
137
+ <form id="appointment-form">
138
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  <div>
140
+ <label for="client-name" class="block text-sm font-medium text-gray-700 mb-1">Client Name*</label>
141
+ <input type="text" id="client-name" name="client-name" required
142
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
 
 
 
 
 
 
 
 
143
  </div>
144
+ <div>
145
+ <label for="appointment-date" class="block text-sm font-medium text-gray-700 mb-1">Appointment Date*</label>
146
+ <input type="datetime-local" id="appointment-date" name="appointment-date" required
147
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  </div>
149
  <div>
150
+ <label for="treatment-area" class="block text-sm font-medium text-gray-700 mb-1">Treatment Area*</label>
151
+ <select id="treatment-area" name="treatment-area" required
152
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
153
+ <option value="">Select area</option>
154
+ <option value="Face">Face</option>
155
+ <option value="Underarms">Underarms</option>
156
+ <option value="Arms">Arms</option>
157
+ <option value="Legs">Legs</option>
158
+ <option value="Bikini">Bikini</option>
159
+ <option value="Back">Back</option>
160
+ <option value="Chest">Chest</option>
161
+ <option value="Brazilian">Brazilian</option>
162
+ </select>
163
  </div>
164
+ <div>
165
+ <label for="contact-info" class="block text-sm font-medium text-gray-700 mb-1">Contact Info</label>
166
+ <input type="text" id="contact-info" name="contact-info"
167
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
 
 
 
 
 
 
168
  </div>
169
+ </div>
170
+ <div class="mt-8">
171
+ <button type="submit"
172
+ class="w-full md:w-auto bg-purple-600 text-white py-2 px-6 rounded-md hover:bg-purple-700 transition duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2">
173
+ Save Appointment
174
+ </button>
175
+ </div>
176
+ </form>
177
+ <div id="form-error" class="mt-4 text-red-500 text-sm hidden"></div>
178
+ <div id="form-success" class="mt-4 text-green-500 text-sm hidden"></div>
179
  </div>
180
  </div>
181
+ </div>
182
  </div>
183
 
184
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  // DOM Elements
186
+ const loginPage = document.getElementById('login-page');
187
  const appContainer = document.getElementById('app-container');
188
  const loginForm = document.getElementById('login-form');
189
+ const loginError = document.getElementById('login-error');
190
+ const menuToggle = document.getElementById('menu-toggle');
191
+ const sidebar = document.getElementById('sidebar');
192
+ const dashboardLink = document.getElementById('dashboard-link');
193
+ const addAppointmentLink = document.getElementById('add-appointment-link');
194
+ const logoutLink = document.getElementById('logout-link');
195
+ const dashboard = document.getElementById('dashboard');
196
+ const addAppointment = document.getElementById('add-appointment');
197
+ const backToDashboard = document.getElementById('back-to-dashboard');
198
+ const addAppointmentBtn = document.getElementById('add-appointment-btn');
199
+ const appointmentsList = document.getElementById('appointments-list');
200
+ const noAppointments = document.getElementById('no-appointments');
201
+ const appointmentForm = document.getElementById('appointment-form');
202
+ const formError = document.getElementById('form-error');
203
+ const formSuccess = document.getElementById('form-success');
204
+
205
+ // Login
206
+ loginForm.addEventListener('submit', function(e) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  e.preventDefault();
208
  const username = document.getElementById('username').value;
209
  const password = document.getElementById('password').value;
210
 
211
+ if (username === 'Epiliz' && password === 'Epilat2110') {
212
+ loginError.classList.add('hidden');
213
+ loginPage.classList.add('hidden');
 
 
214
  appContainer.classList.remove('hidden');
215
+ loadAppointments();
 
216
  } else {
217
+ loginError.textContent = 'Invalid username or password';
218
+ loginError.classList.remove('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  }
 
 
 
 
 
 
 
 
220
  });
221
 
222
+ // Navigation
223
+ dashboardLink.addEventListener('click', function(e) {
224
+ e.preventDefault();
225
+ showDashboard();
 
 
 
226
  });
227
 
228
+ addAppointmentLink.addEventListener('click', function(e) {
229
+ e.preventDefault();
230
+ showAddAppointment();
231
  });
232
 
233
+ addAppointmentBtn.addEventListener('click', function(e) {
234
+ e.preventDefault();
235
+ showAddAppointment();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  });
237
 
238
+ backToDashboard.addEventListener('click', function(e) {
 
239
  e.preventDefault();
240
+ showDashboard();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  });
242
 
243
+ logoutLink.addEventListener('click', function(e) {
244
+ e.preventDefault();
245
+ appContainer.classList.add('hidden');
246
+ loginPage.classList.remove('hidden');
247
+ loginForm.reset();
248
  });
249
 
250
+ // Mobile menu toggle
251
+ menuToggle.addEventListener('click', function() {
252
+ sidebar.classList.toggle('open');
253
  });
254
 
255
+ // Show dashboard view
256
+ function showDashboard() {
257
+ dashboard.classList.remove('hidden');
258
+ addAppointment.classList.add('hidden');
259
+ if (window.innerWidth < 768) {
260
+ sidebar.classList.remove('open');
 
 
261
  }
262
+ loadAppointments();
263
+ }
264
 
265
+ // Show add appointment view
266
+ function showAddAppointment() {
267
+ dashboard.classList.add('hidden');
268
+ addAppointment.classList.remove('hidden');
269
+ appointmentForm.reset();
270
+ formError.classList.add('hidden');
271
+ formSuccess.classList.add('hidden');
272
+ if (window.innerWidth < 768) {
273
+ sidebar.classList.remove('open');
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  }
275
+ }
276
 
277
+ // Load appointments from database
278
+ function loadAppointments() {
279
+ // In a real app, this would fetch from your PHP backend
280
+ // For demo purposes, we'll use mock data
281
+ const mockAppointments = [
282
+ {
283
+ id: 1,
284
+ client_name: "Sarah Johnson",
285
+ appointment_date: "2023-06-15 14:30:00",
286
+ treatment_area: "Legs",
287
+ contact_info: "sarah@example.com"
288
+ },
289
+ {
290
+ id: 2,
291
+ client_name: "Michael Chen",
292
+ appointment_date: "2023-06-16 10:00:00",
293
+ treatment_area: "Back",
294
+ contact_info: "555-123-4567"
295
+ },
296
+ {
297
+ id: 3,
298
+ client_name: "Emma Williams",
299
+ appointment_date: "2023-06-17 15:45:00",
300
+ treatment_area: "Bikini",
301
+ contact_info: "emma.[email protected]"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  }
303
+ ];
 
 
 
304
 
305
+ appointmentsList.innerHTML = '';
 
 
 
 
 
 
 
 
306
 
307
+ if (mockAppointments.length === 0) {
308
+ noAppointments.classList.remove('hidden');
 
 
 
309
  } else {
310
+ noAppointments.classList.add('hidden');
311
+ mockAppointments.forEach(appointment => {
312
+ const row = document.createElement('tr');
313
+ row.className = 'hover:bg-gray-50';
314
+
315
+ const formattedDate = new Date(appointment.appointment_date).toLocaleString('en-US', {
316
+ month: 'short',
317
+ day: 'numeric',
318
+ year: 'numeric',
319
+ hour: '2-digit',
320
+ minute: '2-digit'
321
+ });
322
+
323
+ row.innerHTML = `
324
+ <td class="px-6 py-4 whitespace-nowrap">
325
+ <div class="font-medium text-gray-900">${appointment.client_name}</div>
326
+ </td>
327
+ <td class="px-6 py-4 whitespace-nowrap">
328
+ <div class="text-gray-700">${formattedDate}</div>
329
+ </td>
330
+ <td class="px-6 py-4 whitespace-nowrap">
331
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-purple-100 text-purple-800">
332
+ ${appointment.treatment_area}
333
+ </span>
334
+ </td>
335
+ <td class="px-6 py-4 whitespace-nowrap text-gray-700">
336
+ ${appointment.contact_info || 'N/A'}
337
+ </td>
338
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
339
+ <button class="text-red-600 hover:text-red-900 delete-btn" data-id="${appointment.id}">
340
+ <i class="fas fa-trash-alt"></i>
341
+ </button>
342
+ </td>
343
  `;
344
 
345
+ appointmentsList.appendChild(row);
346
  });
347
 
348
+ // Add event listeners to delete buttons
349
+ document.querySelectorAll('.delete-btn').forEach(btn => {
350
+ btn.addEventListener('click', function() {
351
+ const id = this.getAttribute('data-id');
352
+ deleteAppointment(id);
 
353
  });
354
  });
355
  }
 
 
 
356
  }
357
 
358
+ // Delete appointment
359
+ function deleteAppointment(id) {
360
+ if (confirm('Are you sure you want to delete this appointment?')) {
361
+ // In a real app, this would send a request to your PHP backend
362
+ console.log(`Deleting appointment with ID: ${id}`);
363
+
364
+ // Simulate successful deletion
365
+ setTimeout(() => {
366
+ loadAppointments();
367
+ }, 300);
 
 
 
 
368
  }
369
+ }
370
+
371
+ // Add new appointment
372
+ appointmentForm.addEventListener('submit', function(e) {
373
+ e.preventDefault();
374
 
375
+ const clientName = document.getElementById('client-name').value;
376
+ const appointmentDate = document.getElementById('appointment-date').value;
377
+ const treatmentArea = document.getElementById('treatment-area').value;
378
+ const contactInfo = document.getElementById('contact-info').value;
 
 
379
 
380
+ // Simple validation
381
+ if (!clientName || !appointmentDate || !treatmentArea) {
382
+ formError.textContent = 'Please fill in all required fields';
383
+ formError.classList.remove('hidden');
384
+ formSuccess.classList.add('hidden');
 
 
 
385
  return;
386
  }
387
 
388
+ // In a real app, this would send data to your PHP backend
389
+ console.log('Saving appointment:', {
390
+ clientName,
391
+ appointmentDate,
392
+ treatmentArea,
393
+ contactInfo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  });
395
 
396
+ // Simulate successful save
397
+ formError.classList.add('hidden');
398
+ formSuccess.textContent = 'Appointment saved successfully!';
399
+ formSuccess.classList.remove('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
+ // Reset form and show success message
402
+ setTimeout(() => {
403
+ appointmentForm.reset();
404
+ showDashboard();
405
+ }, 1500);
406
+ });
 
 
 
 
 
407
 
408
+ // Initialize
409
+ document.addEventListener('DOMContentLoaded', function() {
410
+ // Set min date for appointment date input to today
411
+ const today = new Date();
412
+ const tomorrow = new Date(today);
413
+ tomorrow.setDate(tomorrow.getDate() + 1);
 
414
 
415
+ const minDate = tomorrow.toISOString().slice(0, 16);
416
+ document.getElementById('appointment-date').min = minDate;
417
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  </script>
419
  <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>
420
  </html>