@simitgroup/simpleapp-generator 1.2.7 → 1.3.0-alpha

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 (106) hide show
  1. package/dist/buildinschemas/organization.d.ts.map +1 -1
  2. package/dist/buildinschemas/organization.js +3 -1
  3. package/dist/buildinschemas/organization.js.map +1 -1
  4. package/dist/framework.d.ts.map +1 -1
  5. package/dist/framework.js +4 -9
  6. package/dist/framework.js.map +1 -1
  7. package/dist/generate.js +14 -14
  8. package/dist/generate.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/buildinschemas/organization.ts +3 -1
  11. package/src/framework.ts +5 -9
  12. package/src/generate.ts +17 -16
  13. package/templates/basic/nest/controller.ts.eta +17 -0
  14. package/templates/basic/nest/default.ts.eta +6 -2
  15. package/templates/basic/nuxt/default.ts.eta +10 -6
  16. package/templates/basic/nuxt/pages.[id].vue.eta +9 -7
  17. package/templates/basic/nuxt/pages.form.vue.eta +16 -36
  18. package/templates/basic/nuxt/pages.landing.vue.eta +42 -35
  19. package/templates/basic/nuxt/pages.viewer.vue.eta +4 -6
  20. package/templates/nest/.env._eta +0 -1
  21. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +14 -2
  22. package/templates/nest/src/simpleapp/generate/controllers/simpleapp.controller.ts.eta +32 -18
  23. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +76 -13
  24. package/templates/nuxt/app.vue.eta +7 -3
  25. package/templates/nuxt/assets/css/calendar.css._eta +21 -8
  26. package/templates/nuxt/assets/css/style.css._eta +17 -10
  27. package/templates/nuxt/assets/primevue/passthrough.ts._eta +44 -22
  28. package/templates/nuxt/components/button/ButtonDefault.vue._eta +1 -2
  29. package/templates/nuxt/components/button/ButtonMultiple.vue._eta +21 -0
  30. package/templates/nuxt/components/calendar/CalendarByResource.vue.eta +29 -8
  31. package/templates/nuxt/components/debug/DebugDocumentData.vue.eta +3 -4
  32. package/templates/nuxt/components/event/EventDocumentViewer.vue._eta +75 -65
  33. package/templates/nuxt/components/event/EventNotification.vue._eta +23 -2
  34. package/templates/nuxt/components/form/FormBranch.vue.eta +83 -0
  35. package/templates/nuxt/components/form/readme.md.eta +1 -0
  36. package/templates/nuxt/components/header/HeaderBar.vue._eta +9 -9
  37. package/templates/nuxt/components/header/HeaderBreadcrumb.vue.eta +2 -2
  38. package/templates/nuxt/components/header/button/HeaderButtonMenuPicker.vue._eta +16 -22
  39. package/templates/nuxt/components/header/button/HeaderButtonProfile.vue.eta +29 -46
  40. package/templates/nuxt/components/list/ListDocument.vue.eta +25 -0
  41. package/templates/nuxt/components/list/ListDocumentTable.vue.eta +77 -0
  42. package/templates/nuxt/components/list/ListView.vue.eta +129 -58
  43. package/templates/nuxt/components/mobile/MobileToolbar.vue.eta +10 -0
  44. package/templates/nuxt/components/overlay/OverlaySideBarCrud.vue.eta +21 -0
  45. package/templates/nuxt/components/overlay/OverlayViewer.vue.eta +20 -0
  46. package/templates/nuxt/components/page/PageDocList.vue.eta +170 -0
  47. package/templates/nuxt/components/renderer/RendererBoolean.vue.eta +8 -4
  48. package/templates/nuxt/components/renderer/RendererDate.vue.eta +4 -4
  49. package/templates/nuxt/components/renderer/RendererLink.vue.eta +12 -20
  50. package/templates/nuxt/components/renderer/RendererMoney.vue.eta +10 -2
  51. package/templates/nuxt/components/renderer/RendererViewer.vue.eta +27 -21
  52. package/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta +202 -150
  53. package/templates/nuxt/components/simpleApp/SimpleAppDocumentNo.vue.eta +7 -1
  54. package/templates/nuxt/components/simpleApp/SimpleAppFieldContainer.vue.eta +112 -101
  55. package/templates/nuxt/components/simpleApp/SimpleAppFormToolBar.vue.eta +183 -91
  56. package/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +383 -246
  57. package/templates/nuxt/components/simpleApp/SimpleAppInputTable.vue.eta +4 -3
  58. package/templates/nuxt/components/table/TableDocuments.vue.eta +3 -3
  59. package/templates/nuxt/components/text/TextDanger.vue._eta +5 -0
  60. package/templates/nuxt/components/text/TextSubtitle.vue._eta +5 -0
  61. package/templates/nuxt/components/text/TextTitle.vue._eta +5 -0
  62. package/templates/nuxt/components/user/UserProfileListItem.vue.eta +2 -2
  63. package/templates/nuxt/composables/date.generate.ts.eta +21 -0
  64. package/templates/nuxt/composables/goTo.generate.ts.eta +1 -0
  65. package/templates/nuxt/composables/stringHelper.generate.ts.eta +0 -5
  66. package/templates/nuxt/error.vue._eta +3 -3
  67. package/templates/nuxt/i18n.config.ts.eta +1 -1
  68. package/templates/nuxt/lang/en.ts.eta +16 -0
  69. package/templates/nuxt/layouts/default.vue._eta +34 -34
  70. package/templates/nuxt/layouts/loginlayout.vue._eta +3 -0
  71. package/templates/nuxt/layouts/mobile.vue._eta +52 -0
  72. package/templates/nuxt/nuxt.config.ts._eta +28 -22
  73. package/templates/nuxt/pages/[xorg]/organization/[id].vue.eta +39 -0
  74. package/templates/nuxt/pages/[xorg]/organization/new.vue.eta +30 -143
  75. package/templates/nuxt/pages/[xorg]/organization/viewer.vue.eta +3 -0
  76. package/templates/nuxt/pages/[xorg]/organization.vue.eta +81 -127
  77. package/templates/nuxt/pages/[xorg]/user/[id].vue.eta +40 -0
  78. package/templates/nuxt/pages/[xorg]/user/form.vue.eta +329 -0
  79. package/templates/nuxt/pages/[xorg]/user/index.vue.eta +37 -0
  80. package/templates/nuxt/pages/[xorg]/{organization/[id]/branches/[bid].vue → user/new.vue.eta} +10 -4
  81. package/templates/nuxt/pages/[xorg]/user/viewer.vue.eta +30 -0
  82. package/templates/nuxt/pages/[xorg]/{user.vue._eta → user.vue.eta} +88 -78
  83. package/templates/nuxt/pages/login.vue._eta +34 -0
  84. package/templates/nuxt/pages/profile.vue.eta +12 -3
  85. package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +36 -35
  86. package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +6 -0
  87. package/templates/nuxt/server/api/[xorg]/[...].ts.eta +7 -40
  88. package/templates/nuxt/server/api/profile/[...].ts.eta +3 -32
  89. package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +42 -3
  90. package/templates/nuxt/types/calendar.ts.eta +8 -1
  91. package/templates/nuxt/types/documentlist.ts.eta +0 -9
  92. package/templates/nuxt/types/events.ts.eta +1 -0
  93. package/templates/nuxt/types/others.ts.eta +4 -1
  94. package/templates/nuxt/types/simpleappinput.ts.eta +12 -2
  95. package/templates/nuxt/types/user.ts.eta +2 -0
  96. package/templates/project/lang/default._json +9 -1
  97. package/tsconfig.tsbuildinfo +1 -1
  98. package/templates/nuxt/components/docPage/DocPageList.vue.eta +0 -125
  99. package/templates/nuxt/lang/en.ts._eta +0 -6
  100. package/templates/nuxt/pages/[xorg]/organization/[bid].vue.eta +0 -14
  101. package/templates/nuxt/pages/[xorg]/organization/[id]/branches/new.vue +0 -149
  102. package/templates/nuxt/pages/[xorg]/organization/[id]/index.vue.eta +0 -1
  103. package/templates/nuxt/pages/[xorg]/user/[id].vue._eta +0 -6
  104. package/templates/nuxt/pages/[xorg]/user/index.vue._eta +0 -302
  105. package/templates/nuxt/pages/login.vue.eta +0 -30
  106. /package/templates/nuxt/lang/{df.ts.eta → df.ts.etaxxxx} +0 -0
@@ -6,14 +6,13 @@ import {ref} from 'vue'
6
6
 
7
7
 
8
8
  const { locale,setLocale,locales } = useI18n()
9
+ const colorMode = useColorMode()
10
+ const modelValue = defineModel<boolean>()
9
11
  const mylocale = ref(locale)
10
- const userprofileoverlay = ref();
11
- const colors = [{value:'light', name:'Light'},{value:'dark', name:'Dark'}]
12
- const toggle = (event:any) => {
13
- userprofileoverlay.value.toggle(event);
14
- }
12
+ const colors = [{value:'light', name:t('light')},{value:'dark', name:t('dark')}]
13
+
15
14
  const toProfile = ()=>{
16
- userprofileoverlay.value.toggle();
15
+ modelValue.value=false
17
16
 
18
17
  if(getCurrentXorg()){
19
18
  navigateTo(`/${getCurrentXorg()}/profile`)
@@ -22,10 +21,6 @@ const toProfile = ()=>{
22
21
  }
23
22
 
24
23
  }
25
- const toFrontpage = () =>{
26
- userprofileoverlay.value.toggle();
27
- navigateTo('/')
28
- }
29
24
  const saveLocale=async (v:string)=>{
30
25
  await setLocale(v)
31
26
  }
@@ -34,50 +29,40 @@ const saveLocale=async (v:string)=>{
34
29
 
35
30
  </script>
36
31
 
37
- <template>
38
- <div>
39
- <div class=" w-[120px] truncate ...">
40
-
41
- <div class="flex flex-row-reverse cursor-pointer border-none w-full" @click="toggle">
42
- <p>{{ getProfileFullName() }}</p>
43
- <!-- <Avatar :image="getUserStore().getAvatarLink(32)" class="" size="normal" shape="circle" /> -->
44
- <div class="w-8 h-8 bg-cover bg-center rounded-md">
45
- <img :src="getAvatarLink(getProfileEmail(),32)" alt="" class="h-full w-full overflow-hidden object-cover rounded-full border-2 border-white dark:border-gray-700 shadow" />
46
- </div>
47
- </div>
48
-
49
- </div>
50
- <OverlayPanel :pt="{root:{class:'w w-80'}}" ref="userprofileoverlay">
51
- <div>
52
- <div class="py-2 transition duration-150 ease-in-out z-10 absolute top-0 right-0 bottom-0 left-0" id="modal">
53
- <div role="alert" class="container mx-auto w-full md:w-full max-w-lg">
54
- <div class="relative p-4 md:p-8 bg-white dark:bg-gray-800 shadow-md rounded border border-gray-400">
32
+ <template>
33
+ <!-- <div class="bg-cover bg-center rounded-md p-3 cursor-pointer profile-button" @click="toggle">
34
+ <i class="pi pi-user dark:text-white"></i> -->
35
+ <!-- <img :src="getAvatarLink(<string>getProfileEmail(),32)" alt="" class="h-full w-full overflow-hidden object-cover rounded-full border-2 border-white dark:border-gray-700 shadow" /> -->
36
+ <!-- </div> -->
37
+
38
+ <Dialog appendTo="body" ref="userprofileoverlay" v-model:visible="modelValue" close-on-escape :header="t('profile')">
39
+ <!-- <div> -->
40
+ <!-- <div class="py-2 transition duration-150 ease-in-out z-10 absolute top-0 right-0 bottom-0 left-0" id="modal"> -->
41
+ <div class="p-4">
42
+ <!-- <div class="relative p-4 md:p-8 bg-white dark:bg-gray-800 shadow-md rounded border border-gray-400"> -->
55
43
  <div class="w-full flex items-center justify-start text-gray-600 dark:text-gray-400 mb-5" >
56
44
  <div class="w-12 h-12 bg-cover bg-center rounded-md">
57
- <img :src="getAvatarLink(getProfileEmail(),32)" alt="" class="h-full w-full overflow-hidden object-cover rounded-full border-2 border-white dark:border-gray-700 shadow" />
45
+ <img :src="getAvatarLink(<string>getProfileEmail(),32)" alt="" class="h-full w-full overflow-hidden object-cover rounded-full border-2 border-white dark:border-gray-700 shadow" />
58
46
  </div>
59
47
  <div class="flex flex-col" >
60
- <h1 class="text-left text-gray-800 dark:text-gray-100 font-lg font-bold tracking-normal leading-tight ml-2">{{ getUserProfile().fullName }}</h1>
61
- <p class="text-gray-400 dark:text-gray-100 font-normal text-base tracking-normal ml-2 mr-4">{{getUserProfile().group }}</p>
48
+ <h1 class="text-left text-gray-800 dark:text-gray-100 font-lg font-bold tracking-normal leading-tight ml-2">{{ getUserProfile()?.fullName }}</h1>
49
+ <p class="text-gray-400 dark:text-gray-100 font-normal text-base tracking-normal ml-2 mr-4">{{getUserProfile()?.group }}</p>
62
50
  </div>
63
51
 
64
52
  </div>
65
- <!-- language -->
66
53
  <div class="col-span-full">
67
54
  <label for="pick-lang" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-300">{{ t('language') }}</label>
68
55
  <div class="mt-2">
69
56
  <Dropdown inputId="pick-lang" @update:model-value="saveLocale" v-model="mylocale" option-value="code" option-label="name" :options="locales" >
70
- </Dropdown>
71
- <!-- <select v-model="$colorMode.preference" id="picklang">
72
- <option value="light">Light</option>
73
- <option value="dark">Dark</option>
74
- </select> -->
57
+ </Dropdown>
75
58
  </div>
76
59
  </div>
77
60
  <div class="mt-3 col-span-full">
78
61
  <label for="pick-theme" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-300">{{ t('theme') }}</label>
79
62
  <div class="mt-2">
80
- <Dropdown inputId="pick-theme" v-model="$colorMode.preference" option-value="value" option-label="name" :options="colors" >
63
+ <h1>Color mode: {{ $colorMode.value }}</h1>
64
+
65
+ <Dropdown inputId="pick-theme" v-model="colorMode.preference" option-value="value" option-label="name" :options="colors" >
81
66
  </Dropdown>
82
67
 
83
68
  </div>
@@ -86,15 +71,13 @@ const saveLocale=async (v:string)=>{
86
71
 
87
72
  <div class="mt-3 flex items-center justify-start w-full">
88
73
  <button class="focus:outline-none transition duration-150 ease-in-out hover:bg-gray-600 bg-gray-700 rounded text-white px-8 py-2 text-sm" @click="toProfile">{{ t('profile') }}</button>
89
- <!-- <button class="focus:outline-none transition duration-150 ease-in-out bg-gray-600 rounded text-white px-8 py-2 text-sm" @click="toFrontpage">Pick Tenant</button> -->
90
- <button class="focus:outline-none ml-3 bg-warning-100 dark:bg-warning-700 dark:border-warning-700 dark:hover:bg-warning-600 transition duration-150 text-gray-600 dark:text-gray-400 ease-in-out hover:border-gray-400 hover:bg-gray-300 border rounded px-8 py-2 text-sm" @click="logout()">{{ t('logout') }}</button>
74
+ <button class="focus:outline-none ml-3 bg-warning-100 dark:bg-warning-700 dark:border-warning-700 dark:hover:bg-warning-600 transition duration-150 text-gray-600 dark:text-white ease-in-out hover:border-gray-400 hover:bg-gray-300 border rounded px-8 py-2 text-sm" @click="logout()">{{ t('logout') }}</button>
91
75
  </div>
92
- </div>
76
+ <!-- </div> -->
93
77
  </div>
94
- </div>
78
+ <!-- </div> -->
95
79
 
96
- </div>
97
-
98
- </OverlayPanel>
99
- </div>
80
+ <!-- </div> -->
81
+ </Dialog>
82
+
100
83
  </template>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <ListView
3
+ :list="recordlist as T[]"
4
+ with-filter
5
+ :title-field="documentTitle"
6
+ :sub-title-field="uniqueKey"
7
+ #default="{ item, index }"
8
+ >
9
+ <div class="cursor-pointer flex flex-row gap-2" @click="emits('click', item)">
10
+ <slot name="default" :index="index" :item="item">
11
+ <TextTitle class="flex-1">{{ item[(documentTitle as keyof typeof item) ] }}</TextTitle>
12
+ <TextSubtitle >{{ item[uniqueKey as keyof typeof item] }}</TextSubtitle>
13
+ </slot>
14
+ </div>
15
+ </ListView>
16
+ </template>
17
+ <script setup lang="ts" generic="T extends { [key: string]: any }">
18
+ import { SearchBody } from "~/types";
19
+ const emits = defineEmits(["click"]);
20
+ const props = defineProps<{
21
+ recordlist: T[];
22
+ uniqueKey?: string;
23
+ documentTitle?: string;
24
+ }>();
25
+ </script>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <DataTable
3
+ v-bind="$attrs"
4
+ stripedRows
5
+ dataKey="_id"
6
+ :showGridlines="true"
7
+ size="small"
8
+ :pt="{
9
+ header: { class: '' },
10
+ headerRow: { class: ' bg-gray-200' },
11
+ }"
12
+ v-on:row-click="clickRow"
13
+ :value="value"
14
+ :filters="filters"
15
+ :paginator="true"
16
+ :rows="20"
17
+ :rowsPerPageOptions="[20, 40, 60, 100]"
18
+ >
19
+ <!-- header -->
20
+ <template #header>
21
+ <div class="flex flex-row w-full text-right justify-content-end">
22
+ <div class="flex-1">
23
+ <slot name="toolbar"></slot>
24
+ </div>
25
+ <div class="flex-1">
26
+ <slot name="title">
27
+ <h1 class="text text-2xl pt-2 text-center dark:text-white">
28
+ {{ t(title) }}
29
+ </h1>
30
+ </slot>
31
+ </div>
32
+ <span class="p-input-icon-left flex-1">
33
+ <i class="pi pi-search mt-4 ml-2 text-gray-400 absolute" />
34
+ <InputText
35
+ type="search"
36
+ v-model="filters['global'].value"
37
+ class="text-right"
38
+ placeholder="Keyword Search"
39
+ />
40
+ </span>
41
+ </div>
42
+ </template>
43
+ <!-- no data found -->
44
+ <template #empty>
45
+ <div class="text-center">
46
+ <div class="text-3xl text-gray-400 pi pi-exclamation-circle"></div>
47
+ <div class="text-3xl text-gray-400">{{ t("noDataFound") }}</div>
48
+ </div>
49
+ </template>
50
+ <slot name="default">
51
+ <Column header="undefineColumns"></Column>
52
+ </slot>
53
+ </DataTable>
54
+ </template>
55
+ <script setup lang="ts" generic="T">
56
+ // import { CellSetting } from "~/types";
57
+ import { FilterMatchMode } from "primevue/api";
58
+ import DataTable from "primevue/datatable";
59
+ import Column from "primevue/column";
60
+ import renderComponent from "~/components/renderer";
61
+ import { emit } from "process";
62
+ const props = defineProps<{
63
+ value: T[]
64
+ title: string
65
+ columns: string[]
66
+ uniqueKey: string
67
+ documentTitle: string
68
+ }>();
69
+ const emits = defineEmits(["selectRow"]);
70
+ const clickRow = (eventdata: any) => {
71
+ emits("selectRow", eventdata.data);
72
+ };
73
+
74
+ const filters = ref({
75
+ global: { value: null, matchMode: FilterMatchMode.CONTAINS },
76
+ });
77
+ </script>
@@ -1,66 +1,137 @@
1
1
  <template>
2
- <div>
3
- <div v-if="withFilter">
4
- <input
5
- autofocus
6
- placeholder="search"
7
- v-model="searchvalue"
8
- class="w-full border p-2"
9
- type="search" />
10
- </div>
11
- <div class="max-h-screen overflow-auto">
12
- <ul >
13
- <li v-for="(item,index) in filterlist">
14
- <div :class="getSelectedCSS(item)">
15
- <NuxtLink :to="url ? `${url}/${item[idField]}`:undefined">
16
- <slot :item="item" :index="index">
17
- <div class="mr-2">{{item[titleField??''] }}</div>
18
- <div class="font font-bold text-right">{{item[subTitleField??'']}}</div>
19
- </slot>
20
- </NuxtLink>
21
- </div>
22
-
23
- </li>
24
- </ul>
2
+ <div class="relative ">
3
+ <div v-if="withFilter" class="flex flex-row ">
4
+ <InputText
5
+ placeholder="search"
6
+ v-model="searchvalue"
7
+ class="w-full dark:text-white"
8
+ type="search"
9
+ />
10
+ <ButtonPrimary v-if="withAddNew" @click="emits('add')"
11
+ ><i class="pi pi-plus"></i
12
+ ></ButtonPrimary>
13
+ </div>
14
+ <div v-if="showIndex" class="flex flex-col text-sm fixed left-1 top-1/5 text-primary-600 dark:text-primary-400 p-1 z-10 rounded border bg-white dark:bg-black opacity-90">
15
+ <A :href="'#'+l" v-for="l in allLetters">{{ l }}</A>
16
+ </div>
17
+
18
+ <div v-if="filterlist && filterlist.length > 0">
19
+ <div >
20
+ <div v-for="(item, index) in filterlist" :class="isMobile() ? '' : 'overflow overflow-y-scroll overflow-auto '">
21
+ <div class="w-full bg-gray-400 dark:bg-gray-500 p-1 pl-2 top-0 sticky opacity-90" v-if="showIndex && titleField && showCategoryBar(item[titleField][0])">
22
+ <a :id="item[titleField][0]">{{ item[titleField][0] }}</a></div>
23
+ <div :class="getSelectedCSS(item)">
24
+ <NuxtLink :to="url ? `${url}/${item[idField]}` : undefined">
25
+
26
+ <slot name="default" :item="item" :index="index">
27
+ <div class="flex flex-row">
28
+ <div class="flex-1 mr-2 dark:text-white">
29
+ {{ item[titleField ?? ""] }}
30
+ </div>
31
+ <div class="text-right dark:text-gray-400 text-gray-600">
32
+ {{ item[subTitleField ?? ""] }}
33
+ </div>
34
+ </div>
35
+ </slot>
36
+ </NuxtLink>
37
+ </div>
25
38
  </div>
39
+ </div>
26
40
  </div>
41
+ <div v-else>
42
+ <div
43
+ class="w-full h-20 p-2 border-l-none border-r-none hover-list-primary"
44
+ >
45
+ <slot name="nodata">
46
+ <div class="border-round border-1 surface-border p-4 surface-card">
47
+ {{ t("noDataFound") }}
48
+ </div>
49
+ </slot>
50
+ </div>
51
+ </div>
52
+ </div>
27
53
  </template>
28
- <script setup lang="ts" generic="T extends {[key:string]:any}">
29
- import {ref} from 'vue'
30
- import {ListItem} from '~/types/listview'
31
- const props = withDefaults(defineProps<{
32
- list:T[],
33
- url?:string,
34
- titleField?:string
35
- idField?:string
36
- subTitleField?:string
37
- withFilter?:boolean
38
- }>(),{
39
- idField:'_id'
40
- })
41
- // const emit = defineEmits(['clickitem'])
42
- const searchvalue = ref('')
43
- const selecteditem =ref('')
44
- const clickRow=(item:ListItem)=>{
45
- // emit('clickitem',item)
46
- // selecteditem.value = item.code
47
- }
48
- const filterlist = computed(()=>{
49
- const newlist =props.list.filter((item:any)=>
50
- {
51
- return String(item[props.titleField??'']).toLowerCase().includes(searchvalue.value.toLowerCase()) ||
52
- String(item[props.subTitleField??'']).toLowerCase().includes(searchvalue.value.toLowerCase())
53
- })
54
+ <script setup lang="ts" generic="T extends { [key: string]: any }">
55
+ import { ref } from "vue";
56
+ import { ListItem } from "~/types/listview";
57
+ const props = withDefaults(
58
+ defineProps<{
59
+ list: T[];
60
+ url?: string;
61
+ titleField?: string;
62
+ idField?: string;
63
+ subTitleField?: string;
64
+ withFilter?: boolean;
65
+ withAddNew?: boolean;
66
+ showIndex?:boolean
67
+ }>(),
68
+ {
69
+ idField: "_id",
70
+ },
71
+ );
72
+ const letters =ref<string[]> ([])
73
+ let lastchar=''
74
+
75
+ const emits = defineEmits(["add"]);
76
+ const searchvalue = ref("");
77
+ const selecteditem = ref("");
78
+ const clickRow = (item: ListItem) => {
79
+ // emit('clickitem',item)
80
+ // selecteditem.value = item.code
81
+ };
82
+ const filterlist = computed(() => {
83
+ let newlist: T[] = [];
84
+ if (!Array.isArray(props.list)) {
85
+ return [];
86
+ }
87
+ if (props.list !== undefined) {
88
+ newlist = props.list.filter((item: T) => {
89
+ return (
90
+ String(item[props.titleField ?? ""])
91
+ .toLowerCase()
92
+ .includes(searchvalue.value.toLowerCase()) ||
93
+ String(item[props.subTitleField ?? ""])
94
+ .toLowerCase()
95
+ .includes(searchvalue.value.toLowerCase())
96
+ );
97
+ });
98
+ }
99
+
100
+ return newlist;
101
+ });
102
+ const getSelectedCSS = (item: T) => {
103
+ if (selecteditem.value == item.code) {
104
+ return "p-2 border-l-none border-r-none bg-sky-200 border-t-2";
105
+ } else {
106
+ return "p-2 border-l-none border-r-none hover-list-primary border-t-2 dark:border-t-gray-700";
107
+ }
108
+ };
109
+
110
+
111
+ const allLetters = computed(()=>{
112
+ console.log('props.titleFieldprops.titleField',props.titleField,props.list)
113
+ if(props.titleField && props.list){
114
+
115
+ return props.list.filter((item)=>{
116
+ const titlevalue:string = item[props.titleField as keyof typeof item]
117
+ if(titlevalue[0]!=lastchar){
118
+ lastchar = item[props.titleField as keyof typeof item][0]
119
+ return true
120
+ }
121
+ }).map(item=>item[props.titleField as keyof typeof item][0])
122
+
123
+ }else{
124
+ return []
125
+ }
54
126
 
55
- return newlist
56
127
  })
57
- const getSelectedCSS= (item:T)=>{
58
- if(selecteditem.value == item.code){
59
- return "p-2 border bg-sky-200"
60
- }else{
61
- return "p-2 border hover-list-primary"
62
- }
63
128
 
129
+ const showCategoryBar=(letter:string)=>{
130
+ if(lastchar!=letter){
131
+ lastchar=letter
132
+ return true
133
+ }else{
134
+ return false
135
+ }
64
136
  }
65
-
66
- </script>
137
+ </script>
@@ -0,0 +1,10 @@
1
+ <template>
2
+ <div
3
+ class="w-full dark:bg-gray-800 z-20 bg-white justify justify-between absolute h h-14 flex flex-row place-items-center opacity-90 dark:text-white"
4
+ >
5
+ <div class="w-full text-left"><slot name="start"></slot></div>
6
+ <div class="w-full text-center"><slot name="center"></slot></div>
7
+ <div class="w-full text-right"><slot name="end"></slot></div>
8
+ </div>
9
+ <div class="h-14"></div>
10
+ </template>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <Sidebar
3
+ position="right"
4
+ v-if="showDialog"
5
+ v-model:visible="showDialog"
6
+ modal
7
+ :show-close-icon="false"
8
+ #container
9
+ >
10
+ <div class="overflow-y-scroll">
11
+ <slot name="default"></slot>
12
+ </div>
13
+ </Sidebar>
14
+ </template>
15
+ <script lang="ts" setup>
16
+ const showDialog = defineModel<boolean>({ required: true });
17
+ const props = defineProps<{ closeEventName: string }>();
18
+ useNuxtApp().$listen("CloseDialog", (closeEventName: string) => {
19
+ if (props.closeEventName == closeEventName) showDialog.value = false;
20
+ });
21
+ </script>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <Sidebar
3
+ position="bottom"
4
+ v-if="showDialog"
5
+ v-model:visible="showDialog"
6
+ modal
7
+ :show-close-icon="false"
8
+ #container
9
+ >
10
+ <div class="overflow-y-scroll">
11
+ <slot name="default"></slot>
12
+ </div>
13
+ </Sidebar>
14
+ </template>
15
+ <script lang="ts" setup>
16
+ const showDialog = defineModel<boolean>({ required: true });
17
+ useNuxtApp().$listen("CloseDialog", (closeEventName: string) => {
18
+ if (closeEventName=='viewer') showDialog.value = false;
19
+ });
20
+ </script>
@@ -0,0 +1,170 @@
1
+ <template>
2
+ <title v-if="!id">{{ t(doc.getDocName()) }}</title>
3
+ <div v-if="isMobile()" class="w-full">
4
+ <div class="p-2 flex flex-row justify-end place-items-center h-14">
5
+ <div class="flex-1">
6
+ <TextTitle>{{ t(doc.getDocName()) }}</TextTitle>
7
+ </div>
8
+ <div>
9
+ <ButtonText
10
+ @click="goTo(doc.getDocName(), 'new')"
11
+ class="pi pi-plus"
12
+ ></ButtonText>
13
+ </div>
14
+ </div>
15
+ <ListDocument
16
+ v-if="recordlist"
17
+ @click="showDialogForm"
18
+ :recordlist="recordlist as T[]"
19
+ :unique-key="uniqueKey"
20
+ :document-title="documentTitle"
21
+ >
22
+ <template #default="{ index, item }">
23
+ <slot name="mobileList" :index="index" :item="item as T"></slot>
24
+ </template>
25
+ </ListDocument>
26
+ </div>
27
+ <div v-else class="simpleapp-crudsimple">
28
+ <ListDocumentTable
29
+ v-if="recordlist"
30
+ :value="recordlist as T[]"
31
+ :title="doc.getDocName()"
32
+ :unique-key="<string>uniqueKey"
33
+ :document-title="<string>documentTitle"
34
+ :columns="columns"
35
+ >
36
+ <template #toolbar>
37
+ <div class="w-full text-left">
38
+ <ButtonPrimary
39
+ class="pi pi-plus"
40
+ @click="newData"
41
+ v-tooltip="t('new')"
42
+ v-if="canPerform(resourcename, 'create')"
43
+ ></ButtonPrimary>
44
+ </div>
45
+ </template>
46
+ <template #default>
47
+ <slot name="dataTableColumns">
48
+ <Column v-for="col in columns.filter((item,index)=>index<6)" :header="t(col)" class="min-w-[5rem]" #body="{index,data}">
49
+ <RendererLink v-if="uniqueKey === col || documentTitle === col"
50
+ :value="data" :setting="{ path: resourcename.toLocaleLowerCase() }"
51
+ :fields="[col]" >
52
+ {{ data[col] }}
53
+ </RendererLink>
54
+ <span v-else-if="data[col]?.label !==undefined">{{ data[col].label }}</span>
55
+ <span v-else>{{ data[col] }}</span>
56
+ </Column>
57
+
58
+ </slot>
59
+ </template>
60
+ </ListDocumentTable>
61
+ </div>
62
+ <OverlaySideBarCrud
63
+ v-model="showDialog"
64
+ :closeEventName="doc.getDocName()"
65
+ #default
66
+ ><slot name="default">
67
+ <NuxtPage :_id="id"></NuxtPage>
68
+ </slot>
69
+ </OverlaySideBarCrud>
70
+ </template>
71
+
72
+ <script setup lang="ts" generic="T extends { [key: string]: any }">
73
+ /**
74
+ * This file was automatically generated by simpleapp generator during initialization.
75
+ * DO NOT MODIFY IT BY HAND.
76
+ * last change 2024-02-04
77
+ * author: Ks Tan
78
+ */
79
+ import { ref } from "vue";
80
+ import _ from "lodash";
81
+ import { SearchBody } from "~/types";
82
+ import { SimpleAppClient } from "~/simpleapp/generate/clients/SimpleAppClient";
83
+ const props = defineProps<{
84
+ document: SimpleAppClient<any, any>;
85
+ data: T;
86
+ columns: string[];
87
+ mobileColumns?: string[];
88
+ sorts?: string[][];
89
+ }>();
90
+
91
+ const emits = defineEmits(["selectRow"]);
92
+ const id = computed(() => getPathPara("id"));
93
+ const resourcename = ref(_.upperFirst(props.document.getDocName()));
94
+ const visible = ref(false);
95
+ const showDialog = ref(false);
96
+ const op = ref();
97
+ const doc = props.document;
98
+ const disabled = ref(false);
99
+
100
+ const router = useRouter();
101
+ const route = useRoute();
102
+ const filters = ref();
103
+ const popuptitle = ref(t(doc.getDocName()));
104
+ const systemwindows = ref(false);
105
+ const { $event, $listen } = useNuxtApp();
106
+ const recordlist = ref<T[]>();
107
+ const uniqueKey = doc.getSchema()["x-simpleapp-config"].uniqueKey;
108
+ const documentTitle = doc.getSchema()["x-simpleapp-config"].documentTitle;
109
+
110
+ const refresh = () => {
111
+ const searchbody: SearchBody = {
112
+ fields: props.columns,
113
+ sorts: props.sorts,
114
+ };
115
+ doc.search(searchbody).then((res: any) => {
116
+ recordlist.value = res;
117
+ disabled.value = false;
118
+ });
119
+ };
120
+ const newData = () => {
121
+ router.push({ path: getDocumentUrl(doc.getDocName(), "new") });
122
+ popuptitle.value = t(doc.getDocName());
123
+ doc.setNew();
124
+ visible.value = true;
125
+ };
126
+
127
+ onMounted(() => {
128
+ refresh();
129
+ });
130
+ $listen("RefreshDocumentList", (data) => {
131
+ if (data.documentName == doc.getDocName()) {
132
+ refresh();
133
+ }
134
+ });
135
+ const showDialogForm = (item: T) => {
136
+ goTo(doc.getDocName(), item._id);
137
+ popuptitle.value = <string>item[documentTitle as keyof T];
138
+ };
139
+
140
+ watch(showDialog, () => {
141
+ if (!showDialog.value) {
142
+ goTo(props.document.getDocName());
143
+ }
144
+ });
145
+ watch(
146
+ () => useRoute().path,
147
+ async () => {
148
+ if (getPathPara("id")) {
149
+ showDialog.value = true;
150
+ } else {
151
+ showDialog.value = false;
152
+ }
153
+ },
154
+ );
155
+
156
+ onMounted(() => {
157
+ if (id.value) {
158
+ showDialog.value = true;
159
+ } else {
160
+ showDialog.value = false;
161
+ }
162
+
163
+ useNuxtApp().$listen("CloseDialog", (documentName: string) => {
164
+ if (documentName == doc.getDocName()) {
165
+ goTo(resourcename.value);
166
+ showDialog.value = false;
167
+ }
168
+ });
169
+ });
170
+ </script>