@drax/crud-vue 0.4.0 → 0.5.1

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": "0.4.0",
6
+ "version": "0.5.1",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,8 +24,9 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^0.4.0",
28
- "@drax/common-share": "^0.4.0"
27
+ "@drax/common-front": "^0.5.1",
28
+ "@drax/crud-front": "^0.5.1",
29
+ "@drax/crud-share": "^0.5.1"
29
30
  },
30
31
  "peerDependencies": {
31
32
  "pinia": "^2.2.2",
@@ -62,5 +63,5 @@
62
63
  "vue-tsc": "^2.0.11",
63
64
  "vuetify": "^3.7.1"
64
65
  },
65
- "gitHead": "481b302fe72f403abf092806ceca540dd2765dfa"
66
+ "gitHead": "6f507c8b52e134f10839fd7698c2466ff65a3d08"
66
67
  }
package/src/EntityCrud.ts CHANGED
@@ -1,102 +1,99 @@
1
- import type {IDraxCrud} from "@drax/common-share";
2
1
  import type {
3
- IFields,
4
- ICrudForm,
5
- ICrudHeaders,
6
- ICrudPermissions,
7
- ICrudRules,
8
- ICrudField
9
- } from "./interfaces/IEntityCrud";
2
+ IEntityCrud, IEntityCrudForm, IEntityCrudHeader, IEntityCrudRefs,
3
+ IEntityCrudRules, IEntityCrudField, IEntityCrudPermissions,
4
+ IDraxCrudProvider
5
+ } from "@drax/crud-share";
10
6
 
11
7
 
12
8
 
13
- class EntityCrud{
9
+ class EntityCrud implements IEntityCrud{
14
10
 
15
11
  name: string = ''
16
12
 
17
13
  constructor() {
18
14
  }
19
15
 
20
- static get instance(){
16
+ static get instance():IEntityCrud{
21
17
  throw new Error('EntityCrud instance not found')
22
18
  }
23
19
 
24
20
 
25
- get headers():ICrudHeaders[]{
21
+ get headers():IEntityCrudHeader[]{
26
22
  return [
27
23
  {title: 'ID',key:'_id'},
28
24
  ]
29
25
  }
30
26
 
31
- get permissions(): ICrudPermissions {
27
+ get permissions(): IEntityCrudPermissions {
32
28
  return {
33
29
  manage: 'manage', view: 'view', create: 'create', update: 'update', delete: 'delete'
34
30
  }
35
31
  }
36
32
 
37
- get provider(): IDraxCrud<any, any, any>{
33
+ get provider(): IDraxCrudProvider<any, any, any>{
38
34
  throw new Error('provider not implemented')
39
35
  }
40
36
 
41
- get fields():IFields{
37
+ get fields():IEntityCrudField[]{
42
38
  return [
43
39
  {name: 'id', type: 'string', label: 'ID', default: '' },
44
40
  ]
45
41
  }
46
42
 
47
- get form():ICrudForm{
48
-
49
- function objectFields(field:ICrudField){
50
- let value:any = {}
51
- if(field.objectFields){
52
- field.objectFields.forEach(subField => {
53
- if(subField.type === 'object'){
54
- value[subField.name] = objectFields(subField)
55
- }else{
56
- value[subField.name] = subField.default
57
- }
58
-
59
- })
60
- }
61
- return value
43
+ objectFields(field:IEntityCrudField){
44
+ let value:any = {}
45
+ if(field.objectFields){
46
+ field.objectFields.forEach(subField => {
47
+ if(subField.type === 'object'){
48
+ value[subField.name] = this.objectFields(subField)
49
+ }else{
50
+ value[subField.name] = subField.default
51
+ }
52
+
53
+ })
62
54
  }
55
+ return value
56
+ }
63
57
 
64
- const form = this.fields.reduce((acc, field) => {
58
+ get form():IEntityCrudForm{
65
59
 
60
+ const form = this.fields.reduce((acc, field) => {
66
61
  let value = null
67
62
  if(field.type === 'object'){
68
- value = objectFields(field)
69
- } else if(field.default != undefined){
63
+ value = this.objectFields(field)
64
+ }else if(field.default != undefined){
70
65
  value = field.default
71
66
  }
72
67
 
73
68
  return {...acc, [field.name]: value }
74
69
  }, {})
75
70
 
76
- console.log("Form: ", form)
71
+ //console.log("Form: ", form)
77
72
 
78
73
  return form
79
74
 
80
75
  }
81
76
 
82
- get refs():{ [key: string]: EntityCrud }{
77
+ get refs():IEntityCrudRefs{
83
78
  return {}
84
79
  }
85
80
 
86
- getRef(ref: string):EntityCrud{
81
+ getRef(ref: string):IEntityCrud{
87
82
  if(!this.refs.hasOwnProperty(ref)) {
88
83
  throw new Error("Ref not found: " + ref)
89
84
  }
90
85
 
91
- return this.refs[ref] as EntityCrud
86
+ return this.refs[ref]
92
87
  }
93
88
 
94
- get rules(): ICrudRules{
89
+ get rules(): IEntityCrudRules{
95
90
  return {}
96
91
  }
97
92
 
98
- get rule() {
99
- return (field:string) => this.rules[field] || []
93
+ get rule(): (field:string|undefined) => Array<Function> {
94
+ return (field:string|undefined) => {
95
+ return field && this.rules[field] ? this.rules[field] : []
96
+ }
100
97
  }
101
98
 
102
99
  get isEditable(){
@@ -111,11 +108,29 @@ class EntityCrud{
111
108
  return true
112
109
  }
113
110
 
114
- get dialogFullscreen(){
115
- return false
111
+ get isExportable(){
112
+ return true
116
113
  }
117
114
 
115
+ get exportFormats(){
116
+ return ['CSV', 'JSON']
117
+ }
118
118
 
119
+ get exportHeaders(){
120
+ return ['_id']
121
+ }
122
+
123
+ get isImportable(){
124
+ return true
125
+ }
126
+
127
+ get importFormats(){
128
+ return ['CSV', 'JSON']
129
+ }
130
+
131
+ get dialogFullscreen(){
132
+ return false
133
+ }
119
134
 
120
135
 
121
136
  }
@@ -1,19 +1,20 @@
1
1
  <script setup lang="ts">
2
2
  import type {PropType} from "vue";
3
- import EntityCrud from "../EntityCrud";
3
+ import type {IEntityCrud} from "@drax/crud-share";
4
4
  import CrudList from "./CrudList.vue";
5
5
  import CrudForm from "./CrudForm.vue";
6
6
  import CrudNotify from "./CrudNotify.vue";
7
7
  import CrudDialog from "./CrudDialog.vue";
8
8
  import {useCrud} from "../composables/UseCrud";
9
9
 
10
+
10
11
  const {entity} = defineProps({
11
- entity: {type: Object as PropType<EntityCrud>, required: true},
12
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
12
13
  })
13
14
 
14
15
  const {
15
16
  onCreate, onEdit, onDelete, onCancel, onSubmit,
16
- operation, dialog, form, formValid, notify, error, message,
17
+ operation, dialog, form, notify, error, message, doExport
17
18
  } = useCrud(entity);
18
19
 
19
20
  </script>
@@ -22,11 +23,14 @@ const {
22
23
  <v-container fluid class="mt-5">
23
24
  <v-card>
24
25
 
26
+
27
+
25
28
  <crud-list
26
29
  :entity="entity"
27
30
  @create="onCreate"
28
31
  @edit="onEdit"
29
32
  @delete="onDelete"
33
+ @export="doExport"
30
34
  >
31
35
  <template v-for="header in entity.headers" :key="header.key" v-slot:[`item.${header.key}`]="{item, value}">
32
36
  <slot :name="`item.${header.key}`" v-bind="{item, value}">
@@ -1,15 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  import {debounce} from "@drax/common-front"
3
- import type { PropType, Ref} from "vue";
3
+ import { type PropType, type Ref} from "vue";
4
4
  import {ref, onBeforeMount} from "vue";
5
- import EntityCrud from "../EntityCrud";
6
- import type {ICrudField} from "@/interfaces/IEntityCrud";
5
+ import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
7
6
 
8
- const valueModel = defineModel({type: [String, Array], required: false})
7
+ const valueModel = defineModel<string | string[]>({type: [String, Array], required: false})
9
8
 
10
9
  const {entity, multiple} = defineProps({
11
- entity: {type: Object as PropType<EntityCrud>, required: true},
12
- field: {type: Object as PropType<ICrudField>, required: true},
10
+ entity: {type: Object as PropType<IEntityCrud|undefined>, required: true},
11
+ field: {type: Object as PropType<IEntityCrudField>, required: true},
13
12
  multiple: {type: Boolean, default: false},
14
13
  chips: {type: Boolean, default: false},
15
14
  closableChips: {type: Boolean, default: true},
@@ -17,10 +16,14 @@ const {entity, multiple} = defineProps({
17
16
  label: {type: String},
18
17
  itemValue: {type: [String], default: '_id'},
19
18
  itemTitle: {type: [String], default: 'name'},
20
- rules: {type: Array<Function>, default: []},
19
+ rules: {type: Array as PropType<any>, default: []},
21
20
  errorMessages: {type: Array as PropType<string[]>, default: []},
22
21
  })
23
22
 
23
+ if(!entity){
24
+ throw new Error('entity is required')
25
+ }
26
+
24
27
  const loading: Ref<boolean> = ref(false)
25
28
  const items: Ref<Array<any>> = ref([])
26
29
 
@@ -28,19 +31,33 @@ const debouncedSearch = debounce(search, 300)
28
31
 
29
32
  onBeforeMount(async () => {
30
33
  if(valueModel.value && valueModel.value.length > 0){
34
+
31
35
  if(multiple && Array.isArray(valueModel.value) ){
32
36
  items.value = valueModel.value
33
- //await findByIds(valueModel.value)
34
- }else{
35
- items.value = [valueModel.value]
36
- //await findByIds([valueModel.value])
37
+
38
+ // valueModel.value = valueModel.value.map((item:any) => item._id)
39
+ await findByIds(valueModel.value)
40
+ }else if(!Array.isArray(valueModel.value)){
41
+ // items.value = [valueModel.value]
42
+ await findByIds([valueModel.value])
37
43
  }
38
44
 
45
+
46
+
39
47
  }
40
48
  })
41
49
 
42
- async function findByIds(ids: Array<string>) {
50
+ async function findByIds(ids: Array<string> = []) {
43
51
  try{
52
+ if(!entity){
53
+ throw new Error('Entity is required')
54
+ }
55
+ if(!entity.provider){
56
+ throw new Error('Provider is not defined')
57
+ }
58
+ if (typeof entity.provider.findByIds !== 'function') {
59
+ throw new Error('Provider does not have a findByIds method');
60
+ }
44
61
  loading.value = true
45
62
  items.value = await entity.provider.findByIds(ids)
46
63
  }catch (e){
@@ -51,11 +68,12 @@ async function findByIds(ids: Array<string>) {
51
68
  }
52
69
 
53
70
 
54
-
55
-
56
71
  async function search(value: any) {
57
72
  try{
58
73
  loading.value = true
74
+ if(!entity){
75
+ throw new Error('Entity is required')
76
+ }
59
77
  if(!entity.provider.search){
60
78
  throw new Error('Provider does not have a search method')
61
79
  }
@@ -1,11 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import type {TOperation} from "../interfaces/TOperation";
3
3
  import type {PropType} from "vue";
4
- import EntityCrud from "../EntityCrud";
4
+ import type {IEntityCrud} from "@drax/crud-share";
5
5
  const dialog = defineModel({type: Boolean, default: false})
6
+ import {useI18n} from "vue-i18n";
7
+ const {t,te} = useI18n()
6
8
 
7
9
  defineProps({
8
- entity: {type: Object as PropType<EntityCrud>, required: true},
10
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
9
11
  operation: {type: String as PropType<TOperation>}
10
12
  })
11
13
 
@@ -19,7 +21,7 @@ defineEmits(
19
21
  <v-dialog v-model="dialog" :fullscreen="entity.dialogFullscreen">
20
22
  <v-card>
21
23
  <v-toolbar>
22
- <v-toolbar-title>{{entity.name}} {{$te('action.'+operation) ? $t('action.'+operation) : operation}}</v-toolbar-title>
24
+ <v-toolbar-title>{{entity.name}} {{te('action.'+operation) ? t('action.'+operation) : operation}}</v-toolbar-title>
23
25
  <v-spacer></v-spacer>
24
26
  <v-btn icon @click="dialog = false"><v-icon>mdi-close</v-icon></v-btn>
25
27
  </v-toolbar>
@@ -0,0 +1,55 @@
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
+ const {t} = useI18n()
7
+ const {entity} = defineProps({
8
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
9
+ })
10
+
11
+ const {
12
+ exportFiles, exportListVisible, exportLoading
13
+ } = useCrud(entity);
14
+
15
+ </script>
16
+
17
+ <template>
18
+ <v-card
19
+ v-if="exportListVisible"
20
+ :loading="exportLoading"
21
+ class="ma-3" density="compact" variant="outlined" color="secondary"
22
+ >
23
+ <v-card-title>
24
+ {{ t('action.exports') }}
25
+
26
+ </v-card-title>
27
+ <v-card-text>
28
+ <v-table density="compact">
29
+ <thead>
30
+ <tr>
31
+ <th>Link</th><th>Rows</th><th>Time</th>
32
+ </tr>
33
+ </thead>
34
+ <tbody>
35
+ <tr v-for="exportFile in exportFiles">
36
+ <td><a :href="exportFile.url" target="_blank">{{ exportFile.url }}</a></td>
37
+ <td>{{ exportFile.rowCount }}</td>
38
+ <td>{{ exportFile.time }}</td>
39
+ </tr>
40
+ </tbody>
41
+ </v-table>
42
+
43
+ </v-card-text>
44
+
45
+ <v-card-actions>
46
+ <v-spacer></v-spacer>
47
+ <v-btn @click="exportFiles = []" :loading="exportLoading">{{ t('action.clear') }}</v-btn>
48
+ <v-btn @click="exportListVisible=false">{{ t('action.close') }}</v-btn>
49
+ </v-card-actions>
50
+ </v-card>
51
+ </template>
52
+
53
+ <style scoped>
54
+
55
+ </style>
@@ -1,16 +1,17 @@
1
1
  <script setup lang="ts">
2
2
  import type {PropType} from "vue";
3
3
  import {ref} from "vue";
4
- import EntityCrud from "../EntityCrud";
5
4
  import CrudFormField from "./CrudFormField.vue";
6
5
  import type {TOperation} from "../interfaces/TOperation";
7
-
6
+ import type {IEntityCrud} from "@drax/crud-share";
7
+ import {useI18n} from "vue-i18n";
8
+ const {t,te} = useI18n()
8
9
 
9
10
  const valueModel = defineModel({type: [Object]})
10
11
 
11
12
 
12
13
  const {entity} = defineProps({
13
- entity: {type: Object as PropType<EntityCrud>, required: true},
14
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
14
15
  operation: {type: String as PropType<TOperation>, required: true},
15
16
  readonly: {type: Boolean, default: false},
16
17
  error: {type: String, required: false},
@@ -38,7 +39,7 @@ const emit = defineEmits(['submit', 'cancel'])
38
39
  <v-form v-model="valid" ref="formRef" @submit.prevent >
39
40
  <v-card flat>
40
41
  <v-card-text v-if="error">
41
- <v-alert color="error">{{ $te(error) ? $t(error) : error }}</v-alert>
42
+ <v-alert color="error">{{ te(error) ? t(error) : error }}</v-alert>
42
43
  </v-card-text>
43
44
  <v-card-text>
44
45
  <template v-for="field in entity.fields" :key="field.name">
@@ -52,9 +53,9 @@ const emit = defineEmits(['submit', 'cancel'])
52
53
 
53
54
  <v-card-actions>
54
55
  <v-spacer></v-spacer>
55
- <v-btn variant="text" color="grey" @click="cancel">{{ $t('action.cancel') }}</v-btn>
56
+ <v-btn variant="text" color="grey" @click="cancel">{{ t('action.cancel') }}</v-btn>
56
57
  <v-btn variant="flat" color="primary" @click="submit">
57
- {{ operation ? $t('action.' + operation) : $t('action.sent') }}
58
+ {{ operation ? t('action.' + operation) : t('action.sent') }}
58
59
  </v-btn>
59
60
  </v-card-actions>
60
61
  </v-card>
@@ -1,26 +1,29 @@
1
1
  <script setup lang="ts">
2
2
  import {computed} from "vue";
3
3
  import type {PropType} from "vue";
4
- import type {ICrudField} from "../interfaces/IEntityCrud";
5
4
  import CrudFormList from "./CrudFormList.vue";
6
5
  import CrudAutocomplete from "./CrudAutocomplete.vue";
7
- import EntityCrud from "@/EntityCrud";
8
6
  import {useI18n} from "vue-i18n";
9
7
  import {useCrudStore} from "../stores/UseCrudStore";
10
8
  import {VDateInput} from 'vuetify/labs/VDateInput'
9
+ import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
11
10
  const {t, te} = useI18n()
12
11
 
13
12
  const store = useCrudStore()
14
13
 
15
- const valueModel = defineModel({type: [String, Number, Boolean, Object, Array], default: false})
14
+ const valueModel = defineModel<any>({type: [String, Number, Boolean, Object, Array], default: false})
16
15
 
17
16
  const {index, entity, field} = defineProps({
18
- entity: {type: Object as PropType<EntityCrud>, required: true},
19
- field: {type: Object as PropType<ICrudField>, required: true},
17
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
18
+ field: {type: Object as PropType<IEntityCrudField|undefined>, required: true},
20
19
  readonly: {type: Boolean, default: false},
21
20
  index: {type: Number, default: 0},
22
21
  })
23
22
 
23
+ if(!field){
24
+ throw new Error("CrudFormField must be provided with a field object")
25
+ }
26
+
24
27
  const name = computed(() => index > 0 ? `${field.name}_${index}` : field.name)
25
28
 
26
29
  const label = computed(() => {
@@ -93,7 +96,7 @@ const inputErrors = computed(() =>
93
96
 
94
97
  <crud-autocomplete
95
98
  v-if="field.type === 'ref'"
96
- :entity="entity.getRef(field.ref).instance"
99
+ :entity="entity.getRef(field.ref)"
97
100
  :field="field"
98
101
  v-model="valueModel"
99
102
  :label="label"
@@ -133,7 +136,7 @@ const inputErrors = computed(() =>
133
136
 
134
137
  <crud-autocomplete
135
138
  v-if="field.type === 'array.ref'"
136
- :entity="entity.getRef(field.ref).instance"
139
+ :entity="entity.getRef(field.ref)"
137
140
  :field="field"
138
141
  v-model="valueModel"
139
142
  :multiple="true"
@@ -1,14 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import type {PropType} from "vue";
3
- import type {ICrudField} from "../interfaces/IEntityCrud";
4
3
  import CrudFormField from "./CrudFormField.vue";
5
- import EntityCrud from "@/EntityCrud";
4
+ import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
6
5
 
7
6
  const valueModel = defineModel({type: Array, default: () => []});
8
7
 
9
8
  const {field} = defineProps({
10
- entity: {type: Object as PropType<EntityCrud>, required: true},
11
- field: {type: Object as PropType<ICrudField>, required: true},
9
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
10
+ field: {type: Object as PropType<IEntityCrudField>, required: true},
12
11
  readonly: {type: Boolean, default: false},
13
12
  })
14
13
 
@@ -16,7 +15,7 @@ function newItem() {
16
15
  return field.objectFields ? field.objectFields.reduce((acc, field) => ({...acc, [field.name]: field.default }), {}) : []
17
16
  }
18
17
 
19
- function getField(key: string):ICrudField|undefined {
18
+ function getField(key: string):IEntityCrudField|undefined {
20
19
  return field.objectFields ? field.objectFields.find(field => field.name === key) : undefined;
21
20
  }
22
21
 
@@ -43,12 +42,12 @@ function removeItem(index: number) {
43
42
  <v-col cols="12" v-for="(item,index) in valueModel" :key="index" class="text-right">
44
43
  <v-row dense align="center">
45
44
  <v-col cols="11">
46
- <template v-for="key in Object.keys(item)" :key="key">
45
+ <template v-for="key in Object.keys(item as Record<string, any>)" :key="key">
47
46
  <crud-form-field
48
47
  v-if="hasField(key)"
49
48
  :entity="entity"
50
49
  :field="getField(key)"
51
- v-model="valueModel[index][key]"
50
+ v-model="(valueModel[index] as any)[key]"
52
51
  :readonly="readonly"
53
52
  :index="index"
54
53
  />
@@ -1,24 +1,34 @@
1
1
  <script setup lang="ts">
2
2
  import type {PropType} from 'vue'
3
3
  import {useAuth} from '@drax/identity-vue'
4
- import EntityCrud from "../EntityCrud";
5
4
  import CrudSearch from "./CrudSearch.vue";
6
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 CrudExportList from "./CrudExportList.vue";
12
+ import type {IEntityCrud} from "@drax/crud-share";
7
13
  import {useI18n} from "vue-i18n";
8
- const {t, te} = useI18n()
14
+ import type {IEntityCrudHeader} from "@drax/crud-share";
15
+
16
+ const {t} = useI18n()
9
17
  const {hasPermission} = useAuth()
10
18
 
11
19
  const {entity} = defineProps({
12
- entity: {type: Object as PropType<EntityCrud>, required: true},
20
+ entity: {type: Object as PropType<IEntityCrud>, required: true},
13
21
  })
14
22
 
15
- const {loading, itemsPerPage, page, sortBy, search, totalItems, items,
16
- loadItems} = useCrud(entity)
23
+ const {
24
+ loading, itemsPerPage, page, sortBy, search, totalItems, items,
25
+ loadItems
26
+ } = useCrud(entity)
17
27
 
18
- const actions = [{title: t('action.actions'),key:'actions', sortable: false, align: 'right'}]
19
- const tHeaders = entity.headers.map(header => ({...header, title: t(`${entity.name}.fields.${header.title}`)}))
28
+ const actions: IEntityCrudHeader[] = [{title: t('action.actions'), key: 'actions', sortable: false, align: 'end', minWidth: '140px'}]
29
+ const tHeaders: IEntityCrudHeader[] = entity.headers.map(header => ({...header, title: t(`${entity.name}.fields.${header.title}`)}))
20
30
 
21
- const headers = [...tHeaders, ...actions]
31
+ const headers: IEntityCrudHeader[] = [...tHeaders, ...actions]
22
32
 
23
33
 
24
34
  defineExpose({
@@ -29,29 +39,46 @@ defineExpose({
29
39
 
30
40
  <template>
31
41
  <v-data-table-server
32
- class="border"
33
- v-if="hasPermission(entity.permissions.view)"
34
- v-model:items-per-page="itemsPerPage"
35
- :items-per-page-options="[5, 10, 20, 50]"
36
- v-model:page="page"
37
- v-model:sort-by="sortBy"
38
- :headers="headers"
39
- :items="items"
40
- :items-length="totalItems"
41
- :loading="loading"
42
- :search="search"
43
- :multi-sort="false"
44
- item-value="name"
45
- @update:options="loadItems"
42
+ class="border"
43
+ v-if="hasPermission(entity.permissions.view)"
44
+ v-model:items-per-page="itemsPerPage"
45
+ :items-per-page-options="[5, 10, 20, 50]"
46
+ v-model:page="page"
47
+ v-model:sort-by="sortBy"
48
+ :headers="headers"
49
+ :items="items"
50
+ :items-length="totalItems"
51
+ :loading="loading"
52
+ :search="search"
53
+ :multi-sort="false"
54
+ item-value="name"
55
+ @update:options="loadItems"
46
56
  >
47
57
  <template v-slot:top>
48
- <v-toolbar density="compact" >
58
+ <v-toolbar density="compact">
49
59
  <v-toolbar-title>{{ entity.name }}</v-toolbar-title>
50
60
  <v-spacer></v-spacer>
51
- <v-btn v-if="entity.isCreatable" icon="mdi-plus" class="mr-1" variant="text" color="primary" @click="$emit('create')">
52
- </v-btn>
61
+
62
+ <crud-import-button
63
+ :entity="entity"
64
+ @import="v => $emit('import', v)"
65
+ />
66
+
67
+ <crud-export-button
68
+ :entity="entity"
69
+ @export="v => $emit('export',v)"
70
+ />
71
+
72
+ <crud-create-button
73
+ v-if="entity.isCreatable"
74
+ :entity="entity"
75
+ @click="$emit('create')"
76
+ />
77
+
53
78
  </v-toolbar>
54
79
 
80
+ <crud-export-list :entity="entity"></crud-export-list>
81
+
55
82
  <v-card>
56
83
  <v-card-text>
57
84
  <crud-search v-model="search"></crud-search>
@@ -62,21 +89,21 @@ defineExpose({
62
89
 
63
90
 
64
91
  <template v-for="header in entity.headers" :key="header.key" v-slot:[`item.${header.key}`]="{item, value}">
65
- <slot :name="`item.${header.key}`" v-bind="{item, value}" >
66
- {{value}}
67
- </slot>
92
+ <slot :name="`item.${header.key}`" v-bind="{item, value}">
93
+ {{ value }}
94
+ </slot>
68
95
  </template>
69
96
 
70
97
 
71
98
  <template v-slot:item.actions="{item}">
72
- <v-btn v-if="entity.isEditable && hasPermission(entity.permissions.update)"
73
- icon="mdi-pencil" variant="text" color="primary"
74
- @click="$emit('edit', item)">
75
- </v-btn>
76
- <v-btn v-if="entity.isDeletable && hasPermission(entity.permissions.delete)"
77
- icon="mdi-delete" class="mr-1" variant="text" color="red"
78
- @click="$emit('delete', item)">
79
- </v-btn>
99
+ <crud-update-button
100
+ v-if="entity.isEditable && hasPermission(entity.permissions.update)"
101
+ @click="$emit('edit', item)"
102
+ />
103
+ <crud-delete-button
104
+ v-if="entity.isDeletable && hasPermission(entity.permissions.delete)"
105
+ @click="$emit('delete', item)"
106
+ />
80
107
  </template>
81
108
 
82
109
  </v-data-table-server>
@@ -1,4 +1,6 @@
1
1
  <script setup lang="ts">
2
+ import {useI18n} from "vue-i18n";
3
+ const {t} = useI18n()
2
4
  const model = defineModel<any>()
3
5
  </script>
4
6
 
@@ -7,7 +9,7 @@ const model = defineModel<any>()
7
9
  density="compact" class="mr-2"
8
10
  variant="outlined"
9
11
  append-inner-icon="mdi-magnify"
10
- :label="$t('action.search')"
12
+ :label="t('action.search')"
11
13
  single-line clearable @click:clear="() => model = ''"
12
14
  />
13
15
  </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import {useI18n} from "vue-i18n";
3
+
4
+ const {t} = useI18n()
5
+ </script>
6
+
7
+ <template>
8
+ <v-tooltip location="top">
9
+ <template v-slot:activator="{ props }">
10
+ <v-btn
11
+ v-bind="{ ...$attrs, ...props }"
12
+ icon="mdi-plus"
13
+ class="mr-1"
14
+ variant="text"
15
+ color="primary"
16
+ >
17
+ </v-btn>
18
+ </template>
19
+ {{ t('action.create')}}
20
+ </v-tooltip>
21
+ </template>
22
+
23
+ <style scoped>
24
+
25
+ </style>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import {useI18n} from "vue-i18n";
3
+
4
+ const {t} = useI18n()
5
+ </script>
6
+
7
+ <template>
8
+ <v-tooltip location="top">
9
+ <template v-slot:activator="{ props}">
10
+ <v-btn
11
+ v-bind="{ ...$attrs, ...props }"
12
+ icon="mdi-delete"
13
+ class="mr-1"
14
+ variant="text"
15
+ color="red"
16
+ >
17
+ </v-btn>
18
+ </template>
19
+ {{ t('action.delete')}}
20
+ </v-tooltip>
21
+ </template>
22
+
23
+ <style scoped>
24
+
25
+ </style>
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import type {PropType} from "vue";
3
+ import EntityCrud from "../../EntityCrud";
4
+ import {useCrud} from "../../composables/UseCrud";
5
+ import {useI18n} from "vue-i18n";
6
+
7
+ const {t} = useI18n()
8
+
9
+ const {entity} = defineProps({
10
+ entity: {type: Object as PropType<EntityCrud>, required: true},
11
+ })
12
+
13
+ const {
14
+ exportLoading
15
+ } = useCrud(entity)
16
+ </script>
17
+
18
+ <template>
19
+ <v-menu v-if="entity.isExportable">
20
+ <template v-slot:activator="{ props: mp }">
21
+ <v-tooltip location="top">
22
+ <template v-slot:activator="{ props: tp }">
23
+ <v-btn
24
+ v-bind="{...mp, ...tp}"
25
+ :disabled="exportLoading"
26
+ class="mr-1"
27
+ color="teal"
28
+ variant="text"
29
+ :loading="exportLoading"
30
+ icon="mdi-database-export-outline"
31
+ ></v-btn>
32
+ </template>
33
+ {{ t('action.export')}}
34
+ </v-tooltip>
35
+
36
+ </template>
37
+ <v-list>
38
+ <v-list-item v-for="format in entity.exportFormats" @click="$emit('export', format)">
39
+ <v-list-item-title>{{format}}</v-list-item-title>
40
+ </v-list-item>
41
+ </v-list>
42
+
43
+ </v-menu>
44
+ </template>
45
+
46
+ <style scoped>
47
+
48
+ </style>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import type {PropType} from "vue";
3
+ import EntityCrud from "../../EntityCrud";
4
+ import {useCrud} from "../../composables/UseCrud";
5
+ import {useI18n} from "vue-i18n";
6
+
7
+ const {t} = useI18n()
8
+ const {entity} = defineProps({
9
+ entity: {type: Object as PropType<EntityCrud>, required: true},
10
+ })
11
+
12
+ const {
13
+ exportLoading
14
+ } = useCrud(entity)
15
+ </script>
16
+
17
+ <template>
18
+ <div v-if="entity.isImportable">
19
+ <v-tooltip location="top">
20
+ <template v-slot:activator="{ props }">
21
+ <v-btn
22
+ v-bind="props"
23
+ :disabled="exportLoading"
24
+ class="mr-1"
25
+ color="purple"
26
+ variant="text"
27
+ :loading="exportLoading"
28
+ icon="mdi-database-import-outline"
29
+ ></v-btn>
30
+ </template>
31
+ {{ t('action.import')}}
32
+ </v-tooltip>
33
+
34
+ </div>
35
+ </template>
36
+
37
+ <style scoped>
38
+
39
+ </style>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import {useI18n} from "vue-i18n";
3
+ const {t} = useI18n()
4
+ </script>
5
+
6
+ <template>
7
+ <v-tooltip location="top">
8
+ <template v-slot:activator="{ props}">
9
+ <v-btn
10
+ v-bind="{ ...$attrs, ...props }"
11
+ icon="mdi-pencil"
12
+ class="mr-1"
13
+ variant="text"
14
+ color="primary"
15
+ slim
16
+ >
17
+ </v-btn>
18
+ </template>
19
+ {{ t('action.update')}}
20
+ </v-tooltip>
21
+ </template>
22
+
23
+ <style scoped>
24
+
25
+ </style>
@@ -1,14 +1,12 @@
1
- import EntityCrud from "../EntityCrud";
2
- import type {IDraxPaginateResult} from "@drax/common-share";
1
+ import type {IDraxPaginateResult, IEntityCrud} from "@drax/crud-share";
3
2
  import {useCrudStore} from "../stores/UseCrudStore";
4
3
  import {computed} from "vue";
5
- import type {ICrudField} from "@/interfaces/IEntityCrud";
6
4
 
7
- export function useCrud(entity: EntityCrud) {
5
+ export function useCrud(entity: IEntityCrud) {
8
6
 
9
7
  const store = useCrudStore()
10
8
 
11
- async function loadItems() {
9
+ async function doPaginate() {
12
10
  store.setLoading(true)
13
11
  try {
14
12
  const r: IDraxPaginateResult<any> = await entity?.provider.paginate({
@@ -27,25 +25,68 @@ export function useCrud(entity: EntityCrud) {
27
25
  }
28
26
  }
29
27
 
30
- function cast(item: any){
28
+ async function doExport(format: 'JSON') {
29
+ store.setExportLoading(true)
30
+ store.setExportListVisible(true)
31
+ try {
31
32
 
32
- entity.fields.filter(field => field.type === 'date')
33
- .forEach(field => {
34
- if(field.type === 'date'){
35
- item[field.name] = new Date(item[field.name])
36
- }
37
- })
33
+ if(!entity?.provider.export) {
34
+ throw new Error("exportCsv not implemented")
35
+ }
38
36
 
39
- return item
37
+ const headers: string = entity.exportHeaders.join(',')
38
+
39
+ const r: any = await entity?.provider.export({
40
+ format: format,
41
+ headers: headers,
42
+ separator: ";",
43
+ orderBy: store.sortBy[0]?.key,
44
+ order: store.sortBy[0]?.order,
45
+ search: store.search
46
+ })
47
+
48
+ if(r && r.url) {
49
+ store.addExportFile(r)
50
+ store.showMessage("Export successful")
51
+ }
52
+
53
+ return r
54
+
55
+ } catch (e) {
56
+ console.error("Error exporting csv", e)
57
+ } finally {
58
+ store.setExportLoading(false)
59
+ }
40
60
  }
41
61
 
42
62
 
63
+
64
+
43
65
  function onCreate() {
44
66
  store.setOperation("create")
45
67
  store.setForm(entity.form)
46
68
  store.setDialog(true)
47
69
  }
48
70
 
71
+ function cast(item: any){
72
+ entity.fields.filter(field => field.type === 'date')
73
+ .forEach(field => {
74
+ item[field.name] = new Date(item[field.name])
75
+ })
76
+
77
+ entity.fields.filter(field => field.type === 'ref')
78
+ .forEach(field => {
79
+ item[field.name] = item[field.name]._id
80
+ })
81
+
82
+ entity.fields.filter(field => field.type === 'array.ref')
83
+ .forEach(field => {
84
+ item[field.name] = item[field.name].map(((i:any) => i._id))
85
+ })
86
+
87
+ return item
88
+ }
89
+
49
90
  function onEdit(item: object) {
50
91
  store.setOperation("edit")
51
92
  store.setForm(cast({...item}))
@@ -83,7 +124,7 @@ export function useCrud(entity: EntityCrud) {
83
124
  async function doCreate(formData: any) {
84
125
  try {
85
126
  await entity?.provider.create(formData)
86
- await loadItems()
127
+ await doPaginate()
87
128
  store.setDialog(false)
88
129
  store.showMessage("Entity created successfully!")
89
130
  } catch (e: any) {
@@ -99,7 +140,7 @@ export function useCrud(entity: EntityCrud) {
99
140
  async function doUpdate(formData: any) {
100
141
  try {
101
142
  await entity?.provider.update(formData._id, formData)
102
- await loadItems()
143
+ await doPaginate()
103
144
  store.setDialog(false)
104
145
  store.showMessage("Entity updated successfully!")
105
146
  } catch (e: any) {
@@ -116,7 +157,7 @@ export function useCrud(entity: EntityCrud) {
116
157
  async function doDelete(formData: any) {
117
158
  try {
118
159
  await entity?.provider.delete(formData._id)
119
- await loadItems()
160
+ await doPaginate()
120
161
  store.setDialog(false)
121
162
  store.showMessage("Entity deleted successfully!")
122
163
  } catch (e: any) {
@@ -140,11 +181,15 @@ export function useCrud(entity: EntityCrud) {
140
181
  const search = computed({get(){return store.search} , set(value){store.setSearch(value)}})
141
182
  const totalItems = computed({get(){return store.totalItems} , set(value){store.setTotalItems(value)}})
142
183
  const items = computed({get(){return store.items} , set(value){store.setItems(value)}})
184
+ const exportFiles = computed({get(){return store.exportFiles} , set(value){store.setExportFiles(value)}})
185
+ const exportLoading = computed({get(){return store.exportLoading} , set(value){store.setExportLoading(value)}})
186
+ const exportListVisible = computed({get(){return store.exportListVisible} , set(value){store.setExportListVisible(value)}})
143
187
 
144
188
  return {
145
- loadItems, onCreate, onEdit, onDelete, onCancel, onSubmit,
146
- operation, dialog, form, notify, error, message,
147
- loading, itemsPerPage, page, sortBy, search, totalItems, items
189
+ loadItems: doPaginate, doExport, onCreate, onEdit, onDelete, onCancel, onSubmit,
190
+ operation, dialog, form, notify, error, message, formValid,
191
+ loading, itemsPerPage, page, sortBy, search, totalItems, items,
192
+ exportFiles,exportLoading,exportListVisible
148
193
  }
149
194
 
150
195
  }
package/src/index.ts CHANGED
@@ -10,8 +10,6 @@ import {useCrudStore} from "./stores/UseCrudStore";
10
10
  import {useCrud} from "./composables/UseCrud";
11
11
  import {EntityCrud} from "./EntityCrud";
12
12
 
13
- import type {IFields, ICrudForm, ICrudHeaders, ICrudPermissions, ICrudRules} from "./interfaces/IEntityCrud";
14
- export type {IFields, ICrudForm, ICrudHeaders, ICrudPermissions, ICrudRules}
15
13
 
16
14
  export {
17
15
  Crud,
@@ -9,8 +9,8 @@ export const useCrudStore = defineStore('CrudStore', {
9
9
  form: {} as any,
10
10
  formValid: {} as any,
11
11
  notify: false as boolean,
12
- error: '' as string,
13
12
  message: '' as string,
13
+ error: '' as string,
14
14
  items: [] as any[],
15
15
  totalItems: 0 as number,
16
16
  itemsPerPage: 5 as number,
@@ -18,7 +18,10 @@ export const useCrudStore = defineStore('CrudStore', {
18
18
  search: '' as string,
19
19
  sortBy: [] as any[],
20
20
  loading: false,
21
- inputErrors: null
21
+ inputErrors: null,
22
+ exportLoading: false,
23
+ exportFiles: [] as string[],
24
+ exportListVisible: false,
22
25
  }
23
26
  ),
24
27
  getters:{
@@ -80,6 +83,18 @@ export const useCrudStore = defineStore('CrudStore', {
80
83
  },
81
84
  setInputErrors(inputErrors: any) {
82
85
  this.inputErrors = inputErrors
86
+ },
87
+ setExportFiles(exportFiles: string[]) {
88
+ this.exportFiles = exportFiles
89
+ },
90
+ addExportFile(exportFile: string) {
91
+ this.exportFiles.push(exportFile)
92
+ },
93
+ setExportLoading(exportLoading: boolean) {
94
+ this.exportLoading = exportLoading
95
+ },
96
+ setExportListVisible(exportListVisible: boolean) {
97
+ this.exportListVisible = exportListVisible
83
98
  }
84
99
  }
85
100
 
@@ -1,33 +0,0 @@
1
- interface ICrudHeaders {
2
- title: string
3
- key: string
4
- }
5
-
6
- interface ICrudRules {
7
- [key: string]: Array<Function>
8
- }
9
-
10
- interface ICrudField {
11
- name: string
12
- type: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'ref' | 'array.string' | 'array.number' | 'array.object' | 'array.ref'
13
- ref?: string
14
- objectFields?: ICrudField[]
15
- label: string,
16
- default: any
17
- }
18
-
19
- interface ICrudForm {
20
- [key: string]: string | number | boolean | Date | null
21
- }
22
-
23
- type IFields = ICrudField[]
24
-
25
- interface ICrudPermissions {
26
- manage: string
27
- view: string
28
- create: string
29
- update: string
30
- delete: string
31
- }
32
-
33
- export type {ICrudHeaders, ICrudRules, ICrudField, IFields, ICrudForm, ICrudPermissions}