@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.
- package/dist/i18n/workflow-chat-i18n.d.ts +5 -55
- package/dist/i18n/workflow-chat-i18n.js +18 -275
- package/dist/i18n/workflow-chat-message.builder.d.ts +103 -0
- package/dist/i18n/workflow-chat-message.builder.js +350 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/i18n/workflow-chat-i18n.ts +5 -375
- package/src/i18n/workflow-chat-message.builder.ts +502 -0
- package/src/index.ts +1 -1
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CAA standard bilingual workflow/chat message builder.
|
|
3
|
+
*
|
|
4
|
+
* All services MUST use these helpers (never hardcode message strings).
|
|
5
|
+
* Templates live in workflow-chat-i18n.json; dynamic values use parallel EN/AR params.
|
|
6
|
+
*/
|
|
7
|
+
import catalog from './workflow-chat-i18n.json';
|
|
8
|
+
|
|
9
|
+
export type BilingualText = { en: string; ar: string };
|
|
10
|
+
|
|
11
|
+
export type WorkflowChatI18nCatalog = {
|
|
12
|
+
workflowTemplates: Record<string, BilingualText>;
|
|
13
|
+
chatTemplates: Record<string, BilingualText>;
|
|
14
|
+
workflowStatusLabels: Record<string, string>;
|
|
15
|
+
chatStatusLabels: Record<string, string>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Master-data fields with English + Arabic columns (roles, departments, sections, users). */
|
|
19
|
+
export type BilingualActorNames = {
|
|
20
|
+
roleName?: string | null;
|
|
21
|
+
roleArabicName?: string | null;
|
|
22
|
+
deptName?: string | null;
|
|
23
|
+
deptArabicName?: string | null;
|
|
24
|
+
sectionName?: string | null;
|
|
25
|
+
sectionArabicName?: string | null;
|
|
26
|
+
userName?: string | null;
|
|
27
|
+
userArabicName?: string | null;
|
|
28
|
+
employeeId?: string | number | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type WorkflowMessageAction =
|
|
32
|
+
| 'request_submitted'
|
|
33
|
+
| 'notification_sent_pending'
|
|
34
|
+
| 'notification_sent_successfully'
|
|
35
|
+
| 'approval_in_progress'
|
|
36
|
+
| 'approval_pending'
|
|
37
|
+
| 'approved'
|
|
38
|
+
| 'rejected'
|
|
39
|
+
| 'returned_for_modification'
|
|
40
|
+
| 'approval_pending_update';
|
|
41
|
+
|
|
42
|
+
export type ChatMessageAction =
|
|
43
|
+
| 'cancellation_submitted'
|
|
44
|
+
| 'request_received'
|
|
45
|
+
| 'approved'
|
|
46
|
+
| 'rejected'
|
|
47
|
+
| 'returned_for_modification';
|
|
48
|
+
|
|
49
|
+
export type WorkflowMessageContext = BilingualActorNames & {
|
|
50
|
+
action: WorkflowMessageAction;
|
|
51
|
+
/** First human approval step → "in progress"; later steps → "pending". */
|
|
52
|
+
isFirstApprovalStep?: boolean;
|
|
53
|
+
comment?: string | null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type ChatMessageContext = BilingualActorNames & {
|
|
57
|
+
action: ChatMessageAction;
|
|
58
|
+
comment?: string | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const WORKFLOW_TEMPLATES = catalog.workflowTemplates as Record<string, BilingualText>;
|
|
62
|
+
const CHAT_TEMPLATES = catalog.chatTemplates as Record<string, BilingualText>;
|
|
63
|
+
const WORKFLOW_STATUS_LABELS = catalog.workflowStatusLabels as Record<string, string>;
|
|
64
|
+
const CHAT_STATUS_LABELS = catalog.chatStatusLabels as Record<string, string>;
|
|
65
|
+
|
|
66
|
+
export function getWorkflowChatI18nCatalog(): WorkflowChatI18nCatalog {
|
|
67
|
+
return catalog as WorkflowChatI18nCatalog;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function interpolate(template: string, params: Record<string, string> = {}): string {
|
|
71
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => params[key] ?? '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Arabic DB column preferred; English fallback so AR keeps identical structure to EN. */
|
|
75
|
+
export function resolveBilingualName(
|
|
76
|
+
englishName?: string | null,
|
|
77
|
+
arabicName?: string | null,
|
|
78
|
+
): string {
|
|
79
|
+
const ar = arabicName?.trim();
|
|
80
|
+
if (ar) return ar;
|
|
81
|
+
return englishName?.trim() || '';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function pickLocalizedName(
|
|
85
|
+
englishName?: string | null,
|
|
86
|
+
arabicName?: string | null,
|
|
87
|
+
fallback = '',
|
|
88
|
+
): string {
|
|
89
|
+
return resolveBilingualName(englishName, arabicName) || fallback;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @deprecated Use resolveBilingualName */
|
|
93
|
+
export function pickArabicName(arabicName?: string | null): string {
|
|
94
|
+
return arabicName?.trim() || '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function joinLabels(...parts: Array<string | null | undefined>): string {
|
|
98
|
+
return parts.map((p) => p?.trim()).filter(Boolean).join(' - ');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Build parallel EN / AR actor label from role OR dept+section. */
|
|
102
|
+
export function resolveActorDisplayNames(actors: BilingualActorNames): { en: string; ar: string } {
|
|
103
|
+
if (actors.roleName?.trim()) {
|
|
104
|
+
const en = actors.roleName.trim();
|
|
105
|
+
return { en, ar: resolveBilingualName(en, actors.roleArabicName) };
|
|
106
|
+
}
|
|
107
|
+
const en = joinLabels(actors.deptName, actors.sectionName);
|
|
108
|
+
const ar = joinLabels(
|
|
109
|
+
resolveBilingualName(actors.deptName, actors.deptArabicName),
|
|
110
|
+
resolveBilingualName(actors.sectionName, actors.sectionArabicName),
|
|
111
|
+
);
|
|
112
|
+
return { en, ar };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @deprecated Use resolveActorDisplayNames */
|
|
116
|
+
export function joinDeptSectionBilingualLabels(
|
|
117
|
+
deptEn?: string | null,
|
|
118
|
+
deptAr?: string | null,
|
|
119
|
+
sectEn?: string | null,
|
|
120
|
+
sectAr?: string | null,
|
|
121
|
+
): { en: string; ar: string } {
|
|
122
|
+
return resolveActorDisplayNames({
|
|
123
|
+
deptName: deptEn,
|
|
124
|
+
deptArabicName: deptAr,
|
|
125
|
+
sectionName: sectEn,
|
|
126
|
+
sectionArabicName: sectAr,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @deprecated Use resolveActorDisplayNames */
|
|
131
|
+
export function joinDeptSectionArabicLabels(
|
|
132
|
+
deptArabic?: string | null,
|
|
133
|
+
sectionArabic?: string | null,
|
|
134
|
+
): string {
|
|
135
|
+
return joinLabels(deptArabic, sectionArabic);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function formatWorkflowCommentSuffix(comment?: string | null): string {
|
|
139
|
+
const text = comment?.trim();
|
|
140
|
+
return text ? `: ${text}` : '';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function formatChatCommentSuffix(comment?: string | null): string {
|
|
144
|
+
const text = comment?.trim();
|
|
145
|
+
return text ? ` - ${text}` : '';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Core renderer — always pass explicit EN and AR param objects with the same keys. */
|
|
149
|
+
export function renderSynchronizedWorkflowMessage(
|
|
150
|
+
templateKey: string,
|
|
151
|
+
enParams: Record<string, string>,
|
|
152
|
+
arParams: Record<string, string>,
|
|
153
|
+
): BilingualText {
|
|
154
|
+
const tpl = WORKFLOW_TEMPLATES[templateKey];
|
|
155
|
+
if (!tpl) return { en: templateKey, ar: templateKey };
|
|
156
|
+
return {
|
|
157
|
+
en: interpolate(tpl.en, enParams),
|
|
158
|
+
ar: interpolate(tpl.ar, arParams),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function renderSynchronizedChatMessage(
|
|
163
|
+
templateKey: string,
|
|
164
|
+
enParams: Record<string, string>,
|
|
165
|
+
arParams: Record<string, string>,
|
|
166
|
+
): BilingualText {
|
|
167
|
+
const tpl = CHAT_TEMPLATES[templateKey];
|
|
168
|
+
if (!tpl) return { en: templateKey, ar: templateKey };
|
|
169
|
+
return {
|
|
170
|
+
en: interpolate(tpl.en, enParams),
|
|
171
|
+
ar: interpolate(tpl.ar, arParams),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function buildBilingualFromTemplate(
|
|
176
|
+
templateCatalog: Record<string, BilingualText>,
|
|
177
|
+
key: string,
|
|
178
|
+
params: Record<string, string> = {},
|
|
179
|
+
arParams?: Record<string, string>,
|
|
180
|
+
): BilingualText {
|
|
181
|
+
const tpl = templateCatalog[key];
|
|
182
|
+
if (!tpl) return { en: key, ar: key };
|
|
183
|
+
const arabicParams = arParams ?? params;
|
|
184
|
+
return {
|
|
185
|
+
en: interpolate(tpl.en, params),
|
|
186
|
+
ar: interpolate(tpl.ar, arabicParams),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function workflowTemplate(
|
|
191
|
+
key: string,
|
|
192
|
+
params: Record<string, string> = {},
|
|
193
|
+
arParams?: Record<string, string>,
|
|
194
|
+
): BilingualText {
|
|
195
|
+
return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params, arParams);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function chatTemplate(
|
|
199
|
+
key: string,
|
|
200
|
+
params: Record<string, string> = {},
|
|
201
|
+
arParams?: Record<string, string>,
|
|
202
|
+
): BilingualText {
|
|
203
|
+
return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params, arParams);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Standard entry point for workflow `content` + `content_ar`.
|
|
208
|
+
* Use for every workflow log insert/update across all CAA modules.
|
|
209
|
+
*/
|
|
210
|
+
export function buildSynchronizedWorkflowMessage(ctx: WorkflowMessageContext): BilingualText {
|
|
211
|
+
const commentWf = formatWorkflowCommentSuffix(ctx.comment);
|
|
212
|
+
const { en: actorEn, ar: actorAr } = resolveActorDisplayNames(ctx);
|
|
213
|
+
|
|
214
|
+
switch (ctx.action) {
|
|
215
|
+
case 'request_submitted':
|
|
216
|
+
return workflowTemplate('request_submitted');
|
|
217
|
+
case 'notification_sent_pending':
|
|
218
|
+
return workflowTemplate('notification_sent_pending');
|
|
219
|
+
case 'notification_sent_successfully':
|
|
220
|
+
return workflowTemplate('notification_sent_successfully');
|
|
221
|
+
case 'approval_in_progress':
|
|
222
|
+
return actorEn
|
|
223
|
+
? renderSynchronizedWorkflowMessage(
|
|
224
|
+
'request_approval_in_progress_from',
|
|
225
|
+
{ name: actorEn },
|
|
226
|
+
{ name: actorAr },
|
|
227
|
+
)
|
|
228
|
+
: workflowTemplate('request_approval_in_progress');
|
|
229
|
+
case 'approval_pending':
|
|
230
|
+
return actorEn
|
|
231
|
+
? renderSynchronizedWorkflowMessage(
|
|
232
|
+
'request_approval_pending_from',
|
|
233
|
+
{ name: actorEn },
|
|
234
|
+
{ name: actorAr },
|
|
235
|
+
)
|
|
236
|
+
: workflowTemplate('request_approval_pending');
|
|
237
|
+
case 'approved':
|
|
238
|
+
return actorEn
|
|
239
|
+
? renderSynchronizedWorkflowMessage(
|
|
240
|
+
'request_approved_by',
|
|
241
|
+
{ name: actorEn, comment: commentWf },
|
|
242
|
+
{ name: actorAr, comment: commentWf },
|
|
243
|
+
)
|
|
244
|
+
: renderSynchronizedWorkflowMessage(
|
|
245
|
+
'request_approved',
|
|
246
|
+
{ comment: commentWf },
|
|
247
|
+
{ comment: commentWf },
|
|
248
|
+
);
|
|
249
|
+
case 'rejected':
|
|
250
|
+
return actorEn
|
|
251
|
+
? renderSynchronizedWorkflowMessage(
|
|
252
|
+
'request_rejected_by',
|
|
253
|
+
{ name: actorEn, comment: commentWf },
|
|
254
|
+
{ name: actorAr, comment: commentWf },
|
|
255
|
+
)
|
|
256
|
+
: renderSynchronizedWorkflowMessage(
|
|
257
|
+
'request_rejected',
|
|
258
|
+
{ comment: commentWf },
|
|
259
|
+
{ comment: commentWf },
|
|
260
|
+
);
|
|
261
|
+
case 'returned_for_modification':
|
|
262
|
+
return actorEn
|
|
263
|
+
? renderSynchronizedWorkflowMessage(
|
|
264
|
+
'request_returned_for_modification_by',
|
|
265
|
+
{ name: actorEn, comment: commentWf },
|
|
266
|
+
{ name: actorAr, comment: commentWf },
|
|
267
|
+
)
|
|
268
|
+
: renderSynchronizedWorkflowMessage(
|
|
269
|
+
'request_returned_for_modification',
|
|
270
|
+
{ comment: commentWf },
|
|
271
|
+
{ comment: commentWf },
|
|
272
|
+
);
|
|
273
|
+
case 'approval_pending_update':
|
|
274
|
+
return actorEn
|
|
275
|
+
? renderSynchronizedWorkflowMessage(
|
|
276
|
+
'request_approval_pending_from',
|
|
277
|
+
{ name: actorEn, comment: commentWf },
|
|
278
|
+
{ name: actorAr, comment: commentWf },
|
|
279
|
+
)
|
|
280
|
+
: renderSynchronizedWorkflowMessage(
|
|
281
|
+
'request_approval_pending',
|
|
282
|
+
{ comment: commentWf },
|
|
283
|
+
{ comment: commentWf },
|
|
284
|
+
);
|
|
285
|
+
default:
|
|
286
|
+
return workflowTemplate('request_submitted');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Standard entry point for chat `message` + `message_ar`.
|
|
292
|
+
* Use for every chat insert/update across all CAA modules.
|
|
293
|
+
*/
|
|
294
|
+
export function buildSynchronizedChatMessage(ctx: ChatMessageContext): BilingualText {
|
|
295
|
+
const commentChat = formatChatCommentSuffix(ctx.comment);
|
|
296
|
+
|
|
297
|
+
switch (ctx.action) {
|
|
298
|
+
case 'cancellation_submitted': {
|
|
299
|
+
const userEn = ctx.userName?.trim() || 'Unknown User';
|
|
300
|
+
const userAr = resolveBilingualName(userEn, ctx.userArabicName);
|
|
301
|
+
const employeeId = String(ctx.employeeId ?? 'N/A');
|
|
302
|
+
return chatTemplate(
|
|
303
|
+
'cancellation_submitted_by',
|
|
304
|
+
{ userName: userEn, employeeId },
|
|
305
|
+
{ userName: userAr, employeeId },
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
case 'request_received':
|
|
309
|
+
return chatTemplate('request_received');
|
|
310
|
+
case 'approved':
|
|
311
|
+
case 'rejected':
|
|
312
|
+
case 'returned_for_modification': {
|
|
313
|
+
const roleEn = ctx.roleName?.trim() || 'Unknown Role';
|
|
314
|
+
const roleAr = resolveBilingualName(roleEn, ctx.roleArabicName);
|
|
315
|
+
const key =
|
|
316
|
+
ctx.action === 'approved'
|
|
317
|
+
? 'request_approved_by_role'
|
|
318
|
+
: ctx.action === 'rejected'
|
|
319
|
+
? 'request_rejected_by_role'
|
|
320
|
+
: 'request_returned_for_modification_by_role';
|
|
321
|
+
return renderSynchronizedChatMessage(
|
|
322
|
+
key,
|
|
323
|
+
{ roleName: roleEn, comment: commentChat },
|
|
324
|
+
{ roleName: roleAr, comment: commentChat },
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
default:
|
|
328
|
+
return chatTemplate('request_received');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ─── Backward-compatible aliases used by Cancellation pilot ───────────────────
|
|
333
|
+
|
|
334
|
+
export function buildApprovalWorkflowContent(params: {
|
|
335
|
+
status: 'Approved' | 'Rejected' | 'Returned for Modification' | 'Pending' | string;
|
|
336
|
+
roleName?: string | null;
|
|
337
|
+
roleArabicName?: string | null;
|
|
338
|
+
comment?: string | null;
|
|
339
|
+
}): BilingualText {
|
|
340
|
+
const status = params.status;
|
|
341
|
+
let action: WorkflowMessageAction;
|
|
342
|
+
if (status === 'Approved') action = 'approved';
|
|
343
|
+
else if (status === 'Rejected') action = 'rejected';
|
|
344
|
+
else if (status === 'Returned for Modification') action = 'returned_for_modification';
|
|
345
|
+
else action = 'approval_pending_update';
|
|
346
|
+
|
|
347
|
+
return buildSynchronizedWorkflowMessage({
|
|
348
|
+
action,
|
|
349
|
+
roleName: params.roleName,
|
|
350
|
+
roleArabicName: params.roleArabicName,
|
|
351
|
+
comment: params.comment,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function buildHumanApprovalWorkflowContent(params: {
|
|
356
|
+
isFirst: boolean;
|
|
357
|
+
roleName?: string | null;
|
|
358
|
+
roleArabicName?: string | null;
|
|
359
|
+
deptName?: string | null;
|
|
360
|
+
deptArabicName?: string | null;
|
|
361
|
+
sectionName?: string | null;
|
|
362
|
+
sectionArabicName?: string | null;
|
|
363
|
+
}): BilingualText {
|
|
364
|
+
return buildSynchronizedWorkflowMessage({
|
|
365
|
+
action: params.isFirst ? 'approval_in_progress' : 'approval_pending',
|
|
366
|
+
roleName: params.roleName,
|
|
367
|
+
roleArabicName: params.roleArabicName,
|
|
368
|
+
deptName: params.deptName,
|
|
369
|
+
deptArabicName: params.deptArabicName,
|
|
370
|
+
sectionName: params.sectionName,
|
|
371
|
+
sectionArabicName: params.sectionArabicName,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function buildApprovalChatMessage(params: {
|
|
376
|
+
status: 'Approved' | 'Rejected' | 'Returned for Modification' | string;
|
|
377
|
+
roleName?: string | null;
|
|
378
|
+
roleArabicName?: string | null;
|
|
379
|
+
comment?: string | null;
|
|
380
|
+
}): BilingualText {
|
|
381
|
+
let action: ChatMessageAction = 'approved';
|
|
382
|
+
if (params.status === 'Rejected') action = 'rejected';
|
|
383
|
+
else if (params.status === 'Returned for Modification') action = 'returned_for_modification';
|
|
384
|
+
|
|
385
|
+
return buildSynchronizedChatMessage({
|
|
386
|
+
action,
|
|
387
|
+
roleName: params.roleName,
|
|
388
|
+
roleArabicName: params.roleArabicName,
|
|
389
|
+
comment: params.comment,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ─── Status labels ───────────────────────────────────────────────────────────
|
|
394
|
+
|
|
395
|
+
export function resolveWorkflowStatusAr(status: string | null | undefined): string | null {
|
|
396
|
+
if (status == null || status === '') return null;
|
|
397
|
+
return WORKFLOW_STATUS_LABELS[status] ?? status;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function resolveChatStatusAr(status: string | null | undefined): string | null {
|
|
401
|
+
if (status == null || status === '') return null;
|
|
402
|
+
return CHAT_STATUS_LABELS[status] ?? status;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ─── Legacy row enrichment (read API) ────────────────────────────────────────
|
|
406
|
+
|
|
407
|
+
export function resolveWorkflowContentAr(content: string | null | undefined): string | null {
|
|
408
|
+
if (content == null || content.trim() === '') return null;
|
|
409
|
+
const normalized = content.trim();
|
|
410
|
+
|
|
411
|
+
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
412
|
+
if (tpl.en === normalized) return tpl.ar;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const commentIdx = normalized.indexOf(': ');
|
|
416
|
+
if (commentIdx > 0) {
|
|
417
|
+
const baseEn = normalized.slice(0, commentIdx).trim();
|
|
418
|
+
const commentText = normalized.slice(commentIdx + 2).trim();
|
|
419
|
+
const commentSuffix = formatWorkflowCommentSuffix(commentText);
|
|
420
|
+
|
|
421
|
+
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
422
|
+
if (!tpl.en.includes('{{comment}}')) continue;
|
|
423
|
+
const withoutComment = tpl.en.replace('{{comment}}', '');
|
|
424
|
+
if (withoutComment.includes('{{name}}')) {
|
|
425
|
+
const staticPrefix = withoutComment.replace('{{name}}', '').trim();
|
|
426
|
+
if (baseEn.startsWith(staticPrefix)) {
|
|
427
|
+
const namePart = baseEn.slice(staticPrefix.length).trim();
|
|
428
|
+
return interpolate(tpl.ar, { name: namePart, comment: commentSuffix });
|
|
429
|
+
}
|
|
430
|
+
} else if (withoutComment.trim() === baseEn) {
|
|
431
|
+
return interpolate(tpl.ar, { comment: commentSuffix });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const inProgressPrefix = WORKFLOW_TEMPLATES.request_approval_in_progress_from?.en.split('{{name}}')[0].trim();
|
|
437
|
+
if (inProgressPrefix && normalized.startsWith(inProgressPrefix)) {
|
|
438
|
+
const namePart = normalized.slice(inProgressPrefix.length).trim();
|
|
439
|
+
return interpolate(WORKFLOW_TEMPLATES.request_approval_in_progress_from.ar, { name: namePart });
|
|
440
|
+
}
|
|
441
|
+
const pendingPrefix = WORKFLOW_TEMPLATES.request_approval_pending_from?.en.split('{{name}}')[0].trim();
|
|
442
|
+
if (pendingPrefix && normalized.startsWith(pendingPrefix)) {
|
|
443
|
+
const namePart = normalized.slice(pendingPrefix.length).trim();
|
|
444
|
+
return interpolate(WORKFLOW_TEMPLATES.request_approval_pending_from.ar, { name: namePart });
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export function resolveChatMessageAr(message: string | null | undefined): string | null {
|
|
451
|
+
if (message == null || message.trim() === '') return null;
|
|
452
|
+
const normalized = message.trim();
|
|
453
|
+
|
|
454
|
+
for (const tpl of Object.values(CHAT_TEMPLATES)) {
|
|
455
|
+
if (tpl.en === normalized) return tpl.ar;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
for (const tpl of Object.values(CHAT_TEMPLATES)) {
|
|
459
|
+
if (!tpl.en.includes('{{roleName}}')) continue;
|
|
460
|
+
const prefix = tpl.en.split('{{roleName}}')[0];
|
|
461
|
+
if (!normalized.startsWith(prefix)) continue;
|
|
462
|
+
const rest = normalized.slice(prefix.length);
|
|
463
|
+
const commentIdx = rest.lastIndexOf(' - ');
|
|
464
|
+
if (commentIdx >= 0) {
|
|
465
|
+
return interpolate(tpl.ar, {
|
|
466
|
+
roleName: rest.slice(0, commentIdx),
|
|
467
|
+
comment: rest.slice(commentIdx),
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
return interpolate(tpl.ar, { roleName: rest, comment: '' });
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** Generic workflow log enricher — use in all module repositories. */
|
|
477
|
+
export function enrichWorkflowLog(log: Record<string, unknown>): Record<string, unknown> {
|
|
478
|
+
const status = log.status != null ? String(log.status) : null;
|
|
479
|
+
const content = log.content != null ? String(log.content) : null;
|
|
480
|
+
return {
|
|
481
|
+
...log,
|
|
482
|
+
content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
|
|
483
|
+
status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Generic chat enricher — use in all module repositories. */
|
|
488
|
+
export function enrichChatMessage(chat: Record<string, unknown>): Record<string, unknown> {
|
|
489
|
+
const status = chat.status != null ? String(chat.status) : null;
|
|
490
|
+
const message = chat.message != null ? String(chat.message) : null;
|
|
491
|
+
return {
|
|
492
|
+
...chat,
|
|
493
|
+
message_ar: chat.message_ar ?? resolveChatMessageAr(message),
|
|
494
|
+
status_ar: chat.status_ar ?? resolveChatStatusAr(status),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** @deprecated Use enrichWorkflowLog */
|
|
499
|
+
export const enrichCancellationWorkflowLog = enrichWorkflowLog;
|
|
500
|
+
|
|
501
|
+
/** @deprecated Use enrichChatMessage */
|
|
502
|
+
export const enrichCancellationChatMessage = enrichChatMessage;
|
package/src/index.ts
CHANGED
|
@@ -504,4 +504,4 @@ export * from './models/SlaApprovalsViewModel';
|
|
|
504
504
|
export * from './models/ServiceSlaApprovalModel';
|
|
505
505
|
export * from './sla/sla-table-sync.service';
|
|
506
506
|
export * from './sla/sla-approval-mirror';
|
|
507
|
-
export * from './i18n/workflow-chat-
|
|
507
|
+
export * from './i18n/workflow-chat-message.builder';
|