@finema/finework-layer 2.0.51 → 2.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/app/components/Layout/Apps.vue +3 -1
- package/app/components/Notifications/index.vue +51 -24
- package/app/components/PortalApp.vue +252 -573
- package/app/composables/useAuth.ts +41 -22
- package/app/constants/routes.ts +8 -0
- package/package.json +1 -1
- package/public/admin/asset.svg +20 -0
- package/public/admin/clock-in-admin-logo.svg +33 -0
- package/public/admin/clock-in-logo.svg +27 -0
- package/public/admin/contract.svg +41 -0
- package/public/admin/cost-sheet.svg +19 -0
- package/public/admin/document.svg +29 -0
- package/public/admin/efactoring.svg +23 -0
- package/public/admin/employee.svg +16 -0
- package/public/admin/evaluation.svg +29 -0
- package/public/admin/finesign.png +0 -0
- package/public/admin/guarantee.svg +25 -0
- package/public/admin/loan.svg +35 -0
- package/public/admin/newsletter.svg +23 -0
- package/public/admin/pmo-logo.svg +28 -0
- package/public/admin/pmo_delivery.svg +21 -0
- package/public/admin/pmo_presale_bidding.svg +30 -0
- package/public/admin/pmo_setup.svg +18 -0
- package/public/admin/pmo_warranty.svg +26 -0
- package/public/admin/purchase.svg +22 -0
- package/public/admin/quotation.svg +21 -0
- package/public/admin/recruit.svg +21 -0
- package/public/admin/report.svg +41 -0
- package/public/admin/request-admin.svg +44 -0
- package/public/admin/request.svg +42 -0
- package/public/admin/resource-allocation.svg +26 -0
- package/public/admin/resource.svg +22 -0
- package/public/admin/spider-web.png +0 -0
- package/public/admin/super-admin-logo.svg +22 -0
- package/public/admin/support.svg +19 -0
- package/public/admin/timesheet-admin-logo.svg +87 -0
- package/public/admin/timesheet-logo.svg +85 -0
- package/public/admin/todo.svg +21 -0
- package/public/admin/tor.svg +43 -0
|
@@ -1,284 +1,126 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
:key="app.name"
|
|
19
|
-
:to="app.status !== StatusPortal.DEVELOPING ? app.to : undefined"
|
|
20
|
-
class="relative flex items-center gap-3 rounded-lg px-2.5 py-[15px]"
|
|
21
|
-
:class="app.status === StatusPortal.DEVELOPING
|
|
22
|
-
? 'cursor-not-allowed bg-gray-200 opacity-60'
|
|
23
|
-
: 'bg-gray-100 hover:bg-gray-200'"
|
|
24
|
-
@click="app.status !== StatusPortal.DEVELOPING && handleLinkClick(app.to)"
|
|
25
|
-
>
|
|
26
|
-
<div
|
|
27
|
-
v-if="app.status === StatusPortal.DEVELOPING"
|
|
28
|
-
class="absolute top-2 right-2 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-semibold text-orange-600"
|
|
29
|
-
>
|
|
30
|
-
กำลังพัฒนา
|
|
31
|
-
</div>
|
|
32
|
-
<img
|
|
33
|
-
:src="app.logo"
|
|
34
|
-
:alt="app.label"
|
|
35
|
-
class="h-[45px] w-[45px] bg-white"
|
|
36
|
-
/>
|
|
37
|
-
<div>
|
|
38
|
-
<div class="text-lg font-bold">
|
|
39
|
-
{{ app.label }}
|
|
40
|
-
</div>
|
|
41
|
-
<div class="text-sm text-gray-500">
|
|
42
|
-
{{ app.description }}
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
</component>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
<div
|
|
49
|
-
v-if="managementApps.length > 0"
|
|
50
|
-
class="space-y-5"
|
|
51
|
-
>
|
|
52
|
-
<div class="text-lg font-bold text-gray-600">
|
|
53
|
-
Management
|
|
54
|
-
</div>
|
|
55
|
-
<div
|
|
56
|
-
class="grid"
|
|
57
|
-
:class="[isNavbar ?`gap-5 md:grid-cols-2 xl:grid-cols-4` : `grid-cols-1 gap-2`]"
|
|
58
|
-
>
|
|
59
|
-
<component
|
|
60
|
-
:is="app.status === StatusPortal.DEVELOPING ? 'div' : NuxtLink"
|
|
61
|
-
v-for="app in managementApps"
|
|
62
|
-
:key="app.name"
|
|
63
|
-
:to="app.status !== StatusPortal.DEVELOPING ? app.to : undefined"
|
|
64
|
-
class="relative flex items-center gap-3 rounded-lg px-2.5 py-[15px]"
|
|
65
|
-
:class="app.status === StatusPortal.DEVELOPING
|
|
66
|
-
? 'cursor-not-allowed bg-gray-200 opacity-60'
|
|
67
|
-
: 'bg-gray-100 hover:bg-gray-200'"
|
|
68
|
-
@click="app.status !== StatusPortal.DEVELOPING && handleLinkClick(app.to)"
|
|
69
|
-
>
|
|
70
|
-
<div
|
|
71
|
-
v-if="app.status === StatusPortal.DEVELOPING"
|
|
72
|
-
class="absolute top-2 right-2 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-semibold text-orange-600"
|
|
73
|
-
>
|
|
74
|
-
กำลังพัฒนา
|
|
75
|
-
</div>
|
|
76
|
-
<img
|
|
77
|
-
:src="app.logo"
|
|
78
|
-
:alt="app.label"
|
|
79
|
-
class="h-[45px] w-[45px] bg-white"
|
|
80
|
-
/>
|
|
81
|
-
<div>
|
|
82
|
-
<div class="text-lg font-bold">
|
|
83
|
-
{{ app.label }}
|
|
84
|
-
</div>
|
|
85
|
-
<div class="text-sm text-gray-500">
|
|
86
|
-
{{ app.description }}
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
</component>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
2
|
+
<Card
|
|
3
|
+
v-if="categories.length > 0"
|
|
4
|
+
:ui="{
|
|
5
|
+
body: isNavbar
|
|
6
|
+
? 'md:max-h-[calc(100vh-115px)] overflow-auto'
|
|
7
|
+
: 'h-full overflow-auto p-4!',
|
|
8
|
+
}"
|
|
9
|
+
>
|
|
10
|
+
<div class="space-y-6 bg-white">
|
|
11
|
+
<Input
|
|
12
|
+
v-model="search"
|
|
13
|
+
icon="ph:magnifying-glass"
|
|
14
|
+
placeholder="Search app"
|
|
15
|
+
size="lg"
|
|
16
|
+
:ui="{ base: 'rounded-lg' }"
|
|
17
|
+
/>
|
|
92
18
|
|
|
93
19
|
<div
|
|
94
|
-
v-
|
|
95
|
-
|
|
20
|
+
v-for="category in filteredCategories"
|
|
21
|
+
:key="category.key"
|
|
22
|
+
class="space-y-3"
|
|
96
23
|
>
|
|
97
|
-
<div class="text-
|
|
98
|
-
|
|
99
|
-
</div>
|
|
100
|
-
<div
|
|
101
|
-
class="grid"
|
|
102
|
-
:class="[isNavbar ?`gap-5 md:grid-cols-2 xl:grid-cols-4` : `grid-cols-1 gap-2`]"
|
|
103
|
-
>
|
|
104
|
-
<component
|
|
105
|
-
:is="app.status === StatusPortal.DEVELOPING ? 'div' : NuxtLink"
|
|
106
|
-
v-for="app in financeApps"
|
|
107
|
-
:key="app.name"
|
|
108
|
-
:to="app.status !== StatusPortal.DEVELOPING ? app.to : undefined"
|
|
109
|
-
class="relative flex items-center gap-3 rounded-lg px-2.5 py-[15px]"
|
|
110
|
-
:class="app.status === StatusPortal.DEVELOPING
|
|
111
|
-
? 'cursor-not-allowed bg-gray-200 opacity-60'
|
|
112
|
-
: 'bg-gray-100 hover:bg-gray-200'"
|
|
113
|
-
@click="app.status !== StatusPortal.DEVELOPING && handleLinkClick(app.to)"
|
|
114
|
-
>
|
|
115
|
-
<div
|
|
116
|
-
v-if="app.status === StatusPortal.DEVELOPING"
|
|
117
|
-
class="absolute top-2 right-2 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-semibold text-orange-600"
|
|
118
|
-
>
|
|
119
|
-
กำลังพัฒนา
|
|
120
|
-
</div>
|
|
121
|
-
|
|
122
|
-
<img
|
|
123
|
-
:src="app.logo"
|
|
124
|
-
:alt="app.label"
|
|
125
|
-
class="h-[45px] w-[45px] bg-white"
|
|
126
|
-
/>
|
|
127
|
-
|
|
128
|
-
<div>
|
|
129
|
-
<div class="text-lg font-bold">
|
|
130
|
-
{{ app.label }}
|
|
131
|
-
</div>
|
|
132
|
-
<div class="text-sm text-gray-500">
|
|
133
|
-
{{ app.description }}
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
</component>
|
|
24
|
+
<div class="text-base font-bold">
|
|
25
|
+
{{ category.title }}
|
|
137
26
|
</div>
|
|
138
|
-
</div>
|
|
139
27
|
|
|
140
|
-
|
|
141
|
-
v-if="peopleApps.length >0"
|
|
142
|
-
class="space-y-5"
|
|
143
|
-
>
|
|
144
|
-
<div class="text-lg font-bold text-gray-600">
|
|
145
|
-
People
|
|
146
|
-
</div>
|
|
28
|
+
<!-- Navbar: compact icon-on-top grid -->
|
|
147
29
|
<div
|
|
148
|
-
|
|
149
|
-
|
|
30
|
+
v-if="isNavbar"
|
|
31
|
+
class="grid gap-x-2 gap-y-4"
|
|
32
|
+
:class="gridColsClass"
|
|
150
33
|
>
|
|
151
34
|
<component
|
|
152
|
-
:is="app
|
|
153
|
-
v-for="app in
|
|
35
|
+
:is="linkComponent(app)"
|
|
36
|
+
v-for="app in category.apps"
|
|
154
37
|
:key="app.name"
|
|
155
|
-
:to="app
|
|
156
|
-
class="
|
|
157
|
-
:class="app
|
|
158
|
-
|
|
159
|
-
: 'bg-gray-100 hover:bg-gray-200'"
|
|
160
|
-
@click="app.status !== StatusPortal.DEVELOPING && handleLinkClick(app.to)"
|
|
38
|
+
:to="linkTo(app)"
|
|
39
|
+
class="group flex flex-col items-center gap-2 rounded-xl p-2 text-center transition-colors"
|
|
40
|
+
:class="isDeveloping(app) ? 'cursor-not-allowed opacity-60' : 'hover:bg-gray-100'"
|
|
41
|
+
@click="onAppClick(app)"
|
|
161
42
|
>
|
|
162
|
-
<div
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
<div class="text-lg font-bold">
|
|
175
|
-
{{ app.label }}
|
|
176
|
-
</div>
|
|
177
|
-
<div class="text-sm text-gray-500">
|
|
178
|
-
{{ app.description }}
|
|
179
|
-
</div>
|
|
43
|
+
<div class="relative">
|
|
44
|
+
<img
|
|
45
|
+
:src="app.logo"
|
|
46
|
+
:alt="app.label"
|
|
47
|
+
class="size-12.5 rounded-xl object-contain"
|
|
48
|
+
/>
|
|
49
|
+
<span
|
|
50
|
+
v-if="isDeveloping(app)"
|
|
51
|
+
class="absolute -top-1.5 -right-1.5 rounded-full bg-orange-100 px-1.5 py-0.5 text-[10px] font-semibold text-orange-600"
|
|
52
|
+
>
|
|
53
|
+
กำลังพัฒนา
|
|
54
|
+
</span>
|
|
180
55
|
</div>
|
|
56
|
+
<span class="line-clamp-2 text-sm font-semibold">
|
|
57
|
+
{{ app.label }}
|
|
58
|
+
</span>
|
|
181
59
|
</component>
|
|
182
60
|
</div>
|
|
183
|
-
</div>
|
|
184
61
|
|
|
185
|
-
|
|
186
|
-
v-if="legalApps.length > 0"
|
|
187
|
-
class="space-y-5"
|
|
188
|
-
>
|
|
189
|
-
<div class="text-lg font-bold text-gray-600">
|
|
190
|
-
Legal
|
|
191
|
-
</div>
|
|
62
|
+
<!-- Standalone: long list with descriptions -->
|
|
192
63
|
<div
|
|
193
|
-
|
|
194
|
-
|
|
64
|
+
v-else
|
|
65
|
+
class="flex flex-col"
|
|
195
66
|
>
|
|
196
67
|
<component
|
|
197
|
-
:is="app
|
|
198
|
-
v-for="app in
|
|
68
|
+
:is="linkComponent(app)"
|
|
69
|
+
v-for="app in category.apps"
|
|
199
70
|
:key="app.name"
|
|
200
|
-
:to="app
|
|
201
|
-
class="
|
|
202
|
-
:class="app
|
|
203
|
-
|
|
204
|
-
: 'bg-gray-100 hover:bg-gray-200'"
|
|
205
|
-
@click="app.status !== StatusPortal.DEVELOPING && handleLinkClick(app.to)"
|
|
71
|
+
:to="linkTo(app)"
|
|
72
|
+
class="flex items-start gap-3 border-b border-gray-100 px-1 py-3 transition-colors last:border-0"
|
|
73
|
+
:class="isDeveloping(app) ? 'cursor-not-allowed opacity-60' : 'hover:bg-gray-50'"
|
|
74
|
+
@click="onAppClick(app)"
|
|
206
75
|
>
|
|
207
|
-
<div
|
|
208
|
-
v-if="app.status === StatusPortal.DEVELOPING"
|
|
209
|
-
class="absolute top-2 right-2 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-semibold text-orange-600"
|
|
210
|
-
>
|
|
211
|
-
กำลังพัฒนา
|
|
212
|
-
</div>
|
|
213
76
|
<img
|
|
214
77
|
:src="app.logo"
|
|
215
78
|
:alt="app.label"
|
|
216
|
-
class="
|
|
79
|
+
class="size-11 shrink-0 object-contain"
|
|
217
80
|
/>
|
|
218
|
-
<div>
|
|
219
|
-
<div class="
|
|
81
|
+
<div class="min-w-0">
|
|
82
|
+
<div class="flex items-center gap-2 font-bold text-gray-900">
|
|
220
83
|
{{ app.label }}
|
|
84
|
+
<span
|
|
85
|
+
v-if="isDeveloping(app)"
|
|
86
|
+
class="rounded-full bg-orange-100 px-1.5 py-0.5 text-[10px] font-semibold text-orange-600"
|
|
87
|
+
>
|
|
88
|
+
กำลังพัฒนา
|
|
89
|
+
</span>
|
|
221
90
|
</div>
|
|
222
|
-
<div class="text-sm text-gray-500">
|
|
91
|
+
<div class="line-clamp-2 text-sm text-gray-500">
|
|
223
92
|
{{ app.description }}
|
|
224
93
|
</div>
|
|
225
94
|
</div>
|
|
226
95
|
</component>
|
|
227
96
|
</div>
|
|
228
97
|
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</DefineTemplate>
|
|
231
|
-
<Card
|
|
232
|
-
v-if="dailyApps.length > 0 || managementApps.length > 0 || financeApps.length > 0 || peopleApps.length > 0 || legalApps.length > 0"
|
|
233
|
-
:ui="{
|
|
234
|
-
body: isNavbar
|
|
235
|
-
? 'md:max-h-[100vh] md:max-h-[calc(100vh-115px)] overflow-auto'
|
|
236
|
-
: 'h-full overflow-auto p-4!',
|
|
237
|
-
}"
|
|
238
|
-
>
|
|
239
|
-
<div class="bg-white">
|
|
240
|
-
<ReuseTemplate v-if="isNavbar" />
|
|
241
|
-
<Collapsible
|
|
242
|
-
v-else
|
|
243
|
-
v-model:open="open"
|
|
244
|
-
default-open
|
|
245
|
-
>
|
|
246
|
-
<div class="flex items-center justify-between gap-4">
|
|
247
|
-
<div class="mb-5 flex items-center gap-2">
|
|
248
|
-
<img
|
|
249
|
-
src="/icon-finema.png"
|
|
250
|
-
alt="icon-finema"
|
|
251
|
-
class="h-8 w-8 -translate-y-1"
|
|
252
|
-
/>
|
|
253
|
-
<div class="text-2xl font-bold">
|
|
254
|
-
My Apps
|
|
255
|
-
</div>
|
|
256
|
-
</div>
|
|
257
98
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
/>
|
|
264
|
-
</div>
|
|
265
|
-
<template #content>
|
|
266
|
-
<ReuseTemplate />
|
|
267
|
-
</template>
|
|
268
|
-
</Collapsible>
|
|
99
|
+
<Empty
|
|
100
|
+
v-if="search && filteredCategories.length === 0"
|
|
101
|
+
icon="ph:magnifying-glass"
|
|
102
|
+
message="No apps found"
|
|
103
|
+
/>
|
|
269
104
|
</div>
|
|
270
105
|
</Card>
|
|
271
106
|
</template>
|
|
272
107
|
|
|
273
108
|
<script lang="ts" setup>
|
|
274
109
|
import { resolveComponent } from 'vue'
|
|
275
|
-
|
|
110
|
+
// Import the shared maps straight from the layer so PortalApp stays deterministic
|
|
111
|
+
// even in apps that shadow UserModuleLabel / UserModuleLogo with their own copy.
|
|
112
|
+
import {
|
|
113
|
+
UserModuleAdminLabel,
|
|
114
|
+
UserModuleAdminLogo,
|
|
115
|
+
UserModuleLabel,
|
|
116
|
+
UserModuleLogo,
|
|
117
|
+
} from '#layer/app/composables/useAuth'
|
|
276
118
|
|
|
277
119
|
const emit = defineEmits<{
|
|
278
120
|
(e: 'close'): void
|
|
279
121
|
}>()
|
|
280
122
|
|
|
281
|
-
|
|
123
|
+
defineProps<{
|
|
282
124
|
isNavbar?: boolean
|
|
283
125
|
}>()
|
|
284
126
|
|
|
@@ -286,11 +128,26 @@ const enum StatusPortal {
|
|
|
286
128
|
ACTIVE = 'active',
|
|
287
129
|
DEVELOPING = 'developing',
|
|
288
130
|
}
|
|
131
|
+
|
|
132
|
+
interface IPortalApp {
|
|
133
|
+
name: string
|
|
134
|
+
label: string
|
|
135
|
+
logo: string
|
|
136
|
+
description: string
|
|
137
|
+
to: string
|
|
138
|
+
status: StatusPortal
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface IPortalCategory {
|
|
142
|
+
key: string
|
|
143
|
+
title: string
|
|
144
|
+
apps: IPortalApp[]
|
|
145
|
+
}
|
|
146
|
+
|
|
289
147
|
const NuxtLink = resolveComponent('NuxtLink')
|
|
290
148
|
const auth = useAuth()
|
|
291
149
|
const route = useRoute()
|
|
292
|
-
const
|
|
293
|
-
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
|
|
150
|
+
const search = ref('')
|
|
294
151
|
|
|
295
152
|
const handleLinkClick = (to: string) => {
|
|
296
153
|
if (route.path === to) {
|
|
@@ -298,372 +155,194 @@ const handleLinkClick = (to: string) => {
|
|
|
298
155
|
}
|
|
299
156
|
}
|
|
300
157
|
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
158
|
+
const isDeveloping = (app: IPortalApp) => app.status === StatusPortal.DEVELOPING
|
|
159
|
+
const linkComponent = (app: IPortalApp) => (isDeveloping(app) ? 'div' : NuxtLink)
|
|
160
|
+
const linkTo = (app: IPortalApp) => (isDeveloping(app) ? undefined : app.to)
|
|
161
|
+
|
|
162
|
+
const onAppClick = (app: IPortalApp) => {
|
|
163
|
+
if (!isDeveloping(app)) {
|
|
164
|
+
handleLinkClick(app.to)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const USER_ADMIN_SUPER = [Permission.USER, Permission.ADMIN, Permission.SUPER]
|
|
169
|
+
const ADMIN_SUPER = [Permission.ADMIN, Permission.SUPER]
|
|
170
|
+
|
|
171
|
+
const can = (module: UserModule, ...permissions: AccessPermission[]) =>
|
|
172
|
+
auth.hasPermission(module, ...permissions)
|
|
173
|
+
|
|
174
|
+
/** App whose label/logo come from the shared UserModule maps. */
|
|
175
|
+
const toApp = (
|
|
176
|
+
module: UserModule,
|
|
177
|
+
to: string,
|
|
178
|
+
description: string,
|
|
179
|
+
overrides: Partial<IPortalApp> = {},
|
|
180
|
+
): IPortalApp => ({
|
|
181
|
+
name: overrides.name ?? module,
|
|
182
|
+
label: overrides.label ?? UserModuleLabel[module as keyof typeof UserModuleLabel],
|
|
183
|
+
logo: overrides.logo ?? UserModuleLogo[module as keyof typeof UserModuleLogo],
|
|
184
|
+
description,
|
|
185
|
+
to,
|
|
186
|
+
status: overrides.status ?? StatusPortal.ACTIVE,
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
/** Admin-console variant of a module, using the shared admin maps. */
|
|
190
|
+
const toAdminApp = (
|
|
191
|
+
module: keyof typeof UserModuleAdminLabel,
|
|
192
|
+
to: string,
|
|
193
|
+
description: string,
|
|
194
|
+
): IPortalApp => ({
|
|
195
|
+
name: `${module}-admin`,
|
|
196
|
+
label: UserModuleAdminLabel[module],
|
|
197
|
+
logo: UserModuleAdminLogo[module],
|
|
198
|
+
description,
|
|
199
|
+
to,
|
|
200
|
+
status: StatusPortal.ACTIVE,
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const dailyApps = computed<IPortalApp[]>(() => [
|
|
204
|
+
...(can(UserModule.CHECKIN, ...USER_ADMIN_SUPER)
|
|
205
|
+
? [toApp(UserModule.CHECKIN, routes.clockin.home.to, 'เช็กอินเวลางาน และเลือกสถานที่ทำงาน')]
|
|
313
206
|
: []),
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
label: ' Timesheet',
|
|
318
|
-
description: 'บันทึกงานที่ทำในแต่ละวัน',
|
|
319
|
-
to: routes.timesheet.home.to,
|
|
320
|
-
status: StatusPortal.ACTIVE,
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
name: 'todo',
|
|
324
|
-
logo: '/admin/todo.png',
|
|
325
|
-
label: 'Todo',
|
|
326
|
-
description: 'ระบบจัดการงาน',
|
|
327
|
-
to: routes.todo.dashboard.to,
|
|
328
|
-
status: StatusPortal.ACTIVE,
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
name: 'Request',
|
|
332
|
-
logo: '/admin/request.png',
|
|
333
|
-
label: 'Request',
|
|
334
|
-
description: 'ระบบขอ ลา เบิก อนุมัติรายการต่างๆ',
|
|
335
|
-
to: routes.request.myRequest.requests.to,
|
|
336
|
-
status: StatusPortal.ACTIVE,
|
|
337
|
-
},
|
|
207
|
+
toApp(UserModule.TIMESHEET, routes.timesheet.home.to, 'บันทึกงานที่ทำในแต่ละวัน'),
|
|
208
|
+
toApp(UserModule.TODO, routes.todo.dashboard.to, 'ระบบจัดการงาน'),
|
|
209
|
+
toApp(UserModule.REQUEST, routes.request.myRequest.requests.to, 'ระบบขอ ลา เบิก อนุมัติรายการต่างๆ'),
|
|
338
210
|
])
|
|
339
211
|
|
|
340
|
-
const managementApps = computed(() => [
|
|
341
|
-
...(
|
|
342
|
-
UserModule.PMO,
|
|
343
|
-
Permission.ADMIN,
|
|
344
|
-
Permission.USER,
|
|
345
|
-
Permission.SUPER,
|
|
346
|
-
)
|
|
347
|
-
? [
|
|
348
|
-
{
|
|
349
|
-
name: 'pmo',
|
|
350
|
-
logo: '/admin/pmo-logo.png',
|
|
351
|
-
label: 'PMO',
|
|
352
|
-
description: 'จัดระเบียบและบริหารโครงการของคุณ',
|
|
353
|
-
to: routes.pmo.project.projects.to,
|
|
354
|
-
status: StatusPortal.ACTIVE,
|
|
355
|
-
},
|
|
356
|
-
]
|
|
212
|
+
const managementApps = computed<IPortalApp[]>(() => [
|
|
213
|
+
...(can(UserModule.PMO, ...USER_ADMIN_SUPER)
|
|
214
|
+
? [toApp(UserModule.PMO, routes.pmo.project.projects.to, 'จัดระเบียบและบริหารโครงการของคุณ')]
|
|
357
215
|
: []),
|
|
358
|
-
...(
|
|
359
|
-
UserModule.PMO_SETUP,
|
|
360
|
-
Permission.ADMIN,
|
|
361
|
-
Permission.USER,
|
|
362
|
-
Permission.SUPER,
|
|
363
|
-
)
|
|
364
|
-
? [
|
|
365
|
-
{
|
|
366
|
-
name: 'pmo-setup',
|
|
367
|
-
logo: '/admin/pmo_setup.png',
|
|
368
|
-
label: 'Lead in',
|
|
369
|
-
description: 'ตั้งค่าและบริหารจัดการโครงการ PMO',
|
|
370
|
-
to: routes.pmoSetup.dashboard.to,
|
|
371
|
-
status: StatusPortal.ACTIVE,
|
|
372
|
-
},
|
|
373
|
-
]
|
|
216
|
+
...(can(UserModule.PMO_SETUP, ...USER_ADMIN_SUPER)
|
|
217
|
+
? [toApp(UserModule.PMO_SETUP, routes.pmoSetup.dashboard.to, 'ตั้งค่าและบริหารจัดการโครงการ PMO')]
|
|
374
218
|
: []),
|
|
375
|
-
...(
|
|
376
|
-
UserModule.PMO_PRESALE_BIDDING,
|
|
377
|
-
Permission.ADMIN,
|
|
378
|
-
Permission.USER,
|
|
379
|
-
Permission.SUPER,
|
|
380
|
-
)
|
|
381
|
-
? [
|
|
382
|
-
{
|
|
383
|
-
name: 'pmo-presale-bidding',
|
|
384
|
-
logo: '/admin/pmo_presale_bidding.png',
|
|
385
|
-
label: 'Presale & Bidding',
|
|
386
|
-
description: 'บริหารจัดการงาน Presale และ Bidding',
|
|
387
|
-
to: routes.pmoPresaleBidding.dashboard.to,
|
|
388
|
-
status: StatusPortal.ACTIVE,
|
|
389
|
-
},
|
|
390
|
-
]
|
|
219
|
+
...(can(UserModule.PMO_PRESALE_BIDDING, ...USER_ADMIN_SUPER)
|
|
220
|
+
? [toApp(UserModule.PMO_PRESALE_BIDDING, routes.pmoPresaleBidding.dashboard.to, 'บริหารจัดการงาน Presale และ Bidding')]
|
|
391
221
|
: []),
|
|
392
|
-
...(
|
|
393
|
-
UserModule.PMO_DELIVERY,
|
|
394
|
-
Permission.ADMIN,
|
|
395
|
-
Permission.USER,
|
|
396
|
-
Permission.SUPER,
|
|
397
|
-
)
|
|
398
|
-
? [
|
|
399
|
-
{
|
|
400
|
-
name: 'pmo-delivery',
|
|
401
|
-
logo: '/admin/pmo_delivery.png',
|
|
402
|
-
label: 'Delivery',
|
|
403
|
-
description: 'บริหารจัดการโครงการช่วง Delivery',
|
|
404
|
-
to: routes.pmoDelivery.dashboard.to,
|
|
405
|
-
status: StatusPortal.ACTIVE,
|
|
406
|
-
},
|
|
407
|
-
]
|
|
222
|
+
...(can(UserModule.PMO_DELIVERY, ...USER_ADMIN_SUPER)
|
|
223
|
+
? [toApp(UserModule.PMO_DELIVERY, routes.pmoDelivery.dashboard.to, 'บริหารจัดการโครงการช่วง Delivery')]
|
|
408
224
|
: []),
|
|
409
|
-
...(
|
|
410
|
-
UserModule.PMO_WARRANTY,
|
|
411
|
-
Permission.ADMIN,
|
|
412
|
-
Permission.USER,
|
|
413
|
-
Permission.SUPER,
|
|
414
|
-
)
|
|
415
|
-
? [
|
|
416
|
-
{
|
|
417
|
-
name: 'pmo-warranty',
|
|
418
|
-
logo: '/admin/pmo_warranty.png',
|
|
419
|
-
label: 'Warranty',
|
|
420
|
-
description: 'บริหารจัดการโครงการช่วง Warranty',
|
|
421
|
-
to: routes.pmoWarranty.dashboard.to,
|
|
422
|
-
status: StatusPortal.ACTIVE,
|
|
423
|
-
},
|
|
424
|
-
]
|
|
225
|
+
...(can(UserModule.PMO_WARRANTY, ...USER_ADMIN_SUPER)
|
|
226
|
+
? [toApp(UserModule.PMO_WARRANTY, routes.pmoWarranty.dashboard.to, 'บริหารจัดการโครงการช่วง Warranty')]
|
|
425
227
|
: []),
|
|
426
|
-
...(
|
|
427
|
-
? [
|
|
428
|
-
{
|
|
429
|
-
name: 'clockin-admin',
|
|
430
|
-
logo: '/admin/clock-in-admin-logo.png',
|
|
431
|
-
label: 'Clock-in Report',
|
|
432
|
-
description: 'รายงานการลงเวลา',
|
|
433
|
-
to: routes.adminClockin.checkinDashboard.to,
|
|
434
|
-
status: StatusPortal.ACTIVE,
|
|
435
|
-
},
|
|
436
|
-
]
|
|
228
|
+
...(can(UserModule.RESOURCE_ALLOCATION, ...ADMIN_SUPER)
|
|
229
|
+
? [toApp(UserModule.RESOURCE_ALLOCATION, routes.resourceAllocation.dashboard.to, 'วางแผนและจัดสรรกำลังคนในโครงการ')]
|
|
437
230
|
: []),
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
? [
|
|
441
|
-
{
|
|
442
|
-
name: 'timesheet-admin',
|
|
443
|
-
logo: '/admin/timesheet-admin-logo.png',
|
|
444
|
-
label: 'Timesheet Report',
|
|
445
|
-
description: 'รายงานการบันทึกข้อมูลไทม์ชีท',
|
|
446
|
-
to: routes.adminTimesheet.summaryReport.to,
|
|
447
|
-
status: StatusPortal.ACTIVE,
|
|
448
|
-
},
|
|
449
|
-
]
|
|
231
|
+
...(can(UserModule.CHECKIN, ...ADMIN_SUPER)
|
|
232
|
+
? [toAdminApp(UserModule.CHECKIN, routes.adminClockin.checkinDashboard.to, 'รายงานการลงเวลา')]
|
|
450
233
|
: []),
|
|
451
|
-
...(
|
|
452
|
-
? [
|
|
453
|
-
{
|
|
454
|
-
name: 'report',
|
|
455
|
-
logo: '/admin/report.svg',
|
|
456
|
-
label: 'Report',
|
|
457
|
-
description: 'รายงานภาพรวมบริษัท',
|
|
458
|
-
to: routes.report.checkinSummaryDashboard.to,
|
|
459
|
-
status: StatusPortal.ACTIVE,
|
|
460
|
-
},
|
|
461
|
-
]
|
|
234
|
+
...(can(UserModule.TIMESHEET, ...ADMIN_SUPER)
|
|
235
|
+
? [toAdminApp(UserModule.TIMESHEET, routes.adminTimesheet.summaryReport.to, 'รายงานการบันทึกข้อมูลไทม์ชีท')]
|
|
462
236
|
: []),
|
|
463
|
-
...(
|
|
464
|
-
? [
|
|
465
|
-
{
|
|
466
|
-
name: 'tor',
|
|
467
|
-
logo: '/admin/tor.svg',
|
|
468
|
-
label: 'Presales Tools',
|
|
469
|
-
description: 'ระบบสร้างตารางเปรียบเทียบข้อกำหนด',
|
|
470
|
-
to: routes.tor.home.to,
|
|
471
|
-
status: StatusPortal.ACTIVE,
|
|
472
|
-
},
|
|
473
|
-
]
|
|
237
|
+
...(can(UserModule.SETTING, Permission.SUPER)
|
|
238
|
+
? [toAdminApp(UserModule.SETTING, routes.report.checkinSummaryDashboard.to, 'รายงานภาพรวมบริษัท')]
|
|
474
239
|
: []),
|
|
475
|
-
...(
|
|
476
|
-
? [
|
|
477
|
-
{
|
|
478
|
-
name: 'support',
|
|
479
|
-
logo: '/admin/support.svg',
|
|
480
|
-
label: 'Support',
|
|
481
|
-
description: 'Heatmap Monitor & Report',
|
|
482
|
-
to: routes.support.home.to,
|
|
483
|
-
status: StatusPortal.ACTIVE,
|
|
484
|
-
},
|
|
485
|
-
]
|
|
240
|
+
...(can(UserModule.TOR, ...USER_ADMIN_SUPER)
|
|
241
|
+
? [toApp(UserModule.TOR, routes.tor.home.to, 'ระบบสร้างตารางเปรียบเทียบข้อกำหนด')]
|
|
486
242
|
: []),
|
|
487
|
-
...(
|
|
488
|
-
? [
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
label: 'Document Flow',
|
|
493
|
-
description: 'ทะเบียนเอกสาร รับ-ส่ง',
|
|
494
|
-
to: routes.docsManagement.root.to,
|
|
495
|
-
status: StatusPortal.ACTIVE,
|
|
496
|
-
},
|
|
497
|
-
]
|
|
243
|
+
...(can(UserModule.SUPPORT, ...USER_ADMIN_SUPER)
|
|
244
|
+
? [toApp(UserModule.SUPPORT, routes.support.home.to, 'Heatmap Monitor & Report')]
|
|
245
|
+
: []),
|
|
246
|
+
...(can(UserModule.DOCUMENT, ...USER_ADMIN_SUPER)
|
|
247
|
+
? [toApp(UserModule.DOCUMENT, routes.docsManagement.root.to, 'ทะเบียนเอกสาร รับ-ส่ง')]
|
|
498
248
|
: []),
|
|
499
|
-
|
|
500
249
|
])
|
|
501
250
|
|
|
502
|
-
const financeApps = computed(() => [
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
? [
|
|
506
|
-
{
|
|
507
|
-
name: 'Quotation',
|
|
508
|
-
logo: '/admin/quotation.png',
|
|
509
|
-
label: 'Quotation',
|
|
510
|
-
description: 'รายรับรายจ่ายงบประมาณโครงการ',
|
|
511
|
-
to: routes.quotation.quotations.home.to,
|
|
512
|
-
status: StatusPortal.ACTIVE,
|
|
513
|
-
},
|
|
514
|
-
]
|
|
251
|
+
const financeApps = computed<IPortalApp[]>(() => [
|
|
252
|
+
...(can(UserModule.QUOTATION, ...USER_ADMIN_SUPER)
|
|
253
|
+
? [toApp(UserModule.QUOTATION, routes.quotation.quotations.home.to, 'รายรับรายจ่ายงบประมาณโครงการ')]
|
|
515
254
|
: []),
|
|
516
|
-
...(
|
|
517
|
-
? [
|
|
518
|
-
{
|
|
519
|
-
name: 'Cost Sheet',
|
|
520
|
-
logo: '/admin/cost-sheet.png',
|
|
521
|
-
label: 'Cost Sheet',
|
|
522
|
-
description: 'รายรับรายจ่ายงบประมาณโครงการ',
|
|
523
|
-
to: routes.costSheet.overview.to,
|
|
524
|
-
status: StatusPortal.ACTIVE,
|
|
525
|
-
},
|
|
526
|
-
]
|
|
255
|
+
...(can(UserModule.COSTSHEET, ...USER_ADMIN_SUPER)
|
|
256
|
+
? [toApp(UserModule.COSTSHEET, routes.costSheet.overview.to, 'รายรับรายจ่ายงบประมาณโครงการ')]
|
|
527
257
|
: []),
|
|
528
|
-
...(
|
|
529
|
-
? [
|
|
530
|
-
{
|
|
531
|
-
name: 'eFactoring',
|
|
532
|
-
logo: '/admin/efactoring.png',
|
|
533
|
-
label: 'eFactoring',
|
|
534
|
-
description: 'การเบิกเงินหมุนเวียนกับธนาคาร',
|
|
535
|
-
to: routes.eFactoring.dashboard.to,
|
|
536
|
-
status: StatusPortal.ACTIVE,
|
|
537
|
-
},
|
|
538
|
-
]
|
|
258
|
+
...(can(UserModule.EFACTORING, ...USER_ADMIN_SUPER)
|
|
259
|
+
? [toApp(UserModule.EFACTORING, routes.eFactoring.dashboard.to, 'การเบิกเงินหมุนเวียนกับธนาคาร')]
|
|
539
260
|
: []),
|
|
540
|
-
...(
|
|
541
|
-
? [
|
|
542
|
-
{
|
|
543
|
-
name: 'Guarantee',
|
|
544
|
-
logo: '/admin/guarantee.png',
|
|
545
|
-
label: 'งบค้ำประกัน',
|
|
546
|
-
description: 'การเบิกเงินค้ำซองและค้ำสัญญา',
|
|
547
|
-
to: routes.lg.overview.to,
|
|
548
|
-
status: StatusPortal.ACTIVE,
|
|
549
|
-
},
|
|
550
|
-
]
|
|
261
|
+
...(can(UserModule.LG, ...USER_ADMIN_SUPER)
|
|
262
|
+
? [toApp(UserModule.LG, routes.lg.overview.to, 'การเบิกเงินค้ำซองและค้ำสัญญา')]
|
|
551
263
|
: []),
|
|
552
|
-
...(
|
|
553
|
-
? [
|
|
554
|
-
{
|
|
555
|
-
name: 'Loan',
|
|
556
|
-
logo: '/admin/loan.svg',
|
|
557
|
-
label: 'Loan',
|
|
558
|
-
description: 'รายการกู้เงินจากธนาคาร',
|
|
559
|
-
to: routes.loan.list.lists.to,
|
|
560
|
-
status: StatusPortal.ACTIVE,
|
|
561
|
-
},
|
|
562
|
-
]
|
|
264
|
+
...(can(UserModule.LOAN, ...USER_ADMIN_SUPER)
|
|
265
|
+
? [toApp(UserModule.LOAN, routes.loan.list.lists.to, 'รายการกู้เงินจากธนาคาร')]
|
|
563
266
|
: []),
|
|
564
267
|
])
|
|
565
268
|
|
|
566
|
-
const peopleApps = computed(() => [
|
|
567
|
-
...(
|
|
568
|
-
? [
|
|
569
|
-
{
|
|
570
|
-
name: 'Employee',
|
|
571
|
-
logo: '/admin/employee.png',
|
|
572
|
-
label: 'Employee',
|
|
573
|
-
description: 'ระบบจัดการข้อมูลพนักงาน',
|
|
574
|
-
to: routes.employee.root.to,
|
|
575
|
-
status: StatusPortal.ACTIVE,
|
|
576
|
-
},
|
|
577
|
-
]
|
|
269
|
+
const peopleApps = computed<IPortalApp[]>(() => [
|
|
270
|
+
...(can(UserModule.EMPLOYEE, ...USER_ADMIN_SUPER)
|
|
271
|
+
? [toApp(UserModule.EMPLOYEE, routes.employee.root.to, 'ระบบจัดการข้อมูลพนักงาน')]
|
|
578
272
|
: []),
|
|
579
|
-
...(
|
|
580
|
-
? [
|
|
581
|
-
{
|
|
582
|
-
name: 'Newsletter',
|
|
583
|
-
logo: '/admin/newsletter.png',
|
|
584
|
-
label: 'Newsletter',
|
|
585
|
-
description: 'ระบบจัดการข่าวสาร',
|
|
586
|
-
to: routes.newsletter.announcement.announcements.to,
|
|
587
|
-
status: StatusPortal.ACTIVE,
|
|
588
|
-
},
|
|
589
|
-
]
|
|
273
|
+
...(can(UserModule.NEWSLETTER, ...USER_ADMIN_SUPER)
|
|
274
|
+
? [toApp(UserModule.NEWSLETTER, routes.newsletter.announcement.announcements.to, 'ระบบจัดการข่าวสาร')]
|
|
590
275
|
: []),
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
? [
|
|
594
|
-
{
|
|
595
|
-
name: 'Assets',
|
|
596
|
-
logo: '/admin/assets.png',
|
|
597
|
-
label: 'Assets',
|
|
598
|
-
description: 'ระบบจัดการสินทรัพย์',
|
|
599
|
-
to: routes.asset.dashboard.to,
|
|
600
|
-
status: StatusPortal.ACTIVE,
|
|
601
|
-
},
|
|
602
|
-
]
|
|
276
|
+
...(can(UserModule.ASSET, ...USER_ADMIN_SUPER)
|
|
277
|
+
? [toApp(UserModule.ASSET, routes.asset.dashboard.to, 'ระบบจัดการสินทรัพย์')]
|
|
603
278
|
: []),
|
|
604
|
-
...(
|
|
605
|
-
? [
|
|
606
|
-
{
|
|
607
|
-
name: 'Resource',
|
|
608
|
-
logo: '/admin/resource.svg',
|
|
609
|
-
label: 'Resource',
|
|
610
|
-
description: 'ระบบจัดการทรัพยากรบุคคล',
|
|
611
|
-
to: routes.resource.talentSearchAndCvBuilder.to,
|
|
612
|
-
status: StatusPortal.ACTIVE,
|
|
613
|
-
},
|
|
614
|
-
]
|
|
279
|
+
...(can(UserModule.RESOURCE, ...USER_ADMIN_SUPER)
|
|
280
|
+
? [toApp(UserModule.RESOURCE, routes.resource.talentSearchAndCvBuilder.to, 'ระบบจัดการทรัพยากรบุคคล')]
|
|
615
281
|
: []),
|
|
616
|
-
...(
|
|
617
|
-
? [
|
|
618
|
-
{
|
|
619
|
-
name: 'Request Admin',
|
|
620
|
-
logo: '/admin/request-admin.svg',
|
|
621
|
-
label: 'Request Admin',
|
|
622
|
-
description: 'ระบบจัดการคำขออนุมัติต่างๆ',
|
|
623
|
-
to: routes.requestAdmin.userRequest.users.to,
|
|
624
|
-
status: StatusPortal.ACTIVE,
|
|
625
|
-
},
|
|
626
|
-
]
|
|
282
|
+
...(can(UserModule.REQUEST, Permission.SUPER, Permission.ADMIN)
|
|
283
|
+
? [toAdminApp(UserModule.REQUEST, routes.requestAdmin.userRequest.users.to, 'ระบบจัดการคำขออนุมัติต่างๆ')]
|
|
627
284
|
: []),
|
|
628
|
-
...(
|
|
629
|
-
? [
|
|
630
|
-
{
|
|
631
|
-
name: 'Evaluation',
|
|
632
|
-
logo: '/admin/evaluation.png',
|
|
633
|
-
label: 'Evaluation',
|
|
634
|
-
description: 'ระบบจัดการการประเมิน',
|
|
635
|
-
to: routes.evaluation.general.generals.to,
|
|
636
|
-
status: StatusPortal.ACTIVE,
|
|
637
|
-
},
|
|
638
|
-
]
|
|
285
|
+
...(can(UserModule.EVALUATION, ...USER_ADMIN_SUPER)
|
|
286
|
+
? [toApp(UserModule.EVALUATION, routes.evaluation.general.generals.to, 'ระบบจัดการการประเมิน')]
|
|
639
287
|
: []),
|
|
640
|
-
...(
|
|
641
|
-
? [
|
|
642
|
-
{
|
|
643
|
-
name: 'Recruit',
|
|
644
|
-
logo: '/admin/recruit.png',
|
|
645
|
-
label: 'Recruit',
|
|
646
|
-
description: 'ระบบการสรรหาบุคลากร',
|
|
647
|
-
to: routes.recruit.root.to,
|
|
648
|
-
status: StatusPortal.ACTIVE,
|
|
649
|
-
},
|
|
650
|
-
]
|
|
288
|
+
...(can(UserModule.RECRUIT, ...ADMIN_SUPER)
|
|
289
|
+
? [toApp(UserModule.RECRUIT, routes.recruit.root.to, 'ระบบการสรรหาบุคลากร')]
|
|
651
290
|
: []),
|
|
652
|
-
|
|
653
291
|
])
|
|
654
292
|
|
|
655
|
-
const legalApps = computed(() => [
|
|
656
|
-
...(
|
|
657
|
-
? [
|
|
658
|
-
{
|
|
659
|
-
name: 'Contract',
|
|
660
|
-
logo: '/admin/contract.png',
|
|
661
|
-
label: 'Contract',
|
|
662
|
-
description: 'สร้างและจัดการสัญญา',
|
|
663
|
-
to: routes.legal.dashboard.to,
|
|
664
|
-
status: StatusPortal.ACTIVE,
|
|
665
|
-
},
|
|
666
|
-
]
|
|
293
|
+
const legalApps = computed<IPortalApp[]>(() => [
|
|
294
|
+
...(can(UserModule.CONTRACT, ...USER_ADMIN_SUPER)
|
|
295
|
+
? [toApp(UserModule.CONTRACT, routes.legal.dashboard.to, 'สร้างและจัดการสัญญา')]
|
|
667
296
|
: []),
|
|
668
297
|
])
|
|
298
|
+
|
|
299
|
+
const categories = computed<IPortalCategory[]>(() =>
|
|
300
|
+
[
|
|
301
|
+
{
|
|
302
|
+
key: 'daily',
|
|
303
|
+
title: 'Daily work Apps',
|
|
304
|
+
apps: dailyApps.value,
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
key: 'management',
|
|
308
|
+
title: 'Management',
|
|
309
|
+
apps: managementApps.value,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
key: 'finance',
|
|
313
|
+
title: 'Finance',
|
|
314
|
+
apps: financeApps.value,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
key: 'people',
|
|
318
|
+
title: 'People',
|
|
319
|
+
apps: peopleApps.value,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
key: 'legal',
|
|
323
|
+
title: 'Legal',
|
|
324
|
+
apps: legalApps.value,
|
|
325
|
+
},
|
|
326
|
+
].filter((category) => category.apps.length > 0),
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
const filteredCategories = computed<IPortalCategory[]>(() => {
|
|
330
|
+
const keyword = search.value.trim().toLowerCase()
|
|
331
|
+
|
|
332
|
+
if (!keyword) {
|
|
333
|
+
return categories.value
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return categories.value
|
|
337
|
+
.map((category) => ({
|
|
338
|
+
...category,
|
|
339
|
+
apps: category.apps.filter((app) =>
|
|
340
|
+
app.label.toLowerCase().includes(keyword)
|
|
341
|
+
|| app.description.toLowerCase().includes(keyword),
|
|
342
|
+
),
|
|
343
|
+
}))
|
|
344
|
+
.filter((category) => category.apps.length > 0)
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
const gridColsClass = 'grid-cols-3 sm:grid-cols-4 lg:grid-cols-6 xl:grid-cols-7'
|
|
669
348
|
</script>
|