@drax/crud-vue 3.39.0 → 3.47.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 (40) hide show
  1. package/package.json +4 -4
  2. package/src/components/Crud.vue +21 -3
  3. package/src/components/CrudActiveFilters.vue +11 -9
  4. package/src/components/CrudAi.vue +38 -25
  5. package/src/components/CrudAutocomplete.vue +14 -2
  6. package/src/components/CrudDialog.vue +6 -6
  7. package/src/components/CrudExportList.vue +13 -12
  8. package/src/components/CrudFieldRange.vue +8 -4
  9. package/src/components/CrudFilters.vue +8 -2
  10. package/src/components/CrudFiltersAction.vue +3 -3
  11. package/src/components/CrudFiltersDynamic.vue +18 -10
  12. package/src/components/CrudForm.vue +44 -31
  13. package/src/components/CrudFormField.vue +48 -8
  14. package/src/components/CrudFormList.vue +44 -32
  15. package/src/components/CrudFormRecord.vue +19 -11
  16. package/src/components/CrudImportList.vue +17 -16
  17. package/src/components/CrudList.vue +41 -12
  18. package/src/components/CrudListGallery.vue +65 -27
  19. package/src/components/CrudListTable.vue +49 -10
  20. package/src/components/CrudNotify.vue +3 -1
  21. package/src/components/CrudRefDisplay.vue +22 -23
  22. package/src/components/CrudRouteForm.vue +8 -4
  23. package/src/components/CrudRowValue.vue +9 -8
  24. package/src/components/CrudSearch.vue +2 -1
  25. package/src/components/buttons/CrudAiButton.vue +3 -2
  26. package/src/components/buttons/CrudColumnsButton.vue +17 -7
  27. package/src/components/buttons/CrudCreateButton.vue +3 -2
  28. package/src/components/buttons/CrudCreateOnTheFlyButton.vue +7 -2
  29. package/src/components/buttons/CrudDeleteButton.vue +3 -2
  30. package/src/components/buttons/CrudExportButton.vue +7 -6
  31. package/src/components/buttons/CrudFilterButton.vue +4 -3
  32. package/src/components/buttons/CrudGroupByButton.vue +28 -15
  33. package/src/components/buttons/CrudImportButton.vue +10 -8
  34. package/src/components/buttons/CrudRefreshButton.vue +3 -2
  35. package/src/components/buttons/CrudSavedQueriesButton.vue +37 -19
  36. package/src/components/buttons/CrudUpdateButton.vue +3 -2
  37. package/src/components/buttons/CrudViewButton.vue +3 -2
  38. package/src/components/combobox/EntityCombobox.vue +4 -0
  39. package/src/composables/useCrudRefDisplay.ts +28 -5
  40. package/src/index.ts +4 -0
@@ -66,25 +66,29 @@ const totalCount = computed(() => {
66
66
  </script>
67
67
 
68
68
  <template>
69
- <div>
69
+ <div id="crud-group-by-wrapper" class="crud-group-by-wrapper">
70
70
  <v-btn
71
+ id="crud-group-by-button"
72
+ class="crud-group-by-button"
71
73
  icon
72
74
  variant="text"
73
75
  @click="openDialog"
74
76
  >
75
- <v-icon>mdi-chart-bar</v-icon>
77
+ <v-icon id="crud-group-by-button-icon" class="crud-group-by-button__icon">mdi-chart-bar</v-icon>
76
78
  <v-tooltip activator="parent" location="bottom">
77
79
  {{ t('crud.groupBy.button') }}
78
80
  </v-tooltip>
79
81
  </v-btn>
80
82
 
81
- <v-dialog v-model="dialog" max-width="800" >
82
- <v-card>
83
- <v-card-title class="d-flex align-center">
84
- <v-icon class="mr-2">mdi-chart-bar</v-icon>
83
+ <v-dialog id="crud-group-by-dialog" class="crud-group-by-dialog" v-model="dialog" max-width="800" >
84
+ <v-card id="crud-group-by-card" class="crud-group-by-dialog__card">
85
+ <v-card-title id="crud-group-by-title" class="crud-group-by-dialog__title d-flex align-center">
86
+ <v-icon id="crud-group-by-title-icon" class="crud-group-by-dialog__title-icon mr-2">mdi-chart-bar</v-icon>
85
87
  {{ t('crud.groupBy.title') }}
86
88
  <v-spacer></v-spacer>
87
89
  <v-btn
90
+ id="crud-group-by-close-button"
91
+ class="crud-group-by-dialog__close-button"
88
92
  icon
89
93
  variant="text"
90
94
  @click="resetAndClose"
@@ -96,11 +100,13 @@ const totalCount = computed(() => {
96
100
 
97
101
  <v-divider></v-divider>
98
102
 
99
- <v-card-text>
100
- <crud-active-filters :entity="entity"></crud-active-filters>
103
+ <v-card-text id="crud-group-by-config" class="crud-group-by-dialog__config">
104
+ <crud-active-filters id="crud-group-by-active-filters" class="crud-group-by-dialog__active-filters" :entity="entity"></crud-active-filters>
101
105
  <v-divider></v-divider>
102
106
 
103
107
  <v-select
108
+ id="crud-group-by-fields-select"
109
+ class="crud-group-by-dialog__fields-select"
104
110
  v-model="selectedFields"
105
111
  :items="availableFields"
106
112
  item-title="label"
@@ -116,12 +122,13 @@ const totalCount = computed(() => {
116
122
  <!-- Selector de formato de fecha -->
117
123
  <v-select
118
124
  v-if="hasDateFields"
125
+ id="crud-group-by-date-format-select"
119
126
  v-model="dateFormat"
120
127
  :items="dateFormatOptions"
121
128
  :label="t('crud.groupBy.dateFormatLabel')"
122
129
  density="compact"
123
130
  variant="outlined"
124
- class="mt-4"
131
+ class="crud-group-by-dialog__date-format-select mt-4"
125
132
  :menu-props="{ zIndex: 3000 }"
126
133
  >
127
134
  <template v-slot:prepend-inner>
@@ -134,9 +141,11 @@ const totalCount = computed(() => {
134
141
 
135
142
  <v-divider></v-divider>
136
143
 
137
- <v-card-actions>
144
+ <v-card-actions id="crud-group-by-actions" class="crud-group-by-dialog__actions">
138
145
  <v-spacer></v-spacer>
139
146
  <v-btn
147
+ id="crud-group-by-apply-button"
148
+ class="crud-group-by-dialog__apply-button"
140
149
  color="primary"
141
150
  variant="flat"
142
151
  @click="handleGroupBy"
@@ -148,20 +157,22 @@ const totalCount = computed(() => {
148
157
  </v-card-actions>
149
158
  <v-divider class="mb-4"></v-divider>
150
159
  <!-- Tabla de resultados -->
151
- <v-card-text v-if="groupByData.length > 0">
160
+ <v-card-text v-if="groupByData.length > 0" id="crud-group-by-results" class="crud-group-by-dialog__results">
152
161
 
153
162
 
154
- <div class="d-flex align-center mb-3">
155
- <v-icon class="mr-2">mdi-table</v-icon>
156
- <span class="text-h6">{{ t('crud.groupBy.results') }}</span>
163
+ <div id="crud-group-by-results-header" class="crud-group-by-dialog__results-header d-flex align-center mb-3">
164
+ <v-icon id="crud-group-by-results-icon" class="crud-group-by-dialog__results-icon mr-2">mdi-table</v-icon>
165
+ <span id="crud-group-by-results-title" class="crud-group-by-dialog__results-title text-h6">{{ t('crud.groupBy.results') }}</span>
157
166
  <v-spacer></v-spacer>
158
- <v-chip color="primary" variant="flat" size="small">
167
+ <v-chip id="crud-group-by-total-chip" class="crud-group-by-dialog__total-chip" color="primary" variant="flat" size="small">
159
168
  {{ t('crud.groupBy.total') }}: {{ totalCount }}
160
169
  </v-chip>
161
170
  </div>
162
171
 
163
172
 
164
173
  <v-data-table
174
+ id="crud-group-by-results-table"
175
+ class="crud-group-by-dialog__results-table"
165
176
  :headers="headers"
166
177
  :items="groupByData"
167
178
  density="compact"
@@ -191,7 +202,9 @@ const totalCount = computed(() => {
191
202
  <!-- Formato especial para el count -->
192
203
  <template v-slot:item.count="{ value }">
193
204
  <v-chip color="primary" size="small" variant="flat">
205
+ <span class="crud-group-by-dialog__count-value">
194
206
  {{ value }}
207
+ </span>
195
208
  </v-chip>
196
209
  </template>
197
210
  </v-data-table>
@@ -37,22 +37,24 @@ function onFileChange(event: Event) {
37
37
  </script>
38
38
 
39
39
  <template>
40
- <div v-if="entity.isImportable">
40
+ <div v-if="entity.isImportable" id="crud-import-button-wrapper" class="crud-import-button-wrapper">
41
41
  <input
42
42
  ref="fileInput"
43
- class="d-none"
43
+ id="crud-import-file-input"
44
+ class="crud-import-button__file-input d-none"
44
45
  type="file"
45
46
  :accept="entity.importFormats.includes('CSV') && entity.importFormats.includes('JSON') ? '.csv,.json,application/json,text/csv' : entity.importFormats.includes('CSV') ? '.csv,text/csv' : '.json,application/json'"
46
47
  @change="onFileChange"
47
48
  />
48
- <v-menu>
49
+ <v-menu id="crud-import-menu" class="crud-import-menu">
49
50
  <template v-slot:activator="{ props: mp }">
50
- <v-tooltip location="top">
51
+ <v-tooltip id="crud-import-button-tooltip" class="crud-import-button__tooltip" location="top">
51
52
  <template v-slot:activator="{ props }">
52
53
  <v-btn
53
54
  v-bind="{...mp, ...props}"
54
55
  :disabled="importLoading"
55
- class="mr-1"
56
+ id="crud-import-button"
57
+ class="crud-import-button mr-1"
56
58
  variant="text"
57
59
  :loading="importLoading"
58
60
  icon="mdi-database-import-outline"
@@ -61,9 +63,9 @@ function onFileChange(event: Event) {
61
63
  {{ t('action.import')}}
62
64
  </v-tooltip>
63
65
  </template>
64
- <v-list>
65
- <v-list-item v-for="format in entity.importFormats" @click="selectFormat(format)">
66
- <v-list-item-title>{{ format }}</v-list-item-title>
66
+ <v-list id="crud-import-format-list" class="crud-import-menu__list">
67
+ <v-list-item v-for="format in entity.importFormats" :id="`crud-import-format-${format}`" class="crud-import-menu__item" @click="selectFormat(format)">
68
+ <v-list-item-title class="crud-import-menu__item-title">{{ format }}</v-list-item-title>
67
69
  </v-list-item>
68
70
  </v-list>
69
71
  </v-menu>
@@ -5,12 +5,13 @@ const {t} = useI18n()
5
5
  </script>
6
6
 
7
7
  <template>
8
- <v-tooltip location="top">
8
+ <v-tooltip :id="`${$attrs.id || 'crud-refresh-button'}-tooltip`" class="crud-refresh-button__tooltip" location="top">
9
9
  <template v-slot:activator="{ props }">
10
10
  <v-btn
11
11
  v-bind="{ ...$attrs, ...props }"
12
12
  icon="mdi-refresh"
13
- class="mr-1"
13
+ :id="$attrs.id || 'crud-refresh-button'"
14
+ class="crud-refresh-button mr-1"
14
15
  variant="text"
15
16
  >
16
17
  </v-btn>
@@ -216,6 +216,8 @@ function onMenuUpdate(value: boolean) {
216
216
  <template>
217
217
  <v-menu
218
218
  v-if="canViewSavedQueries"
219
+ id="crud-saved-queries-menu"
220
+ class="crud-saved-queries-menu"
219
221
  :model-value="menu"
220
222
  offset-y
221
223
  :close-on-content-click="false"
@@ -224,21 +226,25 @@ function onMenuUpdate(value: boolean) {
224
226
  <template #activator="{ props: activatorProps }">
225
227
  <v-btn
226
228
  v-bind="activatorProps"
229
+ id="crud-saved-queries-button"
230
+ class="crud-saved-queries-button"
227
231
  icon
228
232
  variant="text"
229
233
  >
230
- <v-icon>mdi-content-save-cog</v-icon>
234
+ <v-icon id="crud-saved-queries-button-icon" class="crud-saved-queries-button__icon">mdi-content-save-cog</v-icon>
231
235
  <v-tooltip activator="parent" location="bottom">
232
236
  {{ title }}
233
237
  </v-tooltip>
234
238
  </v-btn>
235
239
  </template>
236
240
 
237
- <v-list min-width="280">
238
- <v-list-subheader>{{ title }}</v-list-subheader>
241
+ <v-list id="crud-saved-queries-list" class="crud-saved-queries-menu__list" min-width="280">
242
+ <v-list-subheader id="crud-saved-queries-title" class="crud-saved-queries-menu__title">{{ title }}</v-list-subheader>
239
243
 
240
244
  <v-list-item
241
245
  v-if="canCreateSavedQueries"
246
+ id="crud-saved-queries-save-item"
247
+ class="crud-saved-queries-menu__save-item"
242
248
  @click="openSaveDialog"
243
249
  >
244
250
  <template #prepend>
@@ -249,17 +255,19 @@ function onMenuUpdate(value: boolean) {
249
255
 
250
256
  <v-divider />
251
257
 
252
- <v-list-item v-if="loading">
253
- <v-progress-linear indeterminate />
258
+ <v-list-item v-if="loading" id="crud-saved-queries-loading-item" class="crud-saved-queries-menu__loading-item">
259
+ <v-progress-linear id="crud-saved-queries-loading" class="crud-saved-queries-menu__loading" indeterminate />
254
260
  </v-list-item>
255
261
 
256
- <v-list-item v-else-if="savedQueries.length === 0">
257
- <v-list-item-title class="text-medium-emphasis">{{ noQueriesText }}</v-list-item-title>
262
+ <v-list-item v-else-if="savedQueries.length === 0" id="crud-saved-queries-empty-item" class="crud-saved-queries-menu__empty-item">
263
+ <v-list-item-title class="crud-saved-queries-menu__empty-title text-medium-emphasis">{{ noQueriesText }}</v-list-item-title>
258
264
  </v-list-item>
259
265
 
260
266
  <v-list-item
261
267
  v-for="query in savedQueries"
262
268
  :key="query._id"
269
+ :id="`crud-saved-query-${query._id}`"
270
+ class="crud-saved-queries-menu__query-item"
263
271
  @click="applyQuery(query)"
264
272
  >
265
273
  <template #prepend>
@@ -269,6 +277,8 @@ function onMenuUpdate(value: boolean) {
269
277
  <template #append>
270
278
  <v-btn
271
279
  v-if="canDeleteQuery(query)"
280
+ :id="`crud-saved-query-delete-${query._id}`"
281
+ class="crud-saved-queries-menu__delete-button"
272
282
  icon
273
283
  variant="text"
274
284
  color="red"
@@ -285,11 +295,13 @@ function onMenuUpdate(value: boolean) {
285
295
  </v-list>
286
296
  </v-menu>
287
297
 
288
- <v-dialog v-model="saveDialog" max-width="460">
289
- <v-card>
290
- <v-card-title>{{ saveTitle }}</v-card-title>
291
- <v-card-text>
298
+ <v-dialog id="crud-saved-query-save-dialog" class="crud-saved-query-save-dialog" v-model="saveDialog" max-width="460">
299
+ <v-card id="crud-saved-query-save-card" class="crud-saved-query-save-dialog__card">
300
+ <v-card-title id="crud-saved-query-save-title" class="crud-saved-query-save-dialog__title">{{ saveTitle }}</v-card-title>
301
+ <v-card-text id="crud-saved-query-save-content" class="crud-saved-query-save-dialog__content">
292
302
  <v-text-field
303
+ id="crud-saved-query-name-field"
304
+ class="crud-saved-query-save-dialog__name-field"
293
305
  v-model="form.name"
294
306
  :label="te('crud.savedQueries.name') ? t('crud.savedQueries.name') : 'Name'"
295
307
  density="compact"
@@ -297,18 +309,22 @@ function onMenuUpdate(value: boolean) {
297
309
  autofocus
298
310
  />
299
311
  <v-switch
312
+ id="crud-saved-query-shared-switch"
313
+ class="crud-saved-query-save-dialog__shared-switch"
300
314
  v-model="form.shared"
301
315
  :label="te('crud.savedQueries.shared') ? t('crud.savedQueries.shared') : 'Shared'"
302
316
  color="primary"
303
317
  hide-details
304
318
  />
305
319
  </v-card-text>
306
- <v-card-actions>
320
+ <v-card-actions id="crud-saved-query-save-actions" class="crud-saved-query-save-dialog__actions">
307
321
  <v-spacer />
308
- <v-btn variant="text" @click="saveDialog = false">
322
+ <v-btn id="crud-saved-query-save-cancel-button" class="crud-saved-query-save-dialog__cancel-button" variant="text" @click="saveDialog = false">
309
323
  {{ te('action.cancel') ? t('action.cancel') : 'Cancel' }}
310
324
  </v-btn>
311
325
  <v-btn
326
+ id="crud-saved-query-save-submit-button"
327
+ class="crud-saved-query-save-dialog__submit-button"
312
328
  color="primary"
313
329
  variant="flat"
314
330
  :loading="saving"
@@ -321,18 +337,20 @@ function onMenuUpdate(value: boolean) {
321
337
  </v-card>
322
338
  </v-dialog>
323
339
 
324
- <v-dialog v-model="deleteDialog" max-width="460">
325
- <v-card>
326
- <v-card-title>{{ deleteTitle }}</v-card-title>
327
- <v-card-text>
340
+ <v-dialog id="crud-saved-query-delete-dialog" class="crud-saved-query-delete-dialog" v-model="deleteDialog" max-width="460">
341
+ <v-card id="crud-saved-query-delete-card" class="crud-saved-query-delete-dialog__card">
342
+ <v-card-title id="crud-saved-query-delete-title" class="crud-saved-query-delete-dialog__title">{{ deleteTitle }}</v-card-title>
343
+ <v-card-text id="crud-saved-query-delete-content" class="crud-saved-query-delete-dialog__content">
328
344
  {{ deleteConfirmText }}
329
345
  </v-card-text>
330
- <v-card-actions>
346
+ <v-card-actions id="crud-saved-query-delete-actions" class="crud-saved-query-delete-dialog__actions">
331
347
  <v-spacer />
332
- <v-btn variant="text" @click="deleteDialog = false">
348
+ <v-btn id="crud-saved-query-delete-cancel-button" class="crud-saved-query-delete-dialog__cancel-button" variant="text" @click="deleteDialog = false">
333
349
  {{ te('action.cancel') ? t('action.cancel') : 'Cancel' }}
334
350
  </v-btn>
335
351
  <v-btn
352
+ id="crud-saved-query-delete-confirm-button"
353
+ class="crud-saved-query-delete-dialog__confirm-button"
336
354
  color="red"
337
355
  variant="flat"
338
356
  :loading="deleting"
@@ -4,12 +4,13 @@ const {t} = useI18n()
4
4
  </script>
5
5
 
6
6
  <template>
7
- <v-tooltip location="top">
7
+ <v-tooltip :id="`${$attrs.id || 'crud-update-button'}-tooltip`" class="crud-update-button__tooltip" location="top">
8
8
  <template v-slot:activator="{ props}">
9
9
  <v-btn
10
10
  v-bind="{ ...$attrs, ...props }"
11
11
  icon="mdi-pencil"
12
- class="mr-1"
12
+ :id="$attrs.id || 'crud-update-button'"
13
+ class="crud-update-button mr-1"
13
14
  variant="text"
14
15
  color="blue"
15
16
  slim
@@ -4,12 +4,13 @@ const {t} = useI18n()
4
4
  </script>
5
5
 
6
6
  <template>
7
- <v-tooltip location="top">
7
+ <v-tooltip :id="`${$attrs.id || 'crud-view-button'}-tooltip`" class="crud-view-button__tooltip" location="top">
8
8
  <template v-slot:activator="{ props}">
9
9
  <v-btn
10
10
  v-bind="{ ...$attrs, ...props }"
11
11
  icon="mdi-magnify"
12
- class="mr-1"
12
+ :id="$attrs.id || 'crud-view-button'"
13
+ class="crud-view-button mr-1"
13
14
  variant="text"
14
15
  color="teal"
15
16
  slim
@@ -17,6 +17,8 @@ const getEntityLabel = (entity: IEntityCrud) => {
17
17
  <template>
18
18
 
19
19
  <v-select
20
+ id="entity-combobox"
21
+ class="entity-combobox"
20
22
  :items="entityStore.entities"
21
23
  item-value="name"
22
24
  v-model="valueModel"
@@ -28,6 +30,8 @@ const getEntityLabel = (entity: IEntityCrud) => {
28
30
  <template v-slot:item="{ props: itemProps, item }">
29
31
  <v-list-item
30
32
  v-bind="itemProps"
33
+ :id="`entity-combobox-item-${(item.raw as IEntityCrud).name}`"
34
+ class="entity-combobox__item"
31
35
  density="compact"
32
36
  :title="getEntityLabel(item.raw as IEntityCrud)"
33
37
 
@@ -2,14 +2,37 @@ import type {IEntityCrud} from '@drax/crud-share'
2
2
 
3
3
  export function useCrudRefDisplay() {
4
4
 
5
- async function refDisplay(entity: IEntityCrud, value: any, refDisplay: string) {
5
+ function getDisplayValue(item: any, displayField: string, fallback: any = '') {
6
+ return item?.[displayField] ?? fallback
7
+ }
8
+
9
+ async function refDisplay(entity: IEntityCrud, value: any, displayField?: string) {
6
10
  try{
7
- if(entity?.provider?.findByIds){
8
- // Asegurar que value sea un array
9
- const ids = Array.isArray(value) ? value : [value]
11
+ const field = displayField ?? entity.displayField
12
+
13
+ if(value == null || !field){
14
+ return value
15
+ }
16
+
17
+ if(Array.isArray(value)){
18
+ if(!entity?.provider?.findByIds){
19
+ return value
20
+ }
21
+ const ids = value
10
22
  const items = await entity.provider.findByIds(ids)
11
- return items.map(item => item[refDisplay as any]).join(', ')
23
+ return items.map(item => getDisplayValue(item, field)).join(', ')
12
24
  }
25
+
26
+ if(entity?.provider?.findById){
27
+ const item = await entity.provider.findById(value)
28
+ return item ? getDisplayValue(item, field, value) : value
29
+ }
30
+
31
+ if(entity?.provider?.findByIds){
32
+ const items = await entity.provider.findByIds([value])
33
+ return items[0] ? getDisplayValue(items[0], field, value) : value
34
+ }
35
+
13
36
  return value
14
37
  }catch (e){
15
38
  console.error(e)
package/src/index.ts CHANGED
@@ -15,12 +15,14 @@ import CrudFiltersAction from "./components/CrudFiltersAction.vue";
15
15
  import CrudNotify from "./components/CrudNotify.vue";
16
16
  import CrudSearch from "./components/CrudSearch.vue";
17
17
  import CrudAutocomplete from "./components/CrudAutocomplete.vue";
18
+ import CrudRefDisplay from "./components/CrudRefDisplay.vue";
18
19
  import CrudSavedQueriesButton from "./components/buttons/CrudSavedQueriesButton.vue";
19
20
  import CrudRefreshButton from "./components/buttons/CrudRefreshButton.vue";
20
21
  import EntityCombobox from "./components/combobox/EntityCombobox.vue";
21
22
  import {useCrudStore} from "./stores/UseCrudStore";
22
23
  import {useEntityStore} from "./stores/UseEntityStore";
23
24
  import {useCrud} from "./composables/UseCrud";
25
+ import {useCrudRefDisplay} from "./composables/useCrudRefDisplay";
24
26
  import {useDynamicFilters} from "./composables/UseDynamicFilters";
25
27
  import {useFilterIcon} from "./composables/UseFilterIcon";
26
28
  import {useFormUtils} from "./composables/UseFormUtils";
@@ -43,6 +45,7 @@ export {
43
45
  CrudNotify,
44
46
  CrudSearch,
45
47
  CrudAutocomplete,
48
+ CrudRefDisplay,
46
49
  CrudSavedQueriesButton,
47
50
  CrudRefreshButton,
48
51
  CrudFilters,
@@ -54,6 +57,7 @@ export {
54
57
  useCrudStore,
55
58
  useInputErrorI18n,
56
59
  useFilterIcon,
60
+ useCrudRefDisplay,
57
61
  EntityCrud,
58
62
  useEntityStore,
59
63
  EntityCombobox