@drax/crud-vue 2.6.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.6.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": "57830b50c2c9081b9d7e236d441ed9b2a3aca0ba"
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
  }
@@ -303,11 +312,11 @@ class EntityCrud implements IEntityCrud {
303
312
  }
304
313
 
305
314
  get inputVariantView(): IEntityCrudFieldVariant {
306
- return 'filled'
315
+ return 'solo-filled'
307
316
  }
308
317
 
309
318
  get inputVariantDelete() : IEntityCrudFieldVariant{
310
- return 'underlined'
319
+ return 'solo-filled'
311
320
  }
312
321
 
313
322
 
@@ -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>
@@ -3,7 +3,7 @@ import {useI18n} from "vue-i18n";
3
3
  import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
4
4
  import {getItemId} from "../helpers/getItemId";
5
5
  import CrudFormField from "./CrudFormField.vue";
6
- import {computed, defineEmits, defineProps, ref} from "vue";
6
+ import {computed, ref} from "vue";
7
7
  import type {PropType} from "vue";
8
8
  import {useCrudStore} from "../stores/UseCrudStore";
9
9
  import {useCrud} from "../composables/UseCrud";
@@ -132,6 +132,10 @@ const rules = computed(() => {
132
132
  }
133
133
  })
134
134
 
135
+ const onlyView = computed(()=> {
136
+ return ['delete','view'].includes(operation?.value)
137
+ })
138
+
135
139
  </script>
136
140
 
137
141
  <template>
@@ -158,12 +162,13 @@ const rules = computed(() => {
158
162
  :xl="field.xl ? field.xl : undefined"
159
163
  >
160
164
  <slot :name="`field.${field.name}`" v-bind="{field}">
165
+
161
166
  <crud-form-field
162
167
  :field="field"
163
168
  :entity="entity"
164
169
  v-model="form[field.name]"
165
170
  :clearable="false"
166
- :readonly="['delete','view'].includes(operation) || field.readonly"
171
+ :readonly="onlyView || field.readonly"
167
172
  :variant="variant"
168
173
  :prepend-inner-icon="field?.prependInnerIcon"
169
174
  :prepend-icon="field?.prependIcon"
@@ -206,12 +211,15 @@ const rules = computed(() => {
206
211
  :xl="field.xl ? field.xl : undefined"
207
212
  >
208
213
  <slot :name="`field.${field.name}`" v-bind="{field}">
214
+
215
+
216
+
209
217
  <crud-form-field
210
218
  :field="field"
211
219
  :entity="entity"
212
220
  v-model="form[field.name]"
213
221
  :clearable="false"
214
- :readonly="['delete','view'].includes(operation) || field.readonly"
222
+ :readonly="onlyView || field.readonly"
215
223
  :variant="variant"
216
224
  :prepend-inner-icon="field?.prependInnerIcon"
217
225
  :prepend-icon="field?.prependIcon"