cat-documents-ng 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.
Files changed (210) hide show
  1. package/ng-package.json +10 -0
  2. package/package.json +5 -11
  3. package/src/Shared/components/confirmation-dialog/confirmation-dialog.component.html +3 -0
  4. package/src/Shared/components/confirmation-dialog/confirmation-dialog.component.scss +13 -0
  5. package/src/Shared/components/confirmation-dialog/confirmation-dialog.component.spec.ts +70 -0
  6. package/src/Shared/components/confirmation-dialog/confirmation-dialog.component.ts +133 -0
  7. package/src/Shared/components/table-primary/table-primary.component.html +66 -0
  8. package/src/Shared/components/table-primary/table-primary.component.scss +227 -0
  9. package/src/Shared/components/table-primary/table-primary.component.spec.ts +23 -0
  10. package/src/Shared/components/table-primary/table-primary.component.ts +143 -0
  11. package/src/Shared/components/table-primary/table-primary.model.ts +21 -0
  12. package/src/Shared/constant/ERROR.ts +55 -0
  13. package/src/Shared/constant/PERMISSIONS.ts +17 -0
  14. package/src/Shared/constant/SHARED.ts +936 -0
  15. package/{Shared/constant/URLS.d.ts → src/Shared/constant/URLS.ts} +31 -25
  16. package/src/Shared/services/app-config.service.spec.ts +19 -0
  17. package/src/Shared/services/app-config.service.ts +73 -0
  18. package/{Shared/services/global-error.handler.d.ts → src/Shared/services/global-error.handler.ts} +11 -9
  19. package/src/Shared/services/session.service.spec.ts +16 -0
  20. package/src/Shared/services/session.service.ts +76 -0
  21. package/src/Shared/shared.module.ts +25 -0
  22. package/src/lib/document/components/csv-viewer/csv-viewer.component.ts +1 -0
  23. package/src/lib/document/components/document-actions/document-actions.component.html +59 -0
  24. package/src/lib/document/components/document-actions/document-actions.component.scss +362 -0
  25. package/src/lib/document/components/document-actions/document-actions.component.spec.ts +297 -0
  26. package/src/lib/document/components/document-actions/document-actions.component.ts +163 -0
  27. package/src/lib/document/components/document-container/document-container.component.html +36 -0
  28. package/src/lib/document/components/document-container/document-container.component.scss +144 -0
  29. package/src/lib/document/components/document-container/document-container.component.spec.ts +110 -0
  30. package/src/lib/document/components/document-container/document-container.component.ts +363 -0
  31. package/src/lib/document/components/document-content-viewer/document-content-viewer.component.html +332 -0
  32. package/src/lib/document/components/document-content-viewer/document-content-viewer.component.scss +1877 -0
  33. package/src/lib/document/components/document-content-viewer/document-content-viewer.component.spec.ts +258 -0
  34. package/src/lib/document/components/document-content-viewer/document-content-viewer.component.ts +664 -0
  35. package/src/lib/document/components/document-history/document-history.component.html +96 -0
  36. package/src/lib/document/components/document-history/document-history.component.scss +392 -0
  37. package/src/lib/document/components/document-history/document-history.component.spec.ts +93 -0
  38. package/src/lib/document/components/document-history/document-history.component.ts +373 -0
  39. package/src/lib/document/components/document-list/document-list.component.html +46 -0
  40. package/src/lib/document/components/document-list/document-list.component.scss +513 -0
  41. package/src/lib/document/components/document-list/document-list.component.spec.ts +486 -0
  42. package/src/lib/document/components/document-list/document-list.component.ts +682 -0
  43. package/src/lib/document/components/document-list-item/document-list-item.component.html +36 -0
  44. package/src/lib/document/components/document-list-item/document-list-item.component.scss +34 -0
  45. package/src/lib/document/components/document-list-item/document-list-item.component.spec.ts +75 -0
  46. package/src/lib/document/components/document-list-item/document-list-item.component.ts +40 -0
  47. package/src/lib/document/components/document-search/document-search.component.html +64 -0
  48. package/src/lib/document/components/document-search/document-search.component.scss +206 -0
  49. package/src/lib/document/components/document-search/document-search.component.spec.ts +82 -0
  50. package/src/lib/document/components/document-search/document-search.component.ts +163 -0
  51. package/src/lib/document/components/document-status/document-status.component.html +31 -0
  52. package/src/lib/document/components/document-status/document-status.component.scss +192 -0
  53. package/src/lib/document/components/document-status/document-status.component.spec.ts +23 -0
  54. package/src/lib/document/components/document-status/document-status.component.ts +87 -0
  55. package/src/lib/document/components/document-upload/document-upload.component.html +160 -0
  56. package/src/lib/document/components/document-upload/document-upload.component.scss +235 -0
  57. package/src/lib/document/components/document-upload/document-upload.component.spec.ts +95 -0
  58. package/src/lib/document/components/document-upload/document-upload.component.ts +668 -0
  59. package/src/lib/document/components/document-viewer/document-viewer.component.html +50 -0
  60. package/src/lib/document/components/document-viewer/document-viewer.component.scss +187 -0
  61. package/src/lib/document/components/document-viewer/document-viewer.component.spec.ts +79 -0
  62. package/src/lib/document/components/document-viewer/document-viewer.component.ts +261 -0
  63. package/src/lib/document/components/document-zoom-controls/document-zoom-controls.component.html +48 -0
  64. package/src/lib/document/components/document-zoom-controls/document-zoom-controls.component.scss +320 -0
  65. package/src/lib/document/components/document-zoom-controls/document-zoom-controls.component.spec.ts +59 -0
  66. package/src/lib/document/components/document-zoom-controls/document-zoom-controls.component.ts +150 -0
  67. package/src/lib/document/components/documents-menu/documents-menu.component.html +44 -0
  68. package/src/lib/document/components/documents-menu/documents-menu.component.scss +363 -0
  69. package/src/lib/document/components/documents-menu/documents-menu.component.spec.ts +23 -0
  70. package/src/lib/document/components/documents-menu/documents-menu.component.ts +316 -0
  71. package/src/lib/document/components/folder-block/folder-block.component.html +46 -0
  72. package/src/lib/document/components/folder-block/folder-block.component.scss +9 -0
  73. package/src/lib/document/components/folder-block/folder-block.component.spec.ts +70 -0
  74. package/{lib/document/components/folder-block/folder-block.component.d.ts → src/lib/document/components/folder-block/folder-block.component.ts} +28 -12
  75. package/src/lib/document/components/folder-container/folder-container.component.html +56 -0
  76. package/src/lib/document/components/folder-container/folder-container.component.scss +20 -0
  77. package/src/lib/document/components/folder-container/folder-container.component.spec.ts +27 -0
  78. package/src/lib/document/components/folder-container/folder-container.component.ts +328 -0
  79. package/src/lib/document/components/linked-document/linked-document.component.html +23 -0
  80. package/src/lib/document/components/linked-document/linked-document.component.scss +10 -0
  81. package/src/lib/document/components/linked-document/linked-document.component.spec.ts +61 -0
  82. package/src/lib/document/components/linked-document/linked-document.component.ts +49 -0
  83. package/src/lib/document/components/request-document/request-document.component.html +86 -0
  84. package/src/lib/document/components/request-document/request-document.component.scss +16 -0
  85. package/src/lib/document/components/request-document/request-document.component.ts +278 -0
  86. package/src/lib/document/components/sidebar/sidebar.component.html +75 -0
  87. package/src/lib/document/components/sidebar/sidebar.component.scss +157 -0
  88. package/src/lib/document/components/sidebar/sidebar.component.spec.ts +114 -0
  89. package/src/lib/document/components/sidebar/sidebar.component.ts +223 -0
  90. package/src/lib/document/components/user-list/user-list.component.html +33 -0
  91. package/src/lib/document/components/user-list/user-list.component.scss +118 -0
  92. package/src/lib/document/components/user-list/user-list.component.spec.ts +23 -0
  93. package/src/lib/document/components/user-list/user-list.component.ts +181 -0
  94. package/src/lib/document/constant/DOCUMENT_HISTORY.ts +52 -0
  95. package/src/lib/document/directives/document.directive.ts +32 -0
  96. package/src/lib/document/directives/permission.directive.spec.ts +0 -0
  97. package/src/lib/document/directives/permission.directive.ts +72 -0
  98. package/src/lib/document/document.module.ts +351 -0
  99. package/{lib/document/models/document-alert.model.d.ts → src/lib/document/models/document-alert.model.ts} +11 -4
  100. package/src/lib/document/models/document-category.model.ts +30 -0
  101. package/src/lib/document/models/document-history.model.ts +109 -0
  102. package/src/lib/document/models/document-list-response.model.ts +37 -0
  103. package/src/lib/document/models/document-type.model.ts +44 -0
  104. package/src/lib/document/models/document.model.ts +53 -0
  105. package/{lib/document/models/folder.model.d.ts → src/lib/document/models/folder.model.ts} +10 -4
  106. package/src/lib/document/models/status-data.model.ts +31 -0
  107. package/src/lib/document/models/uploaded-file-response.model.ts +7 -0
  108. package/src/lib/document/models/user-list.model.ts +10 -0
  109. package/src/lib/document/services/csv-parser.service.spec.ts +97 -0
  110. package/src/lib/document/services/csv-parser.service.ts +303 -0
  111. package/src/lib/document/services/document-actions.service.ts +125 -0
  112. package/src/lib/document/services/document-content-type.service.ts +193 -0
  113. package/src/lib/document/services/document-history-style.service.ts +138 -0
  114. package/src/lib/document/services/document-history.service.ts +129 -0
  115. package/src/lib/document/services/document-http.service.spec.ts +119 -0
  116. package/src/lib/document/services/document-http.service.ts +497 -0
  117. package/src/lib/document/services/document-list.service.ts +195 -0
  118. package/src/lib/document/services/document-menu.service.ts +277 -0
  119. package/src/lib/document/services/document-scroll.service.ts +138 -0
  120. package/src/lib/document/services/document-severity.service.ts +98 -0
  121. package/src/lib/document/services/document-table-builder.service.ts +82 -0
  122. package/src/lib/document/services/document-upload-business.service.ts +326 -0
  123. package/src/lib/document/services/document-upload-data.service.ts +82 -0
  124. package/src/lib/document/services/document-upload-form.service.ts +149 -0
  125. package/src/lib/document/services/document-upload.service.spec.ts +99 -0
  126. package/src/lib/document/services/document-upload.service.ts +209 -0
  127. package/src/lib/document/services/document-viewer.service.ts +279 -0
  128. package/src/lib/document/services/document-zoom.service.spec.ts +56 -0
  129. package/src/lib/document/services/document-zoom.service.ts +164 -0
  130. package/src/lib/document/services/document.service.ts +356 -0
  131. package/src/lib/document/services/eml-parser.service.ts +444 -0
  132. package/src/lib/document/services/excel-parser.service.spec.ts +66 -0
  133. package/src/lib/document/services/excel-parser.service.ts +483 -0
  134. package/src/lib/document/services/file-format.service.spec.ts +16 -0
  135. package/src/lib/document/services/file-format.service.ts +63 -0
  136. package/src/lib/document/services/status-calculator.service.ts +44 -0
  137. package/src/lib/document/services/user-list.service.ts +77 -0
  138. package/src/lib/document/state/document.query.ts +378 -0
  139. package/{lib/document/state/document.service.d.ts → src/lib/document/state/document.service.ts} +46 -15
  140. package/src/lib/document/state/document.state.ts +100 -0
  141. package/src/lib/document/state/document.store.ts +200 -0
  142. package/{public-api.d.ts → src/public-api.ts} +4 -0
  143. package/tsconfig.lib.json +15 -0
  144. package/tsconfig.lib.prod.json +11 -0
  145. package/tsconfig.spec.json +15 -0
  146. package/Shared/components/confirmation-dialog/confirmation-dialog.component.d.ts +0 -44
  147. package/Shared/components/table-primary/table-primary.component.d.ts +0 -31
  148. package/Shared/components/table-primary/table-primary.model.d.ts +0 -19
  149. package/Shared/constant/ERROR.d.ts +0 -52
  150. package/Shared/constant/SHARED.d.ts +0 -546
  151. package/Shared/services/app-config.service.d.ts +0 -51
  152. package/Shared/services/session.service.d.ts +0 -46
  153. package/Shared/shared.module.d.ts +0 -14
  154. package/fesm2022/cat-documents-ng.mjs +0 -11392
  155. package/fesm2022/cat-documents-ng.mjs.map +0 -1
  156. package/index.d.ts +0 -5
  157. package/lib/document/components/document-actions/document-actions.component.d.ts +0 -78
  158. package/lib/document/components/document-container/document-container.component.d.ts +0 -162
  159. package/lib/document/components/document-content-viewer/document-content-viewer.component.d.ts +0 -291
  160. package/lib/document/components/document-history/document-history.component.d.ts +0 -160
  161. package/lib/document/components/document-list/document-list.component.d.ts +0 -299
  162. package/lib/document/components/document-list-item/document-list-item.component.d.ts +0 -28
  163. package/lib/document/components/document-search/document-search.component.d.ts +0 -77
  164. package/lib/document/components/document-status/document-status.component.d.ts +0 -24
  165. package/lib/document/components/document-upload/document-upload.component.d.ts +0 -321
  166. package/lib/document/components/document-viewer/document-viewer.component.d.ts +0 -137
  167. package/lib/document/components/document-zoom-controls/document-zoom-controls.component.d.ts +0 -33
  168. package/lib/document/components/documents-menu/documents-menu.component.d.ts +0 -110
  169. package/lib/document/components/folder-container/folder-container.component.d.ts +0 -162
  170. package/lib/document/components/linked-document/linked-document.component.d.ts +0 -39
  171. package/lib/document/components/request-document/request-document.component.d.ts +0 -69
  172. package/lib/document/components/sidebar/sidebar.component.d.ts +0 -109
  173. package/lib/document/components/user-list/user-list.component.d.ts +0 -34
  174. package/lib/document/constant/DOCUMENT_HISTORY.d.ts +0 -41
  175. package/lib/document/directives/document.directive.d.ts +0 -20
  176. package/lib/document/directives/permission.directive.d.ts +0 -38
  177. package/lib/document/document.module.d.ts +0 -60
  178. package/lib/document/models/document-category.model.d.ts +0 -24
  179. package/lib/document/models/document-history.model.d.ts +0 -94
  180. package/lib/document/models/document-list-response.model.d.ts +0 -33
  181. package/lib/document/models/document-type.model.d.ts +0 -37
  182. package/lib/document/models/document.model.d.ts +0 -44
  183. package/lib/document/models/status-data.model.d.ts +0 -27
  184. package/lib/document/models/uploaded-file-response.model.d.ts +0 -7
  185. package/lib/document/models/user-list.model.d.ts +0 -8
  186. package/lib/document/services/csv-parser.service.d.ts +0 -88
  187. package/lib/document/services/document-actions.service.d.ts +0 -48
  188. package/lib/document/services/document-content-type.service.d.ts +0 -85
  189. package/lib/document/services/document-history-style.service.d.ts +0 -34
  190. package/lib/document/services/document-history.service.d.ts +0 -42
  191. package/lib/document/services/document-http.service.d.ts +0 -179
  192. package/lib/document/services/document-list.service.d.ts +0 -74
  193. package/lib/document/services/document-menu.service.d.ts +0 -122
  194. package/lib/document/services/document-scroll.service.d.ts +0 -55
  195. package/lib/document/services/document-table-builder.service.d.ts +0 -38
  196. package/lib/document/services/document-upload-business.service.d.ts +0 -107
  197. package/lib/document/services/document-upload-data.service.d.ts +0 -40
  198. package/lib/document/services/document-upload-form.service.d.ts +0 -41
  199. package/lib/document/services/document-upload.service.d.ts +0 -99
  200. package/lib/document/services/document-viewer.service.d.ts +0 -97
  201. package/lib/document/services/document-zoom.service.d.ts +0 -81
  202. package/lib/document/services/document.service.d.ts +0 -161
  203. package/lib/document/services/eml-parser.service.d.ts +0 -116
  204. package/lib/document/services/excel-parser.service.d.ts +0 -169
  205. package/lib/document/services/file-format.service.d.ts +0 -34
  206. package/lib/document/services/status-calculator.service.d.ts +0 -20
  207. package/lib/document/services/user-list.service.d.ts +0 -29
  208. package/lib/document/state/document.query.d.ts +0 -243
  209. package/lib/document/state/document.state.d.ts +0 -61
  210. package/lib/document/state/document.store.d.ts +0 -56
@@ -0,0 +1,444 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
3
+ import { Observable, throwError } from 'rxjs';
4
+ import { HttpClient, HttpHeaders } from '@angular/common/http';
5
+ import { catchError, map } from 'rxjs/operators';
6
+
7
+ /**
8
+ * Interface for parsed email data
9
+ */
10
+ export interface ParsedEmailData {
11
+ from: string;
12
+ to: string;
13
+ cc?: string;
14
+ subject: string;
15
+ date: string;
16
+ body: string;
17
+ isHtml: boolean;
18
+ attachments?: EmailAttachment[];
19
+ }
20
+
21
+ /**
22
+ * Interface for email attachments
23
+ */
24
+ export interface EmailAttachment {
25
+ filename: string;
26
+ contentType: string;
27
+ size: number;
28
+ }
29
+
30
+ /**
31
+ * Service for parsing EML (email message) files
32
+ * Handles both plain text and HTML email content
33
+ */
34
+ @Injectable({
35
+ providedIn: 'root'
36
+ })
37
+ export class EmlParserService {
38
+
39
+ constructor(
40
+ private sanitizer: DomSanitizer,
41
+ private http: HttpClient
42
+ ) { }
43
+
44
+ /**
45
+ * Parses EML file content
46
+ * @param emlContent - Raw EML file content as string
47
+ * @returns Parsed email data
48
+ */
49
+ parseEmlData(emlContent: string): ParsedEmailData | null {
50
+ try {
51
+ const lines = emlContent.split(/\r?\n/);
52
+ const headers = this.parseHeaders(lines);
53
+ const body = this.parseBody(lines);
54
+
55
+ return {
56
+ from: this.cleanEmailAddress(headers['from'] || 'Unknown'),
57
+ to: this.cleanEmailAddress(headers['to'] || 'Unknown'),
58
+ cc: headers['cc'] ? this.cleanEmailAddress(headers['cc']) : undefined,
59
+ subject: this.decodeHeader(headers['subject'] || 'No Subject'),
60
+ date: this.formatDate(headers['date'] || ''),
61
+ body: body.content,
62
+ isHtml: body.isHtml,
63
+ attachments: this.parseAttachments(emlContent)
64
+ };
65
+ } catch (error) {
66
+ console.error('Error parsing EML file:', error);
67
+ return null;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Parses email headers from EML content
73
+ * @param lines - Lines of the EML file
74
+ * @returns Object containing parsed headers
75
+ */
76
+ private parseHeaders(lines: string[]): { [key: string]: string } {
77
+ const headers: { [key: string]: string } = {};
78
+ let currentHeader = '';
79
+ let currentValue = '';
80
+
81
+ for (const line of lines) {
82
+ // Empty line indicates end of headers
83
+ if (line.trim() === '') {
84
+ if (currentHeader) {
85
+ headers[currentHeader.toLowerCase()] = currentValue.trim();
86
+ }
87
+ break;
88
+ }
89
+
90
+ // Check if line is a new header (starts without whitespace and contains ':')
91
+ if (line.match(/^[\w-]+:/) && !line.startsWith(' ') && !line.startsWith('\t')) {
92
+ // Save previous header if exists
93
+ if (currentHeader) {
94
+ headers[currentHeader.toLowerCase()] = currentValue.trim();
95
+ }
96
+
97
+ // Parse new header
98
+ const colonIndex = line.indexOf(':');
99
+ currentHeader = line.substring(0, colonIndex).trim();
100
+ currentValue = line.substring(colonIndex + 1).trim();
101
+ } else if (currentHeader && (line.startsWith(' ') || line.startsWith('\t'))) {
102
+ // Continuation of previous header (folded header)
103
+ currentValue += ' ' + line.trim();
104
+ }
105
+ }
106
+
107
+ return headers;
108
+ }
109
+
110
+ /**
111
+ * Parses the email body from EML content
112
+ * @param lines - Lines of the EML file
113
+ * @returns Object containing body content and format information
114
+ */
115
+ private parseBody(lines: string[]): { content: string, isHtml: boolean } {
116
+ let inBody = false;
117
+ let bodyLines: string[] = [];
118
+ let contentType = 'text/plain';
119
+ let boundary = '';
120
+ let isBase64 = false;
121
+ let isQuotedPrintable = false;
122
+
123
+ // Find Content-Type and boundary
124
+ for (let i = 0; i < lines.length; i++) {
125
+ const line = lines[i];
126
+
127
+ if (line.toLowerCase().startsWith('content-type:')) {
128
+ contentType = line.toLowerCase().includes('text/html') ? 'text/html' : 'text/plain';
129
+
130
+ // Check for multipart boundary
131
+ const boundaryMatch = line.match(/boundary="?([^";\s]+)"?/i);
132
+ if (boundaryMatch) {
133
+ boundary = boundaryMatch[1];
134
+ }
135
+ }
136
+
137
+ if (line.toLowerCase().startsWith('content-transfer-encoding:')) {
138
+ isBase64 = line.toLowerCase().includes('base64');
139
+ isQuotedPrintable = line.toLowerCase().includes('quoted-printable');
140
+ }
141
+
142
+ // Empty line marks start of body
143
+ if (line.trim() === '' && !inBody) {
144
+ inBody = true;
145
+ continue;
146
+ }
147
+
148
+ if (inBody) {
149
+ bodyLines.push(line);
150
+ }
151
+ }
152
+
153
+ // If multipart, extract the relevant part
154
+ if (boundary) {
155
+ bodyLines = this.extractMultipartBody(bodyLines.join('\n'), boundary, contentType);
156
+ }
157
+
158
+ let bodyContent = bodyLines.join('\n').trim();
159
+
160
+ // Decode body if necessary
161
+ if (isBase64) {
162
+ try {
163
+ bodyContent = atob(bodyContent.replace(/\s/g, ''));
164
+ } catch (e) {
165
+ console.warn('Failed to decode base64 content');
166
+ }
167
+ } else if (isQuotedPrintable) {
168
+ bodyContent = this.decodeQuotedPrintable(bodyContent);
169
+ }
170
+
171
+ // Clean up HTML if present
172
+ if (contentType === 'text/html') {
173
+ bodyContent = this.sanitizeHtmlContent(bodyContent);
174
+ }
175
+
176
+ return {
177
+ content: bodyContent || 'No content available',
178
+ isHtml: contentType === 'text/html'
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Extracts body from multipart email
184
+ * @param content - Email content
185
+ * @param boundary - Multipart boundary string
186
+ * @param preferredType - Preferred content type
187
+ * @returns Array of body lines
188
+ */
189
+ private extractMultipartBody(content: string, boundary: string, preferredType: string): string[] {
190
+ const parts = content.split('--' + boundary);
191
+
192
+ for (const part of parts) {
193
+ const lines = part.split(/\r?\n/);
194
+ let partContentType = 'text/plain';
195
+ let isBase64 = false;
196
+ let isQuotedPrintable = false;
197
+ let inPartBody = false;
198
+ const partBodyLines: string[] = [];
199
+
200
+ for (let i = 0; i < lines.length; i++) {
201
+ const line = lines[i];
202
+
203
+ if (line.toLowerCase().startsWith('content-type:')) {
204
+ partContentType = line.toLowerCase().includes('text/html') ? 'text/html' : 'text/plain';
205
+ }
206
+
207
+ if (line.toLowerCase().startsWith('content-transfer-encoding:')) {
208
+ isBase64 = line.toLowerCase().includes('base64');
209
+ isQuotedPrintable = line.toLowerCase().includes('quoted-printable');
210
+ }
211
+
212
+ if (line.trim() === '' && !inPartBody) {
213
+ inPartBody = true;
214
+ continue;
215
+ }
216
+
217
+ if (inPartBody) {
218
+ partBodyLines.push(line);
219
+ }
220
+ }
221
+
222
+ // If this part matches our preferred content type, use it
223
+ if (partContentType === preferredType && partBodyLines.length > 0) {
224
+ let bodyContent = partBodyLines.join('\n');
225
+
226
+ if (isBase64) {
227
+ try {
228
+ bodyContent = atob(bodyContent.replace(/\s/g, ''));
229
+ } catch (e) {
230
+ console.warn('Failed to decode base64 part');
231
+ }
232
+ } else if (isQuotedPrintable) {
233
+ bodyContent = this.decodeQuotedPrintable(bodyContent);
234
+ }
235
+
236
+ return [bodyContent];
237
+ }
238
+ }
239
+
240
+ // Fallback: return first non-empty part
241
+ for (const part of parts) {
242
+ const lines = part.split(/\r?\n/);
243
+ const bodyLines = lines.filter(line => line.trim() !== '').slice(3); // Skip headers
244
+ if (bodyLines.length > 0) {
245
+ return bodyLines;
246
+ }
247
+ }
248
+
249
+ return [];
250
+ }
251
+
252
+ /**
253
+ * Decodes quoted-printable encoded content
254
+ * @param content - Quoted-printable encoded string
255
+ * @returns Decoded string
256
+ */
257
+ private decodeQuotedPrintable(content: string): string {
258
+ return content
259
+ .replace(/=\r?\n/g, '') // Remove soft line breaks
260
+ .replace(/=([0-9A-F]{2})/gi, (match, hex) => {
261
+ return String.fromCharCode(parseInt(hex, 16));
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Sanitizes HTML content for safe display
267
+ * @param html - Raw HTML content
268
+ * @returns Sanitized HTML
269
+ */
270
+ private sanitizeHtmlContent(html: string): string {
271
+ // Basic sanitization - remove script tags and dangerous attributes
272
+ let sanitized = html
273
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
274
+ .replace(/on\w+\s*=\s*["'][^"']*["']/gi, '')
275
+ .replace(/javascript:/gi, '');
276
+
277
+ return sanitized;
278
+ }
279
+
280
+ /**
281
+ * Parses attachment information from EML content
282
+ * @param emlContent - Raw EML content
283
+ * @returns Array of attachment information
284
+ */
285
+ private parseAttachments(emlContent: string): EmailAttachment[] {
286
+ const attachments: EmailAttachment[] = [];
287
+ const attachmentRegex = /Content-Disposition:\s*attachment[^;]*;\s*filename="?([^"\r\n]+)"?/gi;
288
+ let match;
289
+
290
+ while ((match = attachmentRegex.exec(emlContent)) !== null) {
291
+ attachments.push({
292
+ filename: match[1],
293
+ contentType: 'application/octet-stream',
294
+ size: 0
295
+ });
296
+ }
297
+
298
+ return attachments;
299
+ }
300
+
301
+ /**
302
+ * Cleans and formats email addresses
303
+ * @param email - Raw email address string
304
+ * @returns Cleaned email address
305
+ */
306
+ private cleanEmailAddress(email: string): string {
307
+ // Remove quotes and extra whitespace
308
+ let cleaned = email.replace(/['"]/g, '').trim();
309
+
310
+ // Extract email from format: "Name <email@example.com>"
311
+ const emailMatch = cleaned.match(/<([^>]+)>/);
312
+ if (emailMatch) {
313
+ const name = cleaned.substring(0, cleaned.indexOf('<')).trim();
314
+ const address = emailMatch[1];
315
+ return name ? `${name} <${address}>` : address;
316
+ }
317
+
318
+ return cleaned;
319
+ }
320
+
321
+ /**
322
+ * Decodes encoded email headers (RFC 2047)
323
+ * @param header - Encoded header string
324
+ * @returns Decoded header string
325
+ */
326
+ private decodeHeader(header: string): string {
327
+ // Decode RFC 2047 encoded words (=?charset?encoding?text?=)
328
+ return header.replace(/=\?([^?]+)\?([BQ])\?([^?]+)\?=/gi, (match, charset, encoding, text) => {
329
+ try {
330
+ if (encoding.toUpperCase() === 'B') {
331
+ // Base64 encoding
332
+ return atob(text);
333
+ } else if (encoding.toUpperCase() === 'Q') {
334
+ // Quoted-printable encoding
335
+ return this.decodeQuotedPrintable(text.replace(/_/g, ' '));
336
+ }
337
+ } catch (e) {
338
+ console.warn('Failed to decode header:', match);
339
+ }
340
+ return match;
341
+ });
342
+ }
343
+
344
+ /**
345
+ * Formats email date to readable format
346
+ * @param dateStr - Raw date string from email
347
+ * @returns Formatted date string
348
+ */
349
+ private formatDate(dateStr: string): string {
350
+ try {
351
+ const date = new Date(dateStr);
352
+ if (isNaN(date.getTime())) {
353
+ return dateStr || 'Unknown date';
354
+ }
355
+ return date.toLocaleString('en-GB', {
356
+ day: '2-digit',
357
+ month: 'short',
358
+ year: 'numeric',
359
+ hour: '2-digit',
360
+ minute: '2-digit'
361
+ });
362
+ } catch (e) {
363
+ return dateStr || 'Unknown date';
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Sanitizes HTML for Angular display
369
+ * @param html - HTML string
370
+ * @returns SafeHtml for Angular templates
371
+ */
372
+ getSafeHtml(html: string): SafeHtml {
373
+ return this.sanitizer.sanitize(1, html) || '';
374
+ }
375
+
376
+ /**
377
+ * Downloads and parses an EML file from a URL
378
+ * @param documentUrl - URL of the EML file
379
+ * @returns Observable of parsed email data
380
+ */
381
+ loadAndParseEmail(documentUrl: string): Observable<ParsedEmailData> {
382
+ console.log('EmlParserService: Loading email from URL:', documentUrl);
383
+
384
+ return this.downloadEmailFile(documentUrl).pipe(
385
+ map((emlContent: string) => {
386
+ console.log('EmlParserService: Email downloaded, size:', emlContent?.length || 0, 'bytes');
387
+
388
+ if (!emlContent || emlContent.trim().length === 0) {
389
+ throw new Error('Email file is empty or could not be loaded');
390
+ }
391
+
392
+ const parsedData = this.parseEmlData(emlContent);
393
+ if (!parsedData) {
394
+ throw new Error('Failed to parse email data');
395
+ }
396
+
397
+ console.log('EmlParserService: Email parsed successfully:', parsedData);
398
+ return parsedData;
399
+ }),
400
+ catchError((error) => {
401
+ console.error('EmlParserService: Error loading/parsing email:', error);
402
+ return throwError(() => new Error(error.message || 'Failed to load or parse email file'));
403
+ })
404
+ );
405
+ }
406
+
407
+ /**
408
+ * Downloads an email file with appropriate authentication handling
409
+ * @param documentUrl - URL of the email file
410
+ * @returns Observable of file content as text
411
+ */
412
+ private downloadEmailFile(documentUrl: string): Observable<string> {
413
+ // Check if it's Azure Blob Storage URL
414
+ const isAzureBlobStorage = documentUrl.includes('.blob.core.windows.net');
415
+
416
+ if (isAzureBlobStorage) {
417
+ // For Azure Blob Storage, don't send Authorization header
418
+ // The URL should contain a SAS token or the blob should be publicly accessible
419
+ return this.http.get(documentUrl, {
420
+ responseType: 'text'
421
+ }).pipe(
422
+ catchError((error) => {
423
+ console.error('Error downloading email from blob storage:', error);
424
+ return throwError(() => new Error('Failed to download email file. Please check if the file URL is valid and accessible.'));
425
+ })
426
+ );
427
+ } else {
428
+ // For other storage types, use Bearer token authentication
429
+ const Authorization = `Bearer d8764575-d713-4e31-aeb0-29ea31057461`;
430
+ return this.http.get(documentUrl, {
431
+ headers: new HttpHeaders({
432
+ 'Authorization': Authorization
433
+ }),
434
+ responseType: 'text'
435
+ }).pipe(
436
+ catchError((error) => {
437
+ console.error('Error downloading email file:', error);
438
+ return throwError(() => new Error('Failed to download email file. Please check authentication.'));
439
+ })
440
+ );
441
+ }
442
+ }
443
+ }
444
+
@@ -0,0 +1,66 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { ExcelParserService } from './excel-parser.service';
3
+
4
+ describe('ExcelParserService', () => {
5
+ let service: ExcelParserService;
6
+
7
+ beforeEach(() => {
8
+ TestBed.configureTestingModule({});
9
+ service = TestBed.inject(ExcelParserService);
10
+ });
11
+
12
+ it('should be created', () => {
13
+ expect(service).toBeTruthy();
14
+ });
15
+
16
+ it('should convert RGB to hex correctly', () => {
17
+ // Test 6-character format
18
+ expect(service['rgbToHex']('ED7D31')).toBe('#ed7d31');
19
+
20
+ // Test 8-character format
21
+ expect(service['rgbToHex']('FFFF0000')).toBe('#ff0000');
22
+
23
+ // Test invalid input
24
+ expect(service['rgbToHex']('')).toBe('');
25
+ expect(service['rgbToHex']('12345')).toBe('');
26
+ });
27
+
28
+ it('should convert theme colors correctly', () => {
29
+ expect(service['themeToHex'](0)).toBe('#000000'); // Black
30
+ expect(service['themeToHex'](1)).toBe('#FFFFFF'); // White
31
+ expect(service['themeToHex'](2)).toBe('#FF0000'); // Red
32
+ });
33
+
34
+ it('should convert indexed colors correctly', () => {
35
+ expect(service['indexedToHex'](0)).toBe('#000000'); // Black
36
+ expect(service['indexedToHex'](1)).toBe('#FFFFFF'); // White
37
+ expect(service['indexedToHex'](64)).toBe('#ED7D31'); // Orange
38
+ });
39
+
40
+ it('should ensure data consistency', () => {
41
+ const testData = [
42
+ { cells: [{ value: 'A1' }, { value: 'B1' }] },
43
+ { cells: [{ value: 'A2' }] }
44
+ ];
45
+
46
+ const result = service.ensureDataConsistency(testData);
47
+
48
+ expect(result.length).toBe(2);
49
+ expect(result[0].cells.length).toBe(3); // Padded to min 3 columns
50
+ expect(result[1].cells.length).toBe(3); // Padded to min 3 columns
51
+ });
52
+
53
+ it('should convert to legacy format', () => {
54
+ const testData = [
55
+ { cells: [{ value: 'A1' }, { value: 'B1' }] },
56
+ { cells: [{ value: 'A2' }, { value: 'B2' }] }
57
+ ];
58
+
59
+ const result = service.convertToLegacyFormat(testData);
60
+
61
+ expect(result).toEqual([
62
+ ['A1', 'B1'],
63
+ ['A2', 'B2']
64
+ ]);
65
+ });
66
+ });