@golstats/gsc-reports 1.0.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.
Files changed (135) hide show
  1. package/README.md +2 -0
  2. package/dist/FilterConditions-55d68355-C5NIJbY6-xW04mYhu-DRN4eEO4.js +191 -0
  3. package/dist/FilterField-59a73e38-DO8nYLqs-Da7H_Fvj-ZuLwDGOM.js +21 -0
  4. package/dist/FilterSubcategories-a9b32cc9-DlkHni1L-BXid_sIn-BvoU3OqZ.js +42 -0
  5. package/dist/css/fonts.css +83 -0
  6. package/dist/fonts/BebasNeue-Bold.otf +0 -0
  7. package/dist/fonts/BebasNeue-Bold.ttf +0 -0
  8. package/dist/fonts/BebasNeue-Bold.woff2 +0 -0
  9. package/dist/fonts/BebasNeue-Book.otf +0 -0
  10. package/dist/fonts/BebasNeue-Book.ttf +0 -0
  11. package/dist/fonts/BebasNeue-Book.woff2 +0 -0
  12. package/dist/fonts/BebasNeue-Light.otf +0 -0
  13. package/dist/fonts/BebasNeue-Light.ttf +0 -0
  14. package/dist/fonts/BebasNeue-Light.woff2 +0 -0
  15. package/dist/fonts/BebasNeue-Regular.otf +0 -0
  16. package/dist/fonts/BebasNeue-Regular.ttf +0 -0
  17. package/dist/fonts/BebasNeue-Regular.woff2 +0 -0
  18. package/dist/fonts/BebasNeue-Thin.otf +0 -0
  19. package/dist/fonts/BebasNeue-Thin.ttf +0 -0
  20. package/dist/fonts/BebasNeue-Thin.woff2 +0 -0
  21. package/dist/fonts/Montserrat-Black.otf +0 -0
  22. package/dist/fonts/Montserrat-BlackItalic.otf +0 -0
  23. package/dist/fonts/Montserrat-Bold.otf +0 -0
  24. package/dist/fonts/Montserrat-BoldItalic.otf +0 -0
  25. package/dist/fonts/Montserrat-ExtraBold.otf +0 -0
  26. package/dist/fonts/Montserrat-ExtraBoldItalic.otf +0 -0
  27. package/dist/fonts/Montserrat-ExtraLight.otf +0 -0
  28. package/dist/fonts/Montserrat-ExtraLightItalic.otf +0 -0
  29. package/dist/fonts/Montserrat-Italic.otf +0 -0
  30. package/dist/fonts/Montserrat-Light.otf +0 -0
  31. package/dist/fonts/Montserrat-LightItalic.otf +0 -0
  32. package/dist/fonts/Montserrat-Medium.otf +0 -0
  33. package/dist/fonts/Montserrat-MediumItalic.otf +0 -0
  34. package/dist/fonts/Montserrat-Regular.otf +0 -0
  35. package/dist/fonts/Montserrat-SemiBold.otf +0 -0
  36. package/dist/fonts/Montserrat-SemiBoldItalic.otf +0 -0
  37. package/dist/fonts/Montserrat-Thin.otf +0 -0
  38. package/dist/fonts/Montserrat-ThinItalic.otf +0 -0
  39. package/dist/fonts/Oswald-Bold.ttf +0 -0
  40. package/dist/fonts/Oswald-ExtraLight.ttf +0 -0
  41. package/dist/fonts/Oswald-Light.ttf +0 -0
  42. package/dist/fonts/Oswald-Medium.ttf +0 -0
  43. package/dist/fonts/Oswald-Regular.ttf +0 -0
  44. package/dist/fonts/Oswald-SemiBold.ttf +0 -0
  45. package/dist/fonts/Poppins-Black.otf +0 -0
  46. package/dist/fonts/Poppins-BlackItalic.otf +0 -0
  47. package/dist/fonts/Poppins-Bold.otf +0 -0
  48. package/dist/fonts/Poppins-BoldItalic.otf +0 -0
  49. package/dist/fonts/Poppins-ExtraBold.otf +0 -0
  50. package/dist/fonts/Poppins-ExtraBoldItalic.otf +0 -0
  51. package/dist/fonts/Poppins-ExtraLight.otf +0 -0
  52. package/dist/fonts/Poppins-ExtraLightItalic.otf +0 -0
  53. package/dist/fonts/Poppins-Italic.otf +0 -0
  54. package/dist/fonts/Poppins-Light.otf +0 -0
  55. package/dist/fonts/Poppins-LightItalic.otf +0 -0
  56. package/dist/fonts/Poppins-Medium.otf +0 -0
  57. package/dist/fonts/Poppins-MediumItalic.otf +0 -0
  58. package/dist/fonts/Poppins-Regular.otf +0 -0
  59. package/dist/fonts/Poppins-SemiBold.otf +0 -0
  60. package/dist/fonts/Poppins-SemiBoldItalic.otf +0 -0
  61. package/dist/fonts/Poppins-Thin.otf +0 -0
  62. package/dist/fonts/Poppins-ThinItalic.otf +0 -0
  63. package/dist/gsc-reports.css +1 -0
  64. package/dist/gsc-reports.es.js +4 -0
  65. package/dist/gsc-reports.umd.js +4079 -0
  66. package/dist/icons/icn-close.svg +5 -0
  67. package/dist/icons/icn-delete-delete.svg +3 -0
  68. package/dist/icons/icn-delete.svg +3 -0
  69. package/dist/icons/icn-download.svg +4 -0
  70. package/dist/icons/icn-duplicate-white.svg +11 -0
  71. package/dist/icons/icn-duplicate.svg +11 -0
  72. package/dist/icons/icn-edit.svg +11 -0
  73. package/dist/icons/icn-editar-withe.svg +3 -0
  74. package/dist/icons/icn-rename-white.svg +3 -0
  75. package/dist/icons/icn-rename.svg +3 -0
  76. package/dist/icons/icn-report-white.svg +11 -0
  77. package/dist/icons/icn-report.svg +11 -0
  78. package/dist/images/background-postmatch-template-01.jpg +0 -0
  79. package/dist/images/background-postmatch-template-02.jpg +0 -0
  80. package/dist/images/canchaRPH.svg +30 -0
  81. package/dist/index-B1R4W2EC.js +128151 -0
  82. package/dist/reports/background-postmatch-template-01@2x.jpg +0 -0
  83. package/dist/reports/background-postmatch-template-02@2x.jpg +0 -0
  84. package/dist/reports/thumbnail-portada-01@2x.png +0 -0
  85. package/dist/reports/thumbnail-portada-02@2x.png +0 -0
  86. package/dist/reports/thumbnail-portada-03@2x.png +0 -0
  87. package/dist/reports/thumbnail-portada-04@2x.png +0 -0
  88. package/dist/reports/thumbnail-portada-prmatch-01@2x.png +0 -0
  89. package/dist/reports/thumbnail-portada-prmatch-02@2x.png +0 -0
  90. package/dist/reports/thumbnail-portada-prmatch-03@2x.png +0 -0
  91. package/dist/reports/thumbnail-portada-prmatch-04@2x.png +0 -0
  92. package/dist/thumb1.png +0 -0
  93. package/dist/thumbnail-portada-01.jpg +0 -0
  94. package/dist/thumbnail-portada-02.jpg +0 -0
  95. package/dist/thumbnail-portada-03.jpg +0 -0
  96. package/dist/thumbnail-portada-04.jpg +0 -0
  97. package/dist/thumbnail-portada-prmatch-01.jpg +0 -0
  98. package/dist/thumbnail-portada-prmatch-02.jpg +0 -0
  99. package/dist/thumbnail-portada-prmatch-03.jpg +0 -0
  100. package/dist/thumbnail-portada-prmatch-04.jpg +0 -0
  101. package/package.json +70 -0
  102. package/src/App.vue +28 -0
  103. package/src/components/ReportsSection.vue +764 -0
  104. package/src/components/TemplatesSection.vue +911 -0
  105. package/src/components/elementsTemplates/ModalCreateTemplate.vue +407 -0
  106. package/src/components/elementsTemplates/ModalDeleteReport.vue +231 -0
  107. package/src/components/elementsTemplates/ModalDeleteTemplate.vue +234 -0
  108. package/src/components/elementsTemplates/ModalGenerarReporte.vue +1151 -0
  109. package/src/components/elementsTemplates/ModalRenameReporte.vue +315 -0
  110. package/src/components/elementsTemplates/ModalRenameTemplate.vue +320 -0
  111. package/src/components/elementsTemplates/ModalSoloEscritorio.vue +83 -0
  112. package/src/components/elementsTemplates/ModalduplicateTemplate.vue +283 -0
  113. package/src/components/elementsTemplates/ReportItem.vue +458 -0
  114. package/src/components/elementsTemplates/TemplateItem.vue +360 -0
  115. package/src/components/elementsTemplates/TooltipReportOptions.vue +85 -0
  116. package/src/components/elementsTemplates/TooltipTemplateOptions.vue +141 -0
  117. package/src/components/filters.vue +935 -0
  118. package/src/components/gsc-reports.vue +452 -0
  119. package/src/components/template-report-maker/CoverPage.vue +636 -0
  120. package/src/components/template-report-maker/CoverSelector.vue +165 -0
  121. package/src/components/template-report-maker/ReportAndTemplateMaker.vue +675 -0
  122. package/src/components/template-report-maker/ReportView.vue +66 -0
  123. package/src/components/template-report-maker/TemplateReportPage.vue +398 -0
  124. package/src/components/thumbnails-reports/AnalisisPostMatchType1.vue +741 -0
  125. package/src/components/thumbnails-reports/AnalisisPostMatchType2.vue +743 -0
  126. package/src/components/thumbnails-reports/AnalisisPostMatchType3.vue +463 -0
  127. package/src/components/thumbnails-reports/AnalisisPostMatchType4.vue +462 -0
  128. package/src/components/thumbnails-reports/AnalisisPrematchType1.vue +164 -0
  129. package/src/components/thumbnails-reports/AnalisisPrematchType2.vue +163 -0
  130. package/src/components/thumbnails-reports/AnalisisPrematchType3.vue +173 -0
  131. package/src/components/thumbnails-reports/AnalisisPrematchType4.vue +173 -0
  132. package/src/index.js +4 -0
  133. package/src/main.js +4 -0
  134. package/src/types.d.ts +45 -0
  135. package/src/utils/dateUtils.js +52 -0
@@ -0,0 +1,764 @@
1
+ <template>
2
+ <div class="reports-section" ref="reportsSectionRef">
3
+ <div class="reports-header">
4
+ <span
5
+ >Reportes <span class="count">({{ filteredReports.length }})</span></span
6
+ >
7
+ <div class="sort-dropdown" ref="sortDropdownRef">
8
+ <button class="sort-button" @click="toggleSortDropdown">
9
+ {{ selectedSortOption || 'Ordenar' }}
10
+ <span class="dropdown-arrow" :class="{ rotated: showSortDropdown }"></span>
11
+ </button>
12
+ <div class="sort-options" v-if="showSortDropdown">
13
+ <div
14
+ v-for="option in sortOptions"
15
+ :key="option.value"
16
+ class="sort-option"
17
+ :class="{ selected: selectedSortOption === option.value }"
18
+ @click="selectedSortOption !== option.value && selectSortOption(option.value)"
19
+ >
20
+ <span>{{ option.label }}</span>
21
+ <span v-if="selectedSortOption === option.value" class="checkmark">
22
+ <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
23
+ <path
24
+ d="M5 10.5L9 14.5L15 7.5"
25
+ stroke="#cbee6b"
26
+ stroke-width="2"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ />
30
+ </svg>
31
+ </span>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <!-- Estado de carga -->
37
+ <div v-if="loading" class="loading-state">
38
+ <div class="loading-spinner"></div>
39
+ <p>Cargando reportes...</p>
40
+ </div>
41
+
42
+ <!-- Estado de error -->
43
+ <div v-else-if="error" class="error-state">
44
+ <p>Error al cargar reportes: {{ error }}</p>
45
+ <button @click="loadReports" class="retry-button">Reintentar</button>
46
+ </div>
47
+
48
+ <!-- Lista de reportes -->
49
+ <div v-else class="reports-grid" :class="gridClass" ref="reportsGridRef">
50
+ <ReportItem
51
+ v-for="report in filteredReports"
52
+ :key="report.id"
53
+ :report="report"
54
+ @open-report="onOpenReport"
55
+ @renombrar="onRenombrarReporte"
56
+ @eliminar="onEliminarReporte"
57
+ />
58
+ </div>
59
+
60
+ <!-- Estado vacío -->
61
+ <div v-if="!loading && !error && reports.length === 0" class="empty-reports">
62
+ No tienes reportes aún.
63
+ </div>
64
+
65
+ <!-- Estado de búsqueda sin resultados -->
66
+ <div
67
+ v-if="!loading && !error && reports.length > 0 && filteredReports.length === 0"
68
+ class="empty-reports"
69
+ >
70
+ No se encontraron reportes que coincidan con tu búsqueda.
71
+ </div>
72
+
73
+ <!-- Modal para renombrar reporte -->
74
+ <ModalRenameReporte
75
+ v-if="showModalRenameReporte"
76
+ :initialName="renameReporteName"
77
+ :templateType="renameReporteTemplateType"
78
+ :token="token"
79
+ :userId="userId"
80
+ :reportId="renameReporteId"
81
+ @close="showModalRenameReporte = false"
82
+ @rename="onAcceptRenameReporte"
83
+ @refresh="loadReports"
84
+ />
85
+
86
+ <!-- Modal para eliminar reporte -->
87
+ <ModalDeleteReport
88
+ v-if="showModalDeleteReporte"
89
+ :report-id="deleteReportId"
90
+ :user-id="userId"
91
+ :token="token"
92
+ @close="showModalDeleteReporte = false"
93
+ @report-deleted="onReportDeleted"
94
+ />
95
+ </div>
96
+ </template>
97
+
98
+ <script setup>
99
+ import { ref, onMounted, onBeforeUnmount, computed, nextTick } from 'vue'
100
+ import axios from 'axios'
101
+ import ReportItem from './elementsTemplates/ReportItem.vue'
102
+ import ModalRenameReporte from './elementsTemplates/ModalRenameReporte.vue'
103
+ import ModalDeleteReport from './elementsTemplates/ModalDeleteReport.vue'
104
+
105
+ // Definir las props
106
+ const props = defineProps({
107
+ userId: {
108
+ type: Number,
109
+ required: true,
110
+ },
111
+ team: {
112
+ type: Number,
113
+ required: true,
114
+ },
115
+ token: {
116
+ type: String,
117
+ required: true,
118
+ },
119
+ searchQuery: {
120
+ type: String,
121
+ default: '',
122
+ },
123
+ })
124
+
125
+ const emit = defineEmits(['open-report'])
126
+
127
+ // Estado para los reportes
128
+ const reports = ref([])
129
+ const loading = ref(false)
130
+ const error = ref(null)
131
+
132
+ // Computed property para filtrar y ordenar reportes
133
+ const filteredReports = computed(() => {
134
+ let filtered = reports.value
135
+
136
+ // Aplicar filtro de búsqueda
137
+ if (props.searchQuery.trim()) {
138
+ const query = props.searchQuery.toLowerCase().trim()
139
+
140
+ filtered = reports.value.filter((report) => {
141
+ // Buscar por nombre del reporte
142
+ const reportName = report.report_name?.toLowerCase() || ''
143
+ if (reportName.includes(query)) {
144
+ return true
145
+ }
146
+
147
+ // Buscar por nombre del template
148
+ const templateName = report.name?.toLowerCase() || ''
149
+ if (templateName.includes(query)) {
150
+ return true
151
+ }
152
+
153
+ // Buscar por fecha de creación
154
+ if (report.created_at) {
155
+ const createdDate = new Date(report.created_at)
156
+ const dateString = createdDate
157
+ .toLocaleDateString('es-ES', {
158
+ year: 'numeric',
159
+ month: 'long',
160
+ day: 'numeric',
161
+ })
162
+ .toLowerCase()
163
+
164
+ if (dateString.includes(query)) {
165
+ return true
166
+ }
167
+ }
168
+
169
+ // Buscar por fecha del juego
170
+ if (report.game?.date_time_utc) {
171
+ const gameDate = new Date(report.game.date_time_utc)
172
+ const gameDateString = gameDate
173
+ .toLocaleDateString('es-ES', {
174
+ year: 'numeric',
175
+ month: 'long',
176
+ day: 'numeric',
177
+ })
178
+ .toLowerCase()
179
+
180
+ if (gameDateString.includes(query)) {
181
+ return true
182
+ }
183
+ }
184
+
185
+ // Buscar por nombre de la temporada
186
+ const seasonName = report.game?.season_name?.toLowerCase() || ''
187
+ if (seasonName.includes(query)) {
188
+ return true
189
+ }
190
+
191
+ // Buscar por nombre del torneo/liga
192
+ const tournamentName = report.tournament_name?.toLowerCase() || ''
193
+ if (tournamentName.includes(query)) {
194
+ return true
195
+ }
196
+
197
+ // Buscar por jornada
198
+ if (report.game?.matchday) {
199
+ const matchday = report.game.matchday.toString()
200
+ if (matchday.includes(query)) {
201
+ return true
202
+ }
203
+ }
204
+
205
+ return false
206
+ })
207
+ }
208
+
209
+ // Aplicar ordenamiento
210
+ if (selectedSortOption.value) {
211
+ filtered = [...filtered].sort((a, b) => {
212
+ if (selectedSortOption.value === 'A - Z') {
213
+ // Ordenar alfabéticamente por nombre del reporte
214
+ const nameA = (a.report_name || '').toLowerCase()
215
+ const nameB = (b.report_name || '').toLowerCase()
216
+ return nameA.localeCompare(nameB, 'es', { sensitivity: 'base' })
217
+ } else if (selectedSortOption.value === 'Más recientes') {
218
+ // Ordenar por fecha de creación (más recientes primero)
219
+ const dateA = new Date(a.created_at || 0)
220
+ const dateB = new Date(b.created_at || 0)
221
+ return dateB - dateA
222
+ } else if (selectedSortOption.value === 'Torneo') {
223
+ // Ordenar primero por nombre del torneo y luego por season_id
224
+ const torneoA = (a.tournament_name || '').toLowerCase()
225
+ const torneoB = (b.tournament_name || '').toLowerCase()
226
+
227
+ // Si los nombres de torneo son diferentes, ordenar por nombre
228
+ if (torneoA !== torneoB) {
229
+ return torneoA.localeCompare(torneoB, 'es', { sensitivity: 'base' })
230
+ }
231
+
232
+ // Si los nombres de torneo son iguales, ordenar por season_id (mayor a menor)
233
+ const seasonIdA = a.season_id || 0
234
+ const seasonIdB = b.season_id || 0
235
+ return seasonIdB - seasonIdA
236
+ } else if (selectedSortOption.value === 'Jornada') {
237
+ // Ordenar por matchday_id
238
+ const matchdayA = a.game?.matchday_id || 0
239
+ const matchdayB = b.game?.matchday_id || 0
240
+ return matchdayA - matchdayB
241
+ }
242
+ return 0
243
+ })
244
+ }
245
+
246
+ return filtered
247
+ })
248
+
249
+ const showSortDropdown = ref(false)
250
+ const selectedSortOption = ref('Más recientes') // Opción por defecto
251
+ const showModalRenameReporte = ref(false)
252
+ const renameReporteName = ref('')
253
+ const renameReporteId = ref('')
254
+ const renameReporteTemplateType = ref('')
255
+ const showModalDeleteReporte = ref(false)
256
+ const deleteReporteName = ref('')
257
+ const deleteReportId = ref('')
258
+ const sortDropdownRef = ref(null)
259
+ const reportsSectionRef = ref(null)
260
+ const reportsGridRef = ref(null)
261
+ const containerWidth = ref(0)
262
+
263
+ const sortOptions = [
264
+ { label: 'A - Z', value: 'A - Z' },
265
+ { label: 'Más recientes', value: 'Más recientes' },
266
+ { label: 'Torneo', value: 'Torneo' },
267
+ { label: 'Jornada', value: 'Jornada' },
268
+ ]
269
+
270
+ // Función para cargar los reportes
271
+ const loadReports = async () => {
272
+ loading.value = true
273
+ error.value = null
274
+
275
+ try {
276
+ const response = await axios.get(
277
+ `https://m9qip57rsh.execute-api.us-east-2.amazonaws.com/prod/users/${props.userId}/reports`,
278
+ {
279
+ headers: {
280
+ Authorization: `${props.token}`,
281
+ 'Content-Type': 'application/json',
282
+ },
283
+ },
284
+ )
285
+
286
+ // Verificar la estructura de la respuesta
287
+ if (response.data && Array.isArray(response.data)) {
288
+ reports.value = response.data
289
+ } else if (response.data && response.data.reports) {
290
+ reports.value = response.data.reports
291
+ } else if (response.data && response.data.data) {
292
+ reports.value = response.data.data
293
+ } else {
294
+ reports.value = []
295
+ }
296
+ } catch (err) {
297
+ console.error('Error cargando reportes:', err)
298
+ console.error('Error response:', err.response)
299
+ error.value = err.message || 'Error al cargar los reportes'
300
+ reports.value = [] // Asegurar que esté vacío en caso de error
301
+ } finally {
302
+ loading.value = false
303
+ }
304
+ }
305
+
306
+ // Computed property para determinar las clases del grid basado en el ancho del contenedor
307
+ const gridClass = computed(() => {
308
+ if (!containerWidth.value) return 'grid-1-column'
309
+
310
+ // Calculamos cuántos items caben en el ancho disponible
311
+ const itemWidth = 340 // Ancho base del ReportItem
312
+ const gap = 20 // Gap entre items
313
+ const padding = 40 // Padding del contenedor (20px * 2)
314
+
315
+ // Ancho disponible para el grid
316
+ const availableWidth = containerWidth.value - padding
317
+
318
+ // Calculamos cuántos items caben considerando el gap
319
+ // Para n items necesitamos: n * itemWidth + (n-1) * gap
320
+ // Despejando: n = (availableWidth + gap) / (itemWidth + gap)
321
+ const itemsPerRow = Math.floor((availableWidth + gap) / (itemWidth + gap))
322
+
323
+ // Aseguramos que al menos tengamos 1 columna
324
+ const finalItemsPerRow = Math.max(1, itemsPerRow)
325
+
326
+ if (finalItemsPerRow >= 4) {
327
+ return 'grid-4-columns'
328
+ } else if (finalItemsPerRow >= 3) {
329
+ return 'grid-3-columns'
330
+ } else if (finalItemsPerRow >= 2) {
331
+ return 'grid-2-columns'
332
+ } else {
333
+ return 'grid-1-column'
334
+ }
335
+ })
336
+
337
+ const toggleSortDropdown = () => {
338
+ showSortDropdown.value = !showSortDropdown.value
339
+ }
340
+
341
+ const selectSortOption = (option) => {
342
+ console.log('Ordenando por:', option)
343
+ selectedSortOption.value = option
344
+ showSortDropdown.value = false
345
+ }
346
+
347
+ function onOpenReport(report) {
348
+ console.log('reports section onOpenReport', report)
349
+ // Emitir un evento o manejar la lógica para abrir el reporte
350
+ // Por ejemplo, podrías redirigir a una página de reporte específica
351
+ // router.push(`/reports/${report.id}`)
352
+ emit('open-report', report)
353
+ }
354
+
355
+ const onRenombrarReporte = (nombre, reportId) => {
356
+ renameReporteName.value = nombre
357
+ renameReporteId.value = reportId
358
+ // Buscar el reporte para obtener el template_type
359
+ const report = reports.value.find((r) => r.id === reportId)
360
+ renameReporteTemplateType.value = report ? report.template_type : ''
361
+ showModalRenameReporte.value = true
362
+ }
363
+
364
+ const onAcceptRenameReporte = (nuevoNombre) => {
365
+ console.log('Renombrar reporte de', renameReporteName.value, 'a', nuevoNombre)
366
+ showModalRenameReporte.value = false
367
+
368
+ // Actualizar el nombre del reporte en la lista local
369
+ const reportIndex = reports.value.findIndex((r) => r.id === renameReporteId.value)
370
+ if (reportIndex !== -1) {
371
+ reports.value[reportIndex].report_name = nuevoNombre
372
+ }
373
+ }
374
+
375
+ const onEliminarReporte = (report) => {
376
+ deleteReporteName.value = report.report_name || 'Reporte'
377
+ deleteReportId.value = report.id
378
+ showModalDeleteReporte.value = true
379
+ }
380
+
381
+ const onReportDeleted = (reportId) => {
382
+ showModalDeleteReporte.value = false
383
+
384
+ // Remover el reporte eliminado de la lista
385
+ reports.value = reports.value.filter((report) => report.id !== reportId)
386
+ }
387
+
388
+ const handleClickOutside = (event) => {
389
+ if (
390
+ showSortDropdown.value &&
391
+ sortDropdownRef.value &&
392
+ !sortDropdownRef.value.contains(event.target)
393
+ ) {
394
+ showSortDropdown.value = false
395
+ }
396
+ }
397
+
398
+ // Función para actualizar el ancho del contenedor
399
+ const updateContainerWidth = () => {
400
+ if (reportsSectionRef.value) {
401
+ containerWidth.value = reportsSectionRef.value.offsetWidth
402
+ }
403
+ }
404
+
405
+ // Resize observer para detectar cambios en el tamaño del contenedor
406
+ let resizeObserver = null
407
+
408
+ onMounted(async () => {
409
+ document.addEventListener('mousedown', handleClickOutside)
410
+
411
+ // Cargar los reportes al montar el componente
412
+ await loadReports()
413
+
414
+ // Esperar a que el DOM esté completamente renderizado
415
+ await nextTick()
416
+
417
+ // Inicializar el ancho del contenedor
418
+ updateContainerWidth()
419
+
420
+ // Configurar el ResizeObserver para detectar cambios en el tamaño
421
+ if (window.ResizeObserver) {
422
+ resizeObserver = new ResizeObserver(() => {
423
+ updateContainerWidth()
424
+ })
425
+
426
+ if (reportsSectionRef.value) {
427
+ resizeObserver.observe(reportsSectionRef.value)
428
+ }
429
+ } else {
430
+ // Fallback para navegadores que no soportan ResizeObserver
431
+ window.addEventListener('resize', updateContainerWidth)
432
+ }
433
+ })
434
+
435
+ onBeforeUnmount(() => {
436
+ document.removeEventListener('mousedown', handleClickOutside)
437
+
438
+ if (resizeObserver) {
439
+ resizeObserver.disconnect()
440
+ } else {
441
+ window.removeEventListener('resize', updateContainerWidth)
442
+ }
443
+ })
444
+ </script>
445
+
446
+ <style scoped>
447
+ .reports-section {
448
+ padding: 24px 20px;
449
+ text-align: center;
450
+ border-radius: 8px;
451
+ box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.25);
452
+ background-color: rgba(38, 50, 60, 0.4);
453
+ }
454
+
455
+ .reports-header {
456
+ display: flex;
457
+ justify-content: space-between;
458
+ align-items: center;
459
+ margin-bottom: 20px;
460
+ font-family: Poppins-Medium;
461
+ font-size: 20px;
462
+ font-weight: 500;
463
+ font-stretch: normal;
464
+ font-style: normal;
465
+ line-height: normal;
466
+ letter-spacing: normal;
467
+ text-align: left;
468
+ color: #fff;
469
+ position: relative;
470
+ }
471
+
472
+ .reports-header::after {
473
+ content: '';
474
+ position: absolute;
475
+ bottom: -20px;
476
+ left: 50%;
477
+ transform: translateX(-50%);
478
+ width: 100%;
479
+ height: 1px;
480
+ background-color: #3d4a54;
481
+ }
482
+
483
+ .count {
484
+ color: #8b98a9;
485
+ font-size: 9px;
486
+ }
487
+
488
+ .sort-dropdown {
489
+ position: relative;
490
+ }
491
+
492
+ .sort-button {
493
+ width: 148px;
494
+ height: 40px;
495
+ border-radius: 8px;
496
+ background-color: rgba(255, 255, 255, 0.04);
497
+ font-family: Poppins-Regular;
498
+ font-size: 14px;
499
+ font-weight: normal;
500
+ font-stretch: normal;
501
+ font-style: normal;
502
+ line-height: 1.71;
503
+ letter-spacing: normal;
504
+ text-align: left;
505
+ color: #fff;
506
+ border: none;
507
+ padding: 8px 12px;
508
+ cursor: pointer;
509
+ display: flex;
510
+ justify-content: space-between;
511
+ align-items: center;
512
+ }
513
+
514
+ .sort-button:hover {
515
+ background-color: rgba(255, 255, 255, 0.08);
516
+ }
517
+
518
+ .dropdown-arrow {
519
+ width: 8px;
520
+ height: 8px;
521
+ position: relative;
522
+ transition: transform 0.2s;
523
+ }
524
+
525
+ .dropdown-arrow::before,
526
+ .dropdown-arrow::after {
527
+ content: '';
528
+ position: absolute;
529
+ background-color: #fff;
530
+ width: 1px;
531
+ height: 6px;
532
+ top: 1px;
533
+ }
534
+
535
+ .dropdown-arrow::before {
536
+ left: 2px;
537
+ transform: rotate(45deg);
538
+ }
539
+
540
+ .dropdown-arrow::after {
541
+ right: 2px;
542
+ transform: rotate(-45deg);
543
+ }
544
+
545
+ .dropdown-arrow.rotated {
546
+ transform: rotate(0deg);
547
+ }
548
+
549
+ .dropdown-arrow {
550
+ transform: rotate(180deg);
551
+ }
552
+
553
+ .sort-options {
554
+ position: absolute;
555
+ top: 100%;
556
+ right: 0;
557
+ left: auto;
558
+ width: 206px;
559
+ height: 146px;
560
+ background-color: #2e3b46;
561
+ border-radius: 12px;
562
+ margin-top: 4px;
563
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
564
+ z-index: 3200;
565
+ overflow: hidden;
566
+ padding: 0;
567
+ }
568
+
569
+ .sort-option {
570
+ display: flex;
571
+ align-items: center;
572
+ justify-content: space-between;
573
+ flex-grow: 0;
574
+ font-family: Poppins-Regular;
575
+ font-size: 13px;
576
+ font-weight: normal;
577
+ font-stretch: normal;
578
+ font-style: normal;
579
+ line-height: 1.5;
580
+ letter-spacing: normal;
581
+ text-align: left;
582
+ color: #fff;
583
+ cursor: pointer;
584
+ transition:
585
+ background-color 0.2s,
586
+ color 0.2s;
587
+ background: none;
588
+ padding: 14px 18px;
589
+ }
590
+
591
+ .sort-option.selected {
592
+ color: #cbee6b;
593
+ background-color: rgba(255, 255, 255, 0.1);
594
+ }
595
+
596
+ .sort-option:hover {
597
+ background-color: rgba(255, 255, 255, 0.08);
598
+ }
599
+
600
+ .sort-option:not(:last-child) {
601
+ border-bottom: 1px solid rgba(255, 255, 255, 0.07);
602
+ }
603
+
604
+ .checkmark {
605
+ width: 18px;
606
+ height: 18px;
607
+ display: flex;
608
+ align-items: center;
609
+ justify-content: center;
610
+ }
611
+
612
+ .checkmark svg {
613
+ width: 16px;
614
+ height: 16px;
615
+ color: #cbee6b;
616
+ }
617
+
618
+ /* Base styles para reports-grid */
619
+ .reports-grid {
620
+ display: grid;
621
+ gap: 20px;
622
+ margin-top: 38px;
623
+ width: 100%;
624
+ justify-items: center;
625
+ }
626
+
627
+ /* Clases dinámicas basadas en el número de columnas que caben */
628
+ .grid-4-columns {
629
+ grid-template-columns: repeat(4, 340px);
630
+ gap: 20px;
631
+ justify-content: center;
632
+ }
633
+
634
+ .grid-3-columns {
635
+ grid-template-columns: repeat(3, 340px);
636
+ gap: 20px;
637
+ justify-content: center;
638
+ }
639
+
640
+ .grid-2-columns {
641
+ grid-template-columns: repeat(2, 340px);
642
+ gap: 20px;
643
+ justify-content: center;
644
+ }
645
+
646
+ .grid-1-column {
647
+ grid-template-columns: 340px;
648
+ gap: 20px;
649
+ justify-content: center;
650
+ }
651
+
652
+ /* Ajustes responsivos para el contenedor y header */
653
+ .grid-1-column .reports-section {
654
+ padding: 16px 12px;
655
+ }
656
+
657
+ .grid-2-columns .reports-section {
658
+ padding: 20px 16px;
659
+ }
660
+
661
+ .grid-1-column .reports-header {
662
+ font-size: 18px;
663
+ margin-bottom: 16px;
664
+ }
665
+
666
+ .grid-2-columns .reports-header {
667
+ font-size: 19px;
668
+ margin-bottom: 18px;
669
+ }
670
+
671
+ .grid-1-column .sort-button {
672
+ width: 120px;
673
+ height: 36px;
674
+ font-size: 12px;
675
+ }
676
+
677
+ .grid-2-columns .sort-button {
678
+ width: 130px;
679
+ height: 38px;
680
+ font-size: 13px;
681
+ }
682
+
683
+ /* Asegurar que los items del grid mantengan su tamaño */
684
+ .reports-grid .report-card {
685
+ width: 340px;
686
+ height: 358px;
687
+ flex-shrink: 0;
688
+ }
689
+
690
+ /* Optimización para pantallas muy pequeñas */
691
+ @media (max-width: 400px) {
692
+ .reports-grid {
693
+ gap: 16px;
694
+ }
695
+
696
+ .reports-grid .report-card {
697
+ width: 320px;
698
+ height: 338px;
699
+ }
700
+
701
+ .grid-1-column {
702
+ grid-template-columns: 320px;
703
+ }
704
+ }
705
+
706
+ .empty-reports {
707
+ color: #8b98a9;
708
+ font-size: 20px;
709
+ margin-top: 40px;
710
+ }
711
+
712
+ /* Estados de carga y error */
713
+ .loading-state,
714
+ .error-state {
715
+ display: flex;
716
+ flex-direction: column;
717
+ align-items: center;
718
+ justify-content: center;
719
+ padding: 60px 20px;
720
+ text-align: center;
721
+ color: #fff;
722
+ font-family: 'Poppins-Regular', sans-serif;
723
+ }
724
+
725
+ .loading-spinner {
726
+ width: 40px;
727
+ height: 40px;
728
+ border: 3px solid rgba(255, 255, 255, 0.3);
729
+ border-top: 3px solid #cbee6b;
730
+ border-radius: 50%;
731
+ animation: spin 1s linear infinite;
732
+ margin-bottom: 16px;
733
+ }
734
+
735
+ @keyframes spin {
736
+ 0% {
737
+ transform: rotate(0deg);
738
+ }
739
+ 100% {
740
+ transform: rotate(360deg);
741
+ }
742
+ }
743
+
744
+ .error-state p {
745
+ margin-bottom: 16px;
746
+ color: #ff6b6b;
747
+ }
748
+
749
+ .retry-button {
750
+ background: linear-gradient(90deg, #cbee6b 0%, #b6e14b 100%);
751
+ border: none;
752
+ border-radius: 8px;
753
+ color: #1a2a3a;
754
+ font-family: 'Poppins-Medium', sans-serif;
755
+ font-size: 14px;
756
+ padding: 10px 20px;
757
+ cursor: pointer;
758
+ transition: opacity 0.2s;
759
+ }
760
+
761
+ .retry-button:hover {
762
+ opacity: 0.8;
763
+ }
764
+ </style>