@drax/crud-vue 2.7.0 → 2.8.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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.7.0",
6
+ "version": "2.8.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,9 +24,9 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^2.6.0",
27
+ "@drax/common-front": "^2.8.0",
28
28
  "@drax/crud-front": "^2.0.0",
29
- "@drax/crud-share": "^2.4.0",
29
+ "@drax/crud-share": "^2.8.0",
30
30
  "@drax/media-vue": "^2.2.1"
31
31
  },
32
32
  "peerDependencies": {
@@ -50,5 +50,5 @@
50
50
  "vue-tsc": "^3.2.4",
51
51
  "vuetify": "^3.11.8"
52
52
  },
53
- "gitHead": "6914eb5bfd532fb5e510b95c7d17b5b2ada8b07d"
53
+ "gitHead": "3adeb31ee60eb83c92137dc28162f9226cab06c1"
54
54
  }
package/src/EntityCrud.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import type {
2
2
  IEntityCrud, IEntityCrudForm, IEntityCrudHeader, IEntityCrudRefs,
3
3
  IEntityCrudRules, IEntityCrudField, IEntityCrudPermissions,
4
- IDraxCrudProvider, IEntityCrudFilter, IEntityCrudFormFilter, IEntityCrudFieldVariant
4
+ IDraxCrudProvider, IEntityCrudFilter, IEntityCrudFieldVariant, IDraxFieldFilter
5
5
  } from "@drax/crud-share";
6
6
 
7
7
 
8
+
8
9
  class EntityCrud implements IEntityCrud {
9
10
 
10
11
  name: string = ''
@@ -125,11 +126,11 @@ class EntityCrud implements IEntityCrud {
125
126
  }
126
127
 
127
128
 
128
- get formFilters(): IEntityCrudFormFilter[] {
129
+ get formFilters(): IDraxFieldFilter[] {
129
130
  return this.filters.map(
130
131
  (filter: IEntityCrudFilter) =>
131
132
  ({field: filter.name, value: filter.default ? filter.default : null, operator: (filter.operator ? filter.operator : 'eq')})
132
- ) as IEntityCrudFormFilter[]
133
+ ) as IDraxFieldFilter[]
133
134
  }
134
135
 
135
136
  get refs(): IEntityCrudRefs {
@@ -138,7 +139,7 @@ class EntityCrud implements IEntityCrud {
138
139
 
139
140
  getRef(ref: string): IEntityCrud {
140
141
  if (!this.refs.hasOwnProperty(ref)) {
141
- throw new Error("Ref not found: " + ref)
142
+ throw new Error("Ref not found: " + ref + " Refs Available: " + Object.getOwnPropertyNames(this.refs).join(", "))
142
143
  }
143
144
 
144
145
  return this.refs[ref] as IEntityCrud
@@ -238,6 +239,14 @@ class EntityCrud implements IEntityCrud {
238
239
  return true
239
240
  }
240
241
 
242
+ get filtersEnable(){
243
+ return true
244
+ }
245
+
246
+ get dynamicFiltersEnable(){
247
+ return true
248
+ }
249
+
241
250
  get filterButtons() {
242
251
  return true
243
252
  }
@@ -1,11 +1,13 @@
1
1
  <script setup lang="ts">
2
- import {onBeforeMount, type PropType} from "vue";
2
+ import {computed, onBeforeMount, type PropType} from "vue";
3
3
  import type {IEntityCrud} from "@drax/crud-share";
4
- import CrudList from "./CrudList.vue";
4
+ import CrudListTable from "./CrudListTable.vue";
5
+ import CrudListGallery from "./CrudListGallery.vue";
5
6
  import CrudForm from "./CrudForm.vue";
6
7
  import CrudNotify from "./CrudNotify.vue";
7
8
  import CrudDialog from "./CrudDialog.vue";
8
9
  import {useCrud} from "../composables/UseCrud";
10
+ import {useDisplay} from 'vuetify'
9
11
 
10
12
  const {entity} = defineProps({
11
13
  entity: {type: Object as PropType<IEntityCrud>, required: true},
@@ -24,6 +26,22 @@ onBeforeMount(() => {
24
26
 
25
27
  const emit = defineEmits(['created', 'updated', 'deleted', 'viewed', 'canceled'])
26
28
 
29
+ const listComponent = computed(() => {
30
+ const listMode = entity?.listMode
31
+ switch (listMode){
32
+ case 'responsive':
33
+ return xs.value ? CrudListGallery : CrudListTable
34
+ case 'gallery':
35
+ return CrudListGallery
36
+ case 'table':
37
+ return CrudListTable
38
+ default:
39
+ return xs.value ? CrudListGallery : CrudListTable
40
+ }
41
+ })
42
+
43
+ const {xs} = useDisplay()
44
+
27
45
 
28
46
  </script>
29
47
 
@@ -31,7 +49,8 @@ const emit = defineEmits(['created', 'updated', 'deleted', 'viewed', 'canceled']
31
49
  <v-container :fluid="entity.containerFluid" class="mt-5">
32
50
  <v-card :class="entity.cardClass" :density="entity.cardDensity">
33
51
 
34
- <crud-list
52
+ <component
53
+ :is="listComponent"
35
54
  :entity="entity"
36
55
  @create="onCreate"
37
56
  @edit="onEdit"
@@ -66,13 +85,18 @@ const emit = defineEmits(['created', 'updated', 'deleted', 'viewed', 'canceled']
66
85
  </slot>
67
86
  </template>
68
87
 
88
+ <template v-slot:item="{item}">
89
+ <slot name="item" v-bind="{item}">
90
+ </slot>
91
+ </template>
92
+
69
93
 
70
94
  <template v-slot:item.actions="{item}">
71
95
  <slot name="item.actions" v-bind="{item}">
72
96
  </slot>
73
97
  </template>
74
98
 
75
- </crud-list>
99
+ </component>
76
100
  </v-card>
77
101
 
78
102
  <crud-dialog
@@ -3,7 +3,7 @@ import { computed, type PropType } from 'vue'
3
3
  import type { IEntityCrud } from '@drax/crud-share'
4
4
  import { useCrudStore } from '../stores/UseCrudStore'
5
5
  import { useI18n } from 'vue-i18n'
6
- import { useFilterIcon } from '../composables/useFilterIcon'
6
+ import { useFilterIcon } from '../composables/UseFilterIcon'
7
7
  import CrudRefDisplay from "./CrudRefDisplay.vue";
8
8
  import {formatDate} from "@drax/common-front"
9
9
 
@@ -92,7 +92,7 @@ const removeFilter = (index: number) => {
92
92
  const filterDef = props.entity.filters[index]
93
93
 
94
94
  // Resetear al valor por defecto
95
- filter.value = filterDef.default
95
+ filter.value = filterDef?.default
96
96
 
97
97
  // Emitir evento para aplicar filtros
98
98
  emit('filterRemoved')
@@ -146,14 +146,14 @@ defineEmits(['updateValue'])
146
146
  :title="item.raw[itemTitle]"
147
147
  :color="item.raw?.color"
148
148
  :base-color="item.raw?.color"
149
- :prepend-icon="item.raw?.icon"
149
+ :prepend-icon="typeof item.raw?.icon=== 'string' ? item.raw?.icon : null"
150
150
  />
151
151
  </template>
152
152
 
153
153
  <template v-slot:selection="{item}">
154
154
  <v-chip tile density="compact"
155
155
  :color="item.raw?.color"
156
- :prepend-icon="item.raw?.icon"
156
+ :prepend-icon="typeof item.raw?.icon=== 'string' ? item.raw?.icon : null"
157
157
  >
158
158
  {{ item.raw[itemTitle] }}
159
159
  </v-chip>
@@ -203,14 +203,14 @@ defineEmits(['updateValue'])
203
203
  :title="item.raw[itemTitle]"
204
204
  :color="item.raw?.color"
205
205
  :base-color="item.raw?.color"
206
- :prepend-icon="item.raw?.icon"
206
+ :prepend-icon="typeof item.raw?.icon=== 'string' ? item.raw?.icon : null"
207
207
  />
208
208
  </template>
209
209
 
210
210
  <template v-slot:selection="{item}">
211
211
  <v-chip tile density="compact"
212
212
  :color="item.raw?.color"
213
- :prepend-icon="item.raw?.icon"
213
+ :prepend-icon="typeof item.raw?.icon=== 'string' ? item.raw?.icon : null"
214
214
  >
215
215
  {{ item.raw[itemTitle] }}
216
216
  </v-chip>
@@ -2,18 +2,16 @@
2
2
  import {computed, type PropType} from "vue";
3
3
  import CrudFormField from "./CrudFormField.vue";
4
4
  import type {IEntityCrud, IEntityCrudFilter} from "@drax/crud-share";
5
- import {useI18n} from "vue-i18n";
6
5
  import {useAuth} from "@drax/identity-vue";
7
- import {useFilterIcon} from "../composables/useFilterIcon";
6
+ import {useFilterIcon} from "../composables/UseFilterIcon";
8
7
 
9
- const {t} = useI18n()
10
8
  const valueModel = defineModel({type: [Object]})
11
9
  const {hasPermission} = useAuth()
12
10
  const {filterIcon} = useFilterIcon()
13
11
 
14
- const {entity, actionButtons} = defineProps({
12
+ const {entity, autoFilter} = defineProps({
15
13
  entity: {type: Object as PropType<IEntityCrud>, required: true},
16
- actionButtons: {type: Boolean, default: false},
14
+ autoFilter: {type: Boolean, default: false}
17
15
  })
18
16
 
19
17
  const aFields = computed(() => {
@@ -24,13 +22,15 @@ function filter() {
24
22
  emit('applyFilter')
25
23
  }
26
24
 
25
+ /*
27
26
  function clear() {
28
27
  emit('clearFilter')
29
28
  }
29
+ */
30
30
 
31
31
  function onUpdateValue(){
32
- if(!actionButtons){
33
- emit('applyFilter')
32
+ if(autoFilter){
33
+ filter()
34
34
  }
35
35
  }
36
36
 
@@ -69,14 +69,6 @@ const emit = defineEmits(['applyFilter', 'clearFilter'])
69
69
 
70
70
  </v-row>
71
71
 
72
- <v-card-actions v-if="actionButtons" class="pb-0">
73
- <v-spacer />
74
- <v-btn variant="text" density="compact" :class="entity.cleanFilterClass" @click="clear">{{ t('action.clear') }}</v-btn>
75
- <v-btn variant="flat" density="compact" :class="entity.applyFilterClass" @click="filter">
76
- {{ t('action.filter') }}
77
- </v-btn>
78
- </v-card-actions>
79
-
80
72
  </v-card>
81
73
  </template>
82
74
 
@@ -0,0 +1,37 @@
1
+ <script setup lang="ts">
2
+ import {useI18n} from "vue-i18n";
3
+ import type {PropType} from "vue";
4
+ import type {IEntityCrud} from "@drax/crud-share";
5
+ const {t} = useI18n()
6
+
7
+ const {entity} = defineProps({
8
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
9
+ })
10
+
11
+ function filter() {
12
+ emit('applyFilter')
13
+ }
14
+
15
+ function clear() {
16
+ emit('clearFilter')
17
+ }
18
+
19
+ const emit = defineEmits(['applyFilter', 'clearFilter'])
20
+
21
+ </script>
22
+
23
+ <template>
24
+
25
+ <v-card-actions class="pb-0">
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">
29
+ {{ t('action.filter') }}
30
+ </v-btn>
31
+ </v-card-actions>
32
+
33
+ </template>
34
+
35
+ <style scoped>
36
+
37
+ </style>
@@ -0,0 +1,204 @@
1
+ <script setup lang="ts">
2
+ import {computed, type PropType} from "vue";
3
+ import CrudFormField from "./CrudFormField.vue";
4
+ import type {IEntityCrud, IEntityCrudFilter} from "@drax/crud-share";
5
+ import {useI18n} from "vue-i18n";
6
+ import {useAuth} from "@drax/identity-vue";
7
+ import {useFilterIcon} from "../composables/UseFilterIcon";
8
+ import {useCrudStore} from "../stores/UseCrudStore";
9
+ import {useEntityStore} from "../stores/UseEntityStore";
10
+
11
+ const {t, te} = useI18n()
12
+ const valueModel = defineModel({type: [Object]})
13
+ const {hasPermission} = useAuth()
14
+ const {filterIcon} = useFilterIcon()
15
+
16
+
17
+ const {entity, autoFilter} = defineProps({
18
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
19
+ autoFilter: {type: Boolean, default: false}
20
+ })
21
+
22
+ const store = useCrudStore(entity?.name)
23
+ const entityStore = useEntityStore();
24
+ const storeEntity = entityStore.entities.find((e: any) => e.name === entity.name)
25
+
26
+ const aFields = computed(() => {
27
+ return store.dynamicFilters
28
+ .filter((field: IEntityCrudFilter) => !field.permission || hasPermission(field.permission))
29
+ })
30
+
31
+ const dynamicFilter = computed(() => {
32
+ return (index: string | number) => {
33
+ return store.dynamicFilters[index]
34
+ }
35
+ })
36
+
37
+ function filter() {
38
+ emit('applyFilter')
39
+ }
40
+
41
+ /*
42
+ function clear() {
43
+ emit('clearFilter')
44
+ }
45
+ */
46
+
47
+ function onUpdateValue() {
48
+ if (autoFilter) {
49
+ filter()
50
+ }
51
+ }
52
+
53
+ const fieldI18n = computed(() => {
54
+ return (field: IEntityCrudFilter) => {
55
+ return te(entity.name.toLowerCase() + ".field." + field.name) ? t(entity.name.toLowerCase() + ".field." + field.name) : field.label
56
+ }
57
+ })
58
+
59
+ const selectableFields = computed(() => {
60
+ return storeEntity ? storeEntity.fields
61
+ .filter((f: any) => !['fullFile', 'object', 'array.object'].includes(f.type))
62
+ .map((f: any) => ({title: fieldI18n.value(f), value: f.name})) : []
63
+ })
64
+
65
+ function normalizeFieldType(type: string) :string{
66
+ if (type === 'array.ref') return 'ref';
67
+ if (type === 'array.string') return 'string';
68
+ if (type === 'longString') return 'string';
69
+ if (type === 'array.number') return 'number';
70
+ if (type === 'array.enum') return 'enum';
71
+ return type;
72
+ }
73
+
74
+ function onUpdateField(index: string | number, val: string){
75
+
76
+ const field = storeEntity.fields.find((e: any) => e.name === val)
77
+ dynamicFilter.value(index).value = null
78
+
79
+ if (!field) return
80
+
81
+ if(field.ref){
82
+ dynamicFilter.value(index).ref = field.ref
83
+ }
84
+ if(field.refDisplay){
85
+ dynamicFilter.value(index).refDisplay = field.refDisplay
86
+ }
87
+ if(field.enum){
88
+ dynamicFilter.value(index).enum = field.enum
89
+ }
90
+ if(field.type){
91
+ dynamicFilter.value(index).type = normalizeFieldType(field.type)
92
+
93
+ if(field.type === 'boolean'){
94
+ dynamicFilter.value(index).value = false
95
+ }
96
+
97
+ }
98
+ }
99
+
100
+ const operations = [
101
+ {title: t('operation.equals'), value: 'eq'},
102
+ {title: t('operation.notEquals'), value: 'ne'},
103
+ {title: t('operation.contains'), value: 'like'},
104
+ {title: t('operation.greaterThan'), value: 'gt'},
105
+ {title: t('operation.lessThan'), value: 'lt'},
106
+ {title: t('operation.greaterThanOrEqual'), value: 'gte'},
107
+ {title: t('operation.lessThanOrEqual'), value: 'lte'},
108
+ // {title: t('operation.in'), value: 'in'},
109
+ // {title: t('operation.notIn'), value: 'nin'},
110
+ ]
111
+
112
+ function removeFilter(index: string | number){
113
+ store.removeDynamicFilter(index)
114
+ }
115
+
116
+ function addFilter() {
117
+ const filter: IEntityCrudFilter = {
118
+ default: undefined,
119
+ label: "",
120
+ name: '',
121
+ operator: 'eq',
122
+ type: 'string',
123
+ permission: '',
124
+ value: ''
125
+ }
126
+ store.addDynamicFilter(filter)
127
+ }
128
+
129
+
130
+ const emit = defineEmits(['applyFilter', 'clearFilter'])
131
+
132
+ </script>
133
+
134
+ <template>
135
+ <v-row dense class="mt-4">
136
+ <v-col v-for="(filter,index) in aFields"
137
+ :key="filter.name"
138
+ cols="12" class="tdash"
139
+ >
140
+ <v-row >
141
+ <v-col cols="12" sm="4">
142
+ <v-select
143
+ :items="selectableFields"
144
+ v-model="dynamicFilter(index).name"
145
+ :label="t('crud.field')"
146
+ density="compact"
147
+ variant="outlined"
148
+ hide-details
149
+ @update:modelValue="(v:string) => onUpdateField(index, v)"
150
+ />
151
+ </v-col>
152
+ <v-col cols="12" sm="3">
153
+ <v-select
154
+ :items="operations"
155
+ v-model="dynamicFilter(index).operator"
156
+ :label="t('crud.operator')"
157
+ density="compact"
158
+ variant="outlined"
159
+ hide-details
160
+ />
161
+ </v-col>
162
+ <v-col cols="10" sm="4">
163
+ <crud-form-field
164
+ :field="filter"
165
+ :entity="entity"
166
+ v-model="dynamicFilter(index).value"
167
+ :clearable="true"
168
+ density="compact"
169
+ variant="outlined"
170
+ :prepend-inner-icon="filterIcon(filter)"
171
+ hide-details
172
+ @updateValue="onUpdateValue"
173
+ />
174
+ </v-col>
175
+ <v-col cols="2" sm="1">
176
+ <v-btn @click="removeFilter(index)"
177
+ icon="mdi-delete"
178
+ class="mr-1"
179
+ variant="text"
180
+ color="red"
181
+ >
182
+ </v-btn>
183
+ </v-col>
184
+ </v-row>
185
+
186
+
187
+ </v-col>
188
+
189
+ <v-col cols="12">
190
+ <v-btn size="small" variant="outlined" color="primary" @click="addFilter">+ {{ t('action.addFilter') }}</v-btn>
191
+ </v-col>
192
+
193
+ </v-row>
194
+
195
+ </template>
196
+
197
+ <style scoped>
198
+ .tdash{
199
+ border-bottom-width: 0.5px;
200
+ border-bottom-color: lightgray;
201
+ border-bottom-style: dashed;
202
+ margin-bottom: 10px;
203
+ }
204
+ </style>
@@ -16,6 +16,8 @@ import type {IEntityCrud} from "@drax/crud-share";
16
16
  import {useI18n} from "vue-i18n";
17
17
  import CrudFilters from "./CrudFilters.vue";
18
18
  import { useCrudColumns } from "../composables/UseCrudColumns";
19
+ import CrudFiltersDynamic from "./CrudFiltersDynamic.vue";
20
+ import CrudFiltersAction from "./CrudFiltersAction.vue";
19
21
 
20
22
  const {t, te} = useI18n()
21
23
  const {hasPermission} = useAuth()
@@ -133,25 +135,45 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
133
135
  <v-card-text class="pt-0">
134
136
  <slot name="filters" v-bind="{filters}"></slot>
135
137
 
136
- <crud-filters
137
- v-if="!$slots.filters"
138
- :entity="entity"
139
- v-model="filters"
140
- :action-buttons="entity.filterButtons"
141
- @clearFilter="clearFilters()"
142
- @applyFilter="applyFilters()"
143
- >
144
-
145
- <template v-for="iFilter in entity.filters"
146
- :key="iFilter.name"
147
- v-slot:[`filter.${iFilter.name}`]="{filter, filterIndex}"
138
+ <v-card variant="flat" v-if="!$slots.filters">
139
+
140
+ <crud-filters
141
+ v-if="entity.filtersEnable"
142
+ :entity="entity"
143
+ v-model="filters"
144
+ :auto-filter="!entity.filterButtons"
145
+ @clearFilter="clearFilters()"
146
+ @applyFilter="applyFilters()"
147
+ >
148
+
149
+ <template v-for="iFilter in entity.filters"
150
+ :key="iFilter.name"
151
+ v-slot:[`filter.${iFilter.name}`]="{filter, filterIndex}"
152
+ >
153
+ <slot v-if="$slots[`filter.${iFilter.name}`]"
154
+ :name="`filter.${iFilter.name}`"
155
+ v-bind="{filter, filterIndex}"
156
+ />
157
+ </template>
158
+ </crud-filters>
159
+
160
+ <crud-filters-dynamic
161
+ v-if="entity.dynamicFiltersEnable"
162
+ :entity="entity"
163
+ v-model="filters"
164
+ :auto-filter="!entity.filterButtons"
165
+ @clearFilter="clearFilters()"
166
+ @applyFilter="applyFilters()"
148
167
  >
149
- <slot v-if="$slots[`filter.${iFilter.name}`]"
150
- :name="`filter.${iFilter.name}`"
151
- v-bind="{filter, filterIndex}"
152
- />
153
- </template>
154
- </crud-filters>
168
+ </crud-filters-dynamic>
169
+
170
+ <crud-filters-action v-if="entity.filterButtons"
171
+ :entity="entity"
172
+ @clearFilter="clearFilters()"
173
+ @applyFilter="applyFilters()"
174
+ ></crud-filters-action>
175
+ </v-card>
176
+
155
177
  </v-card-text>
156
178
 
157
179
  </v-card>
@@ -0,0 +1,300 @@
1
+ <script setup lang="ts">
2
+ import type {PropType} from 'vue'
3
+ import {onMounted} from 'vue'
4
+ import {useAuth} from '@drax/identity-vue'
5
+ import CrudSearch from "./CrudSearch.vue";
6
+ import {useCrud} from "../composables/UseCrud";
7
+ import CrudExportButton from "./buttons/CrudExportButton.vue";
8
+ import CrudImportButton from "./buttons/CrudImportButton.vue";
9
+ import CrudCreateButton from "./buttons/CrudCreateButton.vue";
10
+ import CrudUpdateButton from "./buttons/CrudUpdateButton.vue";
11
+ import CrudDeleteButton from "./buttons/CrudDeleteButton.vue";
12
+ import CrudViewButton from "./buttons/CrudViewButton.vue";
13
+ import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
14
+ import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
15
+ import CrudExportList from "./CrudExportList.vue";
16
+ import type {IEntityCrud} from "@drax/crud-share";
17
+ import {useI18n} from "vue-i18n";
18
+ import CrudFilters from "./CrudFilters.vue";
19
+ import {useCrudColumns} from "../composables/UseCrudColumns";
20
+ import CrudFiltersDynamic from "./CrudFiltersDynamic.vue";
21
+ import CrudFiltersAction from "./CrudFiltersAction.vue";
22
+
23
+ const {t, te} = useI18n()
24
+ const {hasPermission} = useAuth()
25
+
26
+ const {entity} = defineProps({
27
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
28
+ })
29
+
30
+ const {
31
+ loading, itemsPerPage, page, search, totalItems, items,
32
+ doPaginate, filters, applyFilters, clearFilters, paginationError
33
+ } = useCrud(entity)
34
+
35
+ // Usar el composable de columnas
36
+ const {filteredHeaders} = useCrudColumns(entity)
37
+
38
+
39
+ defineExpose({
40
+ doPaginate
41
+ });
42
+
43
+ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
44
+
45
+ onMounted(() => {
46
+ doPaginate()
47
+ });
48
+
49
+ </script>
50
+
51
+ <template>
52
+ <div v-if="hasPermission(entity.permissions.view)" class="d-flex flex-column h-100 pb-4">
53
+ <!-- Toolbar -->
54
+ <v-toolbar :class="entity.toolbarClass" :density="entity.toolbarDensity">
55
+ <v-toolbar-title>
56
+ {{ te(`${entity.name.toLowerCase()}.crud`) ? t(`${entity.name.toLowerCase()}.crud`) : entity.name }}
57
+ </v-toolbar-title>
58
+ <v-spacer></v-spacer>
59
+
60
+ <slot name="toolbar">
61
+ </slot>
62
+
63
+ <crud-import-button
64
+ :entity="entity"
65
+ @import="(v:any) => $emit('import', v)"
66
+ />
67
+
68
+ <crud-export-button
69
+ :entity="entity"
70
+ @export="(v:any) => $emit('export',v)"
71
+ />
72
+
73
+ <crud-group-by-button
74
+ v-if="entity.isGroupable"
75
+ :entity="entity"
76
+ />
77
+
78
+ <crud-columns-button
79
+ v-if="entity.isColumnSelectable"
80
+ :entity="entity"
81
+ />
82
+
83
+ <crud-create-button
84
+ v-if="entity.isCreatable"
85
+ :entity="entity"
86
+ @click="$emit('create')"
87
+ />
88
+ </v-toolbar>
89
+
90
+ <crud-export-list
91
+ :entity="entity"
92
+ />
93
+
94
+ <v-card variant="flat">
95
+ <v-card-text v-if="entity.searchEnable">
96
+ <crud-search
97
+ v-model="search"
98
+ />
99
+ </v-card-text>
100
+
101
+ <v-card-text class="pt-0">
102
+ <slot name="filters" v-bind="{filters}"></slot>
103
+
104
+ <v-card variant="flat" v-if="!$slots.filters">
105
+
106
+ <crud-filters
107
+ v-if="entity.filtersEnable"
108
+ :entity="entity"
109
+ v-model="filters"
110
+ :auto-filter="!entity.filterButtons"
111
+ @clearFilter="clearFilters()"
112
+ @applyFilter="applyFilters()"
113
+ >
114
+
115
+ <template v-for="iFilter in entity.filters"
116
+ :key="iFilter.name"
117
+ v-slot:[`filter.${iFilter.name}`]="{filter, filterIndex}"
118
+ >
119
+ <slot v-if="$slots[`filter.${iFilter.name}`]"
120
+ :name="`filter.${iFilter.name}`"
121
+ v-bind="{filter, filterIndex}"
122
+ />
123
+ </template>
124
+ </crud-filters>
125
+
126
+ <crud-filters-dynamic
127
+ v-if="entity.dynamicFiltersEnable"
128
+ :entity="entity"
129
+ v-model="filters"
130
+ :auto-filter="!entity.filterButtons"
131
+ @clearFilter="clearFilters()"
132
+ @applyFilter="applyFilters()"
133
+ >
134
+ </crud-filters-dynamic>
135
+
136
+ <crud-filters-action v-if="entity.filterButtons"
137
+ :entity="entity"
138
+ @clearFilter="clearFilters()"
139
+ @applyFilter="applyFilters()"
140
+ ></crud-filters-action>
141
+ </v-card>
142
+
143
+ </v-card-text>
144
+
145
+ </v-card>
146
+
147
+ <v-divider></v-divider>
148
+
149
+ <!-- CONTENT GALLERY -->
150
+ <v-container fluid class="flex-grow-1 position-relative pa-4">
151
+ <v-overlay :model-value="loading" contained class="align-center justify-center bg-transparent" scrim="transparent"
152
+ persistent z-index="4">
153
+ <v-progress-circular indeterminate color="primary"></v-progress-circular>
154
+ </v-overlay>
155
+
156
+ <v-alert
157
+ v-if="paginationError"
158
+ variant="tonal"
159
+ class="w-100 mb-4"
160
+ prominent
161
+ type="error"
162
+ :text="te(paginationError) ? t(paginationError) : paginationError"
163
+ />
164
+ <v-alert v-else-if="!loading && items.length === 0" variant="tonal" class="w-100 mb-4" type="info"
165
+ :text="te('crud.noData') ? t('crud.noData') : 'No data'"/>
166
+
167
+ <!-- GALLERY GRIDS -->
168
+ <v-row v-if="items.length > 0">
169
+ <v-col v-for="item in items" :key="item.id || item.uuid || item.name || Math.random()" cols="12" sm="6" md="4"
170
+ xl="3">
171
+
172
+
173
+ <v-card class="h-100 d-flex flex-column hover-card" elevation="2" border>
174
+ <slot name="item" v-bind="{item}">
175
+ <v-card-text class="field-grid">
176
+ <template v-for="header in filteredHeaders.filter(h => h.key !=='actions')" :key="header.key">
177
+
178
+ <div class="field-label font-weight-regular text-grey-darken-2">
179
+ {{
180
+ te(`${entity.name.toLowerCase()}.${header.key}`) ? t(`${entity.name.toLowerCase()}.${header.key}`) : (header.title || header.key)
181
+ }}
182
+ </div>
183
+
184
+ <div class="field-value font-weight-medium">
185
+ <slot v-if="$slots[`item.${header.key}`]" :name="`item.${header.key}`"
186
+ v-bind="{item, value: item[header.key]}">
187
+ {{ item[header.key] }}
188
+ </slot>
189
+ <template v-else>
190
+ {{ item[header.key] }}
191
+ </template>
192
+ </div>
193
+
194
+ </template>
195
+ </v-card-text>
196
+ </slot>
197
+
198
+ <v-divider></v-divider>
199
+
200
+ <v-card-actions class="bg-grey-lighten-4 py-2 px-4 d-flex justify-end flex-wrap gap-2">
201
+ <slot name="item.actions" v-bind="{item}">
202
+ </slot>
203
+
204
+ <crud-view-button
205
+ v-if="entity.isViewable && hasPermission(entity.permissions.view)"
206
+ @click="$emit('view', item)"
207
+ />
208
+
209
+ <crud-update-button
210
+ v-if="entity.isEditable && entity.isItemEditable(item) && hasPermission(entity.permissions?.update)"
211
+ @click="$emit('edit', item)"
212
+ />
213
+
214
+ <crud-delete-button
215
+ v-if="entity.isDeletable && hasPermission(entity.permissions?.delete)"
216
+ @click="$emit('delete', item)"
217
+ />
218
+ </v-card-actions>
219
+ </v-card>
220
+ </v-col>
221
+ </v-row>
222
+ </v-container>
223
+
224
+ <!-- FOOTER WITH ALIGNED PAGINATION -->
225
+ <v-divider></v-divider>
226
+ <div :class="['d-flex align-center justify-space-between flex-wrap px-4 py-3 bg-surface', entity.footerClass]">
227
+ <div class="d-flex align-center mb-2 mb-sm-0">
228
+ <span class="text-body-2 mr-2 text-white">{{
229
+ te('crud.itemsPerPage') ? t('crud.itemsPerPage') : 'Items per page:'
230
+ }}</span>
231
+ <v-select
232
+ v-model="itemsPerPage"
233
+ :items="[5, 10, 20, 50]"
234
+ variant="outlined"
235
+ density="compact"
236
+ hide-details
237
+ class="pagination-select"
238
+ @update:model-value="doPaginate"
239
+ ></v-select>
240
+ </div>
241
+
242
+ <v-pagination
243
+ v-model="page"
244
+ :length="Math.ceil(totalItems / itemsPerPage) || 1"
245
+ :total-visible="5"
246
+ density="comfortable"
247
+ active-color="primary"
248
+ @update:model-value="doPaginate"
249
+ ></v-pagination>
250
+ </div>
251
+ </div>
252
+ </template>
253
+
254
+ <style scoped>
255
+ .hover-card {
256
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
257
+ }
258
+
259
+ .hover-card:hover {
260
+ transform: translateY(-4px);
261
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12) !important;
262
+ }
263
+
264
+ .gap-2 {
265
+ gap: 8px;
266
+ }
267
+
268
+ .pagination-select {
269
+ width: 90px;
270
+ }
271
+
272
+ .truncate-label {
273
+ white-space: nowrap;
274
+ min-width: fit-content;
275
+ }
276
+
277
+ .field-grid {
278
+ display: grid;
279
+ grid-template-columns: max-content 1fr;
280
+ column-gap: 14px;
281
+ row-gap: 8px;
282
+ align-items: start;
283
+ }
284
+
285
+ .field-label {
286
+ text-align: right;
287
+ white-space: nowrap;
288
+ }
289
+
290
+ .field-value {
291
+ text-align: left;
292
+ word-break: break-word;
293
+ }
294
+
295
+ .field-label::after {
296
+ content: ":";
297
+ margin-left: 6px;
298
+ }
299
+
300
+ </style>
@@ -0,0 +1,222 @@
1
+ <script setup lang="ts">
2
+ import type {PropType} from 'vue'
3
+ import {useAuth} from '@drax/identity-vue'
4
+ import CrudSearch from "./CrudSearch.vue";
5
+ import {useCrud} from "../composables/UseCrud";
6
+ import CrudExportButton from "./buttons/CrudExportButton.vue";
7
+ import CrudImportButton from "./buttons/CrudImportButton.vue";
8
+ import CrudCreateButton from "./buttons/CrudCreateButton.vue";
9
+ import CrudUpdateButton from "./buttons/CrudUpdateButton.vue";
10
+ import CrudDeleteButton from "./buttons/CrudDeleteButton.vue";
11
+ import CrudViewButton from "./buttons/CrudViewButton.vue";
12
+ import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
13
+ import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
14
+ import CrudExportList from "./CrudExportList.vue";
15
+ import type {IEntityCrud} from "@drax/crud-share";
16
+ import {useI18n} from "vue-i18n";
17
+ import CrudFilters from "./CrudFilters.vue";
18
+ import { useCrudColumns } from "../composables/UseCrudColumns";
19
+ import CrudFiltersDynamic from "./CrudFiltersDynamic.vue";
20
+ import CrudFiltersAction from "./CrudFiltersAction.vue";
21
+
22
+ const {t, te} = useI18n()
23
+ const {hasPermission} = useAuth()
24
+
25
+ const {entity} = defineProps({
26
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
27
+ })
28
+
29
+ const {
30
+ loading, itemsPerPage, page, sortBy, search, totalItems, items,
31
+ doPaginate, filters, applyFilters, clearFilters, paginationError
32
+ } = useCrud(entity)
33
+
34
+ // Usar el composable de columnas
35
+ const { filteredHeaders } = useCrudColumns(entity)
36
+
37
+
38
+ defineExpose({
39
+ doPaginate
40
+ });
41
+
42
+ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
43
+
44
+ </script>
45
+
46
+ <template>
47
+ <v-data-table-server
48
+ :density="entity.tableDensity"
49
+ :striped="entity.tableStriped"
50
+ :header-props="entity.headerProps"
51
+ v-if="hasPermission(entity.permissions.view)"
52
+ v-model:items-per-page="itemsPerPage"
53
+ :items-per-page-options="[5, 10, 20, 50]"
54
+ v-model:page="page"
55
+ v-model:sort-by="sortBy"
56
+ :headers="filteredHeaders"
57
+ :items="items"
58
+ :items-length="totalItems"
59
+ :loading="loading"
60
+ :search="search"
61
+ :multi-sort="false"
62
+ item-value="name"
63
+ @update:options="doPaginate"
64
+ >
65
+
66
+ <template v-slot:no-data>
67
+ <v-alert
68
+ v-if="paginationError"
69
+ variant="tonal"
70
+ class="w-100 ma-2"
71
+ style="width: 100%; min-width: 100%"
72
+ prominent
73
+ type="error"
74
+ :text="te(paginationError) ? t(paginationError) : paginationError"
75
+ />
76
+ <v-alert v-else variant="tonal" class="w-100 ma-2 " type="info" :text="te('crud.noData') ? t('crud.noData') : 'No data' " />
77
+ </template>
78
+
79
+ <template v-slot:bottom>
80
+ <v-data-table-footer :class="entity.footerClass"
81
+ :items-per-page-options="[5, 10, 20, 50]"
82
+ ></v-data-table-footer>
83
+ </template>
84
+
85
+ <template v-slot:top>
86
+ <v-toolbar :class="entity.toolbarClass" :density="entity.toolbarDensity">
87
+ <v-toolbar-title>
88
+ {{ te(`${entity.name.toLowerCase()}.crud`) ? t(`${entity.name.toLowerCase()}.crud`) : entity.name }}
89
+ </v-toolbar-title>
90
+ <v-spacer></v-spacer>
91
+
92
+ <slot name="toolbar">
93
+
94
+ </slot>
95
+
96
+ <crud-import-button
97
+ :entity="entity"
98
+ @import="(v:any) => $emit('import', v)"
99
+ />
100
+
101
+ <crud-export-button
102
+ :entity="entity"
103
+ @export="(v:any) => $emit('export',v)"
104
+ />
105
+
106
+ <crud-group-by-button
107
+ v-if="entity.isGroupable"
108
+ :entity="entity"
109
+ />
110
+
111
+ <crud-columns-button
112
+ v-if="entity.isColumnSelectable"
113
+ :entity="entity"
114
+ />
115
+
116
+ <crud-create-button
117
+ v-if="entity.isCreatable"
118
+ :entity="entity"
119
+ @click="$emit('create')"
120
+ />
121
+
122
+ </v-toolbar>
123
+
124
+ <crud-export-list
125
+ :entity="entity"
126
+ />
127
+
128
+ <v-card variant="flat">
129
+ <v-card-text v-if="entity.searchEnable">
130
+ <crud-search
131
+ v-model="search"
132
+ />
133
+ </v-card-text>
134
+
135
+ <v-card-text class="pt-0">
136
+ <slot name="filters" v-bind="{filters}"></slot>
137
+
138
+ <v-card variant="flat" v-if="!$slots.filters">
139
+
140
+ <crud-filters
141
+ v-if="entity.filtersEnable"
142
+ :entity="entity"
143
+ v-model="filters"
144
+ :auto-filter="!entity.filterButtons"
145
+ @clearFilter="clearFilters()"
146
+ @applyFilter="applyFilters()"
147
+ >
148
+
149
+ <template v-for="iFilter in entity.filters"
150
+ :key="iFilter.name"
151
+ v-slot:[`filter.${iFilter.name}`]="{filter, filterIndex}"
152
+ >
153
+ <slot v-if="$slots[`filter.${iFilter.name}`]"
154
+ :name="`filter.${iFilter.name}`"
155
+ v-bind="{filter, filterIndex}"
156
+ />
157
+ </template>
158
+ </crud-filters>
159
+
160
+ <crud-filters-dynamic
161
+ v-if="entity.dynamicFiltersEnable"
162
+ :entity="entity"
163
+ v-model="filters"
164
+ :auto-filter="!entity.filterButtons"
165
+ @clearFilter="clearFilters()"
166
+ @applyFilter="applyFilters()"
167
+ >
168
+ </crud-filters-dynamic>
169
+
170
+ <crud-filters-action v-if="entity.filterButtons"
171
+ :entity="entity"
172
+ @clearFilter="clearFilters()"
173
+ @applyFilter="applyFilters()"
174
+ ></crud-filters-action>
175
+ </v-card>
176
+
177
+ </v-card-text>
178
+
179
+ </v-card>
180
+
181
+ <v-divider></v-divider>
182
+
183
+
184
+
185
+ </template>
186
+
187
+
188
+ <template v-for="header in entity.headers" :key="header.key" v-slot:[`item.${header.key}`]="{item, value}">
189
+ <slot v-if="$slots[`item.${header.key}`]" :name="`item.${header.key}`" v-bind="{item, value}">
190
+ {{ value }}
191
+ </slot>
192
+ </template>
193
+
194
+
195
+ <template v-slot:item.actions="{item}">
196
+
197
+ <slot name="item.actions" v-bind="{item}">
198
+ </slot>
199
+
200
+ <crud-view-button
201
+ v-if="entity.isViewable && hasPermission(entity.permissions.view)"
202
+ @click="$emit('view', item)"
203
+ />
204
+
205
+ <crud-update-button
206
+ v-if="entity.isEditable && entity.isItemEditable(item) && hasPermission(entity.permissions?.update)"
207
+ @click="$emit('edit', item)"
208
+ />
209
+
210
+ <crud-delete-button
211
+ v-if="entity.isDeletable && hasPermission(entity.permissions?.delete)"
212
+ @click="$emit('delete', item)"
213
+ />
214
+
215
+ </template>
216
+
217
+ </v-data-table-server>
218
+ </template>
219
+
220
+ <style scoped>
221
+
222
+ </style>
@@ -1,13 +1,22 @@
1
- import type {IDraxPaginateResult, IEntityCrud} from "@drax/crud-share";
1
+ import type {
2
+ IDraxFieldFilter,
3
+ IDraxPaginateResult,
4
+ IEntityCrud,
5
+ IEntityCrudFilter,
6
+ } from "@drax/crud-share";
2
7
  import {useCrudStore} from "../stores/UseCrudStore";
3
8
  import {computed, nextTick, toRaw} from "vue";
4
9
  import getItemId from "../helpers/getItemId";
5
10
  import {useI18n} from "vue-i18n";
11
+ import {useRouter} from "vue-router";
12
+ import type {IEntityCrudFilterDynamic} from "@drax/crud-share/types/interfaces/IEntityCrudFilterDynamic";
6
13
 
7
14
  export function useCrud(entity: IEntityCrud) {
8
15
 
9
16
  const store = useCrudStore(entity?.name)
10
17
 
18
+ const router = useRouter();
19
+
11
20
  const {t: $t, te: $te} = useI18n()
12
21
 
13
22
  const exportError = computed({
@@ -33,6 +42,7 @@ export function useCrud(entity: IEntityCrud) {
33
42
  store.setDialog(value)
34
43
  }
35
44
  })
45
+
36
46
  const operation = computed({
37
47
  get() {
38
48
  return store.operation
@@ -40,6 +50,7 @@ export function useCrud(entity: IEntityCrud) {
40
50
  store.setOperation(value)
41
51
  }
42
52
  })
53
+
43
54
  const form = computed({
44
55
  get() {
45
56
  return store.form
@@ -47,6 +58,7 @@ export function useCrud(entity: IEntityCrud) {
47
58
  store.setForm(value)
48
59
  }
49
60
  })
61
+
50
62
  const formValid = computed({
51
63
  get() {
52
64
  return store.formValid
@@ -54,6 +66,7 @@ export function useCrud(entity: IEntityCrud) {
54
66
  store.setFormValid(value)
55
67
  }
56
68
  })
69
+
57
70
  const notify = computed({
58
71
  get() {
59
72
  return store.notify
@@ -61,6 +74,7 @@ export function useCrud(entity: IEntityCrud) {
61
74
  store.setNotify(value)
62
75
  }
63
76
  })
77
+
64
78
  const error = computed({
65
79
  get() {
66
80
  return store.error
@@ -68,6 +82,7 @@ export function useCrud(entity: IEntityCrud) {
68
82
  store.setError(value)
69
83
  }
70
84
  })
85
+
71
86
  const message = computed({
72
87
  get() {
73
88
  return store.message
@@ -75,6 +90,7 @@ export function useCrud(entity: IEntityCrud) {
75
90
  store.setMessage(value)
76
91
  }
77
92
  })
93
+
78
94
  const loading = computed({
79
95
  get() {
80
96
  return store.loading
@@ -82,6 +98,7 @@ export function useCrud(entity: IEntityCrud) {
82
98
  store.setLoading(value)
83
99
  }
84
100
  })
101
+
85
102
  const itemsPerPage = computed({
86
103
  get() {
87
104
  return store.itemsPerPage
@@ -89,6 +106,7 @@ export function useCrud(entity: IEntityCrud) {
89
106
  store.setItemsPerPage(value)
90
107
  }
91
108
  })
109
+
92
110
  const page = computed({
93
111
  get() {
94
112
  return store.page
@@ -96,6 +114,7 @@ export function useCrud(entity: IEntityCrud) {
96
114
  store.setPage(value)
97
115
  }
98
116
  })
117
+
99
118
  const sortBy = computed({
100
119
  get() {
101
120
  return store.sortBy
@@ -103,6 +122,7 @@ export function useCrud(entity: IEntityCrud) {
103
122
  store.setSortBy(value)
104
123
  }
105
124
  })
125
+
106
126
  const search = computed({
107
127
  get() {
108
128
  return store.search
@@ -110,6 +130,7 @@ export function useCrud(entity: IEntityCrud) {
110
130
  store.setSearch(value)
111
131
  }
112
132
  })
133
+
113
134
  const totalItems = computed({
114
135
  get() {
115
136
  return store.totalItems
@@ -117,6 +138,7 @@ export function useCrud(entity: IEntityCrud) {
117
138
  store.setTotalItems(value)
118
139
  }
119
140
  })
141
+
120
142
  const items = computed({
121
143
  get() {
122
144
  return store.items
@@ -124,6 +146,7 @@ export function useCrud(entity: IEntityCrud) {
124
146
  store.setItems(value)
125
147
  }
126
148
  })
149
+
127
150
  const exportFiles = computed({
128
151
  get() {
129
152
  return store.exportFiles
@@ -131,6 +154,7 @@ export function useCrud(entity: IEntityCrud) {
131
154
  store.setExportFiles(value)
132
155
  }
133
156
  })
157
+
134
158
  const exportLoading = computed({
135
159
  get() {
136
160
  return store.exportLoading
@@ -138,6 +162,7 @@ export function useCrud(entity: IEntityCrud) {
138
162
  store.setExportLoading(value)
139
163
  }
140
164
  })
165
+
141
166
  const exportListVisible = computed({
142
167
  get() {
143
168
  return store.exportListVisible
@@ -145,6 +170,7 @@ export function useCrud(entity: IEntityCrud) {
145
170
  store.setExportListVisible(value)
146
171
  }
147
172
  })
173
+
148
174
  const filters = computed({
149
175
  get() {
150
176
  return store.filters
@@ -153,6 +179,24 @@ export function useCrud(entity: IEntityCrud) {
153
179
  }
154
180
  })
155
181
 
182
+ const prepareDynamicFilters = computed(() => {
183
+ return store.dynamicFilters.map((filter: IEntityCrudFilterDynamic) => {
184
+ return {
185
+ field: filter.name,
186
+ operator: filter.operator,
187
+ value: filter.value
188
+ }
189
+ }) as IDraxFieldFilter[]
190
+ })
191
+
192
+
193
+ const getAllFilters = computed(() =>{
194
+ return [
195
+ ...store.filters,
196
+ ...prepareDynamicFilters.value
197
+ ] as IDraxFieldFilter[]
198
+ })
199
+
156
200
 
157
201
  async function doPaginate() {
158
202
  store.setLoading(true)
@@ -165,7 +209,7 @@ export function useCrud(entity: IEntityCrud) {
165
209
  orderBy: store.sortBy[0]?.key,
166
210
  order: store.sortBy[0]?.order,
167
211
  search: store.search,
168
- filters: store.filters
212
+ filters: getAllFilters.value
169
213
  })
170
214
  store.setItems(r.items)
171
215
  store.setTotalItems(r.total)
@@ -204,7 +248,7 @@ export function useCrud(entity: IEntityCrud) {
204
248
  orderBy: store.sortBy[0]?.key,
205
249
  order: store.sortBy[0]?.order,
206
250
  search: store.search,
207
- filters: store.filters
251
+ filters: getAllFilters.value
208
252
  })
209
253
 
210
254
  if (r && r.url) {
@@ -325,6 +369,9 @@ export function useCrud(entity: IEntityCrud) {
325
369
  await doPaginate()
326
370
  closeDialog()
327
371
  store.showMessage("Entity created successfully!")
372
+ if(entity.redirectOnCreate){
373
+ router.push(entity.redirectOnCreate(item))
374
+ }
328
375
  return {status: 'created', item: item}
329
376
  }
330
377
  throw new Error("provider.create not implemented")
@@ -395,7 +442,17 @@ export function useCrud(entity: IEntityCrud) {
395
442
  }
396
443
 
397
444
  function prepareFilters() {
398
- store.setFilters(entity.formFilters)
445
+
446
+ const staticFilters : IDraxFieldFilter[] = entity.filters.map(
447
+ (filter: IEntityCrudFilter) =>
448
+ ({
449
+ field: filter.name,
450
+ value: filter.default ? filter.default : null,
451
+ operator: (filter.operator ? filter.operator : 'eq')
452
+ })
453
+ ) as IDraxFieldFilter[]
454
+
455
+ store.setFilters(staticFilters)
399
456
  }
400
457
 
401
458
  async function clearFilters() {
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import EntityCombobox from "./components/combobox/EntityCombobox.vue";
12
12
  import {useCrudStore} from "./stores/UseCrudStore";
13
13
  import {useEntityStore} from "./stores/UseEntityStore";
14
14
  import {useCrud} from "./composables/UseCrud";
15
+ import {useFilterIcon} from "./composables/UseFilterIcon";
15
16
  import {useFormUtils} from "./composables/UseFormUtils";
16
17
  import {useInputErrorI18n} from "./composables/UseInputErrorI18n";
17
18
  import {EntityCrud} from "./EntityCrud";
@@ -32,6 +33,7 @@ export {
32
33
  useFormUtils,
33
34
  useCrudStore,
34
35
  useInputErrorI18n,
36
+ useFilterIcon,
35
37
  EntityCrud,
36
38
  useEntityStore,
37
39
  EntityCombobox
@@ -13,6 +13,7 @@ export const useCrudStore = (id: string = 'entity') => defineStore('CrudStore'+i
13
13
  error: '' as string,
14
14
  paginationError: '' as string,
15
15
  filters: [] as IDraxFieldFilter[],
16
+ dynamicFilters: [] as IDraxFieldFilter[],
16
17
  items: [] as any[],
17
18
  totalItems: 0 as number,
18
19
  itemsPerPage: 10 as number,
@@ -134,6 +135,21 @@ export const useCrudStore = (id: string = 'entity') => defineStore('CrudStore'+i
134
135
  this.filters[index].value = value
135
136
  }
136
137
  },
138
+ addDynamicFilter(filter: IDraxFieldFilter) {
139
+ this.dynamicFilters.push(filter)
140
+ },
141
+ removeDynamicFilter(index: number) {
142
+ this.dynamicFilters.splice(index, 1)
143
+ },
144
+ setDynamicFilters(filters: IDraxFieldFilter[]) {
145
+ this.dynamicFilters = filters
146
+ },
147
+ setDynamicFilterValue(name:string, value:any) {
148
+ const index = this.getFilterIndex(name)
149
+ if (index >= 0) {
150
+ this.dynamicFilters[index].value = value
151
+ }
152
+ },
137
153
  setVisibleColumns(columns: string[]) {
138
154
  this.visibleColumns = columns
139
155
  },