@ramathibodi/nuxt-commons 0.1.74 → 0.1.75

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.
Files changed (96) hide show
  1. package/README.md +115 -115
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/Alert.vue +58 -58
  4. package/dist/runtime/components/BarcodeReader.vue +130 -130
  5. package/dist/runtime/components/ExportCSV.vue +110 -110
  6. package/dist/runtime/components/FileBtn.vue +79 -79
  7. package/dist/runtime/components/ImportCSV.vue +151 -151
  8. package/dist/runtime/components/MrzReader.vue +168 -168
  9. package/dist/runtime/components/SplitterPanel.vue +67 -67
  10. package/dist/runtime/components/TabsGroup.vue +39 -39
  11. package/dist/runtime/components/TextBarcode.vue +66 -66
  12. package/dist/runtime/components/device/IdCardButton.vue +95 -95
  13. package/dist/runtime/components/device/IdCardWebSocket.vue +207 -207
  14. package/dist/runtime/components/device/Scanner.vue +350 -350
  15. package/dist/runtime/components/dialog/Confirm.vue +112 -112
  16. package/dist/runtime/components/dialog/Host.vue +88 -88
  17. package/dist/runtime/components/dialog/Index.vue +84 -84
  18. package/dist/runtime/components/dialog/Loading.vue +51 -51
  19. package/dist/runtime/components/dialog/default/Confirm.vue +112 -112
  20. package/dist/runtime/components/dialog/default/Loading.vue +60 -60
  21. package/dist/runtime/components/dialog/default/Notify.vue +82 -82
  22. package/dist/runtime/components/dialog/default/Printing.vue +46 -46
  23. package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -144
  24. package/dist/runtime/components/document/Form.vue +50 -50
  25. package/dist/runtime/components/document/TemplateBuilder.vue +536 -536
  26. package/dist/runtime/components/form/ActionPad.vue +156 -156
  27. package/dist/runtime/components/form/Birthdate.vue +116 -116
  28. package/dist/runtime/components/form/CheckboxGroup.vue +99 -99
  29. package/dist/runtime/components/form/CodeEditor.vue +45 -45
  30. package/dist/runtime/components/form/Date.vue +270 -270
  31. package/dist/runtime/components/form/DateTime.vue +220 -220
  32. package/dist/runtime/components/form/Dialog.vue +178 -178
  33. package/dist/runtime/components/form/EditPad.vue +157 -157
  34. package/dist/runtime/components/form/File.vue +295 -295
  35. package/dist/runtime/components/form/Hidden.vue +44 -44
  36. package/dist/runtime/components/form/Iterator.vue +538 -538
  37. package/dist/runtime/components/form/Login.vue +143 -143
  38. package/dist/runtime/components/form/Pad.vue +399 -399
  39. package/dist/runtime/components/form/SignPad.vue +226 -226
  40. package/dist/runtime/components/form/System.vue +34 -34
  41. package/dist/runtime/components/form/Table.vue +391 -391
  42. package/dist/runtime/components/form/TableData.vue +236 -236
  43. package/dist/runtime/components/form/Time.vue +177 -177
  44. package/dist/runtime/components/form/images/Capture.vue +245 -245
  45. package/dist/runtime/components/form/images/Edit.vue +133 -133
  46. package/dist/runtime/components/form/images/Field.vue +331 -331
  47. package/dist/runtime/components/form/images/Pad.vue +54 -54
  48. package/dist/runtime/components/label/Date.vue +37 -37
  49. package/dist/runtime/components/label/DateAgo.vue +102 -102
  50. package/dist/runtime/components/label/DateCount.vue +152 -152
  51. package/dist/runtime/components/label/Field.vue +111 -111
  52. package/dist/runtime/components/label/FormatMoney.vue +37 -37
  53. package/dist/runtime/components/label/Mask.vue +46 -46
  54. package/dist/runtime/components/label/Object.vue +21 -21
  55. package/dist/runtime/components/master/Autocomplete.vue +89 -89
  56. package/dist/runtime/components/master/Combobox.vue +88 -88
  57. package/dist/runtime/components/master/RadioGroup.vue +90 -90
  58. package/dist/runtime/components/master/Select.vue +70 -70
  59. package/dist/runtime/components/master/label.vue +55 -55
  60. package/dist/runtime/components/model/Autocomplete.vue +91 -91
  61. package/dist/runtime/components/model/Combobox.vue +90 -90
  62. package/dist/runtime/components/model/Pad.vue +114 -114
  63. package/dist/runtime/components/model/Select.vue +78 -84
  64. package/dist/runtime/components/model/Table.vue +370 -370
  65. package/dist/runtime/components/model/iterator.vue +497 -497
  66. package/dist/runtime/components/model/label.vue +58 -58
  67. package/dist/runtime/components/pdf/Print.vue +75 -75
  68. package/dist/runtime/components/pdf/View.vue +146 -146
  69. package/dist/runtime/composables/dialog.d.ts +1 -1
  70. package/dist/runtime/composables/graphql.d.ts +1 -1
  71. package/dist/runtime/composables/graphqlModel.d.ts +9 -9
  72. package/dist/runtime/composables/graphqlModelItem.d.ts +7 -7
  73. package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
  74. package/dist/runtime/composables/userPermission.d.ts +1 -1
  75. package/dist/runtime/labs/Calendar.vue +99 -99
  76. package/dist/runtime/labs/form/EditMobile.vue +152 -152
  77. package/dist/runtime/labs/form/TextFieldMask.vue +43 -43
  78. package/dist/runtime/plugins/clientConfig.d.ts +1 -1
  79. package/dist/runtime/plugins/default.d.ts +1 -1
  80. package/dist/runtime/plugins/dialogManager.d.ts +1 -1
  81. package/dist/runtime/plugins/permission.d.ts +1 -1
  82. package/dist/runtime/types/alert.d.ts +11 -11
  83. package/dist/runtime/types/clientConfig.d.ts +13 -13
  84. package/dist/runtime/types/dialogManager.d.ts +35 -35
  85. package/dist/runtime/types/formDialog.d.ts +5 -5
  86. package/dist/runtime/types/graphqlOperation.d.ts +23 -23
  87. package/dist/runtime/types/menu.d.ts +31 -31
  88. package/dist/runtime/types/modules.d.ts +7 -7
  89. package/dist/runtime/types/permission.d.ts +13 -13
  90. package/package.json +131 -131
  91. package/scripts/enrich-vue-docs-from-ai.mjs +197 -197
  92. package/scripts/generate-ai-summary.mjs +321 -321
  93. package/scripts/generate-composables-md.mjs +129 -129
  94. package/scripts/postInstall.cjs +70 -70
  95. package/templates/.codegen/codegen.ts +32 -32
  96. package/templates/.codegen/plugin-schema-object.js +161 -161
@@ -1,370 +1,370 @@
1
- <script lang="ts" setup>
2
- /**
3
- * ModelTable connects model metadata to reusable selection, labeling, iterator, or table UI patterns.
4
- * This doc block is consumed by vue-docgen for generated API documentation.
5
- */
6
- import {computed,watch, nextTick, ref, useAttrs} from 'vue'
7
- import {VDataTable} from 'vuetify/components/VDataTable'
8
- import {clone} from 'lodash-es'
9
- import {useGraphqlModel} from '../../composables/graphqlModel'
10
- import {useDialog} from "../../composables/dialog"
11
- import {useUserPermission} from "../../composables/userPermission"
12
- import type {GraphqlModelProps} from '../../composables/graphqlModel'
13
- import type {FormDialogCallback} from "../../types/formDialog";
14
- import {type AuthenticationState, useState} from "#imports";
15
-
16
- defineOptions({
17
- inheritAttrs: false,
18
- })
19
-
20
- interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
21
- title: string // Toolbar title and default export filename for this model table.
22
- noDataText?: string // Message shown when query returns no rows.
23
- dialogWidth?: string | number // Width passed to create/edit model dialog.
24
- dialogMaxWidth?: string | number // Max-width cap for model dialog on large displays.
25
- dialogHeight?: string | number // Height passed to create/edit model dialog.
26
- dialogMaxHeight?: string | number // Max-height cap for model dialog before internal scrolling.
27
- dialogFullscreen?: boolean // Opens model dialog fullscreen by default.
28
- initialData?: Record<string, any> // Base values merged into create payload before dialog submission.
29
- toolbarColor?: string // Color used by toolbar and built-in action buttons.
30
- importable?: boolean // Enables CSV/XLS import action (requires create permission).
31
- exportable?: boolean // Enables export action for currently loaded model items.
32
- insertable?: boolean // Shows Add button and allows create flow.
33
- searchable?: boolean // Shows search input and forwards keyword into GraphQL model search state.
34
- search?: string // External search keyword; watcher keeps internal model search in sync.
35
- saveAndStay?: boolean // Forwards to dialog form to keep modal open after successful save.
36
- stringFields?: Array<string> // Field paths that must remain plain strings during import/export transforms.
37
- onlyOwnerEdit?: boolean // When true, edit/delete are allowed only for rows created by current user.
38
- onlyOwnerOverridePermission?: string | string[] // Permission key(s) that bypass `onlyOwnerEdit` restriction.
39
- }
40
-
41
- /**
42
- * Public props accepted by ModelTable.
43
- * Document each prop field with intent, defaults, and side effects for clear generated docs.
44
- */
45
- const props = withDefaults(defineProps<Props & GraphqlModelProps>(), {
46
- noDataText: 'ไม่พบข้อมูล',
47
- dialogFullscreen: false,
48
- toolbarColor: 'primary',
49
- importable: true,
50
- exportable: true,
51
- insertable: true,
52
- searchable: true,
53
- saveAndStay: false,
54
- modelKey: 'id',
55
- modelBy: undefined,
56
- fields: () => [],
57
- stringFields: ()=>[],
58
- onlyOwnerEdit: false,
59
- })
60
-
61
- /**
62
- * Custom events emitted by ModelTable.
63
- * Parents can listen to these events to react to user actions and internal state changes.
64
- */
65
- const emit = defineEmits(['open:dialog','close:dialog','create','update','delete'])
66
- const attrs = useAttrs()
67
- const plainAttrs = computed(() => {
68
- const returnAttrs = clone(attrs)
69
- if (props.headers) returnAttrs['headers'] = props.headers
70
- return returnAttrs
71
- })
72
-
73
- const authenState = useState<AuthenticationState>("authentication")
74
- const currentUsername = computed(()=>{
75
- let userProfile = authenState.value?.userProfile as {username: string}
76
- return userProfile?.username ?? ""
77
- })
78
-
79
- const currentItem = ref<Record<string, any> | undefined>(undefined)
80
- const isDialogOpen = ref<boolean>(false)
81
- const isDialogReadonly = ref<boolean>(false)
82
-
83
- const { items, itemsLength,
84
- search,setSearch,
85
- canServerPageable, canServerSearch, canCreate, canUpdate, canDelete,
86
- createItem, importItems, updateItem, deleteItem,
87
- loadItems, reload,
88
- isLoading } = useGraphqlModel(props)
89
-
90
- function openDialog(item?: object) {
91
- isDialogReadonly.value = false
92
- currentItem.value = item
93
- nextTick(() => {
94
- isDialogOpen.value = true
95
- emit('open:dialog' , item)
96
- })
97
- }
98
-
99
- function openDialogReadonly(item?: object) {
100
- isDialogReadonly.value = true
101
- currentItem.value = item
102
- nextTick(() => {
103
- isDialogOpen.value = true
104
- emit('open:dialog' , item)
105
- })
106
- }
107
-
108
- async function confirmDeleteItem(item: Record<string, any>, callback?: FormDialogCallback) {
109
- let confirm = await useDialog().confirm({message: "Do you want to delete record?"})
110
- if (confirm) onDeleteItem(item,callback)
111
- }
112
-
113
- const onCreateItem = (item: Record<string, any>,callback?: FormDialogCallback)=>{
114
- createItem(item,callback).then(()=>{
115
- emit("create",item,callback)
116
- })
117
- }
118
-
119
- const onUpdateItem = (item: Record<string, any>,callback?: FormDialogCallback)=>{
120
- updateItem(item,callback).then(()=>{
121
- emit("update",item,callback)
122
- })
123
- }
124
-
125
- const onDeleteItem = (item: Record<string, any>,callback?: FormDialogCallback)=>{
126
- deleteItem(item,callback).then(()=>{
127
- emit("delete",item,callback)
128
- })
129
- }
130
-
131
- const canEditRow = function (item: Record<string, any>) {
132
- if (props.onlyOwnerEdit) {
133
- if (item?.userstampField?.createdBy && item?.userstampField?.createdBy==currentUsername.value) return true
134
- if (!!props.onlyOwnerOverridePermission && useUserPermission().check(props.onlyOwnerOverridePermission)) return true
135
- return !item?.userstampField?.createdBy
136
- }
137
- return true
138
- }
139
-
140
- const operation = ref({ openDialog, openDialogReadonly, createItem, importItems, updateItem, deleteItem, reload, setSearch, canServerPageable, canServerSearch, canCreate, canUpdate, canDelete, canEditRow, onlyOwnerEdit: props.onlyOwnerEdit, onlyOwnerOverridePermission: props.onlyOwnerOverridePermission })
141
-
142
- const computedInitialData = computed(() => {
143
- return Object.assign({}, props.initialData, props.modelBy)
144
- })
145
-
146
- watch(()=>props.search,()=>{
147
- search.value = props.search
148
- },{immediate:true})
149
-
150
- defineExpose({ reload,operation,items })
151
- </script>
152
-
153
- <template>
154
- <v-card>
155
- <slot
156
- name="header"
157
- :items="items"
158
- :operation="operation"
159
- >
160
- <VToolbar :color="toolbarColor">
161
- <v-row
162
- justify="end"
163
- class="ma-1"
164
- dense
165
- no-gutters
166
- align="center"
167
- >
168
- <v-col cols="7">
169
- <VToolbarTitle class="pl-3">
170
- <slot
171
- name="title"
172
- :reload="reload"
173
- >
174
- {{ title }}
175
- <v-icon
176
- size="small"
177
- @click="reload"
178
- >
179
- mdi mdi-refresh
180
- </v-icon>
181
- </slot>
182
- </VToolbarTitle>
183
- </v-col>
184
- <v-col cols="5">
185
- <slot name="search" :items="items" :operation="operation" v-if="props.searchable">
186
- <VTextField
187
- v-model="search"
188
- class="justify-end w-100"
189
- density="compact"
190
- hide-details
191
- placeholder="ค้นหา"
192
- clearable
193
- variant="solo"
194
- />
195
- </slot>
196
- </v-col>
197
- </v-row>
198
-
199
- <VToolbarItems>
200
- <slot name="toolbarItems" :items="items" :operation="operation"/>
201
- <ImportCSV
202
- v-if="props.importable && canCreate && props.insertable"
203
- icon="mdi mdi-file-upload"
204
- variant="flat"
205
- :color="toolbarColor"
206
- :stringFields="props.stringFields"
207
- @import="importItems"
208
- />
209
- <ExportCSV
210
- v-if="props.exportable && items.length"
211
- icon="mdi mdi-file-download"
212
- variant="flat"
213
- :file-name="title"
214
- :model-value="items"
215
- :color="toolbarColor"
216
- :stringFields="props.stringFields"
217
- />
218
- <VBtn
219
- v-if="canCreate && props.insertable"
220
- :color="toolbarColor"
221
- prepend-icon="mdi mdi-plus"
222
- variant="flat"
223
- @click="openDialog()"
224
- >
225
- add
226
- </VBtn>
227
- </VToolbarItems>
228
- </VToolbar>
229
- </slot>
230
- <v-data-table-server
231
- v-if="canServerPageable"
232
- v-bind="plainAttrs"
233
- color="primary"
234
- :items="items"
235
- :items-length="itemsLength"
236
- :item-value="props.modelKey"
237
- :search="search"
238
- :loading="isLoading"
239
- @update:options="loadItems"
240
- >
241
- <!-- @ts-ignore -->
242
- <template
243
- v-for="(_, name, index) in ($slots as {})"
244
- :key="index"
245
- #[name]="slotData"
246
- >
247
- <slot
248
- :name="name"
249
- v-bind="((slotData || {}) as object)"
250
- :operation="operation"
251
- />
252
- </template>
253
- <template
254
- v-if="!$slots['item.action']"
255
- #item.action="{ item }"
256
- >
257
- <v-btn
258
- v-if="!canUpdate || !canEditRow(item)"
259
- variant="flat"
260
- density="compact"
261
- icon="mdi mdi-note-search"
262
- @click="openDialogReadonly(item)"
263
- />
264
- <v-btn
265
- v-if="canUpdate && canEditRow(item)"
266
- variant="flat"
267
- density="compact"
268
- icon="mdi mdi-note-edit"
269
- @click="openDialog(item)"
270
- />
271
- <v-btn
272
- v-if="canDelete && canEditRow(item)"
273
- variant="flat"
274
- density="compact"
275
- icon="mdi mdi-delete"
276
- @click="confirmDeleteItem(item)"
277
- />
278
- </template>
279
- </v-data-table-server>
280
- <v-data-table
281
- v-else
282
- v-bind="plainAttrs"
283
- color="primary"
284
- :items="items"
285
- :item-value="props.modelKey"
286
- :search="search"
287
- :loading="isLoading"
288
- >
289
- <!-- @ts-ignore -->
290
- <template
291
- v-for="(_, name, index) in ($slots as {})"
292
- :key="index"
293
- #[name]="slotData"
294
- >
295
- <slot
296
- :name="name"
297
- v-bind="((slotData || {}) as object)"
298
- :operation="operation"
299
- />
300
- </template>
301
- <template
302
- v-if="!$slots['item.action']"
303
- #item.action="{ item }"
304
- >
305
- <v-btn
306
- v-if="!canUpdate || !canEditRow(item)"
307
- variant="flat"
308
- density="compact"
309
- icon="mdi mdi-note-search"
310
- @click="openDialogReadonly(item)"
311
- />
312
- <v-btn
313
- v-if="canUpdate && canEditRow(item)"
314
- variant="flat"
315
- density="compact"
316
- icon="mdi mdi-note-edit"
317
- @click="openDialog(item)"
318
- />
319
- <v-btn
320
- v-if="canDelete && canEditRow(item)"
321
- variant="flat"
322
- density="compact"
323
- icon="mdi mdi-delete"
324
- @click="confirmDeleteItem(item)"
325
- />
326
- </template>
327
- </v-data-table>
328
- <FormDialog
329
- v-model="isDialogOpen"
330
- :title="title"
331
- :fullscreen="dialogFullscreen"
332
- :initial-data="computedInitialData"
333
- :form-data="currentItem"
334
- @create="onCreateItem"
335
- @update="onUpdateItem"
336
- @afterLeave="emit('close:dialog')"
337
- :saveAndStay="saveAndStay"
338
- :readonly="isDialogReadonly"
339
- :width="dialogWidth"
340
- :height="dialogHeight"
341
- :max-width="dialogMaxWidth"
342
- :max-height="dialogMaxHeight"
343
- >
344
- <template #default="slotData">
345
- <slot
346
- name="form"
347
- :tableItems="items"
348
- :tableOperation="operation"
349
- v-bind="slotData"
350
- />
351
- </template>
352
- <template #title="slotData">
353
- <slot
354
- name="formTitle"
355
- :tableItems="items"
356
- :tableOperation="operation"
357
- v-bind="slotData"
358
- />
359
- </template>
360
- <template #action="slotData">
361
- <slot
362
- name="formAction"
363
- :tableItems="items"
364
- :tableOperation="operation"
365
- v-bind="slotData"
366
- />
367
- </template>
368
- </FormDialog>
369
- </v-card>
370
- </template>
1
+ <script lang="ts" setup>
2
+ /**
3
+ * ModelTable connects model metadata to reusable selection, labeling, iterator, or table UI patterns.
4
+ * This doc block is consumed by vue-docgen for generated API documentation.
5
+ */
6
+ import {computed,watch, nextTick, ref, useAttrs} from 'vue'
7
+ import {VDataTable} from 'vuetify/components/VDataTable'
8
+ import {clone} from 'lodash-es'
9
+ import {useGraphqlModel} from '../../composables/graphqlModel'
10
+ import {useDialog} from "../../composables/dialog"
11
+ import {useUserPermission} from "../../composables/userPermission"
12
+ import type {GraphqlModelProps} from '../../composables/graphqlModel'
13
+ import type {FormDialogCallback} from "../../types/formDialog";
14
+ import {type AuthenticationState, useState} from "#imports";
15
+
16
+ defineOptions({
17
+ inheritAttrs: false,
18
+ })
19
+
20
+ interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
21
+ title: string // Toolbar title and default export filename for this model table.
22
+ noDataText?: string // Message shown when query returns no rows.
23
+ dialogWidth?: string | number // Width passed to create/edit model dialog.
24
+ dialogMaxWidth?: string | number // Max-width cap for model dialog on large displays.
25
+ dialogHeight?: string | number // Height passed to create/edit model dialog.
26
+ dialogMaxHeight?: string | number // Max-height cap for model dialog before internal scrolling.
27
+ dialogFullscreen?: boolean // Opens model dialog fullscreen by default.
28
+ initialData?: Record<string, any> // Base values merged into create payload before dialog submission.
29
+ toolbarColor?: string // Color used by toolbar and built-in action buttons.
30
+ importable?: boolean // Enables CSV/XLS import action (requires create permission).
31
+ exportable?: boolean // Enables export action for currently loaded model items.
32
+ insertable?: boolean // Shows Add button and allows create flow.
33
+ searchable?: boolean // Shows search input and forwards keyword into GraphQL model search state.
34
+ search?: string // External search keyword; watcher keeps internal model search in sync.
35
+ saveAndStay?: boolean // Forwards to dialog form to keep modal open after successful save.
36
+ stringFields?: Array<string> // Field paths that must remain plain strings during import/export transforms.
37
+ onlyOwnerEdit?: boolean // When true, edit/delete are allowed only for rows created by current user.
38
+ onlyOwnerOverridePermission?: string | string[] // Permission key(s) that bypass `onlyOwnerEdit` restriction.
39
+ }
40
+
41
+ /**
42
+ * Public props accepted by ModelTable.
43
+ * Document each prop field with intent, defaults, and side effects for clear generated docs.
44
+ */
45
+ const props = withDefaults(defineProps<Props & GraphqlModelProps>(), {
46
+ noDataText: 'ไม่พบข้อมูล',
47
+ dialogFullscreen: false,
48
+ toolbarColor: 'primary',
49
+ importable: true,
50
+ exportable: true,
51
+ insertable: true,
52
+ searchable: true,
53
+ saveAndStay: false,
54
+ modelKey: 'id',
55
+ modelBy: undefined,
56
+ fields: () => [],
57
+ stringFields: ()=>[],
58
+ onlyOwnerEdit: false,
59
+ })
60
+
61
+ /**
62
+ * Custom events emitted by ModelTable.
63
+ * Parents can listen to these events to react to user actions and internal state changes.
64
+ */
65
+ const emit = defineEmits(['open:dialog','close:dialog','create','update','delete'])
66
+ const attrs = useAttrs()
67
+ const plainAttrs = computed(() => {
68
+ const returnAttrs = clone(attrs)
69
+ if (props.headers) returnAttrs['headers'] = props.headers
70
+ return returnAttrs
71
+ })
72
+
73
+ const authenState = useState<AuthenticationState>("authentication")
74
+ const currentUsername = computed(()=>{
75
+ let userProfile = authenState.value?.userProfile as {username: string}
76
+ return userProfile?.username ?? ""
77
+ })
78
+
79
+ const currentItem = ref<Record<string, any> | undefined>(undefined)
80
+ const isDialogOpen = ref<boolean>(false)
81
+ const isDialogReadonly = ref<boolean>(false)
82
+
83
+ const { items, itemsLength,
84
+ search,setSearch,
85
+ canServerPageable, canServerSearch, canCreate, canUpdate, canDelete,
86
+ createItem, importItems, updateItem, deleteItem,
87
+ loadItems, reload,
88
+ isLoading } = useGraphqlModel(props)
89
+
90
+ function openDialog(item?: object) {
91
+ isDialogReadonly.value = false
92
+ currentItem.value = item
93
+ nextTick(() => {
94
+ isDialogOpen.value = true
95
+ emit('open:dialog' , item)
96
+ })
97
+ }
98
+
99
+ function openDialogReadonly(item?: object) {
100
+ isDialogReadonly.value = true
101
+ currentItem.value = item
102
+ nextTick(() => {
103
+ isDialogOpen.value = true
104
+ emit('open:dialog' , item)
105
+ })
106
+ }
107
+
108
+ async function confirmDeleteItem(item: Record<string, any>, callback?: FormDialogCallback) {
109
+ let confirm = await useDialog().confirm({message: "Do you want to delete record?"})
110
+ if (confirm) onDeleteItem(item,callback)
111
+ }
112
+
113
+ const onCreateItem = (item: Record<string, any>,callback?: FormDialogCallback)=>{
114
+ createItem(item,callback).then(()=>{
115
+ emit("create",item,callback)
116
+ })
117
+ }
118
+
119
+ const onUpdateItem = (item: Record<string, any>,callback?: FormDialogCallback)=>{
120
+ updateItem(item,callback).then(()=>{
121
+ emit("update",item,callback)
122
+ })
123
+ }
124
+
125
+ const onDeleteItem = (item: Record<string, any>,callback?: FormDialogCallback)=>{
126
+ deleteItem(item,callback).then(()=>{
127
+ emit("delete",item,callback)
128
+ })
129
+ }
130
+
131
+ const canEditRow = function (item: Record<string, any>) {
132
+ if (props.onlyOwnerEdit) {
133
+ if (item?.userstampField?.createdBy && item?.userstampField?.createdBy==currentUsername.value) return true
134
+ if (!!props.onlyOwnerOverridePermission && useUserPermission().check(props.onlyOwnerOverridePermission)) return true
135
+ return !item?.userstampField?.createdBy
136
+ }
137
+ return true
138
+ }
139
+
140
+ const operation = ref({ openDialog, openDialogReadonly, createItem, importItems, updateItem, deleteItem, reload, setSearch, canServerPageable, canServerSearch, canCreate, canUpdate, canDelete, canEditRow, onlyOwnerEdit: props.onlyOwnerEdit, onlyOwnerOverridePermission: props.onlyOwnerOverridePermission })
141
+
142
+ const computedInitialData = computed(() => {
143
+ return Object.assign({}, props.initialData, props.modelBy)
144
+ })
145
+
146
+ watch(()=>props.search,()=>{
147
+ search.value = props.search
148
+ },{immediate:true})
149
+
150
+ defineExpose({ reload,operation,items })
151
+ </script>
152
+
153
+ <template>
154
+ <v-card>
155
+ <slot
156
+ name="header"
157
+ :items="items"
158
+ :operation="operation"
159
+ >
160
+ <VToolbar :color="toolbarColor">
161
+ <v-row
162
+ justify="end"
163
+ class="ma-1"
164
+ dense
165
+ no-gutters
166
+ align="center"
167
+ >
168
+ <v-col cols="7">
169
+ <VToolbarTitle class="pl-3">
170
+ <slot
171
+ name="title"
172
+ :reload="reload"
173
+ >
174
+ {{ title }}
175
+ <v-icon
176
+ size="small"
177
+ @click="reload"
178
+ >
179
+ mdi mdi-refresh
180
+ </v-icon>
181
+ </slot>
182
+ </VToolbarTitle>
183
+ </v-col>
184
+ <v-col cols="5">
185
+ <slot name="search" :items="items" :operation="operation" v-if="props.searchable">
186
+ <VTextField
187
+ v-model="search"
188
+ class="justify-end w-100"
189
+ density="compact"
190
+ hide-details
191
+ placeholder="ค้นหา"
192
+ clearable
193
+ variant="solo"
194
+ />
195
+ </slot>
196
+ </v-col>
197
+ </v-row>
198
+
199
+ <VToolbarItems>
200
+ <slot name="toolbarItems" :items="items" :operation="operation"/>
201
+ <ImportCSV
202
+ v-if="props.importable && canCreate && props.insertable"
203
+ icon="mdi mdi-file-upload"
204
+ variant="flat"
205
+ :color="toolbarColor"
206
+ :stringFields="props.stringFields"
207
+ @import="importItems"
208
+ />
209
+ <ExportCSV
210
+ v-if="props.exportable && items.length"
211
+ icon="mdi mdi-file-download"
212
+ variant="flat"
213
+ :file-name="title"
214
+ :model-value="items"
215
+ :color="toolbarColor"
216
+ :stringFields="props.stringFields"
217
+ />
218
+ <VBtn
219
+ v-if="canCreate && props.insertable"
220
+ :color="toolbarColor"
221
+ prepend-icon="mdi mdi-plus"
222
+ variant="flat"
223
+ @click="openDialog()"
224
+ >
225
+ add
226
+ </VBtn>
227
+ </VToolbarItems>
228
+ </VToolbar>
229
+ </slot>
230
+ <v-data-table-server
231
+ v-if="canServerPageable"
232
+ v-bind="plainAttrs"
233
+ color="primary"
234
+ :items="items"
235
+ :items-length="itemsLength"
236
+ :item-value="props.modelKey"
237
+ :search="search"
238
+ :loading="isLoading"
239
+ @update:options="loadItems"
240
+ >
241
+ <!-- @ts-ignore -->
242
+ <template
243
+ v-for="(_, name, index) in ($slots as {})"
244
+ :key="index"
245
+ #[name]="slotData"
246
+ >
247
+ <slot
248
+ :name="name"
249
+ v-bind="((slotData || {}) as object)"
250
+ :operation="operation"
251
+ />
252
+ </template>
253
+ <template
254
+ v-if="!$slots['item.action']"
255
+ #item.action="{ item }"
256
+ >
257
+ <v-btn
258
+ v-if="!canUpdate || !canEditRow(item)"
259
+ variant="flat"
260
+ density="compact"
261
+ icon="mdi mdi-note-search"
262
+ @click="openDialogReadonly(item)"
263
+ />
264
+ <v-btn
265
+ v-if="canUpdate && canEditRow(item)"
266
+ variant="flat"
267
+ density="compact"
268
+ icon="mdi mdi-note-edit"
269
+ @click="openDialog(item)"
270
+ />
271
+ <v-btn
272
+ v-if="canDelete && canEditRow(item)"
273
+ variant="flat"
274
+ density="compact"
275
+ icon="mdi mdi-delete"
276
+ @click="confirmDeleteItem(item)"
277
+ />
278
+ </template>
279
+ </v-data-table-server>
280
+ <v-data-table
281
+ v-else
282
+ v-bind="plainAttrs"
283
+ color="primary"
284
+ :items="items"
285
+ :item-value="props.modelKey"
286
+ :search="search"
287
+ :loading="isLoading"
288
+ >
289
+ <!-- @ts-ignore -->
290
+ <template
291
+ v-for="(_, name, index) in ($slots as {})"
292
+ :key="index"
293
+ #[name]="slotData"
294
+ >
295
+ <slot
296
+ :name="name"
297
+ v-bind="((slotData || {}) as object)"
298
+ :operation="operation"
299
+ />
300
+ </template>
301
+ <template
302
+ v-if="!$slots['item.action']"
303
+ #item.action="{ item }"
304
+ >
305
+ <v-btn
306
+ v-if="!canUpdate || !canEditRow(item)"
307
+ variant="flat"
308
+ density="compact"
309
+ icon="mdi mdi-note-search"
310
+ @click="openDialogReadonly(item)"
311
+ />
312
+ <v-btn
313
+ v-if="canUpdate && canEditRow(item)"
314
+ variant="flat"
315
+ density="compact"
316
+ icon="mdi mdi-note-edit"
317
+ @click="openDialog(item)"
318
+ />
319
+ <v-btn
320
+ v-if="canDelete && canEditRow(item)"
321
+ variant="flat"
322
+ density="compact"
323
+ icon="mdi mdi-delete"
324
+ @click="confirmDeleteItem(item)"
325
+ />
326
+ </template>
327
+ </v-data-table>
328
+ <FormDialog
329
+ v-model="isDialogOpen"
330
+ :title="title"
331
+ :fullscreen="dialogFullscreen"
332
+ :initial-data="computedInitialData"
333
+ :form-data="currentItem"
334
+ @create="onCreateItem"
335
+ @update="onUpdateItem"
336
+ @afterLeave="emit('close:dialog')"
337
+ :saveAndStay="saveAndStay"
338
+ :readonly="isDialogReadonly"
339
+ :width="dialogWidth"
340
+ :height="dialogHeight"
341
+ :max-width="dialogMaxWidth"
342
+ :max-height="dialogMaxHeight"
343
+ >
344
+ <template #default="slotData">
345
+ <slot
346
+ name="form"
347
+ :tableItems="items"
348
+ :tableOperation="operation"
349
+ v-bind="slotData"
350
+ />
351
+ </template>
352
+ <template #title="slotData">
353
+ <slot
354
+ name="formTitle"
355
+ :tableItems="items"
356
+ :tableOperation="operation"
357
+ v-bind="slotData"
358
+ />
359
+ </template>
360
+ <template #action="slotData">
361
+ <slot
362
+ name="formAction"
363
+ :tableItems="items"
364
+ :tableOperation="operation"
365
+ v-bind="slotData"
366
+ />
367
+ </template>
368
+ </FormDialog>
369
+ </v-card>
370
+ </template>