itsm-widget 1.0.5 → 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.
@@ -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,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 (Áp dụng cho cả 2 cách)
139
+ ## ⚙️ Cấu hình
90
140
 
91
- ### Bước 3: Copy file config (BẮT BUỘC)
141
+ ### Bước quan trọng: Cấu hình `itsm-widget-config.json` (BẮT BUỘC)
92
142
 
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/`:
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` theo nhu cầu:
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:9998"
160
+ "apiBaseUrl": "http://localhost:2411"
109
161
  }
110
162
  ```
111
163
 
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
114
- - 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`
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 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
- ### Bước 4: Đảm bảo assets được copy vào build
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
- ## 🚀 Khởi tạo widget
193
+ ---
194
+
195
+ ## 🚀 Khởi tạo Widget
141
196
 
142
197
  Có 2 cách để khởi tạo widget:
143
198
 
144
- #### Cách 1: Khởi tạo trong `app.component.ts` (Đơn giản)
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 đượ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
222
+ // apiBaseUrl BẮT BUỘC phải 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
- alert('Phiếu yêu cầu đã được gửi thành công!');
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
- #### Cách 2: Tạo Service riêng (Khuyến nghị cho project lớn)
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 { environment } from '../../environments/environment';
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.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
- });
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
- (window as any).itsmWidgetInstance = this.widgetInstance;
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, localStorage, hoặc sessionStorage
239
- // dụ:
240
- // return this.authService.getCurrentUser()?.name || '';
241
- // return localStorage.getItem('userName') || '';
242
- return '';
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
- ### Bước 5: Build và test
391
+ **3. Update token khi login/logout:**
277
392
 
278
- ```bash
279
- # Development
280
- ng serve
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
- # Production build
283
- ng build --prod
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
- ## ⚙️ Cấu hình nâng cao
410
+ ---
287
411
 
288
- ### Lấy thông tin user từ Auth Service
412
+ ## 🔐 Tích hợp với Auth Service
289
413
 
290
- Nếu bạn Auth Service, có thể lấy thông tin user:
414
+ ### dụ với Angular JWT
291
415
 
292
416
  ```typescript
293
- import { AuthService } from './services/auth.service';
417
+ import { JwtHelperService } from '@auth0/angular-jwt';
418
+ import { ItsmWidgetService } from './services/itsm-widget.service';
294
419
 
295
- constructor(
296
- private itsmWidgetService: ItsmWidgetService,
297
- private authService: AuthService
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
- 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 || '';
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 { NotificationService } from './services/notification.service';
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(private notificationService: NotificationService) {}
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
- this.notificationService.showSuccess('Phiếu yêu cầu đã được gửi thành công!');
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
- ### 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
- ```
481
+ ---
340
482
 
341
483
  ## 🐛 Troubleshooting
342
484
 
343
485
  ### Widget không hiển thị
344
486
 
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`
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` đãtrong `src/assets/` chưa?
490
+ 3. lỗi JavaScript trong Console không?
491
+ 4. Token có hợp lệ không?
349
492
 
350
- ### Lỗi TypeScript: Cannot find name 'ITSMWidget'
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
- Thêm declare vào đầu file:
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
- ### Widget không hoạt động sau khi build production
517
+ ### Lỗi: Cannot load itsm-widget-config.json
365
518
 
366
- Đảm bảo files đã được include trong `angular.json`:
519
+ **Nguyên nhân:** File config không tìm thấy hoặc không đúng format.
367
520
 
368
- ```json
369
- "assets": [
370
- "src/favicon.ico",
371
- "src/assets",
372
- {
373
- "glob": "**/*",
374
- "input": "src/assets",
375
- "output": "/assets"
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
- - [INSTALLATION.md](../INSTALLATION.md) - Hướng dẫn cho các platform khác
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ệ