@platform-modules/civil-aviation-authority 2.3.278 → 2.3.279
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.
|
@@ -15,14 +15,19 @@ export declare function workflowTemplate(key: string, params?: Record<string, st
|
|
|
15
15
|
export declare function chatTemplate(key: string, params?: Record<string, string>, arParams?: Record<string, string>): BilingualText;
|
|
16
16
|
export declare function resolveWorkflowStatusAr(status: string | null | undefined): string | null;
|
|
17
17
|
export declare function resolveChatStatusAr(status: string | null | undefined): string | null;
|
|
18
|
-
/**
|
|
18
|
+
/** Arabic DB value when present; otherwise English so AR text stays structurally complete. */
|
|
19
|
+
export declare function resolveBilingualName(englishName?: string | null, arabicName?: string | null): string;
|
|
19
20
|
export declare function pickArabicName(arabicName?: string | null, fallback?: string): string;
|
|
20
|
-
/** Prefer Arabic for Arabic locale; English for English locale. */
|
|
21
21
|
export declare function pickLocalizedName(englishName?: string | null, arabicName?: string | null, fallback?: string): string;
|
|
22
|
-
/**
|
|
22
|
+
/** Parallel EN/AR dept-section labels (Arabic columns preferred; English fallback per field). */
|
|
23
|
+
export declare function joinDeptSectionBilingualLabels(deptEn?: string | null, deptAr?: string | null, sectEn?: string | null, sectAr?: string | null): {
|
|
24
|
+
en: string;
|
|
25
|
+
ar: string;
|
|
26
|
+
};
|
|
27
|
+
/** @deprecated Use joinDeptSectionBilingualLabels */
|
|
23
28
|
export declare function joinDeptSectionArabicLabels(deptArabic?: string | null, sectionArabic?: string | null): string;
|
|
24
29
|
export declare function buildApprovalWorkflowContent(params: {
|
|
25
|
-
status: 'Approved' | 'Rejected' | 'Pending' | string;
|
|
30
|
+
status: 'Approved' | 'Rejected' | 'Returned for Modification' | 'Pending' | string;
|
|
26
31
|
roleName?: string | null;
|
|
27
32
|
roleArabicName?: string | null;
|
|
28
33
|
comment?: string | null;
|
|
@@ -31,18 +36,20 @@ export declare function buildHumanApprovalWorkflowContent(params: {
|
|
|
31
36
|
isFirst: boolean;
|
|
32
37
|
roleName?: string | null;
|
|
33
38
|
roleArabicName?: string | null;
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
deptName?: string | null;
|
|
40
|
+
deptArabicName?: string | null;
|
|
41
|
+
sectionName?: string | null;
|
|
42
|
+
sectionArabicName?: string | null;
|
|
36
43
|
}): BilingualText;
|
|
37
44
|
export declare function buildApprovalChatMessage(params: {
|
|
38
|
-
status: 'Approved' | 'Rejected' | string;
|
|
45
|
+
status: 'Approved' | 'Rejected' | 'Returned for Modification' | string;
|
|
39
46
|
roleName?: string | null;
|
|
40
47
|
roleArabicName?: string | null;
|
|
41
48
|
comment?: string | null;
|
|
42
49
|
}): BilingualText;
|
|
43
|
-
/** Resolve Arabic workflow content from a stored English content string. */
|
|
50
|
+
/** Resolve Arabic workflow content from a stored English content string (legacy rows). */
|
|
44
51
|
export declare function resolveWorkflowContentAr(content: string | null | undefined): string | null;
|
|
45
|
-
/** Resolve Arabic chat message from a stored English message string. */
|
|
52
|
+
/** Resolve Arabic chat message from a stored English message string (legacy rows). */
|
|
46
53
|
export declare function resolveChatMessageAr(message: string | null | undefined): string | null;
|
|
47
54
|
export declare function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown>;
|
|
48
55
|
export declare function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -9,8 +9,10 @@ exports.workflowTemplate = workflowTemplate;
|
|
|
9
9
|
exports.chatTemplate = chatTemplate;
|
|
10
10
|
exports.resolveWorkflowStatusAr = resolveWorkflowStatusAr;
|
|
11
11
|
exports.resolveChatStatusAr = resolveChatStatusAr;
|
|
12
|
+
exports.resolveBilingualName = resolveBilingualName;
|
|
12
13
|
exports.pickArabicName = pickArabicName;
|
|
13
14
|
exports.pickLocalizedName = pickLocalizedName;
|
|
15
|
+
exports.joinDeptSectionBilingualLabels = joinDeptSectionBilingualLabels;
|
|
14
16
|
exports.joinDeptSectionArabicLabels = joinDeptSectionArabicLabels;
|
|
15
17
|
exports.buildApprovalWorkflowContent = buildApprovalWorkflowContent;
|
|
16
18
|
exports.buildHumanApprovalWorkflowContent = buildHumanApprovalWorkflowContent;
|
|
@@ -58,11 +60,16 @@ function resolveChatStatusAr(status) {
|
|
|
58
60
|
return null;
|
|
59
61
|
return CHAT_STATUS_LABELS[status] ?? status;
|
|
60
62
|
}
|
|
61
|
-
/**
|
|
63
|
+
/** Arabic DB value when present; otherwise English so AR text stays structurally complete. */
|
|
64
|
+
function resolveBilingualName(englishName, arabicName) {
|
|
65
|
+
const ar = arabicName?.trim();
|
|
66
|
+
if (ar)
|
|
67
|
+
return ar;
|
|
68
|
+
return englishName?.trim() || '';
|
|
69
|
+
}
|
|
62
70
|
function pickArabicName(arabicName, fallback = '') {
|
|
63
71
|
return arabicName?.trim() || fallback;
|
|
64
72
|
}
|
|
65
|
-
/** Prefer Arabic for Arabic locale; English for English locale. */
|
|
66
73
|
function pickLocalizedName(englishName, arabicName, fallback = '') {
|
|
67
74
|
const ar = arabicName?.trim();
|
|
68
75
|
if (ar)
|
|
@@ -75,61 +82,66 @@ function pickLocalizedName(englishName, arabicName, fallback = '') {
|
|
|
75
82
|
function joinDeptSectionLabels(dept, section) {
|
|
76
83
|
return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
|
|
77
84
|
}
|
|
78
|
-
/**
|
|
85
|
+
/** Parallel EN/AR dept-section labels (Arabic columns preferred; English fallback per field). */
|
|
86
|
+
function joinDeptSectionBilingualLabels(deptEn, deptAr, sectEn, sectAr) {
|
|
87
|
+
const en = joinDeptSectionLabels(deptEn, sectEn);
|
|
88
|
+
const ar = joinDeptSectionLabels(resolveBilingualName(deptEn, deptAr), resolveBilingualName(sectEn, sectAr));
|
|
89
|
+
return { en, ar };
|
|
90
|
+
}
|
|
91
|
+
/** @deprecated Use joinDeptSectionBilingualLabels */
|
|
79
92
|
function joinDeptSectionArabicLabels(deptArabic, sectionArabic) {
|
|
80
93
|
return joinDeptSectionLabels(deptArabic, sectionArabic);
|
|
81
94
|
}
|
|
95
|
+
function formatWorkflowCommentSuffix(comment) {
|
|
96
|
+
const text = comment?.trim();
|
|
97
|
+
return text ? `: ${text}` : '';
|
|
98
|
+
}
|
|
99
|
+
function formatChatCommentSuffix(comment) {
|
|
100
|
+
const text = comment?.trim();
|
|
101
|
+
return text ? ` - ${text}` : '';
|
|
102
|
+
}
|
|
103
|
+
function buildWorkflowApprovalByName(templateKey, nameEn, nameAr, comment) {
|
|
104
|
+
const commentSuffix = formatWorkflowCommentSuffix(comment);
|
|
105
|
+
return workflowTemplate(templateKey, { name: nameEn, comment: commentSuffix }, { name: nameAr, comment: commentSuffix });
|
|
106
|
+
}
|
|
107
|
+
function buildWorkflowApprovalWithoutName(templateKey, comment) {
|
|
108
|
+
const commentSuffix = formatWorkflowCommentSuffix(comment);
|
|
109
|
+
return workflowTemplate(templateKey, { comment: commentSuffix }, { comment: commentSuffix });
|
|
110
|
+
}
|
|
82
111
|
function buildApprovalWorkflowContent(params) {
|
|
83
112
|
const nameEn = params.roleName?.trim() || '';
|
|
84
|
-
const nameAr =
|
|
85
|
-
let base;
|
|
113
|
+
const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
|
|
86
114
|
if (params.status === 'Approved') {
|
|
87
|
-
|
|
88
|
-
?
|
|
89
|
-
:
|
|
90
|
-
base.ar = nameAr
|
|
91
|
-
? workflowTemplate('request_approved_by', { name: nameAr }).ar
|
|
92
|
-
: workflowTemplate('request_approved').ar;
|
|
115
|
+
return nameEn
|
|
116
|
+
? buildWorkflowApprovalByName('request_approved_by', nameEn, nameAr, params.comment)
|
|
117
|
+
: buildWorkflowApprovalWithoutName('request_approved', params.comment);
|
|
93
118
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
?
|
|
97
|
-
:
|
|
98
|
-
base.ar = nameAr
|
|
99
|
-
? workflowTemplate('request_rejected_by', { name: nameAr }).ar
|
|
100
|
-
: workflowTemplate('request_rejected').ar;
|
|
119
|
+
if (params.status === 'Rejected') {
|
|
120
|
+
return nameEn
|
|
121
|
+
? buildWorkflowApprovalByName('request_rejected_by', nameEn, nameAr, params.comment)
|
|
122
|
+
: buildWorkflowApprovalWithoutName('request_rejected', params.comment);
|
|
101
123
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
?
|
|
105
|
-
:
|
|
106
|
-
base.ar = nameAr
|
|
107
|
-
? workflowTemplate('request_approval_pending_from', { name: nameAr }).ar
|
|
108
|
-
: workflowTemplate('request_approval_pending').ar;
|
|
124
|
+
if (params.status === 'Returned for Modification') {
|
|
125
|
+
return nameEn
|
|
126
|
+
? buildWorkflowApprovalByName('request_returned_for_modification_by', nameEn, nameAr, params.comment)
|
|
127
|
+
: buildWorkflowApprovalWithoutName('request_returned_for_modification', params.comment);
|
|
109
128
|
}
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
ar: base.ar,
|
|
116
|
-
};
|
|
129
|
+
const commentSuffix = formatWorkflowCommentSuffix(params.comment);
|
|
130
|
+
if (nameEn) {
|
|
131
|
+
return workflowTemplate('request_approval_pending_from', { name: nameEn, comment: commentSuffix }, { name: nameAr, comment: commentSuffix });
|
|
132
|
+
}
|
|
133
|
+
return workflowTemplate('request_approval_pending', { comment: commentSuffix }, { comment: commentSuffix });
|
|
117
134
|
}
|
|
118
135
|
function buildHumanApprovalWorkflowContent(params) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
? workflowTemplate(key, { name: nameAr }).ar
|
|
129
|
-
: params.isFirst
|
|
130
|
-
? workflowTemplate('request_approval_in_progress').ar
|
|
131
|
-
: workflowTemplate('request_approval_pending').ar,
|
|
132
|
-
};
|
|
136
|
+
const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
|
|
137
|
+
if (params.roleName?.trim()) {
|
|
138
|
+
const nameEn = params.roleName.trim();
|
|
139
|
+
const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
|
|
140
|
+
return workflowTemplate(key, { name: nameEn }, { name: nameAr });
|
|
141
|
+
}
|
|
142
|
+
const { en, ar } = joinDeptSectionBilingualLabels(params.deptName, params.deptArabicName, params.sectionName, params.sectionArabicName);
|
|
143
|
+
if (en) {
|
|
144
|
+
return workflowTemplate(key, { name: en }, { name: ar });
|
|
133
145
|
}
|
|
134
146
|
return params.isFirst
|
|
135
147
|
? workflowTemplate('request_approval_in_progress')
|
|
@@ -137,15 +149,24 @@ function buildHumanApprovalWorkflowContent(params) {
|
|
|
137
149
|
}
|
|
138
150
|
function buildApprovalChatMessage(params) {
|
|
139
151
|
const roleEn = params.roleName?.trim() || 'Unknown Role';
|
|
140
|
-
const roleAr =
|
|
141
|
-
const commentSuffix =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
const roleAr = resolveBilingualName(roleEn, params.roleArabicName);
|
|
153
|
+
const commentSuffix = formatChatCommentSuffix(params.comment);
|
|
154
|
+
let key;
|
|
155
|
+
if (params.status === 'Approved') {
|
|
156
|
+
key = 'request_approved_by_role';
|
|
157
|
+
}
|
|
158
|
+
else if (params.status === 'Rejected') {
|
|
159
|
+
key = 'request_rejected_by_role';
|
|
160
|
+
}
|
|
161
|
+
else if (params.status === 'Returned for Modification') {
|
|
162
|
+
key = 'request_returned_for_modification_by_role';
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
key = 'request_approved_by_role';
|
|
166
|
+
}
|
|
167
|
+
return chatTemplate(key, { roleName: roleEn, comment: commentSuffix }, { roleName: roleAr, comment: commentSuffix });
|
|
147
168
|
}
|
|
148
|
-
/** Resolve Arabic workflow content from a stored English content string. */
|
|
169
|
+
/** Resolve Arabic workflow content from a stored English content string (legacy rows). */
|
|
149
170
|
function resolveWorkflowContentAr(content) {
|
|
150
171
|
if (content == null || content.trim() === '')
|
|
151
172
|
return null;
|
|
@@ -154,9 +175,30 @@ function resolveWorkflowContentAr(content) {
|
|
|
154
175
|
if (tpl.en === normalized)
|
|
155
176
|
return tpl.ar;
|
|
156
177
|
}
|
|
178
|
+
const commentIdx = normalized.indexOf(': ');
|
|
179
|
+
if (commentIdx > 0) {
|
|
180
|
+
const baseEn = normalized.slice(0, commentIdx).trim();
|
|
181
|
+
const commentText = normalized.slice(commentIdx + 2).trim();
|
|
182
|
+
const commentSuffix = formatWorkflowCommentSuffix(commentText);
|
|
183
|
+
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
184
|
+
if (!tpl.en.includes('{{comment}}'))
|
|
185
|
+
continue;
|
|
186
|
+
const withoutComment = tpl.en.replace('{{comment}}', '');
|
|
187
|
+
if (withoutComment.includes('{{name}}')) {
|
|
188
|
+
const staticPrefix = withoutComment.replace('{{name}}', '').trim();
|
|
189
|
+
if (baseEn.startsWith(staticPrefix)) {
|
|
190
|
+
const namePart = baseEn.slice(staticPrefix.length).trim();
|
|
191
|
+
return interpolate(tpl.ar, { name: namePart, comment: commentSuffix });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (withoutComment.trim() === baseEn) {
|
|
195
|
+
return interpolate(tpl.ar, { comment: commentSuffix });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
157
199
|
return null;
|
|
158
200
|
}
|
|
159
|
-
/** Resolve Arabic chat message from a stored English message string. */
|
|
201
|
+
/** Resolve Arabic chat message from a stored English message string (legacy rows). */
|
|
160
202
|
function resolveChatMessageAr(message) {
|
|
161
203
|
if (message == null || message.trim() === '')
|
|
162
204
|
return null;
|
|
@@ -165,9 +207,6 @@ function resolveChatMessageAr(message) {
|
|
|
165
207
|
if (tpl.en === normalized)
|
|
166
208
|
return tpl.ar;
|
|
167
209
|
}
|
|
168
|
-
if (normalized === 'Request Received') {
|
|
169
|
-
return CHAT_TEMPLATES.request_received?.ar ?? null;
|
|
170
|
-
}
|
|
171
210
|
return null;
|
|
172
211
|
}
|
|
173
212
|
function enrichCancellationWorkflowLog(log) {
|
|
@@ -29,20 +29,28 @@
|
|
|
29
29
|
"ar": "في انتظار موافقة {{name}}"
|
|
30
30
|
},
|
|
31
31
|
"request_approved_by": {
|
|
32
|
-
"en": "Request Approved by {{name}}",
|
|
33
|
-
"ar": "تمت الموافقة على الطلب من قبل {{name}}"
|
|
32
|
+
"en": "Request Approved by {{name}}{{comment}}",
|
|
33
|
+
"ar": "تمت الموافقة على الطلب من قبل {{name}}{{comment}}"
|
|
34
34
|
},
|
|
35
35
|
"request_rejected_by": {
|
|
36
|
-
"en": "Request Rejected by {{name}}",
|
|
37
|
-
"ar": "تم رفض الطلب من قبل {{name}}"
|
|
36
|
+
"en": "Request Rejected by {{name}}{{comment}}",
|
|
37
|
+
"ar": "تم رفض الطلب من قبل {{name}}{{comment}}"
|
|
38
38
|
},
|
|
39
39
|
"request_approved": {
|
|
40
|
-
"en": "Request Approved",
|
|
41
|
-
"ar": "تمت الموافقة على الطلب"
|
|
40
|
+
"en": "Request Approved{{comment}}",
|
|
41
|
+
"ar": "تمت الموافقة على الطلب{{comment}}"
|
|
42
42
|
},
|
|
43
43
|
"request_rejected": {
|
|
44
|
-
"en": "Request Rejected",
|
|
45
|
-
"ar": "تم رفض الطلب"
|
|
44
|
+
"en": "Request Rejected{{comment}}",
|
|
45
|
+
"ar": "تم رفض الطلب{{comment}}"
|
|
46
|
+
},
|
|
47
|
+
"request_returned_for_modification_by": {
|
|
48
|
+
"en": "Request Returned for Modification by {{name}}{{comment}}",
|
|
49
|
+
"ar": "تم إرجاع الطلب للتعديل من قبل {{name}}{{comment}}"
|
|
50
|
+
},
|
|
51
|
+
"request_returned_for_modification": {
|
|
52
|
+
"en": "Request Returned for Modification{{comment}}",
|
|
53
|
+
"ar": "تم إرجاع الطلب للتعديل{{comment}}"
|
|
46
54
|
}
|
|
47
55
|
},
|
|
48
56
|
"chatTemplates": {
|
|
@@ -61,6 +69,10 @@
|
|
|
61
69
|
"request_rejected_by_role": {
|
|
62
70
|
"en": "Request Rejected by: {{roleName}}{{comment}}",
|
|
63
71
|
"ar": "تم رفض الطلب من قبل: {{roleName}}{{comment}}"
|
|
72
|
+
},
|
|
73
|
+
"request_returned_for_modification_by_role": {
|
|
74
|
+
"en": "Request Returned for Modification by: {{roleName}}{{comment}}",
|
|
75
|
+
"ar": "تم إرجاع الطلب للتعديل من قبل: {{roleName}}{{comment}}"
|
|
64
76
|
}
|
|
65
77
|
},
|
|
66
78
|
"workflowStatusLabels": {
|
|
@@ -73,6 +85,7 @@
|
|
|
73
85
|
"Received": "تم الاستلام",
|
|
74
86
|
"Approved": "موافق عليه",
|
|
75
87
|
"Rejected": "مرفوض",
|
|
76
|
-
"Pending": "قيد الانتظار"
|
|
88
|
+
"Pending": "قيد الانتظار",
|
|
89
|
+
"Returned for Modification": "أُعيد للتعديل"
|
|
77
90
|
}
|
|
78
91
|
}
|
package/package.json
CHANGED
|
@@ -29,20 +29,28 @@
|
|
|
29
29
|
"ar": "في انتظار موافقة {{name}}"
|
|
30
30
|
},
|
|
31
31
|
"request_approved_by": {
|
|
32
|
-
"en": "Request Approved by {{name}}",
|
|
33
|
-
"ar": "تمت الموافقة على الطلب من قبل {{name}}"
|
|
32
|
+
"en": "Request Approved by {{name}}{{comment}}",
|
|
33
|
+
"ar": "تمت الموافقة على الطلب من قبل {{name}}{{comment}}"
|
|
34
34
|
},
|
|
35
35
|
"request_rejected_by": {
|
|
36
|
-
"en": "Request Rejected by {{name}}",
|
|
37
|
-
"ar": "تم رفض الطلب من قبل {{name}}"
|
|
36
|
+
"en": "Request Rejected by {{name}}{{comment}}",
|
|
37
|
+
"ar": "تم رفض الطلب من قبل {{name}}{{comment}}"
|
|
38
38
|
},
|
|
39
39
|
"request_approved": {
|
|
40
|
-
"en": "Request Approved",
|
|
41
|
-
"ar": "تمت الموافقة على الطلب"
|
|
40
|
+
"en": "Request Approved{{comment}}",
|
|
41
|
+
"ar": "تمت الموافقة على الطلب{{comment}}"
|
|
42
42
|
},
|
|
43
43
|
"request_rejected": {
|
|
44
|
-
"en": "Request Rejected",
|
|
45
|
-
"ar": "تم رفض الطلب"
|
|
44
|
+
"en": "Request Rejected{{comment}}",
|
|
45
|
+
"ar": "تم رفض الطلب{{comment}}"
|
|
46
|
+
},
|
|
47
|
+
"request_returned_for_modification_by": {
|
|
48
|
+
"en": "Request Returned for Modification by {{name}}{{comment}}",
|
|
49
|
+
"ar": "تم إرجاع الطلب للتعديل من قبل {{name}}{{comment}}"
|
|
50
|
+
},
|
|
51
|
+
"request_returned_for_modification": {
|
|
52
|
+
"en": "Request Returned for Modification{{comment}}",
|
|
53
|
+
"ar": "تم إرجاع الطلب للتعديل{{comment}}"
|
|
46
54
|
}
|
|
47
55
|
},
|
|
48
56
|
"chatTemplates": {
|
|
@@ -61,6 +69,10 @@
|
|
|
61
69
|
"request_rejected_by_role": {
|
|
62
70
|
"en": "Request Rejected by: {{roleName}}{{comment}}",
|
|
63
71
|
"ar": "تم رفض الطلب من قبل: {{roleName}}{{comment}}"
|
|
72
|
+
},
|
|
73
|
+
"request_returned_for_modification_by_role": {
|
|
74
|
+
"en": "Request Returned for Modification by: {{roleName}}{{comment}}",
|
|
75
|
+
"ar": "تم إرجاع الطلب للتعديل من قبل: {{roleName}}{{comment}}"
|
|
64
76
|
}
|
|
65
77
|
},
|
|
66
78
|
"workflowStatusLabels": {
|
|
@@ -73,6 +85,7 @@
|
|
|
73
85
|
"Received": "تم الاستلام",
|
|
74
86
|
"Approved": "موافق عليه",
|
|
75
87
|
"Rejected": "مرفوض",
|
|
76
|
-
"Pending": "قيد الانتظار"
|
|
88
|
+
"Pending": "قيد الانتظار",
|
|
89
|
+
"Returned for Modification": "أُعيد للتعديل"
|
|
77
90
|
}
|
|
78
91
|
}
|
|
@@ -66,12 +66,20 @@ export function resolveChatStatusAr(status: string | null | undefined): string |
|
|
|
66
66
|
return CHAT_STATUS_LABELS[status] ?? status;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/**
|
|
69
|
+
/** Arabic DB value when present; otherwise English so AR text stays structurally complete. */
|
|
70
|
+
export function resolveBilingualName(
|
|
71
|
+
englishName?: string | null,
|
|
72
|
+
arabicName?: string | null,
|
|
73
|
+
): string {
|
|
74
|
+
const ar = arabicName?.trim();
|
|
75
|
+
if (ar) return ar;
|
|
76
|
+
return englishName?.trim() || '';
|
|
77
|
+
}
|
|
78
|
+
|
|
70
79
|
export function pickArabicName(arabicName?: string | null, fallback = ''): string {
|
|
71
80
|
return arabicName?.trim() || fallback;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
/** Prefer Arabic for Arabic locale; English for English locale. */
|
|
75
83
|
export function pickLocalizedName(
|
|
76
84
|
englishName?: string | null,
|
|
77
85
|
arabicName?: string | null,
|
|
@@ -91,7 +99,22 @@ function joinDeptSectionLabels(
|
|
|
91
99
|
return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
/**
|
|
102
|
+
/** Parallel EN/AR dept-section labels (Arabic columns preferred; English fallback per field). */
|
|
103
|
+
export function joinDeptSectionBilingualLabels(
|
|
104
|
+
deptEn?: string | null,
|
|
105
|
+
deptAr?: string | null,
|
|
106
|
+
sectEn?: string | null,
|
|
107
|
+
sectAr?: string | null,
|
|
108
|
+
): { en: string; ar: string } {
|
|
109
|
+
const en = joinDeptSectionLabels(deptEn, sectEn);
|
|
110
|
+
const ar = joinDeptSectionLabels(
|
|
111
|
+
resolveBilingualName(deptEn, deptAr),
|
|
112
|
+
resolveBilingualName(sectEn, sectAr),
|
|
113
|
+
);
|
|
114
|
+
return { en, ar };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @deprecated Use joinDeptSectionBilingualLabels */
|
|
95
118
|
export function joinDeptSectionArabicLabels(
|
|
96
119
|
deptArabic?: string | null,
|
|
97
120
|
sectionArabic?: string | null,
|
|
@@ -99,70 +122,110 @@ export function joinDeptSectionArabicLabels(
|
|
|
99
122
|
return joinDeptSectionLabels(deptArabic, sectionArabic);
|
|
100
123
|
}
|
|
101
124
|
|
|
125
|
+
function formatWorkflowCommentSuffix(comment?: string | null): string {
|
|
126
|
+
const text = comment?.trim();
|
|
127
|
+
return text ? `: ${text}` : '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function formatChatCommentSuffix(comment?: string | null): string {
|
|
131
|
+
const text = comment?.trim();
|
|
132
|
+
return text ? ` - ${text}` : '';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function buildWorkflowApprovalByName(
|
|
136
|
+
templateKey: 'request_approved_by' | 'request_rejected_by' | 'request_returned_for_modification_by',
|
|
137
|
+
nameEn: string,
|
|
138
|
+
nameAr: string,
|
|
139
|
+
comment?: string | null,
|
|
140
|
+
): BilingualText {
|
|
141
|
+
const commentSuffix = formatWorkflowCommentSuffix(comment);
|
|
142
|
+
return workflowTemplate(
|
|
143
|
+
templateKey,
|
|
144
|
+
{ name: nameEn, comment: commentSuffix },
|
|
145
|
+
{ name: nameAr, comment: commentSuffix },
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function buildWorkflowApprovalWithoutName(
|
|
150
|
+
templateKey: 'request_approved' | 'request_rejected' | 'request_returned_for_modification',
|
|
151
|
+
comment?: string | null,
|
|
152
|
+
): BilingualText {
|
|
153
|
+
const commentSuffix = formatWorkflowCommentSuffix(comment);
|
|
154
|
+
return workflowTemplate(
|
|
155
|
+
templateKey,
|
|
156
|
+
{ comment: commentSuffix },
|
|
157
|
+
{ comment: commentSuffix },
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
102
161
|
export function buildApprovalWorkflowContent(params: {
|
|
103
|
-
status: 'Approved' | 'Rejected' | 'Pending' | string;
|
|
162
|
+
status: 'Approved' | 'Rejected' | 'Returned for Modification' | 'Pending' | string;
|
|
104
163
|
roleName?: string | null;
|
|
105
164
|
roleArabicName?: string | null;
|
|
106
165
|
comment?: string | null;
|
|
107
166
|
}): BilingualText {
|
|
108
167
|
const nameEn = params.roleName?.trim() || '';
|
|
109
|
-
const nameAr =
|
|
168
|
+
const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
|
|
110
169
|
|
|
111
|
-
let base: BilingualText;
|
|
112
170
|
if (params.status === 'Approved') {
|
|
113
|
-
|
|
114
|
-
?
|
|
115
|
-
:
|
|
116
|
-
base.ar = nameAr
|
|
117
|
-
? workflowTemplate('request_approved_by', { name: nameAr }).ar
|
|
118
|
-
: workflowTemplate('request_approved').ar;
|
|
119
|
-
} else if (params.status === 'Rejected') {
|
|
120
|
-
base = nameEn
|
|
121
|
-
? workflowTemplate('request_rejected_by', { name: nameEn })
|
|
122
|
-
: workflowTemplate('request_rejected');
|
|
123
|
-
base.ar = nameAr
|
|
124
|
-
? workflowTemplate('request_rejected_by', { name: nameAr }).ar
|
|
125
|
-
: workflowTemplate('request_rejected').ar;
|
|
126
|
-
} else {
|
|
127
|
-
base = nameEn
|
|
128
|
-
? workflowTemplate('request_approval_pending_from', { name: nameEn })
|
|
129
|
-
: workflowTemplate('request_approval_pending');
|
|
130
|
-
base.ar = nameAr
|
|
131
|
-
? workflowTemplate('request_approval_pending_from', { name: nameAr }).ar
|
|
132
|
-
: workflowTemplate('request_approval_pending').ar;
|
|
171
|
+
return nameEn
|
|
172
|
+
? buildWorkflowApprovalByName('request_approved_by', nameEn, nameAr, params.comment)
|
|
173
|
+
: buildWorkflowApprovalWithoutName('request_approved', params.comment);
|
|
133
174
|
}
|
|
134
175
|
|
|
135
|
-
|
|
136
|
-
|
|
176
|
+
if (params.status === 'Rejected') {
|
|
177
|
+
return nameEn
|
|
178
|
+
? buildWorkflowApprovalByName('request_rejected_by', nameEn, nameAr, params.comment)
|
|
179
|
+
: buildWorkflowApprovalWithoutName('request_rejected', params.comment);
|
|
180
|
+
}
|
|
137
181
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
182
|
+
if (params.status === 'Returned for Modification') {
|
|
183
|
+
return nameEn
|
|
184
|
+
? buildWorkflowApprovalByName('request_returned_for_modification_by', nameEn, nameAr, params.comment)
|
|
185
|
+
: buildWorkflowApprovalWithoutName('request_returned_for_modification', params.comment);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const commentSuffix = formatWorkflowCommentSuffix(params.comment);
|
|
189
|
+
if (nameEn) {
|
|
190
|
+
return workflowTemplate(
|
|
191
|
+
'request_approval_pending_from',
|
|
192
|
+
{ name: nameEn, comment: commentSuffix },
|
|
193
|
+
{ name: nameAr, comment: commentSuffix },
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return workflowTemplate(
|
|
198
|
+
'request_approval_pending',
|
|
199
|
+
{ comment: commentSuffix },
|
|
200
|
+
{ comment: commentSuffix },
|
|
201
|
+
);
|
|
142
202
|
}
|
|
143
203
|
|
|
144
204
|
export function buildHumanApprovalWorkflowContent(params: {
|
|
145
205
|
isFirst: boolean;
|
|
146
206
|
roleName?: string | null;
|
|
147
207
|
roleArabicName?: string | null;
|
|
148
|
-
|
|
149
|
-
|
|
208
|
+
deptName?: string | null;
|
|
209
|
+
deptArabicName?: string | null;
|
|
210
|
+
sectionName?: string | null;
|
|
211
|
+
sectionArabicName?: string | null;
|
|
150
212
|
}): BilingualText {
|
|
151
|
-
const
|
|
152
|
-
const nameAr = params.roleName
|
|
153
|
-
? pickArabicName(params.roleArabicName)
|
|
154
|
-
: (params.deptSectionAr?.trim() || '');
|
|
213
|
+
const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
|
|
155
214
|
|
|
156
|
-
if (
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
215
|
+
if (params.roleName?.trim()) {
|
|
216
|
+
const nameEn = params.roleName.trim();
|
|
217
|
+
const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
|
|
218
|
+
return workflowTemplate(key, { name: nameEn }, { name: nameAr });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const { en, ar } = joinDeptSectionBilingualLabels(
|
|
222
|
+
params.deptName,
|
|
223
|
+
params.deptArabicName,
|
|
224
|
+
params.sectionName,
|
|
225
|
+
params.sectionArabicName,
|
|
226
|
+
);
|
|
227
|
+
if (en) {
|
|
228
|
+
return workflowTemplate(key, { name: en }, { name: ar });
|
|
166
229
|
}
|
|
167
230
|
|
|
168
231
|
return params.isFirst
|
|
@@ -171,42 +234,75 @@ export function buildHumanApprovalWorkflowContent(params: {
|
|
|
171
234
|
}
|
|
172
235
|
|
|
173
236
|
export function buildApprovalChatMessage(params: {
|
|
174
|
-
status: 'Approved' | 'Rejected' | string;
|
|
237
|
+
status: 'Approved' | 'Rejected' | 'Returned for Modification' | string;
|
|
175
238
|
roleName?: string | null;
|
|
176
239
|
roleArabicName?: string | null;
|
|
177
240
|
comment?: string | null;
|
|
178
241
|
}): BilingualText {
|
|
179
242
|
const roleEn = params.roleName?.trim() || 'Unknown Role';
|
|
180
|
-
const roleAr =
|
|
181
|
-
const commentSuffix =
|
|
182
|
-
const key = params.status === 'Approved' ? 'request_approved_by_role' : 'request_rejected_by_role';
|
|
243
|
+
const roleAr = resolveBilingualName(roleEn, params.roleArabicName);
|
|
244
|
+
const commentSuffix = formatChatCommentSuffix(params.comment);
|
|
183
245
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
246
|
+
let key: string;
|
|
247
|
+
if (params.status === 'Approved') {
|
|
248
|
+
key = 'request_approved_by_role';
|
|
249
|
+
} else if (params.status === 'Rejected') {
|
|
250
|
+
key = 'request_rejected_by_role';
|
|
251
|
+
} else if (params.status === 'Returned for Modification') {
|
|
252
|
+
key = 'request_returned_for_modification_by_role';
|
|
253
|
+
} else {
|
|
254
|
+
key = 'request_approved_by_role';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return chatTemplate(
|
|
258
|
+
key,
|
|
259
|
+
{ roleName: roleEn, comment: commentSuffix },
|
|
260
|
+
{ roleName: roleAr, comment: commentSuffix },
|
|
261
|
+
);
|
|
188
262
|
}
|
|
189
263
|
|
|
190
|
-
/** Resolve Arabic workflow content from a stored English content string. */
|
|
264
|
+
/** Resolve Arabic workflow content from a stored English content string (legacy rows). */
|
|
191
265
|
export function resolveWorkflowContentAr(content: string | null | undefined): string | null {
|
|
192
266
|
if (content == null || content.trim() === '') return null;
|
|
193
267
|
const normalized = content.trim();
|
|
268
|
+
|
|
194
269
|
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
195
270
|
if (tpl.en === normalized) return tpl.ar;
|
|
196
271
|
}
|
|
272
|
+
|
|
273
|
+
const commentIdx = normalized.indexOf(': ');
|
|
274
|
+
if (commentIdx > 0) {
|
|
275
|
+
const baseEn = normalized.slice(0, commentIdx).trim();
|
|
276
|
+
const commentText = normalized.slice(commentIdx + 2).trim();
|
|
277
|
+
const commentSuffix = formatWorkflowCommentSuffix(commentText);
|
|
278
|
+
|
|
279
|
+
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
280
|
+
if (!tpl.en.includes('{{comment}}')) continue;
|
|
281
|
+
const withoutComment = tpl.en.replace('{{comment}}', '');
|
|
282
|
+
if (withoutComment.includes('{{name}}')) {
|
|
283
|
+
const staticPrefix = withoutComment.replace('{{name}}', '').trim();
|
|
284
|
+
if (baseEn.startsWith(staticPrefix)) {
|
|
285
|
+
const namePart = baseEn.slice(staticPrefix.length).trim();
|
|
286
|
+
return interpolate(tpl.ar, { name: namePart, comment: commentSuffix });
|
|
287
|
+
}
|
|
288
|
+
} else if (withoutComment.trim() === baseEn) {
|
|
289
|
+
return interpolate(tpl.ar, { comment: commentSuffix });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
197
294
|
return null;
|
|
198
295
|
}
|
|
199
296
|
|
|
200
|
-
/** Resolve Arabic chat message from a stored English message string. */
|
|
297
|
+
/** Resolve Arabic chat message from a stored English message string (legacy rows). */
|
|
201
298
|
export function resolveChatMessageAr(message: string | null | undefined): string | null {
|
|
202
299
|
if (message == null || message.trim() === '') return null;
|
|
203
300
|
const normalized = message.trim();
|
|
301
|
+
|
|
204
302
|
for (const tpl of Object.values(CHAT_TEMPLATES)) {
|
|
205
303
|
if (tpl.en === normalized) return tpl.ar;
|
|
206
304
|
}
|
|
207
|
-
|
|
208
|
-
return CHAT_TEMPLATES.request_received?.ar ?? null;
|
|
209
|
-
}
|
|
305
|
+
|
|
210
306
|
return null;
|
|
211
307
|
}
|
|
212
308
|
|