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/API-SERVICE.md CHANGED
@@ -31,16 +31,18 @@ const ITSMAPIService = window.ITSMAPIService;
31
31
 
32
32
  ```javascript
33
33
  const apiService = new ITSMAPIService({
34
- baseUrl: 'http://localhost:9998', // URL của Backend API
34
+ // baseUrl sẽ được load tự động từ file itsm-widget-config.json
35
35
  authToken: 'your-jwt-token-here' // JWT token từ đăng nhập thành công
36
36
  });
37
37
  ```
38
38
 
39
+ **⚠️ QUAN TRỌNG:** `baseUrl` **CHỈ** được lấy từ file `itsm-widget-config.json` - đây là cách duy nhất để cấu hình URL của Backend API. Xem [CONFIG.md](./CONFIG.md) để biết thêm chi tiết.
40
+
39
41
  ### Options
40
42
 
41
43
  | Option | Type | Default | Mô tả |
42
44
  |--------|------|---------|-------|
43
- | `baseUrl` | string | `''` | Base URL của Backend API (bắt buộc). Ví dụ: `http://localhost:9998` |
45
+ | `baseUrl` | string | `''` | **KHÔNG truyền vào constructor** - Base URL của Backend API được load tự động từ file `itsm-widget-config.json` (bắt buộc). |
44
46
  | `authToken` | string | `''` | Authorization Bearer token (JWT token từ đăng nhập thành công) (bắt buộc). Đây KHÔNG phải là apiKey. |
45
47
 
46
48
  ## Methods
@@ -177,7 +179,72 @@ const requestInfo = await apiService.getRequest('18391');
177
179
  console.log(requestInfo);
178
180
  ```
179
181
 
180
- ### 5. mapPriority(priority)
182
+ ### 5. getAllCategories()
183
+
184
+ Lấy tất cả categories từ Backend API.
185
+
186
+ **Returns:** `Promise<Object>` - Response với format:
187
+ ```json
188
+ {
189
+ "categories": [...],
190
+ "list_info": {...},
191
+ "response_status": [...]
192
+ }
193
+ ```
194
+
195
+ **Ví dụ:**
196
+ ```javascript
197
+ const categoriesData = await apiService.getAllCategories();
198
+ console.log('Categories:', categoriesData.categories);
199
+ ```
200
+
201
+ ### 6. getSubcategoriesByCategoryId(categoryId)
202
+
203
+ Lấy danh sách subcategories theo category ID.
204
+
205
+ **Parameters:**
206
+ - `categoryId` (string): ID của category
207
+
208
+ **Returns:** `Promise<Object>` - Response với format:
209
+ ```json
210
+ {
211
+ "subcategories": [...],
212
+ "list_info": {...},
213
+ "response_status": [...]
214
+ }
215
+ ```
216
+
217
+ **Ví dụ:**
218
+ ```javascript
219
+ const subcategoriesData = await apiService.getSubcategoriesByCategoryId('301');
220
+ console.log('Subcategories:', subcategoriesData.subcategories);
221
+ ```
222
+
223
+ ### 7. getItemsBySubcategoryId(subcategoryId)
224
+
225
+ Lấy danh sách items theo subcategory ID.
226
+
227
+ **Parameters:**
228
+ - `subcategoryId` (string): ID của subcategory
229
+
230
+ **Returns:** `Promise<Object>` - Response với format:
231
+ ```json
232
+ {
233
+ "items": [...],
234
+ "list_info": {...},
235
+ "response_status": [...]
236
+ }
237
+ ```
238
+
239
+ **Ví dụ:**
240
+ ```javascript
241
+ const itemsData = await apiService.getItemsBySubcategoryId('302');
242
+ console.log('Items:', itemsData.items);
243
+ ```
244
+
245
+ ### 8. mapPriority(priority)
246
+
247
+ ### 8. mapPriority(priority)
181
248
 
182
249
  Helper method để map priority từ form widget sang format API.
183
250
 
@@ -191,7 +258,7 @@ Helper method để map priority từ form widget sang format API.
191
258
  const priority = apiService.mapPriority('high'); // Trả về: 'Cao'
192
259
  ```
193
260
 
194
- ### 6. mapCategory(category)
261
+ ### 9. mapCategory(category)
195
262
 
196
263
  Helper method để map category từ form widget sang format API.
197
264
 
@@ -206,7 +273,7 @@ const category = apiService.mapCategory('hardware');
206
273
  // Trả về: 'A01A00 - Quản lý an toàn thông tin'
207
274
  ```
208
275
 
209
- ### 7. buildRequestData(formData, options)
276
+ ### 10. buildRequestData(formData, options)
210
277
 
211
278
  Build request data từ form data của widget sang format API.
212
279
 
@@ -346,7 +413,64 @@ try {
346
413
  }
347
414
  ```
348
415
 
349
- **Lưu ý:** `apiKey` (authtoken) và `tenant` được xử lý ở Backend, không cần truyền từ Frontend.
416
+ ### 4. Get All Categories
417
+ - **URL:** `POST {baseUrl}/spcit/categories`
418
+ - **Headers:**
419
+ - `Authorization`: `Bearer {authToken}`
420
+ - `Content-Type`: `application/json`
421
+ - **Body:** JSON với `list_info`:
422
+ ```json
423
+ {
424
+ "list_info": {
425
+ "start_index": 1,
426
+ "page": 1,
427
+ "row_count": 100,
428
+ "sort_field": "name",
429
+ "sort_order": "asc",
430
+ "get_total_count": true
431
+ }
432
+ }
433
+ ```
434
+
435
+ ### 5. Get Subcategories by Category ID
436
+ - **URL:** `POST {baseUrl}/spcit/categories/{categoryId}/subcategories`
437
+ - **Headers:**
438
+ - `Authorization`: `Bearer {authToken}`
439
+ - `Content-Type`: `application/json`
440
+ - **Body:** JSON với `list_info`:
441
+ ```json
442
+ {
443
+ "list_info": {
444
+ "start_index": 0,
445
+ "row_count": 100,
446
+ "sort_field": "name",
447
+ "sort_order": "asc",
448
+ "get_total_count": true
449
+ }
450
+ }
451
+ ```
452
+
453
+ ### 6. Get Items by Subcategory ID
454
+ - **URL:** `POST {baseUrl}/spcit/subcategories/{subcategoryId}/items`
455
+ - **Headers:**
456
+ - `Authorization`: `Bearer {authToken}`
457
+ - `Content-Type`: `application/json`
458
+ - **Body:** JSON với `list_info`:
459
+ ```json
460
+ {
461
+ "list_info": {
462
+ "start_index": 0,
463
+ "row_count": 100,
464
+ "sort_field": "name",
465
+ "sort_order": "asc",
466
+ "get_total_count": true
467
+ }
468
+ }
469
+ ```
470
+
471
+ **Lưu ý:**
472
+ - `apiKey` (authtoken) và `tenant` được xử lý ở Backend, không cần truyền từ Frontend.
473
+ - `{baseUrl}` được load từ file `itsm-widget-config.json`, không truyền vào constructor.
350
474
 
351
475
  ## Xem thêm
352
476
 
package/README.md CHANGED
@@ -18,9 +18,13 @@ Plugin HTML **standalone** để Tạo phiếu yêu cầu ITSM. Widget sử dụ
18
18
  - 🎯 Icon nổi ở góc dưới bên phải màn hình
19
19
  - 📝 Form nhập phiếu yêu cầu ITSM với các trường:
20
20
  - Tiêu đề (bắt buộc)
21
- - Mô tả (bắt buộc)
22
- - Danh mục (Phần cứng, Phần mềm, Mạng, Tài khoản, Khác)
23
- - Mức độ ưu tiên (Thấp, Trung bình, Cao, Khẩn cấp)
21
+ - Mô tả (bắt buộc) - Rich text editor với formatting
22
+ - Danh mục dịch vụ (tải từ API, filter tìm kiếm)
23
+ - Dịch vụ con (tải từ API theo danh mục, filter tìm kiếm)
24
+ - Chi tiết dịch vụ (tải từ API theo dịch vụ con, có filter tìm kiếm)
25
+ - Mức độ ưu tiên (Thấp, Trung bình, Cao)
26
+ - Loại hỗ trợ (dropdown)
27
+ - Loại thiết bị sử dụng (dropdown)
24
28
  - Thông tin liên hệ
25
29
  - Tệp đính kèm (nhiều file)
26
30
  - 🎨 Giao diện native HTML/CSS đẹp mắt
@@ -87,7 +91,7 @@ import 'itsm-widget/dist/itsm-widget.min.js';
87
91
  <script>
88
92
  // Cấu hình widget
89
93
  const itsmWidget = new ITSMWidget({
90
- apiBaseUrl: 'http://localhost:9998', // URL của Backend API (hoặc load từ itsm-widget-config.json)
94
+ // apiBaseUrl sẽ được load tự động từ file itsm-widget-config.json
91
95
  authToken: 'your-jwt-token-here', // JWT token từ đăng nhập thành công
92
96
  position: 'bottom-right',
93
97
  iconText: '📝',
@@ -152,7 +156,7 @@ export class AppComponent implements OnInit {
152
156
  ngOnInit() {
153
157
  // Khởi tạo ITSM Widget
154
158
  const itsmWidget = new ITSMWidget({
155
- apiBaseUrl: 'http://localhost:9998', // URL của Backend API (hoặc load từ itsm-widget-config.json)
159
+ // apiBaseUrl sẽ được load tự động từ file itsm-widget-config.json
156
160
  authToken: 'your-jwt-token-here', // JWT token từ đăng nhập thành công
157
161
  position: 'bottom-right',
158
162
  iconText: '📝',
@@ -173,9 +177,14 @@ export class AppComponent implements OnInit {
173
177
  ### Cách 4: Auto-initialize với data attributes
174
178
 
175
179
  ```html
176
- <script
177
- src="path/to/itsm-widget.min.js"
178
- data-itsm-config='{"apiBaseUrl":"http://localhost:9998","authToken":"your-jwt-token","iconColor":"#007bff"}'>
180
+ <!-- Không khuyến nghị dùng data attributes vì apiBaseUrl phải lấy từ itsm-widget-config.json -->
181
+ <script src="path/to/itsm-widget.min.js"></script>
182
+ <script>
183
+ const widget = new ITSMWidget({
184
+ // apiBaseUrl sẽ được load tự động từ file itsm-widget-config.json
185
+ authToken: 'your-jwt-token',
186
+ iconColor: '#007bff'
187
+ });
179
188
  </script>
180
189
  ```
181
190
 
@@ -183,15 +192,20 @@ export class AppComponent implements OnInit {
183
192
 
184
193
  > **📖 Xem chi tiết**: [CONFIG.md](./CONFIG.md) - Hướng dẫn cấu hình `apiBaseUrl` qua file external
185
194
 
186
- Widget hỗ trợ load `apiBaseUrl` từ file `itsm-widget-config.json` để thể thay đổi sau khi build không cần sửa code. Đặt file config cùng thư mục với `itsm-widget.min.js`:
195
+ **⚠️ QUAN TRỌNG:** `apiBaseUrl` **CHỈ** được lấy từ file `itsm-widget-config.json` - đây cách duy nhất để cấu hình URL của Backend API.
196
+
197
+ Widget **BẮT BUỘC** phải có file `itsm-widget-config.json` để hoạt động. Đặt file config cùng thư mục với `itsm-widget.min.js`:
187
198
 
188
199
  ```json
189
200
  {
190
- "apiBaseUrl": "http://localhost:9998"
201
+ "apiBaseUrl": "http://localhost:2411"
191
202
  }
192
203
  ```
193
204
 
194
- Widget sẽ tự động load config từ file này nếu không truyền `apiBaseUrl` trong constructor.
205
+ **Lưu ý:**
206
+ - Widget sẽ tự động load config từ file này khi khởi tạo
207
+ - **KHÔNG** truyền `apiBaseUrl` trong constructor - nó sẽ bị bỏ qua
208
+ - Nếu file không tồn tại hoặc không có `apiBaseUrl`, widget sẽ throw error và không khởi tạo được
195
209
 
196
210
  ## Build từ source
197
211
 
@@ -240,7 +254,7 @@ Rebuild tự động khi có thay đổi (không có dev server)
240
254
 
241
255
  | Option | Type | Default | Mô tả |
242
256
  |--------|------|---------|-------|
243
- | `apiBaseUrl` | string | `''` | Base URL của Backend API (bắt buộc). thể load từ `itsm-widget-config.json` hoặc truyền trực tiếp. Default: `http://localhost:9998` |
257
+ | `apiBaseUrl` | string | `''` | **KHÔNG truyền vào constructor** - Base URL của Backend API được load tự động từ file `itsm-widget-config.json` (bắt buộc). Xem [CONFIG.md](./CONFIG.md) để biết thêm chi tiết. |
244
258
  | `authToken` | string | `''` | Authorization Bearer token (JWT token từ đăng nhập thành công) (bắt buộc). Đây KHÔNG phải là apiKey. |
245
259
  | `position` | string | `'bottom-right'` | Vị trí icon: `'bottom-right'` hoặc `'bottom-left'` |
246
260
  | `iconText` | string | `'📝'` | Text/emoji hiển thị trên icon (mặc định: 📝) |
@@ -278,6 +292,8 @@ Content-Type: application/json
278
292
  "request": {
279
293
  "requester": { "name": "..." },
280
294
  "category": { "name": "..." },
295
+ "subcategory": { "name": "..." },
296
+ "item": { "name": "..." },
281
297
  "priority": { "name": "..." },
282
298
  "subject": "...",
283
299
  "description": "...",
@@ -300,6 +316,79 @@ Content-Type: multipart/form-data
300
316
  **Body (FormData):**
301
317
  - `files`: File hoặc mảng các file
302
318
 
319
+ **3. Lấy danh sách categories:**
320
+ ```
321
+ POST {apiBaseUrl}/spcit/categories
322
+ ```
323
+
324
+ **Headers:**
325
+ ```
326
+ Authorization: Bearer {authToken}
327
+ Content-Type: application/json
328
+ ```
329
+
330
+ **Body (JSON):**
331
+ ```json
332
+ {
333
+ "list_info": {
334
+ "start_index": 1,
335
+ "page": 1,
336
+ "row_count": 100,
337
+ "sort_field": "name",
338
+ "sort_order": "asc",
339
+ "get_total_count": true
340
+ }
341
+ }
342
+ ```
343
+
344
+ **4. Lấy danh sách subcategories theo category:**
345
+ ```
346
+ POST {apiBaseUrl}/spcit/categories/{categoryId}/subcategories
347
+ ```
348
+
349
+ **Headers:**
350
+ ```
351
+ Authorization: Bearer {authToken}
352
+ Content-Type: application/json
353
+ ```
354
+
355
+ **Body (JSON):**
356
+ ```json
357
+ {
358
+ "list_info": {
359
+ "start_index": 0,
360
+ "row_count": 100,
361
+ "sort_field": "name",
362
+ "sort_order": "asc",
363
+ "get_total_count": true
364
+ }
365
+ }
366
+ ```
367
+
368
+ **5. Lấy danh sách items theo subcategory:**
369
+ ```
370
+ POST {apiBaseUrl}/spcit/subcategories/{subcategoryId}/items
371
+ ```
372
+
373
+ **Headers:**
374
+ ```
375
+ Authorization: Bearer {authToken}
376
+ Content-Type: application/json
377
+ ```
378
+
379
+ **Body (JSON):**
380
+ ```json
381
+ {
382
+ "list_info": {
383
+ "start_index": 0,
384
+ "row_count": 100,
385
+ "sort_field": "name",
386
+ "sort_order": "asc",
387
+ "get_total_count": true
388
+ }
389
+ }
390
+ ```
391
+
303
392
  **Response mẫu (tạo request thành công):**
304
393
  ```json
305
394
  {
@@ -335,7 +424,7 @@ Xem chi tiết API tại file tài liệu trong `src/assets/13082025 API v2 SDP
335
424
 
336
425
  ```javascript
337
426
  const widget = new ITSMWidget({
338
- apiBaseUrl: 'http://localhost:9998', // URL của Backend API (hoặc load từ itsm-widget-config.json)
427
+ // apiBaseUrl sẽ được load tự động từ file itsm-widget-config.json
339
428
  authToken: 'your-jwt-token-here', // JWT token từ đăng nhập thành công
340
429
  iconColor: '#28a745',
341
430
  requesterName: 'Nguyễn Văn A', // Optional
@@ -350,7 +439,8 @@ const widget = new ITSMWidget({
350
439
  **Lưu ý:**
351
440
  - `apiKey` và `tenant` đã được config ở Backend (không cần truyền từ FE)
352
441
  - `authToken` là JWT token từ đăng nhập, không phải `apiKey`
353
- - `apiBaseUrl` thể được load từ file `itsm-widget-config.json` (xem [CONFIG.md](./CONFIG.md))
442
+ - `apiBaseUrl` **BẮT BUỘC** phải trong file `itsm-widget-config.json` (xem [CONFIG.md](./CONFIG.md))
443
+ - **KHÔNG** truyền `apiBaseUrl` vào constructor - nó sẽ bị bỏ qua
354
444
 
355
445
  ### Custom styling
356
446
 
@@ -1,4 +1,4 @@
1
1
  {
2
- "apiBaseUrl": "http://localhost:9998"
2
+ "apiBaseUrl": "http://10.170.210.202:2411"
3
3
  }
4
4
 
@@ -1 +1 @@
1
- .itsm-widget-button{align-items:center;background-color:#fff;border:3px solid transparent;border-radius:50%;cursor:pointer;display:flex;justify-content:center;overflow:visible;padding:4px;position:fixed;transition:transform .3s ease,box-shadow .3s ease;z-index:9998}.itsm-widget-button:hover{box-shadow:0 4px 12px rgba(0,0,0,.15);transform:scale(1.1)}.itsm-position-bottom-right{bottom:24px;right:24px}.itsm-position-bottom-left{bottom:24px;left:24px}.itsm-widget-icon{align-items:center;background-color:#fff;border-radius:50%;color:#fff;display:flex;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:18px;font-weight:700;height:48px;justify-content:center;overflow:hidden;padding:8px;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:48px}.itsm-widget-icon-img{filter:brightness(0) saturate(100%) invert(27%) sepia(78%) saturate(1250%) hue-rotate(197deg) brightness(96%) contrast(95%);height:100%;-o-object-fit:contain;object-fit:contain;width:100%}.itsm-modal-overlay{align-items:center;background-color:rgba(0,0,0,.5);bottom:0;display:none;justify-content:center;left:0;opacity:0;padding:16px;position:fixed;right:0;top:0;transition:opacity .3s ease;z-index:9999}.itsm-modal-overlay.itsm-modal-active{display:flex;opacity:1}.itsm-modal-container{background:#fff;border-radius:6px;box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12);display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;max-height:90vh;max-width:50vw;transform:scale(.9);transition:transform .3s ease;width:100%}.itsm-modal-overlay.itsm-modal-active .itsm-modal-container{transform:scale(1)}.itsm-modal-header{align-items:center;background-color:#fff;border-bottom:1px solid #e9ecef;border-radius:6px 6px 0 0;display:flex;flex-shrink:0;justify-content:space-between;padding:1.5rem}.itsm-modal-title{color:#212529;flex:1;font-size:1.5rem;font-weight:600;margin:0}.itsm-modal-header-icons{align-items:center;display:flex;gap:.5rem}.itsm-modal-close{align-items:center;background:transparent;border:none;border-radius:50%;color:#6c757d;cursor:pointer;display:flex;font-size:1.5rem;height:2rem;justify-content:center;line-height:1;padding:.5rem;transition:background-color .2s ease,color .2s ease;width:2rem}.itsm-modal-close:hover{background-color:#e9ecef;color:#212529}.itsm-modal-close span{font-size:1.5rem;line-height:1}.itsm-modal-content{flex:1;overflow-y:auto;padding:1.5rem}.itsm-modal-footer{background-color:#fff;border-radius:0 0 6px 6px;border-top:1px solid #e9ecef;display:flex;flex-shrink:0;gap:.5rem;justify-content:flex-end;padding:1rem 1.5rem}.itsm-form{flex-direction:column}.itsm-form,.itsm-form-row{display:flex;gap:1rem}.itsm-form-group{display:flex;flex-direction:column;gap:.5rem}.itsm-form-group-half{flex:1}.itsm-form-group label{color:#495057;display:block;font-size:.875rem;font-weight:500;margin-bottom:.25rem}.itsm-required{color:#dc3545;margin-left:2px}.itsm-input{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;font-family:inherit;font-size:1rem;line-height:1.5;padding:.5rem .75rem;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-input:hover{border-color:#adb5bd}.itsm-input:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-input:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:1}.itsm-textarea{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;font-family:inherit;font-size:1rem;line-height:1.5;min-height:100px;padding:.5rem .75rem;resize:vertical;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-textarea:hover{border-color:#adb5bd}.itsm-textarea:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-textarea:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:1}.itsm-editor-container{background-color:#fff;border:1px solid #ced4da;border-radius:4px;transition:border-color .2s,box-shadow .2s}.itsm-editor-container:focus-within{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.itsm-editor-toolbar{align-items:center;background-color:#f8f9fa;border-bottom:1px solid #e9ecef;display:flex;flex-wrap:wrap;gap:.25rem;padding:.5rem}.itsm-editor-btn{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:3px;color:#495057;cursor:pointer;display:inline-flex;font-family:inherit;font-size:.875rem;height:2rem;justify-content:center;min-width:2rem;padding:.25rem .5rem;transition:background-color .2s,border-color .2s}.itsm-editor-btn:hover{background-color:#e9ecef;border-color:#adb5bd}.itsm-editor-btn:active{background-color:#dee2e6}.itsm-editor-separator{background-color:#ced4da;height:1.5rem;margin:0 .25rem;width:1px}.itsm-editor-file-btn{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:3px;color:#495057;cursor:pointer;display:inline-flex;font-family:inherit;font-size:.875rem;justify-content:center;margin-left:auto;padding:.25rem .5rem;transition:background-color .2s,border-color .2s}.itsm-editor-file-btn:hover{background-color:#e9ecef;border-color:#adb5bd}.itsm-editor-content{color:#495057;font-family:inherit;font-size:1rem;line-height:1.5;max-height:400px;min-height:150px;outline:none;overflow-y:auto;padding:.75rem}.itsm-editor-content:empty:before{color:#6c757d;content:attr(data-placeholder);pointer-events:none}.itsm-editor-content:focus:empty:before{content:""}.itsm-editor-content img{height:auto;margin:.5rem 0;max-width:100%}.itsm-editor-content ol,.itsm-editor-content ul{margin:.5rem 0;padding-left:1.5rem}.itsm-editor-content p{margin:.5rem 0}.itsm-editor-content p:first-child{margin-top:0}.itsm-editor-content p:last-child{margin-bottom:0}.itsm-select{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;cursor:pointer;font-family:inherit;font-size:1rem;line-height:1.5;padding:.5rem .75rem;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-select:hover{border-color:#adb5bd}.itsm-select:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-select:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:.6}.itsm-btn{align-items:center;border:1px solid transparent;border-radius:4px;cursor:pointer;display:inline-flex;font-family:inherit;font-size:1rem;font-weight:400;justify-content:center;line-height:1.5;overflow:hidden;padding:.5rem 1rem;position:relative;text-align:center;transition:background-color .2s,border-color .2s,box-shadow .2s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.itsm-btn:disabled{cursor:default;opacity:.6}.itsm-btn-text{line-height:inherit}.itsm-btn-primary{background-color:#007bff;border-color:#007bff;color:#fff}.itsm-btn-primary:hover:not(:disabled){background-color:#0056b3;border-color:#0056b3}.itsm-btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5);outline:0 none;outline-offset:0}.itsm-btn-primary:active:not(:disabled){background-color:#004085;border-color:#004085}.itsm-btn-secondary{background-color:#6c757d;border-color:#6c757d;color:#fff}.itsm-btn-secondary:hover:not(:disabled){background-color:#5a6268;border-color:#545b62}.itsm-btn-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5);outline:0 none;outline-offset:0}.itsm-btn-secondary:active:not(:disabled){background-color:#545b62;border-color:#4e555b}.itsm-file-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}.itsm-file-item{align-items:center;background-color:#f8f9fa;border-radius:4px;display:flex;font-size:.875rem;justify-content:space-between;padding:.5rem}.itsm-file-name{color:#495057;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.itsm-file-size{color:#6c757d;margin-left:.5rem}.itsm-message{border-radius:4px;box-shadow:0 4px 6px rgba(0,0,0,.1);font-family:inherit;font-size:.875rem;max-width:500px;min-width:300px;padding:1rem 1.5rem;position:fixed;right:20px;top:20px;transform:translateX(400px);transition:transform .3s ease;z-index:10000}.itsm-message.itsm-message-show{transform:translateX(0)}.itsm-message-success{background-color:#d4edda;border:1px solid #c3e6cb;color:#155724}.itsm-message-error{background-color:#f8d7da;border:1px solid #f5c6cb;color:#721c24}.itsm-message-info{background-color:#d1ecf1;border:1px solid #bee5eb;color:#0c5460}@media (max-width:768px){.itsm-modal-container{max-height:95vh;max-width:95%}.itsm-form-row{flex-direction:column}.itsm-form-group-half{flex:none}.itsm-modal-content,.itsm-modal-footer,.itsm-modal-header{padding:1rem}}
1
+ .itsm-widget-button{align-items:center;background-color:#fff;border:3px solid transparent;border-radius:50%;cursor:pointer;display:flex;justify-content:center;overflow:visible;padding:4px;position:fixed;transition:transform .3s ease,box-shadow .3s ease;z-index:9998}.itsm-widget-button:hover{box-shadow:0 4px 12px rgba(0,0,0,.15);transform:scale(1.1)}.itsm-position-bottom-right{bottom:24px;right:24px}.itsm-position-bottom-left{bottom:24px;left:24px}.itsm-widget-icon{align-items:center;background-color:#fff;border-radius:50%;color:#fff;display:flex;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:18px;font-weight:700;height:48px;justify-content:center;overflow:hidden;padding:8px;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:48px}.itsm-widget-icon-img{filter:brightness(0) saturate(100%) invert(27%) sepia(78%) saturate(1250%) hue-rotate(197deg) brightness(96%) contrast(95%);height:100%;-o-object-fit:contain;object-fit:contain;width:100%}.itsm-modal-overlay{align-items:center;background-color:rgba(0,0,0,.5);bottom:0;display:none;justify-content:center;left:0;opacity:0;padding:16px;position:fixed;right:0;top:0;transition:opacity .3s ease;z-index:9999}.itsm-modal-overlay.itsm-modal-active{display:flex;opacity:1}.itsm-modal-container{background:#fff;border-radius:6px;box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12);display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;max-height:90vh;max-width:50vw;transform:scale(.9);transition:transform .3s ease;width:100%}.itsm-modal-overlay.itsm-modal-active .itsm-modal-container{transform:scale(1)}.itsm-modal-header{align-items:center;background-color:#fff;border-bottom:1px solid #e9ecef;border-radius:6px 6px 0 0;display:flex;flex-shrink:0;justify-content:space-between;padding:1.5rem}.itsm-modal-title{color:#212529;flex:1;font-size:1.5rem;font-weight:600;margin:0}.itsm-modal-header-icons{align-items:center;display:flex;gap:.5rem}.itsm-modal-close{align-items:center;background:transparent;border:none;border-radius:50%;color:#6c757d;cursor:pointer;display:flex;font-size:1.5rem;height:2rem;justify-content:center;line-height:1;padding:.5rem;transition:background-color .2s ease,color .2s ease;width:2rem}.itsm-modal-close:hover{background-color:#e9ecef;color:#212529}.itsm-modal-close span{font-size:1.5rem;line-height:1}.itsm-modal-content{flex:1;overflow-y:auto;padding:1.5rem}.itsm-modal-footer{background-color:#fff;border-radius:0 0 6px 6px;border-top:1px solid #e9ecef;display:flex;flex-shrink:0;gap:.5rem;justify-content:flex-end;padding:1rem 1.5rem}.itsm-form{flex-direction:column}.itsm-form,.itsm-form-row{display:flex;gap:1rem}.itsm-form-group{display:flex;flex-direction:column;gap:.5rem}.itsm-form-group-half{flex:1}.itsm-form-group label{color:#495057;display:block;font-size:.875rem;font-weight:500;margin-bottom:.25rem}.itsm-required{color:#dc3545;margin-left:2px}.itsm-input{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;font-family:inherit;font-size:1rem;line-height:1.5;padding:.5rem .75rem;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-input:hover{border-color:#adb5bd}.itsm-input:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-input:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:1}.itsm-textarea{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;font-family:inherit;font-size:1rem;line-height:1.5;min-height:100px;padding:.5rem .75rem;resize:vertical;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-textarea:hover{border-color:#adb5bd}.itsm-textarea:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-textarea:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:1}.itsm-editor-container{background-color:#fff;border:1px solid #ced4da;border-radius:4px;transition:border-color .2s,box-shadow .2s}.itsm-editor-container:focus-within{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.itsm-editor-toolbar{align-items:center;background-color:#f8f9fa;border-bottom:1px solid #e9ecef;display:flex;flex-wrap:wrap;gap:.25rem;padding:.5rem}.itsm-editor-btn{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:3px;color:#495057;cursor:pointer;display:inline-flex;font-family:inherit;font-size:.875rem;height:2rem;justify-content:center;min-width:2rem;padding:.25rem .5rem;transition:background-color .2s,border-color .2s}.itsm-editor-btn:hover{background-color:#e9ecef;border-color:#adb5bd}.itsm-editor-btn:active{background-color:#dee2e6}.itsm-editor-separator{background-color:#ced4da;height:1.5rem;margin:0 .25rem;width:1px}.itsm-editor-file-btn{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:3px;color:#495057;cursor:pointer;display:inline-flex;font-family:inherit;font-size:.875rem;justify-content:center;margin-left:auto;padding:.25rem .5rem;transition:background-color .2s,border-color .2s}.itsm-editor-file-btn:hover{background-color:#e9ecef;border-color:#adb5bd}.itsm-editor-content{color:#495057;font-family:inherit;font-size:1rem;line-height:1.5;max-height:400px;min-height:150px;outline:none;overflow-y:auto;padding:.75rem}.itsm-editor-content:empty:before{color:#6c757d;content:attr(data-placeholder);pointer-events:none}.itsm-editor-content:focus:empty:before{content:""}.itsm-editor-content img{height:auto;margin:.5rem 0;max-width:100%}.itsm-editor-content ol,.itsm-editor-content ul{margin:.5rem 0;padding-left:1.5rem}.itsm-editor-content p{margin:.5rem 0}.itsm-editor-content p:first-child{margin-top:0}.itsm-editor-content p:last-child{margin-bottom:0}.itsm-filter-input{background-color:#fff;border:1px solid #ced4da;border-bottom:none;border-radius:4px 4px 0 0;box-sizing:border-box;color:#495057;font-family:inherit;font-size:.875rem;line-height:1.5;margin-bottom:0;padding:.5rem .75rem;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-filter-input:hover{border-color:#adb5bd}.itsm-filter-input:focus{border-color:#007bff #007bff #ced4da;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-filter-input:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:1}.itsm-select{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;cursor:pointer;font-family:inherit;font-size:1rem;line-height:1.5;padding:.5rem .75rem;transition:background-color .2s,border-color .2s,box-shadow .2s;width:100%}.itsm-form-group:has(.itsm-filter-input) .itsm-select{border-radius:0 0 4px 4px;border-top:none}.itsm-select:hover{border-color:#adb5bd}.itsm-select:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-select:disabled{background-color:#e9ecef;cursor:not-allowed;opacity:.6}.itsm-custom-select{position:relative;width:100%}.itsm-select-display{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-sizing:border-box;color:#495057;cursor:pointer;display:flex;font-family:inherit;font-size:1rem;justify-content:space-between;line-height:1.5;padding:.5rem .75rem;transition:background-color .2s,border-color .2s,box-shadow .2s;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.itsm-select-display:hover:not(.disabled){border-color:#adb5bd}.itsm-select-display:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25);outline:0 none;outline-offset:0}.itsm-select-display.disabled{background-color:#e9ecef;cursor:not-allowed;opacity:.6}.itsm-select-text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.itsm-select-arrow{color:#6c757d;font-size:.75rem;margin-left:.5rem;transition:transform .2s}.itsm-custom-select[data-disabled=true] .itsm-select-arrow{opacity:.5}.itsm-select-dropdown{background-color:#fff;border:1px solid #ced4da;border-radius:4px;box-shadow:0 4px 6px rgba(0,0,0,.1);display:none;flex-direction:column;left:0;margin-top:.25rem;max-height:300px;overflow:hidden;position:absolute;right:0;top:100%;z-index:1000}.itsm-select-dropdown.open{display:flex}.itsm-select-filter{background-color:#fff;border:none;border-bottom:1px solid #e9ecef;box-sizing:border-box;color:#495057;font-family:inherit;font-size:.875rem;line-height:1.5;outline:none;padding:.5rem .75rem;width:100%}.itsm-select-filter:focus{border-bottom-color:#007bff}.itsm-select-filter:disabled{background-color:#f8f9fa;cursor:not-allowed}.itsm-select-options{max-height:250px;overflow-y:auto;padding:.25rem 0}.itsm-select-option{color:#495057;cursor:pointer;font-size:1rem;line-height:1.5;padding:.5rem .75rem;transition:background-color .15s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.itsm-select-option:hover{background-color:#f8f9fa}.itsm-select-option:active{background-color:#e9ecef}.itsm-select-option[style*="display: none"]{display:none!important}.itsm-btn{align-items:center;border:1px solid transparent;border-radius:4px;cursor:pointer;display:inline-flex;font-family:inherit;font-size:1rem;font-weight:400;justify-content:center;line-height:1.5;overflow:hidden;padding:.5rem 1rem;position:relative;text-align:center;transition:background-color .2s,border-color .2s,box-shadow .2s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.itsm-btn:disabled{cursor:default;opacity:.6}.itsm-btn-text{line-height:inherit}.itsm-btn-primary{background-color:#007bff;border-color:#007bff;color:#fff}.itsm-btn-primary:hover:not(:disabled){background-color:#0056b3;border-color:#0056b3}.itsm-btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5);outline:0 none;outline-offset:0}.itsm-btn-primary:active:not(:disabled){background-color:#004085;border-color:#004085}.itsm-btn-secondary{background-color:#6c757d;border-color:#6c757d;color:#fff}.itsm-btn-secondary:hover:not(:disabled){background-color:#5a6268;border-color:#545b62}.itsm-btn-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5);outline:0 none;outline-offset:0}.itsm-btn-secondary:active:not(:disabled){background-color:#545b62;border-color:#4e555b}.itsm-file-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}.itsm-file-item{align-items:center;background-color:#f8f9fa;border-radius:4px;display:flex;font-size:.875rem;justify-content:space-between;padding:.5rem}.itsm-file-name{color:#495057;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.itsm-file-size{color:#6c757d;margin-left:.5rem}.itsm-message{border-radius:4px;box-shadow:0 4px 6px rgba(0,0,0,.1);font-family:inherit;font-size:.875rem;max-width:500px;min-width:300px;padding:1rem 1.5rem;position:fixed;right:20px;top:20px;transform:translateX(400px);transition:transform .3s ease;z-index:10000}.itsm-message.itsm-message-show{transform:translateX(0)}.itsm-message-success{background-color:#d4edda;border:1px solid #c3e6cb;color:#155724}.itsm-message-error{background-color:#f8d7da;border:1px solid #f5c6cb;color:#721c24}.itsm-message-info{background-color:#d1ecf1;border:1px solid #bee5eb;color:#0c5460}@media (max-width:768px){.itsm-modal-container{max-height:95vh;max-width:95%}.itsm-form-row{flex-direction:column}.itsm-form-group-half{flex:none}.itsm-modal-content,.itsm-modal-footer,.itsm-modal-header{padding:1rem}}