@platform-modules/civil-aviation-authority 2.3.307 → 2.3.309

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 &&
@@ -37,6 +37,7 @@ export declare class User extends BaseModel {
37
37
  fax_number?: string;
38
38
  diplomatic_name?: string;
39
39
  avatar?: string;
40
+ is_selected_lang: "English" | "Arabic";
40
41
  father_name?: string;
41
42
  spouse_name?: string;
42
43
  children1_name?: string;
@@ -210,6 +210,10 @@ __decorate([
210
210
  (0, typeorm_1.Column)({ nullable: true }),
211
211
  __metadata("design:type", String)
212
212
  ], User.prototype, "avatar", void 0);
213
+ __decorate([
214
+ (0, typeorm_1.Column)({ type: "enum", enum: ["English", "Arabic"], nullable: false, default: "English" }),
215
+ __metadata("design:type", String)
216
+ ], User.prototype, "is_selected_lang", void 0);
213
217
  __decorate([
214
218
  (0, typeorm_1.Column)({ nullable: true }),
215
219
  __metadata("design:type", String)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.307",
3
+ "version": "2.3.309",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -0,0 +1,11 @@
1
+ -- Add is_selected_lang column to users table (CAA).
2
+ -- Run once per environment before deploying updated services.
3
+
4
+ DO $$ BEGIN
5
+ CREATE TYPE is_selected_lang_enum AS ENUM ('English', 'Arabic');
6
+ EXCEPTION
7
+ WHEN duplicate_object THEN NULL;
8
+ END $$;
9
+
10
+ ALTER TABLE IF EXISTS users
11
+ ADD COLUMN IF NOT EXISTS is_selected_lang is_selected_lang_enum NOT NULL DEFAULT 'English';
@@ -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 (
@@ -115,6 +115,9 @@ export class User extends BaseModel {
115
115
  @Column({ nullable: true })
116
116
  avatar?: string;
117
117
 
118
+ @Column({ type: "enum", enum: ["English", "Arabic"], nullable: false, default: "English" })
119
+ is_selected_lang: "English" | "Arabic";
120
+
118
121
  @Column({ nullable: true })
119
122
  father_name?: string;
120
123