itsm-widget 1.0.4 → 1.0.6
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/ANGULAR-INSTALLATION.md +303 -115
- package/API-SERVICE.md +130 -6
- package/README.md +104 -14
- package/dist/itsm-widget-config.json +1 -1
- package/dist/itsm-widget.min.css +1 -1
- package/dist/itsm-widget.min.js +1 -1
- package/package.json +1 -1
package/ANGULAR-INSTALLATION.md
CHANGED
|
@@ -1,6 +1,59 @@
|
|
|
1
|
-
# Hướng dẫn
|
|
1
|
+
# Hướng dẫn Tích hợp ITSM Widget vào Angular
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tài liệu này hướng dẫn chi tiết cách tích hợp ITSM Widget vào ứng dụng Angular (mọi version).
|
|
4
|
+
|
|
5
|
+
## 📋 Mục lục
|
|
6
|
+
|
|
7
|
+
1. [Tổng quan](#tổng-quan)
|
|
8
|
+
2. [Yêu cầu](#yêu-cầu)
|
|
9
|
+
3. [Cài đặt](#cài-đặt)
|
|
10
|
+
4. [Cấu hình](#cấu-hình)
|
|
11
|
+
5. [Khởi tạo Widget](#khởi-tạo-widget)
|
|
12
|
+
6. [Tích hợp với Auth Service](#tích-hợp-với-auth-service)
|
|
13
|
+
7. [Troubleshooting](#troubleshooting)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 📖 Tổng quan
|
|
18
|
+
|
|
19
|
+
ITSM Widget là plugin JavaScript standalone cho phép người dùng tạo phiếu yêu cầu ITSM trực tiếp từ ứng dụng Angular. Widget hiển thị icon nổi ở góc màn hình và mở modal form khi được click.
|
|
20
|
+
|
|
21
|
+
### Tính năng
|
|
22
|
+
|
|
23
|
+
- ✅ Icon nổi ở góc màn hình (có thể tùy chỉnh vị trí, màu sắc)
|
|
24
|
+
- ✅ Form tạo phiếu yêu cầu với các trường:
|
|
25
|
+
- Tiêu đề, Mô tả (rich text editor)
|
|
26
|
+
- **Danh mục dịch vụ** (tải từ API, có filter tìm kiếm)
|
|
27
|
+
- **Dịch vụ con** (tải từ API theo danh mục, có filter tìm kiếm)
|
|
28
|
+
- **Chi tiết dịch vụ** (tải từ API theo dịch vụ con, có filter tìm kiếm)
|
|
29
|
+
- Mức độ ưu tiên, Loại hỗ trợ, Loại thiết bị
|
|
30
|
+
- Thông tin liên hệ, Tệp đính kèm
|
|
31
|
+
- ✅ Tự động gọi Backend API để lấy dữ liệu categories/subcategories/items
|
|
32
|
+
- ✅ Responsive, hỗ trợ mobile
|
|
33
|
+
|
|
34
|
+
### Kiến trúc
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Angular App → ITSM Widget → Backend API → ITSM API
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **Angular App**: Hiển thị widget và truyền JWT token
|
|
41
|
+
- **ITSM Widget**: Xử lý UI và gọi Backend API
|
|
42
|
+
- **Backend API**: Xử lý business logic và gọi ITSM API
|
|
43
|
+
- **ITSM API**: Hệ thống ITSM thực tế
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ✅ Yêu cầu
|
|
48
|
+
|
|
49
|
+
- Angular (bất kỳ version nào)
|
|
50
|
+
- Modern browser (Chrome, Firefox, Edge, Safari)
|
|
51
|
+
- Backend API đã được cấu hình và chạy
|
|
52
|
+
- JWT token từ hệ thống authentication của bạn
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 📦 Cài đặt
|
|
4
57
|
|
|
5
58
|
### Cách 1: Cài đặt qua npm (Khuyến nghị)
|
|
6
59
|
|
|
@@ -36,26 +89,21 @@ Thêm vào `scripts` và `styles`:
|
|
|
36
89
|
}
|
|
37
90
|
```
|
|
38
91
|
|
|
39
|
-
**
|
|
40
|
-
|
|
41
|
-
---
|
|
92
|
+
**Lưu ý:** Thay `your-project-name` bằng tên project của bạn.
|
|
42
93
|
|
|
43
94
|
### Cách 2: Copy files thủ công
|
|
44
95
|
|
|
45
96
|
**Bước 1: Copy files vào project**
|
|
46
97
|
|
|
47
|
-
Copy
|
|
98
|
+
Copy các file sau từ `dist/` của widget vào Angular project:
|
|
48
99
|
|
|
49
100
|
```
|
|
50
101
|
src/assets/
|
|
51
102
|
├── itsm-widget.min.js
|
|
52
|
-
|
|
103
|
+
├── itsm-widget.min.css
|
|
104
|
+
└── itsm-widget-config.json
|
|
53
105
|
```
|
|
54
106
|
|
|
55
|
-
**Cách copy:**
|
|
56
|
-
- Copy `dist/itsm-widget.min.js` → `src/assets/itsm-widget.min.js`
|
|
57
|
-
- Copy `dist/itsm-widget.min.css` → `src/assets/itsm-widget.min.css`
|
|
58
|
-
|
|
59
107
|
**Bước 2: Cập nhật `angular.json`**
|
|
60
108
|
|
|
61
109
|
Thêm scripts và styles vào `angular.json`:
|
|
@@ -73,6 +121,10 @@ Thêm scripts và styles vào `angular.json`:
|
|
|
73
121
|
"styles": [
|
|
74
122
|
"src/styles.css",
|
|
75
123
|
"src/assets/itsm-widget.min.css"
|
|
124
|
+
],
|
|
125
|
+
"assets": [
|
|
126
|
+
"src/favicon.ico",
|
|
127
|
+
"src/assets"
|
|
76
128
|
]
|
|
77
129
|
}
|
|
78
130
|
}
|
|
@@ -82,39 +134,40 @@ Thêm scripts và styles vào `angular.json`:
|
|
|
82
134
|
}
|
|
83
135
|
```
|
|
84
136
|
|
|
85
|
-
**Lưu ý:** Thay `your-project-name` bằng tên project của bạn.
|
|
86
|
-
|
|
87
137
|
---
|
|
88
138
|
|
|
89
|
-
## ⚙️ Cấu hình
|
|
139
|
+
## ⚙️ Cấu hình
|
|
90
140
|
|
|
91
|
-
### Bước
|
|
141
|
+
### Bước quan trọng: Cấu hình `itsm-widget-config.json` (BẮT BUỘC)
|
|
92
142
|
|
|
93
|
-
|
|
143
|
+
**⚠️ QUAN TRỌNG:** File `itsm-widget-config.json` là **BẮT BUỘC** để widget hoạt động. Widget sẽ tự động load `apiBaseUrl` từ file này.
|
|
94
144
|
|
|
95
145
|
**Cách 1: Copy từ node_modules (nếu dùng npm install)**
|
|
146
|
+
|
|
96
147
|
```bash
|
|
97
148
|
# Copy file config từ package vào assets
|
|
98
149
|
cp node_modules/itsm-widget/dist/itsm-widget-config.json src/assets/itsm-widget-config.json
|
|
99
150
|
```
|
|
100
151
|
|
|
101
152
|
**Cách 2: Copy thủ công**
|
|
153
|
+
|
|
102
154
|
1. Lấy file `itsm-widget-config.json` từ `node_modules/itsm-widget/dist/` hoặc từ package
|
|
103
155
|
2. Copy vào `src/assets/itsm-widget-config.json`
|
|
104
|
-
3. Chỉnh sửa giá trị `apiBaseUrl
|
|
156
|
+
3. **Chỉnh sửa giá trị `apiBaseUrl`** theo môi trường của bạn:
|
|
105
157
|
|
|
106
158
|
```json
|
|
107
159
|
{
|
|
108
|
-
"apiBaseUrl": "http://localhost:
|
|
160
|
+
"apiBaseUrl": "http://localhost:2411"
|
|
109
161
|
}
|
|
110
162
|
```
|
|
111
163
|
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
164
|
+
**Lưu ý:**
|
|
165
|
+
- Thay `http://localhost:2411` bằng URL thực tế của Backend API
|
|
166
|
+
- File config **PHẢI** nằm trong `src/assets/` để Angular có thể serve
|
|
167
|
+
- Widget sẽ tự động tìm file ở: `/assets/itsm-widget-config.json`
|
|
168
|
+
- **KHÔNG** truyền `apiBaseUrl` vào constructor - nó sẽ bị bỏ qua
|
|
116
169
|
|
|
117
|
-
###
|
|
170
|
+
### Đảm bảo assets được copy vào build
|
|
118
171
|
|
|
119
172
|
Kiểm tra `angular.json` có cấu hình assets đúng:
|
|
120
173
|
|
|
@@ -137,15 +190,16 @@ Kiểm tra `angular.json` có cấu hình assets đúng:
|
|
|
137
190
|
}
|
|
138
191
|
```
|
|
139
192
|
|
|
140
|
-
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 🚀 Khởi tạo Widget
|
|
141
196
|
|
|
142
197
|
Có 2 cách để khởi tạo widget:
|
|
143
198
|
|
|
144
|
-
|
|
199
|
+
### Cách 1: Khởi tạo trong `app.component.ts` (Đơn giản)
|
|
145
200
|
|
|
146
201
|
```typescript
|
|
147
202
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
148
|
-
import { environment } from '../environments/environment';
|
|
149
203
|
|
|
150
204
|
declare var ITSMWidget: any;
|
|
151
205
|
|
|
@@ -159,12 +213,15 @@ export class AppComponent implements OnInit, OnDestroy {
|
|
|
159
213
|
private itsmWidgetInstance: any;
|
|
160
214
|
|
|
161
215
|
ngOnInit() {
|
|
216
|
+
// Lấy JWT token từ auth service hoặc storage
|
|
217
|
+
const token = this.getAuthToken(); // Implement method này
|
|
218
|
+
|
|
162
219
|
// Khởi tạo ITSM Widget
|
|
163
|
-
if (typeof ITSMWidget !== 'undefined') {
|
|
220
|
+
if (token && typeof ITSMWidget !== 'undefined') {
|
|
164
221
|
this.itsmWidgetInstance = new ITSMWidget({
|
|
165
|
-
// apiBaseUrl
|
|
166
|
-
// KHÔNG
|
|
167
|
-
authToken:
|
|
222
|
+
// apiBaseUrl BẮT BUỘC phải có trong file itsm-widget-config.json
|
|
223
|
+
// KHÔNG truyền apiBaseUrl vào constructor - nó sẽ bị bỏ qua
|
|
224
|
+
authToken: token, // JWT token từ đăng nhập thành công
|
|
168
225
|
position: 'bottom-right',
|
|
169
226
|
iconText: '📝',
|
|
170
227
|
iconColor: '#007bff',
|
|
@@ -174,12 +231,24 @@ export class AppComponent implements OnInit, OnDestroy {
|
|
|
174
231
|
onSuccess: (response: any) => {
|
|
175
232
|
console.log('ITSM Request submitted:', response);
|
|
176
233
|
// Hiển thị thông báo thành công
|
|
177
|
-
|
|
234
|
+
const requestId = response?.request?.id || 'N/A';
|
|
235
|
+
alert(`Phiếu yêu cầu đã được gửi thành công!\nID: ${requestId}`);
|
|
236
|
+
},
|
|
237
|
+
onError: (error: any) => {
|
|
238
|
+
console.error('Error submitting request:', error);
|
|
239
|
+
alert('Có lỗi xảy ra khi gửi yêu cầu. Vui lòng thử lại!');
|
|
178
240
|
}
|
|
179
241
|
});
|
|
180
242
|
|
|
181
243
|
// Lưu instance vào window để có thể access từ nơi khác
|
|
182
244
|
(window as any).itsmWidgetInstance = this.itsmWidgetInstance;
|
|
245
|
+
} else {
|
|
246
|
+
if (!token) {
|
|
247
|
+
console.warn('ITSM Widget: JWT token not found');
|
|
248
|
+
}
|
|
249
|
+
if (typeof ITSMWidget === 'undefined') {
|
|
250
|
+
console.error('ITSM Widget: Script not loaded');
|
|
251
|
+
}
|
|
183
252
|
}
|
|
184
253
|
}
|
|
185
254
|
|
|
@@ -187,18 +256,27 @@ export class AppComponent implements OnInit, OnDestroy {
|
|
|
187
256
|
// Cleanup widget khi component bị destroy
|
|
188
257
|
if (this.itsmWidgetInstance) {
|
|
189
258
|
this.itsmWidgetInstance.destroy();
|
|
259
|
+
this.itsmWidgetInstance = null;
|
|
190
260
|
}
|
|
191
261
|
}
|
|
262
|
+
|
|
263
|
+
private getAuthToken(): string {
|
|
264
|
+
// Lấy token từ auth service, localStorage, hoặc sessionStorage
|
|
265
|
+
// Ví dụ:
|
|
266
|
+
// return this.authService.getToken();
|
|
267
|
+
// return localStorage.getItem('jwt_token') || '';
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
192
270
|
}
|
|
193
271
|
```
|
|
194
272
|
|
|
195
|
-
|
|
273
|
+
### Cách 2: Tạo Service riêng (Khuyến nghị cho project lớn)
|
|
196
274
|
|
|
197
275
|
**1. Tạo service:** `src/app/services/itsm-widget.service.ts`
|
|
198
276
|
|
|
199
277
|
```typescript
|
|
200
278
|
import { Injectable, OnDestroy } from '@angular/core';
|
|
201
|
-
import {
|
|
279
|
+
import { AuthService } from './auth.service'; // Import auth service của bạn
|
|
202
280
|
|
|
203
281
|
declare var ITSMWidget: any;
|
|
204
282
|
|
|
@@ -208,44 +286,81 @@ declare var ITSMWidget: any;
|
|
|
208
286
|
export class ItsmWidgetService implements OnDestroy {
|
|
209
287
|
private widgetInstance: any;
|
|
210
288
|
|
|
211
|
-
constructor() {
|
|
289
|
+
constructor(private authService: AuthService) {
|
|
212
290
|
this.init();
|
|
213
291
|
}
|
|
214
292
|
|
|
215
293
|
private init(): void {
|
|
216
294
|
if (typeof ITSMWidget !== 'undefined' && !this.widgetInstance) {
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
295
|
+
const token = this.authService.getToken(); // Lấy token từ auth service
|
|
296
|
+
|
|
297
|
+
if (token) {
|
|
298
|
+
this.widgetInstance = new ITSMWidget({
|
|
299
|
+
// apiBaseUrl BẮT BUỘC phải có trong file itsm-widget-config.json
|
|
300
|
+
// KHÔNG truyền apiBaseUrl vào constructor - nó sẽ bị bỏ qua
|
|
301
|
+
authToken: token,
|
|
302
|
+
position: 'bottom-right',
|
|
303
|
+
iconText: '📝',
|
|
304
|
+
iconColor: '#007bff',
|
|
305
|
+
title: 'Tạo phiếu yêu cầu ITSM',
|
|
306
|
+
requesterName: this.getCurrentUser(),
|
|
307
|
+
site: '', // Optional
|
|
308
|
+
onSuccess: (response: any) => {
|
|
309
|
+
console.log('ITSM Request submitted:', response);
|
|
310
|
+
// Có thể emit event hoặc hiển thị notification ở đây
|
|
311
|
+
this.showSuccessNotification(response);
|
|
312
|
+
},
|
|
313
|
+
onError: (error: any) => {
|
|
314
|
+
console.error('Error submitting request:', error);
|
|
315
|
+
this.showErrorNotification(error);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
232
318
|
|
|
233
|
-
|
|
319
|
+
(window as any).itsmWidgetInstance = this.widgetInstance;
|
|
320
|
+
} else {
|
|
321
|
+
console.warn('ITSM Widget: Token not found');
|
|
322
|
+
}
|
|
234
323
|
}
|
|
235
324
|
}
|
|
236
325
|
|
|
237
326
|
private getCurrentUser(): string {
|
|
238
|
-
// Lấy tên user từ auth service
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
327
|
+
// Lấy tên user từ auth service
|
|
328
|
+
const user = this.authService.getCurrentUser();
|
|
329
|
+
return user?.name || user?.username || '';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private showSuccessNotification(response: any): void {
|
|
333
|
+
// Tích hợp với notification service của bạn
|
|
334
|
+
// Ví dụ: this.notificationService.showSuccess('Yêu cầu đã được gửi thành công!');
|
|
335
|
+
const requestId = response?.request?.id || 'N/A';
|
|
336
|
+
alert(`Phiếu yêu cầu đã được gửi thành công!\nID: ${requestId}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private showErrorNotification(error: any): void {
|
|
340
|
+
// Tích hợp với notification service của bạn
|
|
341
|
+
alert('Có lỗi xảy ra khi gửi yêu cầu. Vui lòng thử lại!');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Update token khi user login/logout
|
|
346
|
+
*/
|
|
347
|
+
updateToken(token: string | null): void {
|
|
348
|
+
if (token && this.widgetInstance) {
|
|
349
|
+
this.widgetInstance.config.authToken = token;
|
|
350
|
+
} else if (!token && this.widgetInstance) {
|
|
351
|
+
this.destroy();
|
|
352
|
+
}
|
|
243
353
|
}
|
|
244
354
|
|
|
245
355
|
ngOnDestroy(): void {
|
|
356
|
+
this.destroy();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private destroy(): void {
|
|
246
360
|
if (this.widgetInstance) {
|
|
247
361
|
this.widgetInstance.destroy();
|
|
248
362
|
this.widgetInstance = null;
|
|
363
|
+
(window as any).itsmWidgetInstance = null;
|
|
249
364
|
}
|
|
250
365
|
}
|
|
251
366
|
}
|
|
@@ -273,83 +388,121 @@ export class AppComponent implements OnInit {
|
|
|
273
388
|
}
|
|
274
389
|
```
|
|
275
390
|
|
|
276
|
-
|
|
391
|
+
**3. Update token khi login/logout:**
|
|
277
392
|
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
|
|
393
|
+
```typescript
|
|
394
|
+
// Trong AuthService hoặc LoginComponent
|
|
395
|
+
import { ItsmWidgetService } from './services/itsm-widget.service';
|
|
396
|
+
|
|
397
|
+
constructor(private itsmWidgetService: ItsmWidgetService) {}
|
|
281
398
|
|
|
282
|
-
|
|
283
|
-
|
|
399
|
+
onLogin(token: string) {
|
|
400
|
+
// ... login logic
|
|
401
|
+
this.itsmWidgetService.updateToken(token);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
onLogout() {
|
|
405
|
+
// ... logout logic
|
|
406
|
+
this.itsmWidgetService.updateToken(null);
|
|
407
|
+
}
|
|
284
408
|
```
|
|
285
409
|
|
|
286
|
-
|
|
410
|
+
---
|
|
287
411
|
|
|
288
|
-
|
|
412
|
+
## 🔐 Tích hợp với Auth Service
|
|
289
413
|
|
|
290
|
-
|
|
414
|
+
### Ví dụ với Angular JWT
|
|
291
415
|
|
|
292
416
|
```typescript
|
|
293
|
-
import {
|
|
417
|
+
import { JwtHelperService } from '@auth0/angular-jwt';
|
|
418
|
+
import { ItsmWidgetService } from './services/itsm-widget.service';
|
|
294
419
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
420
|
+
@Injectable({
|
|
421
|
+
providedIn: 'root'
|
|
422
|
+
})
|
|
423
|
+
export class AuthService {
|
|
424
|
+
private jwtHelper = new JwtHelperService();
|
|
425
|
+
|
|
426
|
+
constructor(private itsmWidgetService: ItsmWidgetService) {}
|
|
427
|
+
|
|
428
|
+
login(username: string, password: string): Observable<string> {
|
|
429
|
+
return this.http.post<{token: string}>('/api/auth/login', {username, password})
|
|
430
|
+
.pipe(
|
|
431
|
+
map(response => response.token),
|
|
432
|
+
tap(token => {
|
|
433
|
+
localStorage.setItem('jwt_token', token);
|
|
434
|
+
// Update widget token
|
|
435
|
+
this.itsmWidgetService.updateToken(token);
|
|
436
|
+
})
|
|
437
|
+
);
|
|
438
|
+
}
|
|
299
439
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
440
|
+
logout(): void {
|
|
441
|
+
localStorage.removeItem('jwt_token');
|
|
442
|
+
// Destroy widget
|
|
443
|
+
this.itsmWidgetService.updateToken(null);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
getToken(): string | null {
|
|
447
|
+
return localStorage.getItem('jwt_token');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
isAuthenticated(): boolean {
|
|
451
|
+
const token = this.getToken();
|
|
452
|
+
return token != null && !this.jwtHelper.isTokenExpired(token);
|
|
307
453
|
}
|
|
308
454
|
}
|
|
309
455
|
```
|
|
310
456
|
|
|
311
457
|
### Tích hợp với Notification Service
|
|
312
458
|
|
|
313
|
-
Có thể tích hợp với notification service của Angular:
|
|
314
|
-
|
|
315
459
|
```typescript
|
|
316
|
-
import {
|
|
460
|
+
import { MatSnackBar } from '@angular/material/snack-bar'; // Angular Material
|
|
461
|
+
// hoặc
|
|
462
|
+
import { ToastrService } from 'ngx-toastr'; // ngx-toastr
|
|
317
463
|
|
|
318
|
-
constructor(
|
|
464
|
+
constructor(
|
|
465
|
+
private snackBar: MatSnackBar,
|
|
466
|
+
// hoặc
|
|
467
|
+
private toastr: ToastrService
|
|
468
|
+
) {}
|
|
319
469
|
|
|
320
470
|
// Trong widget config:
|
|
321
471
|
onSuccess: (response: any) => {
|
|
322
|
-
|
|
472
|
+
const requestId = response?.request?.id || 'N/A';
|
|
473
|
+
this.snackBar.open(`Yêu cầu đã được gửi thành công! ID: ${requestId}`, 'Đóng', {
|
|
474
|
+
duration: 5000
|
|
475
|
+
});
|
|
476
|
+
// hoặc
|
|
477
|
+
this.toastr.success(`Yêu cầu đã được gửi thành công! ID: ${requestId}`);
|
|
323
478
|
}
|
|
324
479
|
```
|
|
325
480
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
Có thể thêm custom UDF fields:
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
new ITSMWidget({
|
|
332
|
-
// ... other config
|
|
333
|
-
udf_fields: {
|
|
334
|
-
udf_pick_301: 'Value 1',
|
|
335
|
-
udf_pick_302: 'Value 2',
|
|
336
|
-
// ... other fields
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
```
|
|
481
|
+
---
|
|
340
482
|
|
|
341
483
|
## 🐛 Troubleshooting
|
|
342
484
|
|
|
343
485
|
### Widget không hiển thị
|
|
344
486
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
487
|
+
**Kiểm tra:**
|
|
488
|
+
1. File JavaScript đã được load chưa? (Kiểm tra Network tab trong DevTools)
|
|
489
|
+
2. File `itsm-widget-config.json` đã có trong `src/assets/` chưa?
|
|
490
|
+
3. Có lỗi JavaScript trong Console không?
|
|
491
|
+
4. Token có hợp lệ không?
|
|
349
492
|
|
|
350
|
-
|
|
493
|
+
**Giải pháp:**
|
|
494
|
+
```typescript
|
|
495
|
+
// Thêm log để debug
|
|
496
|
+
console.log('ITSM Widget loaded:', typeof ITSMWidget);
|
|
497
|
+
console.log('Token:', token ? 'Found' : 'Not found');
|
|
498
|
+
console.log('Config file:', await fetch('/assets/itsm-widget-config.json').then(r => r.json()));
|
|
499
|
+
```
|
|
351
500
|
|
|
352
|
-
|
|
501
|
+
### Lỗi: Cannot find name 'ITSMWidget'
|
|
502
|
+
|
|
503
|
+
**Giải pháp:**
|
|
504
|
+
|
|
505
|
+
Thêm declare vào đầu file TypeScript:
|
|
353
506
|
|
|
354
507
|
```typescript
|
|
355
508
|
declare var ITSMWidget: any;
|
|
@@ -361,29 +514,64 @@ Hoặc tạo file `src/typings.d.ts`:
|
|
|
361
514
|
declare var ITSMWidget: any;
|
|
362
515
|
```
|
|
363
516
|
|
|
364
|
-
###
|
|
517
|
+
### Lỗi: Cannot load itsm-widget-config.json
|
|
365
518
|
|
|
366
|
-
|
|
519
|
+
**Nguyên nhân:** File config không tìm thấy hoặc không đúng format.
|
|
367
520
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
521
|
+
**Giải pháp:**
|
|
522
|
+
1. Đảm bảo file `itsm-widget-config.json` nằm trong `src/assets/`
|
|
523
|
+
2. Kiểm tra file có đúng format JSON không
|
|
524
|
+
3. Kiểm tra `angular.json` có cấu hình assets đúng không
|
|
525
|
+
4. Build lại project: `ng serve` hoặc `ng build`
|
|
526
|
+
|
|
527
|
+
### Lỗi 401 Unauthorized
|
|
528
|
+
|
|
529
|
+
**Nguyên nhân:** Token không hợp lệ hoặc đã hết hạn.
|
|
530
|
+
|
|
531
|
+
**Giải pháp:**
|
|
532
|
+
- Kiểm tra token có đúng format không
|
|
533
|
+
- Kiểm tra token có hết hạn không
|
|
534
|
+
- Đảm bảo token được gửi đúng trong header: `Authorization: Bearer {token}`
|
|
535
|
+
|
|
536
|
+
### Lỗi 403 Forbidden
|
|
537
|
+
|
|
538
|
+
**Nguyên nhân:** Token không có quyền truy cập.
|
|
539
|
+
|
|
540
|
+
**Kiểm tra:**
|
|
541
|
+
- Token có audience (`aud`) = "SPC.HTVTCNTT" hoặc "SPC.QLTK" không?
|
|
542
|
+
- Token có đúng roles không?
|
|
543
|
+
|
|
544
|
+
### Widget không load categories/subcategories/items
|
|
545
|
+
|
|
546
|
+
**Nguyên nhân:** Backend API không accessible hoặc lỗi CORS.
|
|
547
|
+
|
|
548
|
+
**Giải pháp:**
|
|
549
|
+
1. Kiểm tra Backend API đang chạy không
|
|
550
|
+
2. Kiểm tra `apiBaseUrl` trong `itsm-widget-config.json` đúng chưa
|
|
551
|
+
3. Kiểm tra CORS đã được cấu hình trong Backend chưa
|
|
552
|
+
4. Kiểm tra Network tab để xem API calls có thành công không
|
|
553
|
+
|
|
554
|
+
---
|
|
379
555
|
|
|
380
556
|
## 📝 Ví dụ hoàn chỉnh
|
|
381
557
|
|
|
382
558
|
Xem file [example.html](../example.html) để tham khảo cách sử dụng cơ bản.
|
|
383
559
|
|
|
560
|
+
---
|
|
561
|
+
|
|
384
562
|
## 📚 Tài liệu thêm
|
|
385
563
|
|
|
386
564
|
- [README.md](../README.md) - Tài liệu tổng quan
|
|
387
|
-
- [
|
|
565
|
+
- [CONFIG.md](../CONFIG.md) - Hướng dẫn cấu hình chi tiết
|
|
388
566
|
- [API-SERVICE.md](../API-SERVICE.md) - Tài liệu API Service
|
|
567
|
+
- [MVC-INTEGRATION.md](../MVC-INTEGRATION.md) - Hướng dẫn cho ASP.NET MVC
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## 💡 Tips
|
|
389
572
|
|
|
573
|
+
1. **Performance**: Widget chỉ load khi cần thiết, không ảnh hưởng đến performance của trang chính
|
|
574
|
+
2. **Security**: Luôn lưu JWT token một cách an toàn (HttpOnly cookie hoặc secure storage)
|
|
575
|
+
3. **Error Handling**: Luôn xử lý lỗi trong callback `onError` để cung cấp feedback cho người dùng
|
|
576
|
+
4. **Testing**: Test widget trên các browser khác nhau để đảm bảo tương thích
|
|
577
|
+
5. **Token Management**: Update widget token khi user login/logout để đảm bảo widget luôn có token hợp lệ
|