@platform-modules/civil-aviation-authority 2.3.306 → 2.3.308

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.
@@ -1044,7 +1044,9 @@ function matchTemplateMessage(templateEn, message) {
1044
1044
  }
1045
1045
  groupIndex += 1;
1046
1046
  captures.push({ key, groupIndex });
1047
- regexBody += '(.+?)';
1047
+ const rest = templateEn.slice(tokenMatch.index + tokenMatch[0].length);
1048
+ const isLastPlaceholder = !/\{\{\w+\}\}/.test(rest);
1049
+ regexBody += key === 'modifiers' || isLastPlaceholder ? '(.*)' : '(.+?)';
1048
1050
  cursor = tokenMatch.index + tokenMatch[0].length;
1049
1051
  }
1050
1052
  regexBody += escapeRegexLiteral(templateEn.slice(cursor));
@@ -1103,6 +1105,54 @@ function countTemplatePlaceholders(templateEn) {
1103
1105
  function listWorkflowTemplatesBySpecificity() {
1104
1106
  return Object.entries(WORKFLOW_TEMPLATES).sort(([, a], [, b]) => countTemplatePlaceholders(b.en) - countTemplatePlaceholders(a.en));
1105
1107
  }
1108
+ const IT_HELPDESK_REASSIGN_WORKFLOW_KEYS = [
1109
+ 'it_helpdesk_wf_reassigned_l3',
1110
+ 'it_helpdesk_wf_reassigned_technician',
1111
+ 'it_helpdesk_wf_reassigned_generic',
1112
+ ];
1113
+ const IT_HELPDESK_REASSIGN_CHAT_KEYS = [
1114
+ 'it_helpdesk_reassigned_to_l3',
1115
+ 'it_helpdesk_reassigned_to_role',
1116
+ 'it_helpdesk_reassigned_generic',
1117
+ ];
1118
+ /**
1119
+ * IT Helpdesk reassignment templates share placeholder-heavy shapes; the generic
1120
+ * workflow template must not win over "reassigned request to L3/Technician".
1121
+ */
1122
+ function tryResolveItHelpdeskReassignmentWorkflowAr(normalized, actors) {
1123
+ for (const key of IT_HELPDESK_REASSIGN_WORKFLOW_KEYS) {
1124
+ const tpl = WORKFLOW_TEMPLATES[key];
1125
+ if (!tpl)
1126
+ continue;
1127
+ const params = matchTemplateMessage(tpl.en, normalized);
1128
+ if (!params)
1129
+ continue;
1130
+ if (key === 'it_helpdesk_wf_reassigned_generic' && !/^\d+$/.test((params.level ?? '').trim())) {
1131
+ continue;
1132
+ }
1133
+ return interpolate(tpl.ar, buildArabicWorkflowTemplateParams(params, actors));
1134
+ }
1135
+ return null;
1136
+ }
1137
+ function tryResolveItHelpdeskReassignmentChatAr(normalized, actors) {
1138
+ for (const key of IT_HELPDESK_REASSIGN_CHAT_KEYS) {
1139
+ const tpl = CHAT_TEMPLATES[key];
1140
+ if (!tpl)
1141
+ continue;
1142
+ const params = matchTemplateMessage(tpl.en, normalized);
1143
+ if (!params)
1144
+ continue;
1145
+ if (key === 'it_helpdesk_reassigned_generic' && !/^\d+$/.test((params.level ?? '').trim())) {
1146
+ continue;
1147
+ }
1148
+ if (key === 'it_helpdesk_reassigned_to_role' &&
1149
+ (params.reassignedRoleName ?? '').trim().toLowerCase() === 'l3') {
1150
+ continue;
1151
+ }
1152
+ return interpolate(tpl.ar, buildArabicChatTemplateParams(params, actors));
1153
+ }
1154
+ return null;
1155
+ }
1106
1156
  /** Parse legacy IT Helpdesk Muscat EN workflow strings before generic template matching. */
1107
1157
  function tryResolveItHelpdeskMuscatLegacyWorkflowAr(content, actors) {
1108
1158
  const { base, comment } = splitWorkflowColonComment(content);
@@ -1298,6 +1348,9 @@ function resolveWorkflowContentAr(content, actors) {
1298
1348
  const itHelpdeskLegacyAr = tryResolveItHelpdeskMuscatLegacyWorkflowAr(normalized, actors);
1299
1349
  if (itHelpdeskLegacyAr)
1300
1350
  return itHelpdeskLegacyAr;
1351
+ const itHelpdeskReassignAr = tryResolveItHelpdeskReassignmentWorkflowAr(normalized, actors);
1352
+ if (itHelpdeskReassignAr)
1353
+ return itHelpdeskReassignAr;
1301
1354
  for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
1302
1355
  if (tpl.en === normalized || tpl.en.toLowerCase() === normalized.toLowerCase())
1303
1356
  return tpl.ar;
@@ -1308,6 +1361,10 @@ function resolveWorkflowContentAr(content, actors) {
1308
1361
  const params = matchTemplateMessage(tpl.en, normalized);
1309
1362
  if (!params)
1310
1363
  continue;
1364
+ if (tpl.en === WORKFLOW_TEMPLATES.it_helpdesk_wf_reassigned_generic?.en &&
1365
+ !/^\d+$/.test((params.level ?? '').trim())) {
1366
+ continue;
1367
+ }
1311
1368
  return interpolate(tpl.ar, buildArabicWorkflowTemplateParams(params, actors));
1312
1369
  }
1313
1370
  const commentIdx = normalized.indexOf(': ');
@@ -1399,12 +1456,23 @@ function resolveChatMessageAr(message, actors) {
1399
1456
  if (tpl.en === normalized)
1400
1457
  return tpl.ar;
1401
1458
  }
1459
+ const itHelpdeskReassignChatAr = tryResolveItHelpdeskReassignmentChatAr(normalized, actors);
1460
+ if (itHelpdeskReassignChatAr)
1461
+ return itHelpdeskReassignChatAr;
1402
1462
  for (const tpl of Object.values(CHAT_TEMPLATES)) {
1403
1463
  if (!/\{\{\w+\}\}/.test(tpl.en))
1404
1464
  continue;
1405
1465
  const params = matchTemplateMessage(tpl.en, normalized);
1406
1466
  if (!params)
1407
1467
  continue;
1468
+ if (tpl.en === CHAT_TEMPLATES.it_helpdesk_reassigned_generic?.en &&
1469
+ !/^\d+$/.test((params.level ?? '').trim())) {
1470
+ continue;
1471
+ }
1472
+ if (tpl.en === CHAT_TEMPLATES.it_helpdesk_reassigned_to_role?.en &&
1473
+ (params.reassignedRoleName ?? '').trim().toLowerCase() === 'l3') {
1474
+ continue;
1475
+ }
1408
1476
  return interpolate(tpl.ar, buildArabicChatTemplateParams(params, actors));
1409
1477
  }
1410
1478
  const receivedByPrefix = 'Request Received by ';
@@ -1484,6 +1552,13 @@ function enrichWorkflowLog(log) {
1484
1552
  existingAr.includes(actors.roleName.trim())) {
1485
1553
  content_ar = resolvedAr;
1486
1554
  }
1555
+ if (content &&
1556
+ existingAr &&
1557
+ resolvedAr &&
1558
+ existingAr !== resolvedAr &&
1559
+ /reassigned request to (L3|Technician)/i.test(content)) {
1560
+ content_ar = resolvedAr;
1561
+ }
1487
1562
  const itHelpdeskMixedEnglish = /routed to L3|assignment path|Workflow decision made|L3 Technical Support approved|L3 assignment not required|HOS notification not required|routing completed/i;
1488
1563
  if (content &&
1489
1564
  existingAr &&
@@ -40,6 +40,7 @@ export declare class FollowUpReportRequests extends BaseModel {
40
40
  subject_classification: FollowUpReportSubjectClassification;
41
41
  topic: string;
42
42
  concerned_department: FollowUpReportConcernedDepartment;
43
+ relevant_department: string | null;
43
44
  date_from: Date;
44
45
  date_to: Date;
45
46
  general_manager_comment: string | null;
@@ -101,6 +101,10 @@ __decorate([
101
101
  }),
102
102
  __metadata("design:type", String)
103
103
  ], FollowUpReportRequests.prototype, "concerned_department", void 0);
104
+ __decorate([
105
+ (0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
106
+ __metadata("design:type", Object)
107
+ ], FollowUpReportRequests.prototype, "relevant_department", void 0);
104
108
  __decorate([
105
109
  (0, typeorm_1.Column)({ type: 'date', nullable: false }),
106
110
  __metadata("design:type", Date)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.306",
3
+ "version": "2.3.308",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -143,3 +143,7 @@ ALTER TABLE followup_report_items
143
143
  -- 10. Departments: optional parent DG department reference (self FK to departments.id)
144
144
  ALTER TABLE departments
145
145
  ADD COLUMN IF NOT EXISTS "DG_department_id" INTEGER NULL;
146
+
147
+ -- 11. Follow-up report requests: optional relevant department
148
+ ALTER TABLE followup_report_requests
149
+ ADD COLUMN IF NOT EXISTS relevant_department VARCHAR(255) NULL;
@@ -1659,7 +1659,9 @@ function matchTemplateMessage(
1659
1659
 
1660
1660
  groupIndex += 1;
1661
1661
  captures.push({ key, groupIndex });
1662
- regexBody += '(.+?)';
1662
+ const rest = templateEn.slice(tokenMatch.index + tokenMatch[0].length);
1663
+ const isLastPlaceholder = !/\{\{\w+\}\}/.test(rest);
1664
+ regexBody += key === 'modifiers' || isLastPlaceholder ? '(.*)' : '(.+?)';
1663
1665
  cursor = tokenMatch.index + tokenMatch[0].length;
1664
1666
  }
1665
1667
  regexBody += escapeRegexLiteral(templateEn.slice(cursor));
@@ -1733,6 +1735,62 @@ function listWorkflowTemplatesBySpecificity(): Array<[string, BilingualText]> {
1733
1735
  );
1734
1736
  }
1735
1737
 
1738
+ const IT_HELPDESK_REASSIGN_WORKFLOW_KEYS = [
1739
+ 'it_helpdesk_wf_reassigned_l3',
1740
+ 'it_helpdesk_wf_reassigned_technician',
1741
+ 'it_helpdesk_wf_reassigned_generic',
1742
+ ] as const;
1743
+
1744
+ const IT_HELPDESK_REASSIGN_CHAT_KEYS = [
1745
+ 'it_helpdesk_reassigned_to_l3',
1746
+ 'it_helpdesk_reassigned_to_role',
1747
+ 'it_helpdesk_reassigned_generic',
1748
+ ] as const;
1749
+
1750
+ /**
1751
+ * IT Helpdesk reassignment templates share placeholder-heavy shapes; the generic
1752
+ * workflow template must not win over "reassigned request to L3/Technician".
1753
+ */
1754
+ function tryResolveItHelpdeskReassignmentWorkflowAr(
1755
+ normalized: string,
1756
+ actors?: BilingualActorNames,
1757
+ ): string | null {
1758
+ for (const key of IT_HELPDESK_REASSIGN_WORKFLOW_KEYS) {
1759
+ const tpl = WORKFLOW_TEMPLATES[key];
1760
+ if (!tpl) continue;
1761
+ const params = matchTemplateMessage(tpl.en, normalized);
1762
+ if (!params) continue;
1763
+ if (key === 'it_helpdesk_wf_reassigned_generic' && !/^\d+$/.test((params.level ?? '').trim())) {
1764
+ continue;
1765
+ }
1766
+ return interpolate(tpl.ar, buildArabicWorkflowTemplateParams(params, actors));
1767
+ }
1768
+ return null;
1769
+ }
1770
+
1771
+ function tryResolveItHelpdeskReassignmentChatAr(
1772
+ normalized: string,
1773
+ actors?: BilingualActorNames,
1774
+ ): string | null {
1775
+ for (const key of IT_HELPDESK_REASSIGN_CHAT_KEYS) {
1776
+ const tpl = CHAT_TEMPLATES[key];
1777
+ if (!tpl) continue;
1778
+ const params = matchTemplateMessage(tpl.en, normalized);
1779
+ if (!params) continue;
1780
+ if (key === 'it_helpdesk_reassigned_generic' && !/^\d+$/.test((params.level ?? '').trim())) {
1781
+ continue;
1782
+ }
1783
+ if (
1784
+ key === 'it_helpdesk_reassigned_to_role' &&
1785
+ (params.reassignedRoleName ?? '').trim().toLowerCase() === 'l3'
1786
+ ) {
1787
+ continue;
1788
+ }
1789
+ return interpolate(tpl.ar, buildArabicChatTemplateParams(params, actors));
1790
+ }
1791
+ return null;
1792
+ }
1793
+
1736
1794
  /** Parse legacy IT Helpdesk Muscat EN workflow strings before generic template matching. */
1737
1795
  function tryResolveItHelpdeskMuscatLegacyWorkflowAr(
1738
1796
  content: string,
@@ -1957,6 +2015,9 @@ export function resolveWorkflowContentAr(
1957
2015
  const itHelpdeskLegacyAr = tryResolveItHelpdeskMuscatLegacyWorkflowAr(normalized, actors);
1958
2016
  if (itHelpdeskLegacyAr) return itHelpdeskLegacyAr;
1959
2017
 
2018
+ const itHelpdeskReassignAr = tryResolveItHelpdeskReassignmentWorkflowAr(normalized, actors);
2019
+ if (itHelpdeskReassignAr) return itHelpdeskReassignAr;
2020
+
1960
2021
  for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
1961
2022
  if (tpl.en === normalized || tpl.en.toLowerCase() === normalized.toLowerCase()) return tpl.ar;
1962
2023
  }
@@ -1965,6 +2026,12 @@ export function resolveWorkflowContentAr(
1965
2026
  if (!/\{\{\w+\}\}/.test(tpl.en)) continue;
1966
2027
  const params = matchTemplateMessage(tpl.en, normalized);
1967
2028
  if (!params) continue;
2029
+ if (
2030
+ tpl.en === WORKFLOW_TEMPLATES.it_helpdesk_wf_reassigned_generic?.en &&
2031
+ !/^\d+$/.test((params.level ?? '').trim())
2032
+ ) {
2033
+ continue;
2034
+ }
1968
2035
  return interpolate(tpl.ar, buildArabicWorkflowTemplateParams(params, actors));
1969
2036
  }
1970
2037
 
@@ -2067,10 +2134,25 @@ export function resolveChatMessageAr(
2067
2134
  if (tpl.en === normalized) return tpl.ar;
2068
2135
  }
2069
2136
 
2137
+ const itHelpdeskReassignChatAr = tryResolveItHelpdeskReassignmentChatAr(normalized, actors);
2138
+ if (itHelpdeskReassignChatAr) return itHelpdeskReassignChatAr;
2139
+
2070
2140
  for (const tpl of Object.values(CHAT_TEMPLATES)) {
2071
2141
  if (!/\{\{\w+\}\}/.test(tpl.en)) continue;
2072
2142
  const params = matchTemplateMessage(tpl.en, normalized);
2073
2143
  if (!params) continue;
2144
+ if (
2145
+ tpl.en === CHAT_TEMPLATES.it_helpdesk_reassigned_generic?.en &&
2146
+ !/^\d+$/.test((params.level ?? '').trim())
2147
+ ) {
2148
+ continue;
2149
+ }
2150
+ if (
2151
+ tpl.en === CHAT_TEMPLATES.it_helpdesk_reassigned_to_role?.en &&
2152
+ (params.reassignedRoleName ?? '').trim().toLowerCase() === 'l3'
2153
+ ) {
2154
+ continue;
2155
+ }
2074
2156
  return interpolate(tpl.ar, buildArabicChatTemplateParams(params, actors));
2075
2157
  }
2076
2158
 
@@ -2172,6 +2254,15 @@ export function enrichWorkflowLog(log: Record<string, unknown>): Record<string,
2172
2254
  ) {
2173
2255
  content_ar = resolvedAr;
2174
2256
  }
2257
+ if (
2258
+ content &&
2259
+ existingAr &&
2260
+ resolvedAr &&
2261
+ existingAr !== resolvedAr &&
2262
+ /reassigned request to (L3|Technician)/i.test(content)
2263
+ ) {
2264
+ content_ar = resolvedAr;
2265
+ }
2175
2266
  const itHelpdeskMixedEnglish =
2176
2267
  /routed to L3|assignment path|Workflow decision made|L3 Technical Support approved|L3 assignment not required|HOS notification not required|routing completed/i;
2177
2268
  if (
@@ -81,6 +81,9 @@ export class FollowUpReportRequests extends BaseModel {
81
81
  })
82
82
  concerned_department: FollowUpReportConcernedDepartment;
83
83
 
84
+ @Column({ type: 'varchar', length: 255, nullable: true })
85
+ relevant_department: string | null;
86
+
84
87
  @Column({ type: 'date', nullable: false })
85
88
  date_from: Date;
86
89