@golstats/gsc-reports 1.0.53 → 1.0.54

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.
Files changed (36) hide show
  1. package/README.md +2 -2
  2. package/dist/{FilterConditions-55d68355-zHMe0Dyg-CLarFrVK-C2M1z5WD.js → FilterConditions-55d68355-zHMe0Dyg-CrLqkkfP-M-GLdj8H.js} +10 -10
  3. package/dist/FilterField-59a73e38-DtNZKbqt-BMRKmoBk-C6CpYJ5U.js +21 -0
  4. package/dist/{FilterSubcategories-a9b32cc9-BjvvEE_X-Cb3jkvIi-CQ-9zRuN.js → FilterSubcategories-a9b32cc9-BjvvEE_X-DQe88zvC-BZF4ehk1.js} +1 -1
  5. package/dist/css/fonts.css +83 -83
  6. package/dist/gsc-reports.css +1 -1
  7. package/dist/gsc-reports.es.js +1 -1
  8. package/dist/gsc-reports.umd.js +149 -149
  9. package/dist/images/cancha-horizontal.jpg +0 -0
  10. package/dist/{index-B5MO1vo4.js → index-DoXBPhEZ.js} +23546 -23493
  11. package/package.json +2 -3
  12. package/src/components/elementsTemplates/ModalDeleteReport.vue +246 -246
  13. package/src/components/elementsTemplates/ModalDeleteTemplate.vue +249 -249
  14. package/src/components/elementsTemplates/ModalRenameReporte.vue +330 -330
  15. package/src/components/elementsTemplates/ModalRenameTemplate.vue +337 -337
  16. package/src/components/elementsTemplates/ModalSoloEscritorio.vue +83 -83
  17. package/src/components/elementsTemplates/ModalduplicateTemplate.vue +300 -300
  18. package/src/components/elementsTemplates/TooltipReportOptions.vue +97 -97
  19. package/src/components/elementsTemplates/TooltipTemplateOptions.vue +168 -168
  20. package/src/components/filters.vue +935 -935
  21. package/src/components/template-report-maker/CoverPage.vue +636 -636
  22. package/src/components/template-report-maker/CoverSelector.vue +165 -165
  23. package/src/components/template-report-maker/ReportView.vue +66 -66
  24. package/src/components/template-report-maker/TemplateReportPage.vue +398 -398
  25. package/src/components/thumbnails-reports/AnalisisPostMatchType1.vue +741 -741
  26. package/src/components/thumbnails-reports/AnalisisPostMatchType2.vue +743 -743
  27. package/src/components/thumbnails-reports/AnalisisPostMatchType3.vue +441 -441
  28. package/src/components/thumbnails-reports/AnalisisPostMatchType4.vue +440 -440
  29. package/src/components/thumbnails-reports/AnalisisPrematchType1.vue +232 -232
  30. package/src/components/thumbnails-reports/AnalisisPrematchType2.vue +231 -231
  31. package/src/components/thumbnails-reports/AnalisisPrematchType3.vue +173 -173
  32. package/src/components/thumbnails-reports/AnalisisPrematchType4.vue +173 -173
  33. package/src/index.js +4 -4
  34. package/src/types.d.ts +45 -45
  35. package/src/utils/dateUtils.js +52 -52
  36. package/dist/FilterField-59a73e38-DtNZKbqt-CX86CGMh-DPrMukyS.js +0 -21
@@ -1,935 +1,935 @@
1
- <template>
2
- <div class="floating-bar">
3
- <button class="tab active">Nueva vista</button>
4
- <!-- Primer selector: opciones principales -->
5
- <div class="custom-select" @click="toggleFieldMenu">
6
- <div class="select-display">
7
- <span class="select-icon">
8
- <img v-if="selectedMain" :src="selectedMain.url" alt="icono" class="option-img" />
9
- </span>
10
- <span v-if="!selectedMain" class="select-placeholder select-label">Tipo de vista</span>
11
- <span v-else class="select-label">{{ getFieldLabel() }}</span>
12
- <span class="arrow" :class="{ open: showFieldMenu }">
13
- <svg width="16" height="14" viewBox="0 0 16 14" fill="none">
14
- <path
15
- d="M3 5L8 10L13 5"
16
- stroke="#AAB4BE"
17
- stroke-width="2"
18
- stroke-linecap="round"
19
- stroke-linejoin="round"
20
- />
21
- </svg>
22
- </span>
23
- </div>
24
- <div v-if="showFieldMenu" class="dropdown-menu field-menu">
25
- <div
26
- v-for="(option, idx) in mainOptions"
27
- :key="option.id"
28
- class="option-card"
29
- :class="{ active: selectedMainIndex === idx }"
30
- @click.stop="selectMain(idx)"
31
- >
32
- <div class="option-icon">
33
- <img :src="option.url" alt="icono" class="option-img" />
34
- </div>
35
- <div class="option-content">
36
- <div class="option-title">{{ option.name }}</div>
37
- </div>
38
- </div>
39
- </div>
40
- </div>
41
- <!-- Segundo selector: views hijos -->
42
- <div class="custom-select" @click="toggleTypeMenu">
43
- <div class="select-display">
44
- <span class="select-icon">
45
- <img v-if="selectedSecond" :src="selectedSecond.url" alt="icono" class="option-img" />
46
- </span>
47
- <span class="select-label">{{ getTypeLabel() }}</span>
48
- <span class="arrow" :class="{ open: showTypeMenu }">
49
- <svg width="16" height="14" viewBox="0 0 16 14" fill="none">
50
- <path
51
- d="M3 5L8 10L13 5"
52
- stroke="#AAB4BE"
53
- stroke-width="2"
54
- stroke-linecap="round"
55
- stroke-linejoin="round"
56
- />
57
- </svg>
58
- </span>
59
- </div>
60
- <div v-if="showTypeMenu && selectedMain" class="dropdown-menu type-menu type-menu-grid">
61
- <div class="option-grid">
62
- <div
63
- v-for="option in secondOptions"
64
- :key="option.id"
65
- class="option-card-grid"
66
- :class="{ active: selectedSecondId === option.id }"
67
- @click.stop="selectSecond(option)"
68
- >
69
- <div class="option-grid-title">{{ option.name || option.title }}</div>
70
- <div class="option-grid-icon">
71
- <img :src="option.url" alt="icono" class="option-img-grid" />
72
- </div>
73
- </div>
74
- </div>
75
- </div>
76
- </div>
77
- <!-- Separador después del segundo selector -->
78
- <div class="filter-separator"></div>
79
- <!-- Filtros dinámicos -->
80
- <div
81
- v-if="selectedSecond && selectedSecond.filters"
82
- :class="['dynamic-filters', { 'dynamic-filters-wide': selectedSecond.filters.length >= 2 }]"
83
- >
84
- <template v-for="(filter, idx) in selectedSecond.filters" :key="idx">
85
- <div
86
- v-if="filter.type === 'select'"
87
- class="custom-select dynamic-select filter-inline"
88
- @click.stop="toggleDynamicSelect(idx)"
89
- >
90
- <div class="select-display">
91
- <span class="select-icon">
92
- <img v-if="filter.icon" :src="filter.icon" alt="icono" class="option-img" />
93
- </span>
94
- <span class="select-label">
95
- {{ getMultiSelectLabel(idx) || filter.label || 'Selecciona opciones' }}
96
- </span>
97
- <span class="arrow" :class="{ open: dynamicSelectOpen === idx }">
98
- <svg width="16" height="14" viewBox="0 0 16 14" fill="none">
99
- <path
100
- d="M3 5L8 10L13 5"
101
- stroke="#AAB4BE"
102
- stroke-width="2"
103
- stroke-linecap="round"
104
- stroke-linejoin="round"
105
- />
106
- </svg>
107
- </span>
108
- </div>
109
- <div v-if="dynamicSelectOpen === idx" class="dropdown-menu type-menu multi-select-menu">
110
- <div class="multi-select-header" @click.stop>
111
- <label class="select-all-checkbox">
112
- <input
113
- type="checkbox"
114
- :checked="areAllSelected(idx)"
115
- :indeterminate="isIndeterminate(idx)"
116
- @change.stop="toggleSelectAll(idx)"
117
- @click.stop
118
- />
119
- <span class="select-all-label">Agregar todas</span>
120
- </label>
121
- </div>
122
- <div
123
- v-for="option in filter.options"
124
- :key="option.value"
125
- class="option-card multi-select-option"
126
- :class="{ active: isOptionSelected(idx, option.value) }"
127
- @click.stop="toggleMultiSelectOption(idx, option.value)"
128
- >
129
- <div class="option-checkbox">
130
- <input
131
- type="checkbox"
132
- :checked="isOptionSelected(idx, option.value)"
133
- @change.stop="toggleMultiSelectOption(idx, option.value)"
134
- />
135
- </div>
136
- <div class="option-icon">
137
- <img v-if="option.icon" :src="option.icon" alt="icono" class="option-img" />
138
- </div>
139
- <div class="option-content">
140
- <div class="option-title">{{ option.label }}</div>
141
- </div>
142
- </div>
143
- </div>
144
- </div>
145
- <div v-else-if="filter.type === 'radio-button'" class="custom-radio-group filter-inline">
146
- <div class="radio-label">{{ filter.label }}</div>
147
- <div class="radio-options">
148
- <label
149
- v-for="option in filter.options"
150
- :key="option.value"
151
- class="radio-option-card"
152
- :class="{ active: dynamicFilters[idx] === option.value }"
153
- >
154
- <input
155
- type="radio"
156
- :name="'dynamic-radio-' + idx"
157
- :value="option.value"
158
- v-model="dynamicFilters[idx]"
159
- @change="emitChange"
160
- />
161
- <span>{{ option.label }}</span>
162
- </label>
163
- </div>
164
- </div>
165
- <div v-else-if="filter.type === 'check'" class="check-group filter-inline">
166
- <label>
167
- <input type="checkbox" v-model="dynamicFilters[idx]" @change="emitChange" />
168
- {{ filter.label }}
169
- </label>
170
- </div>
171
- <div v-if="idx === 1 && selectedSecond.filters.length > 2" class="filter-separator"></div>
172
- </template>
173
- </div>
174
-
175
- <button class="close-btn" @click="emitClose">✕</button>
176
- </div>
177
- </template>
178
-
179
- <script setup>
180
- import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
181
-
182
- defineOptions({
183
- name: 'FloatingFiltersBar',
184
- })
185
-
186
- const props = defineProps({
187
- token: {
188
- type: String,
189
- required: true,
190
- },
191
- })
192
-
193
- const emit = defineEmits(['filters-changed', 'close'])
194
-
195
- // Estado para los datos de la API
196
- const apiData = ref([])
197
-
198
- // Opciones del primer selector: solo el primer nivel
199
- const mainOptions = computed(() => apiData.value)
200
- const selectedMainIndex = ref(null)
201
- const selectedMain = computed(() => mainOptions.value[selectedMainIndex.value] || null)
202
-
203
- // Opciones del segundo selector: los views hijos del seleccionado
204
- const secondOptions = computed(() => selectedMain.value?.views || [])
205
- const selectedSecondId = ref(null)
206
- const selectedSecond = computed(
207
- () => secondOptions.value.find((v) => v.id === selectedSecondId.value) || null,
208
- )
209
-
210
- // Filtros dinámicos para el view seleccionado
211
- const dynamicFilters = ref([])
212
-
213
- // Sincroniza los filtros dinámicos cuando cambia el view seleccionado
214
- watch(selectedSecond, (newVal) => {
215
- if (!newVal || !Array.isArray(newVal.filters)) {
216
- dynamicFilters.value = []
217
- return
218
- }
219
- // Inicializa los valores según el tipo de filtro
220
- dynamicFilters.value = newVal.filters.map((f) => {
221
- if (f.type === 'select') {
222
- return [] // Array vacío para selección múltiple
223
- } else if (f.type === 'radio-button') {
224
- return f.options?.[0]?.value ?? null
225
- } else if (f.type === 'check') {
226
- return f.value ?? false
227
- }
228
- return null
229
- })
230
- })
231
-
232
- // Funciones para manejo de selección múltiple
233
- function isOptionSelected(filterIdx, optionValue) {
234
- const selectedValues = dynamicFilters.value[filterIdx]
235
- return Array.isArray(selectedValues) && selectedValues.includes(optionValue)
236
- }
237
-
238
- function toggleMultiSelectOption(filterIdx, optionValue) {
239
- if (!Array.isArray(dynamicFilters.value[filterIdx])) {
240
- dynamicFilters.value[filterIdx] = []
241
- }
242
-
243
- const selectedValues = dynamicFilters.value[filterIdx]
244
- const index = selectedValues.indexOf(optionValue)
245
-
246
- if (index > -1) {
247
- selectedValues.splice(index, 1)
248
- } else {
249
- selectedValues.push(optionValue)
250
- }
251
-
252
- emitChange()
253
- }
254
-
255
- function getMultiSelectLabel(filterIdx) {
256
- const selectedValues = dynamicFilters.value[filterIdx]
257
- if (!Array.isArray(selectedValues) || selectedValues.length === 0) {
258
- return 'Selecciona opciones'
259
- }
260
-
261
- const filter = selectedSecond.value?.filters?.[filterIdx]
262
- if (!filter || !filter.options) return 'Opciones seleccionadas'
263
-
264
- const selectedLabels = selectedValues
265
- .map((value) => filter.options.find((opt) => opt.value === value)?.label)
266
- .filter(Boolean)
267
-
268
- if (selectedLabels.length === 1) {
269
- return selectedLabels[0]
270
- } else if (selectedLabels.length <= 3) {
271
- return selectedLabels.join(', ')
272
- } else {
273
- return `${selectedLabels.length} opciones seleccionadas`
274
- }
275
- }
276
-
277
- function areAllSelected(filterIdx) {
278
- const selectedValues = dynamicFilters.value[filterIdx]
279
- const filter = selectedSecond.value?.filters?.[filterIdx]
280
- if (!filter || !filter.options || !Array.isArray(selectedValues)) return false
281
-
282
- return selectedValues.length === filter.options.length
283
- }
284
-
285
- function isIndeterminate(filterIdx) {
286
- const selectedValues = dynamicFilters.value[filterIdx]
287
- const filter = selectedSecond.value?.filters?.[filterIdx]
288
- if (!filter || !filter.options || !Array.isArray(selectedValues)) return false
289
-
290
- return selectedValues.length > 0 && selectedValues.length < filter.options.length
291
- }
292
-
293
- function toggleSelectAll(filterIdx) {
294
- const filter = selectedSecond.value?.filters?.[filterIdx]
295
- if (!filter || !filter.options) return
296
-
297
- const allValues = filter.options.map((opt) => opt.value)
298
-
299
- // Asegurar que dynamicFilters[filterIdx] sea un array
300
- if (!Array.isArray(dynamicFilters.value[filterIdx])) {
301
- dynamicFilters.value[filterIdx] = []
302
- }
303
-
304
- if (areAllSelected(filterIdx)) {
305
- // Si todas están seleccionadas, deseleccionar todas
306
- dynamicFilters.value[filterIdx] = []
307
- } else {
308
- // Si no todas están seleccionadas, seleccionar todas
309
- dynamicFilters.value[filterIdx] = [...allValues]
310
- }
311
-
312
- // Mantener el menú abierto
313
- dynamicSelectOpen.value = filterIdx
314
-
315
- emitChange()
316
- }
317
-
318
- const selectedTeam = ref('ambos')
319
- const showFieldMenu = ref(false)
320
- const showTypeMenu = ref(false)
321
- const dynamicSelectOpen = ref(null)
322
-
323
- function getFieldLabel() {
324
- if (!selectedMain.value) return 'Tipo de vista'
325
- return selectedMain.value.name
326
- }
327
- function getTypeLabel() {
328
- if (!selectedSecond.value) return 'Selecciona una opción'
329
- return selectedSecond.value.name || selectedSecond.value.title
330
- }
331
-
332
- function toggleFieldMenu() {
333
- showFieldMenu.value = !showFieldMenu.value
334
- if (showFieldMenu.value) {
335
- showTypeMenu.value = false
336
- }
337
- }
338
- function toggleTypeMenu() {
339
- if (!selectedMain.value) return // No abrir si no hay selección
340
- showTypeMenu.value = !showTypeMenu.value
341
- if (showTypeMenu.value) {
342
- showFieldMenu.value = false
343
- }
344
- }
345
- function selectMain(index) {
346
- selectedMainIndex.value = index
347
- showFieldMenu.value = false
348
- // Limpiar selección dependiente
349
- selectedSecondId.value = null
350
- emitChange()
351
- }
352
- function selectSecond(option) {
353
- selectedSecondId.value = option.id
354
- showTypeMenu.value = false
355
- emitChange()
356
- }
357
- function toggleDynamicSelect(idx) {
358
- dynamicSelectOpen.value = dynamicSelectOpen.value === idx ? null : idx
359
- }
360
- function handleClickOutside(event) {
361
- if (!event.target.closest('.custom-select')) {
362
- showFieldMenu.value = false
363
- showTypeMenu.value = false
364
- }
365
- }
366
- function emitChange() {
367
- emit('filters-changed', {
368
- main: selectedMain.value,
369
- view: selectedSecond.value,
370
- team: selectedTeam.value,
371
- dynamicFilters: dynamicFilters.value,
372
- })
373
- }
374
- function resetFilters() {
375
- selectedMainIndex.value = null
376
- selectedSecondId.value = null
377
- selectedTeam.value = 'ambos'
378
- dynamicFilters.value = []
379
- showFieldMenu.value = false
380
- showTypeMenu.value = false
381
- dynamicSelectOpen.value = null
382
- }
383
- function emitClose() {
384
- resetFilters()
385
- emit('close')
386
- }
387
-
388
- onMounted(async () => {
389
- document.addEventListener('click', handleClickOutside)
390
- try {
391
- const response = await fetch(
392
- 'https://m9qip57rsh.execute-api.us-east-2.amazonaws.com/prod/views',
393
- {
394
- headers: {
395
- Authorization: `${props.token}`,
396
- },
397
- },
398
- )
399
- const data = await response.json()
400
- apiData.value = data.data || []
401
- } catch (error) {
402
- console.error('Error al obtener datos:', error)
403
- }
404
- })
405
- onUnmounted(() => {
406
- document.removeEventListener('click', handleClickOutside)
407
- })
408
- </script>
409
-
410
- <style scoped>
411
- .floating-bar {
412
- position: absolute;
413
- top: 67px;
414
- left: 50%;
415
- background: #2a3843;
416
- border-radius: 8px;
417
- display: flex;
418
- align-items: center;
419
- padding: 8px 32px;
420
- z-index: 100;
421
- gap: 12px;
422
- height: 62px;
423
- width: auto;
424
- min-width: 600px;
425
- max-width: 100vw;
426
- box-sizing: border-box;
427
- transform: translateX(-50%);
428
- }
429
- .dynamic-select,
430
- .radio-group,
431
- .check-group {
432
- display: inline-flex;
433
- margin-bottom: 0 !important;
434
- }
435
-
436
- .tab {
437
- background: none;
438
- color: #fafafb;
439
- border: none;
440
- border-radius: 6px 6px 0 0;
441
- padding: 8px 18px;
442
- font-weight: bold;
443
- font-size: 16px;
444
- margin-right: 8px;
445
- }
446
-
447
- .tab.active {
448
- color: #fff;
449
- }
450
-
451
- .custom-select {
452
- position: relative;
453
- cursor: pointer;
454
- }
455
-
456
- .select-display {
457
- background: #3f4c56;
458
- color: #fafafb;
459
- border: 1px solid #e9ecee;
460
- border-radius: 6px;
461
- padding: 0px 32px 0px 12px;
462
- font-size: 15px;
463
- display: flex;
464
- align-items: center;
465
- justify-content: space-between;
466
- min-width: 140px;
467
- height: 40px;
468
- transition: border-color 0.2s;
469
- }
470
- .select-icon {
471
- display: flex;
472
- align-items: center;
473
- margin-right: 8px;
474
- }
475
-
476
- .select-display:hover {
477
- border-color: #cbee6b;
478
- }
479
-
480
- .arrow {
481
- display: flex;
482
- align-items: center;
483
- transition: transform 0.2s;
484
- }
485
- .arrow svg {
486
- display: block;
487
- }
488
- .arrow.open {
489
- transform: rotate(180deg);
490
- }
491
-
492
- .custom-select:hover .arrow {
493
- transform: rotate(180deg);
494
- }
495
-
496
- .dropdown-menu {
497
- position: absolute;
498
- top: 100%;
499
- left: 0;
500
- background: #2a3843;
501
- border: 1px solid #3d4a54;
502
- border-radius: 8px;
503
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
504
- padding: 8px;
505
- margin-top: 4px;
506
- min-width: 200px;
507
- z-index: 1000;
508
- }
509
-
510
- .option-card {
511
- display: flex;
512
- align-items: center;
513
- padding: 12px;
514
- border-radius: 6px;
515
- cursor: pointer;
516
- transition: background-color 0.2s;
517
- margin-bottom: 4px;
518
- }
519
-
520
- .option-card:last-child {
521
- margin-bottom: 0;
522
- }
523
-
524
- .option-card:hover {
525
- background: #3d4a54;
526
- }
527
-
528
- .option-card.active {
529
- background: #cbee6b;
530
- color: #1a2a36;
531
- }
532
-
533
- .option-icon {
534
- font-size: 20px;
535
- margin-right: 12px;
536
- width: 24px;
537
- text-align: center;
538
- }
539
-
540
- .option-content {
541
- flex: 1;
542
- }
543
-
544
- .option-title {
545
- font-weight: 600;
546
- font-size: 14px;
547
- margin-bottom: 2px;
548
- }
549
-
550
- .option-description {
551
- font-size: 12px;
552
- opacity: 0.8;
553
- }
554
-
555
- .radio-group {
556
- display: flex;
557
- align-items: center;
558
- gap: 10px;
559
- margin-left: 16px;
560
- }
561
-
562
- .radio-group label {
563
- color: #fafafb;
564
- font-size: 15px;
565
- display: flex;
566
- align-items: center;
567
- gap: 4px;
568
- }
569
-
570
- input[type='radio'] {
571
- appearance: none;
572
- -webkit-appearance: none;
573
- background: #2a3843;
574
- border: 2px solid #e9ecee;
575
- width: 20px;
576
- height: 20px;
577
- border-radius: 50%;
578
- margin-right: 6px;
579
- margin-top: 0px;
580
- position: relative;
581
- cursor: pointer;
582
- outline: none;
583
- transition: border-color 0.2s;
584
- }
585
- input[type='radio']:checked::before {
586
- content: '';
587
- display: block;
588
- width: 12px;
589
- height: 12px;
590
- background: #cbee6b;
591
- border-radius: 50%;
592
- position: absolute;
593
- top: 2px;
594
- left: 2px;
595
- }
596
-
597
- .check-group {
598
- display: flex;
599
- align-items: center;
600
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
601
- font-size: 14px;
602
- color: #fff;
603
- background: none;
604
- border: none;
605
- border-radius: 0;
606
- padding: 0 8px;
607
- min-height: 40px;
608
- margin-right: 8px;
609
- }
610
- .check-group input[type='checkbox'] {
611
- accent-color: #cbee6b;
612
- margin-right: 8px;
613
- width: 18px;
614
- height: 18px;
615
- }
616
-
617
- .close-btn {
618
- background: none;
619
- border: none;
620
- color: #fafafb;
621
- font-size: 22px;
622
- margin-left: auto;
623
- cursor: pointer;
624
- transition: color 0.2s;
625
- }
626
-
627
- .close-btn:hover {
628
- color: #cbee6b;
629
- }
630
-
631
- .select-label {
632
- flex-grow: 0;
633
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
634
- font-size: 14px;
635
- font-weight: 500;
636
- font-stretch: normal;
637
- font-style: normal;
638
- margin-left: -10px;
639
- line-height: normal;
640
- letter-spacing: normal;
641
- text-align: left;
642
- color: #d9d9d9;
643
- }
644
- .option-title {
645
- flex-grow: 0;
646
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
647
- font-size: 14px;
648
- font-weight: 500;
649
- font-stretch: normal;
650
- font-style: normal;
651
- line-height: normal;
652
- letter-spacing: normal;
653
- text-align: left;
654
- color: #fff;
655
- }
656
- .select-placeholder {
657
- font-family: 'Poppins-Regular', 'Poppins', Arial, sans-serif !important;
658
- font-size: 14px;
659
- margin-left: -8px;
660
- color: #d9d9d9;
661
- }
662
- .option-img {
663
- width: 24px;
664
- height: 24px;
665
- object-fit: cover;
666
- border-radius: 4px;
667
- background: #222;
668
- }
669
- .custom-radio-group {
670
- display: flex;
671
- flex-direction: row;
672
- align-items: center;
673
- border-radius: 6px;
674
- padding: 0px 16px;
675
- min-height: 40px;
676
- justify-content: center;
677
- margin-right: 8px;
678
- }
679
- .radio-label {
680
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
681
- font-size: 13px;
682
- color: #d9d9d9;
683
- margin-bottom: 0;
684
- margin-right: 8px;
685
- }
686
- .radio-options {
687
- display: flex;
688
- flex-direction: row;
689
- gap: 8px;
690
- flex-wrap: nowrap;
691
- }
692
- .radio-option-card {
693
- display: flex;
694
- align-items: center;
695
- background: transparent;
696
- border: none;
697
- border-radius: 4px;
698
- padding: 6px 10px;
699
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
700
- font-size: 14px;
701
- color: #fff;
702
- cursor: pointer;
703
- transition:
704
- background 0.2s,
705
- color 0.2s;
706
- white-space: nowrap;
707
- min-width: 0;
708
- max-width: 120px;
709
- justify-content: center;
710
- }
711
-
712
- .filter-separator {
713
- width: 1px;
714
- height: 32px;
715
- border-left: 2px dashed #d9d9d9;
716
- margin: 0 16px;
717
- align-self: center;
718
- }
719
- .filter-inline {
720
- display: inline-flex;
721
- align-items: center;
722
- vertical-align: middle;
723
- margin-bottom: 0 !important;
724
- min-width: 140px;
725
- }
726
- .select-label,
727
- .option-title,
728
- .radio-label,
729
- .radio-option-card span,
730
- .check-group label {
731
- white-space: nowrap;
732
- overflow: hidden;
733
- text-overflow: ellipsis;
734
- max-width: 180px;
735
- display: inline-block;
736
- vertical-align: middle;
737
- }
738
- .dynamic-filters {
739
- display: flex;
740
- align-items: center;
741
- transition:
742
- min-width 0.2s,
743
- padding 0.2s;
744
- }
745
- .dynamic-filters-wide {
746
- min-width: 400px;
747
- padding-right: 24px;
748
- padding-left: 24px;
749
- }
750
- .dynamic-filters-wide > .filter-inline:not(:last-child) {
751
- margin-right: 16px;
752
- }
753
- .type-menu-grid {
754
- min-width: 360px;
755
- max-width: 420px;
756
- }
757
- .option-grid {
758
- display: grid;
759
- grid-template-columns: repeat(3, 1fr);
760
- gap: 16px;
761
- padding: 8px 0;
762
- }
763
- .option-card-grid {
764
- display: flex;
765
- flex-direction: column;
766
- align-items: center;
767
- justify-content: flex-start;
768
- background: #3f4c56;
769
- border: 2px solid transparent;
770
- border-radius: 10px;
771
- padding: 12px 8px 10px 8px;
772
- cursor: pointer;
773
- transition:
774
- border-color 0.2s,
775
- background 0.2s;
776
- min-width: 90px;
777
- min-height: 110px;
778
- box-sizing: border-box;
779
- }
780
- .option-card-grid.active,
781
- .option-card-grid:hover {
782
- border-color: #cbee6b;
783
- background: #22334a;
784
- }
785
- .option-grid-title {
786
- font-size: 11px;
787
- color: #d9d9d9;
788
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
789
- margin-bottom: 8px;
790
- text-align: center;
791
- white-space: nowrap;
792
- overflow: hidden;
793
- text-overflow: ellipsis;
794
- width: 100%;
795
- }
796
- .option-grid-icon {
797
- display: flex;
798
- align-items: center;
799
- justify-content: center;
800
- flex: 1;
801
- }
802
- .option-img-grid {
803
- width: 40px;
804
- height: 40px;
805
- object-fit: cover;
806
- border-radius: 6px;
807
- background: #222;
808
- }
809
-
810
- /* Estilos para selector múltiple */
811
- .multi-select-menu {
812
- min-width: 280px;
813
- max-width: 400px;
814
- }
815
-
816
- .multi-select-header {
817
- display: flex;
818
- justify-content: space-between;
819
- align-items: center;
820
- padding: 8px 12px;
821
- border-bottom: 1px solid #3d4a54;
822
- margin-bottom: 8px;
823
- }
824
-
825
- .select-all-checkbox {
826
- display: flex;
827
- align-items: center;
828
- cursor: pointer;
829
- gap: 8px;
830
- }
831
-
832
- .select-all-checkbox input[type='checkbox'] {
833
- appearance: none;
834
- -webkit-appearance: none;
835
- width: 18px;
836
- height: 18px;
837
- border: 2px solid #e9ecee;
838
- border-radius: 3px;
839
- background: #2a3843;
840
- cursor: pointer;
841
- position: relative;
842
- transition: all 0.2s;
843
- }
844
-
845
- .select-all-checkbox input[type='checkbox']:checked {
846
- background: #cbee6b;
847
- border-color: #cbee6b;
848
- }
849
-
850
- .select-all-checkbox input[type='checkbox']:checked::before {
851
- content: '✓';
852
- position: absolute;
853
- top: 50%;
854
- left: 50%;
855
- transform: translate(-50%, -50%);
856
- color: #1a2a36;
857
- font-size: 12px;
858
- font-weight: bold;
859
- }
860
-
861
- .select-all-checkbox input[type='checkbox']:indeterminate::before {
862
- content: '−';
863
- position: absolute;
864
- top: 50%;
865
- left: 50%;
866
- transform: translate(-50%, -50%);
867
- color: #1a2a36;
868
- font-size: 14px;
869
- font-weight: bold;
870
- }
871
-
872
- .select-all-checkbox input[type='checkbox']:hover {
873
- border-color: #cbee6b;
874
- }
875
-
876
- .select-all-label {
877
- font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
878
- font-size: 14px;
879
- color: #d9d9d9;
880
- font-weight: 500;
881
- }
882
-
883
- .multi-select-option {
884
- padding: 8px 12px;
885
- margin-bottom: 2px;
886
- }
887
-
888
- .multi-select-option:hover {
889
- background: #3d4a54;
890
- }
891
-
892
- .multi-select-option.active {
893
- background: rgba(203, 238, 107, 0.1);
894
- border: 1px solid #cbee6b;
895
- }
896
-
897
- .option-checkbox {
898
- display: flex;
899
- align-items: center;
900
- margin-right: 8px;
901
- }
902
-
903
- .option-checkbox input[type='checkbox'] {
904
- appearance: none;
905
- -webkit-appearance: none;
906
- width: 18px;
907
- height: 18px;
908
- border: 2px solid #e9ecee;
909
- border-radius: 3px;
910
- background: #2a3843;
911
- cursor: pointer;
912
- position: relative;
913
- transition: all 0.2s;
914
- }
915
-
916
- .option-checkbox input[type='checkbox']:checked {
917
- background: #cbee6b;
918
- border-color: #cbee6b;
919
- }
920
-
921
- .option-checkbox input[type='checkbox']:checked::before {
922
- content: '✓';
923
- position: absolute;
924
- top: 50%;
925
- left: 50%;
926
- transform: translate(-50%, -50%);
927
- color: #1a2a36;
928
- font-size: 12px;
929
- font-weight: bold;
930
- }
931
-
932
- .option-checkbox input[type='checkbox']:hover {
933
- border-color: #cbee6b;
934
- }
935
- </style>
1
+ <template>
2
+ <div class="floating-bar">
3
+ <button class="tab active">Nueva vista</button>
4
+ <!-- Primer selector: opciones principales -->
5
+ <div class="custom-select" @click="toggleFieldMenu">
6
+ <div class="select-display">
7
+ <span class="select-icon">
8
+ <img v-if="selectedMain" :src="selectedMain.url" alt="icono" class="option-img" />
9
+ </span>
10
+ <span v-if="!selectedMain" class="select-placeholder select-label">Tipo de vista</span>
11
+ <span v-else class="select-label">{{ getFieldLabel() }}</span>
12
+ <span class="arrow" :class="{ open: showFieldMenu }">
13
+ <svg width="16" height="14" viewBox="0 0 16 14" fill="none">
14
+ <path
15
+ d="M3 5L8 10L13 5"
16
+ stroke="#AAB4BE"
17
+ stroke-width="2"
18
+ stroke-linecap="round"
19
+ stroke-linejoin="round"
20
+ />
21
+ </svg>
22
+ </span>
23
+ </div>
24
+ <div v-if="showFieldMenu" class="dropdown-menu field-menu">
25
+ <div
26
+ v-for="(option, idx) in mainOptions"
27
+ :key="option.id"
28
+ class="option-card"
29
+ :class="{ active: selectedMainIndex === idx }"
30
+ @click.stop="selectMain(idx)"
31
+ >
32
+ <div class="option-icon">
33
+ <img :src="option.url" alt="icono" class="option-img" />
34
+ </div>
35
+ <div class="option-content">
36
+ <div class="option-title">{{ option.name }}</div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <!-- Segundo selector: views hijos -->
42
+ <div class="custom-select" @click="toggleTypeMenu">
43
+ <div class="select-display">
44
+ <span class="select-icon">
45
+ <img v-if="selectedSecond" :src="selectedSecond.url" alt="icono" class="option-img" />
46
+ </span>
47
+ <span class="select-label">{{ getTypeLabel() }}</span>
48
+ <span class="arrow" :class="{ open: showTypeMenu }">
49
+ <svg width="16" height="14" viewBox="0 0 16 14" fill="none">
50
+ <path
51
+ d="M3 5L8 10L13 5"
52
+ stroke="#AAB4BE"
53
+ stroke-width="2"
54
+ stroke-linecap="round"
55
+ stroke-linejoin="round"
56
+ />
57
+ </svg>
58
+ </span>
59
+ </div>
60
+ <div v-if="showTypeMenu && selectedMain" class="dropdown-menu type-menu type-menu-grid">
61
+ <div class="option-grid">
62
+ <div
63
+ v-for="option in secondOptions"
64
+ :key="option.id"
65
+ class="option-card-grid"
66
+ :class="{ active: selectedSecondId === option.id }"
67
+ @click.stop="selectSecond(option)"
68
+ >
69
+ <div class="option-grid-title">{{ option.name || option.title }}</div>
70
+ <div class="option-grid-icon">
71
+ <img :src="option.url" alt="icono" class="option-img-grid" />
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ <!-- Separador después del segundo selector -->
78
+ <div class="filter-separator"></div>
79
+ <!-- Filtros dinámicos -->
80
+ <div
81
+ v-if="selectedSecond && selectedSecond.filters"
82
+ :class="['dynamic-filters', { 'dynamic-filters-wide': selectedSecond.filters.length >= 2 }]"
83
+ >
84
+ <template v-for="(filter, idx) in selectedSecond.filters" :key="idx">
85
+ <div
86
+ v-if="filter.type === 'select'"
87
+ class="custom-select dynamic-select filter-inline"
88
+ @click.stop="toggleDynamicSelect(idx)"
89
+ >
90
+ <div class="select-display">
91
+ <span class="select-icon">
92
+ <img v-if="filter.icon" :src="filter.icon" alt="icono" class="option-img" />
93
+ </span>
94
+ <span class="select-label">
95
+ {{ getMultiSelectLabel(idx) || filter.label || 'Selecciona opciones' }}
96
+ </span>
97
+ <span class="arrow" :class="{ open: dynamicSelectOpen === idx }">
98
+ <svg width="16" height="14" viewBox="0 0 16 14" fill="none">
99
+ <path
100
+ d="M3 5L8 10L13 5"
101
+ stroke="#AAB4BE"
102
+ stroke-width="2"
103
+ stroke-linecap="round"
104
+ stroke-linejoin="round"
105
+ />
106
+ </svg>
107
+ </span>
108
+ </div>
109
+ <div v-if="dynamicSelectOpen === idx" class="dropdown-menu type-menu multi-select-menu">
110
+ <div class="multi-select-header" @click.stop>
111
+ <label class="select-all-checkbox">
112
+ <input
113
+ type="checkbox"
114
+ :checked="areAllSelected(idx)"
115
+ :indeterminate="isIndeterminate(idx)"
116
+ @change.stop="toggleSelectAll(idx)"
117
+ @click.stop
118
+ />
119
+ <span class="select-all-label">Agregar todas</span>
120
+ </label>
121
+ </div>
122
+ <div
123
+ v-for="option in filter.options"
124
+ :key="option.value"
125
+ class="option-card multi-select-option"
126
+ :class="{ active: isOptionSelected(idx, option.value) }"
127
+ @click.stop="toggleMultiSelectOption(idx, option.value)"
128
+ >
129
+ <div class="option-checkbox">
130
+ <input
131
+ type="checkbox"
132
+ :checked="isOptionSelected(idx, option.value)"
133
+ @change.stop="toggleMultiSelectOption(idx, option.value)"
134
+ />
135
+ </div>
136
+ <div class="option-icon">
137
+ <img v-if="option.icon" :src="option.icon" alt="icono" class="option-img" />
138
+ </div>
139
+ <div class="option-content">
140
+ <div class="option-title">{{ option.label }}</div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ <div v-else-if="filter.type === 'radio-button'" class="custom-radio-group filter-inline">
146
+ <div class="radio-label">{{ filter.label }}</div>
147
+ <div class="radio-options">
148
+ <label
149
+ v-for="option in filter.options"
150
+ :key="option.value"
151
+ class="radio-option-card"
152
+ :class="{ active: dynamicFilters[idx] === option.value }"
153
+ >
154
+ <input
155
+ type="radio"
156
+ :name="'dynamic-radio-' + idx"
157
+ :value="option.value"
158
+ v-model="dynamicFilters[idx]"
159
+ @change="emitChange"
160
+ />
161
+ <span>{{ option.label }}</span>
162
+ </label>
163
+ </div>
164
+ </div>
165
+ <div v-else-if="filter.type === 'check'" class="check-group filter-inline">
166
+ <label>
167
+ <input type="checkbox" v-model="dynamicFilters[idx]" @change="emitChange" />
168
+ {{ filter.label }}
169
+ </label>
170
+ </div>
171
+ <div v-if="idx === 1 && selectedSecond.filters.length > 2" class="filter-separator"></div>
172
+ </template>
173
+ </div>
174
+
175
+ <button class="close-btn" @click="emitClose">✕</button>
176
+ </div>
177
+ </template>
178
+
179
+ <script setup>
180
+ import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
181
+
182
+ defineOptions({
183
+ name: 'FloatingFiltersBar',
184
+ })
185
+
186
+ const props = defineProps({
187
+ token: {
188
+ type: String,
189
+ required: true,
190
+ },
191
+ })
192
+
193
+ const emit = defineEmits(['filters-changed', 'close'])
194
+
195
+ // Estado para los datos de la API
196
+ const apiData = ref([])
197
+
198
+ // Opciones del primer selector: solo el primer nivel
199
+ const mainOptions = computed(() => apiData.value)
200
+ const selectedMainIndex = ref(null)
201
+ const selectedMain = computed(() => mainOptions.value[selectedMainIndex.value] || null)
202
+
203
+ // Opciones del segundo selector: los views hijos del seleccionado
204
+ const secondOptions = computed(() => selectedMain.value?.views || [])
205
+ const selectedSecondId = ref(null)
206
+ const selectedSecond = computed(
207
+ () => secondOptions.value.find((v) => v.id === selectedSecondId.value) || null,
208
+ )
209
+
210
+ // Filtros dinámicos para el view seleccionado
211
+ const dynamicFilters = ref([])
212
+
213
+ // Sincroniza los filtros dinámicos cuando cambia el view seleccionado
214
+ watch(selectedSecond, (newVal) => {
215
+ if (!newVal || !Array.isArray(newVal.filters)) {
216
+ dynamicFilters.value = []
217
+ return
218
+ }
219
+ // Inicializa los valores según el tipo de filtro
220
+ dynamicFilters.value = newVal.filters.map((f) => {
221
+ if (f.type === 'select') {
222
+ return [] // Array vacío para selección múltiple
223
+ } else if (f.type === 'radio-button') {
224
+ return f.options?.[0]?.value ?? null
225
+ } else if (f.type === 'check') {
226
+ return f.value ?? false
227
+ }
228
+ return null
229
+ })
230
+ })
231
+
232
+ // Funciones para manejo de selección múltiple
233
+ function isOptionSelected(filterIdx, optionValue) {
234
+ const selectedValues = dynamicFilters.value[filterIdx]
235
+ return Array.isArray(selectedValues) && selectedValues.includes(optionValue)
236
+ }
237
+
238
+ function toggleMultiSelectOption(filterIdx, optionValue) {
239
+ if (!Array.isArray(dynamicFilters.value[filterIdx])) {
240
+ dynamicFilters.value[filterIdx] = []
241
+ }
242
+
243
+ const selectedValues = dynamicFilters.value[filterIdx]
244
+ const index = selectedValues.indexOf(optionValue)
245
+
246
+ if (index > -1) {
247
+ selectedValues.splice(index, 1)
248
+ } else {
249
+ selectedValues.push(optionValue)
250
+ }
251
+
252
+ emitChange()
253
+ }
254
+
255
+ function getMultiSelectLabel(filterIdx) {
256
+ const selectedValues = dynamicFilters.value[filterIdx]
257
+ if (!Array.isArray(selectedValues) || selectedValues.length === 0) {
258
+ return 'Selecciona opciones'
259
+ }
260
+
261
+ const filter = selectedSecond.value?.filters?.[filterIdx]
262
+ if (!filter || !filter.options) return 'Opciones seleccionadas'
263
+
264
+ const selectedLabels = selectedValues
265
+ .map((value) => filter.options.find((opt) => opt.value === value)?.label)
266
+ .filter(Boolean)
267
+
268
+ if (selectedLabels.length === 1) {
269
+ return selectedLabels[0]
270
+ } else if (selectedLabels.length <= 3) {
271
+ return selectedLabels.join(', ')
272
+ } else {
273
+ return `${selectedLabels.length} opciones seleccionadas`
274
+ }
275
+ }
276
+
277
+ function areAllSelected(filterIdx) {
278
+ const selectedValues = dynamicFilters.value[filterIdx]
279
+ const filter = selectedSecond.value?.filters?.[filterIdx]
280
+ if (!filter || !filter.options || !Array.isArray(selectedValues)) return false
281
+
282
+ return selectedValues.length === filter.options.length
283
+ }
284
+
285
+ function isIndeterminate(filterIdx) {
286
+ const selectedValues = dynamicFilters.value[filterIdx]
287
+ const filter = selectedSecond.value?.filters?.[filterIdx]
288
+ if (!filter || !filter.options || !Array.isArray(selectedValues)) return false
289
+
290
+ return selectedValues.length > 0 && selectedValues.length < filter.options.length
291
+ }
292
+
293
+ function toggleSelectAll(filterIdx) {
294
+ const filter = selectedSecond.value?.filters?.[filterIdx]
295
+ if (!filter || !filter.options) return
296
+
297
+ const allValues = filter.options.map((opt) => opt.value)
298
+
299
+ // Asegurar que dynamicFilters[filterIdx] sea un array
300
+ if (!Array.isArray(dynamicFilters.value[filterIdx])) {
301
+ dynamicFilters.value[filterIdx] = []
302
+ }
303
+
304
+ if (areAllSelected(filterIdx)) {
305
+ // Si todas están seleccionadas, deseleccionar todas
306
+ dynamicFilters.value[filterIdx] = []
307
+ } else {
308
+ // Si no todas están seleccionadas, seleccionar todas
309
+ dynamicFilters.value[filterIdx] = [...allValues]
310
+ }
311
+
312
+ // Mantener el menú abierto
313
+ dynamicSelectOpen.value = filterIdx
314
+
315
+ emitChange()
316
+ }
317
+
318
+ const selectedTeam = ref('ambos')
319
+ const showFieldMenu = ref(false)
320
+ const showTypeMenu = ref(false)
321
+ const dynamicSelectOpen = ref(null)
322
+
323
+ function getFieldLabel() {
324
+ if (!selectedMain.value) return 'Tipo de vista'
325
+ return selectedMain.value.name
326
+ }
327
+ function getTypeLabel() {
328
+ if (!selectedSecond.value) return 'Selecciona una opción'
329
+ return selectedSecond.value.name || selectedSecond.value.title
330
+ }
331
+
332
+ function toggleFieldMenu() {
333
+ showFieldMenu.value = !showFieldMenu.value
334
+ if (showFieldMenu.value) {
335
+ showTypeMenu.value = false
336
+ }
337
+ }
338
+ function toggleTypeMenu() {
339
+ if (!selectedMain.value) return // No abrir si no hay selección
340
+ showTypeMenu.value = !showTypeMenu.value
341
+ if (showTypeMenu.value) {
342
+ showFieldMenu.value = false
343
+ }
344
+ }
345
+ function selectMain(index) {
346
+ selectedMainIndex.value = index
347
+ showFieldMenu.value = false
348
+ // Limpiar selección dependiente
349
+ selectedSecondId.value = null
350
+ emitChange()
351
+ }
352
+ function selectSecond(option) {
353
+ selectedSecondId.value = option.id
354
+ showTypeMenu.value = false
355
+ emitChange()
356
+ }
357
+ function toggleDynamicSelect(idx) {
358
+ dynamicSelectOpen.value = dynamicSelectOpen.value === idx ? null : idx
359
+ }
360
+ function handleClickOutside(event) {
361
+ if (!event.target.closest('.custom-select')) {
362
+ showFieldMenu.value = false
363
+ showTypeMenu.value = false
364
+ }
365
+ }
366
+ function emitChange() {
367
+ emit('filters-changed', {
368
+ main: selectedMain.value,
369
+ view: selectedSecond.value,
370
+ team: selectedTeam.value,
371
+ dynamicFilters: dynamicFilters.value,
372
+ })
373
+ }
374
+ function resetFilters() {
375
+ selectedMainIndex.value = null
376
+ selectedSecondId.value = null
377
+ selectedTeam.value = 'ambos'
378
+ dynamicFilters.value = []
379
+ showFieldMenu.value = false
380
+ showTypeMenu.value = false
381
+ dynamicSelectOpen.value = null
382
+ }
383
+ function emitClose() {
384
+ resetFilters()
385
+ emit('close')
386
+ }
387
+
388
+ onMounted(async () => {
389
+ document.addEventListener('click', handleClickOutside)
390
+ try {
391
+ const response = await fetch(
392
+ 'https://m9qip57rsh.execute-api.us-east-2.amazonaws.com/prod/views',
393
+ {
394
+ headers: {
395
+ Authorization: `${props.token}`,
396
+ },
397
+ },
398
+ )
399
+ const data = await response.json()
400
+ apiData.value = data.data || []
401
+ } catch (error) {
402
+ console.error('Error al obtener datos:', error)
403
+ }
404
+ })
405
+ onUnmounted(() => {
406
+ document.removeEventListener('click', handleClickOutside)
407
+ })
408
+ </script>
409
+
410
+ <style scoped>
411
+ .floating-bar {
412
+ position: absolute;
413
+ top: 67px;
414
+ left: 50%;
415
+ background: #2a3843;
416
+ border-radius: 8px;
417
+ display: flex;
418
+ align-items: center;
419
+ padding: 8px 32px;
420
+ z-index: 100;
421
+ gap: 12px;
422
+ height: 62px;
423
+ width: auto;
424
+ min-width: 600px;
425
+ max-width: 100vw;
426
+ box-sizing: border-box;
427
+ transform: translateX(-50%);
428
+ }
429
+ .dynamic-select,
430
+ .radio-group,
431
+ .check-group {
432
+ display: inline-flex;
433
+ margin-bottom: 0 !important;
434
+ }
435
+
436
+ .tab {
437
+ background: none;
438
+ color: #fafafb;
439
+ border: none;
440
+ border-radius: 6px 6px 0 0;
441
+ padding: 8px 18px;
442
+ font-weight: bold;
443
+ font-size: 16px;
444
+ margin-right: 8px;
445
+ }
446
+
447
+ .tab.active {
448
+ color: #fff;
449
+ }
450
+
451
+ .custom-select {
452
+ position: relative;
453
+ cursor: pointer;
454
+ }
455
+
456
+ .select-display {
457
+ background: #3f4c56;
458
+ color: #fafafb;
459
+ border: 1px solid #e9ecee;
460
+ border-radius: 6px;
461
+ padding: 0px 32px 0px 12px;
462
+ font-size: 15px;
463
+ display: flex;
464
+ align-items: center;
465
+ justify-content: space-between;
466
+ min-width: 140px;
467
+ height: 40px;
468
+ transition: border-color 0.2s;
469
+ }
470
+ .select-icon {
471
+ display: flex;
472
+ align-items: center;
473
+ margin-right: 8px;
474
+ }
475
+
476
+ .select-display:hover {
477
+ border-color: #cbee6b;
478
+ }
479
+
480
+ .arrow {
481
+ display: flex;
482
+ align-items: center;
483
+ transition: transform 0.2s;
484
+ }
485
+ .arrow svg {
486
+ display: block;
487
+ }
488
+ .arrow.open {
489
+ transform: rotate(180deg);
490
+ }
491
+
492
+ .custom-select:hover .arrow {
493
+ transform: rotate(180deg);
494
+ }
495
+
496
+ .dropdown-menu {
497
+ position: absolute;
498
+ top: 100%;
499
+ left: 0;
500
+ background: #2a3843;
501
+ border: 1px solid #3d4a54;
502
+ border-radius: 8px;
503
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
504
+ padding: 8px;
505
+ margin-top: 4px;
506
+ min-width: 200px;
507
+ z-index: 1000;
508
+ }
509
+
510
+ .option-card {
511
+ display: flex;
512
+ align-items: center;
513
+ padding: 12px;
514
+ border-radius: 6px;
515
+ cursor: pointer;
516
+ transition: background-color 0.2s;
517
+ margin-bottom: 4px;
518
+ }
519
+
520
+ .option-card:last-child {
521
+ margin-bottom: 0;
522
+ }
523
+
524
+ .option-card:hover {
525
+ background: #3d4a54;
526
+ }
527
+
528
+ .option-card.active {
529
+ background: #cbee6b;
530
+ color: #1a2a36;
531
+ }
532
+
533
+ .option-icon {
534
+ font-size: 20px;
535
+ margin-right: 12px;
536
+ width: 24px;
537
+ text-align: center;
538
+ }
539
+
540
+ .option-content {
541
+ flex: 1;
542
+ }
543
+
544
+ .option-title {
545
+ font-weight: 600;
546
+ font-size: 14px;
547
+ margin-bottom: 2px;
548
+ }
549
+
550
+ .option-description {
551
+ font-size: 12px;
552
+ opacity: 0.8;
553
+ }
554
+
555
+ .radio-group {
556
+ display: flex;
557
+ align-items: center;
558
+ gap: 10px;
559
+ margin-left: 16px;
560
+ }
561
+
562
+ .radio-group label {
563
+ color: #fafafb;
564
+ font-size: 15px;
565
+ display: flex;
566
+ align-items: center;
567
+ gap: 4px;
568
+ }
569
+
570
+ input[type='radio'] {
571
+ appearance: none;
572
+ -webkit-appearance: none;
573
+ background: #2a3843;
574
+ border: 2px solid #e9ecee;
575
+ width: 20px;
576
+ height: 20px;
577
+ border-radius: 50%;
578
+ margin-right: 6px;
579
+ margin-top: 0px;
580
+ position: relative;
581
+ cursor: pointer;
582
+ outline: none;
583
+ transition: border-color 0.2s;
584
+ }
585
+ input[type='radio']:checked::before {
586
+ content: '';
587
+ display: block;
588
+ width: 12px;
589
+ height: 12px;
590
+ background: #cbee6b;
591
+ border-radius: 50%;
592
+ position: absolute;
593
+ top: 2px;
594
+ left: 2px;
595
+ }
596
+
597
+ .check-group {
598
+ display: flex;
599
+ align-items: center;
600
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
601
+ font-size: 14px;
602
+ color: #fff;
603
+ background: none;
604
+ border: none;
605
+ border-radius: 0;
606
+ padding: 0 8px;
607
+ min-height: 40px;
608
+ margin-right: 8px;
609
+ }
610
+ .check-group input[type='checkbox'] {
611
+ accent-color: #cbee6b;
612
+ margin-right: 8px;
613
+ width: 18px;
614
+ height: 18px;
615
+ }
616
+
617
+ .close-btn {
618
+ background: none;
619
+ border: none;
620
+ color: #fafafb;
621
+ font-size: 22px;
622
+ margin-left: auto;
623
+ cursor: pointer;
624
+ transition: color 0.2s;
625
+ }
626
+
627
+ .close-btn:hover {
628
+ color: #cbee6b;
629
+ }
630
+
631
+ .select-label {
632
+ flex-grow: 0;
633
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
634
+ font-size: 14px;
635
+ font-weight: 500;
636
+ font-stretch: normal;
637
+ font-style: normal;
638
+ margin-left: -10px;
639
+ line-height: normal;
640
+ letter-spacing: normal;
641
+ text-align: left;
642
+ color: #d9d9d9;
643
+ }
644
+ .option-title {
645
+ flex-grow: 0;
646
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
647
+ font-size: 14px;
648
+ font-weight: 500;
649
+ font-stretch: normal;
650
+ font-style: normal;
651
+ line-height: normal;
652
+ letter-spacing: normal;
653
+ text-align: left;
654
+ color: #fff;
655
+ }
656
+ .select-placeholder {
657
+ font-family: 'Poppins-Regular', 'Poppins', Arial, sans-serif !important;
658
+ font-size: 14px;
659
+ margin-left: -8px;
660
+ color: #d9d9d9;
661
+ }
662
+ .option-img {
663
+ width: 24px;
664
+ height: 24px;
665
+ object-fit: cover;
666
+ border-radius: 4px;
667
+ background: #222;
668
+ }
669
+ .custom-radio-group {
670
+ display: flex;
671
+ flex-direction: row;
672
+ align-items: center;
673
+ border-radius: 6px;
674
+ padding: 0px 16px;
675
+ min-height: 40px;
676
+ justify-content: center;
677
+ margin-right: 8px;
678
+ }
679
+ .radio-label {
680
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
681
+ font-size: 13px;
682
+ color: #d9d9d9;
683
+ margin-bottom: 0;
684
+ margin-right: 8px;
685
+ }
686
+ .radio-options {
687
+ display: flex;
688
+ flex-direction: row;
689
+ gap: 8px;
690
+ flex-wrap: nowrap;
691
+ }
692
+ .radio-option-card {
693
+ display: flex;
694
+ align-items: center;
695
+ background: transparent;
696
+ border: none;
697
+ border-radius: 4px;
698
+ padding: 6px 10px;
699
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
700
+ font-size: 14px;
701
+ color: #fff;
702
+ cursor: pointer;
703
+ transition:
704
+ background 0.2s,
705
+ color 0.2s;
706
+ white-space: nowrap;
707
+ min-width: 0;
708
+ max-width: 120px;
709
+ justify-content: center;
710
+ }
711
+
712
+ .filter-separator {
713
+ width: 1px;
714
+ height: 32px;
715
+ border-left: 2px dashed #d9d9d9;
716
+ margin: 0 16px;
717
+ align-self: center;
718
+ }
719
+ .filter-inline {
720
+ display: inline-flex;
721
+ align-items: center;
722
+ vertical-align: middle;
723
+ margin-bottom: 0 !important;
724
+ min-width: 140px;
725
+ }
726
+ .select-label,
727
+ .option-title,
728
+ .radio-label,
729
+ .radio-option-card span,
730
+ .check-group label {
731
+ white-space: nowrap;
732
+ overflow: hidden;
733
+ text-overflow: ellipsis;
734
+ max-width: 180px;
735
+ display: inline-block;
736
+ vertical-align: middle;
737
+ }
738
+ .dynamic-filters {
739
+ display: flex;
740
+ align-items: center;
741
+ transition:
742
+ min-width 0.2s,
743
+ padding 0.2s;
744
+ }
745
+ .dynamic-filters-wide {
746
+ min-width: 400px;
747
+ padding-right: 24px;
748
+ padding-left: 24px;
749
+ }
750
+ .dynamic-filters-wide > .filter-inline:not(:last-child) {
751
+ margin-right: 16px;
752
+ }
753
+ .type-menu-grid {
754
+ min-width: 360px;
755
+ max-width: 420px;
756
+ }
757
+ .option-grid {
758
+ display: grid;
759
+ grid-template-columns: repeat(3, 1fr);
760
+ gap: 16px;
761
+ padding: 8px 0;
762
+ }
763
+ .option-card-grid {
764
+ display: flex;
765
+ flex-direction: column;
766
+ align-items: center;
767
+ justify-content: flex-start;
768
+ background: #3f4c56;
769
+ border: 2px solid transparent;
770
+ border-radius: 10px;
771
+ padding: 12px 8px 10px 8px;
772
+ cursor: pointer;
773
+ transition:
774
+ border-color 0.2s,
775
+ background 0.2s;
776
+ min-width: 90px;
777
+ min-height: 110px;
778
+ box-sizing: border-box;
779
+ }
780
+ .option-card-grid.active,
781
+ .option-card-grid:hover {
782
+ border-color: #cbee6b;
783
+ background: #22334a;
784
+ }
785
+ .option-grid-title {
786
+ font-size: 11px;
787
+ color: #d9d9d9;
788
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
789
+ margin-bottom: 8px;
790
+ text-align: center;
791
+ white-space: nowrap;
792
+ overflow: hidden;
793
+ text-overflow: ellipsis;
794
+ width: 100%;
795
+ }
796
+ .option-grid-icon {
797
+ display: flex;
798
+ align-items: center;
799
+ justify-content: center;
800
+ flex: 1;
801
+ }
802
+ .option-img-grid {
803
+ width: 40px;
804
+ height: 40px;
805
+ object-fit: cover;
806
+ border-radius: 6px;
807
+ background: #222;
808
+ }
809
+
810
+ /* Estilos para selector múltiple */
811
+ .multi-select-menu {
812
+ min-width: 280px;
813
+ max-width: 400px;
814
+ }
815
+
816
+ .multi-select-header {
817
+ display: flex;
818
+ justify-content: space-between;
819
+ align-items: center;
820
+ padding: 8px 12px;
821
+ border-bottom: 1px solid #3d4a54;
822
+ margin-bottom: 8px;
823
+ }
824
+
825
+ .select-all-checkbox {
826
+ display: flex;
827
+ align-items: center;
828
+ cursor: pointer;
829
+ gap: 8px;
830
+ }
831
+
832
+ .select-all-checkbox input[type='checkbox'] {
833
+ appearance: none;
834
+ -webkit-appearance: none;
835
+ width: 18px;
836
+ height: 18px;
837
+ border: 2px solid #e9ecee;
838
+ border-radius: 3px;
839
+ background: #2a3843;
840
+ cursor: pointer;
841
+ position: relative;
842
+ transition: all 0.2s;
843
+ }
844
+
845
+ .select-all-checkbox input[type='checkbox']:checked {
846
+ background: #cbee6b;
847
+ border-color: #cbee6b;
848
+ }
849
+
850
+ .select-all-checkbox input[type='checkbox']:checked::before {
851
+ content: '✓';
852
+ position: absolute;
853
+ top: 50%;
854
+ left: 50%;
855
+ transform: translate(-50%, -50%);
856
+ color: #1a2a36;
857
+ font-size: 12px;
858
+ font-weight: bold;
859
+ }
860
+
861
+ .select-all-checkbox input[type='checkbox']:indeterminate::before {
862
+ content: '−';
863
+ position: absolute;
864
+ top: 50%;
865
+ left: 50%;
866
+ transform: translate(-50%, -50%);
867
+ color: #1a2a36;
868
+ font-size: 14px;
869
+ font-weight: bold;
870
+ }
871
+
872
+ .select-all-checkbox input[type='checkbox']:hover {
873
+ border-color: #cbee6b;
874
+ }
875
+
876
+ .select-all-label {
877
+ font-family: 'Poppins-Medium', 'Poppins', Arial, sans-serif;
878
+ font-size: 14px;
879
+ color: #d9d9d9;
880
+ font-weight: 500;
881
+ }
882
+
883
+ .multi-select-option {
884
+ padding: 8px 12px;
885
+ margin-bottom: 2px;
886
+ }
887
+
888
+ .multi-select-option:hover {
889
+ background: #3d4a54;
890
+ }
891
+
892
+ .multi-select-option.active {
893
+ background: rgba(203, 238, 107, 0.1);
894
+ border: 1px solid #cbee6b;
895
+ }
896
+
897
+ .option-checkbox {
898
+ display: flex;
899
+ align-items: center;
900
+ margin-right: 8px;
901
+ }
902
+
903
+ .option-checkbox input[type='checkbox'] {
904
+ appearance: none;
905
+ -webkit-appearance: none;
906
+ width: 18px;
907
+ height: 18px;
908
+ border: 2px solid #e9ecee;
909
+ border-radius: 3px;
910
+ background: #2a3843;
911
+ cursor: pointer;
912
+ position: relative;
913
+ transition: all 0.2s;
914
+ }
915
+
916
+ .option-checkbox input[type='checkbox']:checked {
917
+ background: #cbee6b;
918
+ border-color: #cbee6b;
919
+ }
920
+
921
+ .option-checkbox input[type='checkbox']:checked::before {
922
+ content: '✓';
923
+ position: absolute;
924
+ top: 50%;
925
+ left: 50%;
926
+ transform: translate(-50%, -50%);
927
+ color: #1a2a36;
928
+ font-size: 12px;
929
+ font-weight: bold;
930
+ }
931
+
932
+ .option-checkbox input[type='checkbox']:hover {
933
+ border-color: #cbee6b;
934
+ }
935
+ </style>