@ramathibodi/nuxt-commons 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +14 -9
  3. package/dist/runtime/components/ExportCSV.vue +4 -4
  4. package/dist/runtime/components/FileBtn.vue +10 -4
  5. package/dist/runtime/components/ImportCSV.vue +7 -4
  6. package/dist/runtime/components/document/TemplateBuilder.vue +217 -0
  7. package/dist/runtime/components/form/Dialog.vue +138 -0
  8. package/dist/runtime/components/form/Hidden.vue +32 -0
  9. package/dist/runtime/components/form/Pad.vue +18 -6
  10. package/dist/runtime/components/form/Table.vue +264 -0
  11. package/dist/runtime/components/master/Autocomplete.vue +159 -0
  12. package/dist/runtime/composables/api.d.ts +9 -0
  13. package/dist/runtime/composables/api.mjs +64 -0
  14. package/dist/runtime/composables/document/template.d.ts +2 -0
  15. package/dist/runtime/composables/document/template.mjs +104 -0
  16. package/dist/runtime/composables/graphql.d.ts +17 -0
  17. package/dist/runtime/composables/graphql.mjs +61 -0
  18. package/dist/runtime/composables/menu.d.ts +19 -0
  19. package/dist/runtime/composables/menu.mjs +60 -0
  20. package/dist/runtime/composables/utils/fuzzy.d.ts +2 -0
  21. package/dist/runtime/composables/utils/fuzzy.mjs +19 -0
  22. package/dist/runtime/composables/utils/validation.d.ts +2 -2
  23. package/dist/runtime/composables/utils/validation.mjs +2 -2
  24. package/dist/runtime/labs/form/TextFieldMask.vue +5 -5
  25. package/dist/runtime/plugins/permission.d.ts +2 -0
  26. package/dist/runtime/plugins/permission.mjs +23 -0
  27. package/dist/runtime/types/menu.d.ts +15 -0
  28. package/package.json +6 -3
  29. /package/dist/runtime/components/{Pdf → pdf}/Print.vue +0 -0
  30. /package/dist/runtime/components/{Pdf → pdf}/View.vue +0 -0
@@ -0,0 +1,264 @@
1
+ <script lang="ts" setup>
2
+ import { VDataTable } from 'vuetify/components/VDataTable'
3
+ import { ref, watch, nextTick, defineOptions } from 'vue'
4
+ import type { FormDialogCallback } from './Dialog.vue'
5
+
6
+ defineOptions({
7
+ inheritAttrs: false,
8
+ })
9
+
10
+ interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
11
+ title: string
12
+ noDataText?: string
13
+ modelValue?: Record<string, any>[]
14
+ modelKey?: string
15
+ dialogFullscreen?: boolean
16
+ initialData?: Record<string, any>
17
+ toolbarColor?: string
18
+ importable?: boolean
19
+ exportable?: boolean
20
+ }
21
+
22
+ const props = withDefaults(defineProps<Props>(), {
23
+ noDataText: 'ไม่พบข้อมูล',
24
+ dialogFullscreen: false,
25
+ modelKey: 'id',
26
+ toolbarColor: 'primary',
27
+ importable: true,
28
+ exportable: true,
29
+ })
30
+
31
+ const emit = defineEmits(['update:modelValue'])
32
+
33
+ const attrs = useAttrs()
34
+ const plainAttrs = computed(() => {
35
+ return useOmit(attrs, ['modelValue', 'onUpdate:modelValue'])
36
+ })
37
+
38
+ const items = ref<Record<string, any>[]>([])
39
+ const search = ref<string>()
40
+ const currentItem = ref<Record<string, any> | undefined>(undefined)
41
+
42
+ const isDialogOpen = ref<boolean>(false)
43
+
44
+ watch(() => props.modelValue, (newValue) => {
45
+ if (!Array.isArray(newValue) || !newValue.every(item => typeof item === 'object')) {
46
+ items.value = []
47
+ }
48
+ else {
49
+ let maxKey = 0
50
+
51
+ newValue.forEach((item) => {
52
+ if (!item.hasOwnProperty(props.modelKey)) {
53
+ maxKey = Math.max(maxKey, ...newValue.map(i => i[props.modelKey] || 0))
54
+ item[props.modelKey] = maxKey + 1
55
+ }
56
+ })
57
+
58
+ items.value = newValue
59
+ }
60
+ }, { immediate: true })
61
+
62
+ watch(items, (newValue) => {
63
+ emit('update:modelValue', newValue)
64
+ }, { deep: true })
65
+
66
+ function createItem(item: Record<string, any>, callback?: FormDialogCallback) {
67
+ item[props.modelKey] = Math.max(...items.value.map(i => i[props.modelKey] || 0)) + 1
68
+
69
+ items.value.push(item)
70
+
71
+ if (callback) callback.done()
72
+ }
73
+
74
+ function importItems(importItems: Record<string, any>[], callback?: FormDialogCallback) {
75
+ importItems.forEach((item) => {
76
+ createItem(item)
77
+ })
78
+ if (callback) callback.done()
79
+ }
80
+
81
+ function updateItem(newItem: Record<string, any>, callback?: FormDialogCallback) {
82
+ const index = items.value.findIndex(item => item[props.modelKey] === newItem[props.modelKey])
83
+
84
+ if (index !== -1) {
85
+ items.value[index] = newItem
86
+ }
87
+
88
+ if (callback) callback.done()
89
+ }
90
+
91
+ function moveUpItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
92
+ const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
93
+
94
+ if (index > 0) {
95
+ const temp = items.value[index - 1]
96
+ items.value[index - 1] = items.value[index]
97
+ items.value[index] = temp
98
+ }
99
+
100
+ if (callback) callback.done()
101
+ }
102
+
103
+ function moveDownItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
104
+ const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
105
+
106
+ if (index < items.value.length - 1) {
107
+ const temp = items.value[index + 1]
108
+ items.value[index + 1] = items.value[index]
109
+ items.value[index] = temp
110
+ }
111
+
112
+ if (callback) callback.done()
113
+ }
114
+
115
+ function deleteItem(deleteItem: Record<string, any>, callback?: FormDialogCallback) {
116
+ const index = items.value.findIndex(item => item[props.modelKey] === deleteItem[props.modelKey])
117
+
118
+ if (index !== -1) {
119
+ items.value.splice(index, 1)
120
+ }
121
+
122
+ if (callback) callback.done()
123
+ }
124
+
125
+ const operation = ref({ createItem, updateItem, deleteItem, moveUpItem, moveDownItem })
126
+
127
+ function openDialog(item?: object) {
128
+ currentItem.value = item
129
+ nextTick(() => {
130
+ isDialogOpen.value = true
131
+ })
132
+ }
133
+ </script>
134
+
135
+ <template>
136
+ <v-card>
137
+ <VToolbar :color="toolbarColor">
138
+ <v-row
139
+ justify="end"
140
+ class="ma-1"
141
+ dense
142
+ no-gutters
143
+ align="center"
144
+ >
145
+ <v-col cols="7">
146
+ <VToolbarTitle class="pl-3">
147
+ <slot name="title">
148
+ {{ title }}
149
+ </slot>
150
+ </VToolbarTitle>
151
+ </v-col>
152
+ <v-col cols="5">
153
+ <slot name="search">
154
+ <VTextField
155
+ v-model="search"
156
+ class="justify-end w-100"
157
+ density="compact"
158
+ hide-details
159
+ placeholder="ค้นหา"
160
+ clearable
161
+ variant="solo"
162
+ />
163
+ </slot>
164
+ </v-col>
165
+ </v-row>
166
+
167
+ <VToolbarItems>
168
+ <slot name="toolbarItems" />
169
+ <ImportCSV
170
+ v-if="props.importable"
171
+ icon="mdi mdi-file-upload"
172
+ variant="flat"
173
+ @import="importItems"
174
+ />
175
+ <ExportCSV
176
+ v-if="props.exportable && items.length"
177
+ icon="mdi mdi-file-download"
178
+ variant="flat"
179
+ :file-name="title"
180
+ :model-value="items"
181
+ />
182
+ <VBtn
183
+ :color="toolbarColor"
184
+ prepend-icon="mdi mdi-plus"
185
+ variant="flat"
186
+ @click="openDialog()"
187
+ >
188
+ add
189
+ </VBtn>
190
+ </VToolbarItems>
191
+ </VToolbar>
192
+ <v-data-table
193
+ v-bind="plainAttrs"
194
+ color="primary"
195
+ :items="items"
196
+ :search="search"
197
+ >
198
+ <!-- @ts-ignore -->
199
+ <template
200
+ v-for="(_, name, index) in ($slots as {})"
201
+ :key="index"
202
+ #[name]="slotData"
203
+ >
204
+ <slot
205
+ :name="name"
206
+ v-bind="(slotData as object)"
207
+ :operation="operation"
208
+ />
209
+ </template>
210
+ <template
211
+ v-if="!$slots['item.operation']"
212
+ #item.operation="props"
213
+ >
214
+ <v-btn
215
+ :disabled="props.index==0"
216
+ variant="flat"
217
+ density="compact"
218
+ icon="mdi mdi-arrow-up-thick"
219
+ @click="moveUpItem(props.item)"
220
+ />
221
+ <v-btn
222
+ :disabled="props.index==items.length-1"
223
+ variant="flat"
224
+ density="compact"
225
+ icon="mdi mdi-arrow-down-thick"
226
+ @click="moveDownItem(props.item)"
227
+ />
228
+ </template>
229
+ <template
230
+ v-if="!$slots['item.action']"
231
+ #item.action="{ item }"
232
+ >
233
+ <v-btn
234
+ variant="flat"
235
+ density="compact"
236
+ icon="mdi mdi-note-edit"
237
+ @click="openDialog(item)"
238
+ />
239
+ <v-btn
240
+ variant="flat"
241
+ density="compact"
242
+ icon="mdi mdi-delete"
243
+ @click="deleteItem(item)"
244
+ />
245
+ </template>
246
+ </v-data-table>
247
+ <FormDialog
248
+ v-model="isDialogOpen"
249
+ :title="title"
250
+ :fullscreen="dialogFullscreen"
251
+ :initial-data="initialData"
252
+ :form-data="currentItem"
253
+ @create="createItem"
254
+ @update="updateItem"
255
+ >
256
+ <template #default="slotData">
257
+ <slot
258
+ name="form"
259
+ v-bind="slotData"
260
+ />
261
+ </template>
262
+ </FormDialog>
263
+ </v-card>
264
+ </template>
@@ -0,0 +1,159 @@
1
+ <script lang="ts" setup>
2
+ import {VAutocomplete} from 'vuetify/components/VAutocomplete'
3
+ import {concat, isEmpty} from 'lodash-es'
4
+ import {computed, ref, watch} from 'vue'
5
+ import {watchDebounced} from '@vueuse/core'
6
+ import {useFuzzy} from '../../composables/utils/fuzzy'
7
+ import {useGraphQl} from '../../composables/graphql'
8
+
9
+ interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
10
+ fuzzy?: boolean
11
+ groupKey: string
12
+ lang?: 'TH' | 'EN'
13
+ fields?: string[]
14
+ noDataText?: string
15
+ filterText?: string
16
+ waitForFilter?: boolean
17
+ waitForFilterText?: string
18
+ modelValue?: string
19
+ }
20
+
21
+ const props = withDefaults(defineProps<Props>(), {
22
+ fuzzy: true,
23
+ noDataText: 'ไม่พบข้อมูล',
24
+ lang: 'TH',
25
+ waitForFilter: false,
26
+ })
27
+
28
+ const emit = defineEmits(['update:modelValue'])
29
+
30
+ const masterItems = ref<Array<any>>([])
31
+ const items = ref<Array<any>>([])
32
+ const selectedItem = ref<any>()
33
+
34
+ const searchData = ref<string>('')
35
+
36
+ const isLoading = ref(false)
37
+ const isErrorLoading = ref(false)
38
+
39
+ function query(groupKey: string, filterText: string | undefined) {
40
+ let operation = 'masterItemByGroupKey'
41
+
42
+ const variables: Record<string, any> = { groupKey: { value: groupKey, required: true } }
43
+ if (filterText) {
44
+ variables['filterText'] = { value: filterText }
45
+ operation = 'masterItemByGroupKeyAndFilterText'
46
+ }
47
+
48
+ let fields: any[] = ['itemCode', 'itemValue', 'itemValueAlternative']
49
+ if (props.fields) fields = concat(fields, props.fields)
50
+
51
+ isLoading.value = true
52
+
53
+ useGraphQl().queryPromise(operation, fields, variables).then((result: any) => {
54
+ masterItems.value = result
55
+ items.value = result
56
+ isErrorLoading.value = false
57
+ }).catch((error) => {
58
+ console.log(error)
59
+ isErrorLoading.value = true
60
+ }).finally(() => {
61
+ isLoading.value = false
62
+ })
63
+ }
64
+
65
+ watch(() => props.waitForFilter && !props.filterText, (newValue) => {
66
+ if (newValue) {
67
+ masterItems.value = []
68
+ items.value = []
69
+ selectedItem.value = undefined
70
+ }
71
+ else {
72
+ query(props.groupKey, props.filterText)
73
+ }
74
+ }, { immediate: true })
75
+
76
+ watch(() => props.modelValue, (newValue) => {
77
+ selectedItem.value = newValue
78
+ }, { immediate: true })
79
+
80
+ watch(selectedItem, (newValue) => {
81
+ emit('update:modelValue', newValue)
82
+ })
83
+
84
+ async function fuzzySearch() {
85
+ if (props.fuzzy) {
86
+ if (isEmpty(searchData.value)) {
87
+ items.value = masterItems.value
88
+ }
89
+ else {
90
+ let fields: any[] = ['itemCode', 'itemValue', 'itemValueAlternative']
91
+ if (props.fields) fields = concat(fields, props.fields)
92
+
93
+ const results: any = useFuzzy(searchData.value, masterItems.value, fields)
94
+ items.value = []
95
+ if (results.value.length) {
96
+ for (let i = 0; results.value.length > i; i++) {
97
+ if (results.value[i].item) items.value.push(results.value[i].item)
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ watchDebounced(searchData, fuzzySearch, { debounce: 1000, maxWait: 5000 })
105
+
106
+ const itemTitleField = computed(() => {
107
+ if (props.lang == 'TH') return 'itemValue'
108
+ else return 'itemValueAlternative'
109
+ })
110
+
111
+ const computedNoDataText = computed(() => {
112
+ if (props.waitForFilter && !props.filterText) return props.waitForFilterText
113
+ return props.noDataText
114
+ })
115
+ </script>
116
+
117
+ <template>
118
+ <v-autocomplete
119
+ v-model="selectedItem"
120
+ v-model:search="searchData"
121
+ v-bind="$attrs"
122
+ :items="items"
123
+ :no-filter="props.fuzzy"
124
+ :item-title="itemTitleField"
125
+ item-value="itemCode"
126
+ :no-data-text="computedNoDataText"
127
+ :loading="isLoading"
128
+ >
129
+ <!-- @ts-ignore -->
130
+ <template
131
+ v-for="(_, name, index) in ($slots as {})"
132
+ :key="index"
133
+ #[name]="slotData"
134
+ >
135
+ <slot
136
+ :name="name"
137
+ v-bind="(slotData as object)"
138
+ :operation="operation"
139
+ />
140
+ </template>
141
+ <template
142
+ v-if="!$slots.item"
143
+ #item="{ props, item }"
144
+ >
145
+ <v-list-item
146
+ v-bind="props"
147
+ :title="item.title || item.raw.itemValue || item.raw.itemCode"
148
+ />
149
+ </template>
150
+ <template
151
+ v-if="isErrorLoading"
152
+ #append
153
+ >
154
+ <v-icon color="error">
155
+ mdi mdi-alert
156
+ </v-icon>
157
+ </template>
158
+ </v-autocomplete>
159
+ </template>
@@ -0,0 +1,9 @@
1
+ import type { UseFetchOptions } from 'nuxt/app';
2
+ import type { SearchParameters } from 'ofetch';
3
+ export declare function useApi(): {
4
+ urlBuilder: (url: string | string[]) => string;
5
+ get: (url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>) => any;
6
+ getPromise: (url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>) => Promise<unknown>;
7
+ post: (url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>) => any;
8
+ postPromise: (url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>) => Promise<unknown>;
9
+ };
@@ -0,0 +1,64 @@
1
+ import { trimEnd, trimStart } from "lodash-es";
2
+ import { useRuntimeConfig, useFetch } from "#imports";
3
+ export function useApi() {
4
+ const config = useRuntimeConfig();
5
+ function urlBuilder(url) {
6
+ let returnUrl = "";
7
+ if (Array.isArray(url)) {
8
+ if (url[0].toLowerCase() == "agent")
9
+ url[0] = config?.public.WS_AGENT;
10
+ returnUrl = url.join("/");
11
+ } else {
12
+ returnUrl = url;
13
+ }
14
+ if (returnUrl.startsWith("http://") || returnUrl.startsWith("https://"))
15
+ return returnUrl;
16
+ else
17
+ return trimEnd(config?.public.WS_API, "/") + "/" + trimStart(returnUrl, "/");
18
+ }
19
+ function optionBuilder(method, body, params, options) {
20
+ let returnOption = {
21
+ method,
22
+ body,
23
+ query: params
24
+ };
25
+ const headers = {
26
+ "Content-Type": "application/json",
27
+ "Accept": "application/json"
28
+ };
29
+ if (options) {
30
+ options = Object.assign(options, returnOption);
31
+ if (options.headers)
32
+ options.headers = Object.assign(options.headers, headers);
33
+ else
34
+ options.headers = headers;
35
+ }
36
+ returnOption = Object.assign(returnOption, options);
37
+ return returnOption;
38
+ }
39
+ function get(url, body, params, options) {
40
+ return useFetch(urlBuilder(url), optionBuilder("GET", body, params, options));
41
+ }
42
+ function getPromise(url, body, params, options) {
43
+ return new Promise(async (resolve, reject) => {
44
+ const { data, error } = await get(url, body, params, options);
45
+ if (!error.value)
46
+ resolve(data.value);
47
+ else
48
+ reject(error.value);
49
+ });
50
+ }
51
+ function post(url, body, params, options) {
52
+ return useFetch(urlBuilder(url), optionBuilder("POST", body, params, options));
53
+ }
54
+ function postPromise(url, body, params, options) {
55
+ return new Promise(async (resolve, reject) => {
56
+ const { data, error } = await post(url, body, params, options);
57
+ if (!error.value)
58
+ resolve(data.value);
59
+ else
60
+ reject(error.value);
61
+ });
62
+ }
63
+ return { urlBuilder, get, getPromise, post, postPromise };
64
+ }
@@ -0,0 +1,2 @@
1
+ export declare const validationRulesRegex: RegExp;
2
+ export declare function useDocumentTemplate(items: string | object): string;
@@ -0,0 +1,104 @@
1
+ export const validationRulesRegex = /^(require(?:\([^)]*\))?|requireIf(?:\([^)]*\))?|requireTrue(?:\([^)]*\))?|requireTrueIf(?:\([^)]*\))?|numeric(?:\([^)]*\))?|range(?:\([^)]*\))?|integer(?:\([^)]*\))?|unique(?:\([^)]*\))?|length(?:\([^)]*\))?|lengthGreater(?:\([^)]*\))?|lengthLess(?:\([^)]*\))?|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex(?:\([^)]*\))?)(,(require(?:\([^)]*\))?|requireIf(?:\([^)]*\))?|requireTrue(?:\([^)]*\))?|requireTrueIf(?:\([^)]*\))?|numeric(?:\([^)]*\))?|range(?:\([^)]*\))?|integer(?:\([^)]*\))?|unique(?:\([^)]*\))?|length(?:\([^)]*\))?|lengthGreater(?:\([^)]*\))?|lengthLess(?:\([^)]*\))?|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex(?:\([^)]*\))?))*$/;
2
+ export function useDocumentTemplate(items) {
3
+ if (!items)
4
+ return "";
5
+ if (typeof items === "string") {
6
+ try {
7
+ items = JSON.parse(items);
8
+ } catch (_) {
9
+ return items;
10
+ }
11
+ }
12
+ if (!Array.isArray(items) || !items.every((item) => typeof item === "object" && item !== null)) {
13
+ return "";
14
+ }
15
+ let templateString = items.map((item) => templateItemToString(item)).join("");
16
+ templateString = `<v-container fluid><v-row dense>${templateString}</v-row></v-container>`;
17
+ return templateString;
18
+ }
19
+ function templateItemToString(item) {
20
+ let optionString = "";
21
+ let validationRules = "";
22
+ if (item.inputOptions) {
23
+ if (item.inputType == "MasterAutocomplete") {
24
+ optionString += 'groupKey="' + item.inputOptions + '" ';
25
+ }
26
+ if (item.inputType == "VSelect") {
27
+ const choice = optionStringToChoiceObject(item.inputOptions);
28
+ console.log(choice);
29
+ optionString = `item-value="value" item-title="label" :items='` + JSON.stringify(choice).replaceAll("'", "&#39;") + "' ";
30
+ }
31
+ if (item.inputType == "VRadio" || item.inputType == "VRadioInline") {
32
+ const choice = optionStringToChoiceObject(item.inputOptions);
33
+ optionString = choice.map((choiceItem) => `<v-radio label="${choiceItem.label}" value="${choiceItem.value}"></v-radio>`).join("");
34
+ }
35
+ if (item.inputType == "Header") {
36
+ optionString = item.inputOptions.split(",").join(" ");
37
+ }
38
+ }
39
+ if (item.inputType == "FormDateTime" && !item.inputAttributes?.includes("dense"))
40
+ item.inputAttributes = (item.inputAttributes?.trim() + " dense").trim();
41
+ if (item.validationRules)
42
+ validationRules = buildValidationRules(item.validationRules);
43
+ let templateString = "";
44
+ switch (item.inputType) {
45
+ case "CustomCode":
46
+ templateString = `${item.inputCustomCode}`;
47
+ break;
48
+ case "VRadio":
49
+ templateString = `<v-radio-group v-model="data.${item.variableName}" label="${item.inputLabel || item.variableName}"${validationRules ? " " + validationRules.trim() : ""}>${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
50
+ break;
51
+ case "VRadioInline":
52
+ templateString = `<v-radio-group v-model="data.${item.variableName}" inline${validationRules ? " " + validationRules.trim() : ""}><template #prepend>${item.inputLabel || item.variableName}</template>${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
53
+ break;
54
+ case "Separator":
55
+ templateString = `</v-row><v-row dense>`;
56
+ break;
57
+ case "Header":
58
+ templateString = `<div class="${optionString ? optionString.trim() : "text-h4 font-weight-medium"}">${item.inputLabel || item.variableName}</div>`;
59
+ break;
60
+ default:
61
+ templateString = `<${item.inputType} v-model="data.${item.variableName}" label="${item.inputLabel || item.variableName}"${item.inputAttributes ? " " + item.inputAttributes : ""}${optionString ? " " + optionString.trim() : ""}${validationRules ? " " + validationRules.trim() : ""}></${item.inputType}>`;
62
+ }
63
+ if (!["Separator", "Header"].includes(item.inputType))
64
+ templateString = `<v-col${item.width ? ' cols="' + item.width + '"' : ""}${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col>`;
65
+ if (["Header"].includes(item.inputType))
66
+ templateString = `</v-row><v-row dense><v-col${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col></v-row><v-row dense>`;
67
+ return templateString;
68
+ }
69
+ function optionStringToChoiceObject(option) {
70
+ let returnObject = [];
71
+ if (/^[^'",]+(,[^'",]+)*$/.test(option.trim())) {
72
+ returnObject = option.split(",").map((item) => {
73
+ item = item.trim();
74
+ if (item.includes("|")) {
75
+ const [label, value] = item.split("|").map((str) => str.trim().replace(/^['"]|['"]$/g, ""));
76
+ return { label, value };
77
+ } else {
78
+ item = item.replace(/^['"]|['"]$/g, "");
79
+ return { label: item, value: item };
80
+ }
81
+ });
82
+ }
83
+ return returnObject;
84
+ }
85
+ function buildValidationRules(validationString) {
86
+ validationString = validationString.trim();
87
+ if (validationString.startsWith("["))
88
+ validationString = validationString.substring(1);
89
+ if (validationString.endsWith("]"))
90
+ validationString = validationString.substring(0, validationString.length - 1);
91
+ if (!validationRulesRegex.test(validationString))
92
+ return "";
93
+ validationString = validationString.split(",").map((rule) => {
94
+ rule = rule.trim();
95
+ if (!rule.startsWith("rules.")) {
96
+ rule = "rules." + rule;
97
+ }
98
+ if (!/\(.+\)$/.test(rule)) {
99
+ rule = rule + "()";
100
+ }
101
+ return rule.replaceAll('"', "'");
102
+ }).join(",");
103
+ return `:rules="[${validationString}]"`;
104
+ }
@@ -0,0 +1,17 @@
1
+ import { type ClassConstructor } from '../utils/object';
2
+ declare type VariableOptions = {
3
+ type?: string;
4
+ name?: string;
5
+ value: any;
6
+ list?: boolean | [boolean];
7
+ required?: boolean;
8
+ } | {
9
+ [k: string]: any;
10
+ };
11
+ export declare function useGraphQl(): {
12
+ query: <T>(operation: string, fields: Array<string | Object> | ClassConstructor<any>, variables?: VariableOptions, cache?: boolean) => any;
13
+ queryPromise: <T_1>(operation: string, fields: Array<string | Object> | ClassConstructor<any>, variables?: VariableOptions, cache?: boolean) => Promise<T_1>;
14
+ mutation: <T_2>(operation: string, fields: Array<string | Object> | ClassConstructor<any>, variables?: VariableOptions) => any;
15
+ mutationPromise: <T_3>(operation: string, fields: Array<string | Object> | ClassConstructor<any>, variables?: VariableOptions) => Promise<T_3>;
16
+ };
17
+ export {};
@@ -0,0 +1,61 @@
1
+ import { query as gqlQuery, mutation as gqlMutation } from "gql-query-builder";
2
+ import { useQuery } from "@vue/apollo-composable";
3
+ import { classAttributes, isClassConstructor } from "../utils/object.mjs";
4
+ import { gql, useAsyncQuery, useMutation } from "#imports";
5
+ export function useGraphQl() {
6
+ function query(operation, fields, variables, cache = false) {
7
+ if (isClassConstructor(fields))
8
+ fields = classAttributes(fields);
9
+ const builder = gqlQuery({ operation, fields, variables });
10
+ const options = {
11
+ query: gql(builder.query),
12
+ variables: builder.variables,
13
+ cache
14
+ };
15
+ return useAsyncQuery(options);
16
+ }
17
+ function queryPromise(operation, fields, variables, cache = false) {
18
+ if (isClassConstructor(fields))
19
+ fields = classAttributes(fields);
20
+ const builder = gqlQuery({ operation, fields, variables });
21
+ return new Promise((resolve, reject) => {
22
+ const { onResult, onError } = useQuery(gql(builder.query), builder.variables, { fetchPolicy: cache ? "cache-first" : "no-cache" });
23
+ onResult((queryResult) => {
24
+ const dataObject = queryResult.data;
25
+ try {
26
+ resolve(dataObject[operation]);
27
+ } catch (error) {
28
+ reject(error);
29
+ }
30
+ });
31
+ onError((error) => {
32
+ reject(error);
33
+ });
34
+ });
35
+ }
36
+ function mutation(operation, fields, variables) {
37
+ if (isClassConstructor(fields))
38
+ fields = classAttributes(fields);
39
+ const builder = gqlMutation({ operation, fields, variables });
40
+ return useMutation(gql(builder.query), { variables: builder.variables });
41
+ }
42
+ function mutationPromise(operation, fields, variables) {
43
+ if (isClassConstructor(fields))
44
+ fields = classAttributes(fields);
45
+ const builder = gqlMutation({ operation, fields, variables });
46
+ return new Promise(async (resolve, reject) => {
47
+ const { mutate, error } = useMutation(gql(builder.query), { variables: builder.variables });
48
+ if (!error.value) {
49
+ try {
50
+ const results = await mutate();
51
+ resolve(results.data[operation]);
52
+ } catch (e) {
53
+ reject(e);
54
+ }
55
+ } else {
56
+ reject(error.value);
57
+ }
58
+ });
59
+ }
60
+ return { query, queryPromise, mutation, mutationPromise };
61
+ }