zen-wdg 2.2.7 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,990 @@
1
+ <template>
2
+ <div class="calendar-container" :class="theme === 'dark' ? 'dark' : 'light'">
3
+ <!-- Header con filtros -->
4
+ <div class="calendar-header">
5
+ <div class="view-selector">
6
+ <button
7
+ v-for="view in ['day', 'week', 'month']"
8
+ :key="view"
9
+ :class="['view-btn', { active: currentView === view }]"
10
+ @click="currentView = view"
11
+ >
12
+ {{ viewLabels[view] }}
13
+ </button>
14
+ </div>
15
+ <div class="navigation">
16
+ <button @click="previousPeriod" class="nav-btn">‹</button>
17
+ <span class="current-date">{{ currentDateLabel }}</span>
18
+ <button @click="nextPeriod" class="nav-btn">›</button>
19
+ <button @click="goToToday" class="today-btn">Hoy</button>
20
+ </div>
21
+ <button @click="showEventModal = true" class="add-event-btn">+ Evento</button>
22
+ </div>
23
+
24
+ <!-- Vista del calendario -->
25
+ <div class="calendar-view">
26
+ <!-- Vista de Día -->
27
+ <div v-if="currentView === 'day'" class="day-view">
28
+ <div class="time-grid">
29
+ <div v-for="hour in 24" :key="hour" class="hour-slot">
30
+ <div class="hour-label">{{ formatHour(hour - 1) }}</div>
31
+ <div class="hour-content">
32
+ <div
33
+ v-for="event in getEventsForHour(formatDate(currentDate), hour - 1)"
34
+ :key="event.id"
35
+ class="event-card"
36
+ :style="{ backgroundColor: event.color }"
37
+ @click="editEvent(event)"
38
+ >
39
+ <div class="event-title">{{ event.name }}</div>
40
+ <div class="event-time">{{ event.startTime }} - {{ event.endTime }}</div>
41
+ <div v-if="event.isRecurring" class="event-recurring">🔁</div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <!-- Vista de Semana -->
49
+ <div v-if="currentView === 'week'" class="week-view">
50
+ <div class="week-header">
51
+ <div class="week-time-label"></div>
52
+ <div v-for="day in weekDays" :key="day.date" class="week-day-header">
53
+ <div class="day-name">{{ day.name }}</div>
54
+ <div class="day-number" :class="{ today: isToday(day.date) }">{{ day.number }}</div>
55
+ </div>
56
+ </div>
57
+ <div class="week-grid">
58
+ <div v-for="hour in 24" :key="hour" class="week-hour-row">
59
+ <div class="week-hour-label">{{ formatHour(hour - 1) }}</div>
60
+ <div v-for="day in weekDays" :key="day.date" class="week-hour-cell">
61
+ <div
62
+ v-for="event in getEventsForHour(day.date, hour - 1)"
63
+ :key="event.id"
64
+ class="event-card small"
65
+ :style="{ backgroundColor: event.color }"
66
+ @click="editEvent(event)"
67
+ >
68
+ <div class="event-title">{{ event.name }}</div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+
75
+ <!-- Vista de Mes -->
76
+ <div v-if="currentView === 'month'" class="month-view">
77
+ <div class="month-header">
78
+ <div v-for="day in dayNames" :key="day" class="month-day-name">{{ day }}</div>
79
+ </div>
80
+ <div class="month-grid">
81
+ <div
82
+ v-for="day in monthDays"
83
+ :key="day.date"
84
+ class="month-day"
85
+ :class="{
86
+ 'other-month': !day.isCurrentMonth,
87
+ 'today': isToday(day.date)
88
+ }"
89
+ @click="selectDay(day.date)"
90
+ >
91
+ <div class="day-number">{{ day.number }}</div>
92
+ <div class="day-events">
93
+ <div
94
+ v-for="event in getEventsForDay(day.date).slice(0, 3)"
95
+ :key="event.id"
96
+ class="event-dot"
97
+ :style="{ backgroundColor: event.color }"
98
+ :title="event.name"
99
+ ></div>
100
+ <div v-if="getEventsForDay(day.date).length > 3" class="more-events">
101
+ +{{ getEventsForDay(day.date).length - 3 }}
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+
109
+ <!-- Modal para añadir/editar evento -->
110
+ <div v-if="showEventModal" class="modal-overlay" @click.self="closeModal">
111
+ <div class="modal-content">
112
+ <div class="modal-header">
113
+ <h3>{{ editingEvent ? 'Editar Evento' : 'Nuevo Evento' }}</h3>
114
+ <button @click="closeModal" class="close-btn">✕</button>
115
+ </div>
116
+ <div class="modal-body">
117
+ <div class="form-group">
118
+ <label>Nombre del evento</label>
119
+ <input v-model="eventForm.name" type="text" placeholder="Ej: Reunión con el equipo">
120
+ </div>
121
+ <div class="form-group">
122
+ <label>Fecha</label>
123
+ <input v-model="eventForm.date" type="date">
124
+ </div>
125
+ <div class="form-row">
126
+ <div class="form-group">
127
+ <label>Hora de inicio</label>
128
+ <input v-model="eventForm.startTime" type="time">
129
+ </div>
130
+ <div class="form-group">
131
+ <label>Hora de fin</label>
132
+ <input v-model="eventForm.endTime" type="time">
133
+ </div>
134
+ </div>
135
+ <div class="form-group">
136
+ <label>Color</label>
137
+ <div class="color-picker">
138
+ <div
139
+ v-for="color in colors"
140
+ :key="color"
141
+ class="color-option"
142
+ :style="{ backgroundColor: color }"
143
+ :class="{ selected: eventForm.color === color }"
144
+ @click="eventForm.color = color"
145
+ ></div>
146
+ </div>
147
+ </div>
148
+ <div class="form-group">
149
+ <label class="checkbox-label">
150
+ <input v-model="eventForm.isRecurring" type="checkbox">
151
+ Evento repetitivo
152
+ </label>
153
+ </div>
154
+ <div v-if="eventForm.isRecurring" class="form-group">
155
+ <label>Frecuencia</label>
156
+ <select v-model="eventForm.recurringType">
157
+ <option value="daily">Diario</option>
158
+ <option value="weekly">Semanal</option>
159
+ <option value="monthly">Mensual</option>
160
+ </select>
161
+ </div>
162
+ </div>
163
+ <div class="modal-footer">
164
+ <button v-if="editingEvent" @click="deleteEvent" class="delete-btn">Eliminar</button>
165
+ <button @click="closeModal" class="cancel-btn">Cancelar</button>
166
+ <button @click="saveEvent" class="save-btn">{{ editingEvent ? 'Guardar' : 'Crear' }}</button>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ </template>
172
+
173
+ <script>
174
+ export default {
175
+ name: 'ZCalendarWidget',
176
+ props: {
177
+ theme: {
178
+ type: String,
179
+ default: 'light'
180
+ },
181
+ useStorage: {
182
+ type: Boolean,
183
+ default: true
184
+ }
185
+ },
186
+ data() {
187
+ const STORAGE_KEY = 'calendar_widget_events';
188
+
189
+ // Cargar eventos del localStorage si está habilitado
190
+ let events = [];
191
+ if (this.useStorage) {
192
+ const stored = localStorage.getItem(STORAGE_KEY);
193
+ events = stored ? JSON.parse(stored) : [];
194
+ }
195
+
196
+ // Formulario vacío por defecto
197
+ const emptyForm = {
198
+ name: '',
199
+ date: new Date().toISOString().split('T')[0],
200
+ startTime: '09:00',
201
+ endTime: '10:00',
202
+ color: '#066D5A',
203
+ isRecurring: false,
204
+ recurringType: 'daily'
205
+ };
206
+
207
+ return {
208
+ STORAGE_KEY,
209
+ currentView: 'month', // day, week, month
210
+ currentDate: new Date(),
211
+ showEventModal: false,
212
+ editingEvent: null,
213
+ events: events,
214
+ eventForm: emptyForm,
215
+ viewLabels: {
216
+ day: 'Día',
217
+ week: 'Semana',
218
+ month: 'Mes'
219
+ },
220
+ dayNames: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'],
221
+ colors: ['#066D5A', '#0891b2', '#7c3aed', '#dc2626', '#ea580c', '#ca8a04', '#16a34a', '#ec4899']
222
+ };
223
+ },
224
+ computed: {
225
+ currentDateLabel() {
226
+ const months = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
227
+ 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
228
+
229
+ if (this.currentView === 'day') {
230
+ return `${this.currentDate.getDate()} de ${months[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
231
+ } else if (this.currentView === 'week') {
232
+ const weekStart = this.getWeekStart(this.currentDate);
233
+ const weekEnd = new Date(weekStart);
234
+ weekEnd.setDate(weekEnd.getDate() + 6);
235
+ return `${weekStart.getDate()} - ${weekEnd.getDate()} ${months[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
236
+ } else {
237
+ return `${months[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
238
+ }
239
+ },
240
+ weekDays() {
241
+ const start = this.getWeekStart(this.currentDate);
242
+ const days = [];
243
+ for (let i = 0; i < 7; i++) {
244
+ const date = new Date(start);
245
+ date.setDate(start.getDate() + i);
246
+ days.push({
247
+ date: this.formatDate(date),
248
+ name: this.dayNames[date.getDay()],
249
+ number: date.getDate()
250
+ });
251
+ }
252
+ return days;
253
+ },
254
+ monthDays() {
255
+ const year = this.currentDate.getFullYear();
256
+ const month = this.currentDate.getMonth();
257
+ const firstDay = new Date(year, month, 1);
258
+ const lastDay = new Date(year, month + 1, 0);
259
+ const startDate = new Date(firstDay);
260
+ startDate.setDate(startDate.getDate() - firstDay.getDay());
261
+
262
+ const days = [];
263
+ const currentDay = new Date(startDate);
264
+
265
+ for (let i = 0; i < 42; i++) {
266
+ days.push({
267
+ date: this.formatDate(currentDay),
268
+ number: currentDay.getDate(),
269
+ isCurrentMonth: currentDay.getMonth() === month
270
+ });
271
+ currentDay.setDate(currentDay.getDate() + 1);
272
+ }
273
+
274
+ return days;
275
+ }
276
+ },
277
+ methods: {
278
+ loadEvents() {
279
+ if (!this.useStorage) return [];
280
+ const stored = localStorage.getItem(this.STORAGE_KEY);
281
+ return stored ? JSON.parse(stored) : [];
282
+ },
283
+ saveEvents() {
284
+ if (this.useStorage) {
285
+ localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.events));
286
+ }
287
+ },
288
+ getEmptyForm() {
289
+ return {
290
+ name: '',
291
+ date: this.formatDate(new Date()),
292
+ startTime: '09:00',
293
+ endTime: '10:00',
294
+ color: '#066D5A',
295
+ isRecurring: false,
296
+ recurringType: 'daily'
297
+ };
298
+ },
299
+ formatDate(date) {
300
+ const d = new Date(date);
301
+ const year = d.getFullYear();
302
+ const month = String(d.getMonth() + 1).padStart(2, '0');
303
+ const day = String(d.getDate()).padStart(2, '0');
304
+ return `${year}-${month}-${day}`;
305
+ },
306
+ formatHour(hour) {
307
+ return `${String(hour).padStart(2, '0')}:00`;
308
+ },
309
+ isToday(dateString) {
310
+ return dateString === this.formatDate(new Date());
311
+ },
312
+ getWeekStart(date) {
313
+ const d = new Date(date);
314
+ const day = d.getDay();
315
+ const diff = d.getDate() - day;
316
+ return new Date(d.setDate(diff));
317
+ },
318
+ previousPeriod() {
319
+ const d = new Date(this.currentDate);
320
+ if (this.currentView === 'day') {
321
+ d.setDate(d.getDate() - 1);
322
+ } else if (this.currentView === 'week') {
323
+ d.setDate(d.getDate() - 7);
324
+ } else {
325
+ d.setMonth(d.getMonth() - 1);
326
+ }
327
+ this.currentDate = d;
328
+ },
329
+ nextPeriod() {
330
+ const d = new Date(this.currentDate);
331
+ if (this.currentView === 'day') {
332
+ d.setDate(d.getDate() + 1);
333
+ } else if (this.currentView === 'week') {
334
+ d.setDate(d.getDate() + 7);
335
+ } else {
336
+ d.setMonth(d.getMonth() + 1);
337
+ }
338
+ this.currentDate = d;
339
+ },
340
+ goToToday() {
341
+ this.currentDate = new Date();
342
+ },
343
+ selectDay(dateString) {
344
+ this.currentDate = new Date(dateString);
345
+ this.currentView = 'day';
346
+ },
347
+ getEventsForDay(dateString) {
348
+ return this.events.filter(event => {
349
+ if (event.date === dateString) return true;
350
+
351
+ if (event.isRecurring) {
352
+ const eventDate = new Date(event.date);
353
+ const checkDate = new Date(dateString);
354
+
355
+ if (checkDate < eventDate) return false;
356
+
357
+ if (event.recurringType === 'daily') return true;
358
+
359
+ if (event.recurringType === 'weekly') {
360
+ return eventDate.getDay() === checkDate.getDay();
361
+ }
362
+
363
+ if (event.recurringType === 'monthly') {
364
+ return eventDate.getDate() === checkDate.getDate();
365
+ }
366
+ }
367
+
368
+ return false;
369
+ });
370
+ },
371
+ getEventsForHour(dateString, hour) {
372
+ return this.getEventsForDay(dateString).filter(event => {
373
+ const startHour = parseInt(event.startTime.split(':')[0]);
374
+ const endHour = parseInt(event.endTime.split(':')[0]);
375
+ return hour >= startHour && hour < endHour;
376
+ });
377
+ },
378
+ async saveEvent() {
379
+ if (!this.eventForm.name) {
380
+ await window.ZenModals.showWarning({
381
+ title: 'Nombre requerido',
382
+ message: 'Por favor, ingresa un nombre para el evento'
383
+ });
384
+ return;
385
+ }
386
+
387
+ if (this.editingEvent) {
388
+ const index = this.events.findIndex(e => e.id === this.editingEvent.id);
389
+ this.events[index] = { ...this.eventForm, id: this.editingEvent.id };
390
+ } else {
391
+ this.events.push({
392
+ ...this.eventForm,
393
+ id: Date.now() + Math.random()
394
+ });
395
+ }
396
+
397
+ this.saveEvents();
398
+ this.closeModal();
399
+ },
400
+ editEvent(event) {
401
+ this.editingEvent = event;
402
+ this.eventForm = { ...event };
403
+ this.showEventModal = true;
404
+ },
405
+ async deleteEvent() {
406
+ const confirmed = await window.ZenModals.showDeleteConfirm('este evento');
407
+ if (confirmed) {
408
+ this.events = this.events.filter(e => e.id !== this.editingEvent.id);
409
+ this.saveEvents();
410
+ this.closeModal();
411
+ }
412
+ },
413
+ closeModal() {
414
+ this.showEventModal = false;
415
+ this.editingEvent = null;
416
+ this.eventForm = this.getEmptyForm();
417
+ }
418
+ }
419
+ };
420
+ </script>
421
+
422
+ <style scoped>
423
+ .calendar-container {
424
+ width: 100%;
425
+ height: 100%;
426
+ display: flex;
427
+ flex-direction: column;
428
+ padding: 1rem;
429
+ border-radius: 1rem;
430
+ backdrop-filter: blur(10px) saturate(180%);
431
+ -webkit-backdrop-filter: blur(10px) saturate(180%);
432
+ background: rgba(255, 255, 255, 0.1);
433
+ border: 1px solid rgba(255, 255, 255, 0.3);
434
+ color: #202020;
435
+ }
436
+
437
+ .calendar-container.dark {
438
+ background: rgba(20, 20, 20, 0.3);
439
+ color: #dfdfdf;
440
+ }
441
+
442
+ .calendar-header {
443
+ display: flex;
444
+ justify-content: space-between;
445
+ align-items: center;
446
+ margin-bottom: 1rem;
447
+ gap: 1rem;
448
+ flex-wrap: wrap;
449
+ }
450
+
451
+ .view-selector {
452
+ display: flex;
453
+ gap: 0.5rem;
454
+ background: rgba(0, 0, 0, 0.1);
455
+ padding: 0.25rem;
456
+ border-radius: 0.5rem;
457
+ }
458
+
459
+ .view-btn {
460
+ padding: 0.5rem 1rem;
461
+ border: none;
462
+ background: transparent;
463
+ cursor: pointer;
464
+ border-radius: 0.4rem;
465
+ font-weight: 500;
466
+ color: inherit;
467
+ transition: all 0.2s;
468
+ }
469
+
470
+ .view-btn.active {
471
+ background: rgba(6, 109, 90, 0.6);
472
+ color: white;
473
+ }
474
+
475
+ .navigation {
476
+ display: flex;
477
+ align-items: center;
478
+ gap: 1rem;
479
+ }
480
+
481
+ .nav-btn {
482
+ width: 2rem;
483
+ height: 2rem;
484
+ border: none;
485
+ background: rgba(0, 0, 0, 0.1);
486
+ border-radius: 0.4rem;
487
+ cursor: pointer;
488
+ font-size: 1.5rem;
489
+ color: inherit;
490
+ display: flex;
491
+ align-items: center;
492
+ justify-content: center;
493
+ }
494
+
495
+ .nav-btn:hover {
496
+ background: rgba(0, 0, 0, 0.2);
497
+ }
498
+
499
+ .current-date {
500
+ font-weight: 600;
501
+ min-width: 200px;
502
+ text-align: center;
503
+ }
504
+
505
+ .today-btn {
506
+ padding: 0.5rem 1rem;
507
+ border: none;
508
+ background: rgba(0, 0, 0, 0.1);
509
+ border-radius: 0.4rem;
510
+ cursor: pointer;
511
+ font-weight: 500;
512
+ color: inherit;
513
+ }
514
+
515
+ .today-btn:hover {
516
+ background: rgba(0, 0, 0, 0.2);
517
+ }
518
+
519
+ .add-event-btn {
520
+ padding: 0.5rem 1rem;
521
+ border: none;
522
+ background: #066D5A;
523
+ color: white;
524
+ border-radius: 0.4rem;
525
+ cursor: pointer;
526
+ font-weight: 600;
527
+ }
528
+
529
+ .add-event-btn:hover {
530
+ background: #055a4a;
531
+ }
532
+
533
+ .calendar-view {
534
+ flex: 1;
535
+ overflow-y: auto;
536
+ min-height: 400px;
537
+ }
538
+
539
+ /* Vista de Día */
540
+ .day-view {
541
+ height: 100%;
542
+ }
543
+
544
+ .time-grid {
545
+ display: flex;
546
+ flex-direction: column;
547
+ }
548
+
549
+ .hour-slot {
550
+ display: flex;
551
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
552
+ min-height: 60px;
553
+ }
554
+
555
+ .hour-label {
556
+ width: 60px;
557
+ padding: 0.5rem;
558
+ font-size: 0.85rem;
559
+ color: rgba(0, 0, 0, 0.6);
560
+ }
561
+
562
+ .dark .hour-label {
563
+ color: rgba(255, 255, 255, 0.6);
564
+ }
565
+
566
+ .hour-content {
567
+ flex: 1;
568
+ padding: 0.25rem;
569
+ position: relative;
570
+ }
571
+
572
+ /* Vista de Semana */
573
+ .week-view {
574
+ height: 100%;
575
+ display: flex;
576
+ flex-direction: column;
577
+ }
578
+
579
+ .week-header {
580
+ display: grid;
581
+ grid-template-columns: 60px repeat(7, 1fr);
582
+ border-bottom: 2px solid rgba(0, 0, 0, 0.2);
583
+ margin-bottom: 0.5rem;
584
+ }
585
+
586
+ .week-time-label {
587
+ width: 60px;
588
+ }
589
+
590
+ .week-day-header {
591
+ text-align: center;
592
+ padding: 0.5rem;
593
+ }
594
+
595
+ .day-name {
596
+ font-size: 0.85rem;
597
+ color: rgba(0, 0, 0, 0.6);
598
+ }
599
+
600
+ .dark .day-name {
601
+ color: rgba(255, 255, 255, 0.6);
602
+ }
603
+
604
+ .day-number {
605
+ font-weight: 600;
606
+ font-size: 1.2rem;
607
+ margin-top: 0.25rem;
608
+ }
609
+
610
+ .day-number.today {
611
+ background: #066D5A;
612
+ color: white;
613
+ border-radius: 50%;
614
+ width: 2rem;
615
+ height: 2rem;
616
+ display: inline-flex;
617
+ align-items: center;
618
+ justify-content: center;
619
+ }
620
+
621
+ .week-grid {
622
+ flex: 1;
623
+ overflow-y: auto;
624
+ }
625
+
626
+ .week-hour-row {
627
+ display: grid;
628
+ grid-template-columns: 60px repeat(7, 1fr);
629
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
630
+ min-height: 60px;
631
+ }
632
+
633
+ .week-hour-label {
634
+ padding: 0.5rem;
635
+ font-size: 0.85rem;
636
+ color: rgba(0, 0, 0, 0.6);
637
+ }
638
+
639
+ .dark .week-hour-label {
640
+ color: rgba(255, 255, 255, 0.6);
641
+ }
642
+
643
+ .week-hour-cell {
644
+ border-left: 1px solid rgba(0, 0, 0, 0.05);
645
+ padding: 0.25rem;
646
+ position: relative;
647
+ }
648
+
649
+ /* Vista de Mes */
650
+ .month-view {
651
+ height: 100%;
652
+ display: flex;
653
+ flex-direction: column;
654
+ }
655
+
656
+ .month-header {
657
+ display: grid;
658
+ grid-template-columns: repeat(7, 1fr);
659
+ gap: 0.25rem;
660
+ margin-bottom: 0.5rem;
661
+ }
662
+
663
+ .month-day-name {
664
+ text-align: center;
665
+ font-weight: 600;
666
+ padding: 0.5rem;
667
+ font-size: 0.9rem;
668
+ }
669
+
670
+ .month-grid {
671
+ display: grid;
672
+ grid-template-columns: repeat(7, 1fr);
673
+ gap: 0.25rem;
674
+ flex: 1;
675
+ }
676
+
677
+ .month-day {
678
+ aspect-ratio: 1;
679
+ border: 1px solid rgba(0, 0, 0, 0.1);
680
+ border-radius: 0.4rem;
681
+ padding: 0.5rem;
682
+ cursor: pointer;
683
+ transition: background 0.2s;
684
+ display: flex;
685
+ flex-direction: column;
686
+ }
687
+
688
+ .month-day:hover {
689
+ background: rgba(0, 0, 0, 0.05);
690
+ }
691
+
692
+ .dark .month-day:hover {
693
+ background: rgba(255, 255, 255, 0.05);
694
+ }
695
+
696
+ .month-day.other-month {
697
+ opacity: 0.3;
698
+ }
699
+
700
+ .month-day.today {
701
+ background: rgba(6, 109, 90, 0.2);
702
+ border-color: #066D5A;
703
+ }
704
+
705
+ .month-day .day-number {
706
+ font-weight: 600;
707
+ margin-bottom: 0.25rem;
708
+ }
709
+
710
+ .day-events {
711
+ display: flex;
712
+ gap: 0.25rem;
713
+ flex-wrap: wrap;
714
+ }
715
+
716
+ .event-dot {
717
+ width: 6px;
718
+ height: 6px;
719
+ border-radius: 50%;
720
+ }
721
+
722
+ .more-events {
723
+ font-size: 0.7rem;
724
+ color: rgba(0, 0, 0, 0.6);
725
+ }
726
+
727
+ .dark .more-events {
728
+ color: rgba(255, 255, 255, 0.6);
729
+ }
730
+
731
+ /* Eventos */
732
+ .event-card {
733
+ padding: 0.5rem;
734
+ border-radius: 0.4rem;
735
+ margin-bottom: 0.25rem;
736
+ cursor: pointer;
737
+ color: white;
738
+ position: relative;
739
+ }
740
+
741
+ .event-card:hover {
742
+ opacity: 0.9;
743
+ }
744
+
745
+ .event-card.small {
746
+ padding: 0.25rem;
747
+ font-size: 0.75rem;
748
+ }
749
+
750
+ .event-title {
751
+ font-weight: 600;
752
+ margin-bottom: 0.25rem;
753
+ }
754
+
755
+ .event-time {
756
+ font-size: 0.85rem;
757
+ opacity: 0.9;
758
+ }
759
+
760
+ .event-recurring {
761
+ position: absolute;
762
+ top: 0.25rem;
763
+ right: 0.25rem;
764
+ font-size: 0.75rem;
765
+ }
766
+
767
+ /* Modal */
768
+ .modal-overlay {
769
+ position: fixed;
770
+ top: 0;
771
+ left: 0;
772
+ right: 0;
773
+ bottom: 0;
774
+ background: rgba(0, 0, 0, 0.5);
775
+ display: flex;
776
+ align-items: center;
777
+ justify-content: center;
778
+ z-index: 1000;
779
+ }
780
+
781
+ .modal-content {
782
+ background: white;
783
+ border-radius: 1rem;
784
+ width: 90%;
785
+ max-width: 500px;
786
+ max-height: 90vh;
787
+ overflow-y: auto;
788
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
789
+ }
790
+
791
+ .dark .modal-content {
792
+ background: #2a2a2a;
793
+ }
794
+
795
+ .modal-header {
796
+ display: flex;
797
+ justify-content: space-between;
798
+ align-items: center;
799
+ padding: 1.5rem;
800
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
801
+ }
802
+
803
+ .dark .modal-header {
804
+ border-bottom-color: rgba(255, 255, 255, 0.1);
805
+ }
806
+
807
+ .modal-header h3 {
808
+ margin: 0;
809
+ font-size: 1.5rem;
810
+ }
811
+
812
+ .close-btn {
813
+ width: 2rem;
814
+ height: 2rem;
815
+ border: none;
816
+ background: transparent;
817
+ font-size: 1.5rem;
818
+ cursor: pointer;
819
+ border-radius: 0.4rem;
820
+ color: inherit;
821
+ }
822
+
823
+ .close-btn:hover {
824
+ background: rgba(0, 0, 0, 0.1);
825
+ }
826
+
827
+ .dark .close-btn:hover {
828
+ background: rgba(255, 255, 255, 0.1);
829
+ }
830
+
831
+ .modal-body {
832
+ padding: 1.5rem;
833
+ }
834
+
835
+ .form-group {
836
+ margin-bottom: 1.5rem;
837
+ }
838
+
839
+ .form-group label {
840
+ display: block;
841
+ margin-bottom: 0.5rem;
842
+ font-weight: 600;
843
+ }
844
+
845
+ .form-group input[type="text"],
846
+ .form-group input[type="date"],
847
+ .form-group input[type="time"],
848
+ .form-group select {
849
+ width: 100%;
850
+ padding: 0.75rem;
851
+ border: 1px solid rgba(0, 0, 0, 0.2);
852
+ border-radius: 0.4rem;
853
+ font-size: 1rem;
854
+ background: white;
855
+ color: #202020;
856
+ }
857
+
858
+ .dark .form-group input[type="text"],
859
+ .dark .form-group input[type="date"],
860
+ .dark .form-group input[type="time"],
861
+ .dark .form-group select {
862
+ background: #1a1a1a;
863
+ border-color: rgba(255, 255, 255, 0.2);
864
+ color: #dfdfdf;
865
+ }
866
+
867
+ .form-row {
868
+ display: grid;
869
+ grid-template-columns: 1fr 1fr;
870
+ gap: 1rem;
871
+ }
872
+
873
+ .checkbox-label {
874
+ display: flex !important;
875
+ align-items: center;
876
+ gap: 0.5rem;
877
+ cursor: pointer;
878
+ }
879
+
880
+ .checkbox-label input[type="checkbox"] {
881
+ width: 1.25rem;
882
+ height: 1.25rem;
883
+ cursor: pointer;
884
+ }
885
+
886
+ .color-picker {
887
+ display: flex;
888
+ gap: 0.5rem;
889
+ flex-wrap: wrap;
890
+ }
891
+
892
+ .color-option {
893
+ width: 2.5rem;
894
+ height: 2.5rem;
895
+ border-radius: 0.4rem;
896
+ cursor: pointer;
897
+ border: 3px solid transparent;
898
+ transition: all 0.2s;
899
+ }
900
+
901
+ .color-option:hover {
902
+ transform: scale(1.1);
903
+ }
904
+
905
+ .color-option.selected {
906
+ border-color: white;
907
+ box-shadow: 0 0 0 2px #066D5A;
908
+ }
909
+
910
+ .modal-footer {
911
+ display: flex;
912
+ justify-content: flex-end;
913
+ gap: 0.5rem;
914
+ padding: 1.5rem;
915
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
916
+ }
917
+
918
+ .dark .modal-footer {
919
+ border-top-color: rgba(255, 255, 255, 0.1);
920
+ }
921
+
922
+ .modal-footer button {
923
+ padding: 0.75rem 1.5rem;
924
+ border: none;
925
+ border-radius: 0.4rem;
926
+ font-weight: 600;
927
+ cursor: pointer;
928
+ transition: all 0.2s;
929
+ }
930
+
931
+ .cancel-btn {
932
+ background: rgba(0, 0, 0, 0.1);
933
+ color: inherit;
934
+ }
935
+
936
+ .cancel-btn:hover {
937
+ background: rgba(0, 0, 0, 0.2);
938
+ }
939
+
940
+ .save-btn {
941
+ background: #066D5A;
942
+ color: white;
943
+ }
944
+
945
+ .save-btn:hover {
946
+ background: #055a4a;
947
+ }
948
+
949
+ .delete-btn {
950
+ background: #dc2626;
951
+ color: white;
952
+ margin-right: auto;
953
+ }
954
+
955
+ .delete-btn:hover {
956
+ background: #b91c1c;
957
+ }
958
+
959
+ /* Responsive */
960
+ @media (max-width: 768px) {
961
+ .calendar-header {
962
+ flex-direction: column;
963
+ align-items: stretch;
964
+ }
965
+
966
+ .navigation {
967
+ justify-content: center;
968
+ }
969
+
970
+ .current-date {
971
+ min-width: auto;
972
+ }
973
+
974
+ .week-header,
975
+ .week-hour-row {
976
+ grid-template-columns: 40px repeat(7, 1fr);
977
+ }
978
+
979
+ .hour-label,
980
+ .week-hour-label,
981
+ .week-time-label {
982
+ width: 40px;
983
+ font-size: 0.7rem;
984
+ }
985
+
986
+ .form-row {
987
+ grid-template-columns: 1fr;
988
+ }
989
+ }
990
+ </style>