@simitgroup/simpleapp-generator 1.0.29 → 1.0.32

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 (39) hide show
  1. package/README.md +136 -16
  2. package/dist/framework.js +8 -3
  3. package/dist/framework.js.map +1 -1
  4. package/dist/generate.js +14 -3
  5. package/dist/generate.js.map +1 -1
  6. package/dist/processors/jsonschemabuilder.js +109 -16
  7. package/dist/processors/jsonschemabuilder.js.map +1 -1
  8. package/dist/type.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/framework.ts +8 -3
  11. package/src/generate.ts +18 -5
  12. package/src/processors/jsonschemabuilder.ts +129 -20
  13. package/src/type.ts +45 -1
  14. package/templates/basic/controller.eta +34 -17
  15. package/templates/basic/model.eta +4 -1
  16. package/templates/basic/pageindex.vue.eta +60 -5
  17. package/templates/basic/service.eta +29 -3
  18. package/templates/basic/simpleappclient.eta +54 -1
  19. package/templates/nest/SimpleAppService.eta +136 -39
  20. package/templates/nest/TenantMiddleware.eta +8 -13
  21. package/templates/nest/UserProvider.eta +127 -0
  22. package/templates/nest/Workflow.eta +75 -0
  23. package/templates/nest/app.controller.eta +12 -0
  24. package/templates/nest/app.module.eta +6 -2
  25. package/templates/nest/app.service.eta +8 -0
  26. package/templates/nest/nest.env.eta +7 -0
  27. package/templates/nest/nest.main.eta +2 -2
  28. package/templates/nuxt/components.crudsimple.vue.eta +2 -2
  29. package/templates/nuxt/components.debugdocdata.vue.eta +12 -6
  30. package/templates/nuxt/components.menus.vue.eta +22 -7
  31. package/templates/nuxt/composables.getautocomplete.ts.eta +3 -5
  32. package/templates/nuxt/composables.getmenus.ts.eta +12 -2
  33. package/templates/nuxt/pages.[xorg].index.vue.eta +9 -8
  34. package/templates/nuxt/pages.index.vue.eta +61 -8
  35. package/templates/nuxt/plugins.simpleapp.ts.eta +7 -0
  36. package/templates/nuxt/server.api.ts.eta +2 -1
  37. package/templates/nuxt/tailwind.css.eta +22 -1
  38. package/tsconfig.json +4 -1
  39. package/templates/nest/User.eta +0 -115
package/src/framework.ts CHANGED
@@ -20,6 +20,11 @@ let config = {
20
20
  "OAUTH2_CLIENTID":"client-id",
21
21
  "OAUTH2_CLIENTSECRET":"client-secret-value",
22
22
  "AUTH_SECRET_KEY":"my-secret",
23
+ },
24
+ "bpmnsetting":{
25
+ "BPMN_HOST":"127.0.0.1",
26
+ "BPMN_PORT":"9000",
27
+ "BPMN_API_KEY":"12345"
23
28
  }
24
29
  }
25
30
 
@@ -61,7 +66,7 @@ export const prepareNest = (callback:Function)=>{
61
66
  if(!fs.existsSync(`${targetfolder}/.env`)){
62
67
 
63
68
 
64
- exec(`cd ${targetfolder};pnpm install --save nest-keycloak-connect keycloak-connect @nestjs/serve-static jsonwebtoken axios @darkwolf/base64url json-schema @wearenova/mongoose-tenant @nestjs/swagger @nestjs/mongoose mongoose ajv ajv-formats @nestjs/config`,async (error, stdout, stderr)=>{
69
+ exec(`cd ${targetfolder};pnpm install --save nest-keycloak-connect keycloak-connect bpmn-client @nestjs/serve-static jsonwebtoken axios @darkwolf/base64url json-schema @wearenova/mongoose-tenant @nestjs/swagger @nestjs/mongoose mongoose ajv ajv-formats ajv-errors @nestjs/config`,async (error, stdout, stderr)=>{
65
70
  // log.info(`dependency installed`)
66
71
  if(!error){
67
72
  fs.mkdirSync(`${targetfolder}/public_html`,{recursive:true})
@@ -101,7 +106,7 @@ export const prepareNuxt = (callback:Function)=>{
101
106
  exec(`cd ${targetfolder};pnpm install;pnpm install -D @sidebase/nuxt-auth @nuxt/ui @types/node @vueuse/nuxt @sidebase/nuxt-auth @vueuse/core nuxt-security prettier `, (error, stdout, stderr)=>{
102
107
  //;pnpm install
103
108
  console.log(error, stdout, stderr)
104
- exec(`cd ${targetfolder};pnpm install --save next-auth@4.21.1 @darkwolf/base64url @nuxt/ui ajv dotenv @fullcalendar/core @fullcalendar/vue3 quill uuid ajv-formats primeflex primeicons prettier primevue axios json-schema mitt @simitgroup/simpleapp-vue-component@latest`, (error, stdout, stderr)=>{
109
+ exec(`cd ${targetfolder};pnpm install --save @darkwolf/base64url next-auth@4.21.1 @darkwolf/base64url @nuxt/ui ajv ajv-formats ajv-errors dotenv @fullcalendar/core @fullcalendar/vue3 quill uuid primeflex primeicons prettier primevue axios json-schema mitt @simitgroup/simpleapp-vue-component@latest`, (error, stdout, stderr)=>{
105
110
  console.log(error, stdout, stderr)
106
111
 
107
112
  fs.mkdirSync(`${targetfolder}/assets/css/`,{recursive:true})
@@ -159,7 +164,7 @@ export const prettyNuxt = ()=>{
159
164
 
160
165
  }
161
166
  export const prettyNest = ()=>{
162
- exec(`cd ${config.backendFolder};npm run format`)
167
+ exec(`cd ${config.backendFolder};npm run format;npx prettier --write src/dicts/foreignkeys.json`)
163
168
  }
164
169
 
165
170
  export const prepareOpenApiClient = () => {
package/src/generate.ts CHANGED
@@ -26,20 +26,21 @@ export const initialize = async (defFolder:string,backendfolder:string,frontend
26
26
  let activatemodules:ModuleObject[]=[]
27
27
  //
28
28
  const files = readdirSync(defFolder)
29
+ // log.warn("all schemas:",files)
29
30
  // readdir(defFolder, (err, files) => {
30
31
  // files.forEach((file) => {
31
32
  for(let j = 0; j< files.length;j++){
32
33
  const file = files[j]
34
+ log.info(`Load `+clc.green(file))
33
35
  const filearr = file.split('.');
34
36
  let rendertype = 'basic';
35
37
  const docname = filearr[0].toLowerCase();
36
38
  const doctype = filearr[1].toLowerCase();
37
- const jsonstring = readFileSync(defFolder +path.sep+ file, 'utf-8');
38
- const jsondata = JSON.parse(jsonstring);
39
+ const jsonstring = readFileSync(defFolder +path.sep+ file, 'utf-8');
39
40
  let allmodels: ChildModels = {} as ChildModels;
40
41
 
41
- if (file.endsWith(extjsonschema)) {
42
- log.info(`Load `+clc.green(file))
42
+ if (file.endsWith(extjsonschema)) {
43
+ const jsondata = JSON.parse(jsonstring);
43
44
  rendertype = 'basic';
44
45
  jsonschemas[docname] = jsondata;
45
46
  // foreignkeys:
@@ -96,6 +97,7 @@ const generate = (
96
97
  models: allmodels,
97
98
  autocompletecode:allmodels[capitalizeFirstLetter(docname)].codeField,
98
99
  autocompletename:allmodels[capitalizeFirstLetter(docname)].nameField,
100
+ moreAutoComplete:allmodels[capitalizeFirstLetter(docname)].moreAutoComplete,
99
101
  schema: allmodels[capitalizeFirstLetter(docname)].model,
100
102
  apiSchemaName: capitalizeFirstLetter(docname), //capitalizeFirstLetter(doctype) + 'ApiSchema',
101
103
  typename: capitalizeFirstLetter(docname),
@@ -108,6 +110,10 @@ const generate = (
108
110
  backEndCode: '',
109
111
  controllerCode:'',
110
112
  apiSchemaCode:'',
113
+ docStatusSettings:allmodels[capitalizeFirstLetter(docname)].docStatusSettings,
114
+ apiSettings:allmodels[capitalizeFirstLetter(docname)].apiSettings,
115
+ requireautocomplete:allmodels[capitalizeFirstLetter(docname)].requireautocomplete,
116
+ isolationtype:allmodels[capitalizeFirstLetter(docname)].isolationtype
111
117
  };
112
118
 
113
119
  // console.log('generate 2', JSON.stringify(variables));
@@ -269,8 +275,9 @@ const prepareEnvironments = (backendfolder:string,frontendfolder:string)=>{
269
275
 
270
276
  copyFileSync(`${constants.templatedir}/nest/SimpleAppService.eta`,`${targetfolder}/SimpleAppService.ts`)
271
277
  copyFileSync(`${constants.templatedir}/nest/SimpleAppController.eta`,`${targetfolder}/SimpleAppController.ts`)
278
+ copyFileSync(`${constants.templatedir}/nest/Workflow.eta`,`${targetfolder}/Workflow.ts`)
272
279
  copyFileSync(`${constants.templatedir}/nest/TenantMiddleware.eta`,`${targetfolder}/TenantMiddleware.ts`)
273
- copyFileSync(`${constants.templatedir}/nest/User.eta`,`${targetfolder}/User.ts`)
280
+ copyFileSync(`${constants.templatedir}/nest/UserProvider.eta`,`${targetfolder}/UserProvider.ts`)
274
281
 
275
282
  //copy over frontend apiabstract class
276
283
  // copyFileSync(`${constants.templatedir}/nuxt.apigateway.eta`,`${targetfrontendfolder}/[...].ts`)
@@ -289,6 +296,12 @@ const finalize=(modules:ModuleObject[],backendfolder:string,frontendfolder:strin
289
296
  const eta = new Eta({views:constants.templatedir});
290
297
  const txtMainModule = eta.render('./nest/app.module.eta', modules);
291
298
  writeFileSync(`${backendfolder}/src/app.module.ts`, txtMainModule);
299
+
300
+ const txtMainService = eta.render('./nest/app.service.eta', modules);
301
+ writeFileSync(`${backendfolder}/src/app.service.ts`, txtMainService);
302
+
303
+ const txtAppController = eta.render('./nest/app.controller.eta', modules);
304
+ writeFileSync(`${backendfolder}/src/app.controller.ts`, txtAppController);
292
305
 
293
306
  const foreignkeyfile =`${backendfolder}/src/dicts/foreignkeys.json`
294
307
  writeFileSync(foreignkeyfile, JSON.stringify(foreignkeys));
@@ -13,28 +13,64 @@ import {
13
13
  ChildModels,
14
14
  SchemaModel,
15
15
  TypeForeignKey,
16
- TypeForeignKeyCatalogue
16
+ TypeForeignKeyCatalogue,
17
+ DocSetting,ApiSetting,DocStatusSetting,
18
+
17
19
  } from '../type';
18
20
  import { json } from 'stream/consumers';
19
21
  const log: Logger<ILogObj> = new Logger();
22
+
20
23
  const X_DOCUMENT_NO='x-document-no'
24
+ const X_DOCUMENT_LABEL='x-document-label'
25
+ const X_AUTOCOMPLETE_FIELD='x-autocomplete-field'
21
26
  const X_DOCUMENT_NAME='x-document-name'
27
+ const X_DOCUMENT_TYPE='x-document-type'
28
+ const X_COLLECTION_NAME='x-document-collection'
29
+ const X_DOCUMENT_STATUS='x-document-status'
30
+ const X_DOCUMENT_API='x-document-api'
31
+ const X_IGNORE_AUTOCOMPLETE='x-ignore-autocomplete'
22
32
  const FOREIGNKEY_PROPERTY='x-foreignkey'
33
+ const X_TEL_NO='x-tel'
34
+ const X_ISOLATION_TYPE='x-isolation-type'
35
+
36
+ let docSetting:DocSetting={} as DocSetting
37
+
38
+
39
+
23
40
  let allmodels: ChildModels = {};
24
41
  let fullschema={}
25
- let fieldAutoCompleteCode=''
26
- let fieldAutoCompleteName=''
42
+ // let fieldAutoCompleteCode=''
43
+ // let fieldAutoCompleteName=''
44
+ let moreAutoComplete:string[]=[]
45
+
46
+
47
+ const newDocSetting=(doctype:string,docname:string):DocSetting=>{
48
+ return {
49
+ docName:docname, //done
50
+ docType:doctype, //done
51
+ colDocNo:'', //done
52
+ colDocLabel:'', //done
53
+ collectionName:docname, //done
54
+ autocompleteFields:[],
55
+ docStatusSettings:[],
56
+ apiSettings:[],
57
+ requireautocomplete:true,
58
+ isolationtype:"org"
27
59
 
60
+ }
61
+ }
28
62
  export const readJsonSchemaBuilder = async (
29
63
  doctype: string,
30
64
  docname: string,
31
65
  orijsondata:JSONSchema7,
32
66
  allforeignkeys:TypeForeignKeyCatalogue
33
67
  ) => {
34
-
35
- fieldAutoCompleteCode=''
36
- fieldAutoCompleteName=''
68
+ docSetting=newDocSetting(doctype,docname)
69
+ // fieldAutoCompleteCode=''
70
+ // fieldAutoCompleteName=''
71
+ moreAutoComplete=[]
37
72
  allmodels = {};
73
+
38
74
  const validateddata: JSONSchema7 = { ...orijsondata };
39
75
  let schema: SchemaModel | SchemaModel[];
40
76
  const tmpjsondata = await $RefParser.dereference(orijsondata).then((schema)=>{
@@ -52,12 +88,12 @@ export const readJsonSchemaBuilder = async (
52
88
  } else if (jsondata.type == 'array') {
53
89
  throw(`unsupport array type for ${docname}.${doctype}`)
54
90
  }
55
- if(fieldAutoCompleteCode=='') {
91
+ if(docSetting.colDocNo=='' && docSetting.requireautocomplete) {
56
92
  log.error(`you shall define 1 field with format:'${X_DOCUMENT_NO}'`)
57
93
  throw "missing field format"
58
94
  }
59
- if(fieldAutoCompleteName=='') {
60
- log.error(`you shall define 1 field with format: '${X_DOCUMENT_NAME}'}`)
95
+ if(docSetting.colDocLabel=='' && docSetting.requireautocomplete) {
96
+ log.error(`you shall define 1 field with format: '${X_DOCUMENT_LABEL}'}`)
61
97
  throw "missing field format"
62
98
  }
63
99
 
@@ -82,6 +118,50 @@ const processObject = (doctype: string,
82
118
  jsondata.properties['updated'] = {type: 'string',description: 'Control value, dont edit it',};
83
119
  jsondata.properties['createdby'] = {type: 'string',description: 'Control value, dont edit it',};
84
120
  jsondata.properties['updatedby'] = {type: 'string',description: 'Control value, dont edit it',};
121
+
122
+ if(jsondata[X_ISOLATION_TYPE] && ['none','tenant','org','branch'].includes(jsondata[X_ISOLATION_TYPE]) ){
123
+ docSetting.isolationtype=jsondata[X_ISOLATION_TYPE]
124
+ }
125
+ if(jsondata[X_IGNORE_AUTOCOMPLETE] && jsondata[X_IGNORE_AUTOCOMPLETE]==true){
126
+ docSetting.requireautocomplete=false
127
+ }
128
+ if(jsondata[X_DOCUMENT_API] && Array.isArray(jsondata[X_DOCUMENT_API])){
129
+ log.warn("x-document-api exists:")
130
+ log.warn(jsondata[X_DOCUMENT_API])
131
+ for(let i=0; i<jsondata[X_DOCUMENT_API].length;i++){
132
+ const tmp:ApiSetting =jsondata[X_DOCUMENT_API][i]
133
+ if(!tmp.action){
134
+ const errmsg = "x-document-api defined but undefine property 'action'"
135
+ log.error(errmsg)
136
+
137
+ throw errmsg
138
+ }
139
+ if(!tmp.method){
140
+ const errmsg = "x-document-api defined but undefine property 'method'"
141
+ log.error(errmsg)
142
+ throw errmsg
143
+ }
144
+ docSetting.apiSettings.push(tmp)
145
+ }
146
+ }
147
+ if(jsondata[X_DOCUMENT_STATUS] && Array.isArray(jsondata[X_DOCUMENT_STATUS])){
148
+ for(let i=0; i<jsondata[X_DOCUMENT_STATUS].length;i++){
149
+ const tmp:DocStatusSetting =jsondata[X_DOCUMENT_STATUS][i]
150
+ if(tmp.statusCode ===undefined){
151
+ const errmsg = "x-document-status defined but undefine property 'statusCode'"
152
+ log.error(errmsg)
153
+ throw errmsg
154
+ }
155
+ if(!tmp.statusName ===undefined){
156
+ const errmsg = "x-document-status defined but undefine property 'statusName'"
157
+ log.error(errmsg)
158
+ throw errmsg
159
+ }
160
+ docSetting.docStatusSettings.push(tmp)
161
+ }
162
+ }
163
+
164
+
85
165
  return genSchema(
86
166
  capitalizeFirstLetter(docname),
87
167
  'object',
@@ -90,12 +170,8 @@ const processObject = (doctype: string,
90
170
  );
91
171
  }
92
172
 
93
- const genSchema = (
94
- docname: string,
95
- schematype: string,
96
- jsondata: JsonSchemaProperties,//JSONSchema7,//|JsonSchemaProperties|JSONSchema7Definition,
97
- requiredlist: string[] | undefined,
98
- ): SchemaModel => {
173
+ const genSchema = (docname: string,schematype: string,jsondata: JsonSchemaProperties,
174
+ requiredlist: string[] | undefined): SchemaModel => {
99
175
  const newmodel: SchemaModel = {};
100
176
  const props = Object.getOwnPropertyNames(jsondata ??{});
101
177
  // console.log('==== jsondata', jsondata);
@@ -112,11 +188,29 @@ const genSchema = (
112
188
  const isrequired = requiredlist && requiredlist.includes(key);
113
189
  const newName: string = docname + capitalizeFirstLetter(key);
114
190
  if(obj.format && obj.format==X_DOCUMENT_NO){
115
- fieldAutoCompleteCode=key
191
+ docSetting.colDocNo=key
192
+ obj.minLength=obj.minLength??1
193
+ jsondata[key]['minLength']=obj.minLength
194
+ }
195
+ if(obj.format && obj.format==X_DOCUMENT_LABEL){
196
+ docSetting.colDocLabel=key
197
+ obj.minLength=obj.minLength??1
198
+ jsondata[key]['minLength']=obj.minLength
116
199
  }
117
- if(obj.format && obj.format==X_DOCUMENT_NAME){
118
- fieldAutoCompleteName=key
200
+
201
+ if(obj[X_COLLECTION_NAME]){
202
+ docSetting.collectionName=key
119
203
  }
204
+
205
+
206
+ if(obj[X_AUTOCOMPLETE_FIELD]){
207
+ docSetting.autocompleteFields.push(key)
208
+ }
209
+
210
+
211
+ // if(obj.format && obj.format==X_TEL_NO){
212
+ // obj.pattern=obj.pattern ?? '/^\d{7,15}$/gm'
213
+ // }
120
214
  // if (obj.type == 'object' && obj.items ){
121
215
  // console.log("Refer to another object",docname,': ',key,obj,obj.items)
122
216
 
@@ -132,11 +226,14 @@ const genSchema = (
132
226
  // console.warn("FOREIGNKEY_PROPERTY exists",FOREIGNKEY_PROPERTY,obj[FOREIGNKEY_PROPERTY])
133
227
  const masterdatacollection = obj[FOREIGNKEY_PROPERTY]
134
228
  const clientdatacollection = docname.toLowerCase()
135
- const foreignkeyidentity= key+'._id'
229
+ const foreignkeyidentity= key
136
230
  if(!foreignkeys[masterdatacollection]){
137
231
  let tmp:TypeForeignKey = {} as TypeForeignKey
138
232
  tmp[clientdatacollection]=[foreignkeyidentity]
139
233
  foreignkeys[masterdatacollection] = tmp
234
+ }
235
+ else if(!foreignkeys[masterdatacollection][clientdatacollection]){
236
+ foreignkeys[masterdatacollection][clientdatacollection]=[foreignkeyidentity]
140
237
  }else{
141
238
  foreignkeys[masterdatacollection][clientdatacollection].push(foreignkeyidentity)
142
239
  }
@@ -159,7 +256,18 @@ const genSchema = (
159
256
  // console.log(key,'--------newmodel',obj, newmodel[key]);
160
257
  }
161
258
  }
162
- allmodels[docname] = { type: schematype, model: newmodel,codeField: fieldAutoCompleteCode ,nameField: fieldAutoCompleteName };
259
+ allmodels[docname] = {
260
+ type: schematype,
261
+ model: newmodel,
262
+ codeField: docSetting.colDocNo ,
263
+ nameField: docSetting.colDocLabel,
264
+ moreAutoComplete:docSetting.autocompleteFields,
265
+ docStatusSettings:docSetting.docStatusSettings,
266
+ apiSettings:docSetting.apiSettings,
267
+ requireautocomplete:docSetting.requireautocomplete,
268
+ isolationtype:docSetting.isolationtype
269
+ };
270
+ // console.warn(docname,docSetting.isolationtype)
163
271
  return newmodel;
164
272
  };
165
273
 
@@ -198,3 +306,4 @@ const getField = (
198
306
  }
199
307
  return f;
200
308
  };
309
+
package/src/type.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  import { JSONSchema7, JSONSchema7Definition } from 'json-schema';
2
2
  export type ChildModels = {
3
- [key: string]: { type: string; model: SchemaModel,codeField:string,nameField:string };
3
+ [key: string]: {
4
+ type: string;
5
+ model: SchemaModel,
6
+ codeField:string,
7
+ nameField:string,
8
+ moreAutoComplete:string[],
9
+ docStatusSettings:DocStatusSetting[],
10
+ apiSettings:ApiSetting[],
11
+ requireautocomplete: boolean
12
+ isolationtype:string
13
+ };
4
14
  };
5
15
  export enum Fieldtypes {
6
16
  'string' = 'string',
@@ -51,6 +61,7 @@ doctype: string
51
61
  models: ChildModels
52
62
  autocompletecode:string
53
63
  autocompletename:string
64
+ moreAutoComplete:string[]
54
65
  schema: SchemaModel
55
66
  apiSchemaName: string
56
67
  typename: string
@@ -62,4 +73,37 @@ frontEndCode: string
62
73
  backEndCode: string
63
74
  controllerCode:string
64
75
  apiSchemaCode:string
76
+ docStatusSettings:DocStatusSetting[],
77
+ apiSettings:ApiSetting[],
78
+ requireautocomplete:boolean,
79
+ isolationtype:string
80
+ }
81
+
82
+ export type DocStatusSetting = {
83
+ statusCode:string,
84
+ statusName:string,
85
+ allowApi:string[],
86
+ readonly?:boolean,
87
+ description?:''
88
+ }
89
+
90
+ export type ApiSetting = {
91
+ method:string,
92
+ action:string,
93
+ setDocStatus?:'',
94
+ description?:'',
95
+ bpmnApi?:'',
96
+ data?:any
97
+ }
98
+ export type DocSetting = {
99
+ docName:string,
100
+ docType:string,
101
+ colDocNo:string,
102
+ colDocLabel:string,
103
+ collectionName:string,
104
+ autocompleteFields:string[],
105
+ docStatusSettings:DocStatusSetting[],
106
+ apiSettings:ApiSetting[],
107
+ requireautocomplete:boolean
108
+ isolationtype:string
65
109
  }
@@ -2,7 +2,7 @@
2
2
  * This file was automatically generated by simpleapp generator. Every
3
3
  * MODIFICATION OVERRIDE BY GENERATEOR Except content between:
4
4
  * <begin-controller-code><end-controller-code>
5
- * last change 2023-09-09
5
+ * last change 2023-09-18
6
6
  * Author: Ks Tan
7
7
  */
8
8
  import {
@@ -59,20 +59,6 @@ export class <%= it.typename %>Controller extends SimpleAppController<
59
59
  }
60
60
 
61
61
 
62
- @Get(':id')
63
- @ApiResponse({
64
- status: 200,
65
- description: 'Founds',
66
- type: <%= it.fullApiSchemaName%>
67
- })
68
- @ApiResponse({ status: 404, description: 'Document not found' })
69
- @ApiResponse({ status: 500, description: 'Internal error' })
70
- @ApiOperation({ operationId: 'runFindOne' })
71
- async findOne(@Param('id') id: string) {
72
- return await this._findOne(id);
73
- }
74
-
75
-
76
62
 
77
63
  @Post()
78
64
  @ApiResponse({
@@ -102,6 +88,38 @@ export class <%= it.typename %>Controller extends SimpleAppController<
102
88
  return await this._search(data)
103
89
  }
104
90
 
91
+
92
+ /***************************** x-document-api definations *****************************************/
93
+
94
+ <% for(let i=0;i<it.apiSettings.length;i++){%>
95
+ <% let api = it.apiSettings[i] %>
96
+ <%~ `@${capitalizeFirstLetter(api.method)}(':id/${api.action}')`%>
97
+ @ApiResponse({status: 200,description: '<%=api.description%>'})
98
+ @ApiOperation({ operationId: 'exec<%=capitalizeFirstLetter(api.action)%>' })
99
+ async exec<%=capitalizeFirstLetter(api.action)%>(@Param('id') id: string){
100
+ return await this.service.exec<%=capitalizeFirstLetter(api.action)%>(id)
101
+ }
102
+ <%}%>
103
+
104
+ /*****************************customized code begin here *****************************************/
105
+ <%~ it.controllerCode %>
106
+
107
+ /*****************************customized code end here *****************************************/
108
+
109
+ @Get(':id')
110
+ @ApiResponse({
111
+ status: 200,
112
+ description: 'Founds',
113
+ type: <%= it.fullApiSchemaName%>
114
+ })
115
+ @ApiResponse({ status: 404, description: 'Document not found' })
116
+ @ApiResponse({ status: 500, description: 'Internal error' })
117
+ @ApiOperation({ operationId: 'runFindOne' })
118
+ async findOne(@Param('id') id: string) {
119
+ return await this._findOne(id);
120
+ }
121
+
122
+
105
123
  @Put(':id')
106
124
  @ApiResponse({
107
125
  status: 200,
@@ -129,7 +147,6 @@ export class <%= it.typename %>Controller extends SimpleAppController<
129
147
  }
130
148
 
131
149
 
132
- /*****************************customized code begin here *****************************************/
133
- <%~ it.controllerCode %>
150
+
134
151
 
135
152
  }
@@ -29,4 +29,7 @@ const schemasetting = {
29
29
  };
30
30
 
31
31
  export const <%= it.doctype %>MongoSchema = new Schema(schemasetting,{collection: '<%= it.name %>'})
32
- .index({<%=it.autocompletecode%>:1,orgId:1},{unique:true});
32
+ <%if(it.requireautocomplete){%>
33
+ .index({<%=it.autocompletecode%>:1,orgId:1},{unique:true})
34
+ .index({<%=it.autocompletename%>:1,orgId:1});
35
+ <%}%>
@@ -9,9 +9,11 @@
9
9
  <% let skipcolumns = ['_id','createdby','created','updatedby','updated','orgId','branchId','tenantId','doctype'] %>
10
10
 
11
11
  import { <%= it.typename %>Doc } from "../../../simpleapp/simpleappdocs/<%= it.typename %>Doc";
12
+ import SimpleAppDatatable from "@simitgroup/simpleapp-vue-component/src/components/SimpleAppDatatable.vue";
13
+ import {InputTableColumnType,InputTableColumn} from "@simitgroup/simpleapp-vue-component/src/type";
12
14
  const { $event, $listen } = useNuxtApp();
13
15
  const route = useRoute()
14
- const doc = new <%= it.typename %>Doc(route.params.xorg,$event, $listen);
16
+ const doc = new <%= it.typename %>Doc(route.params.xorg.toString(),$event, $listen);
15
17
  const documentpath = `/${route.params.xorg}/<%= it.name %>`;
16
18
  const data = doc.getReactiveData();
17
19
  const columns = [
@@ -21,6 +23,20 @@
21
23
  <%} else if(['string','number','integer'].indexOf(obj.type)>=0){%>'<%=key%>',<%}%>
22
24
  <%})%>
23
25
  ]
26
+ //prepare subtable if exists
27
+ <%Object.keys(it.jsonschema.properties).forEach(function(key) { %>
28
+ <% let obj=it.jsonschema.properties[key] %>
29
+
30
+ <% if(obj.type=='array' && obj.items.type=='object' && obj.items['properties']){ %>
31
+ <%let tablefields = Object.keys(obj.items.properties) %>
32
+ const <%=it.name%>_<%=key%> :InputTableColumn[] = [
33
+ <% for(let a=0;a<tablefields.length;a++){%>
34
+ {type:InputTableColumnType.field, field:'<%= tablefields[a] %>',style:'width:10%'},
35
+ <%}%>
36
+ ]
37
+ <%}%>
38
+ <%})%>
39
+
24
40
  </script>
25
41
  <template>
26
42
  <div>
@@ -39,7 +55,7 @@
39
55
  <% } else if(obj.type=='array' && obj.items && obj.items.type =='string' ){ %>
40
56
  <SimpleAppChip autofocus :setting="o.getField('#/properties/<%= key %>')" v-model="data.<%= key %>"/>
41
57
  <% } else if(obj.type=='object' && typeof obj['x-foreignkey']!='undefined'){ %>
42
- <SimpleAppAutocomplete autofocus :setting="o.getField('#/properties/<%= key %>')"
58
+ <SimpleAppAutocomplete :setting="o.getField('#/properties/<%= key %>')"
43
59
  v-model="data.<%= key %>" optionLabel="label" :remote-src="getAutocomplete('<%=obj['x-foreignkey']%>')"/>
44
60
  <% } else if(obj.type=='string'){ %>
45
61
  <% if(obj.format=='date'){ %>
@@ -55,9 +71,48 @@
55
71
  <% } else {%>
56
72
  <SimpleAppText autofocus :setting="o.getField('#/properties/<%= key %>')" v-model="data.<%= key %>"/>
57
73
  <% }%>
58
- <% } else { %>
59
- <SimpleAppText autofocus :setting="o.getField('#/properties/<%= key %>')" v-model="data.<%= key %>"/>
60
- <% }%>
74
+ <% } else if(obj.type=='object' && obj.properties){ %>
75
+ <SimpleAppValue :setting="o.getField('#/properties/<%= key %>')">
76
+ <%Object.keys(obj.properties).forEach(function(skey) { %>
77
+ <% let sobj=obj.properties[skey] %>
78
+ <% let fieldpath=`o.getField('#/properties/${key}/properties/${skey}')` %>
79
+ <% let vmodel=`data.${key}.${skey}` %>
80
+ <% let instancePath=`#/${key}/${skey}` %>
81
+
82
+ <% if(sobj.type=='boolean'){ %>
83
+ <SimpleAppCheckbox autofocus :setting="<%~fieldpath%>" instancePath="<%=instancePath%>" v-model="<%= vmodel %>"/>
84
+ <% } else if(sobj.type=='number' || sobj.type=='integer'){ %>
85
+ <SimpleAppNumber autofocus :setting="<%~fieldpath%>" instancePath="<%=instancePath%>" v-model="<%= vmodel %>"/>
86
+ <% } else if(sobj.type=='array' && sobj.items && sobj.items.type =='string' ){ %>
87
+ <SimpleAppChip autofocus :setting="<%~fieldpath%>" instancePath="<%=instancePath%>" v-model="<%= vmodel %>"/>
88
+ <% } else if(sobj.type=='object' && typeof sobj['x-foreignkey']!='undefined'){ %>
89
+ <SimpleAppAutocomplete autofocus :setting="<%~fieldpath%>" instancePath="<%=instancePath%>"
90
+ v-model="<%= vmodel %>" optionLabel="label" :remote-src="getAutocomplete('<%=sobj['x-foreignkey']%>')"/>
91
+ <% } else if(sobj.type=='string'){ %>
92
+ <% if(sobj.format=='date'){ %>
93
+ <SimpleAppText type="<%=sobj.format%>" instancePath="<%=instancePath%>" autofocus :setting="<%~fieldpath%>" v-model="<%= vmodel %>"/>
94
+ <% } else if(sobj.format=='x-text'){ %>
95
+ <SimpleAppTextarea :setting="<%~fieldpath%>'" instancePath="<%=instancePath%>" v-model="<%= vmodel %>"/>
96
+ <% } else if(sobj.format=='x-html'){ %>
97
+ <SimpleAppEditor editorStyle="height: 320px" instancePath="<%=instancePath%>" :setting="<%=fieldpath%>" v-model="<%= vmodel %>"/>
98
+ <% } else if(sobj.format=='email'){ %>
99
+ <SimpleAppText autofocus type="<%=~obj.type%>" instancePath="<%=instancePath%>" :setting="<%=fieldpath%>" v-model="<%= vmodel %>"/>
100
+ <% } else if(sobj.enum){ %>
101
+ <SimpleAppRadio autofocus :setting="<%~fieldpath%>" instancePath="<%=instancePath%>" v-model="<%= vmodel %>"/>
102
+ <% } else {%>
103
+ <SimpleAppText autofocus :setting="<%~fieldpath%>" instancePath="<%=instancePath%>" v-model="<%= vmodel %>"/>
104
+ <% }%>
105
+ <% } %>
106
+ <%})%>
107
+ </SimpleAppValue>
108
+ <% } else if(obj.type=='array' && obj.items.type=='object'){%>
109
+ <button @click="doc.add<%=key%>()" type="button">+</button>
110
+ <SimpleAppInputTable :getAutocomplete="getAutocomplete" :getField="o.getField"
111
+ :setting="o.getField('#/properties/<%=key%>')" #default="row" v-model="data.<%=key%>" :columns="<%=it.name%>_<%=key%>"></SimpleAppInputTable>
112
+ <% } else{%>
113
+ <!-- Can auto generate <%=obj.key %> -->
114
+ <!-- <%~ JSON.stringify(obj) %>-->
115
+ <%}%>
61
116
  <%})%>
62
117
 
63
118
  </CrudSimple>
@@ -6,11 +6,12 @@
6
6
  * last change 2023-09-09
7
7
  * Author: Ks Tan
8
8
  */
9
+ import { UserProvider } from '../../class/UserProvider'
9
10
  import { Injectable } from '@nestjs/common';
10
11
  import { InjectModel } from '@nestjs/mongoose';
11
12
  import { Model } from 'mongoose';
12
13
  import {<%= it.typename%>JsonSchema } from './<%= it.doctype %>.jsonschema'
13
- import { SimpleAppService,IsolationType } from '../../class/SimpleAppService';
14
+ import { SimpleAppService,IsolationType,HookType } from '../../class/SimpleAppService';
14
15
  import { <%Object.keys(it.models).forEach(function(key) { %> <%=key %>, <%})%> } from './<%= it.doctype %>.type';
15
16
 
16
17
 
@@ -20,11 +21,36 @@ export class <%= it.typename %>Service extends SimpleAppService<<%= it.typename
20
21
  protected documentIdentityName='<%~ it.autocompletename %>'
21
22
  constructor(@InjectModel('<%= it.name %>') mydoc: Model<<%= it.typename %>>,
22
23
  ) {
23
- super(mydoc,IsolationType.org);
24
+ super('<%= it.doctype %>','<%= it.name %>',mydoc,IsolationType.<%=it.isolationtype%>);
24
25
  this.setSchema(<%= it.typename%>JsonSchema)
26
+
27
+ <%if(it.moreAutoComplete.length>0){%>
28
+ this.addAutoCompleteField({
29
+ <%Object.keys(it.moreAutoComplete).forEach(function(key) { %>
30
+ <% let f=it.moreAutoComplete[key] %>
31
+ <%~ `${f}: '${f}'`, %>
32
+ <%})%>
33
+ })
34
+ <%}%>
35
+
25
36
  }
26
37
 
27
-
38
+ /***************************** x-document-api definations *****************************************/
39
+
40
+ <%for(let i=0;i<it.apiSettings.length;i++){%>
41
+ <% let api = it.apiSettings[i] %>
42
+ async exec<%=capitalizeFirstLetter(api.action)%>(id:string){
43
+ <%if(api.bpmn!==undefined){%>
44
+ return await this.executeWorkFlow(id,"<%=api.bpmn%>","<%=api.setDocumentStatus%>")
45
+ <%}else if (api.setDocumentStatus !== undefined){%>
46
+ return await this.setDocumentStatus(id,'<%=api.setDocumentStatus%>')
47
+ <%}else {%>
48
+ return Promise.resolve("unknown action")
49
+ <%}%>
50
+
51
+ }
52
+ <%}%>
53
+
28
54
  /*****************************customized frontend + backend code*****************************************/
29
55
 
30
56
  <%~ it.bothEndCode %>