@finema/finework-layer 2.0.62 → 2.0.64

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.64](https://gitlab.finema.co/finema/internal/finework/compare/2.0.63...2.0.64) (2026-09-15)
4
+
5
+ ### Bug Fixes
6
+
7
+ * fetch PDF as blob to bypass X-Frame-Options restrictions and add loading states ([4584b67](https://gitlab.finema.co/finema/internal/finework/commit/4584b67b555b19a3d2f10a25f1d89f9340dec134))
8
+
9
+ ## [2.0.63](https://gitlab.finema.co/finema/internal/finework/compare/2.0.62...2.0.63) (2026-09-15)
10
+
3
11
  ## [2.0.62](https://gitlab.finema.co/finema/internal/finework/compare/2.0.61...2.0.62) (2026-09-15)
4
12
 
5
13
  ### Bug Fixes
@@ -137,7 +137,7 @@
137
137
  :alt="group.label"
138
138
  class="size-4 rounded"
139
139
  />
140
- <span class="text-xs font-semibold tracking-wide text-gray-500 uppercase">
140
+ <span class="text-xs font-semibold">
141
141
  {{ group.label }}
142
142
  </span>
143
143
  <span
@@ -246,9 +246,9 @@
246
246
  class="relative flex-1 overflow-hidden bg-gray-600"
247
247
  >
248
248
  <iframe
249
- v-if="current?.file"
250
- :key="current.file.url"
251
- :src="`${current.file.url}#view=FitH`"
249
+ v-if="viewerBlobUrl"
250
+ :key="viewerBlobUrl"
251
+ :src="`${viewerBlobUrl}#view=FitH`"
252
252
  title="User manual"
253
253
  class="size-full border-0"
254
254
  />
@@ -256,7 +256,7 @@
256
256
  v-else
257
257
  class="flex h-full items-center justify-center bg-white text-sm text-gray-500"
258
258
  >
259
- ไม่พบไฟล์คู่มือ
259
+ {{ viewerError ? 'ไม่สามารถโหลดไฟล์ได้' : (current?.file ? 'กำลังโหลด...' : 'ไม่พบไฟล์คู่มือ') }}
260
260
  </div>
261
261
  </div>
262
262
  </div>
@@ -333,6 +333,41 @@ const current = computed<IManual | null>(
333
333
  () => props.manuals.find((m) => m.id === selectedId.value) ?? null,
334
334
  )
335
335
 
336
+ // The API sets X-Frame-Options/CSP frame-ancestors that block embedding the
337
+ // PDF URL directly in an <iframe> across subdomains. Fetching the bytes and
338
+ // rendering via a blob: URL sidesteps that, since it isn't a framed request.
339
+ const viewerBlobUrl = ref<string | null>(null)
340
+ const viewerError = ref(false)
341
+
342
+ const revokeViewerBlobUrl = () => {
343
+ if (viewerBlobUrl.value) {
344
+ URL.revokeObjectURL(viewerBlobUrl.value)
345
+ viewerBlobUrl.value = null
346
+ }
347
+ }
348
+
349
+ watch(() => current.value?.file?.url, async (url) => {
350
+ revokeViewerBlobUrl()
351
+ viewerError.value = false
352
+
353
+ if (!url) return
354
+
355
+ try {
356
+ const res = await fetch(url)
357
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
358
+
359
+ const blob = await res.blob()
360
+
361
+ viewerBlobUrl.value = URL.createObjectURL(blob)
362
+ } catch {
363
+ viewerError.value = true
364
+ }
365
+ }, {
366
+ immediate: true,
367
+ })
368
+
369
+ onBeforeUnmount(revokeViewerBlobUrl)
370
+
336
371
  const canManage = (app: string) =>
337
372
  auth.isSuperAdmin.value || auth.hasPermission(app as UserModule, Permission.SUPER)
338
373
 
@@ -116,63 +116,63 @@ export enum UserModule {
116
116
  }
117
117
 
118
118
  export const UserModuleLabel = {
119
+ [UserModule.ASSET]: 'Asset',
119
120
  [UserModule.CHECKIN]: 'Clock-In',
120
- [UserModule.TIMESHEET]: 'Timesheet',
121
- [UserModule.TODO]: 'Todo',
122
- [UserModule.PMO]: 'PMO',
123
- [UserModule.REQUEST]: 'Request',
124
- [UserModule.SETTING]: 'Setting',
125
- [UserModule.QUOTATION]: 'Quotation',
126
- [UserModule.COSTSHEET]: 'Cost Sheet',
127
- [UserModule.NEWSLETTER]: 'Newsletter',
128
121
  [UserModule.CONTRACT]: 'Contract',
129
- [UserModule.EVALUATION]: 'Evaluation',
130
- [UserModule.EMPLOYEE]: 'Employee',
131
- [UserModule.ASSET]: 'Asset',
132
- [UserModule.RECRUIT]: 'Recruit',
133
- [UserModule.LG]: 'งบค้ำประกัน',
122
+ [UserModule.COSTSHEET]: 'Cost Sheet',
123
+ [UserModule.DOCUMENT]: 'Document',
134
124
  [UserModule.EFACTORING]: 'E-Factoring',
135
- [UserModule.RESOURCE]: 'Resource',
125
+ [UserModule.EMPLOYEE]: 'Employee',
126
+ [UserModule.EVALUATION]: 'Evaluation',
127
+ [UserModule.PMO_SETUP]: 'Lead in',
136
128
  [UserModule.LOAN]: 'Loan',
137
- [UserModule.PURCHASE]: 'Purchase',
129
+ [UserModule.NEWSLETTER]: 'Newsletter',
130
+ [UserModule.PMO]: 'PMO',
131
+ [UserModule.PMO_PRESALE_BIDDING]: 'Presale & Bidding',
138
132
  [UserModule.TOR]: 'Presales Tools',
133
+ [UserModule.PMO_DELIVERY]: 'Project Delivery',
134
+ [UserModule.PURCHASE]: 'Purchase',
135
+ [UserModule.QUOTATION]: 'Quotation',
136
+ [UserModule.RECRUIT]: 'Recruit',
137
+ [UserModule.REQUEST]: 'Request',
138
+ [UserModule.RESOURCE]: 'Resource',
139
+ [UserModule.RESOURCE_ALLOCATION]: 'Resource Allocation',
140
+ [UserModule.SETTING]: 'Setting',
139
141
  [UserModule.SUPPORT]: 'Support',
140
- [UserModule.PMO_SETUP]: 'Lead In',
141
- [UserModule.PMO_PRESALE_BIDDING]: 'Presale & Bidding',
142
- [UserModule.PMO_DELIVERY]: 'Delivery',
142
+ [UserModule.TIMESHEET]: 'Timesheet',
143
+ [UserModule.TODO]: 'Todo',
143
144
  [UserModule.PMO_WARRANTY]: 'Warranty',
144
- [UserModule.RESOURCE_ALLOCATION]: 'Resource Allocation',
145
- [UserModule.DOCUMENT]: 'Document',
145
+ [UserModule.LG]: 'งบค้ำประกัน',
146
146
  }
147
147
 
148
148
  export const UserModuleLogo = {
149
+ [UserModule.ASSET]: '/admin/asset.svg',
149
150
  [UserModule.CHECKIN]: '/admin/clock-in-logo.svg',
150
- [UserModule.TIMESHEET]: '/admin/timesheet-logo.svg',
151
- [UserModule.TODO]: '/admin/todo.svg',
152
- [UserModule.PMO]: '/admin/pmo-logo.svg',
153
- [UserModule.REQUEST]: '/admin/request.svg',
154
- [UserModule.SETTING]: '/admin/report.svg',
155
- [UserModule.QUOTATION]: '/admin/quotation.svg',
156
- [UserModule.COSTSHEET]: '/admin/cost-sheet.svg',
157
- [UserModule.NEWSLETTER]: '/admin/newsletter.svg',
158
151
  [UserModule.CONTRACT]: '/admin/contract.svg',
159
- [UserModule.EVALUATION]: '/admin/evaluation.svg',
160
- [UserModule.EMPLOYEE]: '/admin/employee.svg',
161
- [UserModule.ASSET]: '/admin/asset.svg',
162
- [UserModule.RECRUIT]: '/admin/recruit.svg',
163
- [UserModule.LG]: '/admin/guarantee.svg',
152
+ [UserModule.COSTSHEET]: '/admin/cost-sheet.svg',
153
+ [UserModule.DOCUMENT]: '/admin/document.svg',
164
154
  [UserModule.EFACTORING]: '/admin/efactoring.svg',
165
- [UserModule.RESOURCE]: '/admin/resource.svg',
166
- [UserModule.LOAN]: '/admin/loan.svg',
167
- [UserModule.PURCHASE]: '/admin/purchase.svg',
168
- [UserModule.TOR]: '/admin/presale_tool.svg',
169
- [UserModule.SUPPORT]: '/admin/support.svg',
155
+ [UserModule.EMPLOYEE]: '/admin/employee.svg',
156
+ [UserModule.EVALUATION]: '/admin/evaluation.svg',
170
157
  [UserModule.PMO_SETUP]: '/admin/pmo_setup.svg',
158
+ [UserModule.LOAN]: '/admin/loan.svg',
159
+ [UserModule.NEWSLETTER]: '/admin/newsletter.svg',
160
+ [UserModule.PMO]: '/admin/pmo-logo.svg',
171
161
  [UserModule.PMO_PRESALE_BIDDING]: '/admin/pmo_presale_bidding.svg',
162
+ [UserModule.TOR]: '/admin/presale_tool.svg',
172
163
  [UserModule.PMO_DELIVERY]: '/admin/pmo_delivery.svg',
173
- [UserModule.PMO_WARRANTY]: '/admin/pmo_warranty.svg',
164
+ [UserModule.PURCHASE]: '/admin/purchase.svg',
165
+ [UserModule.QUOTATION]: '/admin/quotation.svg',
166
+ [UserModule.RECRUIT]: '/admin/recruit.svg',
167
+ [UserModule.REQUEST]: '/admin/request.svg',
168
+ [UserModule.RESOURCE]: '/admin/resource.svg',
174
169
  [UserModule.RESOURCE_ALLOCATION]: '/admin/resource-allocation.svg',
175
- [UserModule.DOCUMENT]: '/admin/document.svg',
170
+ [UserModule.SETTING]: '/admin/report.svg',
171
+ [UserModule.SUPPORT]: '/admin/support.svg',
172
+ [UserModule.TIMESHEET]: '/admin/timesheet-logo.svg',
173
+ [UserModule.TODO]: '/admin/todo.svg',
174
+ [UserModule.PMO_WARRANTY]: '/admin/pmo_warranty.svg',
175
+ [UserModule.LG]: '/admin/guarantee.svg',
176
176
  }
177
177
 
178
178
  // Admin-console variants of a module (shown as their own app in the portal)
@@ -190,14 +190,16 @@ export const UserModuleAdminLogo = {
190
190
  [UserModule.SETTING]: '/admin/report.svg',
191
191
  }
192
192
 
193
- export const UserModuleOptions = Object.entries(UserModuleLabel).map(([value, label]) => ({
194
- value,
195
- label,
196
- avatar: {
197
- src: UserModuleLogo[value as keyof typeof UserModuleLogo],
198
- alt: label,
199
- },
200
- }))
193
+ export const UserModuleOptions = Object.entries(UserModuleLabel)
194
+ .map(([value, label]) => ({
195
+ value,
196
+ label,
197
+ avatar: {
198
+ src: UserModuleLogo[value as keyof typeof UserModuleLogo],
199
+ alt: label,
200
+ },
201
+ }))
202
+ .sort((a, b) => a.label.localeCompare(b.label))
201
203
 
202
204
  export interface IUserAccessLevel {
203
205
  used_id: string
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@finema/finework-layer",
3
3
  "type": "module",
4
- "version": "2.0.62",
4
+ "version": "2.0.64",
5
5
  "main": "./nuxt.config.ts",
6
6
  "scripts": {
7
7
  "dev": "nuxi dev .playground -o",