@drax/crud-vue 3.46.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 (38) hide show
  1. package/package.json +3 -3
  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 +2 -2
  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
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.46.0",
6
+ "version": "3.47.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -27,7 +27,7 @@
27
27
  "@drax/common-front": "^3.29.0",
28
28
  "@drax/crud-front": "^3.21.0",
29
29
  "@drax/crud-share": "^3.46.0",
30
- "@drax/media-vue": "^3.46.0"
30
+ "@drax/media-vue": "^3.47.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "pinia": "^3.0.4",
@@ -50,5 +50,5 @@
50
50
  "vue-tsc": "^3.2.4",
51
51
  "vuetify": "^3.11.8"
52
52
  },
53
- "gitHead": "a90e6d81ea836f1f921c7baf6f149b82a0aced38"
53
+ "gitHead": "b9954d6259a8f567a028f29127a932f53d887d65"
54
54
  }
@@ -137,6 +137,8 @@ watch(dialog, (value) => {
137
137
  <template>
138
138
  <crud-route-form
139
139
  v-if="isRouteCrudForm"
140
+ :id="`crud-route-form-${entity.name}`"
141
+ class="crud__route-form"
140
142
  :entity="entity"
141
143
  @created="item => emit('created', item)"
142
144
  @updated="item => emit('updated', item)"
@@ -150,11 +152,13 @@ watch(dialog, (value) => {
150
152
  </template>
151
153
  </crud-route-form>
152
154
 
153
- <v-container v-else :fluid="entity.containerFluid" class="mt-5">
154
- <v-card :class="entity.cardClass" :density="entity.cardDensity">
155
+ <v-container v-else :id="`crud-container-${entity.name}`" :fluid="entity.containerFluid" class="crud mt-5">
156
+ <v-card :id="`crud-card-${entity.name}`" :class="['crud__card', entity.cardClass]" :density="entity.cardDensity">
155
157
 
156
158
  <component
157
159
  :is="listComponent"
160
+ :id="`crud-list-component-${entity.name}`"
161
+ class="crud__list"
158
162
  :entity="entity"
159
163
  @create="onCreate"
160
164
  @edit="onEditAt"
@@ -229,6 +233,8 @@ watch(dialog, (value) => {
229
233
  </v-card>
230
234
 
231
235
  <crud-dialog
236
+ :id="`crud-dialog-${entity.name}`"
237
+ class="crud__dialog"
232
238
  v-model="dialog"
233
239
  :entity="entity"
234
240
  :operation="operation"
@@ -236,6 +242,8 @@ watch(dialog, (value) => {
236
242
  <template #toolbar-actions>
237
243
  <v-btn
238
244
  v-if="canOpenRouteCrudForm"
245
+ id="crud-open-route-form-button"
246
+ class="crud__open-route-form-button"
239
247
  icon="mdi-open-in-new"
240
248
  variant="text"
241
249
  @click="openRouteCrudForm"
@@ -243,6 +251,8 @@ watch(dialog, (value) => {
243
251
 
244
252
  <v-btn
245
253
  v-if="canNavigateItems"
254
+ id="crud-dialog-prev-button"
255
+ class="crud__dialog-prev-button"
246
256
  icon="mdi-chevron-left"
247
257
  variant="text"
248
258
  :disabled="!canNavigatePrev"
@@ -251,6 +261,8 @@ watch(dialog, (value) => {
251
261
 
252
262
  <v-btn
253
263
  v-if="canNavigateItems"
264
+ id="crud-dialog-next-button"
265
+ class="crud__dialog-next-button"
254
266
  icon="mdi-chevron-right"
255
267
  variant="text"
256
268
  :disabled="!canNavigateNext"
@@ -259,6 +271,8 @@ watch(dialog, (value) => {
259
271
 
260
272
  <crud-ai-button
261
273
  v-if="entity.isAiAssistable && ['create', 'edit'].includes(operation) && hasPermission('ai:promptCrud')"
274
+ id="crud-ai-toggle-button"
275
+ class="crud__ai-toggle-button"
262
276
  :entity="entity"
263
277
  v-model="aiExpanded"
264
278
  />
@@ -267,6 +281,8 @@ watch(dialog, (value) => {
267
281
  <slot name="tools">
268
282
  <crud-ai
269
283
  v-if="entity.isAiAssistable && ['create', 'edit'].includes(operation) && hasPermission('ai:promptCrud')"
284
+ id="crud-ai"
285
+ class="crud__ai"
270
286
  :entity="entity"
271
287
  v-model="aiExpanded"
272
288
  @apply="applyAiSuggestions"
@@ -277,6 +293,8 @@ watch(dialog, (value) => {
277
293
  <slot name="form" v-bind="{form, operation}">
278
294
 
279
295
  <crud-form
296
+ id="crud-form-dialog"
297
+ class="crud__form"
280
298
  :entity="entity"
281
299
  @created="item => emit('created', item)"
282
300
  @updated="item => emit('updated', item)"
@@ -295,7 +313,7 @@ watch(dialog, (value) => {
295
313
 
296
314
  </crud-dialog>
297
315
 
298
- <crud-notify v-model="notify" :message="message"></crud-notify>
316
+ <crud-notify id="crud-notify" class="crud__notify" v-model="notify" :message="message"></crud-notify>
299
317
  </v-container>
300
318
  </template>
301
319
 
@@ -122,16 +122,18 @@ const emit = defineEmits(['filterRemoved', 'filtersCleared'])
122
122
  </script>
123
123
 
124
124
  <template>
125
- <v-card v-if="activeFilters.length > 0" flat class="mb-2">
126
- <v-card-text class="py-2">
127
- <div class="d-flex align-center flex-wrap ga-2">
128
- <span class="text-caption text-medium-emphasis">
129
- <v-icon size="x-small">mdi-filter</v-icon> {{ t('crud.activeFilters') }}:
125
+ <v-card v-if="activeFilters.length > 0" id="crud-active-filters" flat class="crud-active-filters mb-2">
126
+ <v-card-text id="crud-active-filters-content" class="crud-active-filters__content py-2">
127
+ <div id="crud-active-filters-list" class="crud-active-filters__list d-flex align-center flex-wrap ga-2">
128
+ <span id="crud-active-filters-label" class="crud-active-filters__label text-caption text-medium-emphasis">
129
+ <v-icon id="crud-active-filters-icon" class="crud-active-filters__icon" size="x-small">mdi-filter</v-icon> {{ t('crud.activeFilters') }}:
130
130
  </span>
131
131
 
132
132
  <v-chip
133
133
  v-for="filter in activeFilters"
134
134
  :key="filter.index"
135
+ :id="`crud-active-filter-${filter.index}`"
136
+ class="crud-active-filters__chip"
135
137
  closable
136
138
  size="small"
137
139
  color="primary"
@@ -139,15 +141,15 @@ const emit = defineEmits(['filterRemoved', 'filtersCleared'])
139
141
  @click:close="removeFilter(filter.index)"
140
142
  >
141
143
 
142
- <span class="font-weight-medium">{{ getFilterLabel(filter) }}</span>
143
- <v-icon :icon="filterIcon(filter)" size="x-small" class="mx-1" />
144
- <span v-if="['ref','array.ref'].includes(filter.type)">
144
+ <span class="crud-active-filters__chip-label font-weight-medium">{{ getFilterLabel(filter) }}</span>
145
+ <v-icon :icon="filterIcon(filter)" size="x-small" class="crud-active-filters__chip-icon mx-1" />
146
+ <span v-if="['ref','array.ref'].includes(filter.type)" class="crud-active-filters__chip-ref-value">
145
147
  <crud-ref-display
146
148
  :ref-display="filter.refDisplay"
147
149
  :value="filter.value"
148
150
  :entity="entity.getRef(filter.ref)" />
149
151
  </span>
150
- <span v-else>{{ getFilterValue(filter) }}</span>
152
+ <span v-else class="crud-active-filters__chip-value">{{ getFilterValue(filter) }}</span>
151
153
 
152
154
  <v-tooltip
153
155
  v-if="filter.endOfDay"
@@ -300,32 +300,36 @@ watch(prompt, () => {
300
300
 
301
301
  <template>
302
302
  <v-expand-transition>
303
- <div v-if="expanded" class="mb-4">
304
- <v-card variant="tonal" class="crud-ai-panel">
305
- <v-card-text>
306
- <div class="d-flex align-center mb-4">
307
- <div class="text-subtitle-1">{{ subtitle }}</div>
303
+ <div v-if="expanded" id="crud-ai-panel-wrapper" class="crud-ai-panel-wrapper mb-4">
304
+ <v-card id="crud-ai-panel" variant="tonal" class="crud-ai-panel">
305
+ <v-card-text id="crud-ai-content" class="crud-ai-panel__content">
306
+ <div id="crud-ai-header" class="crud-ai-panel__header d-flex align-center mb-4">
307
+ <div id="crud-ai-subtitle" class="crud-ai-panel__subtitle text-subtitle-1">{{ subtitle }}</div>
308
308
  <v-spacer />
309
- <v-btn icon="mdi-close" variant="text" @click="expanded = false" />
309
+ <v-btn id="crud-ai-close-button" class="crud-ai-panel__close-button" icon="mdi-close" variant="text" @click="expanded = false" />
310
310
  </div>
311
311
 
312
312
  <v-alert
313
313
  v-if="error"
314
+ id="crud-ai-error"
314
315
  type="error"
315
316
  variant="tonal"
316
- class="mb-4"
317
+ class="crud-ai-panel__error mb-4"
317
318
  :text="error"
318
319
  />
319
320
 
320
321
  <v-alert
321
322
  v-if="editableFields.length === 0"
323
+ id="crud-ai-empty-fields-warning"
322
324
  type="warning"
323
325
  variant="tonal"
324
- class="mb-4"
326
+ class="crud-ai-panel__empty-fields-warning mb-4"
325
327
  :text="te('ai.noEditableFields') ? t('ai.noEditableFields') : 'No hay campos editables disponibles para asistir con IA.'"
326
328
  />
327
329
 
328
330
  <v-textarea
331
+ id="crud-ai-prompt"
332
+ class="crud-ai-panel__prompt"
329
333
  v-model="prompt"
330
334
  rows="3"
331
335
  auto-grow
@@ -334,8 +338,10 @@ watch(prompt, () => {
334
338
  :placeholder="te('ai.promptPlaceholder') ? t('ai.promptPlaceholder') : 'Ejemplo: completá este formulario para un producto premium orientado a pequeñas empresas.'"
335
339
  />
336
340
 
337
- <div class="d-flex justify-end mt-2">
341
+ <div id="crud-ai-prompt-actions" class="crud-ai-panel__prompt-actions d-flex justify-end mt-2">
338
342
  <v-btn
343
+ id="crud-ai-generate-button"
344
+ class="crud-ai-panel__generate-button"
339
345
  color="primary"
340
346
  variant="flat"
341
347
  :loading="loading"
@@ -346,54 +352,59 @@ watch(prompt, () => {
346
352
  </v-btn>
347
353
  </div>
348
354
 
349
- <v-card v-if="response" variant="outlined" class="mt-6">
350
- <v-card-title>
355
+ <v-card v-if="response" id="crud-ai-preview" variant="outlined" class="crud-ai-panel__preview mt-6">
356
+ <v-card-title id="crud-ai-preview-title" class="crud-ai-panel__preview-title">
351
357
  {{ te('ai.preview') ? t('ai.preview') : 'Vista previa' }}
352
358
  </v-card-title>
353
359
 
354
- <v-card-text>
360
+ <v-card-text id="crud-ai-preview-content" class="crud-ai-panel__preview-content">
355
361
  <v-alert
356
362
  v-if="response.message"
363
+ id="crud-ai-response-message"
357
364
  variant="tonal"
358
365
  type="info"
359
- class="mb-4"
366
+ class="crud-ai-panel__response-message mb-4"
360
367
  :text="response.message"
361
368
  />
362
369
 
363
370
  <v-alert
364
371
  v-if="changedEntries.length === 0"
372
+ id="crud-ai-no-changes"
365
373
  variant="tonal"
366
374
  type="info"
375
+ class="crud-ai-panel__no-changes"
367
376
  :text="te('ai.noChanges') ? t('ai.noChanges') : 'La IA no propuso cambios aplicables para este formulario.'"
368
377
  />
369
378
 
370
- <v-list v-else lines="three">
379
+ <v-list v-else id="crud-ai-changes-list" class="crud-ai-panel__changes-list" lines="three">
371
380
  <v-list-item
372
381
  v-for="entry in changedEntries"
373
382
  :key="entry.field.name"
374
- class="px-0"
383
+ :id="`crud-ai-change-${entry.field.name}`"
384
+ class="crud-ai-panel__change px-0"
375
385
  >
376
- <v-list-item-title>
386
+ <v-list-item-title class="crud-ai-panel__change-title">
377
387
  {{ entry.field.label || entry.field.name }}
378
388
  </v-list-item-title>
379
- <div class="mt-1 preview-block">
389
+ <div class="crud-ai-panel__change-preview mt-1 preview-block">
380
390
  <template v-if="showCurrentValues">
381
- <div class="text-caption text-medium-emphasis">Actual</div>
382
- <pre class="value-preview">{{ formatValue(entry.currentValue) }}</pre>
391
+ <div class="crud-ai-panel__current-label text-caption text-medium-emphasis">Actual</div>
392
+ <pre class="crud-ai-panel__current-value value-preview">{{ formatValue(entry.currentValue) }}</pre>
383
393
  </template>
384
- <div class="text-caption text-medium-emphasis mt-2">Sugerido</div>
385
- <pre class="value-preview">{{ formatValue(entry.suggestedValue) }}</pre>
394
+ <div class="crud-ai-panel__suggested-label text-caption text-medium-emphasis mt-2">Sugerido</div>
395
+ <pre class="crud-ai-panel__suggested-value value-preview">{{ formatValue(entry.suggestedValue) }}</pre>
386
396
  </div>
387
397
  </v-list-item>
388
398
  </v-list>
389
399
 
390
- <div v-if="(response.warnings || []).length > 0" class="mt-4">
400
+ <div v-if="(response.warnings || []).length > 0" id="crud-ai-warnings" class="crud-ai-panel__warnings mt-4">
391
401
  <v-alert
392
402
  v-for="warning in response.warnings || []"
393
403
  :key="warning"
404
+ :id="`crud-ai-warning-${warning.replace(/[^a-zA-Z0-9_-]/g, '-')}`"
394
405
  variant="tonal"
395
406
  type="warning"
396
- class="mb-3"
407
+ class="crud-ai-panel__warning mb-3"
397
408
  :text="warning"
398
409
  />
399
410
  </div>
@@ -401,12 +412,14 @@ watch(prompt, () => {
401
412
  </v-card>
402
413
  </v-card-text>
403
414
 
404
- <v-card-actions v-if="response">
415
+ <v-card-actions v-if="response" id="crud-ai-actions" class="crud-ai-panel__actions">
405
416
  <v-spacer />
406
- <v-btn variant="text" @click="expanded = false">
417
+ <v-btn id="crud-ai-cancel-button" class="crud-ai-panel__cancel-button" variant="text" @click="expanded = false">
407
418
  {{ t('action.cancel') }}
408
419
  </v-btn>
409
420
  <v-btn
421
+ id="crud-ai-apply-button"
422
+ class="crud-ai-panel__apply-button"
410
423
  color="primary"
411
424
  variant="flat"
412
425
  :disabled="changedEntries.length === 0"
@@ -109,6 +109,8 @@ defineEmits(['updateValue'])
109
109
 
110
110
  <v-select
111
111
  v-if="noFilter"
112
+ :id="`crud-autocomplete-select-${field.name}`"
113
+ class="crud-autocomplete crud-autocomplete--select"
112
114
  v-model="valueModel"
113
115
  :label="label ? label : field.label"
114
116
  :placeholder="label ? label : field.label"
@@ -143,12 +145,14 @@ defineEmits(['updateValue'])
143
145
  >
144
146
 
145
147
  <template v-if="addOnTheFly" v-slot:append>
146
- <crud-create-on-the-fly-button :entity="entity" @created="onCreated"></crud-create-on-the-fly-button>
148
+ <crud-create-on-the-fly-button :id="`crud-autocomplete-create-${field.name}`" class="crud-autocomplete__create-button" :entity="entity" @created="onCreated"></crud-create-on-the-fly-button>
147
149
  </template>
148
150
 
149
151
  <template v-slot:item="{ props: itemProps, item }">
150
152
  <v-list-item
151
153
  v-bind="itemProps"
154
+ :id="`crud-autocomplete-select-item-${field.name}-${item.raw[itemValue]}`"
155
+ class="crud-autocomplete__item"
152
156
  density="compact"
153
157
  :title="item.raw[itemTitle]"
154
158
  :color="item.raw?.color"
@@ -159,6 +163,8 @@ defineEmits(['updateValue'])
159
163
 
160
164
  <template v-slot:selection="{item}">
161
165
  <v-chip tile density="compact"
166
+ :id="`crud-autocomplete-select-selection-${field.name}-${item.raw[itemValue]}`"
167
+ class="crud-autocomplete__selection-chip"
162
168
  :color="item.raw?.color"
163
169
  :prepend-icon="typeof item.raw?.icon=== 'string' ? item.raw?.icon : null"
164
170
  >
@@ -171,6 +177,8 @@ defineEmits(['updateValue'])
171
177
 
172
178
  <v-autocomplete
173
179
  v-else
180
+ :id="`crud-autocomplete-input-${field.name}`"
181
+ class="crud-autocomplete crud-autocomplete--filterable"
174
182
  v-model="valueModel"
175
183
  :label="label ? label : field.label"
176
184
  :placeholder="label ? label : field.label"
@@ -206,12 +214,14 @@ defineEmits(['updateValue'])
206
214
  >
207
215
 
208
216
  <template v-if="addOnTheFly" v-slot:append>
209
- <crud-create-on-the-fly-button :entity="entity" @created="onCreated"></crud-create-on-the-fly-button>
217
+ <crud-create-on-the-fly-button :id="`crud-autocomplete-create-${field.name}`" class="crud-autocomplete__create-button" :entity="entity" @created="onCreated"></crud-create-on-the-fly-button>
210
218
  </template>
211
219
 
212
220
  <template v-slot:item="{ props: itemProps, item }">
213
221
  <v-list-item
214
222
  v-bind="itemProps"
223
+ :id="`crud-autocomplete-item-${field.name}-${item.raw[itemValue]}`"
224
+ class="crud-autocomplete__item"
215
225
  density="compact"
216
226
  :title="item.raw[itemTitle]"
217
227
  :color="item.raw?.color"
@@ -222,6 +232,8 @@ defineEmits(['updateValue'])
222
232
 
223
233
  <template v-slot:selection="{item}">
224
234
  <v-chip tile density="compact"
235
+ :id="`crud-autocomplete-selection-${field.name}-${item.raw[itemValue]}`"
236
+ class="crud-autocomplete__selection-chip"
225
237
  :color="item.raw?.color"
226
238
  :prepend-icon="typeof item.raw?.icon=== 'string' ? item.raw?.icon : null"
227
239
  >
@@ -32,19 +32,19 @@ const title = computed(() => {
32
32
  </script>
33
33
 
34
34
  <template>
35
- <v-dialog v-model="dialog"
35
+ <v-dialog id="crud-dialog" class="crud-dialog" v-model="dialog"
36
36
  :z-index="entity.dialogZindex"
37
37
  :fullscreen="fullscreen === undefined ? entity.dialogFullscreen : fullscreen"
38
38
  :max-width="maxWidth === undefined ? entity.dialogMaxWidth : maxWidth"
39
39
  >
40
- <v-card>
41
- <v-toolbar>
42
- <v-toolbar-title>{{title}}</v-toolbar-title>
40
+ <v-card id="crud-dialog-card" class="crud-dialog__card">
41
+ <v-toolbar id="crud-dialog-toolbar" class="crud-dialog__toolbar">
42
+ <v-toolbar-title id="crud-dialog-title" class="crud-dialog__title">{{title}}</v-toolbar-title>
43
43
  <v-spacer></v-spacer>
44
44
  <slot name="toolbar-actions"></slot>
45
- <v-btn icon @click="dialog = false"><v-icon>mdi-close</v-icon></v-btn>
45
+ <v-btn id="crud-dialog-close-button" class="crud-dialog__close-button" icon @click="dialog = false"><v-icon>mdi-close</v-icon></v-btn>
46
46
  </v-toolbar>
47
- <v-card-text>
47
+ <v-card-text id="crud-dialog-content" class="crud-dialog__content">
48
48
  <slot></slot>
49
49
  </v-card-text>
50
50
  </v-card>
@@ -17,20 +17,21 @@ const {
17
17
  <template>
18
18
  <v-card
19
19
  v-if="exportListVisible"
20
+ id="crud-export-list"
20
21
  :loading="exportLoading"
21
- class="ma-3" density="compact" variant="outlined" color="secondary"
22
+ class="crud-export-list ma-3" density="compact" variant="outlined" color="secondary"
22
23
  >
23
- <v-card-title>
24
+ <v-card-title id="crud-export-list-title" class="crud-export-list__title">
24
25
  {{ t('action.exports') }}
25
26
 
26
27
  </v-card-title>
27
- <v-card-text>
28
- <v-alert v-if="exportError" type="error">
28
+ <v-card-text id="crud-export-list-content" class="crud-export-list__content">
29
+ <v-alert v-if="exportError" id="crud-export-list-error" class="crud-export-list__error" type="error">
29
30
  {{ t('error.crud.export') }}
30
31
  </v-alert>
31
32
  <template v-else>
32
33
  <slot name="export-table" :exportFiles="exportFiles">
33
- <v-table density="compact">
34
+ <v-table id="crud-export-list-table" class="crud-export-list__table" density="compact">
34
35
  <thead>
35
36
  <tr>
36
37
  <th>Link</th>
@@ -39,10 +40,10 @@ const {
39
40
  </tr>
40
41
  </thead>
41
42
  <tbody>
42
- <tr v-for="exportFile in exportFiles">
43
- <td><a :href="exportFile.url" target="_blank">{{ exportFile.url }}</a></td>
44
- <td>{{ exportFile.rowCount }}</td>
45
- <td>{{ exportFile.time }}</td>
43
+ <tr v-for="(exportFile, index) in exportFiles" :id="`crud-export-list-row-${index}`" class="crud-export-list__row">
44
+ <td class="crud-export-list__url-cell"><a :id="`crud-export-list-link-${index}`" class="crud-export-list__link" :href="exportFile.url" target="_blank">{{ exportFile.url }}</a></td>
45
+ <td class="crud-export-list__row-count-cell">{{ exportFile.rowCount }}</td>
46
+ <td class="crud-export-list__time-cell">{{ exportFile.time }}</td>
46
47
  </tr>
47
48
  </tbody>
48
49
  </v-table>
@@ -50,10 +51,10 @@ const {
50
51
  </template>
51
52
  </v-card-text>
52
53
 
53
- <v-card-actions>
54
+ <v-card-actions id="crud-export-list-actions" class="crud-export-list__actions">
54
55
  <v-spacer></v-spacer>
55
- <v-btn @click="exportFiles = []" :loading="exportLoading">{{ t('action.clear') }}</v-btn>
56
- <v-btn @click="exportListVisible=false">{{ t('action.close') }}</v-btn>
56
+ <v-btn id="crud-export-list-clear-button" class="crud-export-list__clear-button" @click="exportFiles = []" :loading="exportLoading">{{ t('action.clear') }}</v-btn>
57
+ <v-btn id="crud-export-list-close-button" class="crud-export-list__close-button" @click="exportListVisible=false">{{ t('action.close') }}</v-btn>
57
58
  </v-card-actions>
58
59
  </v-card>
59
60
  </template>
@@ -149,9 +149,11 @@ const toInnerIcon = computed(() => {
149
149
  </script>
150
150
 
151
151
  <template>
152
- <v-row dense>
153
- <v-col cols="12" sm="6">
152
+ <v-row :id="`crud-field-range-${name}`" class="crud-field-range" dense>
153
+ <v-col :id="`crud-field-range-from-column-${name}`" class="crud-field-range__from-column" cols="12" sm="6">
154
154
  <v-date-input
155
+ :id="`crud-field-range-from-${name}`"
156
+ class="crud-field-range__from-input"
155
157
  :name="`${name}_from`"
156
158
  :label="`${label} ${t('crud.from')}`"
157
159
  :hint="hint ?? field.hint"
@@ -177,8 +179,10 @@ const toInnerIcon = computed(() => {
177
179
  />
178
180
  </v-col>
179
181
 
180
- <v-col cols="12" sm="6">
182
+ <v-col :id="`crud-field-range-to-column-${name}`" class="crud-field-range__to-column" cols="12" sm="6">
181
183
  <v-date-input
184
+ :id="`crud-field-range-to-${name}`"
185
+ class="crud-field-range__to-input"
182
186
  :name="`${name}_to`"
183
187
  :label="`${label} ${t('crud.to')}`"
184
188
  :hint="hint ?? field.hint"
@@ -204,7 +208,7 @@ const toInnerIcon = computed(() => {
204
208
  @input="onInput"
205
209
  >
206
210
  <template v-if="field.endOfDay && field.showEndOfDayChip !== false" v-slot:append-inner>
207
- <v-chip size="small">23:59</v-chip>
211
+ <v-chip :id="`crud-field-range-end-of-day-${name}`" class="crud-field-range__end-of-day-chip" size="small">23:59</v-chip>
208
212
  </template>
209
213
  </v-date-input>
210
214
  </v-col>
@@ -44,10 +44,12 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
44
44
  </script>
45
45
 
46
46
  <template>
47
- <v-card flat >
48
- <v-row dense class="mt-1">
47
+ <v-card id="crud-filters" class="crud-filters" flat >
48
+ <v-row id="crud-filters-row" class="crud-filters__row mt-1" dense>
49
49
  <v-col v-for="(filter,index) in aFields"
50
50
  :key="filter.name"
51
+ :id="`crud-filter-column-${filter.name}`"
52
+ class="crud-filters__column"
51
53
  :cols="filter.cols ? filter.cols : 12"
52
54
  :sm="filter.sm ? filter.sm : 6"
53
55
  :md="filter.md ? filter.md : 6"
@@ -58,6 +60,8 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
58
60
  <slot :name="`filter.${filter.name}`" v-bind="{filter, filterIndex: index}">
59
61
  <crud-field-range
60
62
  v-if="filter && valueModel[index] !== undefined && isRangeFilter(filter)"
63
+ :id="`crud-filter-range-${filter.name}`"
64
+ class="crud-filters__range-field"
61
65
  :name="filter.name"
62
66
  :label="filter.label"
63
67
  :entity="entity"
@@ -71,6 +75,8 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
71
75
  />
72
76
  <crud-form-field
73
77
  v-else-if="filter && valueModel[index] !== undefined"
78
+ :id="`crud-filter-field-${filter.name}`"
79
+ class="crud-filters__field"
74
80
  :field="filter"
75
81
  :entity="entity"
76
82
  v-model="valueModel[index].value"
@@ -22,10 +22,10 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
22
22
 
23
23
  <template>
24
24
 
25
- <v-card-actions class="pb-0">
25
+ <v-card-actions id="crud-filters-actions" class="crud-filters-actions pb-0">
26
26
  <v-spacer />
27
- <v-btn variant="text" density="compact" :class="entity.cleanFilterClass" @click="clear">{{ t('action.clear') }}</v-btn>
28
- <v-btn variant="flat" density="compact" :class="entity.applyFilterClass" @click="filter">
27
+ <v-btn id="crud-filters-clear-button" variant="text" density="compact" :class="['crud-filters-actions__clear-button', entity.cleanFilterClass]" @click="clear">{{ t('action.clear') }}</v-btn>
28
+ <v-btn id="crud-filters-apply-button" variant="flat" density="compact" :class="['crud-filters-actions__apply-button', entity.applyFilterClass]" @click="filter">
29
29
  {{ t('action.filter') }}
30
30
  </v-btn>
31
31
  </v-card-actions>
@@ -96,14 +96,17 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
96
96
  </script>
97
97
 
98
98
  <template>
99
- <v-row dense class="mt-4">
99
+ <v-row id="crud-dynamic-filters" class="crud-dynamic-filters mt-4" dense>
100
100
  <v-col v-for="(filter,index) in aFields"
101
101
  :key="filter.name"
102
- cols="12" class="tdash"
102
+ :id="`crud-dynamic-filter-${index}`"
103
+ cols="12" class="crud-dynamic-filters__item tdash"
103
104
  >
104
- <v-row >
105
- <v-col cols="12" sm="4">
105
+ <v-row :id="`crud-dynamic-filter-row-${index}`" class="crud-dynamic-filters__item-row">
106
+ <v-col :id="`crud-dynamic-filter-field-column-${index}`" class="crud-dynamic-filters__field-column" cols="12" sm="4">
106
107
  <v-select
108
+ :id="`crud-dynamic-filter-field-select-${index}`"
109
+ class="crud-dynamic-filters__field-select"
107
110
  :items="selectableFields"
108
111
  v-model="getDynamicFilter(index)!.name"
109
112
  :label="t('crud.field')"
@@ -113,8 +116,10 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
113
116
  @update:modelValue="(_v:string) => updateDynamicField(index, true)"
114
117
  />
115
118
  </v-col>
116
- <v-col cols="12" sm="3">
119
+ <v-col :id="`crud-dynamic-filter-operator-column-${index}`" class="crud-dynamic-filters__operator-column" cols="12" sm="3">
117
120
  <v-select
121
+ :id="`crud-dynamic-filter-operator-select-${index}`"
122
+ class="crud-dynamic-filters__operator-select"
118
123
  :items="getDynamicOperations(index)"
119
124
  v-model="getDynamicFilter(index)!.operator"
120
125
  :label="t('crud.operator')"
@@ -124,9 +129,11 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
124
129
  @update:modelValue="(_v:string) => updateDynamicField(index)"
125
130
  />
126
131
  </v-col>
127
- <v-col cols="10" sm="4">
132
+ <v-col :id="`crud-dynamic-filter-value-column-${index}`" class="crud-dynamic-filters__value-column" cols="10" sm="4">
128
133
  <crud-form-field
129
134
  v-if="dynamicValueRequired(index)"
135
+ :id="`crud-dynamic-filter-value-field-${index}`"
136
+ class="crud-dynamic-filters__value-field"
130
137
  :field="filter"
131
138
  :entity="entity"
132
139
  v-model="getDynamicFilter(index)!.value"
@@ -138,10 +145,11 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
138
145
  @updateValue="onUpdateValue"
139
146
  />
140
147
  </v-col>
141
- <v-col cols="2" sm="1">
148
+ <v-col :id="`crud-dynamic-filter-delete-column-${index}`" class="crud-dynamic-filters__delete-column" cols="2" sm="1">
142
149
  <v-btn @click="deleteFilter(index)"
150
+ :id="`crud-dynamic-filter-delete-button-${index}`"
143
151
  icon="mdi-delete"
144
- class="mr-1"
152
+ class="crud-dynamic-filters__delete-button mr-1"
145
153
  variant="text"
146
154
  color="red"
147
155
  >
@@ -152,8 +160,8 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
152
160
 
153
161
  </v-col>
154
162
 
155
- <v-col cols="12">
156
- <v-btn size="small" variant="outlined" color="primary" @click="addFilter">+ {{ t('action.addFilter') }}</v-btn>
163
+ <v-col id="crud-dynamic-filters-add-column" class="crud-dynamic-filters__add-column" cols="12">
164
+ <v-btn id="crud-dynamic-filters-add-button" class="crud-dynamic-filters__add-button" size="small" variant="outlined" color="primary" @click="addFilter">+ {{ t('action.addFilter') }}</v-btn>
157
165
  </v-col>
158
166
 
159
167
  </v-row>