@ramathibodi/nuxt-commons 0.1.9 → 0.1.10
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 +1 -1
- package/dist/module.mjs +8 -0
- package/dist/runtime/components/ImportCSV.vue +3 -6
- package/dist/runtime/components/form/CodeEditor.vue +6 -6
- package/dist/runtime/components/form/Dialog.vue +3 -7
- package/dist/runtime/components/form/Table.vue +5 -4
- package/dist/runtime/components/model/Table.vue +217 -0
- package/dist/runtime/composables/api.mjs +2 -2
- package/dist/runtime/composables/graphqlModel.d.ts +44 -0
- package/dist/runtime/composables/graphqlModel.mjs +261 -0
- package/dist/runtime/composables/graphqlOperation.d.ts +10 -0
- package/dist/runtime/composables/graphqlOperation.mjs +123 -0
- package/dist/runtime/types/formDialog.d.ts +4 -0
- package/dist/runtime/types/graphqlOperation.d.ts +23 -0
- package/package.json +24 -4
- package/scripts/postInstall.cjs +76 -0
- package/templates/.codegen/codegen.ts +32 -0
- package/templates/.codegen/plugin-schema-object.js +148 -0
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -33,6 +33,14 @@ const module = defineNuxtModule({
|
|
|
33
33
|
src: resolver.resolve("runtime/types/menu.d.ts"),
|
|
34
34
|
filename: "types/menu.d.ts"
|
|
35
35
|
});
|
|
36
|
+
addTypeTemplate({
|
|
37
|
+
src: resolver.resolve("runtime/types/graphqlOperation.d.ts"),
|
|
38
|
+
filename: "types/graphqlOperation.d.ts"
|
|
39
|
+
});
|
|
40
|
+
addTypeTemplate({
|
|
41
|
+
src: resolver.resolve("runtime/types/formDialog.d.ts"),
|
|
42
|
+
filename: "types/formDialog.d.ts"
|
|
43
|
+
});
|
|
36
44
|
}
|
|
37
45
|
});
|
|
38
46
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import * as XLSX from 'xlsx'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {ref} from 'vue'
|
|
4
|
+
import {useAlert} from '../composables/alert'
|
|
5
5
|
|
|
6
6
|
const alert = useAlert()
|
|
7
7
|
const emit = defineEmits<{
|
|
@@ -12,10 +12,7 @@ const loading = ref(false)
|
|
|
12
12
|
const fileBtnRef = ref()
|
|
13
13
|
|
|
14
14
|
function uploadedFile(files: File[] | File | undefined) {
|
|
15
|
-
if (!files)
|
|
16
|
-
alert?.addAlert({ message: 'Please upload a file again', alertType: 'error' })
|
|
17
|
-
return
|
|
18
|
-
}
|
|
15
|
+
if (!files) return
|
|
19
16
|
|
|
20
17
|
if (Array.isArray(files) && files.length != 1) {
|
|
21
18
|
alert?.addAlert({ message: 'Please select a single file for import', alertType: 'error' })
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import {Codemirror} from 'vue-codemirror'
|
|
3
|
+
import {oneDark} from '@codemirror/theme-one-dark'
|
|
4
|
+
import {javascript} from '@codemirror/lang-javascript'
|
|
5
|
+
import {html} from '@codemirror/lang-html'
|
|
6
|
+
import {vue as vueLang} from '@codemirror/lang-vue'
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
height?: string | number
|
|
@@ -19,7 +19,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
19
19
|
|
|
20
20
|
const extensions = [oneDark]
|
|
21
21
|
if (props.lang == 'vue') {
|
|
22
|
-
extensions.push(
|
|
22
|
+
extensions.push(vueLang({ base: html({ autoCloseTags: true, matchClosingTags: true, selfClosingTags: true }) }))
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
25
|
extensions.push(javascript({ typescript: true }))
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export interface FormDialogCallback {
|
|
6
|
-
done: Function
|
|
7
|
-
error: Function
|
|
8
|
-
}
|
|
2
|
+
import {computed, defineModel, ref, watch, watchEffect} from 'vue'
|
|
3
|
+
import {cloneDeep, isEqual} from 'lodash-es'
|
|
4
|
+
import type {FormDialogCallback} from '../../types/formDialog'
|
|
9
5
|
|
|
10
6
|
interface Props {
|
|
11
7
|
maxWidth?: number | string
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
2
|
+
import {VDataTable} from 'vuetify/components/VDataTable'
|
|
3
|
+
import {computed, defineOptions, nextTick, ref, useAttrs, watch} from 'vue'
|
|
4
|
+
import type {FormDialogCallback} from '../../types/formDialog'
|
|
5
|
+
import {omit} from 'lodash-es'
|
|
6
|
+
|
|
6
7
|
defineOptions({
|
|
7
8
|
inheritAttrs: false,
|
|
8
9
|
})
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import {computed, nextTick, ref, useAttrs} from 'vue'
|
|
3
|
+
import type {GraphqlModelProps, HeaderProps} from '../../composables/graphqlModel'
|
|
4
|
+
import {useGraphqlModel} from '../../composables/graphqlModel'
|
|
5
|
+
import {VDataTable} from "vuetify/components/VDataTable";
|
|
6
|
+
import {omit} from "lodash-es";
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
inheritAttrs: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
|
|
13
|
+
title: string
|
|
14
|
+
noDataText?: 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 & GraphqlModelProps & HeaderProps>(), {
|
|
23
|
+
noDataText: 'ไม่พบข้อมูล',
|
|
24
|
+
dialogFullscreen: false,
|
|
25
|
+
toolbarColor: 'primary',
|
|
26
|
+
importable: true,
|
|
27
|
+
exportable: true,
|
|
28
|
+
modelKey: 'id',
|
|
29
|
+
modelBy: undefined,
|
|
30
|
+
fields: ()=>[]
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const attrs = useAttrs()
|
|
34
|
+
const plainAttrs = computed(() => {
|
|
35
|
+
let returnAttrs = omit(attrs, ['modelValue', 'onUpdate:modelValue'])
|
|
36
|
+
if (props.headers) returnAttrs['headers'] = props.headers
|
|
37
|
+
return returnAttrs
|
|
38
|
+
})
|
|
39
|
+
const currentItem = ref<Record<string, any> | undefined>(undefined)
|
|
40
|
+
const isDialogOpen = ref<boolean>(false)
|
|
41
|
+
|
|
42
|
+
const {items,itemsLength,
|
|
43
|
+
search,
|
|
44
|
+
canServerPageable,canServerSearch,canCreate,canUpdate,canDelete,
|
|
45
|
+
createItem,importItems,updateItem,deleteItem,
|
|
46
|
+
loadItems,reload,
|
|
47
|
+
isLoading} = useGraphqlModel(props)
|
|
48
|
+
|
|
49
|
+
function openDialog(item?: object) {
|
|
50
|
+
currentItem.value = item
|
|
51
|
+
nextTick(() => {
|
|
52
|
+
isDialogOpen.value = true
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
defineExpose({ reload })
|
|
57
|
+
</script>
|
|
58
|
+
<template>
|
|
59
|
+
<v-card>
|
|
60
|
+
<VToolbar :color="toolbarColor">
|
|
61
|
+
<v-row
|
|
62
|
+
justify="end"
|
|
63
|
+
class="ma-1"
|
|
64
|
+
dense
|
|
65
|
+
no-gutters
|
|
66
|
+
align="center"
|
|
67
|
+
>
|
|
68
|
+
<v-col cols="7">
|
|
69
|
+
<VToolbarTitle class="pl-3">
|
|
70
|
+
<slot name="title">
|
|
71
|
+
{{ title }}
|
|
72
|
+
</slot>
|
|
73
|
+
</VToolbarTitle>
|
|
74
|
+
</v-col>
|
|
75
|
+
<v-col cols="5">
|
|
76
|
+
<slot name="search">
|
|
77
|
+
<VTextField
|
|
78
|
+
v-model="search"
|
|
79
|
+
class="justify-end w-100"
|
|
80
|
+
density="compact"
|
|
81
|
+
hide-details
|
|
82
|
+
placeholder="ค้นหา"
|
|
83
|
+
clearable
|
|
84
|
+
variant="solo"
|
|
85
|
+
/>
|
|
86
|
+
</slot>
|
|
87
|
+
</v-col>
|
|
88
|
+
</v-row>
|
|
89
|
+
|
|
90
|
+
<VToolbarItems>
|
|
91
|
+
<slot name="toolbarItems" />
|
|
92
|
+
<ImportCSV
|
|
93
|
+
v-if="props.importable"
|
|
94
|
+
icon="mdi mdi-file-upload"
|
|
95
|
+
variant="flat"
|
|
96
|
+
@import="importItems"
|
|
97
|
+
/>
|
|
98
|
+
<ExportCSV
|
|
99
|
+
v-if="props.exportable && items.length"
|
|
100
|
+
icon="mdi mdi-file-download"
|
|
101
|
+
variant="flat"
|
|
102
|
+
:file-name="title"
|
|
103
|
+
:model-value="items"
|
|
104
|
+
/>
|
|
105
|
+
<VBtn
|
|
106
|
+
:color="toolbarColor"
|
|
107
|
+
prepend-icon="mdi mdi-plus"
|
|
108
|
+
variant="flat"
|
|
109
|
+
@click="openDialog()"
|
|
110
|
+
v-if="canCreate"
|
|
111
|
+
>
|
|
112
|
+
add
|
|
113
|
+
</VBtn>
|
|
114
|
+
</VToolbarItems>
|
|
115
|
+
</VToolbar>
|
|
116
|
+
<v-data-table-server
|
|
117
|
+
v-if="canServerPageable"
|
|
118
|
+
v-bind="plainAttrs"
|
|
119
|
+
color="primary"
|
|
120
|
+
:items="items"
|
|
121
|
+
:items-length="itemsLength"
|
|
122
|
+
:item-value="props.modelKey"
|
|
123
|
+
:search="search"
|
|
124
|
+
:loading="isLoading"
|
|
125
|
+
@update:options="loadItems"
|
|
126
|
+
>
|
|
127
|
+
<!-- @ts-ignore -->
|
|
128
|
+
<template
|
|
129
|
+
v-for="(_, name, index) in ($slots as {})"
|
|
130
|
+
:key="index"
|
|
131
|
+
#[name]="slotData"
|
|
132
|
+
>
|
|
133
|
+
<slot
|
|
134
|
+
:name="name"
|
|
135
|
+
v-bind="(slotData as object)"
|
|
136
|
+
:operation="operation"
|
|
137
|
+
/>
|
|
138
|
+
</template>
|
|
139
|
+
<template
|
|
140
|
+
v-if="!$slots['item.action']"
|
|
141
|
+
#item.action="{ item }"
|
|
142
|
+
>
|
|
143
|
+
<v-btn
|
|
144
|
+
variant="flat"
|
|
145
|
+
density="compact"
|
|
146
|
+
icon="mdi mdi-note-edit"
|
|
147
|
+
@click="openDialog(item)"
|
|
148
|
+
v-if="canUpdate"
|
|
149
|
+
/>
|
|
150
|
+
<v-btn
|
|
151
|
+
variant="flat"
|
|
152
|
+
density="compact"
|
|
153
|
+
icon="mdi mdi-delete"
|
|
154
|
+
@click="deleteItem(item)"
|
|
155
|
+
v-if="canDelete"
|
|
156
|
+
/>
|
|
157
|
+
</template>
|
|
158
|
+
</v-data-table-server>
|
|
159
|
+
<v-data-table
|
|
160
|
+
v-else
|
|
161
|
+
v-bind="plainAttrs"
|
|
162
|
+
color="primary"
|
|
163
|
+
:items="items"
|
|
164
|
+
:item-value="props.modelKey"
|
|
165
|
+
:search="search"
|
|
166
|
+
:loading="isLoading"
|
|
167
|
+
>
|
|
168
|
+
<!-- @ts-ignore -->
|
|
169
|
+
<template
|
|
170
|
+
v-for="(_, name, index) in ($slots as {})"
|
|
171
|
+
:key="index"
|
|
172
|
+
#[name]="slotData"
|
|
173
|
+
>
|
|
174
|
+
<slot
|
|
175
|
+
:name="name"
|
|
176
|
+
v-bind="(slotData as object)"
|
|
177
|
+
:operation="operation"
|
|
178
|
+
/>
|
|
179
|
+
</template>
|
|
180
|
+
<template
|
|
181
|
+
v-if="!$slots['item.action']"
|
|
182
|
+
#item.action="{ item }"
|
|
183
|
+
>
|
|
184
|
+
<v-btn
|
|
185
|
+
variant="flat"
|
|
186
|
+
density="compact"
|
|
187
|
+
icon="mdi mdi-note-edit"
|
|
188
|
+
@click="openDialog(item)"
|
|
189
|
+
v-if="canUpdate"
|
|
190
|
+
/>
|
|
191
|
+
<v-btn
|
|
192
|
+
variant="flat"
|
|
193
|
+
density="compact"
|
|
194
|
+
icon="mdi mdi-delete"
|
|
195
|
+
@click="deleteItem(item)"
|
|
196
|
+
v-if="canDelete"
|
|
197
|
+
/>
|
|
198
|
+
</template>
|
|
199
|
+
</v-data-table>
|
|
200
|
+
<FormDialog
|
|
201
|
+
v-model="isDialogOpen"
|
|
202
|
+
:title="title"
|
|
203
|
+
:fullscreen="dialogFullscreen"
|
|
204
|
+
:initial-data="initialData"
|
|
205
|
+
:form-data="currentItem"
|
|
206
|
+
@create="createItem"
|
|
207
|
+
@update="updateItem"
|
|
208
|
+
>
|
|
209
|
+
<template #default="slotData">
|
|
210
|
+
<slot
|
|
211
|
+
name="form"
|
|
212
|
+
v-bind="slotData"
|
|
213
|
+
/>
|
|
214
|
+
</template>
|
|
215
|
+
</FormDialog>
|
|
216
|
+
</v-card>
|
|
217
|
+
</template>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { trimEnd, trimStart } from "lodash-es";
|
|
2
|
-
import {
|
|
2
|
+
import { useFetch, useRuntimeConfig } from "#imports";
|
|
3
3
|
export function useApi() {
|
|
4
4
|
const config = useRuntimeConfig();
|
|
5
5
|
function urlBuilder(url) {
|
|
6
6
|
let returnUrl = "";
|
|
7
7
|
if (Array.isArray(url)) {
|
|
8
8
|
if (url[0].toLowerCase() == "agent")
|
|
9
|
-
url[0] = config?.public.WS_AGENT;
|
|
9
|
+
url[0] = config?.public.WS_AGENT || config?.public.WS_DEVICE;
|
|
10
10
|
returnUrl = url.join("/");
|
|
11
11
|
} else {
|
|
12
12
|
returnUrl = url;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { FormDialogCallback } from "../types/formDialog";
|
|
2
|
+
import type { graphqlOperationObject } from "../types/graphqlOperation";
|
|
3
|
+
export interface HeaderProps {
|
|
4
|
+
headers?: any[];
|
|
5
|
+
}
|
|
6
|
+
export interface GraphqlModelProps {
|
|
7
|
+
modelName: string;
|
|
8
|
+
modelKey?: string;
|
|
9
|
+
modelBy?: object;
|
|
10
|
+
operationCreate?: graphqlOperationObject<any, any> | string;
|
|
11
|
+
operationUpdate?: graphqlOperationObject<any, any> | string;
|
|
12
|
+
operationDelete?: graphqlOperationObject<any, any> | string;
|
|
13
|
+
operationRead?: graphqlOperationObject<any, any> | string;
|
|
14
|
+
operationReadPageable?: graphqlOperationObject<any, any> | string;
|
|
15
|
+
operationSearch?: graphqlOperationObject<any, any> | string;
|
|
16
|
+
fields?: Array<string | object>;
|
|
17
|
+
}
|
|
18
|
+
type GraphqlModelPropsWithOptionalHeaders = GraphqlModelProps & Partial<HeaderProps>;
|
|
19
|
+
export declare function useGraphqlModel<T extends GraphqlModelPropsWithOptionalHeaders>(props: T): {
|
|
20
|
+
items: import("vue").Ref<Record<string, any>[]>;
|
|
21
|
+
itemsLength: import("vue").Ref<number>;
|
|
22
|
+
search: import("vue").Ref<string | undefined>;
|
|
23
|
+
currentOptions: import("vue").Ref<any>;
|
|
24
|
+
operationCreate: import("vue").ComputedRef<any>;
|
|
25
|
+
operationUpdate: import("vue").ComputedRef<any>;
|
|
26
|
+
operationDelete: import("vue").ComputedRef<any>;
|
|
27
|
+
operationRead: import("vue").ComputedRef<any>;
|
|
28
|
+
operationReadPageable: import("vue").ComputedRef<any>;
|
|
29
|
+
operationSearch: import("vue").ComputedRef<any>;
|
|
30
|
+
fields: import("vue").ComputedRef<any[]>;
|
|
31
|
+
canServerPageable: import("vue").ComputedRef<boolean | "">;
|
|
32
|
+
canServerSearch: import("vue").ComputedRef<boolean>;
|
|
33
|
+
canCreate: import("vue").ComputedRef<boolean>;
|
|
34
|
+
canUpdate: import("vue").ComputedRef<boolean>;
|
|
35
|
+
canDelete: import("vue").ComputedRef<boolean>;
|
|
36
|
+
createItem: (item: Record<string, any>, callback?: FormDialogCallback, importing?: boolean) => any;
|
|
37
|
+
importItems: (importItems: Record<string, any>[], callback?: FormDialogCallback) => void;
|
|
38
|
+
updateItem: (item: Record<string, any>, callback?: FormDialogCallback) => any;
|
|
39
|
+
deleteItem: (item: Record<string, any>, callback?: FormDialogCallback) => any;
|
|
40
|
+
loadItems: (options: any) => void;
|
|
41
|
+
reload: () => void;
|
|
42
|
+
isLoading: import("vue").Ref<boolean>;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { computed, ref, watch } from "vue";
|
|
2
|
+
import { useAlert } from "./alert.mjs";
|
|
3
|
+
import { graphqlOperation } from "#imports";
|
|
4
|
+
import { buildRequiredInputFields } from "./graphqlOperation.mjs";
|
|
5
|
+
export function useGraphqlModel(props) {
|
|
6
|
+
const alert = useAlert();
|
|
7
|
+
const items = ref([]);
|
|
8
|
+
const itemsLength = ref(0);
|
|
9
|
+
const search = ref();
|
|
10
|
+
const currentOptions = ref();
|
|
11
|
+
const isLoading = ref(false);
|
|
12
|
+
function capitalizeFirstLetter(string) {
|
|
13
|
+
return string?.charAt(0).toUpperCase() + string?.slice(1);
|
|
14
|
+
}
|
|
15
|
+
function lowercaseFirstLetter(string) {
|
|
16
|
+
return string?.charAt(0).toLowerCase() + string?.slice(1);
|
|
17
|
+
}
|
|
18
|
+
const operationCreate = computed(() => {
|
|
19
|
+
if (props.operationCreate) {
|
|
20
|
+
if (typeof props.operationCreate === "string") {
|
|
21
|
+
if (graphqlOperation[props.operationCreate])
|
|
22
|
+
return graphqlOperation[props.operationCreate];
|
|
23
|
+
} else {
|
|
24
|
+
return props.operationCreate;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return graphqlOperation["create" + capitalizeFirstLetter(props.modelName)];
|
|
28
|
+
});
|
|
29
|
+
const operationUpdate = computed(() => {
|
|
30
|
+
if (props.operationUpdate) {
|
|
31
|
+
if (typeof props.operationUpdate === "string") {
|
|
32
|
+
if (graphqlOperation[props.operationUpdate])
|
|
33
|
+
return graphqlOperation[props.operationUpdate];
|
|
34
|
+
} else {
|
|
35
|
+
return props.operationUpdate;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return graphqlOperation["update" + capitalizeFirstLetter(props.modelName)];
|
|
39
|
+
});
|
|
40
|
+
const operationDelete = computed(() => {
|
|
41
|
+
if (props.operationDelete) {
|
|
42
|
+
if (typeof props.operationDelete === "string") {
|
|
43
|
+
if (graphqlOperation[props.operationDelete])
|
|
44
|
+
return graphqlOperation[props.operationDelete];
|
|
45
|
+
} else {
|
|
46
|
+
return props.operationDelete;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return graphqlOperation["delete" + capitalizeFirstLetter(props.modelName)];
|
|
50
|
+
});
|
|
51
|
+
const operationRead = computed(() => {
|
|
52
|
+
if (props.operationRead) {
|
|
53
|
+
if (typeof props.operationRead === "string") {
|
|
54
|
+
if (graphqlOperation[props.operationRead])
|
|
55
|
+
return graphqlOperation[props.operationRead];
|
|
56
|
+
} else {
|
|
57
|
+
return props.operationRead;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return graphqlOperation[lowercaseFirstLetter(props.modelName)];
|
|
61
|
+
});
|
|
62
|
+
const operationReadPageable = computed(() => {
|
|
63
|
+
if (props.operationReadPageable) {
|
|
64
|
+
if (typeof props.operationReadPageable === "string") {
|
|
65
|
+
if (graphqlOperation[props.operationReadPageable])
|
|
66
|
+
return graphqlOperation[props.operationReadPageable];
|
|
67
|
+
} else {
|
|
68
|
+
return props.operationReadPageable;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return graphqlOperation[lowercaseFirstLetter(props.modelName) + "Pageable"];
|
|
72
|
+
});
|
|
73
|
+
const operationSearch = computed(() => {
|
|
74
|
+
if (props.operationSearch) {
|
|
75
|
+
if (typeof props.operationSearch === "string") {
|
|
76
|
+
if (graphqlOperation[props.operationSearch])
|
|
77
|
+
return graphqlOperation[props.operationSearch];
|
|
78
|
+
} else {
|
|
79
|
+
return props.operationSearch;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return graphqlOperation["search" + capitalizeFirstLetter(props.modelName)];
|
|
83
|
+
});
|
|
84
|
+
function keyToField(key) {
|
|
85
|
+
const parts = key.split(".");
|
|
86
|
+
if (parts.length > 1) {
|
|
87
|
+
const result = {};
|
|
88
|
+
let current = result;
|
|
89
|
+
for (let i = 0; i < parts.length; i++) {
|
|
90
|
+
const part = parts[i];
|
|
91
|
+
const isLast = i === parts.length - 1;
|
|
92
|
+
if (isLast) {
|
|
93
|
+
current[part] = [part];
|
|
94
|
+
} else {
|
|
95
|
+
current[part] = [{}];
|
|
96
|
+
current = current[part][0];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
} else {
|
|
101
|
+
return key;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const fields = computed(() => {
|
|
105
|
+
let tmpFields = [];
|
|
106
|
+
let fieldsProps = props.fields || [];
|
|
107
|
+
let requiredInputFields = [];
|
|
108
|
+
if (props.headers && props.headers.length > 0) {
|
|
109
|
+
props.headers.forEach((header) => {
|
|
110
|
+
if (!fieldsProps.includes(header.key))
|
|
111
|
+
tmpFields.push(keyToField(header.key));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (canUpdate.value) {
|
|
115
|
+
requiredInputFields = buildRequiredInputFields(operationUpdate.value?.variables);
|
|
116
|
+
}
|
|
117
|
+
return [.../* @__PURE__ */ new Set([...fieldsProps, ...tmpFields, ...requiredInputFields])];
|
|
118
|
+
});
|
|
119
|
+
const canServerPageable = computed(() => {
|
|
120
|
+
return !!operationReadPageable.value && (!search.value || search.value && canServerSearch.value);
|
|
121
|
+
});
|
|
122
|
+
const canServerSearch = computed(() => {
|
|
123
|
+
return !!operationSearch.value;
|
|
124
|
+
});
|
|
125
|
+
const canCreate = computed(() => {
|
|
126
|
+
return !!operationCreate.value;
|
|
127
|
+
});
|
|
128
|
+
const canUpdate = computed(() => {
|
|
129
|
+
return !!operationUpdate.value;
|
|
130
|
+
});
|
|
131
|
+
const canDelete = computed(() => {
|
|
132
|
+
return !!operationDelete.value;
|
|
133
|
+
});
|
|
134
|
+
function createItem(item, callback, importing = false) {
|
|
135
|
+
isLoading.value = true;
|
|
136
|
+
return operationCreate.value?.call(fields.value, { input: item }).then((result) => {
|
|
137
|
+
if (canServerPageable) {
|
|
138
|
+
if (!importing)
|
|
139
|
+
loadItems(currentOptions.value);
|
|
140
|
+
} else
|
|
141
|
+
items.value.push(result);
|
|
142
|
+
}).catch((error) => {
|
|
143
|
+
alert?.addAlert({ alertType: "error", message: error });
|
|
144
|
+
}).finally(() => {
|
|
145
|
+
if (!importing)
|
|
146
|
+
isLoading.value = false;
|
|
147
|
+
if (callback)
|
|
148
|
+
callback.done();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function importItems(importItems2, callback) {
|
|
152
|
+
isLoading.value = true;
|
|
153
|
+
let importPromise = [];
|
|
154
|
+
importItems2.forEach((item) => {
|
|
155
|
+
let eachPromise = createItem(item, void 0, true);
|
|
156
|
+
if (eachPromise)
|
|
157
|
+
importPromise.push(eachPromise);
|
|
158
|
+
});
|
|
159
|
+
Promise.all(importPromise).finally(() => {
|
|
160
|
+
isLoading.value = false;
|
|
161
|
+
reload();
|
|
162
|
+
if (callback)
|
|
163
|
+
callback.done();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function updateItem(item, callback) {
|
|
167
|
+
isLoading.value = true;
|
|
168
|
+
return operationUpdate.value?.call(fields.value, { input: item }).then((result) => {
|
|
169
|
+
if (canServerPageable)
|
|
170
|
+
loadItems(currentOptions.value);
|
|
171
|
+
else {
|
|
172
|
+
let index = items.value.findIndex((item2) => item2[props.modelKey || "id"] === result[props.modelKey || "id"]);
|
|
173
|
+
if (index !== -1) {
|
|
174
|
+
items.value[index] = result;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}).catch((error) => {
|
|
178
|
+
alert?.addAlert({ alertType: "error", message: error });
|
|
179
|
+
}).finally(() => {
|
|
180
|
+
isLoading.value = false;
|
|
181
|
+
if (callback)
|
|
182
|
+
callback.done();
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function deleteItem(item, callback) {
|
|
186
|
+
isLoading.value = true;
|
|
187
|
+
return operationDelete.value?.call(fields.value, { input: item }).catch((error) => {
|
|
188
|
+
alert?.addAlert({ alertType: "error", message: error });
|
|
189
|
+
}).finally(() => {
|
|
190
|
+
isLoading.value = false;
|
|
191
|
+
reload();
|
|
192
|
+
if (callback)
|
|
193
|
+
callback.done();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function loadItems(options) {
|
|
197
|
+
currentOptions.value = options;
|
|
198
|
+
if (canServerPageable) {
|
|
199
|
+
let pageableVariable = {
|
|
200
|
+
page: options.page,
|
|
201
|
+
perPage: options.itemsPerPage,
|
|
202
|
+
sortBy: options.sortBy
|
|
203
|
+
};
|
|
204
|
+
isLoading.value = true;
|
|
205
|
+
operationReadPageable.value?.call([{ data: fields.value }, "meta"], Object.assign(props.modelBy || {}, { pageable: pageableVariable })).then((result) => {
|
|
206
|
+
items.value = result.data;
|
|
207
|
+
itemsLength.value = result.meta.totalItems;
|
|
208
|
+
}).catch((error) => {
|
|
209
|
+
alert?.addAlert({ alertType: "error", message: error });
|
|
210
|
+
}).finally(() => {
|
|
211
|
+
isLoading.value = false;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function reload() {
|
|
216
|
+
if (canServerPageable.value) {
|
|
217
|
+
loadItems(currentOptions.value);
|
|
218
|
+
} else {
|
|
219
|
+
isLoading.value = true;
|
|
220
|
+
operationRead.value?.call(fields.value, props.modelBy).then((result) => {
|
|
221
|
+
items.value = result;
|
|
222
|
+
}).catch((error) => {
|
|
223
|
+
alert?.addAlert({ alertType: "error", message: error });
|
|
224
|
+
}).finally(() => {
|
|
225
|
+
isLoading.value = false;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
watch(() => props.modelName, () => {
|
|
230
|
+
if (!canServerPageable.value)
|
|
231
|
+
reload();
|
|
232
|
+
}, { immediate: true });
|
|
233
|
+
watch(canServerPageable, () => {
|
|
234
|
+
reload();
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
items,
|
|
238
|
+
itemsLength,
|
|
239
|
+
search,
|
|
240
|
+
currentOptions,
|
|
241
|
+
operationCreate,
|
|
242
|
+
operationUpdate,
|
|
243
|
+
operationDelete,
|
|
244
|
+
operationRead,
|
|
245
|
+
operationReadPageable,
|
|
246
|
+
operationSearch,
|
|
247
|
+
fields,
|
|
248
|
+
canServerPageable,
|
|
249
|
+
canServerSearch,
|
|
250
|
+
canCreate,
|
|
251
|
+
canUpdate,
|
|
252
|
+
canDelete,
|
|
253
|
+
createItem,
|
|
254
|
+
importItems,
|
|
255
|
+
updateItem,
|
|
256
|
+
deleteItem,
|
|
257
|
+
loadItems,
|
|
258
|
+
reload,
|
|
259
|
+
isLoading
|
|
260
|
+
};
|
|
261
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ClassConstructor } from "../utils/object";
|
|
2
|
+
import type { graphqlTypeObject, graphqlVariable } from "../types/graphqlOperation";
|
|
3
|
+
export declare function buildFields(operationFields: graphqlTypeObject, fields?: Array<string | Object> | ClassConstructor<any>, depth?: number): Array<string | Object>;
|
|
4
|
+
export declare function buildVariables(outputVariables?: graphqlVariable[], inputVariables?: {
|
|
5
|
+
[p: string]: any;
|
|
6
|
+
}, reject?: Function, isRoot?: boolean): Record<string, any> | undefined;
|
|
7
|
+
export declare function buildRequiredInputFields(variables: graphqlVariable[] | undefined, inputField?: string): string[];
|
|
8
|
+
export declare function useGraphQlOperation<T>(operationType: string, operation: string, fields?: Array<string | Object> | ClassConstructor<any>, variables?: {
|
|
9
|
+
[p: string]: any;
|
|
10
|
+
}, cache?: boolean): Promise<T>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { graphqlInputType, graphqlOperation, graphqlType, scalarType } from "#imports";
|
|
2
|
+
import { classAttributes, isClassConstructor } from "../utils/object.mjs";
|
|
3
|
+
import { useGraphQl } from "./graphql.mjs";
|
|
4
|
+
export function buildFields(operationFields, fields, depth = 0) {
|
|
5
|
+
if (!operationFields)
|
|
6
|
+
return [];
|
|
7
|
+
if (isClassConstructor(fields))
|
|
8
|
+
fields = classAttributes(fields);
|
|
9
|
+
if (!fields || fields.length == 0)
|
|
10
|
+
fields = [...operationFields.fields];
|
|
11
|
+
if (fields.includes("*")) {
|
|
12
|
+
operationFields.fields.forEach((field) => {
|
|
13
|
+
if (!fields.includes(field))
|
|
14
|
+
fields.push(field);
|
|
15
|
+
});
|
|
16
|
+
fields.splice(fields.indexOf("*"), 1);
|
|
17
|
+
}
|
|
18
|
+
if (depth > 0)
|
|
19
|
+
operationFields.relations.forEach((relation) => fields.push(relation.name));
|
|
20
|
+
if (!fields.includes("timestampField") && operationFields.relations.find((relation) => relation.name == "timestampField"))
|
|
21
|
+
fields.push("timestampField");
|
|
22
|
+
if (!fields.includes("userstampField") && operationFields.relations.find((relation) => relation.name == "userstampField"))
|
|
23
|
+
fields.push("userstampField");
|
|
24
|
+
return fields.map((field) => {
|
|
25
|
+
if (typeof field == "string" && !operationFields?.fields?.includes(field)) {
|
|
26
|
+
let relationField = operationFields?.relations?.find((relation) => relation.name == field);
|
|
27
|
+
if (relationField && relationField.type) {
|
|
28
|
+
return { [relationField.name]: buildFields(graphqlType[relationField.type], void 0, depth - 1) };
|
|
29
|
+
} else {
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
} else if (typeof field == "object") {
|
|
33
|
+
Object.keys(field).forEach((key) => {
|
|
34
|
+
let relationField = operationFields?.relations?.find((relation) => relation.name == key);
|
|
35
|
+
if (relationField && relationField.type) {
|
|
36
|
+
let castField = field;
|
|
37
|
+
castField[key] = buildFields(graphqlType[relationField.type], castField[key], depth - 1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return field;
|
|
42
|
+
}).filter((field) => !!field);
|
|
43
|
+
}
|
|
44
|
+
export function buildVariables(outputVariables, inputVariables, reject, isRoot = true) {
|
|
45
|
+
if (!outputVariables)
|
|
46
|
+
return void 0;
|
|
47
|
+
if (inputVariables) {
|
|
48
|
+
outputVariables.map((variable) => {
|
|
49
|
+
if (variable.type && !scalarType.includes(variable.type)) {
|
|
50
|
+
if (variable.list) {
|
|
51
|
+
if (inputVariables[variable.name] && Array.isArray(inputVariables[variable.name])) {
|
|
52
|
+
let tmpVariables = [];
|
|
53
|
+
inputVariables[variable.name].forEach((inputVariable) => {
|
|
54
|
+
tmpVariables.push(buildVariables(graphqlInputType[variable.type].variables, inputVariable, reject, false));
|
|
55
|
+
});
|
|
56
|
+
variable.value = tmpVariables;
|
|
57
|
+
} else {
|
|
58
|
+
variable.value = void 0;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
variable.value = buildVariables(graphqlInputType[variable.type].variables, inputVariables[variable.name], reject, false);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
variable.value = inputVariables[variable.name];
|
|
65
|
+
}
|
|
66
|
+
return variable;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
outputVariables.forEach((variable) => {
|
|
70
|
+
if (variable.required && variable.value === void 0) {
|
|
71
|
+
console.warn("Required variable " + variable.name + " is not found, operation abort");
|
|
72
|
+
if (reject) {
|
|
73
|
+
reject("Required variable " + variable.name + " is not found");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
let usedVariables = outputVariables.map((variable) => variable.name);
|
|
79
|
+
let droppedVariable = Object.keys(inputVariables || {}).filter((key) => !usedVariables.includes(key));
|
|
80
|
+
if (droppedVariable.length > 0)
|
|
81
|
+
console.debug("There is data not appeared in schema and dropped before operation.", droppedVariable);
|
|
82
|
+
return outputVariables.reduce((acc, item) => {
|
|
83
|
+
acc[item.name] = isRoot ? item : item.value;
|
|
84
|
+
return acc;
|
|
85
|
+
}, {});
|
|
86
|
+
}
|
|
87
|
+
export function buildRequiredInputFields(variables, inputField = "input") {
|
|
88
|
+
let inputVariable = variables?.find((variable) => variable.name == inputField);
|
|
89
|
+
if (inputVariable) {
|
|
90
|
+
let returnFields = [];
|
|
91
|
+
graphqlInputType[inputVariable.type]?.variables?.forEach((variable) => {
|
|
92
|
+
if (variable.required)
|
|
93
|
+
returnFields.push(variable.name);
|
|
94
|
+
});
|
|
95
|
+
return returnFields;
|
|
96
|
+
} else {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function useGraphQlOperation(operationType, operation, fields, variables, cache = false) {
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
let rejectReason = void 0;
|
|
103
|
+
function rejectInside(reason) {
|
|
104
|
+
rejectReason = reason;
|
|
105
|
+
}
|
|
106
|
+
variables = buildVariables(graphqlOperation[operation]?.variables, variables, rejectInside);
|
|
107
|
+
if (rejectReason) {
|
|
108
|
+
reject(rejectReason);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (graphqlOperation[operation]?.fields) {
|
|
112
|
+
fields = buildFields(graphqlOperation[operation]?.fields, fields, 0);
|
|
113
|
+
if (operationType == "Query")
|
|
114
|
+
useGraphQl().queryPromise(operation, fields, variables, cache).then((result) => resolve(result)).catch((error) => reject(error));
|
|
115
|
+
else if (operationType == "Mutation")
|
|
116
|
+
useGraphQl().mutationPromise(operation, fields, variables).then((result) => resolve(result)).catch((error) => reject(error));
|
|
117
|
+
else
|
|
118
|
+
reject("Invalid Operation Type");
|
|
119
|
+
} else {
|
|
120
|
+
reject("No schema of return data available");
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface graphqlVariable {
|
|
2
|
+
name: string;
|
|
3
|
+
list?: boolean;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
type?: string;
|
|
6
|
+
value?: any;
|
|
7
|
+
}
|
|
8
|
+
export interface graphqlOperationObject<T, U> {
|
|
9
|
+
variables?: graphqlVariable[];
|
|
10
|
+
fields?: graphqlTypeObject;
|
|
11
|
+
operationType: "Query" | "Mutation";
|
|
12
|
+
name: string;
|
|
13
|
+
call: (fields?: Array<string | Object>, variables?: U) => Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface graphqlTypeObject {
|
|
17
|
+
fields: string[];
|
|
18
|
+
relations: graphqlVariable[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface graphqlInputObject {
|
|
22
|
+
variables: graphqlVariable[];
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ramathibodi/nuxt-commons",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Ramathibodi Nuxt modules for common components",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,13 +19,27 @@
|
|
|
19
19
|
"import": "./dist/runtime/utils/*.mjs",
|
|
20
20
|
"require": "./dist/runtime/utils/*.cjs"
|
|
21
21
|
},
|
|
22
|
+
"./composables/*": {
|
|
23
|
+
"types": "./dist/runtime/composables/*.d.ts",
|
|
24
|
+
"import": "./dist/runtime/composables/*.mjs",
|
|
25
|
+
"require": "./dist/runtime/composables/*.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./composables/*/*": {
|
|
28
|
+
"types": "./dist/runtime/composables/*/*.d.ts",
|
|
29
|
+
"import": "./dist/runtime/composables/*/*.mjs",
|
|
30
|
+
"require": "./dist/runtime/composables/*/*.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./components/*": "./dist/runtime/components/*.vue",
|
|
33
|
+
"./components/*/*": "./dist/runtime/components/*/*.vue",
|
|
22
34
|
"./lab/*": "./dist/runtime/labs/*.vue",
|
|
23
35
|
"./lab/*/*": "./dist/runtime/labs/*/*.vue"
|
|
24
36
|
},
|
|
25
37
|
"main": "./dist/module.cjs",
|
|
26
38
|
"types": "./dist/types.d.ts",
|
|
27
39
|
"files": [
|
|
28
|
-
"dist"
|
|
40
|
+
"dist",
|
|
41
|
+
"scripts",
|
|
42
|
+
"templates"
|
|
29
43
|
],
|
|
30
44
|
"scripts": {
|
|
31
45
|
"prepack": "nuxt-module-build build",
|
|
@@ -36,7 +50,8 @@
|
|
|
36
50
|
"lint": "eslint .",
|
|
37
51
|
"lint:fix": "eslint . --fix",
|
|
38
52
|
"test": "vitest run",
|
|
39
|
-
"test:watch": "vitest watch"
|
|
53
|
+
"test:watch": "vitest watch",
|
|
54
|
+
"postinstall": "node scripts/postInstall.cjs"
|
|
40
55
|
},
|
|
41
56
|
"dependencies": {
|
|
42
57
|
"@codemirror/lang-html": "^6.4.9",
|
|
@@ -51,6 +66,10 @@
|
|
|
51
66
|
"@fullcalendar/multimonth": "^6.1.11",
|
|
52
67
|
"@fullcalendar/timegrid": "^6.1.11",
|
|
53
68
|
"@fullcalendar/vue3": "^6.1.11",
|
|
69
|
+
"@graphql-codegen/cli": "^5.0.2",
|
|
70
|
+
"@graphql-codegen/add": "^5.0.2",
|
|
71
|
+
"@graphql-codegen/plugin-helpers": "^5.0.4",
|
|
72
|
+
"@graphql-codegen/typescript": "^4.0.6",
|
|
54
73
|
"@mdi/font": "^7.4.47",
|
|
55
74
|
"@nuxt/kit": "^3.11.2",
|
|
56
75
|
"@nuxtjs/apollo": "5.0.0-alpha.14",
|
|
@@ -61,6 +80,7 @@
|
|
|
61
80
|
"cropperjs": "^1.6.2",
|
|
62
81
|
"currency.js": "^2.0.4",
|
|
63
82
|
"fuse.js": "^7.0.0",
|
|
83
|
+
"graphql": "^16.9.0",
|
|
64
84
|
"gql-query-builder": "^3.8.0",
|
|
65
85
|
"lodash": "^4.17.21",
|
|
66
86
|
"luxon": "^3.4.4",
|
|
@@ -95,4 +115,4 @@
|
|
|
95
115
|
"vitest": "^1.5.1",
|
|
96
116
|
"vue-tsc": "^1.8.27"
|
|
97
117
|
}
|
|
98
|
-
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
// Function to copy a file from source to destination
|
|
6
|
+
function copyFile(src, dest) {
|
|
7
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
8
|
+
fs.copyFileSync(src, dest);
|
|
9
|
+
console.log(`Copied ${src} to ${dest}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Function to copy all files in a directory recursively
|
|
13
|
+
function copyDirectory(srcDir, destDir) {
|
|
14
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
15
|
+
|
|
16
|
+
for (let entry of entries) {
|
|
17
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
18
|
+
const destPath = path.join(destDir, entry.name);
|
|
19
|
+
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
copyDirectory(srcPath, destPath);
|
|
22
|
+
} else {
|
|
23
|
+
copyFile(srcPath, destPath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Function to add copied files to git
|
|
29
|
+
function addFilesToGit(files, projectRoot) {
|
|
30
|
+
files.forEach(file => {
|
|
31
|
+
const filePath = path.join(projectRoot, file);
|
|
32
|
+
execSync(`git add ${filePath}`, { stdio: 'inherit' });
|
|
33
|
+
console.log(`Added ${filePath} to git`);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Function to modify package.json to add a new script
|
|
38
|
+
function addScriptToPackageJson(scriptName, scriptCommand) {
|
|
39
|
+
const packageJsonPath = path.join(process.env.INIT_CWD, 'package.json');
|
|
40
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
41
|
+
|
|
42
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
43
|
+
packageJson.scripts[scriptName] = scriptCommand;
|
|
44
|
+
|
|
45
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
46
|
+
console.log(`Added script "${scriptName}": "${scriptCommand}" to package.json`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Function to run npm run codegen safely
|
|
50
|
+
function runCodegen(projectRoot) {
|
|
51
|
+
try {
|
|
52
|
+
execSync('npm run codegen', { cwd: projectRoot, stdio: 'inherit' });
|
|
53
|
+
console.log('Successfully ran npm run codegen');
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.log('npm run codegen failed or script not found, continuing...');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (process.env.INIT_CWD === process.cwd()) process.exit()
|
|
60
|
+
|
|
61
|
+
// Define source and destination directories
|
|
62
|
+
const srcDir = path.join(__dirname, '..', 'templates');
|
|
63
|
+
const destDir = process.env.INIT_CWD; // Project root
|
|
64
|
+
|
|
65
|
+
// Copy all files from templates directory to project root
|
|
66
|
+
copyDirectory(srcDir, destDir);
|
|
67
|
+
|
|
68
|
+
// Add new script to package.json
|
|
69
|
+
addScriptToPackageJson('codegen', 'graphql-codegen --require dotenv/config --config ./.codegen/codegen.ts dotenv_config_path=.env');
|
|
70
|
+
|
|
71
|
+
// Add copied files to git
|
|
72
|
+
const copiedFiles = fs.readdirSync(srcDir).map(file => path.join('.', file));
|
|
73
|
+
addFilesToGit(copiedFiles, destDir);
|
|
74
|
+
|
|
75
|
+
// Run npm run codegen safely
|
|
76
|
+
runCodegen(destDir);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type {CodegenConfig} from '@graphql-codegen/cli';
|
|
2
|
+
|
|
3
|
+
const config: CodegenConfig = {
|
|
4
|
+
overwrite: true,
|
|
5
|
+
schema: process.env.NUXT_PUBLIC_WS_GRAPHQL,
|
|
6
|
+
generates: {
|
|
7
|
+
'./types/graphql.ts': {
|
|
8
|
+
plugins: [{
|
|
9
|
+
add: {
|
|
10
|
+
content: '//Auto-generated file, do not make any change. Content could be overwritten.',
|
|
11
|
+
}
|
|
12
|
+
},'typescript'],
|
|
13
|
+
config: {
|
|
14
|
+
maybeValue: 'T | null | undefined',
|
|
15
|
+
inputMaybeValue: 'T | null | undefined',
|
|
16
|
+
declarationKind: 'class',
|
|
17
|
+
enumsAsTypes: true,
|
|
18
|
+
skipTypename: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
'./composables/graphqlObject.ts': {
|
|
22
|
+
plugins: [{
|
|
23
|
+
add: {
|
|
24
|
+
content: '//Auto-generated file, do not make any change. Content could be overwritten.',
|
|
25
|
+
}
|
|
26
|
+
},'./.codegen/plugin-schema-object.js'],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
hooks: { afterAllFileWrite: ['prettier --parser typescript --tab-width 4 --write','git add'] }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default config;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const { getCachedDocumentNodeFromSchema } = require('@graphql-codegen/plugin-helpers')
|
|
2
|
+
const { visit } = require('graphql')
|
|
3
|
+
const { camelCase } = require('lodash')
|
|
4
|
+
|
|
5
|
+
function capitalizeFirstLetter(string) {
|
|
6
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function inputTypeToObject(inputType,result) {
|
|
10
|
+
if (inputType.kind==="NonNullType") {
|
|
11
|
+
result["required"] = true
|
|
12
|
+
inputTypeToObject(inputType.type,result)
|
|
13
|
+
}
|
|
14
|
+
if (inputType.kind==="ListType") {
|
|
15
|
+
result["list"] = true
|
|
16
|
+
inputTypeToObject(inputType.type,result)
|
|
17
|
+
}
|
|
18
|
+
if (inputType.kind==="NamedType") {
|
|
19
|
+
result["type"] = inputType.name.value
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
plugin(schema, _documents, _config) {
|
|
25
|
+
const astNode = getCachedDocumentNodeFromSchema(schema) // Transforms the GraphQLSchema into ASTNode
|
|
26
|
+
|
|
27
|
+
const operationNode = []
|
|
28
|
+
const typeNode = []
|
|
29
|
+
const inputNode = []
|
|
30
|
+
|
|
31
|
+
const scalarType =['ID','String','Boolean','Int','Float']
|
|
32
|
+
|
|
33
|
+
const result = visit(astNode, {
|
|
34
|
+
ObjectTypeDefinition(node) {
|
|
35
|
+
if (node.name.value==="Query" || node.name.value==="Mutation")
|
|
36
|
+
{
|
|
37
|
+
node.fields.forEach((field)=>{
|
|
38
|
+
let fieldType = {}
|
|
39
|
+
let fieldVariables = []
|
|
40
|
+
inputTypeToObject(field.type,fieldType)
|
|
41
|
+
field.arguments.forEach((argument)=>{
|
|
42
|
+
let fieldVariablesItem = {name: argument.name.value}
|
|
43
|
+
if (argument.defaultValue) fieldVariablesItem.value = argument.defaultValue.value
|
|
44
|
+
if (argument.type) inputTypeToObject(argument.type,fieldVariablesItem)
|
|
45
|
+
fieldVariables.push(fieldVariablesItem)
|
|
46
|
+
})
|
|
47
|
+
operationNode.push({
|
|
48
|
+
name: field.name.value,
|
|
49
|
+
variables: fieldVariables,
|
|
50
|
+
type: fieldType.type,
|
|
51
|
+
operationType: node.name.value
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
} else {
|
|
55
|
+
let fields = []
|
|
56
|
+
node.fields.forEach((field)=>{
|
|
57
|
+
let fieldVariablesItem = {name: field.name.value}
|
|
58
|
+
if (field.type) inputTypeToObject(field.type,fieldVariablesItem)
|
|
59
|
+
fields.push(fieldVariablesItem)
|
|
60
|
+
})
|
|
61
|
+
typeNode.push({
|
|
62
|
+
name: node.name.value,
|
|
63
|
+
fields: fields
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
InputObjectTypeDefinition(node) {
|
|
68
|
+
let fields = []
|
|
69
|
+
node.fields.forEach((field)=>{
|
|
70
|
+
let fieldVariablesItem = {name: field.name.value}
|
|
71
|
+
if (field.defaultValue) fieldVariablesItem.value = field.defaultValue.value
|
|
72
|
+
if (field.type) inputTypeToObject(field.type,fieldVariablesItem)
|
|
73
|
+
fields.push(fieldVariablesItem)
|
|
74
|
+
})
|
|
75
|
+
inputNode.push({
|
|
76
|
+
name: node.name.value,
|
|
77
|
+
variables: fields,
|
|
78
|
+
})
|
|
79
|
+
},
|
|
80
|
+
ScalarTypeDefinition(node) {
|
|
81
|
+
scalarType.push(node.name.value)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
typeNode.map((node)=>{
|
|
86
|
+
let scalars = []
|
|
87
|
+
let relations = []
|
|
88
|
+
node.fields.forEach((field)=>{
|
|
89
|
+
if (scalarType.includes(field.type)) scalars.push(field.name)
|
|
90
|
+
else relations.push(field)
|
|
91
|
+
})
|
|
92
|
+
node.scalars = scalars
|
|
93
|
+
node.relations = relations
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return `
|
|
97
|
+
import * as graphQlClass from "~/types/graphql"
|
|
98
|
+
import { useGraphQlOperation } from "#imports";
|
|
99
|
+
|
|
100
|
+
export const scalarType = ${JSON.stringify(scalarType)}
|
|
101
|
+
|
|
102
|
+
function operationCall${"<"+"T,U>"}(operationType:string, operation: string,fields?: ${"Array"+"<string | Object>"},variables?: U) {
|
|
103
|
+
return useGraphQlOperation${"<"+"T>"}(operationType,operation,fields,variables as {[p: string] : any} | undefined)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function createGraphQLOperation${"<"+"T,U>"}(operationType: "Query" | "Mutation",name: string,variables?: graphqlVariable[],fields?: graphqlTypeObject): graphqlOperationObject${"<"+"T,U>"} {
|
|
107
|
+
return {
|
|
108
|
+
operationType,
|
|
109
|
+
name,
|
|
110
|
+
variables,
|
|
111
|
+
fields,
|
|
112
|
+
call: async function(fields?: ${"Array"+"<string | Object>"}, variables?: U): Promise${"<"+"T>"} {
|
|
113
|
+
return operationCall(operationType,name, fields, variables);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface graphqlTypeObjects extends ${"Record<string,"+"graphqlTypeObject>"} {
|
|
119
|
+
${typeNode.map((node)=>`${node.name}: graphqlTypeObject`).join("\n")}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const graphqlType: graphqlTypeObjects= {
|
|
123
|
+
${typeNode.map((node)=>`${node.name} : {
|
|
124
|
+
fields : ${JSON.stringify(node.scalars)},
|
|
125
|
+
relations: ${JSON.stringify(node.relations)}
|
|
126
|
+
} as graphqlTypeObject `).join(",")}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface graphqlInputObjects extends ${"Record<string,"+"graphqlInputObject>"} {
|
|
130
|
+
${inputNode.map((node)=>`${node.name}: graphqlInputObject`).join("\n")}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const graphqlInputType: graphqlInputObjects = {
|
|
134
|
+
${inputNode.map((node)=>`${node.name} : {
|
|
135
|
+
variables: ${JSON.stringify(node.variables)},
|
|
136
|
+
}`).join(",")}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface graphqlOperationObjects extends ${"Record<string,"+"graphqlOperationObject"+"<"+"any"+",any>"+">"} {
|
|
140
|
+
${operationNode.map((node)=>`${node.name}: graphqlOperationObject${"<graphQlClass"+"."+node.operationType+"['"+node.name+"'],"+ ((node.variables.length) ? "graphQlClass."+node.operationType+capitalizeFirstLetter(camelCase(node.name))+"Args" : "undefined")+">"}`).join("\n")}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const graphqlOperation: graphqlOperationObjects = {
|
|
144
|
+
${operationNode.map((node)=>`${node.name}: createGraphQLOperation${"<graphQlClass"+"."+node.operationType+"['"+node.name+"'],"+ ((node.variables.length) ? "graphQlClass."+node.operationType+capitalizeFirstLetter(camelCase(node.name))+"Args" : "undefined")+">"}('${node.operationType}','${node.name}',${JSON.stringify(node.variables)},${typeNode.find((type)=>type.name===node.type) ? "graphqlType."+node.type : "undefined"})`).join(",")}
|
|
145
|
+
}
|
|
146
|
+
`
|
|
147
|
+
}
|
|
148
|
+
}
|