@simitgroup/simpleapp-generator 1.0.59 → 1.0.61

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 (97) hide show
  1. package/dist/buildinschemas/branch.d.ts.map +1 -1
  2. package/dist/buildinschemas/branch.js +1 -0
  3. package/dist/buildinschemas/branch.js.map +1 -1
  4. package/dist/buildinschemas/organization.d.ts.map +1 -1
  5. package/dist/buildinschemas/organization.js +1 -0
  6. package/dist/buildinschemas/organization.js.map +1 -1
  7. package/dist/buildinschemas/permission.js +1 -1
  8. package/dist/buildinschemas/permission.js.map +1 -1
  9. package/dist/buildinschemas/user.d.ts.map +1 -1
  10. package/dist/buildinschemas/user.js +4 -1
  11. package/dist/buildinschemas/user.js.map +1 -1
  12. package/dist/framework.js +1 -1
  13. package/dist/framework.js.map +1 -1
  14. package/dist/generate.js +11 -6
  15. package/dist/generate.js.map +1 -1
  16. package/dist/type.d.ts +1 -0
  17. package/dist/type.d.ts.map +1 -1
  18. package/docs/jsonschema.md +1 -0
  19. package/package.json +1 -1
  20. package/src/buildinschemas/branch.ts +1 -0
  21. package/src/buildinschemas/organization.ts +1 -0
  22. package/src/buildinschemas/permission.ts +1 -1
  23. package/src/buildinschemas/user.ts +4 -1
  24. package/src/framework.ts +1 -1
  25. package/src/generate.ts +15 -7
  26. package/src/type.ts +1 -0
  27. package/templates/basic/nest/controller.ts.eta +5 -3
  28. package/templates/basic/nest/model.ts.eta +32 -7
  29. package/templates/basic/nuxt/pages.[id].vue.eta +2 -2
  30. package/templates/basic/nuxt/pages.crud.vue.eta +79 -40
  31. package/templates/basic/nuxt/pages.landing.vue.eta +82 -0
  32. package/templates/nest/.env._eta +1 -0
  33. package/templates/nest/src/simpleapp/generate/commons/exceptions/SimpleAppExceptionFilter.ts.eta +1 -1
  34. package/templates/nest/src/simpleapp/generate/commons/roles/roles.group.ts.eta +1 -1
  35. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +127 -22
  36. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +1 -1
  37. package/templates/nest/src/simpleapp/profile/profile.controller.ts.eta +25 -2
  38. package/templates/nest/src/simpleapp/profile/profile.service.ts.eta +20 -2
  39. package/templates/nest/src/simpleapp/services/branch.service.ts.eta +41 -40
  40. package/templates/nest/src/simpleapp/services/user.service.ts.eta +10 -9
  41. package/templates/nuxt/app.vue.eta +2 -1
  42. package/templates/nuxt/assets/css/style.css._eta +3 -12
  43. package/templates/nuxt/assets/primevue/passthrough.ts._eta +24 -20
  44. package/templates/nuxt/components/ButtonCreateTenant.vue.eta +30 -23
  45. package/templates/nuxt/components/ButtonHome.vue.eta +15 -2
  46. package/templates/nuxt/components/ButtonMenuPicker.vue.eta +5 -20
  47. package/templates/nuxt/components/ButtonPermissionInfo.vue.eta +5 -7
  48. package/templates/nuxt/components/ButtonProfile.vue.eta +42 -25
  49. package/templates/nuxt/components/CrudSimple.vue.eta +1 -1
  50. package/templates/nuxt/components/EventDecision.vue.eta +115 -0
  51. package/templates/nuxt/components/EventNotification.vue.eta +157 -0
  52. package/templates/nuxt/components/HeaderBar.vue.eta +2 -2
  53. package/templates/nuxt/components/Invitation.vue.eta +3 -3
  54. package/templates/nuxt/components/SelectBranch.vue.eta +5 -2
  55. package/templates/nuxt/components/SimpleAppDatatable.vue.eta +151 -5
  56. package/templates/nuxt/components/TenantPicker.vue.eta +75 -0
  57. package/templates/nuxt/components/UserProfileListItem.vue.eta +65 -0
  58. package/templates/nuxt/components/renderers/BooleanRender.vue.eta +7 -0
  59. package/templates/nuxt/components/renderers/DateRender.vue.eta +6 -0
  60. package/templates/nuxt/components/renderers/ForeignKeyRender.vue.eta +10 -0
  61. package/templates/nuxt/components/renderers/MoneyRender.vue.eta +7 -0
  62. package/templates/nuxt/components/renderers/MultiTextRender.vue.eta +11 -0
  63. package/templates/nuxt/composables/getDocument.generate.ts.eta +4 -0
  64. package/templates/nuxt/composables/getMenus.generate.ts.eta +35 -31
  65. package/templates/nuxt/composables/getUserStore.generate.ts.eta +6 -1
  66. package/templates/nuxt/composables/goTo.generate.ts.eta +15 -0
  67. package/templates/nuxt/composables/notifications.generate.ts.eta +21 -0
  68. package/templates/nuxt/composables/stringHelper.generate.ts.eta +11 -1
  69. package/templates/nuxt/error.vue._eta +20 -5
  70. package/templates/nuxt/layouts/documentlist.vue.eta +166 -0
  71. package/templates/nuxt/middleware/30.acl.global.ts.eta +4 -1
  72. package/templates/nuxt/pages/[xorg]/organization/index.vue.eta +4 -4
  73. package/templates/nuxt/pages/[xorg]/profile.vue.eta +6 -0
  74. package/templates/nuxt/pages/[xorg]/user/[id].vue.eta +6 -0
  75. package/templates/nuxt/pages/[xorg]/user/index.vue.eta +211 -377
  76. package/templates/nuxt/pages/[xorg]/user.vue.eta +197 -0
  77. package/templates/nuxt/pages/index.vue._eta +101 -0
  78. package/templates/nuxt/pages/profile.vue.eta +94 -0
  79. package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +4 -4
  80. package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +11 -10
  81. package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +69 -17
  82. package/templates/nuxt/simpleapp/generate/commons/documents.ts.eta +6 -3
  83. package/templates/nuxt/tailwind.config.ts._eta +10 -0
  84. package/templates/nuxt/types/documentlist.ts.eta +9 -0
  85. package/templates/nuxt/types/events.ts.eta +20 -0
  86. package/templates/nuxt/types/index.ts.eta +6 -79
  87. package/templates/nuxt/types/notifications.ts.eta +16 -0
  88. package/templates/nuxt/types/others.ts.eta +42 -0
  89. package/templates/nuxt/types/user.ts.eta +44 -0
  90. package/tsconfig.tsbuildinfo +1 -1
  91. package/templates/nest/src/simpleapp/generate/models/perm.model.ts.eta +0 -52
  92. package/templates/nest/src/simpleapp/generate/models/tenant.model.ts.eta +0 -43
  93. package/templates/nest/src/simpleapp/generate/models/user.model.ts.eta +0 -56
  94. package/templates/nuxt/components/EventMonitor.vue.eta +0 -85
  95. package/templates/nuxt/pages/[xorg]/tenant/index.vue.eta +0 -89
  96. package/templates/nuxt/pages/index.vue.eta +0 -116
  97. package/templates/nuxt/simpleapp/generate/commons/events.ts.eta +0 -11
@@ -0,0 +1,157 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * This file was automatically generated by simpleapp generator during initialization.
4
+ * DO NOT MODIFY IT BY HAND.
5
+ * last change 2023-09-09
6
+ * author: Ks Tan
7
+ */
8
+ import {Notification, NotificationStatus} from '~/types'
9
+ import { useToast, } from 'primevue/usetoast';
10
+ import type { ToastMessageOptions } from 'primevue/toast';
11
+ import Toast from 'primevue/toast';
12
+ import { stringify } from 'ajv';
13
+ import {upperFirst, last } from 'lodash'
14
+
15
+
16
+ const toast = useToast();
17
+ const { $event,$listen } = useNuxtApp()
18
+ // WildcardHandler<Record<EventType, unknown>>
19
+ let resmsg:ToastMessageOptions = {} as ToastMessageOptions
20
+
21
+ $listen("Notification",(data:Notification)=>{
22
+ // $listen('*',(type:key of EventType,data:any)=>{
23
+ console.log("Event type",'data',data)
24
+ const type:NotificationStatus = data.status
25
+ // let arrupdate = type.split(':')
26
+ // let eventdata = data
27
+ let duration = 3000
28
+ // let severity:typeof resmsg['severity']
29
+ // let isshow=true
30
+ let toastgroup='default'
31
+ // let eventname = arrupdate[1]
32
+ // let eventtype = arrupdate[0]
33
+ // let title = ''
34
+ switch(type){
35
+ case NotificationStatus.success:
36
+ duration = 3000
37
+ break;
38
+ case NotificationStatus.danger:
39
+ duration = 0
40
+ break;
41
+ case NotificationStatus.warning:
42
+ duration = 5000
43
+ break;
44
+ case NotificationStatus.info:
45
+ duration = 3000
46
+ break;
47
+
48
+ }
49
+ // if(type=='error'){
50
+ // duration = 0
51
+ // severity='error'
52
+ // title=upperFirst(eventname) +' failed'
53
+ // }
54
+ // else if(type.indexOf('warn')>=0){
55
+ // duration = 10000
56
+ // severity='warn'
57
+ // title=upperFirst(eventname) +' with warning'
58
+ // }
59
+ // else if(type.indexOf('info')>=0){
60
+ // duration = 3000
61
+ // severity='info'
62
+ // isshow=false
63
+ // title='Info'
64
+ // }
65
+ // else if(type.indexOf('success')>=0){
66
+ // duration = 3000
67
+ // severity='success'
68
+ // title= upperFirst(eventname) +' successfully'
69
+ // }
70
+ // if(Array.isArray(data)){
71
+ // toastgroup='list'
72
+ // }else if(typeof data == 'object'){
73
+ // // title = camelCaseToWords(useCamelCase(title.replace(":","-")))
74
+ // eventdata = data.message
75
+ // }
76
+
77
+
78
+ // if(isshow && severity){
79
+ // console.log("isshow",isshow,"event:",severity,eventdata)
80
+ toast.removeAllGroups()
81
+ resmsg = { severity: type as typeof resmsg['severity'], summary: data.summary, detail :data.data, life: duration, group:toastgroup}
82
+ toast.add(resmsg)
83
+ // }
84
+ // isshow=false
85
+
86
+ })
87
+
88
+
89
+ const getFieldName = (path:string)=>{
90
+ const lastword :string= last<string>(path.split('/'))??''
91
+ return camelCaseToWords(lastword)
92
+ // const lastField = path.split
93
+ }
94
+ </script>
95
+ <template>
96
+ <Toast group="default" :pt="{}">
97
+ <template #message="p">
98
+ <div class="bg-gray-200 dark:bg-gray-900 h-full w-full border">
99
+ <div class=" flex content content-end w-full">
100
+ <div role="alert" class="w-full bg-white dark:bg-gray-800 shadow-lg rounded flex flex-row transition duration-150 ease-in-out" id="notification">
101
+ <div :class="'flex items-center justify-center sm:rounded-tl sm:rounded-bl h-12 sm:h-auto sm:w-auto text-white'+getStatusColor(p.message.severity)">
102
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="40" height="40" fill="currentColor">
103
+ <path class="heroicon-ui" d="M12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-2.3-8.7l1.3 1.29 3.3-3.3a1 1 0 0 1 1.4 1.42l-4 4a1 1 0 0 1-1.4 0l-2-2a1 1 0 0 1 1.4-1.42z" />
104
+ </svg>
105
+ </div>
106
+ <div class="flex flex-col justify-center pl-4 xl:pl-1 w-96 pt-4 sm:pb-4 pb-2">
107
+ <p class="text-lg text-gray-800 dark:text-gray-100 font-semibold pb-1">{{ p.message.summary }}</p>
108
+
109
+ <div v-if="p.message.detail">
110
+ <p v-if="Array.isArray(p.message.detail)"
111
+ v-for="item in p.message.detail"
112
+ class="text-sm text-gray-600 dark:text-gray-400 font-normal">
113
+ <span v-if="item.instancePath">{{ item.instancePath }}&nbsp;</span>
114
+ <span v-if="item.message"> {{ item.message }}&nbsp;</span>
115
+
116
+ </p>
117
+ <p v-else-if="typeof p.message.detail == 'string'" class="text-sm text-gray-600 dark:text-gray-400 font-normal">{{ p.message.detail }}</p>
118
+ <p v-else></p>
119
+ </div>
120
+ </div>
121
+ <div class="rounded m m-6"><button @click="toast.remove(p.message)" class="text-gray-800 hover:text-gray-300 "><i class="pi pi-times"></i></button></div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </template>
126
+ </Toast>
127
+ <!-- <Toast group="list">
128
+ <template #message="p">
129
+ <div class="bg-gray-200 dark:bg-gray-900 h-full">
130
+ <div class="relative mx-auto flex justify-center sm:justify-end pt-16 sm:pt-6 pb-6 sm:pb-16 h-64 overflow-x-hidden">
131
+ <div role="alert" class="sm:mr-6 mt-16 sm:mt-6 mb-6 sm:mb-0 xl:w-5/12 mx-auto absolute left-0 sm:left-auto right-0 sm:top-0 sm:w-6/12 md:w-3/5 justify-between w-11/12 bg-white dark:bg-gray-800 shadow-lg rounded flex sm:flex-row flex-col transition duration-150 ease-in-out" id="notification">
132
+ <div class="sm:px-6 p-2 flex mt-4 sm:mt-0 ml-4 sm:ml-0 items-center justify-center bg-green-400 sm:rounded-tl sm:rounded-bl w-12 h-12 sm:h-auto sm:w-auto text-white">
133
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="40" height="40" fill="currentColor">
134
+ <path class="heroicon-ui" d="M12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-2.3-8.7l1.3 1.29 3.3-3.3a1 1 0 0 1 1.4 1.42l-4 4a1 1 0 0 1-1.4 0l-2-2a1 1 0 0 1 1.4-1.42z" />
135
+ </svg>
136
+ </div>
137
+ <div class="flex flex-col justify-center xl:-ml-4 pl-4 xl:pl-1 sm:w-3/5 pt-4 sm:pb-4 pb-2">
138
+ <p class="text-lg text-gray-800 dark:text-gray-100 font-semibold pb-1">Action Completed</p>
139
+ <p class="text-sm text-gray-600 dark:text-gray-400 font-normal">You have successfully completed the survey. You will soon receive a reward email. Stay tuned.</p>
140
+ </div>
141
+ <div class="flex sm:flex-col sm:justify-center sm:border-l dark:border-gray-700 items-center border-gray-300 sm:w-1/6 pl-4 sm:pl-0">
142
+ <div class="sm:pt-4 pb-4 sm:border-b dark:border-gray-700 border-gray-300 sm:w-full flex sm:justify-center">
143
+ <span class="sm:text-sm text-xs text-green-400 font-bold mr-4 sm:mr-0 cursor-pointer">View</span>
144
+ </div>
145
+ <div class="sm:pt-4 pb-4 flex sm:justify-center w-full cursor-pointer" @click="toast.remove(p.message)">
146
+ <span class="sm:text-sm text-xs text-gray-600 dark:text-gray-400 cursor-pointer">Dismiss</span>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ <ol>
153
+ <li v-for="(item,index) in p.message.detail" :key="index"><b>{{getFieldName(item.instancePath)}}</b>: {{ item.message }}</li>
154
+ </ol>
155
+ </template>
156
+ </Toast> -->
157
+ </template>
@@ -8,7 +8,7 @@ import ButtonProfile from './ButtonProfile.vue';
8
8
  <template>
9
9
  <!-- <header> -->
10
10
  <!-- <MegaMenu :model="getMenus()" orientation="horizontal" /> -->
11
- <div class="bg-white border-b-2 border-b-gray-300 h-10 flex flex-row">
11
+ <div class="bg-white border-b-gray-300 h-10 flex flex-row">
12
12
 
13
13
 
14
14
  <div class="flex-1 flex flex-row gap-2 p-2">
@@ -17,7 +17,7 @@ import ButtonProfile from './ButtonProfile.vue';
17
17
 
18
18
  </div>
19
19
  <div class="">
20
- <ButtonHome @click="navigateTo(`/${getUserProfile().currentxorg}`)"/>
20
+ <ButtonHome/>
21
21
  </div>
22
22
 
23
23
  </div>
@@ -18,7 +18,7 @@
18
18
  <script setup lang="ts">
19
19
  import Dialog from 'primevue/dialog';
20
20
  import {onMounted, ref} from 'vue'
21
- import {SimpleAppEventType} from '../simpleapp/generate/commons/events'
21
+ import {EventType} from '../types'
22
22
  import ProgressSpinner from 'primevue/progressspinner'
23
23
  const {$userstore,$event}= useNuxtApp()
24
24
  const disabled = ref(false)
@@ -37,13 +37,13 @@
37
37
 
38
38
  // await reloadUserStore()
39
39
  await $userstore.decideInvitation(id,'accept')
40
- $event(SimpleAppEventType.InvitationAccepted,id)
40
+ $event('InvitationAccepted',id)
41
41
  visible.value=false
42
42
  }
43
43
  const decline = async (id:string) => {
44
44
  // await reloadUserStore()
45
45
  await $userstore.decideInvitation(id,'reject')
46
- $event(SimpleAppEventType.InvitationRejected,id)
46
+ $event('InvitationRejected',id)
47
47
  visible.value=false
48
48
  }
49
49
 
@@ -2,7 +2,7 @@
2
2
  import _ from 'lodash'
3
3
  import {ref} from 'vue'
4
4
 
5
- const selectedbranch = ref(getUserProfile().currentxorg)
5
+ const selectedbranch = ref(getCurrentXorg())
6
6
  const currentTenant=ref(getUserProfile().tenantId)
7
7
  console.log("currentTenant",currentTenant.value)
8
8
  type Org = {orgId:number, label:string,_id:string}
@@ -23,10 +23,13 @@ const getBranchesForOrg = (org:Org)=>{
23
23
  const switchXorg = ()=>{
24
24
  navigateTo(`/${selectedbranch.value}`,{external:true})
25
25
  }
26
+
27
+ console.log("selectedbranch",selectedbranch.value)
26
28
  </script>
27
29
  <template>
28
30
  <div>
29
- <select v-model="selectedbranch" class=" text-right" @change="switchXorg()">
31
+
32
+ <select v-if="selectedbranch" v-model="selectedbranch" class=" text-right" @change="switchXorg()">
30
33
  <optgroup v-for="o in orgList" :label="o.label">
31
34
  <option v-for="b in getBranchesForOrg(o)" :value="b['xOrg']">{{ b['branch']['branchCode']}} </option>
32
35
  </optgroup>
@@ -1,7 +1,36 @@
1
- <template>
2
- <DataTable v-bind="$attrs" class="simpleapp-datatable" stripedRows :value="valueModel">
1
+ <!-- <template>
2
+ <DataTable v-bind="$attrs"
3
+ stripedRows
4
+ dataKey="_id"
5
+ :pt="{
6
+ bodyRow:{class:'bg-blue-200'},
7
+ wrapper:{class:'bg-red-100'},
8
+
9
+ root:{class:'bg-green-100'},
10
+ header:{class:'bg-blue-600'},
11
+ pagebutton:{class:'bg-red-800'},
12
+
13
+ tfoot:{class:'bg-red-800 boder round m-4 gap-4'},
14
+ }"
15
+
16
+ :value="valueModel"
17
+ :filters="filters"
18
+ :paginator="true" :rows="20"
19
+ :rowsPerPageOptions="[20,40,60,100]"
20
+ filterDisplay="row"
21
+ >
22
+
3
23
  <template #empty> <div class="text-center">No record found.</div> </template>
4
- <template #header><slot name="header"></slot></template>
24
+ <template #header><slot name="header">
25
+ <div class="flex flex-wrap gap-2 align-items-center justify-content-between">
26
+ <h4 class="m-0 flex-1">Records</h4>
27
+ <span class="p-input-icon-left">
28
+ <i class="pi pi-search" />
29
+ <InputText v-model="filters['global'].value" placeholder="Search..." />
30
+ </span>
31
+ </div>
32
+
33
+ </slot></template>
5
34
  <Column v-for="(col,index) in columns" :field="col" :header="camelCaseToWords(col)"></Column>
6
35
  <slot >
7
36
 
@@ -9,11 +38,128 @@
9
38
  </DataTable>
10
39
  </template>
11
40
  <script setup lang="ts">
41
+ import { FilterMatchMode } from 'primevue/api';
12
42
  import DataTable from 'primevue/datatable';
13
43
  import Column from 'primevue/column';
14
44
  import {camelCaseToWords} from './helper'
15
- const props = defineProps<{columns:string[]}>()
45
+ const props = defineProps<{columns:string[]|string[][]}>()
16
46
  const valueModel = defineModel<Object[]>()
47
+ const filters = ref({
48
+ 'global': {value: null, matchMode: FilterMatchMode.CONTAINS},
49
+ });
50
+
51
+ </script> -->
52
+ <template>
53
+ <DataTable v-bind="$attrs"
54
+ stripedRows
55
+ dataKey="_id"
56
+ :showGridlines="true"
57
+ :pt="{
58
+ // header:{ class:'border bg-gray-100'},
59
+ headerRow:{ class:'border bg-gray-200'},
60
+ }"
61
+ :value="value"
62
+ :filters="filters"
63
+ :paginator="true" :rows="20"
64
+ :rowsPerPageOptions="[20,40,60,100]"
65
+ >
66
+ <!-- header -->
67
+ <template #header>
68
+ <div class=" flex flex-row w-full text-right justify-content-end">
69
+ <div class="flex-1">
70
+ <slot name="toolbar"></slot>
71
+ </div>
72
+ <div class="flex-1">
73
+ <slot name="title">
74
+ <h1 class="text text-2xl pt-2 text-center">{{ title }}</h1>
75
+ </slot>
76
+
77
+ </div>
78
+ <span class="p-input-icon-left flex-1">
79
+ <i class="pi pi-search mt-4 ml-2 text-gray-400 absolute" />
80
+ <InputText type ="search" v-model="filters['global'].value" class="text-right" placeholder="Keyword Search" />
81
+ </span>
82
+ </div>
83
+ </template>
84
+ <!-- no data found -->
85
+ <template #empty>
86
+ <div class="text-center ">
87
+ <div class="text-3xl text-gray-400 pi pi-exclamation-circle"></div>
88
+ <div class="text-3xl text-gray-400">no data found</div>
89
+
90
+ </div>
91
+ </template>
92
+
93
+ <Column v-for="(col,index) in columns" :field="typeof col == 'string' ? col : col.field">
94
+ <template v-if="typeof col == 'string'" #header>
95
+ <p >{{ col }}</p>
96
+ </template>
97
+ <template v-else-if="typeof col =='object'" #header>
98
+ <span>{{ col.title }} </span>
99
+ </template>
100
+ <template v-else #header>
101
+ <span class="text-danger-600">unknown</span>
102
+ </template>
103
+
104
+ <template v-if="typeof col == 'string'" #body="{index,data}">
105
+ <p >{{ data[col] }}</p>
106
+ </template>
107
+ <template v-else-if="typeof col =='object'" #body="{index,data}">
108
+ <div v-if="col.component && col.field=='*'">
109
+ <component :is="col.component" :value="data" :fields="col.moreFields" :class="col.cssClass" :setting="col.componentSetting"></component>
110
+ </div>
111
+ <div v-else-if="col.component && col.field!='*'">
112
+ <component :is="col.component" v-model="data[col.field]" :moreFields="col.moreFields" :class="col.cssClass" :setting="col.componentSetting" ></component>
113
+ </div>
114
+ <div v-else class="flex flex-col">
115
+ <div :class="col.class">{{ data[col.field] }}</div>
116
+ <div v-for="(f,findex) in col.moreFields" class="flex flex-col">
117
+ <!-- additional field define as string -->
118
+ <div v-if="typeof f == 'string'" :class="'text-gray-400 text-sm' + f.cssClass??'' ">{{ data[f] }}</div>
119
+ <!-- additional field define as object -->
120
+ <div v-else>
121
+ <div v-if="f.component && f.field=='*'">
122
+ <component :is="f.component" :value="data" :class="f.cssClass" :setting="f.componentSetting"></component>
123
+ </div>
124
+ <div v-else-if="f.component && f.field!='*'">
125
+ <component :is="f.component" v-model="data[f.field]" :class="f.cssClass" :setting="f.componentSetting" ></component>
126
+ </div>
127
+ <div v-else>
128
+ <div :class="f.cssClass">{{ data[f.field] }}</div>
129
+ </div>
130
+ </div>
131
+
132
+
133
+ </div>
134
+ </div>
135
+ </template>
136
+ <template v-else #body>
137
+ <span class="text-danger-600">unknown col {{ col }}</span>
138
+ </template>
139
+ </Column>
140
+ <slot>
141
+
142
+ </slot>
143
+ </Datatable>
144
+ </template>
145
+ <script setup lang="ts">
146
+ import {SearchBody,CellSetting} from '~/types'
147
+ import { FilterMatchMode } from 'primevue/api';
148
+ import DataTable from 'primevue/datatable';
149
+ import Column from 'primevue/column';
150
+
151
+ const props = defineProps<{
152
+ value:any[]
153
+ columns: CellSetting[]
154
+ title:string
155
+
156
+
157
+ }>()
158
+
159
+ const filters = ref({
160
+ 'global': {value: null, matchMode: FilterMatchMode.CONTAINS},
161
+ });
162
+
17
163
 
164
+ </script>
18
165
 
19
- </script>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <card>
3
+ <template #header>
4
+ <h1 class="font font-bold text text-gray-700">{{ tenant.tenantName }}</h1>
5
+ </template>
6
+ <template #content>
7
+
8
+ <ul >
9
+ <li v-for="o in orglist">
10
+ <h1>{{ o.orgName }}</h1>
11
+ <ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200">
12
+ <li v-for="b in branchlist" class="flex items-center justify-between py-4 pl-4 pr-5 text-sm leading-6">
13
+ <NuxtLink :href="`/${b.xOrg }`" v-if="o.orgId==b.orgId" :external="true" class="hover:bg-primary-100">
14
+ <div class="flex w-full flex-1 items-center ">
15
+ <!-- <i class="pi pi-sitemap"></i> -->
16
+ <p class="flex-1 flex-shrink-0 font-bold text-primary-600 ">
17
+ {{ b.branchCode }}
18
+ </p>
19
+ <p class=" text-right min-w-0 flex-1 gap-2">
20
+ <span href="#" class="font-medium text-gray-600 ">{{ b.group }}</span>
21
+ </p>
22
+ </div>
23
+ <div class=" flex-shrink-0">
24
+
25
+ <span class=" font-medium text-gray-400">{{useRuntimeConfig().public.APP_URL}}/{{b.xOrg }}</span>
26
+
27
+ </div>
28
+
29
+ </NuxtLink>
30
+ </li>
31
+ </ul>
32
+
33
+ </li>
34
+ </ul>
35
+
36
+ <!-- <ul>
37
+ <li v-for="b in branches" class="border border-b-1">
38
+ <NuxtLink :href="`/${b.xOrg }`" :external="true">
39
+
40
+ <div class="flex flex-col">
41
+ <div class="flex">
42
+ <p class="flex-1">{{b.branch.branchCode}}</p>
43
+ <p class="flex-1 text-right">{{b.group }}</p>
44
+ </div>
45
+
46
+
47
+ <p class="text-gray-500">{{useRuntimeConfig().public.APP_URL}}/{{b.xOrg }}</p>
48
+
49
+ </div>
50
+
51
+ </NuxtLink>
52
+ </li>
53
+ </ul> -->
54
+ </template>
55
+ </card>
56
+ </template>
57
+
58
+ <script setup lang="ts">
59
+ import _ from 'lodash'
60
+ const props = defineProps<{
61
+ tenant:any
62
+ }>()
63
+ const branchlist = computed(()=>props.tenant.permissions)
64
+
65
+
66
+ const orglist = computed(()=>_.uniqBy(branchlist.value,'orgId')
67
+ .map((item:any)=>({ orgId:item.orgId,orgCode:item.orgCode,orgName:item.orgName}) ) )
68
+
69
+
70
+
71
+ onMounted(()=>{
72
+
73
+ })
74
+
75
+ </script>
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <!-- <ul role="list" class="divide-y divide-gray-100">
3
+ <li v-for="person in people" :key="person.email" class="flex justify-between gap-x-6 py-5"> -->
4
+ <div class="flex justify-between gap-x-6 py-5 cursor-pointer">
5
+ <div class="flex min-w-0 gap-x-4">
6
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" :src="`${getAvatarLink(data.email,32)}`" alt="" />
7
+ <div class="min-w-0 flex-auto">
8
+ <p class="text-sm font-semibold leading-6 text-gray-900">{{ data.fullname }}</p>
9
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">{{ data.email }}</p>
10
+ </div>
11
+ </div>
12
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
13
+
14
+ <div v-if="isOnline" class="mt-1 flex items-center gap-x-1.5">
15
+ <div class="flex-none rounded-full bg-emerald-500/20 p-1">
16
+ <div class="h-1.5 w-1.5 rounded-full bg-emerald-500" />
17
+ </div>
18
+ <p class="text-xs leading-5 text-gray-500">Online</p>
19
+ </div>
20
+ <p v-else class="mt-1 text-xs leading-5 text-gray-500">
21
+ <span v-if="datedifferent.get('years')>0">{{ datedifferent.get('years') }} year(s)</span>
22
+ <span v-if="datedifferent.get('months')>0">{{ datedifferent.get('months') }} month(s)</span>
23
+ <span v-if="datedifferent.get('days')>0">{{ datedifferent.get('days') }} day(s)</span>
24
+ <span v-if="datedifferent.get('hours')>0">{{ datedifferent.get('hours') }} Hour(s)</span>
25
+ <span v-if="datedifferent.get('minutes')>0">{{ datedifferent.get('minutes') }} min(s)</span>
26
+ </p>
27
+ <p class="text-sm text-right leading-6 text-gray-400 w-[120px] truncate ...">
28
+ {{ data.description }}
29
+ </p>
30
+ </div>
31
+ </div>
32
+
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import {computed} from 'vue'
37
+ import moment from 'moment'
38
+ const props = defineProps<{
39
+ data:any
40
+ }>()
41
+
42
+
43
+
44
+ const datedifferent = computed(()=>{
45
+ const data = moment.duration(differentTime.value)
46
+ return data
47
+ })
48
+ const differentTime = computed(()=>{
49
+ const lasttime = new Date(props.data.lastActivity).getTime()
50
+ const current = new Date().getTime()
51
+ const result = current - lasttime
52
+ return result
53
+ })
54
+ const isOnline = computed(()=>{
55
+ if(props.data.lastActivity) {
56
+ const diffminutes = differentTime.value/ 1000/60
57
+ return diffminutes < 1
58
+ }else{
59
+ return false
60
+ }
61
+
62
+ })
63
+
64
+
65
+ </script>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <i class="pi" :class="{ 'pi-check-circle text-green-500 ': modelValue, 'pi-times-circle text-red-500': !modelValue }"></i>
3
+ </template>
4
+ <script lang="ts" setup>
5
+
6
+ const modelValue = defineModel()
7
+ </script>
@@ -0,0 +1,6 @@
1
+ <template >
2
+ <span>{{ toLocalDate(modelValue) }}</span>
3
+ </template>
4
+ <script lang="ts" setup>
5
+ const modelValue = defineModel()
6
+ </script>
@@ -0,0 +1,10 @@
1
+ <template>
2
+ <NuxtLink :to="getDocumentUrl(setting.collection,modelValue ? modelValue['_id']:'')" class="text-primary-700 hover:text-primary-500">
3
+ {{modelValue?.label}}
4
+ </NuxtLink>
5
+ </template>
6
+ <script setup lang="ts">
7
+ const modelValue = defineModel()
8
+
9
+ const props = defineProps<{setting:any}>()
10
+ </script>
@@ -0,0 +1,7 @@
1
+
2
+ <template>
3
+ <span>{{ modelValue }}</span>
4
+ </template>
5
+ <script lang="ts" setup>
6
+ const props = defineProps(['modelValue'])
7
+ </script>
@@ -0,0 +1,11 @@
1
+
2
+ <template>
3
+ <p v-for="(f,index) in fields" >
4
+ <span v-if="index==0">{{ value[f] }}</span>
5
+ <span v-else class="text-gray-400 text-sm">{{ value[f] }}</span>
6
+ </p>
7
+ </template>
8
+ <script lang="ts" setup>
9
+ const props = defineProps(['value','fields'])
10
+
11
+ </script>
@@ -0,0 +1,4 @@
1
+ import {getAllDocuments} from '~/simpleapp/generate/commons/documents'
2
+ export const getDocument = (docname:string) =>{
3
+ return getAllDocuments().find((item)=>item.docName==docname)?.docClass
4
+ }