@drax/crud-vue 3.10.0 → 3.11.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.11.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.11.0",
30
+ "@drax/media-vue": "^3.11.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": "9f9de6fe51c4f11d885385bd31a12edec799823e"
54
54
  }
@@ -15,7 +15,7 @@ const {entity} = defineProps({
15
15
 
16
16
  const {
17
17
  onView, onCreate, onEdit, onDelete, resetCrudStore,
18
- operation, dialog, notify, message, doExport,
18
+ operation, dialog, notify, message, doExport, doImport,
19
19
  prepareFilters
20
20
  } = useCrud(entity);
21
21
 
@@ -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
 
@@ -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
 
@@ -159,6 +159,14 @@ export function useCrud(entity: IEntityCrud) {
159
159
  }
160
160
  })
161
161
 
162
+ const importFiles = computed({
163
+ get() {
164
+ return store.importFiles
165
+ }, set(value) {
166
+ store.setImportFiles(value)
167
+ }
168
+ })
169
+
162
170
  const exportLoading = computed({
163
171
  get() {
164
172
  return store.exportLoading
@@ -167,6 +175,14 @@ export function useCrud(entity: IEntityCrud) {
167
175
  }
168
176
  })
169
177
 
178
+ const importLoading = computed({
179
+ get() {
180
+ return store.importLoading
181
+ }, set(value) {
182
+ store.setImportLoading(value)
183
+ }
184
+ })
185
+
170
186
  const exportListVisible = computed({
171
187
  get() {
172
188
  return store.exportListVisible
@@ -175,6 +191,22 @@ export function useCrud(entity: IEntityCrud) {
175
191
  }
176
192
  })
177
193
 
194
+ const importListVisible = computed({
195
+ get() {
196
+ return store.importListVisible
197
+ }, set(value) {
198
+ store.setImportListVisible(value)
199
+ }
200
+ })
201
+
202
+ const importError = computed({
203
+ get() {
204
+ return store.importError
205
+ }, set(value) {
206
+ store.setImportError(value)
207
+ }
208
+ })
209
+
178
210
  const filters = computed({
179
211
  get() {
180
212
  return store.filters
@@ -270,6 +302,30 @@ export function useCrud(entity: IEntityCrud) {
270
302
  }
271
303
  }
272
304
 
305
+ async function doImport(file: File, format: 'CSV' | 'JSON') {
306
+ store.setImportLoading(true)
307
+ store.setImportListVisible(true)
308
+ store.setImportError(false)
309
+ try {
310
+ if (!entity?.provider.import) {
311
+ throw new Error("provider.import not implemented")
312
+ }
313
+
314
+ const result = await entity.provider.import(file, format)
315
+ store.addImportFile(result)
316
+ await doPaginate()
317
+ store.showMessage(result?.message || "Import successful")
318
+ return result
319
+ } catch (e: any) {
320
+ store.setImportError(true)
321
+ store.setError(e.message || "An error occurred while importing")
322
+ console.error("Error importing", e)
323
+ return {status: 'error'}
324
+ } finally {
325
+ store.setImportLoading(false)
326
+ }
327
+ }
328
+
273
329
 
274
330
  function cast(item: any) {
275
331
  entity.fields.filter(field => field.type === 'date')
@@ -475,12 +531,12 @@ export function useCrud(entity: IEntityCrud) {
475
531
 
476
532
 
477
533
  return {
478
- doPaginate, doExport, doUpdate, doCreate, doDelete,
534
+ doPaginate, doExport, doImport, doUpdate, doCreate, doDelete,
479
535
  onView, onCreate, onEdit, onDelete, onCancel, onSubmit, resetCrudStore,
480
536
  operation, dialog, form, notify, error, paginationError, message, formValid,
481
537
  loading, itemsPerPage, page, sortBy, search, totalItems, items,
482
538
  prepareFilters, filters, clearFilters, applyFilters,
483
- exportFiles, exportLoading, exportListVisible, exportError,
539
+ exportFiles, importFiles, exportLoading, importLoading, exportListVisible, importListVisible, exportError, importError,
484
540
  cloneItem, cast, prepareEdit,
485
541
  isDynamicFiltersEnable
486
542
  }
@@ -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
  },