itsm-widget 1.0.5 → 1.0.7

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.
@@ -1,6 +1,59 @@
1
- # Hướng dẫn cài đặt ITSM Widget vào Angular Project
1
+ # Hướng dẫn Tích hợp ITSM Widget vào Angular
2
2
 
3
- ## 📋 Các bước cài đặt
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
- **Bước 3:** Xem tiếp phần "Khởi tạo widget" bên dưới.
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 2 files sau từ `dist/` của widget vào Angular project:
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
- └── itsm-widget.min.css
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,41 +134,116 @@ 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 (Áp dụng cho cả 2 cách)
139
+ ## ⚙️ Cấu hình
140
+
141
+ ### Bước quan trọng: Cấu hình `itsm-widget-config.json` (BẮT BUỘC)
142
+
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.
144
+
145
+ **Widget sẽ tự động tìm file ở các vị trí sau (theo thứ tự ưu tiên):**
146
+ 1. `/assets/itsm-widget-config.json` - Angular assets folder (khuyến nghị cho production)
147
+ 2. `/node_modules/itsm-widget/dist/itsm-widget-config.json` - node_modules (hoạt động trong development)
148
+ 3. Các vị trí khác...
149
+
150
+ **Cách 1: Sử dụng trực tiếp từ node_modules (Development - Khuyến nghị)**
151
+
152
+ Trong môi trường development (`ng serve`), widget có thể tự động tìm file từ `node_modules/itsm-widget/dist/itsm-widget-config.json` mà không cần copy.
153
+
154
+ **Chỉ cần chỉnh sửa file trong node_modules:**
155
+ ```bash
156
+ # Mở file để chỉnh sửa
157
+ code node_modules/itsm-widget/dist/itsm-widget-config.json
158
+ # hoặc
159
+ notepad node_modules\itsm-widget\dist\itsm-widget-config.json
160
+ ```
161
+
162
+ **Lưu ý:**
163
+ - File trong `node_modules` sẽ bị mất khi chạy `npm install` lại
164
+ - Chỉ hoạt động trong development mode (`ng serve`)
165
+ - **Không hoạt động trong production build** (`ng build --prod`)
90
166
 
91
- ### Bước 3: Copy file config (BẮT BUỘC)
167
+ **Cách 2: Copy vào assets (Production - Khuyến nghị)**
92
168
 
93
- **File `itsm-widget-config.json` là BẮT BUỘC** để widget hoạt động. Bạn cần copy file này vào `src/assets/`:
169
+ Để hoạt động trong production, copy file vào `src/assets/`:
94
170
 
95
- **Cách 1: Copy từ node_modules (nếu dùng npm install)**
96
171
  ```bash
97
172
  # Copy file config từ package vào assets
98
173
  cp node_modules/itsm-widget/dist/itsm-widget-config.json src/assets/itsm-widget-config.json
99
174
  ```
100
175
 
101
176
  **Cách 2: Copy thủ công**
177
+
102
178
  1. Lấy file `itsm-widget-config.json` từ `node_modules/itsm-widget/dist/` hoặc từ package
103
179
  2. Copy vào `src/assets/itsm-widget-config.json`
104
- 3. Chỉnh sửa giá trị `apiBaseUrl` theo nhu cầu:
180
+ 3. **Chỉnh sửa giá trị `apiBaseUrl`** theo môi trường của bạn:
105
181
 
106
182
  ```json
107
183
  {
108
- "apiBaseUrl": "http://localhost:9998"
184
+ "apiBaseUrl": "http://localhost:2411"
109
185
  }
110
186
  ```
111
187
 
112
- **⚠️ Lưu ý quan trọng:**
113
- - File config **BẮT BUỘC** phải có trong `src/assets/` để Angular thể serve file này
188
+ **Lưu ý:**
189
+ - Thay `http://localhost:2411` bằng URL thực tế của Backend API
190
+ - File config **PHẢI** nằm trong `src/assets/` để Angular có thể serve
114
191
  - Widget sẽ tự động tìm file ở: `/assets/itsm-widget-config.json` (ưu tiên nhất)
115
- - Sau khi copy, nhớ build lại project: `ng serve` hoặc `ng build`
192
+ - **KHÔNG** truyền `apiBaseUrl` vào constructor - sẽ bị bỏ qua
193
+
194
+ **⚠️ QUAN TRỌNG - Kiểm tra file đã được copy đúng:**
195
+
196
+ Sau khi copy file, kiểm tra:
197
+ 1. File có tồn tại tại `src/assets/itsm-widget-config.json` không?
198
+ 2. File có đúng format JSON không?
199
+ 3. Sau khi build (`ng serve` hoặc `ng build`), file có được serve tại `http://localhost:4200/assets/itsm-widget-config.json` không?
200
+
201
+ **Kiểm tra trong browser:**
202
+ - Mở DevTools → Network tab
203
+ - Reload trang
204
+ - Tìm request đến `itsm-widget-config.json`
205
+ - Nếu thấy 404, file chưa được copy đúng hoặc `angular.json` chưa cấu hình assets
206
+
207
+ ### Đảm bảo assets được copy vào build
208
+
209
+ **Cách 1: Copy thủ công (Đơn giản)**
210
+
211
+ Sau mỗi lần `npm install`, copy file vào `src/assets/`:
212
+ ```bash
213
+ cp node_modules/itsm-widget/dist/itsm-widget-config.json src/assets/itsm-widget-config.json
214
+ ```
215
+
216
+ **Cách 2: Tự động copy khi build (Khuyến nghị)**
217
+
218
+ Cấu hình `angular.json` để tự động copy file từ node_modules vào assets khi build:
219
+
220
+ ```json
221
+ {
222
+ "projects": {
223
+ "your-project-name": {
224
+ "architect": {
225
+ "build": {
226
+ "options": {
227
+ "assets": [
228
+ "src/favicon.ico",
229
+ "src/assets",
230
+ {
231
+ "glob": "itsm-widget-config.json",
232
+ "input": "node_modules/itsm-widget/dist",
233
+ "output": "/assets"
234
+ }
235
+ ]
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+ ```
116
243
 
117
- ### Bước 4: Đảm bảo assets được copy vào build
244
+ Với cấu hình này, Angular sẽ tự động copy file `itsm-widget-config.json` từ `node_modules/itsm-widget/dist/` vào `assets/` khi build, không cần copy thủ công.
118
245
 
119
- Kiểm tra `angular.json` có cấu hình assets đúng:
246
+ **Kiểm tra `angular.json` có cấu hình assets cơ bản:**
120
247
 
121
248
  ```json
122
249
  {
@@ -137,15 +264,16 @@ Kiểm tra `angular.json` có cấu hình assets đúng:
137
264
  }
138
265
  ```
139
266
 
140
- ## 🚀 Khởi tạo widget
267
+ ---
268
+
269
+ ## 🚀 Khởi tạo Widget
141
270
 
142
271
  Có 2 cách để khởi tạo widget:
143
272
 
144
- #### Cách 1: Khởi tạo trong `app.component.ts` (Đơn giản)
273
+ ### Cách 1: Khởi tạo trong `app.component.ts` (Đơn giản)
145
274
 
146
275
  ```typescript
147
276
  import { Component, OnInit, OnDestroy } from '@angular/core';
148
- import { environment } from '../environments/environment';
149
277
 
150
278
  declare var ITSMWidget: any;
151
279
 
@@ -159,12 +287,15 @@ export class AppComponent implements OnInit, OnDestroy {
159
287
  private itsmWidgetInstance: any;
160
288
 
161
289
  ngOnInit() {
290
+ // Lấy JWT token từ auth service hoặc storage
291
+ const token = this.getAuthToken(); // Implement method này
292
+
162
293
  // Khởi tạo ITSM Widget
163
- if (typeof ITSMWidget !== 'undefined') {
294
+ if (token && typeof ITSMWidget !== 'undefined') {
164
295
  this.itsmWidgetInstance = new ITSMWidget({
165
- // apiBaseUrl được load tự động từ file itsm-widget-config.json
166
- // KHÔNG cần truyền apiBaseUrl vào đây
167
- authToken: 'your-jwt-token-here', // JWT token từ đăng nhập thành công
296
+ // apiBaseUrl BẮT BUỘC phải trong file itsm-widget-config.json
297
+ // KHÔNG truyền apiBaseUrl vào constructor - nó sẽ bị bỏ qua
298
+ authToken: token, // JWT token từ đăng nhập thành công
168
299
  position: 'bottom-right',
169
300
  iconText: '📝',
170
301
  iconColor: '#007bff',
@@ -174,12 +305,24 @@ export class AppComponent implements OnInit, OnDestroy {
174
305
  onSuccess: (response: any) => {
175
306
  console.log('ITSM Request submitted:', response);
176
307
  // Hiển thị thông báo thành công
177
- alert('Phiếu yêu cầu đã được gửi thành công!');
308
+ const requestId = response?.request?.id || 'N/A';
309
+ alert(`Phiếu yêu cầu đã được gửi thành công!\nID: ${requestId}`);
310
+ },
311
+ onError: (error: any) => {
312
+ console.error('Error submitting request:', error);
313
+ alert('Có lỗi xảy ra khi gửi yêu cầu. Vui lòng thử lại!');
178
314
  }
179
315
  });
180
316
 
181
317
  // Lưu instance vào window để có thể access từ nơi khác
182
318
  (window as any).itsmWidgetInstance = this.itsmWidgetInstance;
319
+ } else {
320
+ if (!token) {
321
+ console.warn('ITSM Widget: JWT token not found');
322
+ }
323
+ if (typeof ITSMWidget === 'undefined') {
324
+ console.error('ITSM Widget: Script not loaded');
325
+ }
183
326
  }
184
327
  }
185
328
 
@@ -187,18 +330,27 @@ export class AppComponent implements OnInit, OnDestroy {
187
330
  // Cleanup widget khi component bị destroy
188
331
  if (this.itsmWidgetInstance) {
189
332
  this.itsmWidgetInstance.destroy();
333
+ this.itsmWidgetInstance = null;
190
334
  }
191
335
  }
336
+
337
+ private getAuthToken(): string {
338
+ // Lấy token từ auth service, localStorage, hoặc sessionStorage
339
+ // Ví dụ:
340
+ // return this.authService.getToken();
341
+ // return localStorage.getItem('jwt_token') || '';
342
+ return '';
343
+ }
192
344
  }
193
345
  ```
194
346
 
195
- #### Cách 2: Tạo Service riêng (Khuyến nghị cho project lớn)
347
+ ### Cách 2: Tạo Service riêng (Khuyến nghị cho project lớn)
196
348
 
197
349
  **1. Tạo service:** `src/app/services/itsm-widget.service.ts`
198
350
 
199
351
  ```typescript
200
352
  import { Injectable, OnDestroy } from '@angular/core';
201
- import { environment } from '../../environments/environment';
353
+ import { AuthService } from './auth.service'; // Import auth service của bạn
202
354
 
203
355
  declare var ITSMWidget: any;
204
356
 
@@ -208,44 +360,81 @@ declare var ITSMWidget: any;
208
360
  export class ItsmWidgetService implements OnDestroy {
209
361
  private widgetInstance: any;
210
362
 
211
- constructor() {
363
+ constructor(private authService: AuthService) {
212
364
  this.init();
213
365
  }
214
366
 
215
367
  private init(): void {
216
368
  if (typeof ITSMWidget !== 'undefined' && !this.widgetInstance) {
217
- this.widgetInstance = new ITSMWidget({
218
- // apiBaseUrl được load tự động từ file itsm-widget-config.json
219
- // KHÔNG cần truyền apiBaseUrl vào đây
220
- authToken: 'your-jwt-token-here', // JWT token từ đăng nhập thành công
221
- position: 'bottom-right',
222
- iconText: '📝',
223
- iconColor: '#007bff',
224
- title: 'Tạo phiếu yêu cầu ITSM',
225
- requesterName: this.getCurrentUser(), // Lấy từ auth service
226
- site: '', // Optional
227
- onSuccess: (response: any) => {
228
- console.log('ITSM Request submitted:', response);
229
- // thể emit event hoặc hiển thị notification ở đây
230
- }
231
- });
369
+ const token = this.authService.getToken(); // Lấy token từ auth service
370
+
371
+ if (token) {
372
+ this.widgetInstance = new ITSMWidget({
373
+ // apiBaseUrl BẮT BUỘC phải có trong file itsm-widget-config.json
374
+ // KHÔNG truyền apiBaseUrl vào constructor - nó sẽ bị bỏ qua
375
+ authToken: token,
376
+ position: 'bottom-right',
377
+ iconText: '📝',
378
+ iconColor: '#007bff',
379
+ title: 'Tạo phiếu yêu cầu ITSM',
380
+ requesterName: this.getCurrentUser(),
381
+ site: '', // Optional
382
+ onSuccess: (response: any) => {
383
+ console.log('ITSM Request submitted:', response);
384
+ // Có thể emit event hoặc hiển thị notification ở đây
385
+ this.showSuccessNotification(response);
386
+ },
387
+ onError: (error: any) => {
388
+ console.error('Error submitting request:', error);
389
+ this.showErrorNotification(error);
390
+ }
391
+ });
232
392
 
233
- (window as any).itsmWidgetInstance = this.widgetInstance;
393
+ (window as any).itsmWidgetInstance = this.widgetInstance;
394
+ } else {
395
+ console.warn('ITSM Widget: Token not found');
396
+ }
234
397
  }
235
398
  }
236
399
 
237
400
  private getCurrentUser(): string {
238
- // Lấy tên user từ auth service, localStorage, hoặc sessionStorage
239
- // dụ:
240
- // return this.authService.getCurrentUser()?.name || '';
241
- // return localStorage.getItem('userName') || '';
242
- return '';
401
+ // Lấy tên user từ auth service
402
+ const user = this.authService.getCurrentUser();
403
+ return user?.name || user?.username || '';
404
+ }
405
+
406
+ private showSuccessNotification(response: any): void {
407
+ // Tích hợp với notification service của bạn
408
+ // Ví dụ: this.notificationService.showSuccess('Yêu cầu đã được gửi thành công!');
409
+ const requestId = response?.request?.id || 'N/A';
410
+ alert(`Phiếu yêu cầu đã được gửi thành công!\nID: ${requestId}`);
411
+ }
412
+
413
+ private showErrorNotification(error: any): void {
414
+ // Tích hợp với notification service của bạn
415
+ alert('Có lỗi xảy ra khi gửi yêu cầu. Vui lòng thử lại!');
416
+ }
417
+
418
+ /**
419
+ * Update token khi user login/logout
420
+ */
421
+ updateToken(token: string | null): void {
422
+ if (token && this.widgetInstance) {
423
+ this.widgetInstance.config.authToken = token;
424
+ } else if (!token && this.widgetInstance) {
425
+ this.destroy();
426
+ }
243
427
  }
244
428
 
245
429
  ngOnDestroy(): void {
430
+ this.destroy();
431
+ }
432
+
433
+ private destroy(): void {
246
434
  if (this.widgetInstance) {
247
435
  this.widgetInstance.destroy();
248
436
  this.widgetInstance = null;
437
+ (window as any).itsmWidgetInstance = null;
249
438
  }
250
439
  }
251
440
  }
@@ -273,83 +462,121 @@ export class AppComponent implements OnInit {
273
462
  }
274
463
  ```
275
464
 
276
- ### Bước 5: Build và test
465
+ **3. Update token khi login/logout:**
277
466
 
278
- ```bash
279
- # Development
280
- ng serve
467
+ ```typescript
468
+ // Trong AuthService hoặc LoginComponent
469
+ import { ItsmWidgetService } from './services/itsm-widget.service';
281
470
 
282
- # Production build
283
- ng build --prod
471
+ constructor(private itsmWidgetService: ItsmWidgetService) {}
472
+
473
+ onLogin(token: string) {
474
+ // ... login logic
475
+ this.itsmWidgetService.updateToken(token);
476
+ }
477
+
478
+ onLogout() {
479
+ // ... logout logic
480
+ this.itsmWidgetService.updateToken(null);
481
+ }
284
482
  ```
285
483
 
286
- ## ⚙️ Cấu hình nâng cao
484
+ ---
287
485
 
288
- ### Lấy thông tin user từ Auth Service
486
+ ## 🔐 Tích hợp với Auth Service
289
487
 
290
- Nếu bạn Auth Service, có thể lấy thông tin user:
488
+ ### dụ với Angular JWT
291
489
 
292
490
  ```typescript
293
- import { AuthService } from './services/auth.service';
491
+ import { JwtHelperService } from '@auth0/angular-jwt';
492
+ import { ItsmWidgetService } from './services/itsm-widget.service';
294
493
 
295
- constructor(
296
- private itsmWidgetService: ItsmWidgetService,
297
- private authService: AuthService
298
- ) {}
494
+ @Injectable({
495
+ providedIn: 'root'
496
+ })
497
+ export class AuthService {
498
+ private jwtHelper = new JwtHelperService();
499
+
500
+ constructor(private itsmWidgetService: ItsmWidgetService) {}
501
+
502
+ login(username: string, password: string): Observable<string> {
503
+ return this.http.post<{token: string}>('/api/auth/login', {username, password})
504
+ .pipe(
505
+ map(response => response.token),
506
+ tap(token => {
507
+ localStorage.setItem('jwt_token', token);
508
+ // Update widget token
509
+ this.itsmWidgetService.updateToken(token);
510
+ })
511
+ );
512
+ }
299
513
 
300
- ngOnInit() {
301
- const user = this.authService.getCurrentUser();
302
-
303
- // Update widget với thông tin user
304
- if ((window as any).itsmWidgetInstance && user) {
305
- (window as any).itsmWidgetInstance.config.requesterName = user.name;
306
- (window as any).itsmWidgetInstance.config.site = user.site || '';
514
+ logout(): void {
515
+ localStorage.removeItem('jwt_token');
516
+ // Destroy widget
517
+ this.itsmWidgetService.updateToken(null);
518
+ }
519
+
520
+ getToken(): string | null {
521
+ return localStorage.getItem('jwt_token');
522
+ }
523
+
524
+ isAuthenticated(): boolean {
525
+ const token = this.getToken();
526
+ return token != null && !this.jwtHelper.isTokenExpired(token);
307
527
  }
308
528
  }
309
529
  ```
310
530
 
311
531
  ### Tích hợp với Notification Service
312
532
 
313
- Có thể tích hợp với notification service của Angular:
314
-
315
533
  ```typescript
316
- import { NotificationService } from './services/notification.service';
534
+ import { MatSnackBar } from '@angular/material/snack-bar'; // Angular Material
535
+ // hoặc
536
+ import { ToastrService } from 'ngx-toastr'; // ngx-toastr
317
537
 
318
- constructor(private notificationService: NotificationService) {}
538
+ constructor(
539
+ private snackBar: MatSnackBar,
540
+ // hoặc
541
+ private toastr: ToastrService
542
+ ) {}
319
543
 
320
544
  // Trong widget config:
321
545
  onSuccess: (response: any) => {
322
- this.notificationService.showSuccess('Phiếu yêu cầu đã được gửi thành công!');
546
+ const requestId = response?.request?.id || 'N/A';
547
+ this.snackBar.open(`Yêu cầu đã được gửi thành công! ID: ${requestId}`, 'Đóng', {
548
+ duration: 5000
549
+ });
550
+ // hoặc
551
+ this.toastr.success(`Yêu cầu đã được gửi thành công! ID: ${requestId}`);
323
552
  }
324
553
  ```
325
554
 
326
- ### Custom UDF Fields
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
- ```
555
+ ---
340
556
 
341
557
  ## 🐛 Troubleshooting
342
558
 
343
559
  ### Widget không hiển thị
344
560
 
345
- 1. Kiểm tra `angular.json` đã add scripts và styles chưa
346
- 2. Kiểm tra files đã copy đúng vào `src/assets/` chưa
347
- 3. Kiểm tra consolelỗi JavaScript không
348
- 4. Đảm bảo đã build lại project: `ng serve` hoặc `ng build`
561
+ **Kiểm tra:**
562
+ 1. File JavaScript đã được load chưa? (Kiểm tra Network tab trong DevTools)
563
+ 2. File `itsm-widget-config.json` đãtrong `src/assets/` chưa?
564
+ 3. lỗi JavaScript trong Console không?
565
+ 4. Token có hợp lệ không?
349
566
 
350
- ### Lỗi TypeScript: Cannot find name 'ITSMWidget'
567
+ **Giải pháp:**
568
+ ```typescript
569
+ // Thêm log để debug
570
+ console.log('ITSM Widget loaded:', typeof ITSMWidget);
571
+ console.log('Token:', token ? 'Found' : 'Not found');
572
+ console.log('Config file:', await fetch('/assets/itsm-widget-config.json').then(r => r.json()));
573
+ ```
574
+
575
+ ### Lỗi: Cannot find name 'ITSMWidget'
351
576
 
352
- Thêm declare vào đầu file:
577
+ **Giải pháp:**
578
+
579
+ Thêm declare vào đầu file TypeScript:
353
580
 
354
581
  ```typescript
355
582
  declare var ITSMWidget: any;
@@ -361,29 +588,115 @@ Hoặc tạo file `src/typings.d.ts`:
361
588
  declare var ITSMWidget: any;
362
589
  ```
363
590
 
364
- ### Widget không hoạt động sau khi build production
365
-
366
- Đảm bảo files đã được include trong `angular.json`:
591
+ ### Lỗi: Cannot load itsm-widget-config.json
592
+
593
+ **Lỗi:** `File itsm-widget-config.json not found. Đã thử các vị trí: ...`
594
+
595
+ **Nguyên nhân:** File config không tìm thấy hoặc không đúng format.
596
+
597
+ **Giải pháp từng bước:**
598
+
599
+ 1. **Kiểm tra file đã được copy chưa:**
600
+ ```bash
601
+ # Kiểm tra file có tồn tại không
602
+ ls src/assets/itsm-widget-config.json
603
+ # hoặc trên Windows
604
+ dir src\assets\itsm-widget-config.json
605
+ ```
606
+
607
+ 2. **Kiểm tra file có đúng format JSON không:**
608
+ ```json
609
+ {
610
+ "apiBaseUrl": "http://localhost:2411"
611
+ }
612
+ ```
613
+
614
+ 3. **Kiểm tra `angular.json` có cấu hình assets:**
615
+ ```json
616
+ {
617
+ "projects": {
618
+ "your-project-name": {
619
+ "architect": {
620
+ "build": {
621
+ "options": {
622
+ "assets": [
623
+ "src/favicon.ico",
624
+ "src/assets"
625
+ ]
626
+ }
627
+ }
628
+ }
629
+ }
630
+ }
631
+ }
632
+ ```
633
+
634
+ 4. **Kiểm tra file có được serve không:**
635
+ - Chạy `ng serve`
636
+ - Mở browser: `http://localhost:4200/assets/itsm-widget-config.json`
637
+ - Nếu thấy 404, file chưa được copy hoặc assets chưa được cấu hình
638
+
639
+ 5. **Build lại project:**
640
+ ```bash
641
+ ng serve
642
+ # hoặc
643
+ ng build
644
+ ```
645
+
646
+ 6. **Kiểm tra Network tab trong DevTools:**
647
+ - Mở DevTools → Network tab
648
+ - Reload trang
649
+ - Tìm request đến `itsm-widget-config.json`
650
+ - Xem status code và response
651
+
652
+ ### Lỗi 401 Unauthorized
653
+
654
+ **Nguyên nhân:** Token không hợp lệ hoặc đã hết hạn.
655
+
656
+ **Giải pháp:**
657
+ - Kiểm tra token có đúng format không
658
+ - Kiểm tra token có hết hạn không
659
+ - Đảm bảo token được gửi đúng trong header: `Authorization: Bearer {token}`
660
+
661
+ ### Lỗi 403 Forbidden
662
+
663
+ **Nguyên nhân:** Token không có quyền truy cập.
664
+
665
+ **Kiểm tra:**
666
+ - Token có audience (`aud`) = "SPC.HTVTCNTT" hoặc "SPC.QLTK" không?
667
+ - Token có đúng roles không?
668
+
669
+ ### Widget không load categories/subcategories/items
670
+
671
+ **Nguyên nhân:** Backend API không accessible hoặc lỗi CORS.
672
+
673
+ **Giải pháp:**
674
+ 1. Kiểm tra Backend API đang chạy không
675
+ 2. Kiểm tra `apiBaseUrl` trong `itsm-widget-config.json` đúng chưa
676
+ 3. Kiểm tra CORS đã được cấu hình trong Backend chưa
677
+ 4. Kiểm tra Network tab để xem API calls có thành công không
367
678
 
368
- ```json
369
- "assets": [
370
- "src/favicon.ico",
371
- "src/assets",
372
- {
373
- "glob": "**/*",
374
- "input": "src/assets",
375
- "output": "/assets"
376
- }
377
- ]
378
- ```
679
+ ---
379
680
 
380
681
  ## 📝 Ví dụ hoàn chỉnh
381
682
 
382
683
  Xem file [example.html](../example.html) để tham khảo cách sử dụng cơ bản.
383
684
 
685
+ ---
686
+
384
687
  ## 📚 Tài liệu thêm
385
688
 
386
689
  - [README.md](../README.md) - Tài liệu tổng quan
387
- - [INSTALLATION.md](../INSTALLATION.md) - Hướng dẫn cho các platform khác
690
+ - [CONFIG.md](../CONFIG.md) - Hướng dẫn cấu hình chi tiết
388
691
  - [API-SERVICE.md](../API-SERVICE.md) - Tài liệu API Service
692
+ - [MVC-INTEGRATION.md](../MVC-INTEGRATION.md) - Hướng dẫn cho ASP.NET MVC
693
+
694
+ ---
695
+
696
+ ## 💡 Tips
389
697
 
698
+ 1. **Performance**: Widget chỉ load khi cần thiết, không ảnh hưởng đến performance của trang chính
699
+ 2. **Security**: Luôn lưu JWT token một cách an toàn (HttpOnly cookie hoặc secure storage)
700
+ 3. **Error Handling**: Luôn xử lý lỗi trong callback `onError` để cung cấp feedback cho người dùng
701
+ 4. **Testing**: Test widget trên các browser khác nhau để đảm bảo tương thích
702
+ 5. **Token Management**: Update widget token khi user login/logout để đảm bảo widget luôn có token hợp lệ