verben-ticketing-ui 0.6.0

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.
Files changed (237) hide show
  1. package/README.md +25 -0
  2. package/esm2022/public-api.mjs +9 -0
  3. package/esm2022/src/lib/components/add-ticket/add-ticket.component.mjs +306 -0
  4. package/esm2022/src/lib/components/add-ticket/add-ticket.module.mjs +98 -0
  5. package/esm2022/src/lib/components/add-ticket/index.mjs +2 -0
  6. package/esm2022/src/lib/components/add-ticket/public-api.mjs +3 -0
  7. package/esm2022/src/lib/components/add-ticket/verben-ticketing-ui-src-lib-components-add-ticket.mjs +5 -0
  8. package/esm2022/src/lib/components/file-render/file-render.component.mjs +34 -0
  9. package/esm2022/src/lib/components/file-render/file-render.module.mjs +94 -0
  10. package/esm2022/src/lib/components/file-render/index.mjs +2 -0
  11. package/esm2022/src/lib/components/file-render/public-api.mjs +3 -0
  12. package/esm2022/src/lib/components/file-render/verben-ticketing-ui-src-lib-components-file-render.mjs +5 -0
  13. package/esm2022/src/lib/components/ticketing/base-table-style.mjs +56 -0
  14. package/esm2022/src/lib/components/ticketing/index.mjs +2 -0
  15. package/esm2022/src/lib/components/ticketing/public-api.mjs +4 -0
  16. package/esm2022/src/lib/components/ticketing/ticketing.component.mjs +813 -0
  17. package/esm2022/src/lib/components/ticketing/ticketing.module.mjs +96 -0
  18. package/esm2022/src/lib/components/ticketing/ticketing.service.mjs +220 -0
  19. package/esm2022/src/lib/components/ticketing/verben-ticketing-ui-src-lib-components-ticketing.mjs +5 -0
  20. package/esm2022/src/lib/components/ticketing-detail/chat-control/chat-control.component.mjs +240 -0
  21. package/esm2022/src/lib/components/ticketing-detail/chat-control/chat-control.module.mjs +98 -0
  22. package/esm2022/src/lib/components/ticketing-detail/index.mjs +2 -0
  23. package/esm2022/src/lib/components/ticketing-detail/public-api.mjs +3 -0
  24. package/esm2022/src/lib/components/ticketing-detail/ticketing-detail.component.mjs +592 -0
  25. package/esm2022/src/lib/components/ticketing-detail/ticketing-detail.module.mjs +109 -0
  26. package/esm2022/src/lib/components/ticketing-detail/verben-ticketing-ui-src-lib-components-ticketing-detail.mjs +5 -0
  27. package/esm2022/src/lib/constants/index.mjs +2 -0
  28. package/esm2022/src/lib/constants/keynames.mjs +2 -0
  29. package/esm2022/src/lib/constants/pagesize.mjs +3 -0
  30. package/esm2022/src/lib/constants/public-api.mjs +3 -0
  31. package/esm2022/src/lib/constants/verben-ticketing-ui-src-lib-constants.mjs +5 -0
  32. package/esm2022/src/lib/models/Application.mjs +2 -0
  33. package/esm2022/src/lib/models/ApplicationRoleAction.mjs +2 -0
  34. package/esm2022/src/lib/models/ApplicationRoleContext.mjs +2 -0
  35. package/esm2022/src/lib/models/BaseModel.mjs +2 -0
  36. package/esm2022/src/lib/models/CacheDataRequest.mjs +2 -0
  37. package/esm2022/src/lib/models/Company.mjs +2 -0
  38. package/esm2022/src/lib/models/ErrorResponse.mjs +11 -0
  39. package/esm2022/src/lib/models/File.mjs +2 -0
  40. package/esm2022/src/lib/models/FilterTypes.mjs +9 -0
  41. package/esm2022/src/lib/models/HttpRequestType.mjs +8 -0
  42. package/esm2022/src/lib/models/KeyValueParam.mjs +2 -0
  43. package/esm2022/src/lib/models/ModelRelationship.mjs +2 -0
  44. package/esm2022/src/lib/models/OAuthParam.mjs +2 -0
  45. package/esm2022/src/lib/models/OAuthType.mjs +9 -0
  46. package/esm2022/src/lib/models/PagedResult.mjs +2 -0
  47. package/esm2022/src/lib/models/PasswordRequestParam.mjs +2 -0
  48. package/esm2022/src/lib/models/PermissionConfig.mjs +2 -0
  49. package/esm2022/src/lib/models/ResponseKeyValue.mjs +2 -0
  50. package/esm2022/src/lib/models/Role.mjs +2 -0
  51. package/esm2022/src/lib/models/RoleAction.mjs +2 -0
  52. package/esm2022/src/lib/models/RoleContext.mjs +2 -0
  53. package/esm2022/src/lib/models/Schedule.mjs +2 -0
  54. package/esm2022/src/lib/models/ScheduleType.mjs +6 -0
  55. package/esm2022/src/lib/models/SearchDynamicWrapper.mjs +2 -0
  56. package/esm2022/src/lib/models/SearchOperator.mjs +6 -0
  57. package/esm2022/src/lib/models/SearchPropertySign.mjs +11 -0
  58. package/esm2022/src/lib/models/SearchPropertyValue.mjs +2 -0
  59. package/esm2022/src/lib/models/SearchPropertyValueType.mjs +11 -0
  60. package/esm2022/src/lib/models/SortDirection.mjs +7 -0
  61. package/esm2022/src/lib/models/TaskUser.mjs +2 -0
  62. package/esm2022/src/lib/models/Tenant.mjs +2 -0
  63. package/esm2022/src/lib/models/TenantConfig.mjs +2 -0
  64. package/esm2022/src/lib/models/TenantPriority.mjs +2 -0
  65. package/esm2022/src/lib/models/Ticket.mjs +2 -0
  66. package/esm2022/src/lib/models/TicketMessage.mjs +2 -0
  67. package/esm2022/src/lib/models/TicketStatus.mjs +8 -0
  68. package/esm2022/src/lib/models/UserRequest.mjs +2 -0
  69. package/esm2022/src/lib/models/auth-mechanism.mjs +8 -0
  70. package/esm2022/src/lib/models/base.mjs +2 -0
  71. package/esm2022/src/lib/models/conditions.mjs +13 -0
  72. package/esm2022/src/lib/models/index.mjs +2 -0
  73. package/esm2022/src/lib/models/log-in.mjs +2 -0
  74. package/esm2022/src/lib/models/mainUser.mjs +2 -0
  75. package/esm2022/src/lib/models/oauth-resp.mjs +2 -0
  76. package/esm2022/src/lib/models/oauth-response.mjs +2 -0
  77. package/esm2022/src/lib/models/object-state.mjs +8 -0
  78. package/esm2022/src/lib/models/otpValue.mjs +2 -0
  79. package/esm2022/src/lib/models/paged.mjs +2 -0
  80. package/esm2022/src/lib/models/public-api.mjs +57 -0
  81. package/esm2022/src/lib/models/query-params.mjs +2 -0
  82. package/esm2022/src/lib/models/request-status.mjs +5 -0
  83. package/esm2022/src/lib/models/resend-otp-data.mjs +2 -0
  84. package/esm2022/src/lib/models/sign-up.mjs +2 -0
  85. package/esm2022/src/lib/models/single-sign-on.mjs +2 -0
  86. package/esm2022/src/lib/models/status.mjs +6 -0
  87. package/esm2022/src/lib/models/user-access-request-status.mjs +7 -0
  88. package/esm2022/src/lib/models/user-access-request.mjs +2 -0
  89. package/esm2022/src/lib/models/user-management.mjs +2 -0
  90. package/esm2022/src/lib/models/user.mjs +2 -0
  91. package/esm2022/src/lib/models/verben-ticketing-ui-src-lib-models.mjs +5 -0
  92. package/esm2022/src/lib/pipes/async-label.pipe.mjs +26 -0
  93. package/esm2022/src/lib/pipes/company-date.pipe.mjs +30 -0
  94. package/esm2022/src/lib/pipes/ellipse-word.pipe.mjs +23 -0
  95. package/esm2022/src/lib/pipes/index.mjs +2 -0
  96. package/esm2022/src/lib/pipes/public-api.mjs +4 -0
  97. package/esm2022/src/lib/pipes/verben-ticketing-ui-src-lib-pipes.mjs +5 -0
  98. package/esm2022/src/lib/services/company-settings.service.mjs +23 -0
  99. package/esm2022/src/lib/services/environment.service.mjs +26 -0
  100. package/esm2022/src/lib/services/http-web-request.service.mjs +100 -0
  101. package/esm2022/src/lib/services/import.service.mjs +247 -0
  102. package/esm2022/src/lib/services/index.mjs +2 -0
  103. package/esm2022/src/lib/services/label.service.mjs +261 -0
  104. package/esm2022/src/lib/services/public-api.mjs +8 -0
  105. package/esm2022/src/lib/services/shared.service.mjs +58 -0
  106. package/esm2022/src/lib/services/util.service.mjs +72 -0
  107. package/esm2022/src/lib/services/verben-ticketing-ui-src-lib-services.mjs +5 -0
  108. package/esm2022/src/utils/components/base-data-view.component.mjs +111 -0
  109. package/esm2022/src/utils/index.mjs +2 -0
  110. package/esm2022/src/utils/models/DataViewConfig.mjs +2 -0
  111. package/esm2022/src/utils/public-api.mjs +3 -0
  112. package/esm2022/src/utils/verben-ticketing-ui-src-utils.mjs +5 -0
  113. package/esm2022/verben-ticketing-ui.mjs +5 -0
  114. package/fesm2022/verben-ticketing-ui-src-lib-components-add-ticket.mjs +403 -0
  115. package/fesm2022/verben-ticketing-ui-src-lib-components-add-ticket.mjs.map +1 -0
  116. package/fesm2022/verben-ticketing-ui-src-lib-components-file-render.mjs +130 -0
  117. package/fesm2022/verben-ticketing-ui-src-lib-components-file-render.mjs.map +1 -0
  118. package/fesm2022/verben-ticketing-ui-src-lib-components-ticketing-detail.mjs +1018 -0
  119. package/fesm2022/verben-ticketing-ui-src-lib-components-ticketing-detail.mjs.map +1 -0
  120. package/fesm2022/verben-ticketing-ui-src-lib-components-ticketing.mjs +1166 -0
  121. package/fesm2022/verben-ticketing-ui-src-lib-components-ticketing.mjs.map +1 -0
  122. package/fesm2022/verben-ticketing-ui-src-lib-constants.mjs +11 -0
  123. package/fesm2022/verben-ticketing-ui-src-lib-constants.mjs.map +1 -0
  124. package/fesm2022/verben-ticketing-ui-src-lib-models.mjs +138 -0
  125. package/fesm2022/verben-ticketing-ui-src-lib-models.mjs.map +1 -0
  126. package/fesm2022/verben-ticketing-ui-src-lib-pipes.mjs +81 -0
  127. package/fesm2022/verben-ticketing-ui-src-lib-pipes.mjs.map +1 -0
  128. package/fesm2022/verben-ticketing-ui-src-lib-services.mjs +770 -0
  129. package/fesm2022/verben-ticketing-ui-src-lib-services.mjs.map +1 -0
  130. package/fesm2022/verben-ticketing-ui-src-utils.mjs +118 -0
  131. package/fesm2022/verben-ticketing-ui-src-utils.mjs.map +1 -0
  132. package/fesm2022/verben-ticketing-ui.mjs +14 -0
  133. package/fesm2022/verben-ticketing-ui.mjs.map +1 -0
  134. package/index.d.ts +5 -0
  135. package/package.json +82 -0
  136. package/public-api.d.ts +4 -0
  137. package/src/lib/components/add-ticket/add-ticket.component.d.ts +44 -0
  138. package/src/lib/components/add-ticket/add-ticket.module.d.ts +15 -0
  139. package/src/lib/components/add-ticket/index.d.ts +1 -0
  140. package/src/lib/components/add-ticket/public-api.d.ts +2 -0
  141. package/src/lib/components/file-render/file-render.component.d.ts +14 -0
  142. package/src/lib/components/file-render/file-render.module.d.ts +14 -0
  143. package/src/lib/components/file-render/index.d.ts +1 -0
  144. package/src/lib/components/file-render/public-api.d.ts +2 -0
  145. package/src/lib/components/ticketing/base-table-style.d.ts +1 -0
  146. package/src/lib/components/ticketing/index.d.ts +1 -0
  147. package/src/lib/components/ticketing/public-api.d.ts +3 -0
  148. package/src/lib/components/ticketing/ticketing.component.d.ts +100 -0
  149. package/src/lib/components/ticketing/ticketing.module.d.ts +12 -0
  150. package/src/lib/components/ticketing/ticketing.service.d.ts +27 -0
  151. package/src/lib/components/ticketing-detail/chat-control/chat-control.component.d.ts +47 -0
  152. package/src/lib/components/ticketing-detail/chat-control/chat-control.module.d.ts +15 -0
  153. package/src/lib/components/ticketing-detail/index.d.ts +1 -0
  154. package/src/lib/components/ticketing-detail/public-api.d.ts +2 -0
  155. package/src/lib/components/ticketing-detail/ticketing-detail.component.d.ts +77 -0
  156. package/src/lib/components/ticketing-detail/ticketing-detail.module.d.ts +17 -0
  157. package/src/lib/constants/index.d.ts +1 -0
  158. package/src/lib/constants/keynames.d.ts +1 -0
  159. package/src/lib/constants/pagesize.d.ts +2 -0
  160. package/src/lib/constants/public-api.d.ts +2 -0
  161. package/src/lib/models/Application.d.ts +5 -0
  162. package/src/lib/models/ApplicationRoleAction.d.ts +9 -0
  163. package/src/lib/models/ApplicationRoleContext.d.ts +9 -0
  164. package/src/lib/models/BaseModel.d.ts +14 -0
  165. package/src/lib/models/CacheDataRequest.d.ts +7 -0
  166. package/src/lib/models/Company.d.ts +22 -0
  167. package/src/lib/models/ErrorResponse.d.ts +6 -0
  168. package/src/lib/models/File.d.ts +7 -0
  169. package/src/lib/models/FilterTypes.d.ts +6 -0
  170. package/src/lib/models/HttpRequestType.d.ts +6 -0
  171. package/src/lib/models/KeyValueParam.d.ts +5 -0
  172. package/src/lib/models/ModelRelationship.d.ts +7 -0
  173. package/src/lib/models/OAuthParam.d.ts +9 -0
  174. package/src/lib/models/OAuthType.d.ts +7 -0
  175. package/src/lib/models/PagedResult.d.ts +9 -0
  176. package/src/lib/models/PasswordRequestParam.d.ts +5 -0
  177. package/src/lib/models/PermissionConfig.d.ts +7 -0
  178. package/src/lib/models/ResponseKeyValue.d.ts +5 -0
  179. package/src/lib/models/Role.d.ts +9 -0
  180. package/src/lib/models/RoleAction.d.ts +10 -0
  181. package/src/lib/models/RoleContext.d.ts +10 -0
  182. package/src/lib/models/Schedule.d.ts +22 -0
  183. package/src/lib/models/ScheduleType.d.ts +4 -0
  184. package/src/lib/models/SearchDynamicWrapper.d.ts +7 -0
  185. package/src/lib/models/SearchOperator.d.ts +4 -0
  186. package/src/lib/models/SearchPropertySign.d.ts +9 -0
  187. package/src/lib/models/SearchPropertyValue.d.ts +14 -0
  188. package/src/lib/models/SearchPropertyValueType.d.ts +9 -0
  189. package/src/lib/models/SortDirection.d.ts +5 -0
  190. package/src/lib/models/TaskUser.d.ts +10 -0
  191. package/src/lib/models/Tenant.d.ts +22 -0
  192. package/src/lib/models/TenantConfig.d.ts +20 -0
  193. package/src/lib/models/TenantPriority.d.ts +10 -0
  194. package/src/lib/models/Ticket.d.ts +21 -0
  195. package/src/lib/models/TicketMessage.d.ts +11 -0
  196. package/src/lib/models/TicketStatus.d.ts +6 -0
  197. package/src/lib/models/UserRequest.d.ts +32 -0
  198. package/src/lib/models/auth-mechanism.d.ts +14 -0
  199. package/src/lib/models/base.d.ts +12 -0
  200. package/src/lib/models/conditions.d.ts +11 -0
  201. package/src/lib/models/index.d.ts +1 -0
  202. package/src/lib/models/log-in.d.ts +8 -0
  203. package/src/lib/models/mainUser.d.ts +19 -0
  204. package/src/lib/models/oauth-resp.d.ts +6 -0
  205. package/src/lib/models/oauth-response.d.ts +6 -0
  206. package/src/lib/models/object-state.d.ts +6 -0
  207. package/src/lib/models/otpValue.d.ts +9 -0
  208. package/src/lib/models/paged.d.ts +9 -0
  209. package/src/lib/models/public-api.d.ts +56 -0
  210. package/src/lib/models/query-params.d.ts +7 -0
  211. package/src/lib/models/request-status.d.ts +3 -0
  212. package/src/lib/models/resend-otp-data.d.ts +8 -0
  213. package/src/lib/models/sign-up.d.ts +9 -0
  214. package/src/lib/models/single-sign-on.d.ts +7 -0
  215. package/src/lib/models/status.d.ts +4 -0
  216. package/src/lib/models/user-access-request-status.d.ts +5 -0
  217. package/src/lib/models/user-access-request.d.ts +9 -0
  218. package/src/lib/models/user-management.d.ts +8 -0
  219. package/src/lib/models/user.d.ts +24 -0
  220. package/src/lib/pipes/async-label.pipe.d.ts +11 -0
  221. package/src/lib/pipes/company-date.pipe.d.ts +10 -0
  222. package/src/lib/pipes/ellipse-word.pipe.d.ts +8 -0
  223. package/src/lib/pipes/index.d.ts +1 -0
  224. package/src/lib/pipes/public-api.d.ts +3 -0
  225. package/src/lib/services/company-settings.service.d.ts +11 -0
  226. package/src/lib/services/environment.service.d.ts +22 -0
  227. package/src/lib/services/http-web-request.service.d.ts +24 -0
  228. package/src/lib/services/import.service.d.ts +14 -0
  229. package/src/lib/services/index.d.ts +1 -0
  230. package/src/lib/services/label.service.d.ts +24 -0
  231. package/src/lib/services/public-api.d.ts +7 -0
  232. package/src/lib/services/shared.service.d.ts +16 -0
  233. package/src/lib/services/util.service.d.ts +15 -0
  234. package/src/utils/components/base-data-view.component.d.ts +34 -0
  235. package/src/utils/index.d.ts +1 -0
  236. package/src/utils/models/DataViewConfig.d.ts +11 -0
  237. package/src/utils/public-api.d.ts +2 -0
@@ -0,0 +1,1018 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Component, Input, Output, ViewChildren, NgModule } from '@angular/core';
3
+ import { TicketStatus, ObjectState, ErrorResponse, SearchOperator, SearchPropertySign, SortDirection } from 'verben-ticketing-ui/src/lib/models';
4
+ import { DEFAULT_MESSAGE_FOR_INITIAL_UPLOAD } from 'verben-ticketing-ui/src/lib/constants';
5
+ import { Subject, debounceTime } from 'rxjs';
6
+ import * as i1 from '@angular/router';
7
+ import { RouterModule } from '@angular/router';
8
+ import * as i2$1 from 'verben-ticketing-ui/src/lib/services';
9
+ import * as i3 from '@angular/common';
10
+ import { CommonModule } from '@angular/common';
11
+ import * as i2 from '@angular/forms';
12
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
13
+ import * as i5 from 'verben-ng-ui';
14
+ import { DataTableModule, SvgModule, VerbenPopUpModule, DataViewModule, VerbenaButtonModule, CardDataViewModule, DropDownModule, VerbenaSwitchModule, TableFilterModule, VisibleColumnModule, SortTableModule, DataExportModule, TooltipModule, VerbenDialogueModule, VerbenaInputModule, DataImportModule, DataXportModule } from 'verben-ng-ui';
15
+ import * as i4 from '@ctrl/ngx-emoji-mart';
16
+ import { PickerComponent, PickerModule } from '@ctrl/ngx-emoji-mart';
17
+ import * as i7 from 'verben-ticketing-ui/src/lib/components/file-render';
18
+ import { FileRenderLibModule } from 'verben-ticketing-ui/src/lib/components/file-render';
19
+ import * as i8 from 'verben-ticketing-ui/src/lib/pipes';
20
+ import { AsyncLabelPipe, EllipseWordPipe, CompanyDatePipe } from 'verben-ticketing-ui/src/lib/pipes';
21
+ import { AddTicketLibModule } from 'verben-ticketing-ui/src/lib/components/add-ticket';
22
+
23
+ class ChatControlComponent {
24
+ message = '';
25
+ files = [];
26
+ color = '#ffe681';
27
+ textColor = '#000';
28
+ disabled = false;
29
+ placeholder = '';
30
+ hideBtn = false;
31
+ messageChange = new EventEmitter();
32
+ formSubmit = new EventEmitter();
33
+ sendMessageResult = new EventEmitter();
34
+ filesChange = new EventEmitter();
35
+ isOpen = false;
36
+ isRecording = false;
37
+ audioBlobUrl = null;
38
+ audioDuration = 0;
39
+ isPlaying = false;
40
+ mediaRecorder = null;
41
+ audioChunks = [];
42
+ audio = null;
43
+ currentTime = "0:00";
44
+ showDefault = false;
45
+ addEmoji(event) {
46
+ if (event.emoji?.native) {
47
+ this.message += event.emoji.native;
48
+ this.messageChange.emit(this.message);
49
+ }
50
+ }
51
+ onFileSelected(event) {
52
+ const selectedFiles = event.target.files;
53
+ if (selectedFiles) {
54
+ for (let i = 0; i < selectedFiles.length; i++) {
55
+ this.files.push(selectedFiles[i]);
56
+ }
57
+ this.filesChange.emit(this.files);
58
+ }
59
+ }
60
+ toggleEmojiPicker() {
61
+ this.isOpen = !this.isOpen;
62
+ }
63
+ onClose(value) {
64
+ this.isOpen = value;
65
+ }
66
+ getFileIcon(file) {
67
+ const fileType = file.type.split('/')[0];
68
+ switch (fileType) {
69
+ case 'image': return '🖼️';
70
+ case 'video': return '🎥';
71
+ case 'audio': return '🎵';
72
+ case 'application': return '📄';
73
+ default: return '📁';
74
+ }
75
+ }
76
+ sendMessage() {
77
+ const payload = {
78
+ message: this.message,
79
+ };
80
+ console.log('Sending:', payload);
81
+ this.message = '';
82
+ this.audioBlobUrl = null;
83
+ this.audioDuration = 0;
84
+ this.sendMessageResult.emit();
85
+ this.messageChange.emit(this.message);
86
+ }
87
+ onMessageInput(event) {
88
+ const inputElement = event.target;
89
+ this.message = inputElement.value;
90
+ this.messageChange.emit(this.message);
91
+ }
92
+ onKeyDown(event) {
93
+ if (event.key === 'Enter' && !event.shiftKey && this.message.trim() !== '' && !this.hideBtn) {
94
+ event.preventDefault();
95
+ this.sendMessage();
96
+ }
97
+ }
98
+ toggleRecording() {
99
+ this.isRecording = !this.isRecording;
100
+ if (this.isRecording) {
101
+ this.startWaveAnimation();
102
+ this.startRecording();
103
+ }
104
+ else {
105
+ this.stopWaveAnimation();
106
+ this.stopRecording();
107
+ }
108
+ }
109
+ async startRecording() {
110
+ this.deleteRecording();
111
+ try {
112
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
113
+ this.mediaRecorder = new MediaRecorder(stream);
114
+ this.audioChunks = [];
115
+ this.mediaRecorder.ondataavailable = (event) => {
116
+ this.audioChunks.push(event.data);
117
+ };
118
+ this.mediaRecorder.onstop = async () => {
119
+ const audioBlob = new Blob(this.audioChunks, { type: "audio/wav" });
120
+ this.audioBlobUrl = URL.createObjectURL(audioBlob);
121
+ this.audio = new Audio(this.audioBlobUrl);
122
+ this.generateWaveform();
123
+ this.audio.onloadedmetadata = () => {
124
+ if (!isNaN(this.audio.duration) && this.audio.duration !== Infinity) {
125
+ this.audioDuration = this.formatTime(this.audio.duration);
126
+ this.currentTime = this.audioDuration;
127
+ }
128
+ else {
129
+ this.fixAudioDuration();
130
+ }
131
+ };
132
+ this.audio.onended = () => {
133
+ this.isPlaying = false;
134
+ this.currentTime = this.audioDuration;
135
+ };
136
+ };
137
+ this.mediaRecorder.start();
138
+ this.isRecording = true;
139
+ }
140
+ catch (error) {
141
+ console.error("Error accessing microphone:", error);
142
+ }
143
+ }
144
+ getDuration = (event) => {
145
+ const target = event.target;
146
+ target.currentTime = 0; // Reset time
147
+ target.removeEventListener("timeupdate", this.getDuration);
148
+ this.audioDuration = Math.floor(target.duration) || 0;
149
+ console.log("Fixed Duration:", this.audioDuration);
150
+ };
151
+ stopRecording() {
152
+ if (this.mediaRecorder) {
153
+ this.mediaRecorder.stop();
154
+ console.log("Stopping recording...");
155
+ this.isRecording = false;
156
+ }
157
+ }
158
+ toggleAudioPlayback() {
159
+ if (!this.audio)
160
+ return;
161
+ this.showDefault = false;
162
+ if (this.audio.paused) {
163
+ this.audio.play();
164
+ if (!this.isPlaying) {
165
+ this.startWaveAnimation();
166
+ }
167
+ this.isPlaying = true;
168
+ this.audio.ontimeupdate = () => {
169
+ if (this.isPlaying) {
170
+ const remainingTime = this.audio.duration - this.audio.currentTime;
171
+ this.currentTime = this.formatTime(remainingTime);
172
+ }
173
+ };
174
+ this.audio.onended = () => {
175
+ this.isPlaying = false;
176
+ this.currentTime = this.audioDuration;
177
+ this.stopWaveAnimation();
178
+ };
179
+ }
180
+ else {
181
+ this.audio.pause();
182
+ this.stopWaveAnimation();
183
+ this.isPlaying = false;
184
+ }
185
+ }
186
+ formatTime(seconds) {
187
+ const minutes = Math.floor(seconds / 60);
188
+ const secs = Math.floor(seconds % 60);
189
+ return `${minutes}:${secs < 10 ? "0" : ""}${secs}`;
190
+ }
191
+ deleteRecording() {
192
+ this.audioBlobUrl = null;
193
+ this.audioDuration = 0;
194
+ }
195
+ bars = new Array(20).fill(0); // Creates 30 bars dynamically
196
+ generateWaveform() {
197
+ this.bars = Array.from({ length: 20 }, () => ({
198
+ height: Math.random() * (20 - 5) + 5 // Generate random height between 5px and 20px
199
+ }));
200
+ }
201
+ startWaveAnimation() {
202
+ setTimeout(() => {
203
+ const bars = document.querySelectorAll(".bar");
204
+ bars.forEach((bar) => {
205
+ bar.style.animationDuration = `${Math.random() * (0.75 - 0.25) + 0.25}s`;
206
+ });
207
+ }, 100);
208
+ }
209
+ fixAudioDuration() {
210
+ if (!this.audio)
211
+ return;
212
+ this.audio.currentTime = 1e101;
213
+ this.audio.ontimeupdate = () => {
214
+ this.audio.ontimeupdate = null;
215
+ this.audio.currentTime = 0;
216
+ this.audioDuration = this.formatTime(this.audio.duration);
217
+ this.currentTime = this.audioDuration;
218
+ };
219
+ }
220
+ stopWaveAnimation() {
221
+ const bars = document.querySelectorAll(".bar");
222
+ bars.forEach((bar) => {
223
+ const el = bar;
224
+ el.style.animation = "none"; // Stop animation
225
+ el.offsetHeight; // Force reflow to reset animation
226
+ el.style.animation = ""; // Restart animation (CSS should handle it)
227
+ });
228
+ }
229
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
230
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChatControlComponent, selector: "lib-chat-control", inputs: { message: "message", color: "color", textColor: "textColor", disabled: "disabled", placeholder: "placeholder", hideBtn: "hideBtn" }, outputs: { messageChange: "messageChange", formSubmit: "formSubmit", sendMessageResult: "sendMessageResult", filesChange: "filesChange" }, ngImport: i0, template: "<div class=\"chat-control\">\n <div class=\"chat-control-container\">\n <div class=\"file-previews\">\n <div *ngFor=\"let file of files\" class=\"file-preview\">\n <span class=\"file-icon\">{{ getFileIcon(file) }}</span>\n <span class=\"file-name\">{{ file.name }}</span>\n </div>\n </div>\n\n \n <div class=\"flex items-center gap-1\" *ngIf=\"audioBlobUrl\">\n <div class=\"voice-message bg-primary rounded-[40px] px-2 py-2 w-[300px] flex items-center gap-1\">\n <button (click)=\"toggleAudioPlayback()\" class=\"pr-2\">\n <verben-svg [width]=\"18\" [height]=\"18\" [stroke]=\"'#fff'\" *ngIf=\"!isPlaying\" [icon]=\"'play'\"></verben-svg>\n <verben-svg [width]=\"18\" [height]=\"18\" *ngIf=\"isPlaying\" [icon]=\"'pause'\"></verben-svg>\n </button>\n <div class=\"sound-wave-container\">\n <div class=\"sound-wave-bars\">\n <div *ngFor=\"let bar of bars\" class=\"bar bg-[white]\" [ngStyle]=\"{ height: bar.height + 'px' }\"></div>\n </div>\n </div> \n \n \n \n <span class=\"duration bg-[white] px-2 block rounded-2xl\">{{ currentTime }}</span>\n \n </div>\n <button (click)=\"deleteRecording()\" class=\"delete-button \">\n <verben-svg [width]=\"12\" [height]=\"12\" [stroke]=\"'#D4A007'\" [icon]=\"'close-no-circle'\"></verben-svg>\n </button>\n </div>\n\n <textarea\n *ngIf=\"!isRecording\"\n [placeholder]=\"placeholder\"\n (input)=\"onMessageInput($event)\"\n (keydown)=\"onKeyDown($event)\"\n [(ngModel)]=\"message\"\n class=\"text-sm\"\n placeholder=\"Type a message...\"\n ></textarea>\n\n\n <div class=\"flex justify-between\">\n <div class=\"flex\">\n <div class=\"emoji-picker\" (click)=\"toggleEmojiPicker()\">\n <verben-pop-Up\n (dropdownOpenChange)=\"onClose($event)\"\n [customStyles]=\"{ 'z-index': '99' }\"\n [dropdownOpen]=\"isOpen\"\n [enableMouseLeave]=\"false\"\n (close)=\"isOpen = false\"\n >\n <verben-svg\n *ngIf=\"!isRecording\" \n class=\"cursor-pointer actions-icon\"\n (click)=\"isOpen = true\"\n dropdown-trigger\n [stroke]=\"'#d3d1d1'\"\n [icon]=\"'emoji'\"\n ></verben-svg>\n <div class=\"pop-up-content absolute top-[-490px] left-0\" dropdown-content>\n <emoji-mart [showPreview]=\"false\" (emojiSelect)=\"addEmoji($event)\" title=\"Pick an emoji\"></emoji-mart>\n </div>\n </verben-pop-Up>\n </div>\n\n <label *ngIf=\"!isRecording\" for=\"file-upload\" class=\"file-upload\">\n <verben-svg [icon]=\"'file'\"></verben-svg>\n <input id=\"file-upload\" type=\"file\" (change)=\"onFileSelected($event)\" multiple />\n </label>\n\n \n <div class=\"flex items-center gap-5\">\n <!-- Voice Recording Button -->\n <button (click)=\"toggleRecording()\" class=\"voice-button\"> \n <verben-svg class=\"opacity-[0.5]\" *ngIf=\"!isRecording\" [icon]=\"'mic'\"></verben-svg>\n <verben-svg *ngIf=\"isRecording\" [icon]=\"'stop'\"></verben-svg>\n </button>\n <div class=\"sound-wave-container\" *ngIf=\"isRecording\">\n <div class=\"sound-wave-bars\">\n <div *ngFor=\"let bar of bars\" class=\"bar bg-primary\"></div>\n </div>\n </div> \n </div>\n </div>\n\n <!-- Send Button -->\n <div>\n <button *ngIf=\"!hideBtn\" [disabled]=\"disabled\" (click)=\"sendMessage()\" [style.background-color]=\"color\" [style.color]=\"textColor\" class=\"disabled:opacity-60 disabled:cursor-not-allowed\">\n Send\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["textarea{width:100%;height:80px;overflow-y:auto;padding:10px;outline:none;margin:0;border-radius:0;resize:none;font-family:Arial,sans-serif}.backdrop{overflow:auto}.highlights{white-space:pre-wrap;word-wrap:break-word}textarea{color:#444;background-color:transparent}.highlights{color:transparent}mark{color:transparent;background-color:#d4e9ab}.backdrop{background-color:#fff}.emoji-picker,.file-upload{cursor:pointer;font-size:20px;padding:5px;border-radius:4px;background-color:#fff;transition:background-color .2s}.emoji-picker:hover,.file-upload:hover{background-color:#e0e0e0}.file-upload input{display:none}button{padding:8px 16px;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}::ng-deep .emoji-mart{width:300px!important;height:100%!important}.file-previews{display:flex;flex-direction:column;gap:5px}.file-preview{display:flex;align-items:center;gap:8px;padding:4px;border:1px solid #ddd;border-radius:4px;background-color:#fff}.file-icon{font-size:20px}.file-name{font-size:14px;color:#333}.sound-wave-bars{display:flex;height:40px;align-items:center}.bar{height:20px;width:3px;border-radius:4px;margin:0 3px;animation:waveAnim ease-in-out infinite alternate}@keyframes waveAnim{0%{height:20px;opacity:.3}to{height:40px;opacity:1}}\n"], dependencies: [{ kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.SvgComponent, selector: "verben-svg", inputs: ["icon", "width", "height", "fill", "stroke", "type", "size"] }, { kind: "component", type: i5.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave", "cdkPosition"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: i4.PickerComponent, selector: "emoji-mart", inputs: ["perLine", "totalFrequentLines", "i18n", "style", "title", "emoji", "darkMode", "color", "hideObsolete", "categories", "activeCategories", "set", "skin", "isNative", "emojiSize", "sheetSize", "emojisToShowFilter", "showPreview", "emojiTooltip", "autoFocus", "custom", "hideRecent", "imageUrlFn", "include", "exclude", "notFoundEmoji", "categoriesIcons", "searchIcons", "useButton", "enableFrequentEmojiSort", "enableSearch", "showSingleCategory", "virtualize", "virtualizeOffset", "recent", "backgroundImageFn"], outputs: ["emojiClick", "emojiSelect", "skinChange"] }] });
231
+ }
232
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatControlComponent, decorators: [{
233
+ type: Component,
234
+ args: [{ selector: 'lib-chat-control', template: "<div class=\"chat-control\">\n <div class=\"chat-control-container\">\n <div class=\"file-previews\">\n <div *ngFor=\"let file of files\" class=\"file-preview\">\n <span class=\"file-icon\">{{ getFileIcon(file) }}</span>\n <span class=\"file-name\">{{ file.name }}</span>\n </div>\n </div>\n\n \n <div class=\"flex items-center gap-1\" *ngIf=\"audioBlobUrl\">\n <div class=\"voice-message bg-primary rounded-[40px] px-2 py-2 w-[300px] flex items-center gap-1\">\n <button (click)=\"toggleAudioPlayback()\" class=\"pr-2\">\n <verben-svg [width]=\"18\" [height]=\"18\" [stroke]=\"'#fff'\" *ngIf=\"!isPlaying\" [icon]=\"'play'\"></verben-svg>\n <verben-svg [width]=\"18\" [height]=\"18\" *ngIf=\"isPlaying\" [icon]=\"'pause'\"></verben-svg>\n </button>\n <div class=\"sound-wave-container\">\n <div class=\"sound-wave-bars\">\n <div *ngFor=\"let bar of bars\" class=\"bar bg-[white]\" [ngStyle]=\"{ height: bar.height + 'px' }\"></div>\n </div>\n </div> \n \n \n \n <span class=\"duration bg-[white] px-2 block rounded-2xl\">{{ currentTime }}</span>\n \n </div>\n <button (click)=\"deleteRecording()\" class=\"delete-button \">\n <verben-svg [width]=\"12\" [height]=\"12\" [stroke]=\"'#D4A007'\" [icon]=\"'close-no-circle'\"></verben-svg>\n </button>\n </div>\n\n <textarea\n *ngIf=\"!isRecording\"\n [placeholder]=\"placeholder\"\n (input)=\"onMessageInput($event)\"\n (keydown)=\"onKeyDown($event)\"\n [(ngModel)]=\"message\"\n class=\"text-sm\"\n placeholder=\"Type a message...\"\n ></textarea>\n\n\n <div class=\"flex justify-between\">\n <div class=\"flex\">\n <div class=\"emoji-picker\" (click)=\"toggleEmojiPicker()\">\n <verben-pop-Up\n (dropdownOpenChange)=\"onClose($event)\"\n [customStyles]=\"{ 'z-index': '99' }\"\n [dropdownOpen]=\"isOpen\"\n [enableMouseLeave]=\"false\"\n (close)=\"isOpen = false\"\n >\n <verben-svg\n *ngIf=\"!isRecording\" \n class=\"cursor-pointer actions-icon\"\n (click)=\"isOpen = true\"\n dropdown-trigger\n [stroke]=\"'#d3d1d1'\"\n [icon]=\"'emoji'\"\n ></verben-svg>\n <div class=\"pop-up-content absolute top-[-490px] left-0\" dropdown-content>\n <emoji-mart [showPreview]=\"false\" (emojiSelect)=\"addEmoji($event)\" title=\"Pick an emoji\"></emoji-mart>\n </div>\n </verben-pop-Up>\n </div>\n\n <label *ngIf=\"!isRecording\" for=\"file-upload\" class=\"file-upload\">\n <verben-svg [icon]=\"'file'\"></verben-svg>\n <input id=\"file-upload\" type=\"file\" (change)=\"onFileSelected($event)\" multiple />\n </label>\n\n \n <div class=\"flex items-center gap-5\">\n <!-- Voice Recording Button -->\n <button (click)=\"toggleRecording()\" class=\"voice-button\"> \n <verben-svg class=\"opacity-[0.5]\" *ngIf=\"!isRecording\" [icon]=\"'mic'\"></verben-svg>\n <verben-svg *ngIf=\"isRecording\" [icon]=\"'stop'\"></verben-svg>\n </button>\n <div class=\"sound-wave-container\" *ngIf=\"isRecording\">\n <div class=\"sound-wave-bars\">\n <div *ngFor=\"let bar of bars\" class=\"bar bg-primary\"></div>\n </div>\n </div> \n </div>\n </div>\n\n <!-- Send Button -->\n <div>\n <button *ngIf=\"!hideBtn\" [disabled]=\"disabled\" (click)=\"sendMessage()\" [style.background-color]=\"color\" [style.color]=\"textColor\" class=\"disabled:opacity-60 disabled:cursor-not-allowed\">\n Send\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["textarea{width:100%;height:80px;overflow-y:auto;padding:10px;outline:none;margin:0;border-radius:0;resize:none;font-family:Arial,sans-serif}.backdrop{overflow:auto}.highlights{white-space:pre-wrap;word-wrap:break-word}textarea{color:#444;background-color:transparent}.highlights{color:transparent}mark{color:transparent;background-color:#d4e9ab}.backdrop{background-color:#fff}.emoji-picker,.file-upload{cursor:pointer;font-size:20px;padding:5px;border-radius:4px;background-color:#fff;transition:background-color .2s}.emoji-picker:hover,.file-upload:hover{background-color:#e0e0e0}.file-upload input{display:none}button{padding:8px 16px;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}::ng-deep .emoji-mart{width:300px!important;height:100%!important}.file-previews{display:flex;flex-direction:column;gap:5px}.file-preview{display:flex;align-items:center;gap:8px;padding:4px;border:1px solid #ddd;border-radius:4px;background-color:#fff}.file-icon{font-size:20px}.file-name{font-size:14px;color:#333}.sound-wave-bars{display:flex;height:40px;align-items:center}.bar{height:20px;width:3px;border-radius:4px;margin:0 3px;animation:waveAnim ease-in-out infinite alternate}@keyframes waveAnim{0%{height:20px;opacity:.3}to{height:40px;opacity:1}}\n"] }]
235
+ }], propDecorators: { message: [{
236
+ type: Input
237
+ }], color: [{
238
+ type: Input
239
+ }], textColor: [{
240
+ type: Input
241
+ }], disabled: [{
242
+ type: Input
243
+ }], placeholder: [{
244
+ type: Input
245
+ }], hideBtn: [{
246
+ type: Input
247
+ }], messageChange: [{
248
+ type: Output
249
+ }], formSubmit: [{
250
+ type: Output
251
+ }], sendMessageResult: [{
252
+ type: Output
253
+ }], filesChange: [{
254
+ type: Output
255
+ }] } });
256
+
257
+ class TicketingDetailComponent {
258
+ route;
259
+ server;
260
+ utilService;
261
+ sharedService;
262
+ location;
263
+ envSvc;
264
+ user;
265
+ defaultCompany = '';
266
+ constructor(route, server, utilService, sharedService, location, envSvc) {
267
+ this.route = route;
268
+ this.server = server;
269
+ this.utilService = utilService;
270
+ this.sharedService = sharedService;
271
+ this.location = location;
272
+ this.envSvc = envSvc;
273
+ this.ticketingAPI = `${this.envSvc.environment.TicketingAPI}/Ticket`;
274
+ this.glAPI = this.envSvc.environment.GLAPI;
275
+ }
276
+ showForm = false;
277
+ data;
278
+ typingQuery = '';
279
+ isSearchingUsers = false;
280
+ userPopups;
281
+ ticketStatus = TicketStatus;
282
+ ticketingAPI = '';
283
+ tenantOptions = [];
284
+ glAPI = '';
285
+ attachedMessage = null;
286
+ maxFileSize = 15 * 1024 * 1024;
287
+ message = '';
288
+ action;
289
+ searchTrigger$ = new Subject();
290
+ fileData = [];
291
+ code = '';
292
+ color = '#ffe681';
293
+ textColor = '#000';
294
+ statusOptions = Object.keys(TicketStatus);
295
+ ngOnInit() {
296
+ this.route.paramMap.subscribe(async (params) => {
297
+ const code = params.get('code');
298
+ if (code) {
299
+ this.code = code;
300
+ this.getTicketDetail(code);
301
+ this.getAllTask(code);
302
+ this.getTenantPriority();
303
+ }
304
+ this.searchTrigger$.pipe(debounceTime(300)).subscribe((query) => {
305
+ if (query) {
306
+ this.searchUsers(query);
307
+ }
308
+ });
309
+ });
310
+ }
311
+ onFormClose(isShow) {
312
+ this.showForm = isShow;
313
+ }
314
+ onFormOpen(isShow) {
315
+ this.showForm = true;
316
+ }
317
+ async onFilesUpdate(files) {
318
+ if (!files || files.length === 0)
319
+ return;
320
+ // Helper function to convert a file to base64
321
+ const toBase64 = (file) => new Promise((resolve, reject) => {
322
+ const reader = new FileReader();
323
+ reader.readAsDataURL(file);
324
+ reader.onload = () => resolve(reader.result);
325
+ reader.onerror = (error) => reject(error);
326
+ });
327
+ try {
328
+ const convertedFiles = await Promise.all(files.map(async (file) => {
329
+ const base64Data = await toBase64(file);
330
+ const name = file.name || 'file';
331
+ const extension = name.split('.').pop() || '';
332
+ return {
333
+ Name: name,
334
+ Extension: extension,
335
+ Data: base64Data,
336
+ DataState: ObjectState.New,
337
+ };
338
+ }));
339
+ this.fileData = convertedFiles;
340
+ }
341
+ catch (error) {
342
+ console.error('Error converting files to base64:', error);
343
+ }
344
+ }
345
+ async saveMessageChat(ticket, newMessageContent) {
346
+ if (!newMessageContent || newMessageContent.trim().length === 0) {
347
+ this.utilService.showInfo('Message cannot be empty');
348
+ return;
349
+ }
350
+ const newMessage = {
351
+ Code: '',
352
+ Name: this.user?.MailAddress || 'perfection.chizuruoke@verbenalogic.com',
353
+ TicketCode: ticket.Code || '',
354
+ Subject: '',
355
+ Content: newMessageContent.trim(),
356
+ Attachments: this.fileData.length ? this.fileData : [],
357
+ AttachmentUrls: [],
358
+ Company: ticket.Company,
359
+ TenantId: '',
360
+ Upsert: false,
361
+ ServiceName: '',
362
+ CreatedAt: new Date(),
363
+ UpdatedAt: new Date(),
364
+ UpdatedBy: '',
365
+ CreatedBy: '',
366
+ DataState: ObjectState.New,
367
+ Id: '',
368
+ };
369
+ // if (!this.data.Messages) this.data.Messages = [];
370
+ // this.data.Messages.push(newMessage);
371
+ // this.data.DataState = ObjectState.Changed;
372
+ try {
373
+ this.utilService.sendBI(true);
374
+ const res = await this.server.post(`Ticket/SendMailToTicketOwner`, newMessage, this.envSvc.environment.TicketingAPI);
375
+ this.utilService.sendBI(false);
376
+ if (res instanceof ErrorResponse) {
377
+ this.utilService.showError('Failed to send message.');
378
+ return;
379
+ }
380
+ const result = res;
381
+ if (result) {
382
+ this.data.Messages = [...this.data.Messages, result];
383
+ this.data = { ...this.data };
384
+ this.utilService.showSuccess('Message sent successfully');
385
+ }
386
+ }
387
+ catch (error) {
388
+ console.error(error);
389
+ this.utilService.sendBI(false);
390
+ this.utilService.showError('Something went wrong while sending message');
391
+ }
392
+ }
393
+ async save() {
394
+ const item = this.data;
395
+ ((item.DataState = this.data ? ObjectState.Changed : ObjectState.New),
396
+ this.utilService.sendBI(true));
397
+ const res = await this.server.post(`Ticket/SaveTicket`, [item], this.envSvc.environment.TicketingAPI);
398
+ this.utilService.sendBI(false);
399
+ if (res instanceof ErrorResponse) {
400
+ }
401
+ else {
402
+ this.utilService.showSuccess('Ticket saved successfully');
403
+ const result = res;
404
+ if (result !== null) {
405
+ this.data = { ...this.data, ...result };
406
+ }
407
+ }
408
+ }
409
+ goBack() {
410
+ this.location.back();
411
+ }
412
+ disableChatBoxButton() {
413
+ return false;
414
+ }
415
+ onKeyDown(e) { }
416
+ messageChange(e) {
417
+ this.message = e;
418
+ this.detectUserSearchTrigger(e);
419
+ }
420
+ selectUser(user) {
421
+ const before = this.message.replace(/@([a-zA-Z0-9_]*)$/, '');
422
+ this.message = before + '@' + user.MailAddress + ' ';
423
+ this.messageChange(this.message);
424
+ this.closeUserPopup();
425
+ }
426
+ openUserPopup() {
427
+ this.userPopups.forEach((p) => {
428
+ if (!p.dropdownOpen)
429
+ p.toggleDropdown();
430
+ });
431
+ }
432
+ closeUserPopup() {
433
+ this.userPopups.forEach((p) => {
434
+ if (p.dropdownOpen)
435
+ p.toggleDropdown();
436
+ });
437
+ }
438
+ detectUserSearchTrigger(text) {
439
+ const regex = /@([a-zA-Z0-9_]*)$/;
440
+ const match = text.match(regex);
441
+ if (match) {
442
+ this.typingQuery = match[1];
443
+ this.isSearchingUsers = true;
444
+ this.searchTrigger$.next(this.typingQuery);
445
+ }
446
+ else {
447
+ this.typingQuery = '';
448
+ this.isSearchingUsers = false;
449
+ this.closeUserPopup();
450
+ }
451
+ }
452
+ usersResult = [];
453
+ async searchUsers(query) {
454
+ if (!query) {
455
+ this.usersResult = [];
456
+ return;
457
+ }
458
+ const payload = [
459
+ {
460
+ PropertyName: 'MailAddress',
461
+ EntityValue: query,
462
+ Operator: SearchOperator.And,
463
+ Sign: SearchPropertySign.LIKE,
464
+ },
465
+ ];
466
+ const res = await this.server.post(`SearchUsers/0/20`, payload, this.envSvc.environment.AuthenticationAPI);
467
+ if (res instanceof ErrorResponse)
468
+ return;
469
+ this.usersResult = res.Result;
470
+ this.openUserPopup();
471
+ }
472
+ triggerFileUpload() {
473
+ const fileInput = document.createElement('input');
474
+ fileInput.type = 'file';
475
+ fileInput.accept = '.png, .jpg, .jpeg, .pdf';
476
+ fileInput.style.display = 'none'; // Hidden file input
477
+ document.body.appendChild(fileInput); // Temporarily add to DOM
478
+ fileInput.addEventListener('change', () => {
479
+ const file = fileInput.files?.[0];
480
+ if (file) {
481
+ this.processFile(file);
482
+ }
483
+ document.body.removeChild(fileInput); // Clean up after use
484
+ });
485
+ fileInput.click(); // Programmatically trigger click
486
+ }
487
+ async processFile(selectedFile) {
488
+ // Check file size
489
+ if (selectedFile.size > this.maxFileSize) {
490
+ const limitInMB = this.maxFileSize / 1024 / 1024;
491
+ this.utilService.showInfo(`File size limit of ${limitInMB}mb exceeded`);
492
+ return;
493
+ }
494
+ // let fileToProcess: globalThis.File | null = selectedFile;
495
+ // const safeNumber: number = 1 * 1024 * 1024;
496
+ // if (fileToProcess.size > safeNumber) {
497
+ // fileToProcess = await this.utilService.compressImage(selectedFile);
498
+ // if (!fileToProcess) {
499
+ // this.utilService.showError('Error compressing image');
500
+ // return;
501
+ // }
502
+ // }
503
+ // Read file and convert to base64
504
+ const reader = new FileReader();
505
+ reader.readAsDataURL(selectedFile);
506
+ reader.onload = () => {
507
+ const base64data = reader.result;
508
+ const file = this.sharedService.createCustomFile(base64data, 0, selectedFile);
509
+ if (file && this.attachedMessage) {
510
+ this.attachedMessage.Attachments.push(file);
511
+ this.attachedMessage.AttachmentUrls.push(`${file.Name}`);
512
+ // this.profileImage = base64data;
513
+ }
514
+ };
515
+ }
516
+ async getTicketDetail(code) {
517
+ const payload = [
518
+ {
519
+ PropertyName: 'Code',
520
+ EntityValue: code,
521
+ Operator: SearchOperator.And,
522
+ Sign: SearchPropertySign.EQ,
523
+ },
524
+ {
525
+ PropertyName: 'TenantId',
526
+ EntityValue: '',
527
+ Operator: SearchOperator.And,
528
+ Sign: SearchPropertySign.NEQ,
529
+ },
530
+ ];
531
+ this.utilService.sendBI(true);
532
+ const res = await this.server.post(`Ticket/SearchTicket/0/50`, payload, this.envSvc.environment.TicketingAPI);
533
+ this.utilService.sendBI(false);
534
+ if (res instanceof ErrorResponse)
535
+ return undefined;
536
+ const result = res.Result;
537
+ if (result?.length > 0) {
538
+ this.data = result[0];
539
+ this.getMessages(this.data.Code).then((data) => {
540
+ if (data) {
541
+ this.data.Messages = data;
542
+ }
543
+ });
544
+ return result[0];
545
+ }
546
+ return undefined;
547
+ }
548
+ async getMessages(code) {
549
+ const payload = [
550
+ {
551
+ PropertyName: 'TicketCode',
552
+ EntityValue: code,
553
+ Operator: SearchOperator.And,
554
+ Sign: SearchPropertySign.EQ,
555
+ Sort: SortDirection.Desc,
556
+ },
557
+ {
558
+ PropertyName: 'TenantId',
559
+ EntityValue: '',
560
+ Operator: SearchOperator.And,
561
+ Sign: SearchPropertySign.NEQ,
562
+ Sort: SortDirection.Desc,
563
+ },
564
+ ];
565
+ this.utilService.sendBI(true);
566
+ const res = await this.server.post(`Ticket/SearchTicketMessage/0/500`, payload, this.envSvc.environment.TicketingAPI);
567
+ this.utilService.sendBI(false);
568
+ if (res instanceof ErrorResponse)
569
+ return undefined;
570
+ const result = res.Result;
571
+ if (result?.length > 0) {
572
+ return result;
573
+ }
574
+ return undefined;
575
+ }
576
+ async getTenantPriority() {
577
+ const payload = [
578
+ {
579
+ PropertyName: 'TenantId',
580
+ EntityValue: '',
581
+ Operator: SearchOperator.And,
582
+ Sign: SearchPropertySign.NEQ,
583
+ },
584
+ ];
585
+ this.utilService.sendBI(true);
586
+ const res = await this.server.post(`Ticket/SearchTenantPriority/0/50`, payload, this.envSvc.environment.TicketingAPI);
587
+ this.utilService.sendBI(false);
588
+ if (res instanceof ErrorResponse)
589
+ return undefined;
590
+ const result = res.Result;
591
+ if (result?.length > 0) {
592
+ this.tenantOptions = result;
593
+ }
594
+ return undefined;
595
+ }
596
+ cssColorToHex(color) {
597
+ const ctx = document.createElement('canvas').getContext('2d');
598
+ if (!ctx)
599
+ return null;
600
+ ctx.fillStyle = '#000'; // set a known color
601
+ ctx.fillStyle = color; // assign any CSS-valid color
602
+ const computed = ctx.fillStyle;
603
+ // If the browser does NOT understand the value, it won’t change from '#000'
604
+ if (computed.toLowerCase() === '#000000' &&
605
+ color.toLowerCase() !== 'black') {
606
+ return null; // invalid color name or unsupported value
607
+ }
608
+ return computed.toUpperCase(); // returns hex like #1E90FF
609
+ }
610
+ softenColor(hex, lightenPercent = 55, alphaPercent = 55) {
611
+ // Remove #
612
+ if (!hex.startsWith('#')) {
613
+ const converted = this.cssColorToHex(hex);
614
+ if (converted) {
615
+ hex = converted;
616
+ }
617
+ }
618
+ hex = hex.replace(/^#/, '');
619
+ if (hex.length === 3) {
620
+ // Convert shorthand (#abc → #aabbcc)
621
+ hex = hex
622
+ .split('')
623
+ .map((x) => x + x)
624
+ .join('');
625
+ }
626
+ const num = parseInt(hex, 16);
627
+ let r = (num >> 16) & 0xff;
628
+ let g = (num >> 8) & 0xff;
629
+ let b = num & 0xff;
630
+ // Lighten toward white by the given percentage
631
+ r = Math.min(255, Math.round(r + (255 - r) * (lightenPercent / 100)));
632
+ g = Math.min(255, Math.round(g + (255 - g) * (lightenPercent / 100)));
633
+ b = Math.min(255, Math.round(b + (255 - b) * (lightenPercent / 100)));
634
+ // Convert alpha percent to 2-digit hex
635
+ const alpha = Math.round((alphaPercent / 100) * 255);
636
+ const aHex = alpha.toString(16).padStart(2, '0').toUpperCase();
637
+ return `#${r.toString(16).padStart(2, '0').toUpperCase()}${g
638
+ .toString(16)
639
+ .padStart(2, '0')
640
+ .toUpperCase()}${b.toString(16).padStart(2, '0').toUpperCase()}${aHex}`;
641
+ }
642
+ getTicketMessage() {
643
+ if (this.data) {
644
+ const initialMessage = this.data.Messages.find((x) => x.Subject == DEFAULT_MESSAGE_FOR_INITIAL_UPLOAD);
645
+ if (initialMessage) {
646
+ return { ...initialMessage, DataState: ObjectState.Changed };
647
+ }
648
+ else {
649
+ const newMessage = {
650
+ TicketCode: '',
651
+ Subject: DEFAULT_MESSAGE_FOR_INITIAL_UPLOAD,
652
+ Content: '',
653
+ Attachments: [],
654
+ AttachmentUrls: [],
655
+ Company: this.defaultCompany,
656
+ Id: '',
657
+ Code: '',
658
+ TenantId: '',
659
+ ServiceName: '',
660
+ id: '',
661
+ CreatedAt: new Date(),
662
+ UpdatedAt: new Date(),
663
+ DataState: ObjectState.New,
664
+ };
665
+ return newMessage;
666
+ }
667
+ }
668
+ else {
669
+ const newMessage = {
670
+ TicketCode: '',
671
+ Subject: DEFAULT_MESSAGE_FOR_INITIAL_UPLOAD,
672
+ Content: '',
673
+ Attachments: [],
674
+ AttachmentUrls: [],
675
+ Company: this.defaultCompany,
676
+ Id: '',
677
+ Code: '',
678
+ TenantId: '',
679
+ ServiceName: '',
680
+ id: '',
681
+ CreatedAt: new Date(),
682
+ UpdatedAt: new Date(),
683
+ DataState: ObjectState.New,
684
+ };
685
+ return newMessage;
686
+ }
687
+ }
688
+ async getAllTask(code) {
689
+ this.utilService.sendBI(true);
690
+ const res = await this.server.get(`Task/GetTaskInfo/${code}`, this.envSvc.environment.WorkFlowAPI);
691
+ this.utilService.sendBI(false);
692
+ if (res instanceof ErrorResponse) {
693
+ return undefined;
694
+ }
695
+ else {
696
+ const result = res;
697
+ if (result) {
698
+ this.data.TaskData = result;
699
+ }
700
+ return result;
701
+ }
702
+ }
703
+ async passOn(data, fromStage, toStage, comment, recipient) {
704
+ if (!data)
705
+ return;
706
+ const payload = {
707
+ ...data.TaskData,
708
+ DataState: ObjectState.Changed,
709
+ HistoryToDate: data.TaskData.HistoryToDate.map((item) => ({
710
+ ...item,
711
+ Actor: item.Actor
712
+ ? {
713
+ ...item.Actor,
714
+ ActorTags: [],
715
+ }
716
+ : null,
717
+ })),
718
+ CurrentActors: data.TaskData.CurrentActors.map((item) => ({
719
+ ...item,
720
+ ActorTags: [],
721
+ })),
722
+ };
723
+ delete payload.FullStages;
724
+ this.utilService.sendBI(true);
725
+ try {
726
+ const res = await this.server.post(`Task/PassOn/${fromStage}/${toStage}/${comment}${recipient ? '/' + recipient : ''}`, payload, this.envSvc.environment.WorkFlowAPI);
727
+ this.utilService.sendBI(false);
728
+ if (res instanceof ErrorResponse) {
729
+ return undefined;
730
+ }
731
+ else {
732
+ const result = res;
733
+ if (result) {
734
+ data.TaskData = result;
735
+ this.getAllTask(data.Code).then((taskRes) => {
736
+ if (taskRes && data) {
737
+ data.TaskData = taskRes;
738
+ }
739
+ });
740
+ this.showForm = false;
741
+ this.action = null;
742
+ data.TaskComment = '';
743
+ const updatedItem = await this.getUpdatedTicket(data.Code);
744
+ if (updatedItem) {
745
+ data.Status = updatedItem.Status;
746
+ }
747
+ }
748
+ return result;
749
+ }
750
+ }
751
+ catch (err) {
752
+ this.utilService.sendBI(false);
753
+ console.error('PassOn Error:', err);
754
+ return undefined;
755
+ }
756
+ }
757
+ async formFill(formValues, ticket) {
758
+ const payload = formValues;
759
+ this.utilService.sendBI(true);
760
+ try {
761
+ const res = await this.server.post(`Form/FillForm/${this.code}`, payload, this.envSvc.environment.WorkFlowAPI);
762
+ this.utilService.sendBI(false);
763
+ if (res instanceof ErrorResponse) {
764
+ return undefined;
765
+ }
766
+ else {
767
+ await this.passOn(ticket, this.action.action?.FromStage, this.action.action?.ToStage, this.action.comment, '');
768
+ }
769
+ }
770
+ catch (err) {
771
+ this.utilService.sendBI(false);
772
+ console.error('PassOn Error:', err);
773
+ return undefined;
774
+ }
775
+ }
776
+ async getUpdatedTicket(code) {
777
+ const payload = [
778
+ {
779
+ PropertyName: 'Code',
780
+ EntityValue: code,
781
+ Operator: SearchOperator.And,
782
+ Sign: SearchPropertySign.EQ,
783
+ },
784
+ {
785
+ PropertyName: 'TenantId',
786
+ EntityValue: '',
787
+ Operator: SearchOperator.And,
788
+ Sign: SearchPropertySign.NEQ,
789
+ },
790
+ ];
791
+ const res = await this.server.post(`Ticket/SearchTicket/0/50`, payload, this.envSvc.environment.TicketingAPI);
792
+ if (res instanceof ErrorResponse) {
793
+ return undefined;
794
+ }
795
+ var result = res;
796
+ if (result.Result.length > 0) {
797
+ return result.Result[0];
798
+ }
799
+ else {
800
+ return undefined;
801
+ }
802
+ }
803
+ async handleActionComplete(action, data) {
804
+ data.TaskComment = action.comment;
805
+ const hasForms = data.TaskData?.TaskForms?.length > 0;
806
+ if (hasForms) {
807
+ this.showForm = true;
808
+ this.action = action;
809
+ return;
810
+ }
811
+ await this.passOn(data, action?.action.FromStage, action?.action.ToStage, data.TaskComment, '');
812
+ }
813
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TicketingDetailComponent, deps: [{ token: i1.ActivatedRoute }, { token: i2$1.HttpWebRequestService }, { token: i2$1.UtilService }, { token: i2$1.SharedService }, { token: i3.Location }, { token: i2$1.EnvironmentService }], target: i0.ɵɵFactoryTarget.Component });
814
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: TicketingDetailComponent, selector: "app-ticketing-detail", inputs: { user: "user", defaultCompany: "defaultCompany", color: "color", textColor: "textColor" }, viewQueries: [{ propertyName: "userPopups", predicate: ["userPopup"], descendants: true }], ngImport: i0, template: "<div\n (click)=\"goBack()\"\n class=\"my-4 w-fit ml-auto border p-1 rounded-lg border-gray-400 cursor-pointer\"\n>\n <verben-svg [icon]=\"'arrow-left'\"></verben-svg>\n</div>\n<div *ngIf=\"data\" class=\"grid grid-cols-12 gap-8\">\n <div\n class=\"lg:col-span-8 col-span-12 border relative rounded-2xl p-6 border-[#0000001A]\"\n >\n \n <div class=\"flex gap-2 items-center mt-6\">\n <h3 class=\"mt-2 font-bold\">{{ data.Title }}</h3>\n\n <div\n *ngIf=\"\n data.Priority && data.Priority\n | asyncLabel : 'TenantPriority' : 'Color' : ticketingAPI\n | async as color\n \"\n class=\"enum-val rounded-[8px] flex gap-2 items-center justify-center\"\n [style.backgroundColor]=\"softenColor(color)\"\n >\n <span [style.backgroundColor]=\"color\" class=\"enum-dot\"></span>\n <span>{{\n data.Priority\n | asyncLabel : \"TenantPriority\" : \"Name\" : ticketingAPI\n | async\n }}</span>\n </div>\n </div>\n <p class=\"mt-2 text-sm\">{{ data.Description }}</p>\n <div class=\"attachment-section p-2 flex flex-col gap-3\">\n <div\n class=\"attachments-container grid grid-cols-3 gap-2\"\n *ngIf=\"data.FileUrls\"\n >\n <ng-container *ngFor=\"let file of data.FileUrls\">\n <app-file-render [fileUrl]=\"file\"></app-file-render>\n </ng-container>\n </div>\n </div>\n <div class=\"max-h-[500px] lg:max-h-[230px] overflow-y-auto pr-1\">\n <div *ngFor=\"let message of data.Messages; let last = last\">\n <div\n class=\"mt-6 flex justify-between items-center pb-4\"\n [ngClass]=\"{ 'border-b border-gray-300': !last }\"\n >\n <div class=\"flex items-center gap-2\">\n <img\n class=\"w-[50px] h-[50px] object-cover rounded-full\"\n [src]=\"'/assets/images/avatar.png'\"\n alt=\"avatar\"\n />\n <div>\n <h4 class=\"font-medium\">\n {{\n message.Company\n | asyncLabel : \"Company\" : \"Description\" : glAPI\n | async\n }}\n </h4>\n <p class=\"text-xs\">{{ !message.Name ? \"Nil\" : message.Name }}</p>\n </div>\n </div>\n\n <p class=\"text-sm text-gray-600\">\n {{ message.CreatedAt | companyDate }}\n </p>\n </div>\n\n <p class=\"mt-6 text-sm\">\n {{ !message.Content ? \"Nil\" : message.Content }}\n </p>\n\n <div class=\"attachment-section p-2 flex flex-col gap-3\">\n <div\n class=\"attachments-container grid grid-cols-3 gap-2\"\n *ngIf=\"message.AttachmentUrls\"\n >\n <ng-container *ngFor=\"let file of message.AttachmentUrls\">\n <app-file-render [fileUrl]=\"file\"></app-file-render>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"pt-5\">\n <div class=\"border rounded-lg p-4 border-[#0000001A]\">\n \n\n <lib-chat-control\n (messageChange)=\"messageChange($event)\"\n [placeholder]=\"'Start typing a message....'\"\n [disabled]=\"disableChatBoxButton()\"\n (sendMessageResult)=\"saveMessageChat(data, message)\"\n [(message)]=\"message\"\n [color]=\"color\"\n [textColor]=\"textColor\"\n (filesChange)=\"onFilesUpdate($event)\"\n (formSubmit)=\"onKeyDown($event)\"\n ></lib-chat-control>\n </div>\n <verben-pop-Up\n #userPopup\n [customStyles]=\"{\n 'z-index': '99',\n position: 'absolute',\n bottom: '70px',\n left: '300px'\n }\"\n [enableMouseLeave]=\"false\"\n >\n <div\n class=\"pop-up-content flex flex-col max-h-[200px] w-[300px] overflow-y-auto p-3\"\n dropdown-content\n [style.border-color]=\"color\"\n > \n <p class=\"text-gray-500 p-2\">Suggestions</p>\n\n <div\n *ngFor=\"let u of usersResult\"\n class=\"p-2 hover:bg-gray-100 text-sm cursor-pointer w-fit\"\n (click)=\"selectUser(u)\"\n >\n {{ u.MailAddress }}\n </div>\n\n <p *ngIf=\"usersResult.length === 0\" class=\"text-gray-500 text-sm\">\n No matching users\n </p>\n </div>\n </verben-pop-Up>\n </div>\n </div>\n <div\n class=\"lg:col-span-4 col-span-12 p-4 border rounded-md border-[#0000001A]\"\n >\n <h3 class=\"mt-2 font-bold\">Details</h3>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Ticket ID :</p>\n <p>{{ data.Code }}</p>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Subject:</p>\n <p class=\"text-xs text-right\">{{ data.Title }}</p>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Status:</p>\n <div\n *ngIf=\"data.Status\"\n class=\"enum-val rounded-[8px] flex gap-2 items-center justify-center {{\n data.Status\n }}\"\n >\n <span class=\"enum-dot\"></span>\n <span>{{ data.Status }}</span>\n </div>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">CreatedAt :</p>\n <p class=\"text-sm\">{{ data.CreatedAt | companyDate }}</p>\n </div>\n <div\n *ngIf=\"data.Status === ticketStatus.Completed\"\n class=\"flex items-center justify-between mt-4\"\n >\n <p class=\"text-sm font-light\">Execution Time :</p>\n <p class=\"text-sm\">{{ data.ExecutionTime }}</p>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Priority :</p>\n <div\n *ngIf=\"\n data.Priority && data.Priority\n | asyncLabel : 'TenantPriority' : 'Color' : ticketingAPI\n | async as color\n \"\n class=\"enum-val rounded-[8px] flex gap-2 items-center justify-center\"\n [style.backgroundColor]=\"softenColor(color)\"\n >\n <span [style.backgroundColor]=\"color\" class=\"enum-dot\"></span>\n <span>{{\n data.Priority\n | asyncLabel : \"TenantPriority\" : \"Name\" : ticketingAPI\n | async\n }}</span>\n </div>\n </div>\n <section class=\"mt-6\">\n <h3 class=\"font-bold\">Properties</h3>\n\n <div class=\"flex flex-col gap-1 mt-2\">\n <div class=\"text-sm text-gray-600\">Status</div>\n <verben-drop-down\n width=\"100%\"\n placeholder=\"Select Status\"\n [disabled]=\"true\"\n [options]=\"statusOptions\"\n [(ngModel)]=\"data.Status\"\n >\n </verben-drop-down>\n </div>\n <div class=\"flex flex-col gap-1 mt-2 mb-18\">\n <div class=\"text-sm text-gray-600\">Priority</div>\n <verben-drop-down\n optionLabel=\"Name\"\n [filter]=\"true\"\n optionValue=\"Code\"\n [disabled]=\"true\"\n [options]=\"tenantOptions\"\n [(ngModel)]=\"data.Priority\"\n [width]=\"'100%'\"\n ></verben-drop-down>\n </div>\n <!-- <verbena-button\n (click)=\"save()\"\n [width]=\"'100%'\"\n [textColor]=\"'#fff'\"\n [text]=\"'Update'\"\n [bgColor]=\"'#9C56BA'\"\n ></verbena-button> -->\n </section>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.SvgComponent, selector: "verben-svg", inputs: ["icon", "width", "height", "fill", "stroke", "type", "size"] }, { kind: "component", type: i5.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave", "cdkPosition"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: i5.DropDownComponent, selector: "verben-drop-down", inputs: ["options", "width", "showHorizontalLine", "horizontalLineColor", "optionLabel", "optionSubLabel", "optionValue", "placeholder", "invalidMessage", "errorPosition", "loadMoreCaption", "display", "showClear", "lazyLoad", "selectKey", "styleClass", "group", "multiselect", "filter", "avoidDuplication", "filterBy", "debounceTime", "minChar", "disabled", "required", "load", "asyncLabel", "search"], outputs: ["optionsChange", "onChange", "onClick", "onClear"] }, { kind: "component", type: ChatControlComponent, selector: "lib-chat-control", inputs: ["message", "color", "textColor", "disabled", "placeholder", "hideBtn"], outputs: ["messageChange", "formSubmit", "sendMessageResult", "filesChange"] }, { kind: "component", type: i7.FileRenderComponent, selector: "app-file-render", inputs: ["fileUrl"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i8.AsyncLabelPipe, name: "asyncLabel" }, { kind: "pipe", type: i8.CompanyDatePipe, name: "companyDate" }] });
815
+ }
816
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TicketingDetailComponent, decorators: [{
817
+ type: Component,
818
+ args: [{ selector: 'app-ticketing-detail', template: "<div\n (click)=\"goBack()\"\n class=\"my-4 w-fit ml-auto border p-1 rounded-lg border-gray-400 cursor-pointer\"\n>\n <verben-svg [icon]=\"'arrow-left'\"></verben-svg>\n</div>\n<div *ngIf=\"data\" class=\"grid grid-cols-12 gap-8\">\n <div\n class=\"lg:col-span-8 col-span-12 border relative rounded-2xl p-6 border-[#0000001A]\"\n >\n \n <div class=\"flex gap-2 items-center mt-6\">\n <h3 class=\"mt-2 font-bold\">{{ data.Title }}</h3>\n\n <div\n *ngIf=\"\n data.Priority && data.Priority\n | asyncLabel : 'TenantPriority' : 'Color' : ticketingAPI\n | async as color\n \"\n class=\"enum-val rounded-[8px] flex gap-2 items-center justify-center\"\n [style.backgroundColor]=\"softenColor(color)\"\n >\n <span [style.backgroundColor]=\"color\" class=\"enum-dot\"></span>\n <span>{{\n data.Priority\n | asyncLabel : \"TenantPriority\" : \"Name\" : ticketingAPI\n | async\n }}</span>\n </div>\n </div>\n <p class=\"mt-2 text-sm\">{{ data.Description }}</p>\n <div class=\"attachment-section p-2 flex flex-col gap-3\">\n <div\n class=\"attachments-container grid grid-cols-3 gap-2\"\n *ngIf=\"data.FileUrls\"\n >\n <ng-container *ngFor=\"let file of data.FileUrls\">\n <app-file-render [fileUrl]=\"file\"></app-file-render>\n </ng-container>\n </div>\n </div>\n <div class=\"max-h-[500px] lg:max-h-[230px] overflow-y-auto pr-1\">\n <div *ngFor=\"let message of data.Messages; let last = last\">\n <div\n class=\"mt-6 flex justify-between items-center pb-4\"\n [ngClass]=\"{ 'border-b border-gray-300': !last }\"\n >\n <div class=\"flex items-center gap-2\">\n <img\n class=\"w-[50px] h-[50px] object-cover rounded-full\"\n [src]=\"'/assets/images/avatar.png'\"\n alt=\"avatar\"\n />\n <div>\n <h4 class=\"font-medium\">\n {{\n message.Company\n | asyncLabel : \"Company\" : \"Description\" : glAPI\n | async\n }}\n </h4>\n <p class=\"text-xs\">{{ !message.Name ? \"Nil\" : message.Name }}</p>\n </div>\n </div>\n\n <p class=\"text-sm text-gray-600\">\n {{ message.CreatedAt | companyDate }}\n </p>\n </div>\n\n <p class=\"mt-6 text-sm\">\n {{ !message.Content ? \"Nil\" : message.Content }}\n </p>\n\n <div class=\"attachment-section p-2 flex flex-col gap-3\">\n <div\n class=\"attachments-container grid grid-cols-3 gap-2\"\n *ngIf=\"message.AttachmentUrls\"\n >\n <ng-container *ngFor=\"let file of message.AttachmentUrls\">\n <app-file-render [fileUrl]=\"file\"></app-file-render>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"pt-5\">\n <div class=\"border rounded-lg p-4 border-[#0000001A]\">\n \n\n <lib-chat-control\n (messageChange)=\"messageChange($event)\"\n [placeholder]=\"'Start typing a message....'\"\n [disabled]=\"disableChatBoxButton()\"\n (sendMessageResult)=\"saveMessageChat(data, message)\"\n [(message)]=\"message\"\n [color]=\"color\"\n [textColor]=\"textColor\"\n (filesChange)=\"onFilesUpdate($event)\"\n (formSubmit)=\"onKeyDown($event)\"\n ></lib-chat-control>\n </div>\n <verben-pop-Up\n #userPopup\n [customStyles]=\"{\n 'z-index': '99',\n position: 'absolute',\n bottom: '70px',\n left: '300px'\n }\"\n [enableMouseLeave]=\"false\"\n >\n <div\n class=\"pop-up-content flex flex-col max-h-[200px] w-[300px] overflow-y-auto p-3\"\n dropdown-content\n [style.border-color]=\"color\"\n > \n <p class=\"text-gray-500 p-2\">Suggestions</p>\n\n <div\n *ngFor=\"let u of usersResult\"\n class=\"p-2 hover:bg-gray-100 text-sm cursor-pointer w-fit\"\n (click)=\"selectUser(u)\"\n >\n {{ u.MailAddress }}\n </div>\n\n <p *ngIf=\"usersResult.length === 0\" class=\"text-gray-500 text-sm\">\n No matching users\n </p>\n </div>\n </verben-pop-Up>\n </div>\n </div>\n <div\n class=\"lg:col-span-4 col-span-12 p-4 border rounded-md border-[#0000001A]\"\n >\n <h3 class=\"mt-2 font-bold\">Details</h3>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Ticket ID :</p>\n <p>{{ data.Code }}</p>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Subject:</p>\n <p class=\"text-xs text-right\">{{ data.Title }}</p>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Status:</p>\n <div\n *ngIf=\"data.Status\"\n class=\"enum-val rounded-[8px] flex gap-2 items-center justify-center {{\n data.Status\n }}\"\n >\n <span class=\"enum-dot\"></span>\n <span>{{ data.Status }}</span>\n </div>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">CreatedAt :</p>\n <p class=\"text-sm\">{{ data.CreatedAt | companyDate }}</p>\n </div>\n <div\n *ngIf=\"data.Status === ticketStatus.Completed\"\n class=\"flex items-center justify-between mt-4\"\n >\n <p class=\"text-sm font-light\">Execution Time :</p>\n <p class=\"text-sm\">{{ data.ExecutionTime }}</p>\n </div>\n <div class=\"flex items-center justify-between mt-4\">\n <p class=\"text-sm font-light\">Priority :</p>\n <div\n *ngIf=\"\n data.Priority && data.Priority\n | asyncLabel : 'TenantPriority' : 'Color' : ticketingAPI\n | async as color\n \"\n class=\"enum-val rounded-[8px] flex gap-2 items-center justify-center\"\n [style.backgroundColor]=\"softenColor(color)\"\n >\n <span [style.backgroundColor]=\"color\" class=\"enum-dot\"></span>\n <span>{{\n data.Priority\n | asyncLabel : \"TenantPriority\" : \"Name\" : ticketingAPI\n | async\n }}</span>\n </div>\n </div>\n <section class=\"mt-6\">\n <h3 class=\"font-bold\">Properties</h3>\n\n <div class=\"flex flex-col gap-1 mt-2\">\n <div class=\"text-sm text-gray-600\">Status</div>\n <verben-drop-down\n width=\"100%\"\n placeholder=\"Select Status\"\n [disabled]=\"true\"\n [options]=\"statusOptions\"\n [(ngModel)]=\"data.Status\"\n >\n </verben-drop-down>\n </div>\n <div class=\"flex flex-col gap-1 mt-2 mb-18\">\n <div class=\"text-sm text-gray-600\">Priority</div>\n <verben-drop-down\n optionLabel=\"Name\"\n [filter]=\"true\"\n optionValue=\"Code\"\n [disabled]=\"true\"\n [options]=\"tenantOptions\"\n [(ngModel)]=\"data.Priority\"\n [width]=\"'100%'\"\n ></verben-drop-down>\n </div>\n <!-- <verbena-button\n (click)=\"save()\"\n [width]=\"'100%'\"\n [textColor]=\"'#fff'\"\n [text]=\"'Update'\"\n [bgColor]=\"'#9C56BA'\"\n ></verbena-button> -->\n </section>\n </div>\n</div>\n" }]
819
+ }], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: i2$1.HttpWebRequestService }, { type: i2$1.UtilService }, { type: i2$1.SharedService }, { type: i3.Location }, { type: i2$1.EnvironmentService }], propDecorators: { user: [{
820
+ type: Input
821
+ }], defaultCompany: [{
822
+ type: Input
823
+ }], userPopups: [{
824
+ type: ViewChildren,
825
+ args: ['userPopup']
826
+ }], color: [{
827
+ type: Input
828
+ }], textColor: [{
829
+ type: Input
830
+ }] } });
831
+
832
+ const appRoutes$1 = [];
833
+ class ChatControlLibModule {
834
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatControlLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
835
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ChatControlLibModule, declarations: [ChatControlComponent], imports: [CommonModule,
836
+ FormsModule,
837
+ ReactiveFormsModule,
838
+ DataTableModule,
839
+ SvgModule,
840
+ VerbenPopUpModule,
841
+ DataViewModule,
842
+ VerbenaButtonModule,
843
+ CardDataViewModule,
844
+ DropDownModule,
845
+ VerbenaSwitchModule,
846
+ TableFilterModule,
847
+ VisibleColumnModule,
848
+ AsyncLabelPipe,
849
+ SortTableModule,
850
+ DataExportModule,
851
+ TooltipModule,
852
+ VerbenDialogueModule,
853
+ VerbenaInputModule,
854
+ DataImportModule,
855
+ DataExportModule,
856
+ PickerComponent,
857
+ PickerModule,
858
+ DataXportModule, i1.RouterModule], exports: [ChatControlComponent] });
859
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatControlLibModule, imports: [CommonModule,
860
+ FormsModule,
861
+ ReactiveFormsModule,
862
+ DataTableModule,
863
+ SvgModule,
864
+ VerbenPopUpModule,
865
+ DataViewModule,
866
+ VerbenaButtonModule,
867
+ CardDataViewModule,
868
+ DropDownModule,
869
+ VerbenaSwitchModule,
870
+ TableFilterModule,
871
+ VisibleColumnModule,
872
+ SortTableModule,
873
+ DataExportModule,
874
+ TooltipModule,
875
+ VerbenDialogueModule,
876
+ VerbenaInputModule,
877
+ DataImportModule,
878
+ DataExportModule,
879
+ PickerComponent,
880
+ PickerModule,
881
+ DataXportModule,
882
+ RouterModule.forChild(appRoutes$1)] });
883
+ }
884
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatControlLibModule, decorators: [{
885
+ type: NgModule,
886
+ args: [{
887
+ declarations: [ChatControlComponent],
888
+ imports: [
889
+ CommonModule,
890
+ FormsModule,
891
+ ReactiveFormsModule,
892
+ DataTableModule,
893
+ SvgModule,
894
+ VerbenPopUpModule,
895
+ DataViewModule,
896
+ VerbenaButtonModule,
897
+ CardDataViewModule,
898
+ DropDownModule,
899
+ VerbenaSwitchModule,
900
+ TableFilterModule,
901
+ VisibleColumnModule,
902
+ AsyncLabelPipe,
903
+ SortTableModule,
904
+ DataExportModule,
905
+ TooltipModule,
906
+ VerbenDialogueModule,
907
+ VerbenaInputModule,
908
+ DataImportModule,
909
+ DataExportModule,
910
+ PickerComponent,
911
+ PickerModule,
912
+ DataXportModule,
913
+ RouterModule.forChild(appRoutes$1),
914
+ ],
915
+ exports: [ChatControlComponent],
916
+ }]
917
+ }] });
918
+
919
+ const appRoutes = [];
920
+ class TicketDetailLibModule {
921
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TicketDetailLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
922
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: TicketDetailLibModule, declarations: [TicketingDetailComponent], imports: [CommonModule,
923
+ FormsModule,
924
+ ReactiveFormsModule,
925
+ DataTableModule,
926
+ SvgModule,
927
+ VerbenPopUpModule,
928
+ DataViewModule,
929
+ VerbenaButtonModule,
930
+ CardDataViewModule,
931
+ DropDownModule,
932
+ VerbenaSwitchModule,
933
+ TableFilterModule,
934
+ VisibleColumnModule,
935
+ AsyncLabelPipe,
936
+ SortTableModule,
937
+ DataExportModule,
938
+ TooltipModule,
939
+ VerbenDialogueModule,
940
+ VerbenaInputModule,
941
+ DataImportModule,
942
+ DataExportModule,
943
+ DataXportModule,
944
+ EllipseWordPipe,
945
+ CompanyDatePipe,
946
+ ChatControlLibModule,
947
+ AddTicketLibModule,
948
+ FileRenderLibModule, i1.RouterModule], exports: [TicketingDetailComponent] });
949
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TicketDetailLibModule, imports: [CommonModule,
950
+ FormsModule,
951
+ ReactiveFormsModule,
952
+ DataTableModule,
953
+ SvgModule,
954
+ VerbenPopUpModule,
955
+ DataViewModule,
956
+ VerbenaButtonModule,
957
+ CardDataViewModule,
958
+ DropDownModule,
959
+ VerbenaSwitchModule,
960
+ TableFilterModule,
961
+ VisibleColumnModule,
962
+ SortTableModule,
963
+ DataExportModule,
964
+ TooltipModule,
965
+ VerbenDialogueModule,
966
+ VerbenaInputModule,
967
+ DataImportModule,
968
+ DataExportModule,
969
+ DataXportModule,
970
+ ChatControlLibModule,
971
+ AddTicketLibModule,
972
+ FileRenderLibModule,
973
+ RouterModule.forChild(appRoutes)] });
974
+ }
975
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TicketDetailLibModule, decorators: [{
976
+ type: NgModule,
977
+ args: [{
978
+ declarations: [TicketingDetailComponent],
979
+ imports: [
980
+ CommonModule,
981
+ FormsModule,
982
+ ReactiveFormsModule,
983
+ DataTableModule,
984
+ SvgModule,
985
+ VerbenPopUpModule,
986
+ DataViewModule,
987
+ VerbenaButtonModule,
988
+ CardDataViewModule,
989
+ DropDownModule,
990
+ VerbenaSwitchModule,
991
+ TableFilterModule,
992
+ VisibleColumnModule,
993
+ AsyncLabelPipe,
994
+ SortTableModule,
995
+ DataExportModule,
996
+ TooltipModule,
997
+ VerbenDialogueModule,
998
+ VerbenaInputModule,
999
+ DataImportModule,
1000
+ DataExportModule,
1001
+ DataXportModule,
1002
+ EllipseWordPipe,
1003
+ CompanyDatePipe,
1004
+ ChatControlLibModule,
1005
+ AddTicketLibModule,
1006
+ FileRenderLibModule,
1007
+ RouterModule.forChild(appRoutes),
1008
+ ],
1009
+ exports: [TicketingDetailComponent],
1010
+ }]
1011
+ }] });
1012
+
1013
+ /**
1014
+ * Generated bundle index. Do not edit.
1015
+ */
1016
+
1017
+ export { TicketDetailLibModule, TicketingDetailComponent, appRoutes };
1018
+ //# sourceMappingURL=verben-ticketing-ui-src-lib-components-ticketing-detail.mjs.map