@platform-modules/civil-aviation-authority 2.3.280 → 2.3.281

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.
@@ -1,375 +1,5 @@
1
- import catalog from './workflow-chat-i18n.json';
2
-
3
- export type BilingualText = { en: string; ar: string };
4
-
5
- type WorkflowChatI18nCatalog = {
6
- workflowTemplates: Record<string, BilingualText>;
7
- chatTemplates: Record<string, BilingualText>;
8
- workflowStatusLabels: Record<string, string>;
9
- chatStatusLabels: Record<string, string>;
10
- };
11
-
12
- const WORKFLOW_TEMPLATES = catalog.workflowTemplates as Record<string, BilingualText>;
13
- const CHAT_TEMPLATES = catalog.chatTemplates as Record<string, BilingualText>;
14
- const WORKFLOW_STATUS_LABELS = catalog.workflowStatusLabels as Record<string, string>;
15
- const CHAT_STATUS_LABELS = catalog.chatStatusLabels as Record<string, string>;
16
-
17
- /** Read-only view of the shared workflow/chat translation catalog. */
18
- export function getWorkflowChatI18nCatalog(): WorkflowChatI18nCatalog {
19
- return catalog as WorkflowChatI18nCatalog;
20
- }
21
-
22
- function interpolate(template: string, params: Record<string, string> = {}): string {
23
- return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => params[key] ?? '');
24
- }
25
-
26
- export function buildBilingualFromTemplate(
27
- templateCatalog: Record<string, BilingualText>,
28
- key: string,
29
- params: Record<string, string> = {},
30
- arParams?: Record<string, string>,
31
- ): BilingualText {
32
- const tpl = templateCatalog[key];
33
- if (!tpl) {
34
- return { en: key, ar: key };
35
- }
36
- const arabicParams = arParams ?? params;
37
- return {
38
- en: interpolate(tpl.en, params),
39
- ar: interpolate(tpl.ar, arabicParams),
40
- };
41
- }
42
-
43
- export function workflowTemplate(
44
- key: string,
45
- params: Record<string, string> = {},
46
- arParams?: Record<string, string>,
47
- ): BilingualText {
48
- return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params, arParams);
49
- }
50
-
51
- export function chatTemplate(
52
- key: string,
53
- params: Record<string, string> = {},
54
- arParams?: Record<string, string>,
55
- ): BilingualText {
56
- return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params, arParams);
57
- }
58
-
59
- export function resolveWorkflowStatusAr(status: string | null | undefined): string | null {
60
- if (status == null || status === '') return null;
61
- return WORKFLOW_STATUS_LABELS[status] ?? status;
62
- }
63
-
64
- export function resolveChatStatusAr(status: string | null | undefined): string | null {
65
- if (status == null || status === '') return null;
66
- return CHAT_STATUS_LABELS[status] ?? status;
67
- }
68
-
69
- /** Arabic column value only — never falls back to English (for content_ar / message_ar). */
70
- export function pickArabicName(arabicName?: string | null): string {
71
- return arabicName?.trim() || '';
72
- }
73
-
74
- export function pickLocalizedName(
75
- englishName?: string | null,
76
- arabicName?: string | null,
77
- fallback = ''
78
- ): string {
79
- const ar = arabicName?.trim();
80
- if (ar) return ar;
81
- const en = englishName?.trim();
82
- if (en) return en;
83
- return fallback;
84
- }
85
-
86
- function joinDeptSectionLabels(
87
- dept?: string | null,
88
- section?: string | null,
89
- ): string {
90
- return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
91
- }
92
-
93
- /** EN from English columns; AR from Arabic columns only (no English in ar). */
94
- export function joinDeptSectionBilingualLabels(
95
- deptEn?: string | null,
96
- deptAr?: string | null,
97
- sectEn?: string | null,
98
- sectAr?: string | null,
99
- ): { en: string; ar: string } {
100
- return {
101
- en: joinDeptSectionLabels(deptEn, sectEn),
102
- ar: joinDeptSectionLabels(deptAr, sectAr),
103
- };
104
- }
105
-
106
- /** @deprecated Use joinDeptSectionBilingualLabels */
107
- export function joinDeptSectionArabicLabels(
108
- deptArabic?: string | null,
109
- sectionArabic?: string | null,
110
- ): string {
111
- return joinDeptSectionLabels(deptArabic, sectionArabic);
112
- }
113
-
114
- function formatWorkflowCommentSuffix(comment?: string | null): string {
115
- const text = comment?.trim();
116
- return text ? `: ${text}` : '';
117
- }
118
-
119
- function formatChatCommentSuffix(comment?: string | null): string {
120
- const text = comment?.trim();
121
- return text ? ` - ${text}` : '';
122
- }
123
-
124
- type WorkflowByNameKey =
125
- | 'request_approved_by'
126
- | 'request_rejected_by'
127
- | 'request_returned_for_modification_by';
128
-
129
- type WorkflowWithoutNameKey =
130
- | 'request_approved'
131
- | 'request_rejected'
132
- | 'request_returned_for_modification';
133
-
134
- function buildWorkflowApprovalByName(
135
- templateKey: WorkflowByNameKey,
136
- fallbackKey: WorkflowWithoutNameKey,
137
- nameEn: string,
138
- nameAr: string,
139
- comment?: string | null,
140
- ): BilingualText {
141
- const commentSuffix = formatWorkflowCommentSuffix(comment);
142
- return {
143
- en: workflowTemplate(templateKey, { name: nameEn, comment: commentSuffix }).en,
144
- ar: nameAr
145
- ? workflowTemplate(templateKey, { name: nameAr, comment: '' }).ar
146
- : workflowTemplate(fallbackKey, { comment: '' }).ar,
147
- };
148
- }
149
-
150
- function buildWorkflowApprovalWithoutName(
151
- templateKey: WorkflowWithoutNameKey,
152
- comment?: string | null,
153
- ): BilingualText {
154
- const commentSuffix = formatWorkflowCommentSuffix(comment);
155
- return {
156
- en: workflowTemplate(templateKey, { comment: commentSuffix }).en,
157
- ar: workflowTemplate(templateKey, { comment: '' }).ar,
158
- };
159
- }
160
-
161
- function buildWorkflowFromNamePair(
162
- templateWithNameKey: 'request_approval_in_progress_from' | 'request_approval_pending_from',
163
- templateWithoutNameKey: 'request_approval_in_progress' | 'request_approval_pending',
164
- nameEn: string,
165
- nameAr: string,
166
- ): BilingualText {
167
- return {
168
- en: workflowTemplate(templateWithNameKey, { name: nameEn }).en,
169
- ar: nameAr
170
- ? workflowTemplate(templateWithNameKey, { name: nameAr }).ar
171
- : workflowTemplate(templateWithoutNameKey).ar,
172
- };
173
- }
174
-
175
- export function buildApprovalWorkflowContent(params: {
176
- status: 'Approved' | 'Rejected' | 'Returned for Modification' | 'Pending' | string;
177
- roleName?: string | null;
178
- roleArabicName?: string | null;
179
- comment?: string | null;
180
- }): BilingualText {
181
- const nameEn = params.roleName?.trim() || '';
182
- const nameAr = pickArabicName(params.roleArabicName);
183
-
184
- if (params.status === 'Approved') {
185
- return nameEn
186
- ? buildWorkflowApprovalByName('request_approved_by', 'request_approved', nameEn, nameAr, params.comment)
187
- : buildWorkflowApprovalWithoutName('request_approved', params.comment);
188
- }
189
-
190
- if (params.status === 'Rejected') {
191
- return nameEn
192
- ? buildWorkflowApprovalByName('request_rejected_by', 'request_rejected', nameEn, nameAr, params.comment)
193
- : buildWorkflowApprovalWithoutName('request_rejected', params.comment);
194
- }
195
-
196
- if (params.status === 'Returned for Modification') {
197
- return nameEn
198
- ? buildWorkflowApprovalByName(
199
- 'request_returned_for_modification_by',
200
- 'request_returned_for_modification',
201
- nameEn,
202
- nameAr,
203
- params.comment,
204
- )
205
- : buildWorkflowApprovalWithoutName('request_returned_for_modification', params.comment);
206
- }
207
-
208
- const commentSuffix = formatWorkflowCommentSuffix(params.comment);
209
- if (nameEn) {
210
- return {
211
- en: workflowTemplate('request_approval_pending_from', { name: nameEn, comment: commentSuffix }).en,
212
- ar: nameAr
213
- ? workflowTemplate('request_approval_pending_from', { name: nameAr, comment: '' }).ar
214
- : workflowTemplate('request_approval_pending', { comment: '' }).ar,
215
- };
216
- }
217
-
218
- return {
219
- en: workflowTemplate('request_approval_pending', { comment: commentSuffix }).en,
220
- ar: workflowTemplate('request_approval_pending', { comment: '' }).ar,
221
- };
222
- }
223
-
224
- export function buildHumanApprovalWorkflowContent(params: {
225
- isFirst: boolean;
226
- roleName?: string | null;
227
- roleArabicName?: string | null;
228
- deptName?: string | null;
229
- deptArabicName?: string | null;
230
- sectionName?: string | null;
231
- sectionArabicName?: string | null;
232
- }): BilingualText {
233
- const withNameKey = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
234
- const withoutNameKey = params.isFirst ? 'request_approval_in_progress' : 'request_approval_pending';
235
-
236
- if (params.roleName?.trim()) {
237
- const nameEn = params.roleName.trim();
238
- const nameAr = pickArabicName(params.roleArabicName);
239
- return buildWorkflowFromNamePair(withNameKey, withoutNameKey, nameEn, nameAr);
240
- }
241
-
242
- const { en, ar } = joinDeptSectionBilingualLabels(
243
- params.deptName,
244
- params.deptArabicName,
245
- params.sectionName,
246
- params.sectionArabicName,
247
- );
248
- if (en) {
249
- return buildWorkflowFromNamePair(withNameKey, withoutNameKey, en, ar);
250
- }
251
-
252
- return params.isFirst
253
- ? workflowTemplate('request_approval_in_progress')
254
- : workflowTemplate('request_approval_pending');
255
- }
256
-
257
- export function buildApprovalChatMessage(params: {
258
- status: 'Approved' | 'Rejected' | 'Returned for Modification' | string;
259
- roleName?: string | null;
260
- roleArabicName?: string | null;
261
- comment?: string | null;
262
- }): BilingualText {
263
- const roleEn = params.roleName?.trim() || 'Unknown Role';
264
- const roleAr = pickArabicName(params.roleArabicName);
265
- const commentSuffix = formatChatCommentSuffix(params.comment);
266
-
267
- let byRoleKey: string;
268
- let withoutRoleKey: string;
269
- if (params.status === 'Approved') {
270
- byRoleKey = 'request_approved_by_role';
271
- withoutRoleKey = 'request_approved';
272
- } else if (params.status === 'Rejected') {
273
- byRoleKey = 'request_rejected_by_role';
274
- withoutRoleKey = 'request_rejected';
275
- } else if (params.status === 'Returned for Modification') {
276
- byRoleKey = 'request_returned_for_modification_by_role';
277
- withoutRoleKey = 'request_returned_for_modification';
278
- } else {
279
- byRoleKey = 'request_approved_by_role';
280
- withoutRoleKey = 'request_approved';
281
- }
282
-
283
- return {
284
- en: chatTemplate(byRoleKey, { roleName: roleEn, comment: commentSuffix }).en,
285
- ar: roleAr
286
- ? chatTemplate(byRoleKey, { roleName: roleAr, comment: '' }).ar
287
- : chatTemplate(withoutRoleKey, { comment: '' }).ar,
288
- };
289
- }
290
-
291
- /** Resolve Arabic workflow content from a stored English content string (legacy rows). */
292
- export function resolveWorkflowContentAr(content: string | null | undefined): string | null {
293
- if (content == null || content.trim() === '') return null;
294
- const normalized = content.trim();
295
-
296
- for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
297
- if (tpl.en === normalized) return tpl.ar;
298
- }
299
-
300
- const commentIdx = normalized.indexOf(': ');
301
- if (commentIdx > 0) {
302
- const baseEn = normalized.slice(0, commentIdx).trim();
303
-
304
- for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
305
- if (!tpl.en.includes('{{comment}}')) continue;
306
- const withoutComment = tpl.en.replace('{{comment}}', '');
307
- if (withoutComment.includes('{{name}}')) {
308
- const staticPrefix = withoutComment.replace('{{name}}', '').trim();
309
- if (baseEn.startsWith(staticPrefix)) {
310
- const fallbackKey = Object.entries(WORKFLOW_TEMPLATES).find(([, t]) => t === tpl)?.[0] ?? '';
311
- if (fallbackKey.includes('_by')) {
312
- const genericKey = fallbackKey.replace('_by', '') as WorkflowWithoutNameKey;
313
- if (WORKFLOW_TEMPLATES[genericKey]) {
314
- return WORKFLOW_TEMPLATES[genericKey].ar;
315
- }
316
- }
317
- return tpl.ar.split('{{name}}')[0].trim();
318
- }
319
- } else if (withoutComment.trim() === baseEn) {
320
- return interpolate(tpl.ar, { comment: '' });
321
- }
322
- }
323
- }
324
-
325
- const inProgressPrefix = WORKFLOW_TEMPLATES.request_approval_in_progress_from?.en.split('{{name}}')[0].trim();
326
- if (inProgressPrefix && normalized.startsWith(inProgressPrefix)) {
327
- return WORKFLOW_TEMPLATES.request_approval_in_progress.ar;
328
- }
329
- const pendingPrefix = WORKFLOW_TEMPLATES.request_approval_pending_from?.en.split('{{name}}')[0].trim();
330
- if (pendingPrefix && normalized.startsWith(pendingPrefix)) {
331
- return WORKFLOW_TEMPLATES.request_approval_pending.ar;
332
- }
333
-
334
- return null;
335
- }
336
-
337
- /** Resolve Arabic chat message from a stored English message string (legacy rows). */
338
- export function resolveChatMessageAr(message: string | null | undefined): string | null {
339
- if (message == null || message.trim() === '') return null;
340
- const normalized = message.trim();
341
-
342
- for (const tpl of Object.values(CHAT_TEMPLATES)) {
343
- if (tpl.en === normalized) return tpl.ar;
344
- }
345
-
346
- return null;
347
- }
348
-
349
- export function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown> {
350
- const status = log.status != null ? String(log.status) : null;
351
- const content = log.content != null ? String(log.content) : null;
352
- const storedAr = log.content_ar != null ? String(log.content_ar).trim() : '';
353
- const hasEnglishInAr = storedAr && /[A-Za-z]/.test(storedAr);
354
- return {
355
- ...log,
356
- content_ar: hasEnglishInAr
357
- ? (resolveWorkflowContentAr(content) ?? storedAr)
358
- : (log.content_ar ?? resolveWorkflowContentAr(content)),
359
- status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
360
- };
361
- }
362
-
363
- export function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown> {
364
- const status = chat.status != null ? String(chat.status) : null;
365
- const message = chat.message != null ? String(chat.message) : null;
366
- const storedAr = chat.message_ar != null ? String(chat.message_ar).trim() : '';
367
- const hasEnglishInAr = storedAr && /[A-Za-z]/.test(storedAr);
368
- return {
369
- ...chat,
370
- message_ar: hasEnglishInAr
371
- ? (resolveChatMessageAr(message) ?? storedAr)
372
- : (chat.message_ar ?? resolveChatMessageAr(message)),
373
- status_ar: chat.status_ar ?? resolveChatStatusAr(status),
374
- };
375
- }
1
+ /**
2
+ * Re-exports the CAA standard bilingual message builder.
3
+ * Import from '@platform-modules/civil-aviation-authority' in all services.
4
+ */
5
+ export * from './workflow-chat-message.builder';