@drax/crud-vue 3.10.0 → 3.13.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": "3.10.0",
6
+ "version": "3.13.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,10 +24,10 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^3.0.0",
28
- "@drax/crud-front": "^3.0.0",
29
- "@drax/crud-share": "^3.10.0",
30
- "@drax/media-vue": "^3.10.0"
27
+ "@drax/common-front": "^3.11.0",
28
+ "@drax/crud-front": "^3.11.0",
29
+ "@drax/crud-share": "^3.13.0",
30
+ "@drax/media-vue": "^3.13.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": "063b6aba11e891c5374c2d3de6546a3ff120b4c9"
53
+ "gitHead": "5f68eefbcb01c876471e387a815fee1040489c2c"
54
54
  }
@@ -15,8 +15,8 @@ const {entity} = defineProps({
15
15
 
16
16
  const {
17
17
  onView, onCreate, onEdit, onDelete, resetCrudStore,
18
- operation, dialog, notify, message, doExport,
19
- prepareFilters
18
+ operation, dialog, notify, message, doExport, doImport,
19
+ prepareFilters, form
20
20
  } = useCrud(entity);
21
21
 
22
22
  onBeforeMount(() => {
@@ -56,6 +56,7 @@ const {xs} = useDisplay()
56
56
  @edit="onEdit"
57
57
  @delete="onDelete"
58
58
  @export="doExport"
59
+ @import="doImport"
59
60
  @view="onView"
60
61
  >
61
62
 
@@ -112,6 +113,11 @@ const {xs} = useDisplay()
112
113
  </slot>
113
114
  </template>
114
115
 
116
+ <template v-slot:import-table="{ importFiles }">
117
+ <slot name="import-table" :importFiles="importFiles">
118
+ </slot>
119
+ </template>
120
+
115
121
  </component>
116
122
  </v-card>
117
123
 
@@ -124,7 +130,8 @@ const {xs} = useDisplay()
124
130
 
125
131
  </slot>
126
132
 
127
- <slot name="form">
133
+ <slot name="form" v-bind="{form, operation}">
134
+
128
135
  <crud-form
129
136
  :entity="entity"
130
137
  @created="item => emit('created', item)"
@@ -0,0 +1,72 @@
1
+ <script setup lang="ts">
2
+ import {useCrud} from "../composables/UseCrud";
3
+ import type {PropType} from "vue";
4
+ import type {IEntityCrud} from "@drax/crud-share";
5
+ import {useI18n} from "vue-i18n";
6
+
7
+ const {t} = useI18n()
8
+ const {entity} = defineProps({
9
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
10
+ })
11
+
12
+ const {
13
+ importFiles, importListVisible, importLoading, importError
14
+ } = useCrud(entity);
15
+
16
+ </script>
17
+
18
+ <template>
19
+ <v-card
20
+ v-if="importListVisible"
21
+ :loading="importLoading"
22
+ class="ma-3" density="compact" variant="outlined" color="secondary"
23
+ >
24
+ <v-card-title>
25
+ {{ t('action.imports') }}
26
+ </v-card-title>
27
+ <v-card-text>
28
+ <v-alert v-if="importError" type="error">
29
+ {{ t('error.crud.import') }}
30
+ </v-alert>
31
+ <template v-else>
32
+ <slot name="import-table" :importFiles="importFiles">
33
+ <v-table density="compact">
34
+ <thead>
35
+ <tr>
36
+ <th> {{t('crud.import.link')}}</th>
37
+ <th> {{t('crud.import.rows')}}</th>
38
+ <th> {{t('crud.import.success')}}</th>
39
+ <th> {{t('crud.import.error')}}</th>
40
+ <th> {{t('crud.import.time')}}</th>
41
+ </tr>
42
+ </thead>
43
+ <tbody>
44
+ <tr v-for="importFile in importFiles">
45
+ <td>
46
+ <a v-if="importFile.url" :href="importFile.url" target="_blank">
47
+ {{t('crud.import.report')}}
48
+ </a>
49
+ <span v-else>-</span>
50
+ </td>
51
+ <td>{{ importFile.rowCount }}</td>
52
+ <td>{{ importFile.successCount ?? '-' }}</td>
53
+ <td>{{ importFile.errorCount ?? '-' }}</td>
54
+ <td>{{ importFile.time }}</td>
55
+ </tr>
56
+ </tbody>
57
+ </v-table>
58
+ </slot>
59
+ </template>
60
+ </v-card-text>
61
+
62
+ <v-card-actions>
63
+ <v-spacer></v-spacer>
64
+ <v-btn @click="importFiles = []" :loading="importLoading">{{ t('action.clear') }}</v-btn>
65
+ <v-btn @click="importListVisible = false">{{ t('action.close') }}</v-btn>
66
+ </v-card-actions>
67
+ </v-card>
68
+ </template>
69
+
70
+ <style scoped>
71
+
72
+ </style>
@@ -13,6 +13,7 @@ import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
13
13
  import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
14
14
  import CrudFilterButton from "./buttons/CrudFilterButton.vue";
15
15
  import CrudExportList from "./CrudExportList.vue";
16
+ import CrudImportList from "./CrudImportList.vue";
16
17
  import type {IEntityCrud} from "@drax/crud-share";
17
18
  import {useI18n} from "vue-i18n";
18
19
  import CrudFilters from "./CrudFilters.vue";
@@ -96,7 +97,7 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
96
97
 
97
98
  <crud-import-button
98
99
  :entity="entity"
99
- @import="(v:any) => $emit('import', v)"
100
+ @import="(file:any, format:any) => $emit('import', file, format)"
100
101
  />
101
102
 
102
103
  <crud-export-button
@@ -132,6 +133,12 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
132
133
  </template>
133
134
  </crud-export-list>
134
135
 
136
+ <crud-import-list :entity="entity">
137
+ <template #import-table="{ importFiles }">
138
+ <slot name="import-table" :importFiles="importFiles" />
139
+ </template>
140
+ </crud-import-list>
141
+
135
142
  <v-card variant="flat">
136
143
  <v-card-text v-if="entity.searchEnable">
137
144
  <crud-search
@@ -13,6 +13,7 @@ import CrudViewButton from "./buttons/CrudViewButton.vue";
13
13
  import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
14
14
  import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
15
15
  import CrudExportList from "./CrudExportList.vue";
16
+ import CrudImportList from "./CrudImportList.vue";
16
17
  import type {IEntityCrud} from "@drax/crud-share";
17
18
  import {useI18n} from "vue-i18n";
18
19
  import CrudFilters from "./CrudFilters.vue";
@@ -67,7 +68,7 @@ onMounted(() => {
67
68
 
68
69
  <crud-import-button
69
70
  :entity="entity"
70
- @import="(v:any) => $emit('import', v)"
71
+ @import="(file:any, format:any) => $emit('import', file, format)"
71
72
  />
72
73
 
73
74
  <crud-export-button
@@ -115,6 +116,14 @@ onMounted(() => {
115
116
  </template>
116
117
  </crud-export-list>
117
118
 
119
+ <crud-import-list
120
+ :entity="entity"
121
+ >
122
+ <template #import-table="{ importFiles }">
123
+ <slot name="import-table" :importFiles="importFiles" />
124
+ </template>
125
+ </crud-import-list>
126
+
118
127
  <v-card variant="flat">
119
128
  <v-card-text v-if="entity.searchEnable">
120
129
  <crud-search
@@ -12,6 +12,7 @@ import CrudViewButton from "./buttons/CrudViewButton.vue";
12
12
  import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
13
13
  import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
14
14
  import CrudExportList from "./CrudExportList.vue";
15
+ import CrudImportList from "./CrudImportList.vue";
15
16
  import type {IEntityCrud} from "@drax/crud-share";
16
17
  import {useI18n} from "vue-i18n";
17
18
  import CrudFilters from "./CrudFilters.vue";
@@ -97,7 +98,7 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
97
98
 
98
99
  <crud-import-button
99
100
  :entity="entity"
100
- @import="(v:any) => $emit('import', v)"
101
+ @import="(file:any, format:any) => $emit('import', file, format)"
101
102
  />
102
103
 
103
104
  <crud-export-button
@@ -142,6 +143,14 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
142
143
  </template>
143
144
  </crud-export-list>
144
145
 
146
+ <crud-import-list
147
+ :entity="entity"
148
+ >
149
+ <template #import-table="{ importFiles }">
150
+ <slot name="import-table" :importFiles="importFiles" />
151
+ </template>
152
+ </crud-import-list>
153
+
145
154
  <v-card variant="flat">
146
155
  <v-card-text v-if="entity.searchEnable">
147
156
  <crud-search
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import {ref} from "vue";
2
3
  import type {PropType} from "vue";
3
4
  import type {IEntityCrud} from "@drax/crud-share"
4
5
  import {useCrud} from "../../composables/UseCrud";
@@ -10,26 +11,62 @@ const {entity} = defineProps({
10
11
  })
11
12
 
12
13
  const {
13
- exportLoading
14
+ importLoading
14
15
  } = useCrud(entity)
16
+
17
+ const fileInput = ref<HTMLInputElement | null>(null)
18
+ const selectedFormat = ref<'CSV' | 'JSON'>('CSV')
19
+
20
+ const emit = defineEmits(['import'])
21
+
22
+ function selectFormat(format: string) {
23
+ selectedFormat.value = format as 'CSV' | 'JSON'
24
+ fileInput.value?.click()
25
+ }
26
+
27
+ function onFileChange(event: Event) {
28
+ const target = event.target as HTMLInputElement
29
+ const file = target.files?.[0]
30
+ if (!file) {
31
+ return
32
+ }
33
+
34
+ emit('import', file, selectedFormat.value)
35
+ target.value = ''
36
+ }
15
37
  </script>
16
38
 
17
39
  <template>
18
40
  <div v-if="entity.isImportable">
41
+ <input
42
+ ref="fileInput"
43
+ class="d-none"
44
+ type="file"
45
+ :accept="entity.importFormats.includes('CSV') && entity.importFormats.includes('JSON') ? '.csv,.json,application/json,text/csv' : entity.importFormats.includes('CSV') ? '.csv,text/csv' : '.json,application/json'"
46
+ @change="onFileChange"
47
+ />
48
+ <v-menu>
49
+ <template v-slot:activator="{ props: mp }">
19
50
  <v-tooltip location="top">
20
51
  <template v-slot:activator="{ props }">
21
52
  <v-btn
22
- v-bind="props"
23
- :disabled="exportLoading"
53
+ v-bind="{...mp, ...props}"
54
+ :disabled="importLoading"
24
55
  class="mr-1"
25
56
  variant="text"
26
- :loading="exportLoading"
57
+ :loading="importLoading"
27
58
  icon="mdi-database-import-outline"
28
59
  ></v-btn>
29
60
  </template>
30
61
  {{ t('action.import')}}
31
62
  </v-tooltip>
32
-
63
+ </template>
64
+ <v-list>
65
+ <v-list-item v-for="format in entity.importFormats" @click="selectFormat(format)">
66
+ <v-list-item-title>{{ format }}</v-list-item-title>
67
+ </v-list-item>
68
+ </v-list>
69
+ </v-menu>
33
70
  </div>
34
71
  </template>
35
72
 
@@ -3,13 +3,14 @@ import type {
3
3
  IDraxPaginateResult,
4
4
  IEntityCrud,
5
5
  IEntityCrudFilter,
6
+ IEntityCrudOperation
6
7
  } from "@drax/crud-share";
7
8
  import {useCrudStore} from "../stores/UseCrudStore";
8
9
  import {computed, nextTick, toRaw} from "vue";
9
10
  import getItemId from "../helpers/getItemId";
10
11
  import {useI18n} from "vue-i18n";
11
12
  import {useRouter} from "vue-router";
12
- import type {IEntityCrudFilterDynamic} from "@drax/crud-share/types/interfaces/IEntityCrudFilterDynamic";
13
+
13
14
 
14
15
  export function useCrud(entity: IEntityCrud) {
15
16
 
@@ -55,6 +56,14 @@ export function useCrud(entity: IEntityCrud) {
55
56
  }
56
57
  })
57
58
 
59
+ const getOperation = computed(() => {
60
+ return store.operation
61
+ })
62
+
63
+ const setOperation = (operation: IEntityCrudOperation) => {
64
+ store.setOperation(operation)
65
+ }
66
+
58
67
  const form = computed({
59
68
  get() {
60
69
  return store.form
@@ -63,6 +72,14 @@ export function useCrud(entity: IEntityCrud) {
63
72
  }
64
73
  })
65
74
 
75
+ const getForm = computed(() => {
76
+ return store.form
77
+ })
78
+
79
+ const setForm = (form: any) => {
80
+ store.setForm(form)
81
+ }
82
+
66
83
  const formValid = computed({
67
84
  get() {
68
85
  return store.formValid
@@ -159,6 +176,14 @@ export function useCrud(entity: IEntityCrud) {
159
176
  }
160
177
  })
161
178
 
179
+ const importFiles = computed({
180
+ get() {
181
+ return store.importFiles
182
+ }, set(value) {
183
+ store.setImportFiles(value)
184
+ }
185
+ })
186
+
162
187
  const exportLoading = computed({
163
188
  get() {
164
189
  return store.exportLoading
@@ -167,6 +192,14 @@ export function useCrud(entity: IEntityCrud) {
167
192
  }
168
193
  })
169
194
 
195
+ const importLoading = computed({
196
+ get() {
197
+ return store.importLoading
198
+ }, set(value) {
199
+ store.setImportLoading(value)
200
+ }
201
+ })
202
+
170
203
  const exportListVisible = computed({
171
204
  get() {
172
205
  return store.exportListVisible
@@ -175,6 +208,22 @@ export function useCrud(entity: IEntityCrud) {
175
208
  }
176
209
  })
177
210
 
211
+ const importListVisible = computed({
212
+ get() {
213
+ return store.importListVisible
214
+ }, set(value) {
215
+ store.setImportListVisible(value)
216
+ }
217
+ })
218
+
219
+ const importError = computed({
220
+ get() {
221
+ return store.importError
222
+ }, set(value) {
223
+ store.setImportError(value)
224
+ }
225
+ })
226
+
178
227
  const filters = computed({
179
228
  get() {
180
229
  return store.filters
@@ -184,7 +233,7 @@ export function useCrud(entity: IEntityCrud) {
184
233
  })
185
234
 
186
235
  const prepareDynamicFilters = computed(() => {
187
- return store.dynamicFilters.map((filter: IEntityCrudFilterDynamic) => {
236
+ return store.dynamicFilters.map((filter: IEntityCrudFilter) => {
188
237
  return {
189
238
  field: filter.name,
190
239
  operator: filter.operator,
@@ -270,6 +319,30 @@ export function useCrud(entity: IEntityCrud) {
270
319
  }
271
320
  }
272
321
 
322
+ async function doImport(file: File, format: 'CSV' | 'JSON') {
323
+ store.setImportLoading(true)
324
+ store.setImportListVisible(true)
325
+ store.setImportError(false)
326
+ try {
327
+ if (!entity?.provider.import) {
328
+ throw new Error("provider.import not implemented")
329
+ }
330
+
331
+ const result = await entity.provider.import(file, format)
332
+ store.addImportFile(result)
333
+ await doPaginate()
334
+ store.showMessage(result?.message || "Import successful")
335
+ return result
336
+ } catch (e: any) {
337
+ store.setImportError(true)
338
+ store.setError(e.message || "An error occurred while importing")
339
+ console.error("Error importing", e)
340
+ return {status: 'error'}
341
+ } finally {
342
+ store.setImportLoading(false)
343
+ }
344
+ }
345
+
273
346
 
274
347
  function cast(item: any) {
275
348
  entity.fields.filter(field => field.type === 'date')
@@ -475,12 +548,14 @@ export function useCrud(entity: IEntityCrud) {
475
548
 
476
549
 
477
550
  return {
478
- doPaginate, doExport, doUpdate, doCreate, doDelete,
551
+ doPaginate, doExport, doImport, doUpdate, doCreate, doDelete,
479
552
  onView, onCreate, onEdit, onDelete, onCancel, onSubmit, resetCrudStore,
480
- operation, dialog, form, notify, error, paginationError, message, formValid,
553
+ dialog, notify, error, paginationError, message, formValid,
554
+ form, getForm, setForm,
555
+ operation, getOperation, setOperation,
481
556
  loading, itemsPerPage, page, sortBy, search, totalItems, items,
482
557
  prepareFilters, filters, clearFilters, applyFilters,
483
- exportFiles, exportLoading, exportListVisible, exportError,
558
+ exportFiles, importFiles, exportLoading, importLoading, exportListVisible, importListVisible, exportError, importError,
484
559
  cloneItem, cast, prepareEdit,
485
560
  isDynamicFiltersEnable
486
561
  }
@@ -32,9 +32,13 @@ export const useCrudStore = (id: string = 'entity') => defineStore('CrudStore'+i
32
32
  loading: false,
33
33
  inputErrors: null,
34
34
  exportLoading: false,
35
+ importLoading: false,
35
36
  exportFiles: [] as string[],
37
+ importFiles: [] as any[],
36
38
  exportListVisible: false,
39
+ importListVisible: false,
37
40
  exportError: false,
41
+ importError: false,
38
42
  visibleColumns: []
39
43
  }
40
44
  ),
@@ -134,15 +138,30 @@ export const useCrudStore = (id: string = 'entity') => defineStore('CrudStore'+i
134
138
  addExportFile(exportFile: string) {
135
139
  this.exportFiles.push(exportFile)
136
140
  },
141
+ setImportFiles(importFiles: any[]) {
142
+ this.importFiles = importFiles
143
+ },
144
+ addImportFile(importFile: any) {
145
+ this.importFiles.push(importFile)
146
+ },
137
147
  setExportLoading(exportLoading: boolean) {
138
148
  this.exportLoading = exportLoading
139
149
  },
150
+ setImportLoading(importLoading: boolean) {
151
+ this.importLoading = importLoading
152
+ },
140
153
  setExportListVisible(exportListVisible: boolean) {
141
154
  this.exportListVisible = exportListVisible
142
155
  },
156
+ setImportListVisible(importListVisible: boolean) {
157
+ this.importListVisible = importListVisible
158
+ },
143
159
  setExportError(error: boolean){
144
160
  this.exportError = error
145
161
  },
162
+ setImportError(error: boolean){
163
+ this.importError = error
164
+ },
146
165
  setFilters(filters: IDraxFieldFilter[]) {
147
166
  this.filters = filters
148
167
  },