@simitgroup/simpleapp-generator 1.1.26 → 1.1.28

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 (44) hide show
  1. package/dist/framework.d.ts.map +1 -1
  2. package/dist/framework.js +1 -0
  3. package/dist/framework.js.map +1 -1
  4. package/dist/generate.d.ts.map +1 -1
  5. package/dist/generate.js +9 -2
  6. package/dist/generate.js.map +1 -1
  7. package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
  8. package/dist/processors/jsonschemabuilder.js +3 -4
  9. package/dist/processors/jsonschemabuilder.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/framework.ts +1 -0
  12. package/src/generate.ts +11 -2
  13. package/src/processors/jsonschemabuilder.ts +4 -4
  14. package/templates/basic/nest/processor.ts.eta +13 -1
  15. package/templates/basic/nuxt/pages.[id].vue.eta +24 -2
  16. package/templates/basic/nuxt/pages.form.vue.eta +44 -93
  17. package/templates/basic/nuxt/pages.viewer.vue.eta +17 -5
  18. package/templates/basic/nuxt/simpleapp.generate.client.ts.eta +57 -17
  19. package/templates/nest/src/simpleapp/generate/commons/middlewares/tenant.middleware.ts.eta +1 -1
  20. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +9 -3
  21. package/templates/nest/src/simpleapp/profile/profile.controller.ts.eta +22 -0
  22. package/templates/nest/src/simpleapp/profile/profile.service.ts.eta +4 -0
  23. package/templates/nuxt/components/header/HeaderBreadcrumb.vue.eta +2 -1
  24. package/templates/nuxt/components/renderer/RendererDate.vue.eta +2 -2
  25. package/templates/nuxt/components/renderer/RendererForeignKey.vue.eta +13 -4
  26. package/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta +6 -4
  27. package/templates/nuxt/components/simpleApp/SimpleAppFieldContainer.vue.eta +1 -1
  28. package/templates/nuxt/components/simpleApp/SimpleAppForm.vue.eta +12 -21
  29. package/templates/nuxt/components/simpleApp/SimpleAppFormToolBar.vue.eta +106 -0
  30. package/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +128 -106
  31. package/templates/nuxt/composables/getOpenApi.generate.ts.eta +2 -0
  32. package/templates/nuxt/composables/refreshDocumentList.generate.ts.eta +1 -0
  33. package/templates/nuxt/composables/stringHelper.generate.ts.eta +3 -1
  34. package/templates/nuxt/lang/df.ts.eta +17 -0
  35. package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +16 -3
  36. package/templates/nuxt/types/others.ts.eta +9 -4
  37. package/templates/nuxt/types/simpleappinput.ts.eta +11 -0
  38. package/templates/project/lang/default._json +37 -0
  39. package/tsconfig.tsbuildinfo +1 -1
  40. package/templates/nuxt/lang/df.ts._eta +0 -37
  41. /package/templates/basic/nuxt/{pages.new.vue.eta → pages.new.vue.etaxxx} +0 -0
  42. /package/templates/nuxt/components/header/{HeaderBar.vue.eta → HeaderBar.vue._eta} +0 -0
  43. /package/templates/nuxt/components/header/button/{HeaderButtonMenuPicker.vue.eta → HeaderButtonMenuPicker.vue._eta} +0 -0
  44. /package/templates/nuxt/layouts/{default.vue.eta → default.vue._eta} +0 -0
@@ -81,13 +81,25 @@ export class <%= it.typename%>Client extends SimpleAppClient<<%= it.typename%>,<
81
81
  })
82
82
 
83
83
  }
84
- setNew = ()=>{
85
-
86
-
87
- const newdata = Default<%= it.typename%>(crypto.randomUUID())
88
- this.setData(newdata)
84
+ setNew = ()=>{
85
+ const newdata = Default<%= it.typename%>(crypto.randomUUID())
86
+ this.setData(newdata)
87
+ return true
89
88
  }
90
-
89
+ <%if(it.jsonschema.properties['readOnly'] || it.jsonschema.properties['documentStatus']){%>
90
+ public isReadOnly():boolean{
91
+ <%if(it.jsonschema.properties['readOnly']){%>
92
+ const readonly = this.data.value?.readOnly
93
+ if(readonly) return true
94
+ <%}%>
95
+ <%if(it.jsonschema.properties['documentStatus']){%>
96
+ const docstatus = this.data.value.documentStatus
97
+ const statussetting = this.schema['x-simpleapp-config'].allStatus?.find((item)=>item.status==docstatus)
98
+ if(statussetting && statussetting.readOnly) return true
99
+ <%}%>
100
+ return false
101
+ }
102
+ <%}%>
91
103
  <%Object.keys(it.jsonschema.properties).forEach(function(key) { %>
92
104
  <% let obj=it.jsonschema.properties[key] %>
93
105
 
@@ -96,26 +108,54 @@ export class <%= it.typename%>Client extends SimpleAppClient<<%= it.typename%>,<
96
108
  public add<%=key%> = () => {
97
109
 
98
110
  this.getReactiveData().value.<%= key %>.push(Default<%= it.typename%><%=capitalizeFirstLetter(key)%>(crypto.randomUUID()))
111
+ return this.getReactiveData().value.<%= key %>.length -1
99
112
  }
100
113
 
101
114
 
102
115
  <%}%>
103
116
  <%})%>
104
117
 
118
+
119
+ public getActions(){
120
+ const actions = super.getActions()
121
+ actions.docstatus=[
122
+ <%for(let i=0;i<it.docStatusSettings.length;i++){%>
123
+ <% let setting = it.docStatusSettings[i] %>
124
+ '<%=setting.statusName%>',
125
+ <%}%>
126
+ ]
127
+ actions.api=[
128
+ <%for(let i=0;i<it.apiSettings.length;i++){%>
129
+ <% let api = it.apiSettings[i] %>
130
+ '<%=api.action%>',
131
+ <%}%>
132
+ ]
133
+ return actions
134
+ }
135
+
136
+
105
137
  /*****************************begin x-document-api code*****************************************/
106
138
 
107
139
  <%for(let i=0;i<it.apiSettings.length;i++){%>
108
140
  <% let api = it.apiSettings[i] %>
109
- async run<%=capitalizeFirstLetter(api.action)%>(<% if(api.entryPoint && api.entryPoint.includes(':')) {%>
110
- <%let subpath = api.entryPoint.split('/')%>
111
- <% for(let a=0;a<subpath.length;a++){%>
112
- <%const partstr = subpath[a]%>
113
- <%if(partstr.includes(':')){%>
114
- <% const paraname = partstr.replace(':','') %>
115
- <%=paraname%>: string,
116
- <%}%>
117
- <%}%>
118
- <%}%>
141
+ async run<%=capitalizeFirstLetter(api.action)%>(
142
+ <% if(api.entryPoint && api.entryPoint.includes(':')) {%>
143
+ <%let subpath = api.entryPoint.split('/')%>
144
+ <% for(let a=0;a<subpath.length;a++){%>
145
+ <%const partstr = subpath[a]%>
146
+ <%if(partstr.includes(':')){%>
147
+ <% const paraname = partstr.replace(':','') %>
148
+ <%=paraname%>: string,
149
+ <%}%>
150
+ <%}%>
151
+ <%}%>
152
+ <% if(api.queryPara && api.queryPara.length>0) {%>
153
+ <%for(let j=0; j<api.queryPara.length ; j++){%>
154
+ <%=api.queryPara[j]%>: string,
155
+ <%}%>
156
+ <%}%>
157
+
158
+
119
159
  <% if(['post','put','patch'].includes(api.method)){ %>data:any<%}%>
120
160
  ){
121
161
  //const recordid: string = this.data.value._id ?? '';
@@ -236,7 +276,7 @@ export class <%= it.typename%>Client extends SimpleAppClient<<%= it.typename%>,<
236
276
  /***************************** begin document status code*****************************************/
237
277
  <%for(let i=0;i<it.docStatusSettings.length;i++){%>
238
278
  <% let setting = it.docStatusSettings[i] %>
239
- async setStatus<%=capitalizeFirstLetter(setting.status)%>(id:string,data:<%= it.typename%>,runValidate:boolean=false) {
279
+ async setStatus<%=capitalizeFirstLetter(setting.status)%>(id?:string,data:<%= it.typename%>,runValidate:boolean=false) {
240
280
  const {$event} =useNuxtApp()
241
281
  if(runValidate){
242
282
  const errors = this.validateFailed();
@@ -96,7 +96,7 @@ export class TenantMiddleware implements NestMiddleware {
96
96
  this.logger.warn(err, 'invalid xorg or user info');
97
97
  this.logger.error(err);
98
98
  if (err == 'invalid x-org') {
99
- return res.status(403).send(err);
99
+ return res.status(401).send(err);
100
100
  } else {
101
101
  return res.status(401).send(err);
102
102
  }
@@ -90,6 +90,9 @@ export class SimpleAppService<T extends { _id?: string }> {
90
90
  this.data = { ...newdata };
91
91
  return this;
92
92
  };
93
+ public isReadOnly(): boolean {
94
+ return false;
95
+ }
93
96
  reCalculateValue(data: T) {}
94
97
  getIsolationFilter = (appuser: UserContext) => {
95
98
  let isolationFilter = {};
@@ -506,7 +509,7 @@ export class SimpleAppService<T extends { _id?: string }> {
506
509
  .session(dbsession);
507
510
  appuser.addUpdatedRecordId(this.documentName, data._id);
508
511
  await this.hook(appuser, HookType.afterUpdate, result);
509
- return await this.findById(appuser,id);
512
+ return await this.findById(appuser, id);
510
513
  } catch (err) {
511
514
  throw new InternalServerErrorException(err.message);
512
515
  }
@@ -593,8 +596,11 @@ export class SimpleAppService<T extends { _id?: string }> {
593
596
  await this.hook(appuser, HookType.afterSetStatus, createresult);
594
597
  return createresult;
595
598
  } else {
596
- const updateresult = await this.findIdThenUpdate(appuser, id, data);
597
- await this.hook(appuser, HookType.afterSetStatus, data);
599
+ const originalresult = await this.findById(appuser,id)
600
+ const newdata = {...originalresult,...data}
601
+ const updateresult = await this.findIdThenUpdate(appuser, id, newdata);
602
+ const finaldata = await this.findById(appuser,id)
603
+ await this.hook(appuser, HookType.afterSetStatus, finaldata);
598
604
  return updateresult;
599
605
  }
600
606
  }
@@ -67,6 +67,28 @@ export class ProfileController {
67
67
  }
68
68
  }
69
69
 
70
+
71
+ @Get('session')
72
+ @Roles(Role.Everyone, Role.User)
73
+ @ApiOperation({
74
+ operationId: 'getSession',
75
+ description: 'Get current user session',
76
+ })
77
+ @ApiResponse({
78
+ status: 200,
79
+ type: () => String,
80
+ description: 'Success',
81
+ })
82
+ @ApiResponse({ status: 401, type: Object, description: 'Expired' })
83
+ async getSession(@AppUser() appuser: UserContext) {
84
+ const result = await this.profileservice.getSession(appuser);
85
+ if (result) {
86
+ return result;
87
+ } else {
88
+ throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
89
+ }
90
+ }
91
+
70
92
  @Get('/tenants')
71
93
  @Roles(Role.Everyone, Role.User)
72
94
  @ApiOperation({
@@ -224,4 +224,8 @@ export class ProfileService {
224
224
  async getAllTenants(appuser: UserContext) {
225
225
  return await appuser.getAllTenants();
226
226
  }
227
+
228
+ async getSession(appuser:UserContext){
229
+ return 'OK'
230
+ }
227
231
  }
@@ -24,7 +24,8 @@ const items = computed(()=>{
24
24
  pathlabel = getUserProfile().branchName
25
25
  data.push ({ label: pathlabel })
26
26
  }else{
27
- pathlabel = t(p)
27
+
28
+ pathlabel = useNuxtApp().$i18n.te(p) ? t(p) :p
28
29
  data.push ({ label: pathlabel , url:pathurl})
29
30
  }
30
31
 
@@ -1,6 +1,6 @@
1
1
  <template >
2
- <span>{{ toLocalDate(modelValue) }}</span>
2
+ <span>{{ toLocalDate(value??'') }}</span>
3
3
  </template>
4
4
  <script lang="ts" setup>
5
- const modelValue = defineModel()
5
+ const props = defineProps<{value?:string}>()
6
6
  </script>
@@ -1,14 +1,23 @@
1
1
  <template>
2
2
  <span v-if="setting?.collection" @click="viewRecord" class="text-primary-700 hover:text-primary-500 cursor-pointer">
3
- {{modelValue?.label}}
3
+ {{ displayText }}
4
4
  </span>
5
- <span v-else>{{modelValue?.label || modelValue}}</span>
5
+ <span v-else>{{ displayText }}</span>
6
6
  </template>
7
7
  <script setup lang="ts">
8
+ import { ForeignKey } from '~/types';
8
9
 
9
10
 
10
- const modelValue = defineModel<{label:string,_id:string}>()
11
- const props = defineProps<{setting:any}>()
11
+ type SettingProp = {collection:string, displayField?:string}
12
+ const modelValue = defineModel<ForeignKey>()
13
+ const props = defineProps<{setting:SettingProp}>()
14
+ const displayText = computed(()=>{
15
+ const s = props.setting
16
+ if (!modelValue.value) return undefined
17
+ else if(!s.collection) return modelValue.value
18
+ else if(s.displayField) return modelValue.value[s.displayField]
19
+ else return modelValue.value.label
20
+ })
12
21
  const {$event} = useNuxtApp()
13
22
  const viewer=ref()
14
23
  const viewRecord = () => {
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <AutoComplete v-model="modelValue" v-if="!readonly" ref="autocompleteinput"
3
- forceSelection optionLabel="label"
3
+ forceSelection optionLabel="label"
4
+ @focus="($event.target as HTMLInputElement).select()"
4
5
  @item-select="pickAutoComplete"
5
6
  @complete="getListFromAutocompleteApi"
6
7
  :placeholder="t('keyword')"
@@ -128,13 +129,14 @@ const openViewer = (readonly:boolean) =>{
128
129
  const schema:SchemaType = remotedoc.docClass.getSchema()
129
130
  const labelfield = schema['x-simpleapp-config'].documentTitle as string
130
131
  const codefield = schema['x-simpleapp-config'].uniqueKey as string
132
+ const docname = props.setting.fieldsetting['x-foreignkey']
131
133
  $event('ViewRecord',{
132
134
  _id: modelValue.value?._id as string,
133
135
  eventId: crypto.randomUUID(),
134
- label: modelValue.value?.label as string,
136
+ label: (readonly ? modelValue.value?.label : `${t('new') + ' '+ t(docname) }`) as string,
135
137
  readonly:readonly,
136
- viewer : getDocument(props.setting.fieldsetting['x-foreignkey'])?.viewer,
137
- documentName: props.setting.fieldsetting['x-foreignkey'],
138
+ viewer : getDocument(docname)?.viewer,
139
+ documentName: docname,
138
140
 
139
141
  //after create, auto copy value into auto complete
140
142
  afterCreate:(data:any)=>{
@@ -57,7 +57,7 @@ const readonlyclass="simpleapp-value-readonly"
57
57
  let schema:any
58
58
 
59
59
  const fielddesc = computed(()=>{
60
- return props.description ?? schema.description ?? ''
60
+ return props.description ?? ''
61
61
  })
62
62
 
63
63
  if(props.setting.fieldsetting && props.setting.fieldsetting.type){
@@ -6,6 +6,7 @@
6
6
  </template>
7
7
  <script setup lang="ts" >
8
8
  // import type {SimpleAppFieldSetting} from '/types'
9
+ import * as jsonpath from 'jsonpath';
9
10
  import { SimpleAppClient } from '~/simpleapp/generate/clients/SimpleAppClient'
10
11
  import type { JSONSchema7,JSONSchema7Definition } from 'json-schema';
11
12
  import _ from 'lodash'
@@ -17,6 +18,9 @@ import _ from 'lodash'
17
18
  if(!props.document){
18
19
  throw "undefine SimpleAppForm property 'document'"
19
20
  }
21
+
22
+ const isreadonly = computed(()=> props.readonly ? props.readonly: props.document.isReadOnly())
23
+
20
24
  // const obj = {schema:props.schema,data: props.schema}
21
25
  const getField = (path:string)=>{
22
26
  // console.log("simpleform topath",path)
@@ -37,7 +41,7 @@ import _ from 'lodash'
37
41
  modelField: 'email',
38
42
  isrequired: getIsRequired(schema,path),
39
43
  errors: props.document.getErrors(),
40
- readonly: props.readonly
44
+ readonly: isreadonly.value
41
45
  } //as SimpleAppFieldSetting
42
46
  }
43
47
 
@@ -102,31 +106,18 @@ import _ from 'lodash'
102
106
  // let paths = path.replace('#/','').split('/')
103
107
  // return '/'+paths[1]
104
108
  }
105
- const getPathObject=(schema:JSONSchema7,path:string):JSONSchema7|undefined=>{
109
+ const getPathObject=(schema:JSONSchema7,path:string):JSONSchema7|undefined=>{
106
110
  // console.log("path",path)
107
- if(!path){
111
+ if(!path || !path.includes('#/properties')){
108
112
  console.error('unknown path')
109
113
  return undefined
110
114
  }
111
- try{
112
- let paths:string[] = path.replace('#/','').split('/')
113
- let tmp :JSONSchema7Definition= schema
114
- // console.log(path)
115
- for(let i=0;i<paths.length;i++){
116
-
117
- //silly code, but it seems require to avoid typescript complaint.
118
- //ultimately it is to obtain result as "tmp=tmp[path[i]]"
119
- const key1 = paths[i] as keyof JSONSchema7
120
- let jsonkey: keyof JSONSchema7 = key1
121
- let obj:JSONSchema7 = {} as JSONSchema7
122
- Object.assign(obj,tmp[jsonkey])
123
- tmp = {...obj}
124
- }
125
- // console.log('final ',path,tmp)
126
- return tmp
115
+ try{
116
+ const modifiedpath = path.replace('#','$').replaceAll('/','.')
117
+ const queryresult = jsonpath.query(schema,modifiedpath)[0]
118
+ return queryresult
127
119
  }catch(err:any){
128
- console.error(err.message)
129
-
120
+ console.error(err.message)
130
121
  }
131
122
  }
132
123
  </script>
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <div class="simpleapp-tool-bar flex flex-row text-left gap-4">
3
+ <div v-for="(menu,index) in menus" :key="index">
4
+ <div>
5
+ <Button v-if="showMenuButton(menu)" @click="emitEvent(menu,$event)">{{ menu.label }}</Button>
6
+ </div>
7
+
8
+ </div>
9
+ <div v-for="(menu,index) in getDocActions()" :key="index">
10
+ <div>
11
+ <Button @click="emitEvent(menu,$event)">{{ menu.label }}</Button>
12
+ </div>
13
+
14
+ </div>
15
+ <ConfirmPopup></ConfirmPopup>
16
+ </div>
17
+ </template>
18
+ <script setup lang="ts">
19
+
20
+ import { SimpleAppClient } from '~/simpleapp/generate/clients/SimpleAppClient';
21
+ import { useConfirm } from "primevue/useconfirm";
22
+ const confirm = useConfirm();
23
+ import {FormActions,FormMenu} from '~/types'
24
+
25
+ const ismodify = ref(false)
26
+ const props = defineProps<{
27
+ document: SimpleAppClient<any,any>,
28
+ disableaction? : string[]
29
+ }>()
30
+
31
+ const emits = defineEmits(['on'])
32
+ const doc = props.document
33
+ const data = doc.getReactiveData()
34
+
35
+ const menus = ref<FormMenu[]>([])
36
+
37
+
38
+ const getActions = () => {
39
+ const actions = doc.getActions()
40
+
41
+ // Object.keys(actions).forEach((key)=>{ //crud, api, docstatus
42
+ actions['crud'].forEach((item)=>{
43
+ if(props.disableaction && props.disableaction.includes(item)){/*skip this item*/}
44
+ else{
45
+ menus.value.push({
46
+ action: item,
47
+ label: t(item),
48
+ type: 'crud'
49
+ })
50
+ }
51
+ })
52
+ // })
53
+ return actions
54
+ }
55
+
56
+ getActions()
57
+
58
+ const emitEvent = (menu:FormMenu, clickEvent:any)=>{
59
+ if(menu.action=='delete'){
60
+ confirm.require({
61
+ target: clickEvent.currentTarget as HTMLElement,
62
+ message: `${t("delete")}?`,
63
+ icon: "pi pi-exclamation-triangle",
64
+ acceptClass: "p-button-danger",
65
+ accept: () => emits('on',menu.action)
66
+ });
67
+ }else{
68
+ emits('on',menu.action)
69
+ }
70
+
71
+ }
72
+
73
+
74
+ const getDocActions = () =>{
75
+ const docstatus:string = data.value.documentStatus
76
+ const allstatus = doc.getSchema()['x-simpleapp-config'].allStatus
77
+ let docactionmenus:FormMenu[] = []
78
+ type stringlist = {[key:string]:string}
79
+ const statusNames:stringlist = {}
80
+ allstatus?.forEach(item=>{
81
+ statusNames[item.status]=t(item.statusName)
82
+ })
83
+ if(allstatus){
84
+ const stateconfig = allstatus.find((item)=>item.status===docstatus)
85
+ docactionmenus = stateconfig?.actions.map(item=>({
86
+ action: item,
87
+ label: statusNames[item],
88
+ type: 'docstatus'
89
+ })) ?? []
90
+ }
91
+ return docactionmenus
92
+
93
+ }
94
+ const showMenuButton = (menu:FormMenu)=>{
95
+
96
+ if(!canPerform(doc.getDocName(),menu.action)) return false
97
+ if(doc.isNew() && menu.action == 'create') return true
98
+ if(!doc.isNew() && menu.action == 'new') return true
99
+ if(!doc.isNew() && menu.action == 'update') return true
100
+ if(!doc.isNew() && menu.action == 'delete') return true
101
+
102
+ if(menu.type == 'api' ) return false
103
+ return false
104
+ }
105
+
106
+ </script>