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.
- package/ANGULAR-INSTALLATION.md +430 -117
- 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,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
|
|
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
|
-
|
|
167
|
+
**Cách 2: Copy vào assets (Production - Khuyến nghị)**
|
|
92
168
|
|
|
93
|
-
|
|
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
|
|
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:
|
|
184
|
+
"apiBaseUrl": "http://localhost:2411"
|
|
109
185
|
}
|
|
110
186
|
```
|
|
111
187
|
|
|
112
|
-
|
|
113
|
-
-
|
|
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
|
-
-
|
|
192
|
+
- **KHÔNG** truyền `apiBaseUrl` vào constructor - nó 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
|
-
|
|
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
|
|
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
|
-
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 🚀 Khởi tạo Widget
|
|
141
270
|
|
|
142
271
|
Có 2 cách để khởi tạo widget:
|
|
143
272
|
|
|
144
|
-
|
|
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
|
|
166
|
-
// KHÔNG
|
|
167
|
-
authToken:
|
|
296
|
+
// apiBaseUrl BẮT BUỘC phải có 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
|
-
|
|
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
|
-
|
|
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 {
|
|
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.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
465
|
+
**3. Update token khi login/logout:**
|
|
277
466
|
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
|
|
467
|
+
```typescript
|
|
468
|
+
// Trong AuthService hoặc LoginComponent
|
|
469
|
+
import { ItsmWidgetService } from './services/itsm-widget.service';
|
|
281
470
|
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
484
|
+
---
|
|
287
485
|
|
|
288
|
-
|
|
486
|
+
## 🔐 Tích hợp với Auth Service
|
|
289
487
|
|
|
290
|
-
|
|
488
|
+
### Ví dụ với Angular JWT
|
|
291
489
|
|
|
292
490
|
```typescript
|
|
293
|
-
import {
|
|
491
|
+
import { JwtHelperService } from '@auth0/angular-jwt';
|
|
492
|
+
import { ItsmWidgetService } from './services/itsm-widget.service';
|
|
294
493
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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 {
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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` đã có trong `src/assets/` chưa?
|
|
564
|
+
3. Có lỗi JavaScript trong Console không?
|
|
565
|
+
4. Token có hợp lệ không?
|
|
349
566
|
|
|
350
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
- [
|
|
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ệ
|