itsm-widget 1.0.1

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.
@@ -0,0 +1,372 @@
1
+ # Hướng dẫn cài đặt ITSM Widget vào Angular Project
2
+
3
+ ## 📋 Các bước cài đặt
4
+
5
+ ### Cách 1: Cài đặt qua npm (Khuyến nghị)
6
+
7
+ **Bước 1: Install package**
8
+
9
+ ```bash
10
+ npm install itsm-widget
11
+ ```
12
+
13
+ **Bước 2: Cập nhật `angular.json`**
14
+
15
+ Thêm vào `scripts` và `styles`:
16
+
17
+ ```json
18
+ {
19
+ "projects": {
20
+ "your-project-name": {
21
+ "architect": {
22
+ "build": {
23
+ "options": {
24
+ "scripts": [
25
+ "node_modules/itsm-widget/dist/itsm-widget.min.js"
26
+ ],
27
+ "styles": [
28
+ "src/styles.css",
29
+ "node_modules/itsm-widget/dist/itsm-widget.min.css"
30
+ ]
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ **Bước 3:** Xem tiếp phần "Khởi tạo widget" bên dưới.
40
+
41
+ ---
42
+
43
+ ### Cách 2: Copy files thủ công
44
+
45
+ **Bước 1: Copy files vào project**
46
+
47
+ Copy 2 files sau từ `dist/` của widget vào Angular project:
48
+
49
+ ```
50
+ src/assets/
51
+ ├── itsm-widget.min.js
52
+ └── itsm-widget.min.css
53
+ ```
54
+
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
+ **Bước 2: Cập nhật `angular.json`**
60
+
61
+ Thêm scripts và styles vào `angular.json`:
62
+
63
+ ```json
64
+ {
65
+ "projects": {
66
+ "your-project-name": {
67
+ "architect": {
68
+ "build": {
69
+ "options": {
70
+ "scripts": [
71
+ "src/assets/itsm-widget.min.js"
72
+ ],
73
+ "styles": [
74
+ "src/styles.css",
75
+ "src/assets/itsm-widget.min.css"
76
+ ]
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ **Lưu ý:** Thay `your-project-name` bằng tên project của bạn.
86
+
87
+ ---
88
+
89
+ ## ⚙️ Cấu hình (Áp dụng cho cả 2 cách)
90
+
91
+ ### Bước 3: Cấu hình environment (Khuyến nghị)
92
+
93
+ Thêm config vào `src/environments/environment.ts`:
94
+
95
+ ```typescript
96
+ export const environment = {
97
+ production: false,
98
+ // ... other config
99
+ itsm: {
100
+ apiBaseUrl: 'https://your-api-domain.com',
101
+ apiKey: 'your-api-key',
102
+ tenant: 'your-tenant-name',
103
+ site: 'Your Site Name'
104
+ }
105
+ };
106
+ ```
107
+
108
+ Và `src/environments/environment.prod.ts`:
109
+
110
+ ```typescript
111
+ export const environment = {
112
+ production: true,
113
+ // ... other config
114
+ itsm: {
115
+ apiBaseUrl: 'https://your-api-domain.com',
116
+ apiKey: 'your-production-api-key',
117
+ tenant: 'your-tenant-name',
118
+ site: 'Your Site Name'
119
+ }
120
+ };
121
+ ```
122
+
123
+ ## 🚀 Khởi tạo widget
124
+
125
+ Có 2 cách để khởi tạo widget:
126
+
127
+ #### Cách 1: Khởi tạo trong `app.component.ts` (Đơn giản)
128
+
129
+ ```typescript
130
+ import { Component, OnInit, OnDestroy } from '@angular/core';
131
+ import { environment } from '../environments/environment';
132
+
133
+ declare var ITSMWidget: any;
134
+
135
+ @Component({
136
+ selector: 'app-root',
137
+ templateUrl: './app.component.html',
138
+ styleUrls: ['./app.component.css']
139
+ })
140
+ export class AppComponent implements OnInit, OnDestroy {
141
+ title = 'your-app';
142
+ private itsmWidgetInstance: any;
143
+
144
+ ngOnInit() {
145
+ // Khởi tạo ITSM Widget
146
+ if (typeof ITSMWidget !== 'undefined') {
147
+ this.itsmWidgetInstance = new ITSMWidget({
148
+ apiBaseUrl: environment.itsm.apiBaseUrl,
149
+ apiKey: environment.itsm.apiKey,
150
+ tenant: environment.itsm.tenant,
151
+ position: 'bottom-right',
152
+ iconText: '📝',
153
+ iconColor: '#007bff',
154
+ title: 'Tạo phiếu yêu cầu ITSM',
155
+ requesterName: '', // Optional - có thể lấy từ service
156
+ site: environment.itsm.site,
157
+ onSuccess: (response: any) => {
158
+ console.log('ITSM Request submitted:', response);
159
+ // Hiển thị thông báo thành công
160
+ alert('Phiếu yêu cầu đã được gửi thành công!');
161
+ }
162
+ });
163
+
164
+ // Lưu instance vào window để có thể access từ nơi khác
165
+ (window as any).itsmWidgetInstance = this.itsmWidgetInstance;
166
+ }
167
+ }
168
+
169
+ ngOnDestroy() {
170
+ // Cleanup widget khi component bị destroy
171
+ if (this.itsmWidgetInstance) {
172
+ this.itsmWidgetInstance.destroy();
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ #### Cách 2: Tạo Service riêng (Khuyến nghị cho project lớn)
179
+
180
+ **1. Tạo service:** `src/app/services/itsm-widget.service.ts`
181
+
182
+ ```typescript
183
+ import { Injectable, OnDestroy } from '@angular/core';
184
+ import { environment } from '../../environments/environment';
185
+
186
+ declare var ITSMWidget: any;
187
+
188
+ @Injectable({
189
+ providedIn: 'root'
190
+ })
191
+ export class ItsmWidgetService implements OnDestroy {
192
+ private widgetInstance: any;
193
+
194
+ constructor() {
195
+ this.init();
196
+ }
197
+
198
+ private init(): void {
199
+ if (typeof ITSMWidget !== 'undefined' && !this.widgetInstance) {
200
+ this.widgetInstance = new ITSMWidget({
201
+ apiBaseUrl: environment.itsm.apiBaseUrl,
202
+ apiKey: environment.itsm.apiKey,
203
+ tenant: environment.itsm.tenant,
204
+ position: 'bottom-right',
205
+ iconText: '📝',
206
+ iconColor: '#007bff',
207
+ title: 'Tạo phiếu yêu cầu ITSM',
208
+ requesterName: this.getCurrentUser(), // Lấy từ auth service
209
+ site: environment.itsm.site,
210
+ onSuccess: (response: any) => {
211
+ console.log('ITSM Request submitted:', response);
212
+ // Có thể emit event hoặc hiển thị notification ở đây
213
+ }
214
+ });
215
+
216
+ (window as any).itsmWidgetInstance = this.widgetInstance;
217
+ }
218
+ }
219
+
220
+ private getCurrentUser(): string {
221
+ // Lấy tên user từ auth service, localStorage, hoặc sessionStorage
222
+ // Ví dụ:
223
+ // return this.authService.getCurrentUser()?.name || '';
224
+ // return localStorage.getItem('userName') || '';
225
+ return '';
226
+ }
227
+
228
+ ngOnDestroy(): void {
229
+ if (this.widgetInstance) {
230
+ this.widgetInstance.destroy();
231
+ this.widgetInstance = null;
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ **2. Inject service vào `app.component.ts`:**
238
+
239
+ ```typescript
240
+ import { Component, OnInit } from '@angular/core';
241
+ import { ItsmWidgetService } from './services/itsm-widget.service';
242
+
243
+ @Component({
244
+ selector: 'app-root',
245
+ templateUrl: './app.component.html',
246
+ styleUrls: ['./app.component.css']
247
+ })
248
+ export class AppComponent implements OnInit {
249
+ title = 'your-app';
250
+
251
+ constructor(private itsmWidgetService: ItsmWidgetService) {}
252
+
253
+ ngOnInit() {
254
+ // Service sẽ tự động khởi tạo widget
255
+ }
256
+ }
257
+ ```
258
+
259
+ ### Bước 5: Build và test
260
+
261
+ ```bash
262
+ # Development
263
+ ng serve
264
+
265
+ # Production build
266
+ ng build --prod
267
+ ```
268
+
269
+ ## ⚙️ Cấu hình nâng cao
270
+
271
+ ### Lấy thông tin user từ Auth Service
272
+
273
+ Nếu bạn có Auth Service, có thể lấy thông tin user:
274
+
275
+ ```typescript
276
+ import { AuthService } from './services/auth.service';
277
+
278
+ constructor(
279
+ private itsmWidgetService: ItsmWidgetService,
280
+ private authService: AuthService
281
+ ) {}
282
+
283
+ ngOnInit() {
284
+ const user = this.authService.getCurrentUser();
285
+
286
+ // Update widget với thông tin user
287
+ if ((window as any).itsmWidgetInstance && user) {
288
+ (window as any).itsmWidgetInstance.config.requesterName = user.name;
289
+ (window as any).itsmWidgetInstance.config.site = user.site || '';
290
+ }
291
+ }
292
+ ```
293
+
294
+ ### Tích hợp với Notification Service
295
+
296
+ Có thể tích hợp với notification service của Angular:
297
+
298
+ ```typescript
299
+ import { NotificationService } from './services/notification.service';
300
+
301
+ constructor(private notificationService: NotificationService) {}
302
+
303
+ // Trong widget config:
304
+ onSuccess: (response: any) => {
305
+ this.notificationService.showSuccess('Phiếu yêu cầu đã được gửi thành công!');
306
+ }
307
+ ```
308
+
309
+ ### Custom UDF Fields
310
+
311
+ Có thể thêm custom UDF fields:
312
+
313
+ ```typescript
314
+ new ITSMWidget({
315
+ // ... other config
316
+ udf_fields: {
317
+ udf_pick_301: 'Value 1',
318
+ udf_pick_302: 'Value 2',
319
+ // ... other fields
320
+ }
321
+ });
322
+ ```
323
+
324
+ ## 🐛 Troubleshooting
325
+
326
+ ### Widget không hiển thị
327
+
328
+ 1. Kiểm tra `angular.json` đã add scripts và styles chưa
329
+ 2. Kiểm tra files đã copy đúng vào `src/assets/` chưa
330
+ 3. Kiểm tra console có lỗi JavaScript không
331
+ 4. Đảm bảo đã build lại project: `ng serve` hoặc `ng build`
332
+
333
+ ### Lỗi TypeScript: Cannot find name 'ITSMWidget'
334
+
335
+ Thêm declare vào đầu file:
336
+
337
+ ```typescript
338
+ declare var ITSMWidget: any;
339
+ ```
340
+
341
+ Hoặc tạo file `src/typings.d.ts`:
342
+
343
+ ```typescript
344
+ declare var ITSMWidget: any;
345
+ ```
346
+
347
+ ### Widget không hoạt động sau khi build production
348
+
349
+ Đảm bảo files đã được include trong `angular.json`:
350
+
351
+ ```json
352
+ "assets": [
353
+ "src/favicon.ico",
354
+ "src/assets",
355
+ {
356
+ "glob": "**/*",
357
+ "input": "src/assets",
358
+ "output": "/assets"
359
+ }
360
+ ]
361
+ ```
362
+
363
+ ## 📝 Ví dụ hoàn chỉnh
364
+
365
+ Xem file [example.html](../example.html) để tham khảo cách sử dụng cơ bản.
366
+
367
+ ## 📚 Tài liệu thêm
368
+
369
+ - [README.md](../README.md) - Tài liệu tổng quan
370
+ - [INSTALLATION.md](../INSTALLATION.md) - Hướng dẫn cho các platform khác
371
+ - [API-SERVICE.md](../API-SERVICE.md) - Tài liệu API Service
372
+
package/API-SERVICE.md ADDED
@@ -0,0 +1,334 @@
1
+ # ITSM API Service
2
+
3
+ Service JavaScript để gọi các API của SDP ITSM theo tài liệu API v2 SDP ITSM SPC.
4
+
5
+ ## Tổng quan
6
+
7
+ `ITSMAPIService` cung cấp các method để tương tác với ITSM API:
8
+ - Tạo yêu cầu mới (ticket)
9
+ - Upload file đính kèm
10
+ - Lấy thông tin request
11
+ - Helper methods để map và build data
12
+
13
+ ## Cài đặt
14
+
15
+ ### Import trong module:
16
+ ```javascript
17
+ import ITSMAPIService from './itsm-api.service.js';
18
+ ```
19
+
20
+ ### Hoặc sử dụng global (sau khi load script):
21
+ ```javascript
22
+ const ITSMAPIService = window.ITSMAPIService;
23
+ ```
24
+
25
+ ## Khởi tạo
26
+
27
+ ```javascript
28
+ const apiService = new ITSMAPIService({
29
+ baseUrl: 'https://your-api-domain.com',
30
+ apiKey: 'your-api-key',
31
+ tenant: 'your-tenant-name'
32
+ });
33
+ ```
34
+
35
+ ### Options
36
+
37
+ | Option | Type | Default | Mô tả |
38
+ |--------|------|---------|-------|
39
+ | `baseUrl` | string | `''` | Base URL của API ITSM (bắt buộc) |
40
+ | `apiKey` | string | `''` | API Key để xác thực (bắt buộc) |
41
+ | `tenant` | string | `''` | Tenant name (pn header) (bắt buộc) |
42
+
43
+ ## Methods
44
+
45
+ ### 1. createRequest(requestData)
46
+
47
+ Tạo yêu cầu mới (ticket).
48
+
49
+ **Parameters:**
50
+ - `requestData` (Object): Dữ liệu yêu cầu theo format API
51
+
52
+ **Returns:** `Promise<Object>`
53
+
54
+ **Ví dụ:**
55
+ ```javascript
56
+ const requestData = {
57
+ requester: {
58
+ name: 'Lê Trường Tình'
59
+ },
60
+ category: {
61
+ name: 'A01A00 - Quản lý an toàn thông tin'
62
+ },
63
+ subcategory: {
64
+ name: 'A01A01 - Hệ thống'
65
+ },
66
+ priority: {
67
+ name: 'Trung bình'
68
+ },
69
+ subject: 'Test ITSM',
70
+ description: '<div>Test description</div>',
71
+ site: {
72
+ name: 'Your Site Name'
73
+ },
74
+ item: {
75
+ name: 'Danh sách chức năng'
76
+ },
77
+ template: {
78
+ name: 'A01A00 - Quản lý an toàn thông tin'
79
+ },
80
+ udf_fields: {
81
+ udf_pick_301: 'Value 1',
82
+ udf_pick_302: 'Value 2',
83
+ udf_pick_303: 'Value 3',
84
+ udf_pick_304: 'Value 4',
85
+ udf_pick_305: 'Value 5',
86
+ udf_pick_306: 'Value 6'
87
+ },
88
+ attachments: [],
89
+ assets: []
90
+ };
91
+
92
+ const response = await apiService.createRequest(requestData);
93
+ console.log('Request ID:', response.request.id);
94
+ ```
95
+
96
+ **Response:**
97
+ ```json
98
+ {
99
+ "response_status": {
100
+ "status_code": 2000,
101
+ "status": "success"
102
+ },
103
+ "request": {
104
+ "id": "18391",
105
+ "ola_due_by_time": null,
106
+ "resolution": {
107
+ "resolution_attachments": [],
108
+ "content": null
109
+ },
110
+ "onhold_time": null,
111
+ "is_trashed": false
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### 2. uploadAttachment(requestId, files)
117
+
118
+ Upload file đính kèm vào ticket đã tạo.
119
+
120
+ **Parameters:**
121
+ - `requestId` (string): ID của ticket
122
+ - `files` (File | File[]): File hoặc mảng các file cần upload
123
+
124
+ **Returns:** `Promise<Object>`
125
+
126
+ **Ví dụ:**
127
+ ```javascript
128
+ // Upload 1 file
129
+ const fileInput = document.getElementById('file-input');
130
+ const file = fileInput.files[0];
131
+ const response = await apiService.uploadAttachment('18391', file);
132
+
133
+ // Upload nhiều file
134
+ const files = Array.from(fileInput.files);
135
+ const response = await apiService.uploadAttachment('18391', files);
136
+ ```
137
+
138
+ ### 3. createRequestWithAttachments(requestData, files)
139
+
140
+ Tạo request và upload file cùng lúc (all-in-one method).
141
+
142
+ **Parameters:**
143
+ - `requestData` (Object): Dữ liệu yêu cầu
144
+ - `files` (File | File[]): File đính kèm (optional)
145
+
146
+ **Returns:** `Promise<Object>`
147
+
148
+ **Ví dụ:**
149
+ ```javascript
150
+ const requestData = {
151
+ requester: { name: 'Nguyễn Văn A' },
152
+ category: { name: 'A01A00 - Quản lý an toàn thông tin' },
153
+ priority: { name: 'Trung bình' },
154
+ subject: 'Test with attachment',
155
+ description: 'Test description'
156
+ };
157
+
158
+ const files = Array.from(document.getElementById('file-input').files);
159
+ const response = await apiService.createRequestWithAttachments(requestData, files);
160
+ ```
161
+
162
+ ### 4. getRequest(requestId)
163
+
164
+ Lấy thông tin request theo ID.
165
+
166
+ **Parameters:**
167
+ - `requestId` (string): ID của request
168
+
169
+ **Returns:** `Promise<Object>`
170
+
171
+ **Ví dụ:**
172
+ ```javascript
173
+ const requestInfo = await apiService.getRequest('18391');
174
+ console.log(requestInfo);
175
+ ```
176
+
177
+ ### 5. mapPriority(priority)
178
+
179
+ Helper method để map priority từ form widget sang format API.
180
+
181
+ **Parameters:**
182
+ - `priority` (string): 'low', 'medium', 'high', 'none'
183
+
184
+ **Returns:** `string`
185
+
186
+ **Ví dụ:**
187
+ ```javascript
188
+ const priority = apiService.mapPriority('high'); // Trả về: 'Cao'
189
+ ```
190
+
191
+ ### 6. mapCategory(category)
192
+
193
+ Helper method để map category từ form widget sang format API.
194
+
195
+ **Parameters:**
196
+ - `category` (string): Category từ form
197
+
198
+ **Returns:** `string | null`
199
+
200
+ **Ví dụ:**
201
+ ```javascript
202
+ const category = apiService.mapCategory('hardware');
203
+ // Trả về: 'A01A00 - Quản lý an toàn thông tin'
204
+ ```
205
+
206
+ ### 7. buildRequestData(formData, options)
207
+
208
+ Build request data từ form data của widget sang format API.
209
+
210
+ **Parameters:**
211
+ - `formData` (Object): Dữ liệu từ form widget
212
+ - `title`: string
213
+ - `description`: string
214
+ - `category`: string
215
+ - `priority`: string
216
+ - `contact`: string
217
+ - `options` (Object): Options bổ sung
218
+ - `requesterName`: string
219
+ - `site`: string
220
+ - `udf_fields`: object
221
+ - `zia_properties`: object
222
+ - `template`: string
223
+ - `item`: string
224
+ - `subcategory`: string
225
+
226
+ **Returns:** `Object`
227
+
228
+ **Ví dụ:**
229
+ ```javascript
230
+ const formData = {
231
+ title: 'Test Request',
232
+ description: 'Test description',
233
+ category: 'hardware',
234
+ priority: 'none',
235
+ contact: 'test@example.com'
236
+ };
237
+
238
+ const requestData = apiService.buildRequestData(formData, {
239
+ requesterName: 'Người dùng',
240
+ site: 'Your Site Name',
241
+ udf_fields: {
242
+ udf_pick_301: 'Value 1'
243
+ }
244
+ });
245
+ ```
246
+
247
+ ## Cấu trúc Request Data
248
+
249
+ ### Đầy đủ các trường:
250
+
251
+ ```javascript
252
+ {
253
+ requester: {
254
+ name: string
255
+ },
256
+ assets: [],
257
+ category: {
258
+ name: string
259
+ } | null,
260
+ group: string | null,
261
+ subcategory: {
262
+ name: string
263
+ } | null,
264
+ technician: string | null,
265
+ item: {
266
+ name: string
267
+ } | null,
268
+ priority: {
269
+ name: string // 'Thấp', 'Trung bình', 'Cao', 'Khẩn cấp'
270
+ },
271
+ subject: string,
272
+ description: string, // HTML string
273
+ site: {
274
+ name: string
275
+ } | null,
276
+ attachments: [],
277
+ udf_fields: {
278
+ udf_pick_301: string,
279
+ udf_pick_302: string,
280
+ // ... các custom fields khác
281
+ },
282
+ template: {
283
+ name: string
284
+ } | null,
285
+ zia_properties: {}
286
+ }
287
+ ```
288
+
289
+ ## Error Handling
290
+
291
+ Tất cả các methods đều throw Error nếu có lỗi:
292
+
293
+ ```javascript
294
+ try {
295
+ const response = await apiService.createRequest(requestData);
296
+ } catch (error) {
297
+ console.error('Error:', error.message);
298
+ // error.message có thể là:
299
+ // - "API Key không hợp lệ."
300
+ // - "HTTP error! status: 400"
301
+ // - hoặc các thông báo lỗi khác
302
+ }
303
+ ```
304
+
305
+ ## API Endpoints
306
+
307
+ ### 1. Tạo Request
308
+ - **URL:** `POST {baseUrl}/{tenant}/send`
309
+ - **Headers:**
310
+ - `authtoken`: API key
311
+ - `pn`: Tenant name
312
+ - `Content-Type`: `multipart/form-data` (auto)
313
+ - **Body:** FormData với key `input_data` chứa JSON string
314
+
315
+ ### 2. Upload Attachment
316
+ - **URL:** `PUT {baseUrl}/{tenant}/{id}/upload`
317
+ - **Headers:**
318
+ - `authtoken`: API key
319
+ - `pn`: Tenant name
320
+ - `Content-Type`: `multipart/form-data` (auto)
321
+ - **Body:** FormData với các file
322
+
323
+ ### 3. Get Request
324
+ - **URL:** `GET {baseUrl}/{tenant}/{id}`
325
+ - **Headers:**
326
+ - `authtoken`: API key
327
+ - `pn`: Tenant name
328
+ - `Accept`: `application/json`
329
+
330
+ ## Xem thêm
331
+
332
+ - Xem tài liệu API đầy đủ tại: `src/assets/13082025 API v2 SDP ITSM SPC.doc`
333
+ - Xem ví dụ sử dụng widget tại: [example.html](./example.html)
334
+