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