@ramathibodi/nuxt-commons 0.1.29 → 0.1.31

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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.29",
7
+ "version": "0.1.31",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.3",
10
10
  "unbuild": "2.0.0"
@@ -0,0 +1,426 @@
1
+ <script lang="ts" setup>
2
+ import {computed, defineOptions, nextTick, ref, useAttrs, useSlots, watch} from 'vue'
3
+ import {omit} from 'lodash-es'
4
+ import type {FormDialogCallback} from '../../types/formDialog'
5
+ import {VDataIterator} from "vuetify/components/VDataIterator";
6
+ import {VDataTable} from "vuetify/components/VDataTable";
7
+
8
+ defineOptions({
9
+ inheritAttrs: false,
10
+ })
11
+
12
+ interface Props extends /* @vue-ignore */ InstanceType<typeof VDataIterator['$props']>,/* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
13
+ title: string
14
+ noDataText?: string
15
+ modelValue?: Record<string, any>[]
16
+ modelKey?: string
17
+ dialogFullscreen?: boolean
18
+ initialData?: Record<string, any>
19
+ toolbarColor?: string
20
+ importable?: boolean
21
+ exportable?: boolean
22
+
23
+ viewSwitch?: boolean
24
+ viewSwitchMultiple?: boolean
25
+
26
+ cols?: string | number | boolean
27
+ xxl?: string | number | boolean
28
+ xl?: string | number | boolean
29
+ lg?: string | number | boolean
30
+ md?: string | number | boolean
31
+ sm?: string | number | boolean
32
+ itemsPerPage?: string | number
33
+ }
34
+
35
+ const props = withDefaults(defineProps<Props>(), {
36
+ noDataText: 'ไม่พบข้อมูล',
37
+ dialogFullscreen: false,
38
+ modelKey: 'id',
39
+ toolbarColor: 'primary',
40
+ importable: true,
41
+ exportable: true,
42
+
43
+ viewSwitch: false,
44
+ viewSwitchMultiple:false,
45
+
46
+ cols: 12,
47
+ xxl: false,
48
+ xl: false,
49
+ lg: false,
50
+ md: 2,
51
+ sm: 6,
52
+ itemsPerPage: 12,
53
+ })
54
+
55
+ const emit = defineEmits(['update:modelValue'])
56
+
57
+ const attrs = useAttrs()
58
+ const plainAttrs = computed(() => {
59
+ return omit(attrs, ['modelValue', 'onUpdate:modelValue'])
60
+ })
61
+
62
+ const slots = useSlots()
63
+ const tableSlots = computed(() => {
64
+ return omit(slots, ['item'])
65
+ })
66
+
67
+ const viewType = ref<string[] | string>('iterator')
68
+
69
+ const items = ref<Record<string, any>[]>([])
70
+ const search = ref<string>()
71
+ const currentItem = ref<Record<string, any> | undefined>(undefined)
72
+
73
+ const isDialogOpen = ref<boolean>(false)
74
+
75
+ watch(() => props.modelValue, (newValue) => {
76
+ if (!Array.isArray(newValue) || !newValue.every(item => typeof item === 'object')) {
77
+ items.value = []
78
+ }
79
+ else {
80
+ let maxKey = 0
81
+
82
+ newValue.forEach((item) => {
83
+ if (!item.hasOwnProperty(props.modelKey)) {
84
+ maxKey = Math.max(maxKey, ...newValue.map(i => i[props.modelKey] || 0))
85
+ item[props.modelKey] = maxKey + 1
86
+ }
87
+ })
88
+
89
+ items.value = newValue
90
+ }
91
+ }, { immediate: true })
92
+
93
+ watch(items, (newValue) => {
94
+ emit('update:modelValue', newValue)
95
+ }, { deep: true })
96
+
97
+ const itemsPerPageInternal = ref<string | number>()
98
+ watch(() => props.itemsPerPage, (newValue) => {
99
+ if (newValue.toString().toLowerCase() == 'all') itemsPerPageInternal.value = '-1'
100
+ else if (newValue) itemsPerPageInternal.value = newValue
101
+ }, { immediate: true })
102
+
103
+ function createItem(item: Record<string, any>, callback?: FormDialogCallback) {
104
+ if (items.value.length > 0) item[props.modelKey] = Math.max(...items.value.map(i => i[props.modelKey] || 0)) + 1
105
+ else item[props.modelKey] = 1
106
+
107
+ items.value.push(item)
108
+
109
+ if (callback) callback.done()
110
+ }
111
+
112
+ function importItems(importItems: Record<string, any>[], callback?: FormDialogCallback) {
113
+ importItems.forEach((item) => {
114
+ createItem(item)
115
+ })
116
+ if (callback) callback.done()
117
+ }
118
+
119
+ function updateItem(newItem: Record<string, any>, callback?: FormDialogCallback) {
120
+ const index = items.value.findIndex(item => item[props.modelKey] === newItem[props.modelKey])
121
+
122
+ if (index !== -1) {
123
+ items.value[index] = newItem
124
+ }
125
+
126
+ if (callback) callback.done()
127
+ }
128
+
129
+ function moveUpItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
130
+ const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
131
+
132
+ if (index > 0) {
133
+ const temp = items.value[index - 1]
134
+ items.value[index - 1] = items.value[index]
135
+ items.value[index] = temp
136
+ }
137
+
138
+ if (callback) callback.done()
139
+ }
140
+
141
+ function moveDownItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
142
+ const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
143
+
144
+ if (index >= 0 && index < items.value.length - 1) {
145
+ const temp = items.value[index + 1]
146
+ items.value[index + 1] = items.value[index]
147
+ items.value[index] = temp
148
+ }
149
+
150
+ if (callback) callback.done()
151
+ }
152
+
153
+ function moveToItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
154
+ const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey]);
155
+
156
+ if (index !== -1) {
157
+ const newPosition = prompt("Enter the new position (0-based index):");
158
+ const parsedPosition = parseInt(<string>newPosition, 10);
159
+
160
+ if (isNaN(parsedPosition) || parsedPosition < 0 || parsedPosition >= items.value.length) {
161
+ alert("Invalid position entered. Please enter a number between 0 and " + (items.value.length - 1));
162
+ return
163
+ }
164
+
165
+ const [temp] = items.value.splice(index, 1);
166
+
167
+ items.value.splice(parsedPosition, 0, temp);
168
+ }
169
+
170
+ if (callback) callback.done();
171
+ }
172
+
173
+ function deleteItem(deleteItem: Record<string, any>, callback?: FormDialogCallback) {
174
+ const index = items.value.findIndex(item => item[props.modelKey] === deleteItem[props.modelKey])
175
+
176
+ if (index !== -1) {
177
+ items.value.splice(index, 1)
178
+ }
179
+
180
+ if (callback) callback.done()
181
+ }
182
+
183
+ function openDialog(item?: object) {
184
+ currentItem.value = item
185
+ nextTick(() => {
186
+ isDialogOpen.value = true
187
+ })
188
+ }
189
+
190
+ const operation = ref({ openDialog, createItem, updateItem, deleteItem, moveUpItem, moveDownItem,moveToItem })
191
+ </script>
192
+
193
+ <template>
194
+ <v-card>
195
+ <v-data-iterator
196
+ v-bind="plainAttrs"
197
+ v-model:items-per-page="itemsPerPageInternal"
198
+ :items="items"
199
+ :item-value="modelKey"
200
+ :search="search"
201
+ >
202
+ <template #default="defaultProps" v-if="viewType.includes('iterator')">
203
+ <slot
204
+ v-bind="defaultProps"
205
+ :operation="operation"
206
+ >
207
+ <v-container fluid>
208
+ <v-row>
209
+ <v-col
210
+ v-for="(item, index) in defaultProps.items"
211
+ :key="index"
212
+ :cols="cols"
213
+ :sm="sm"
214
+ :md="md"
215
+ :lg="lg"
216
+ :xl="xl"
217
+ :xxl="xxl"
218
+ >
219
+ <slot
220
+ name="item"
221
+ :item="item"
222
+ :operation="operation"
223
+ />
224
+ </v-col>
225
+ </v-row>
226
+ </v-container>
227
+ </slot>
228
+ </template>
229
+ <template #loader="loaderProps">
230
+ <slot
231
+ name="loader"
232
+ v-bind="loaderProps"
233
+ >
234
+ <v-container fluid>
235
+ <v-row>
236
+ <v-col
237
+ v-for="key in computedSkeletonPerPage"
238
+ :key="key"
239
+ :cols="cols"
240
+ :sm="sm"
241
+ :md="md"
242
+ :lg="lg"
243
+ :xl="xl"
244
+ :xxl="xxl"
245
+ >
246
+ <slot name="loaderItem">
247
+ <v-skeleton-loader
248
+ type="paragraph"
249
+ />
250
+ </slot>
251
+ </v-col>
252
+ </v-row>
253
+ </v-container>
254
+ </slot>
255
+ </template>
256
+ <template #header="headerProps">
257
+ <slot
258
+ name="header"
259
+ v-bind="headerProps"
260
+ :items="items"
261
+ :operation="operation"
262
+ >
263
+ <VToolbar :color="toolbarColor">
264
+ <v-row
265
+ justify="end"
266
+ class="ma-1"
267
+ dense
268
+ no-gutters
269
+ align="center"
270
+ >
271
+ <v-col cols="7">
272
+ <VToolbarTitle class="pl-3">
273
+ <slot name="title">
274
+ {{ title }}
275
+ </slot>
276
+ </VToolbarTitle>
277
+ </v-col>
278
+ <v-col cols="5">
279
+ <slot name="search">
280
+ <VTextField
281
+ v-model="search"
282
+ class="justify-end w-100"
283
+ density="compact"
284
+ hide-details
285
+ placeholder="ค้นหา"
286
+ clearable
287
+ variant="solo"
288
+ />
289
+ </slot>
290
+ </v-col>
291
+ </v-row>
292
+
293
+ <VToolbarItems>
294
+ <slot name="toolbarItems" />
295
+ <ImportCSV
296
+ v-if="props.importable"
297
+ icon="mdi:mdi-file-upload"
298
+ variant="flat"
299
+ @import="importItems"
300
+ :color="toolbarColor"
301
+ />
302
+ <ExportCSV
303
+ v-if="props.exportable && items.length"
304
+ icon="mdi:mdi-file-download"
305
+ variant="flat"
306
+ :file-name="title"
307
+ :model-value="items"
308
+ :color="toolbarColor"
309
+ />
310
+ <VBtn
311
+ :color="toolbarColor"
312
+ prepend-icon="mdi:mdi-plus"
313
+ variant="flat"
314
+ @click="openDialog()"
315
+ >
316
+ add
317
+ </VBtn>
318
+ <v-row align="center" class="mr-2 ml-2" v-if="viewSwitch">
319
+ <v-btn-toggle v-model="viewType" :multiple="viewSwitchMultiple">
320
+ <v-btn icon="mdi mdi-view-grid-outline" value="iterator"></v-btn>
321
+ <v-btn icon="mdi mdi-table-large" value="table"></v-btn>
322
+ </v-btn-toggle>
323
+ </v-row>
324
+ </VToolbarItems>
325
+ </VToolbar>
326
+ </slot>
327
+ <v-data-table
328
+ v-bind="plainAttrs"
329
+ color="primary"
330
+ :items="items"
331
+ :search="search"
332
+ v-if="viewType.includes('table')"
333
+ >
334
+ <!-- @ts-ignore -->
335
+ <template
336
+ v-for="(_, name, index) in (tableSlots as {})"
337
+ :key="index"
338
+ #[name]="slotData"
339
+ >
340
+ <slot
341
+ :name="name"
342
+ v-bind="((slotData || {}) as object)"
343
+ :operation="operation"
344
+ />
345
+ </template>
346
+ <template
347
+ v-if="!$slots['item.operation']"
348
+ #item.operation="props"
349
+ >
350
+ <v-icon density="compact" :disabled="props.index==0" @click="moveUpItem(props.item)">mdi:mdi-arrow-up-thick</v-icon>
351
+ <v-icon density="compact" @click="moveToItem(props.item)">fa:fas fa-arrow-right-to-bracket</v-icon>
352
+ <v-icon density="compact" :disabled="props.index==items.length-1" @click="moveDownItem(props.item)">mdi:mdi-arrow-down-thick</v-icon>
353
+ </template>
354
+ <template
355
+ v-if="!$slots['item.action']"
356
+ #item.action="{ item }"
357
+ >
358
+ <v-btn
359
+ variant="flat"
360
+ density="compact"
361
+ icon="mdi:mdi-note-edit"
362
+ @click="openDialog(item)"
363
+ />
364
+ <v-btn
365
+ variant="flat"
366
+ density="compact"
367
+ icon="mdi:mdi-delete"
368
+ @click="deleteItem(item)"
369
+ />
370
+ </template>
371
+ </v-data-table>
372
+ </template>
373
+ <template #footer="footerProps" v-if="viewType.includes('iterator')">
374
+ <v-container fluid>
375
+ <v-row
376
+ align-content="center"
377
+ justify="end"
378
+ dense
379
+ >
380
+ <v-spacer />
381
+ <v-select
382
+ v-model="itemsPerPageInternal"
383
+ density="compact"
384
+ variant="outlined"
385
+ :items="[6, 12, 18, 24, 30, { title: 'All', value: '-1' }]"
386
+ min-width="220"
387
+ max-width="220"
388
+ hide-details
389
+ >
390
+ <template #prepend>
391
+ Items per page:
392
+ </template>
393
+ </v-select>
394
+ <v-pagination
395
+ density="compact"
396
+ :length="footerProps.pageCount"
397
+ total-visible="6"
398
+ show-first-last-page
399
+ @first="footerProps.setPage(1)"
400
+ @last="footerProps.setPage(footerProps.pageCount)"
401
+ @next="footerProps.nextPage"
402
+ @prev="footerProps.prevPage"
403
+ @update:model-value="footerProps.setPage"
404
+ />
405
+ </v-row>
406
+ </v-container>
407
+ </template >
408
+ </v-data-iterator>
409
+ <FormDialog
410
+ v-model="isDialogOpen"
411
+ :title="title"
412
+ :fullscreen="dialogFullscreen"
413
+ :initial-data="initialData"
414
+ :form-data="currentItem"
415
+ @create="createItem"
416
+ @update="updateItem"
417
+ >
418
+ <template #default="slotData">
419
+ <slot
420
+ name="form"
421
+ v-bind="slotData"
422
+ />
423
+ </template>
424
+ </FormDialog>
425
+ </v-card>
426
+ </template>
@@ -58,8 +58,7 @@ function query(groupKey: string, filterText: string | undefined) {
58
58
  masterItems.value = result
59
59
  items.value = result
60
60
  isErrorLoading.value = false
61
- }).catch((error) => {
62
- console.log(error)
61
+ }).catch((_error) => {
63
62
  isErrorLoading.value = true
64
63
  }).finally(() => {
65
64
  isLoading.value = false
@@ -119,7 +118,8 @@ const computedNoDataText = computed(() => {
119
118
  const computedSortBy = computed(()=>{
120
119
  let sortByField = props.sortBy
121
120
  if (sortByField == 'itemValue') {
122
- return [itemTitleField.value,'itemValue','itemCode']
121
+ if (props.showCode) return ['itemCode']
122
+ else return [itemTitleField.value,'itemValue','itemCode']
123
123
  } else {
124
124
  return [sortByField]
125
125
  }
@@ -0,0 +1,141 @@
1
+ <script lang="ts" setup>
2
+ import {VAutocomplete} from 'vuetify/components/VAutocomplete'
3
+ import {concat, isEmpty, isArray, sortBy} from 'lodash-es'
4
+ import {computed, ref,watchEffect} from 'vue'
5
+ import {watchDebounced} from '@vueuse/core'
6
+ import {useFuzzy} from '../../composables/utils/fuzzy'
7
+ import {useGraphQlOperation} from "../../composables/graphqlOperation";
8
+
9
+ interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
10
+ fuzzy?: boolean
11
+ sortBy?: string
12
+ showCode?: boolean
13
+
14
+ modelName: string
15
+ modelBy?: object
16
+ itemTitle: string
17
+ itemValue: string
18
+ fields?: Array<string | object>
19
+ cache?: boolean
20
+
21
+ serverSearch?: boolean
22
+ serverSearchKey?: string
23
+ }
24
+
25
+ const props = withDefaults(defineProps<Props>(), {
26
+ fuzzy: false,
27
+ showCode: false,
28
+
29
+ cache: false,
30
+
31
+ serverSearch: false,
32
+ })
33
+
34
+ const modelItems = ref<Array<any>>([])
35
+ const items = ref<Array<any>>([])
36
+
37
+ const searchData = ref<string>('')
38
+
39
+ const isLoading = ref(false)
40
+ const isErrorLoading = ref(false)
41
+
42
+ watchEffect(()=>{
43
+ let fields: any[] = [props.itemTitle,props.itemValue]
44
+ if (props.fields) fields = concat(fields, props.fields)
45
+
46
+ const variables: Record<string, any> = Object.assign({},props.modelBy)
47
+ if (props.serverSearch && props.serverSearchKey) {
48
+ variables[props.serverSearchKey] = searchData.value
49
+ }
50
+
51
+ isLoading.value = true
52
+
53
+ useGraphQlOperation('Query',props.modelName,fields,variables,props.cache).then((result: any) => {
54
+ if (isArray(result)) {
55
+ modelItems.value = result
56
+ items.value = result
57
+ isErrorLoading.value = false
58
+ } else {
59
+ isErrorLoading.value = true
60
+ }
61
+ }).catch((_error) => {
62
+ isErrorLoading.value = true
63
+ }).finally(() => {
64
+ isLoading.value = false
65
+ })
66
+ })
67
+
68
+ async function fuzzySearch() {
69
+ if (props.fuzzy) {
70
+ if (isEmpty(searchData.value)) {
71
+ items.value = modelItems.value
72
+ }
73
+ else {
74
+ let fields: any[] = [props.itemTitle,props.itemValue]
75
+ if (props.fields) fields = concat(fields, props.fields)
76
+
77
+ const results: any = useFuzzy(searchData.value, modelItems.value, fields)
78
+ items.value = []
79
+ if (results.value.length) {
80
+ for (let i = 0; results.value.length > i; i++) {
81
+ if (results.value[i].item) items.value.push(results.value[i].item)
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ watchDebounced(searchData, fuzzySearch, { debounce: 1000, maxWait: 5000 })
89
+
90
+ const computedItems = computed(()=>{
91
+ let sortByField = props.sortBy || ((props.showCode) ? props.itemValue : props.itemTitle)
92
+
93
+ if (props.fuzzy && !isEmpty(searchData.value)) {
94
+ return items.value
95
+ } else {
96
+ return sortBy(items.value, [sortByField])
97
+ }
98
+ })
99
+ </script>
100
+
101
+ <template>
102
+ <v-autocomplete
103
+ v-model:search="searchData"
104
+ v-bind="$attrs"
105
+ :items="computedItems"
106
+ :no-filter="props.fuzzy"
107
+ :item-title="props.itemTitle"
108
+ :item-value="props.itemValue"
109
+ :loading="isLoading"
110
+ >
111
+ <!-- @ts-ignore -->
112
+ <template
113
+ v-for="(_, name, index) in ($slots as {})"
114
+ :key="index"
115
+ #[name]="slotData"
116
+ >
117
+ <slot
118
+ :name="name"
119
+ v-bind="((slotData || {}) as object)"
120
+ :operation="operation"
121
+ />
122
+ </template>
123
+ <template
124
+ v-if="!$slots.item"
125
+ #item="{ props, item }"
126
+ >
127
+ <v-list-item
128
+ v-bind="props"
129
+ :title="(showCode ? item.value+'-' : '')+item.title"
130
+ />
131
+ </template>
132
+ <template
133
+ v-if="isErrorLoading"
134
+ #append
135
+ >
136
+ <v-icon color="error">
137
+ mdi mdi-alert
138
+ </v-icon>
139
+ </template>
140
+ </v-autocomplete>
141
+ </template>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
- import {computed, nextTick, ref, useAttrs, watch} from 'vue'
2
+ import {computed, nextTick, ref, useAttrs, useSlots, watch} from 'vue'
3
3
  import {VDataIterator} from 'vuetify/components/VDataIterator'
4
+ import {VDataTable} from "vuetify/components/VDataTable";
4
5
  import {omit} from 'lodash-es'
5
6
  import type {GraphqlModelProps} from '../../composables/graphqlModel'
6
7
  import {useGraphqlModel} from '../../composables/graphqlModel'
@@ -20,6 +21,10 @@ interface Props extends /* @vue-ignore */ InstanceType<typeof VDataIterator['$pr
20
21
  insertable?: boolean
21
22
  searchable?: boolean
22
23
  search?: string
24
+
25
+ viewSwitch?: boolean
26
+ viewSwitchMultiple?: boolean
27
+
23
28
  cols?: string | number | boolean
24
29
  xxl?: string | number | boolean
25
30
  xl?: string | number | boolean
@@ -40,6 +45,10 @@ const props = withDefaults(defineProps<Props & GraphqlModelProps>(), {
40
45
  modelKey: 'id',
41
46
  modelBy: undefined,
42
47
  fields: () => [],
48
+
49
+ viewSwitch: false,
50
+ viewSwitchMultiple:false,
51
+
43
52
  cols: 12,
44
53
  xxl: false,
45
54
  xl: false,
@@ -55,6 +64,13 @@ const plainAttrs = computed(() => {
55
64
  if (props.headers) returnAttrs['headers'] = props.headers
56
65
  return returnAttrs
57
66
  })
67
+ const slots = useSlots()
68
+ const tableSlots = computed(() => {
69
+ return omit(slots, ['item'])
70
+ })
71
+
72
+ const viewType = ref<string[] | string>('iterator')
73
+
58
74
  const currentItem = ref<Record<string, any> | undefined>(undefined)
59
75
  const isDialogOpen = ref<boolean>(false)
60
76
 
@@ -126,7 +142,7 @@ defineExpose({ reload })
126
142
  :search="search"
127
143
  :loading="isLoading"
128
144
  >
129
- <template #default="defaultProps">
145
+ <template #default="defaultProps" v-if="viewType.includes('iterator')">
130
146
  <slot
131
147
  v-bind="defaultProps"
132
148
  :operation="operation"
@@ -153,7 +169,7 @@ defineExpose({ reload })
153
169
  </v-container>
154
170
  </slot>
155
171
  </template>
156
- <template #loader="loaderProps">
172
+ <template #loader="loaderProps" v-if="viewType.includes('iterator')">
157
173
  <slot
158
174
  name="loader"
159
175
  v-bind="loaderProps"
@@ -252,11 +268,103 @@ defineExpose({ reload })
252
268
  >
253
269
  add
254
270
  </VBtn>
271
+ <v-row align="center" class="mr-2 ml-2" v-if="viewSwitch">
272
+ <v-btn-toggle v-model="viewType" :multiple="viewSwitchMultiple">
273
+ <v-btn icon="mdi mdi-view-grid-outline" value="iterator"></v-btn>
274
+ <v-btn icon="mdi mdi-table-large" value="table"></v-btn>
275
+ </v-btn-toggle>
276
+ </v-row>
255
277
  </VToolbarItems>
256
278
  </VToolbar>
257
279
  </slot>
280
+ <template v-if="viewType.includes('table')">
281
+ <v-data-table-server
282
+ v-if="canServerPageable"
283
+ v-bind="plainAttrs"
284
+ color="primary"
285
+ :items="items"
286
+ :items-length="itemsLength"
287
+ :item-value="props.modelKey"
288
+ :search="search"
289
+ :loading="isLoading"
290
+ @update:options="loadItems"
291
+ >
292
+ <!-- @ts-ignore -->
293
+ <template
294
+ v-for="(_, name, index) in (tableSlots as {})"
295
+ :key="index"
296
+ #[name]="slotData"
297
+ >
298
+ <slot
299
+ :name="name"
300
+ v-bind="((slotData || {}) as object)"
301
+ :operation="operation"
302
+ />
303
+ </template>
304
+ <template
305
+ v-if="!$slots['item.action']"
306
+ #item.action="{ item }"
307
+ >
308
+ <v-btn
309
+ v-if="canUpdate"
310
+ variant="flat"
311
+ density="compact"
312
+ icon="mdi mdi-note-edit"
313
+ @click="openDialog(item)"
314
+ />
315
+ <v-btn
316
+ v-if="canDelete"
317
+ variant="flat"
318
+ density="compact"
319
+ icon="mdi mdi-delete"
320
+ @click="deleteItem(item)"
321
+ />
322
+ </template>
323
+ </v-data-table-server>
324
+ <v-data-table
325
+ v-else
326
+ v-bind="plainAttrs"
327
+ color="primary"
328
+ :items="items"
329
+ :item-value="props.modelKey"
330
+ :search="search"
331
+ :loading="isLoading"
332
+ >
333
+ <!-- @ts-ignore -->
334
+ <template
335
+ v-for="(_, name, index) in (tableSlots as {})"
336
+ :key="index"
337
+ #[name]="slotData"
338
+ >
339
+ <slot
340
+ :name="name"
341
+ v-bind="((slotData || {}) as object)"
342
+ :operation="operation"
343
+ />
344
+ </template>
345
+ <template
346
+ v-if="!$slots['item.action']"
347
+ #item.action="{ item }"
348
+ >
349
+ <v-btn
350
+ v-if="canUpdate"
351
+ variant="flat"
352
+ density="compact"
353
+ icon="mdi mdi-note-edit"
354
+ @click="openDialog(item)"
355
+ />
356
+ <v-btn
357
+ v-if="canDelete"
358
+ variant="flat"
359
+ density="compact"
360
+ icon="mdi mdi-delete"
361
+ @click="deleteItem(item)"
362
+ />
363
+ </template>
364
+ </v-data-table>
365
+ </template>
258
366
  </template>
259
- <template #footer="footerProps">
367
+ <template #footer="footerProps" v-if="viewType.includes('iterator')">
260
368
  <v-container fluid>
261
369
  <v-row
262
370
  align-content="center"
@@ -288,7 +396,7 @@ defineExpose({ reload })
288
396
  @last="footerProps.setPage(footerProps.pageCount)"
289
397
  @next="footerProps.nextPage"
290
398
  @prev="footerProps.prevPage"
291
- @update:model-value="footerProps.setPage"
399
+ @update:model-value="footerProps.setPage"
292
400
  />
293
401
  <v-pagination
294
402
  v-else
@@ -71,8 +71,17 @@ export function useGraphqlModel(props) {
71
71
  isLoading.value = true;
72
72
  const importPromise = [];
73
73
  importItems2.forEach((item) => {
74
- const eachPromise = createItem(Object.assign({}, props.initialData, item), void 0, true);
75
- if (eachPromise) importPromise.push(eachPromise);
74
+ if (item[props.modelKey || "id"]) {
75
+ importPromise.push(operationUpdate.value?.call(fields.value, { input: item }).then((result) => {
76
+ if (!result) {
77
+ return createItem(Object.assign({}, props.initialData, item), void 0, true);
78
+ }
79
+ }).catch((error) => {
80
+ alert?.addAlert({ alertType: "error", message: error });
81
+ }));
82
+ } else {
83
+ importPromise.push(createItem(Object.assign({}, props.initialData, item), void 0, true));
84
+ }
76
85
  });
77
86
  Promise.all(importPromise).finally(() => {
78
87
  isLoading.value = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramathibodi/nuxt-commons",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Ramathibodi Nuxt modules for common components",
5
5
  "repository": {
6
6
  "type": "git",