@drax/crud-vue 3.19.0 → 3.20.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 +5 -5
- package/src/components/CrudListGallery.vue +6 -0
- package/src/components/CrudListTable.vue +8 -0
- package/src/components/buttons/CrudSavedQueriesButton.vue +346 -0
- package/src/composables/UseCrudColumns.ts +11 -0
- package/src/cruds/EntityCrud.ts +4 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.20.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@drax/common-front": "^3.19.0",
|
|
28
|
-
"@drax/crud-front": "^3.
|
|
29
|
-
"@drax/crud-share": "^3.
|
|
30
|
-
"@drax/media-vue": "^3.
|
|
28
|
+
"@drax/crud-front": "^3.20.0",
|
|
29
|
+
"@drax/crud-share": "^3.20.0",
|
|
30
|
+
"@drax/media-vue": "^3.20.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": "
|
|
53
|
+
"gitHead": "6d4aea4d05133be679166e398ec6a3ae61503d9e"
|
|
54
54
|
}
|
|
@@ -21,6 +21,7 @@ import {useCrudColumns} from "../composables/UseCrudColumns";
|
|
|
21
21
|
import CrudFiltersDynamic from "./CrudFiltersDynamic.vue";
|
|
22
22
|
import CrudFiltersAction from "./CrudFiltersAction.vue";
|
|
23
23
|
import CrudFilterButton from "./buttons/CrudFilterButton.vue";
|
|
24
|
+
import CrudSavedQueriesButton from "./buttons/CrudSavedQueriesButton.vue";
|
|
24
25
|
|
|
25
26
|
const {t, te} = useI18n()
|
|
26
27
|
const {hasPermission} = useAuth()
|
|
@@ -89,6 +90,11 @@ onMounted(() => {
|
|
|
89
90
|
:entity="entity"
|
|
90
91
|
/>
|
|
91
92
|
|
|
93
|
+
<crud-saved-queries-button
|
|
94
|
+
v-if="entity.isSavedQueriesEnabled"
|
|
95
|
+
:entity="entity"
|
|
96
|
+
/>
|
|
97
|
+
|
|
92
98
|
<slot name="toolbar">
|
|
93
99
|
</slot>
|
|
94
100
|
|
|
@@ -20,6 +20,7 @@ import { useCrudColumns } from "../composables/UseCrudColumns";
|
|
|
20
20
|
import CrudFiltersDynamic from "./CrudFiltersDynamic.vue";
|
|
21
21
|
import CrudFiltersAction from "./CrudFiltersAction.vue";
|
|
22
22
|
import CrudFilterButton from "./buttons/CrudFilterButton.vue";
|
|
23
|
+
import CrudSavedQueriesButton from "./buttons/CrudSavedQueriesButton.vue";
|
|
23
24
|
|
|
24
25
|
const {t, te} = useI18n()
|
|
25
26
|
const {hasPermission} = useAuth()
|
|
@@ -96,6 +97,12 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
|
|
|
96
97
|
|
|
97
98
|
</slot>
|
|
98
99
|
|
|
100
|
+
<crud-saved-queries-button
|
|
101
|
+
v-if="entity.isSavedQueriesEnabled"
|
|
102
|
+
:entity="entity"
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
|
|
99
106
|
<crud-import-button
|
|
100
107
|
:entity="entity"
|
|
101
108
|
@import="(file:any, format:any) => $emit('import', file, format)"
|
|
@@ -119,6 +126,7 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
|
|
|
119
126
|
:entity="entity"
|
|
120
127
|
/>
|
|
121
128
|
|
|
129
|
+
|
|
122
130
|
<slot name="toolbar">
|
|
123
131
|
|
|
124
132
|
</slot>
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {computed, ref, type PropType} from "vue";
|
|
3
|
+
import type {ICrudSavedQuery, IEntityCrud, IEntityCrudFilter, IDraxFieldFilter} from "@drax/crud-share";
|
|
4
|
+
import {useI18n} from "vue-i18n";
|
|
5
|
+
import {useCrudStore} from "../../stores/UseCrudStore";
|
|
6
|
+
import {useCrud} from "../../composables/UseCrud";
|
|
7
|
+
import {useCrudColumns} from "../../composables/UseCrudColumns";
|
|
8
|
+
import {CrudSavedQueryProvider} from "@drax/crud-front";
|
|
9
|
+
import {useAuth, useAuthStore} from "@drax/identity-vue";
|
|
10
|
+
import {createCrudFilterValue} from "../../helpers/CrudRangeFilters";
|
|
11
|
+
|
|
12
|
+
const {t, te} = useI18n();
|
|
13
|
+
const {hasPermission} = useAuth();
|
|
14
|
+
const authStore = useAuthStore();
|
|
15
|
+
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const store = useCrudStore(props.entity.name);
|
|
21
|
+
const {doPaginate} = useCrud(props.entity);
|
|
22
|
+
const {setVisibleColumns} = useCrudColumns(props.entity);
|
|
23
|
+
|
|
24
|
+
const menu = ref(false);
|
|
25
|
+
const saveDialog = ref(false);
|
|
26
|
+
const deleteDialog = ref(false);
|
|
27
|
+
const loading = ref(false);
|
|
28
|
+
const saving = ref(false);
|
|
29
|
+
const deleting = ref(false);
|
|
30
|
+
const savedQueries = ref<ICrudSavedQuery[]>([]);
|
|
31
|
+
const queryToDelete = ref<ICrudSavedQuery | null>(null);
|
|
32
|
+
const form = ref({
|
|
33
|
+
name: "",
|
|
34
|
+
shared: false,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
type UserIdLike = {
|
|
38
|
+
id?: string;
|
|
39
|
+
_id?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const title = computed(() => {
|
|
43
|
+
const key = "crud.savedQueries.title";
|
|
44
|
+
return te(key) ? t(key) : "Saved queries";
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const saveTitle = computed(() => {
|
|
48
|
+
const key = "crud.savedQueries.save";
|
|
49
|
+
return te(key) ? t(key) : "Save query";
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const noQueriesText = computed(() => {
|
|
53
|
+
const key = "crud.savedQueries.empty";
|
|
54
|
+
return te(key) ? t(key) : "No saved queries";
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const deleteTitle = computed(() => {
|
|
58
|
+
const key = "crud.savedQueries.delete";
|
|
59
|
+
return te(key) ? t(key) : "Delete saved query";
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const deleteConfirmText = computed(() => {
|
|
63
|
+
const key = "crud.savedQueries.deleteConfirm";
|
|
64
|
+
return te(key) ? t(key, {name: queryToDelete.value?.name || ""}) : `Delete "${queryToDelete.value?.name || ""}"?`;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const canViewSavedQueries = computed(() => hasPermission("crudSavedQuery:view"));
|
|
68
|
+
const canCreateSavedQueries = computed(() => hasPermission("crudSavedQuery:create"));
|
|
69
|
+
const canDeleteOwnSavedQueries = computed(() => hasPermission("crudSavedQuery:delete"));
|
|
70
|
+
const canDeleteAllSavedQueries = computed(() => hasPermission("crudSavedQuery:all") || hasPermission("crudSavedQuery:deleteAll"));
|
|
71
|
+
|
|
72
|
+
function clone<T>(value: T): T {
|
|
73
|
+
return JSON.parse(JSON.stringify(value));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function entityFilter(): IDraxFieldFilter[] {
|
|
77
|
+
return [{field: "entity", operator: "eq", value: props.entity.name}];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function definedDynamicFilters(): IEntityCrudFilter[] {
|
|
81
|
+
return clone(store.dynamicFilters.filter((filter: IEntityCrudFilter) => filter.name));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildStaticFilters(savedFilters: IDraxFieldFilter[] = []): IDraxFieldFilter[] {
|
|
85
|
+
const savedByField = new Map(savedFilters.map(filter => [filter.field, filter]));
|
|
86
|
+
|
|
87
|
+
return props.entity.filters.map(filter => {
|
|
88
|
+
const savedFilter = savedByField.get(filter.name);
|
|
89
|
+
return {
|
|
90
|
+
field: filter.name,
|
|
91
|
+
operator: savedFilter?.operator || filter.operator || "eq",
|
|
92
|
+
value: savedFilter ? savedFilter.value : createCrudFilterValue(filter)
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function queryUserId(query: ICrudSavedQuery): string | undefined {
|
|
98
|
+
const user = query.user;
|
|
99
|
+
if (!user) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
if (typeof user === "string") {
|
|
103
|
+
return user;
|
|
104
|
+
}
|
|
105
|
+
const id = user._id || user.id;
|
|
106
|
+
return id ? String(id) : undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function currentUserId(): string | undefined {
|
|
110
|
+
const authUser = authStore.authUser as UserIdLike | null;
|
|
111
|
+
const id = authUser?.id || authUser?._id;
|
|
112
|
+
return id ? String(id) : undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function canDeleteQuery(query: ICrudSavedQuery): boolean {
|
|
116
|
+
if (canDeleteAllSavedQueries.value) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (!canDeleteOwnSavedQueries.value) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const ownerId = queryUserId(query);
|
|
124
|
+
return !ownerId || ownerId === currentUserId();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function loadQueries() {
|
|
128
|
+
loading.value = true;
|
|
129
|
+
try {
|
|
130
|
+
savedQueries.value = await CrudSavedQueryProvider.instance.find({
|
|
131
|
+
limit: 100,
|
|
132
|
+
orderBy: "name",
|
|
133
|
+
order: "asc",
|
|
134
|
+
filters: entityFilter()
|
|
135
|
+
});
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("Error loading saved queries", error);
|
|
138
|
+
savedQueries.value = [];
|
|
139
|
+
} finally {
|
|
140
|
+
loading.value = false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function openSaveDialog() {
|
|
145
|
+
form.value = {name: "", shared: false};
|
|
146
|
+
saveDialog.value = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function saveQuery() {
|
|
150
|
+
if (!form.value.name.trim()) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
saving.value = true;
|
|
155
|
+
try {
|
|
156
|
+
await CrudSavedQueryProvider.instance.create({
|
|
157
|
+
entity: props.entity.name,
|
|
158
|
+
name: form.value.name.trim(),
|
|
159
|
+
shared: form.value.shared,
|
|
160
|
+
columns: clone(store.visibleColumns),
|
|
161
|
+
staticFilters: clone(store.filters),
|
|
162
|
+
dynamicFilters: definedDynamicFilters(),
|
|
163
|
+
});
|
|
164
|
+
saveDialog.value = false;
|
|
165
|
+
await loadQueries();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error("Error saving query", error);
|
|
168
|
+
} finally {
|
|
169
|
+
saving.value = false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function openDeleteDialog(query: ICrudSavedQuery) {
|
|
174
|
+
queryToDelete.value = query;
|
|
175
|
+
deleteDialog.value = true;
|
|
176
|
+
menu.value = false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function deleteQuery() {
|
|
180
|
+
if (!queryToDelete.value) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
deleting.value = true;
|
|
185
|
+
try {
|
|
186
|
+
await CrudSavedQueryProvider.instance.delete(queryToDelete.value._id);
|
|
187
|
+
deleteDialog.value = false;
|
|
188
|
+
queryToDelete.value = null;
|
|
189
|
+
await loadQueries();
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error("Error deleting saved query", error);
|
|
192
|
+
} finally {
|
|
193
|
+
deleting.value = false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function applyQuery(query: ICrudSavedQuery) {
|
|
198
|
+
setVisibleColumns(query.columns || []);
|
|
199
|
+
store.setFilters(buildStaticFilters(clone(query.staticFilters || [])));
|
|
200
|
+
const dynamicFilters = clone(query.dynamicFilters || []);
|
|
201
|
+
store.setDynamicFilters(dynamicFilters);
|
|
202
|
+
store.setDynamicFiltersEnable(dynamicFilters.length > 0);
|
|
203
|
+
store.setPage(1);
|
|
204
|
+
menu.value = false;
|
|
205
|
+
await doPaginate();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function onMenuUpdate(value: boolean) {
|
|
209
|
+
menu.value = value;
|
|
210
|
+
if (value) {
|
|
211
|
+
loadQueries();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
</script>
|
|
215
|
+
|
|
216
|
+
<template>
|
|
217
|
+
<v-menu
|
|
218
|
+
v-if="canViewSavedQueries"
|
|
219
|
+
:model-value="menu"
|
|
220
|
+
offset-y
|
|
221
|
+
:close-on-content-click="false"
|
|
222
|
+
@update:model-value="onMenuUpdate"
|
|
223
|
+
>
|
|
224
|
+
<template #activator="{ props: activatorProps }">
|
|
225
|
+
<v-btn
|
|
226
|
+
v-bind="activatorProps"
|
|
227
|
+
icon
|
|
228
|
+
variant="text"
|
|
229
|
+
>
|
|
230
|
+
<v-icon>mdi-content-save-cog</v-icon>
|
|
231
|
+
<v-tooltip activator="parent" location="bottom">
|
|
232
|
+
{{ title }}
|
|
233
|
+
</v-tooltip>
|
|
234
|
+
</v-btn>
|
|
235
|
+
</template>
|
|
236
|
+
|
|
237
|
+
<v-list min-width="280">
|
|
238
|
+
<v-list-subheader>{{ title }}</v-list-subheader>
|
|
239
|
+
|
|
240
|
+
<v-list-item
|
|
241
|
+
v-if="canCreateSavedQueries"
|
|
242
|
+
@click="openSaveDialog"
|
|
243
|
+
>
|
|
244
|
+
<template #prepend>
|
|
245
|
+
<v-icon>mdi-content-save-outline</v-icon>
|
|
246
|
+
</template>
|
|
247
|
+
<v-list-item-title>{{ saveTitle }}</v-list-item-title>
|
|
248
|
+
</v-list-item>
|
|
249
|
+
|
|
250
|
+
<v-divider />
|
|
251
|
+
|
|
252
|
+
<v-list-item v-if="loading">
|
|
253
|
+
<v-progress-linear indeterminate />
|
|
254
|
+
</v-list-item>
|
|
255
|
+
|
|
256
|
+
<v-list-item v-else-if="savedQueries.length === 0">
|
|
257
|
+
<v-list-item-title class="text-medium-emphasis">{{ noQueriesText }}</v-list-item-title>
|
|
258
|
+
</v-list-item>
|
|
259
|
+
|
|
260
|
+
<v-list-item
|
|
261
|
+
v-for="query in savedQueries"
|
|
262
|
+
:key="query._id"
|
|
263
|
+
@click="applyQuery(query)"
|
|
264
|
+
>
|
|
265
|
+
<template #prepend>
|
|
266
|
+
<v-icon>{{ query.shared ? "mdi-account-group-outline" : "mdi-account-outline" }}</v-icon>
|
|
267
|
+
</template>
|
|
268
|
+
<v-list-item-title>{{ query.name }}</v-list-item-title>
|
|
269
|
+
<template #append>
|
|
270
|
+
<v-btn
|
|
271
|
+
v-if="canDeleteQuery(query)"
|
|
272
|
+
icon
|
|
273
|
+
variant="text"
|
|
274
|
+
color="red"
|
|
275
|
+
size="small"
|
|
276
|
+
@click.stop="openDeleteDialog(query)"
|
|
277
|
+
>
|
|
278
|
+
<v-icon>mdi-delete</v-icon>
|
|
279
|
+
<v-tooltip activator="parent" location="bottom">
|
|
280
|
+
{{ te('action.delete') ? t('action.delete') : 'Delete' }}
|
|
281
|
+
</v-tooltip>
|
|
282
|
+
</v-btn>
|
|
283
|
+
</template>
|
|
284
|
+
</v-list-item>
|
|
285
|
+
</v-list>
|
|
286
|
+
</v-menu>
|
|
287
|
+
|
|
288
|
+
<v-dialog v-model="saveDialog" max-width="460">
|
|
289
|
+
<v-card>
|
|
290
|
+
<v-card-title>{{ saveTitle }}</v-card-title>
|
|
291
|
+
<v-card-text>
|
|
292
|
+
<v-text-field
|
|
293
|
+
v-model="form.name"
|
|
294
|
+
:label="te('crud.savedQueries.name') ? t('crud.savedQueries.name') : 'Name'"
|
|
295
|
+
density="compact"
|
|
296
|
+
variant="outlined"
|
|
297
|
+
autofocus
|
|
298
|
+
/>
|
|
299
|
+
<v-switch
|
|
300
|
+
v-model="form.shared"
|
|
301
|
+
:label="te('crud.savedQueries.shared') ? t('crud.savedQueries.shared') : 'Shared'"
|
|
302
|
+
color="primary"
|
|
303
|
+
hide-details
|
|
304
|
+
/>
|
|
305
|
+
</v-card-text>
|
|
306
|
+
<v-card-actions>
|
|
307
|
+
<v-spacer />
|
|
308
|
+
<v-btn variant="text" @click="saveDialog = false">
|
|
309
|
+
{{ te('action.cancel') ? t('action.cancel') : 'Cancel' }}
|
|
310
|
+
</v-btn>
|
|
311
|
+
<v-btn
|
|
312
|
+
color="primary"
|
|
313
|
+
variant="flat"
|
|
314
|
+
:loading="saving"
|
|
315
|
+
:disabled="!form.name.trim()"
|
|
316
|
+
@click="saveQuery"
|
|
317
|
+
>
|
|
318
|
+
{{ te('action.save') ? t('action.save') : 'Save' }}
|
|
319
|
+
</v-btn>
|
|
320
|
+
</v-card-actions>
|
|
321
|
+
</v-card>
|
|
322
|
+
</v-dialog>
|
|
323
|
+
|
|
324
|
+
<v-dialog v-model="deleteDialog" max-width="460">
|
|
325
|
+
<v-card>
|
|
326
|
+
<v-card-title>{{ deleteTitle }}</v-card-title>
|
|
327
|
+
<v-card-text>
|
|
328
|
+
{{ deleteConfirmText }}
|
|
329
|
+
</v-card-text>
|
|
330
|
+
<v-card-actions>
|
|
331
|
+
<v-spacer />
|
|
332
|
+
<v-btn variant="text" @click="deleteDialog = false">
|
|
333
|
+
{{ te('action.cancel') ? t('action.cancel') : 'Cancel' }}
|
|
334
|
+
</v-btn>
|
|
335
|
+
<v-btn
|
|
336
|
+
color="red"
|
|
337
|
+
variant="flat"
|
|
338
|
+
:loading="deleting"
|
|
339
|
+
@click="deleteQuery"
|
|
340
|
+
>
|
|
341
|
+
{{ te('action.delete') ? t('action.delete') : 'Delete' }}
|
|
342
|
+
</v-btn>
|
|
343
|
+
</v-card-actions>
|
|
344
|
+
</v-card>
|
|
345
|
+
</v-dialog>
|
|
346
|
+
</template>
|
|
@@ -158,6 +158,16 @@ export function useCrudColumns(entity: IEntityCrud) {
|
|
|
158
158
|
saveColumnsToStorage(defaultColumns)
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
const setVisibleColumns = (columns: string[]) => {
|
|
162
|
+
const availableHeaders = entity.headers
|
|
163
|
+
.filter(header => !header.permission || hasPermission(header.permission))
|
|
164
|
+
.map(header => header.key)
|
|
165
|
+
|
|
166
|
+
const validColumns = columns.filter(key => availableHeaders.includes(key))
|
|
167
|
+
crudStore.setVisibleColumns(validColumns)
|
|
168
|
+
saveColumnsToStorage(validColumns)
|
|
169
|
+
}
|
|
170
|
+
|
|
161
171
|
return {
|
|
162
172
|
visibleColumns,
|
|
163
173
|
translatedHeaders,
|
|
@@ -170,5 +180,6 @@ export function useCrudColumns(entity: IEntityCrud) {
|
|
|
170
180
|
selectAll,
|
|
171
181
|
deselectAll,
|
|
172
182
|
resetToDefault,
|
|
183
|
+
setVisibleColumns,
|
|
173
184
|
}
|
|
174
185
|
}
|
package/src/cruds/EntityCrud.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import CrudFiltersAction from "./components/CrudFiltersAction.vue";
|
|
|
12
12
|
import CrudNotify from "./components/CrudNotify.vue";
|
|
13
13
|
import CrudSearch from "./components/CrudSearch.vue";
|
|
14
14
|
import CrudAutocomplete from "./components/CrudAutocomplete.vue";
|
|
15
|
+
import CrudSavedQueriesButton from "./components/buttons/CrudSavedQueriesButton.vue";
|
|
15
16
|
import EntityCombobox from "./components/combobox/EntityCombobox.vue";
|
|
16
17
|
import {useCrudStore} from "./stores/UseCrudStore";
|
|
17
18
|
import {useEntityStore} from "./stores/UseEntityStore";
|
|
@@ -35,6 +36,7 @@ export {
|
|
|
35
36
|
CrudNotify,
|
|
36
37
|
CrudSearch,
|
|
37
38
|
CrudAutocomplete,
|
|
39
|
+
CrudSavedQueriesButton,
|
|
38
40
|
CrudFilters,
|
|
39
41
|
CrudFiltersAction,
|
|
40
42
|
useCrud,
|