@finema/finework-layer 2.0.61 → 2.0.62

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 (47) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/app/app.vue +1 -1
  3. package/app/assets/css/main.css +5 -0
  4. package/app/components/HelpHub/Action.vue +28 -0
  5. package/app/components/HelpHub/ManualForm.vue +224 -0
  6. package/app/components/HelpHub/ManualViewer.vue +464 -0
  7. package/app/components/HelpHub/index.vue +116 -0
  8. package/app/composables/useCurrentModule.ts +9 -0
  9. package/app/constants/module-route.ts +48 -0
  10. package/app/loaders/manual/manual.ts +60 -0
  11. package/docs/help-hub.md +100 -0
  12. package/package.json +1 -1
  13. package/public/admin/asset.svg +15 -0
  14. package/public/admin/clock-in-admin-logo.svg +28 -0
  15. package/public/admin/clock-in-logo.svg +22 -0
  16. package/public/admin/contract.svg +36 -0
  17. package/public/admin/cost-sheet.svg +14 -0
  18. package/public/admin/document.svg +24 -0
  19. package/public/admin/efactoring.svg +18 -0
  20. package/public/admin/employee.svg +9 -0
  21. package/public/admin/evaluation.svg +24 -0
  22. package/public/admin/finesign.png +0 -0
  23. package/public/admin/guarantee.svg +20 -0
  24. package/public/admin/loan.svg +30 -0
  25. package/public/admin/newsletter.svg +18 -0
  26. package/public/admin/pmo-logo.svg +23 -0
  27. package/public/admin/pmo_delivery.svg +16 -0
  28. package/public/admin/pmo_presale_bidding.svg +25 -0
  29. package/public/admin/pmo_setup.svg +13 -0
  30. package/public/admin/pmo_warranty.svg +21 -0
  31. package/public/admin/presale_tool.svg +38 -0
  32. package/public/admin/purchase.svg +17 -0
  33. package/public/admin/quotation.svg +16 -0
  34. package/public/admin/recruit.svg +16 -0
  35. package/public/admin/report.svg +36 -0
  36. package/public/admin/request-admin.svg +39 -0
  37. package/public/admin/request.svg +37 -0
  38. package/public/admin/resource-allocation.svg +21 -0
  39. package/public/admin/resource.svg +15 -0
  40. package/public/admin/spider-web.png +0 -0
  41. package/public/admin/super-admin-logo.svg +17 -0
  42. package/public/admin/support.svg +14 -0
  43. package/public/admin/timesheet-admin-logo.svg +82 -0
  44. package/public/admin/timesheet-logo.svg +80 -0
  45. package/public/admin/todo.svg +16 -0
  46. package/public/admin/tor.svg +38 -0
  47. package/app/components/FeedbackTicket/index.vue +0 -27
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.62](https://gitlab.finema.co/finema/internal/finework/compare/2.0.61...2.0.62) (2026-09-15)
4
+
5
+ ### Bug Fixes
6
+
7
+ * disable manual viewer dismissible ([35780ce](https://gitlab.finema.co/finema/internal/finework/commit/35780ce41d2f542220b873340be5aacb5a5a8595))
8
+
3
9
  ## [2.0.61](https://gitlab.finema.co/finema/internal/finework/compare/2.0.60...2.0.61) (2026-09-11)
4
10
 
5
11
  ## [2.0.60](https://gitlab.finema.co/finema/internal/finework/compare/2.0.59...2.0.60) (2026-09-11)
package/app/app.vue CHANGED
@@ -6,6 +6,6 @@
6
6
  }"
7
7
  >
8
8
  <NuxtPage />
9
- <FeedbackTicket />
9
+ <HelpHub />
10
10
  </App>
11
11
  </template>
@@ -88,3 +88,8 @@ body {
88
88
  to { transform: translateY(-20px); opacity: 0; }
89
89
  }
90
90
 
91
+ @keyframes helpHubDialIn {
92
+ from { transform: translateY(6px); opacity: 0; }
93
+ to { transform: translateY(0); opacity: 1; }
94
+ }
95
+
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ class="group flex cursor-pointer items-center gap-3"
5
+ @click="emit('click')"
6
+ >
7
+ <span class="group-hover:text-primary-600 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm font-semibold text-gray-700 shadow-md transition">
8
+ {{ label }}
9
+ </span>
10
+ <span class="text-primary-600 flex size-11 items-center justify-center rounded-full bg-white shadow-lg ring-1 ring-gray-200 transition group-hover:scale-105">
11
+ <Icon
12
+ :name="icon"
13
+ class="size-5"
14
+ />
15
+ </span>
16
+ </button>
17
+ </template>
18
+
19
+ <script lang="ts" setup>
20
+ const emit = defineEmits<{
21
+ (e: 'click'): void
22
+ }>()
23
+
24
+ defineProps<{
25
+ label: string
26
+ icon: string
27
+ }>()
28
+ </script>
@@ -0,0 +1,224 @@
1
+ <template>
2
+ <div class="flex h-full flex-col">
3
+ <div class="flex-1 overflow-y-auto px-5 py-4">
4
+ <FormFields
5
+ :form="form"
6
+ :options="formFields"
7
+ class="flex flex-col gap-4 space-y-0"
8
+ />
9
+ </div>
10
+
11
+ <div class="flex gap-3 border-t border-gray-100 p-4">
12
+ <Button
13
+ variant="outline"
14
+ color="neutral"
15
+ class="flex-1 justify-center"
16
+ @click="emit('cancel')"
17
+ >
18
+ ยกเลิก
19
+ </Button>
20
+ <Button
21
+ class="flex-1 justify-center"
22
+ :loading="saveLoader.status.value.isLoading"
23
+ :disabled="saveLoader.status.value.isLoading"
24
+ @click="onSubmit"
25
+ >
26
+ {{ isEdit ? 'บันทึก' : 'เพิ่มคู่มือ' }}
27
+ </Button>
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <script lang="ts" setup>
33
+ import type { IManual } from '#layer/app/loaders/manual/manual'
34
+ import { useManualAddLoader, useManualUpdateLoader } from '#layer/app/loaders/manual/manual'
35
+ import { Permission } from '#layer/app/composables/useAuth'
36
+ import type { UserModule } from '#layer/app/composables/useAuth'
37
+
38
+ const emit = defineEmits<{
39
+ (e: 'saved'): void
40
+ (e: 'cancel'): void
41
+ }>()
42
+
43
+ const props = defineProps<{
44
+ manual?: IManual | null
45
+ defaultApp?: string
46
+ }>()
47
+
48
+ const auth = useAuth()
49
+ const noti = useNotification()
50
+
51
+ const isEdit = computed(() => !!props.manual)
52
+
53
+ const addLoader = useManualAddLoader()
54
+ const updateLoader = useManualUpdateLoader()
55
+ const saveLoader = computed(() => (isEdit.value ? updateLoader : addLoader))
56
+
57
+ const manageableApps = computed(() =>
58
+ UserModuleOptions.filter(
59
+ (opt) =>
60
+ auth.isSuperAdmin.value
61
+ || auth.hasPermission(opt.value as UserModule, Permission.SUPER),
62
+ ),
63
+ )
64
+
65
+ const form = useForm({
66
+ validationSchema: toTypedSchema(
67
+ v.object({
68
+ app: v.pipe(v.string(), v.minLength(1, 'เลือกแอพ')),
69
+ name: v.pipe(v.string(), v.minLength(1, 'ใส่ชื่อคู่มือ')),
70
+ description: v.nullish(v.string()),
71
+ file: v.nullish(
72
+ v.object({
73
+ id: v.string(),
74
+ url: v.string(),
75
+ name: v.string(),
76
+ path: v.string(),
77
+ size: v.number(),
78
+ }),
79
+ ),
80
+ }),
81
+ ),
82
+ })
83
+
84
+ const applyInitial = () => {
85
+ if (props.manual) {
86
+ form.resetForm({
87
+ values: {
88
+ app: props.manual.app,
89
+ name: props.manual.name,
90
+ description: props.manual.description ?? '',
91
+ file: props.manual.file
92
+ ? {
93
+ id: props.manual.file.id,
94
+ url: props.manual.file.url,
95
+ name: props.manual.file.name,
96
+ path: props.manual.file.path,
97
+ size: props.manual.file.size,
98
+ }
99
+ : null,
100
+ },
101
+ })
102
+
103
+ return
104
+ }
105
+
106
+ form.resetForm({
107
+ values: {
108
+ app: props.defaultApp && manageableApps.value.some((a) => a.value === props.defaultApp)
109
+ ? props.defaultApp
110
+ : (manageableApps.value[0]?.value ?? ''),
111
+ name: '',
112
+ description: '',
113
+ file: null,
114
+ },
115
+ })
116
+ }
117
+
118
+ applyInitial()
119
+ watch(() => props.manual?.id, applyInitial)
120
+
121
+ const formFields = createFormFields(() => [
122
+ {
123
+ type: INPUT_TYPES.SELECT,
124
+ class: 'w-full',
125
+ props: {
126
+ form,
127
+ name: 'app',
128
+ label: 'แอพ',
129
+ placeholder: 'เลือกแอพ',
130
+ options: manageableApps.value,
131
+ required: true,
132
+ disabled: isEdit.value,
133
+ },
134
+ },
135
+ {
136
+ type: INPUT_TYPES.TEXT,
137
+ class: 'w-full',
138
+ props: {
139
+ form,
140
+ name: 'name',
141
+ label: 'ชื่อคู่มือ',
142
+ placeholder: 'เช่น เริ่มต้นใช้งาน, การอนุมัติ',
143
+ required: true,
144
+ },
145
+ },
146
+ {
147
+ type: INPUT_TYPES.TEXTAREA,
148
+ class: 'w-full',
149
+ props: {
150
+ form,
151
+ name: 'description',
152
+ label: 'คำอธิบายสั้น ๆ (ไม่บังคับ)',
153
+ placeholder: 'สรุปว่าคู่มือนี้ครอบคลุมเรื่องอะไร',
154
+ rows: 2,
155
+ },
156
+ },
157
+ {
158
+ type: INPUT_TYPES.UPLOAD_DROPZONE_AUTO,
159
+ class: 'w-full',
160
+ props: {
161
+ form,
162
+ name: 'file',
163
+ label: 'ไฟล์ PDF',
164
+ placeholder: 'PDF เท่านั้น (สูงสุด 20MB)',
165
+ requestOptions: useRequestOptions().filePublic(FileAppType.COMMON),
166
+ accept: '.pdf',
167
+ maxSize: 20480,
168
+ required: true,
169
+ },
170
+ on: {
171
+ change: () => {},
172
+ success: () => {},
173
+ delete: () => form.setFieldValue('file', null),
174
+ },
175
+ },
176
+ ])
177
+
178
+ const onSubmit = form.handleSubmit((values) => {
179
+ const fileId = (values.file as { id?: string } | null)?.id
180
+
181
+ if (!fileId) {
182
+ form.setFieldError('file', 'อัปโหลดไฟล์ PDF ก่อน')
183
+
184
+ return
185
+ }
186
+
187
+ const data = {
188
+ app: values.app,
189
+ name: values.name,
190
+ description: values.description || null,
191
+ file_id: fileId,
192
+ }
193
+
194
+ if (isEdit.value && props.manual) {
195
+ updateLoader.run({
196
+ urlBind: {
197
+ id: props.manual.id,
198
+ },
199
+ data,
200
+ })
201
+
202
+ return
203
+ }
204
+
205
+ addLoader.run({
206
+ data,
207
+ })
208
+ })
209
+
210
+ useWatchTrue(() => saveLoader.value.status.value.isSuccess, () => {
211
+ noti.success({
212
+ title: isEdit.value ? 'บันทึกคู่มือแล้ว' : 'เพิ่มคู่มือแล้ว',
213
+ })
214
+
215
+ emit('saved')
216
+ })
217
+
218
+ useWatchTrue(() => saveLoader.value.status.value.isError, () => {
219
+ noti.error({
220
+ title: 'บันทึกไม่สำเร็จ',
221
+ description: StringHelper.getError(saveLoader.value.status.value.errorData, 'ลองใหม่อีกครั้ง'),
222
+ })
223
+ })
224
+ </script>