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

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,11 +15,10 @@ 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
- /** 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;
20
- export declare function pickArabicName(arabicName?: string | null, fallback?: string): string;
18
+ /** Arabic column value only never falls back to English (for content_ar / message_ar). */
19
+ export declare function pickArabicName(arabicName?: string | null): string;
21
20
  export declare function pickLocalizedName(englishName?: string | null, arabicName?: string | null, fallback?: string): string;
22
- /** Parallel EN/AR dept-section labels (Arabic columns preferred; English fallback per field). */
21
+ /** EN from English columns; AR from Arabic columns only (no English in ar). */
23
22
  export declare function joinDeptSectionBilingualLabels(deptEn?: string | null, deptAr?: string | null, sectEn?: string | null, sectAr?: string | null): {
24
23
  en: string;
25
24
  ar: string;
@@ -9,7 +9,6 @@ exports.workflowTemplate = workflowTemplate;
9
9
  exports.chatTemplate = chatTemplate;
10
10
  exports.resolveWorkflowStatusAr = resolveWorkflowStatusAr;
11
11
  exports.resolveChatStatusAr = resolveChatStatusAr;
12
- exports.resolveBilingualName = resolveBilingualName;
13
12
  exports.pickArabicName = pickArabicName;
14
13
  exports.pickLocalizedName = pickLocalizedName;
15
14
  exports.joinDeptSectionBilingualLabels = joinDeptSectionBilingualLabels;
@@ -60,15 +59,9 @@ function resolveChatStatusAr(status) {
60
59
  return null;
61
60
  return CHAT_STATUS_LABELS[status] ?? status;
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
- }
70
- function pickArabicName(arabicName, fallback = '') {
71
- return arabicName?.trim() || fallback;
62
+ /** Arabic column value only never falls back to English (for content_ar / message_ar). */
63
+ function pickArabicName(arabicName) {
64
+ return arabicName?.trim() || '';
72
65
  }
73
66
  function pickLocalizedName(englishName, arabicName, fallback = '') {
74
67
  const ar = arabicName?.trim();
@@ -82,11 +75,12 @@ function pickLocalizedName(englishName, arabicName, fallback = '') {
82
75
  function joinDeptSectionLabels(dept, section) {
83
76
  return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
84
77
  }
85
- /** Parallel EN/AR dept-section labels (Arabic columns preferred; English fallback per field). */
78
+ /** EN from English columns; AR from Arabic columns only (no English in ar). */
86
79
  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 };
80
+ return {
81
+ en: joinDeptSectionLabels(deptEn, sectEn),
82
+ ar: joinDeptSectionLabels(deptAr, sectAr),
83
+ };
90
84
  }
91
85
  /** @deprecated Use joinDeptSectionBilingualLabels */
92
86
  function joinDeptSectionArabicLabels(deptArabic, sectionArabic) {
@@ -100,48 +94,73 @@ function formatChatCommentSuffix(comment) {
100
94
  const text = comment?.trim();
101
95
  return text ? ` - ${text}` : '';
102
96
  }
103
- function buildWorkflowApprovalByName(templateKey, nameEn, nameAr, comment) {
97
+ function buildWorkflowApprovalByName(templateKey, fallbackKey, nameEn, nameAr, comment) {
104
98
  const commentSuffix = formatWorkflowCommentSuffix(comment);
105
- return workflowTemplate(templateKey, { name: nameEn, comment: commentSuffix }, { name: nameAr, comment: commentSuffix });
99
+ return {
100
+ en: workflowTemplate(templateKey, { name: nameEn, comment: commentSuffix }).en,
101
+ ar: nameAr
102
+ ? workflowTemplate(templateKey, { name: nameAr, comment: '' }).ar
103
+ : workflowTemplate(fallbackKey, { comment: '' }).ar,
104
+ };
106
105
  }
107
106
  function buildWorkflowApprovalWithoutName(templateKey, comment) {
108
107
  const commentSuffix = formatWorkflowCommentSuffix(comment);
109
- return workflowTemplate(templateKey, { comment: commentSuffix }, { comment: commentSuffix });
108
+ return {
109
+ en: workflowTemplate(templateKey, { comment: commentSuffix }).en,
110
+ ar: workflowTemplate(templateKey, { comment: '' }).ar,
111
+ };
112
+ }
113
+ function buildWorkflowFromNamePair(templateWithNameKey, templateWithoutNameKey, nameEn, nameAr) {
114
+ return {
115
+ en: workflowTemplate(templateWithNameKey, { name: nameEn }).en,
116
+ ar: nameAr
117
+ ? workflowTemplate(templateWithNameKey, { name: nameAr }).ar
118
+ : workflowTemplate(templateWithoutNameKey).ar,
119
+ };
110
120
  }
111
121
  function buildApprovalWorkflowContent(params) {
112
122
  const nameEn = params.roleName?.trim() || '';
113
- const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
123
+ const nameAr = pickArabicName(params.roleArabicName);
114
124
  if (params.status === 'Approved') {
115
125
  return nameEn
116
- ? buildWorkflowApprovalByName('request_approved_by', nameEn, nameAr, params.comment)
126
+ ? buildWorkflowApprovalByName('request_approved_by', 'request_approved', nameEn, nameAr, params.comment)
117
127
  : buildWorkflowApprovalWithoutName('request_approved', params.comment);
118
128
  }
119
129
  if (params.status === 'Rejected') {
120
130
  return nameEn
121
- ? buildWorkflowApprovalByName('request_rejected_by', nameEn, nameAr, params.comment)
131
+ ? buildWorkflowApprovalByName('request_rejected_by', 'request_rejected', nameEn, nameAr, params.comment)
122
132
  : buildWorkflowApprovalWithoutName('request_rejected', params.comment);
123
133
  }
124
134
  if (params.status === 'Returned for Modification') {
125
135
  return nameEn
126
- ? buildWorkflowApprovalByName('request_returned_for_modification_by', nameEn, nameAr, params.comment)
136
+ ? buildWorkflowApprovalByName('request_returned_for_modification_by', 'request_returned_for_modification', nameEn, nameAr, params.comment)
127
137
  : buildWorkflowApprovalWithoutName('request_returned_for_modification', params.comment);
128
138
  }
129
139
  const commentSuffix = formatWorkflowCommentSuffix(params.comment);
130
140
  if (nameEn) {
131
- return workflowTemplate('request_approval_pending_from', { name: nameEn, comment: commentSuffix }, { name: nameAr, comment: commentSuffix });
141
+ return {
142
+ en: workflowTemplate('request_approval_pending_from', { name: nameEn, comment: commentSuffix }).en,
143
+ ar: nameAr
144
+ ? workflowTemplate('request_approval_pending_from', { name: nameAr, comment: '' }).ar
145
+ : workflowTemplate('request_approval_pending', { comment: '' }).ar,
146
+ };
132
147
  }
133
- return workflowTemplate('request_approval_pending', { comment: commentSuffix }, { comment: commentSuffix });
148
+ return {
149
+ en: workflowTemplate('request_approval_pending', { comment: commentSuffix }).en,
150
+ ar: workflowTemplate('request_approval_pending', { comment: '' }).ar,
151
+ };
134
152
  }
135
153
  function buildHumanApprovalWorkflowContent(params) {
136
- const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
154
+ const withNameKey = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
155
+ const withoutNameKey = params.isFirst ? 'request_approval_in_progress' : 'request_approval_pending';
137
156
  if (params.roleName?.trim()) {
138
157
  const nameEn = params.roleName.trim();
139
- const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
140
- return workflowTemplate(key, { name: nameEn }, { name: nameAr });
158
+ const nameAr = pickArabicName(params.roleArabicName);
159
+ return buildWorkflowFromNamePair(withNameKey, withoutNameKey, nameEn, nameAr);
141
160
  }
142
161
  const { en, ar } = joinDeptSectionBilingualLabels(params.deptName, params.deptArabicName, params.sectionName, params.sectionArabicName);
143
162
  if (en) {
144
- return workflowTemplate(key, { name: en }, { name: ar });
163
+ return buildWorkflowFromNamePair(withNameKey, withoutNameKey, en, ar);
145
164
  }
146
165
  return params.isFirst
147
166
  ? workflowTemplate('request_approval_in_progress')
@@ -149,22 +168,32 @@ function buildHumanApprovalWorkflowContent(params) {
149
168
  }
150
169
  function buildApprovalChatMessage(params) {
151
170
  const roleEn = params.roleName?.trim() || 'Unknown Role';
152
- const roleAr = resolveBilingualName(roleEn, params.roleArabicName);
171
+ const roleAr = pickArabicName(params.roleArabicName);
153
172
  const commentSuffix = formatChatCommentSuffix(params.comment);
154
- let key;
173
+ let byRoleKey;
174
+ let withoutRoleKey;
155
175
  if (params.status === 'Approved') {
156
- key = 'request_approved_by_role';
176
+ byRoleKey = 'request_approved_by_role';
177
+ withoutRoleKey = 'request_approved';
157
178
  }
158
179
  else if (params.status === 'Rejected') {
159
- key = 'request_rejected_by_role';
180
+ byRoleKey = 'request_rejected_by_role';
181
+ withoutRoleKey = 'request_rejected';
160
182
  }
161
183
  else if (params.status === 'Returned for Modification') {
162
- key = 'request_returned_for_modification_by_role';
184
+ byRoleKey = 'request_returned_for_modification_by_role';
185
+ withoutRoleKey = 'request_returned_for_modification';
163
186
  }
164
187
  else {
165
- key = 'request_approved_by_role';
188
+ byRoleKey = 'request_approved_by_role';
189
+ withoutRoleKey = 'request_approved';
166
190
  }
167
- return chatTemplate(key, { roleName: roleEn, comment: commentSuffix }, { roleName: roleAr, comment: commentSuffix });
191
+ return {
192
+ en: chatTemplate(byRoleKey, { roleName: roleEn, comment: commentSuffix }).en,
193
+ ar: roleAr
194
+ ? chatTemplate(byRoleKey, { roleName: roleAr, comment: '' }).ar
195
+ : chatTemplate(withoutRoleKey, { comment: '' }).ar,
196
+ };
168
197
  }
169
198
  /** Resolve Arabic workflow content from a stored English content string (legacy rows). */
170
199
  function resolveWorkflowContentAr(content) {
@@ -178,8 +207,6 @@ function resolveWorkflowContentAr(content) {
178
207
  const commentIdx = normalized.indexOf(': ');
179
208
  if (commentIdx > 0) {
180
209
  const baseEn = normalized.slice(0, commentIdx).trim();
181
- const commentText = normalized.slice(commentIdx + 2).trim();
182
- const commentSuffix = formatWorkflowCommentSuffix(commentText);
183
210
  for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
184
211
  if (!tpl.en.includes('{{comment}}'))
185
212
  continue;
@@ -187,15 +214,29 @@ function resolveWorkflowContentAr(content) {
187
214
  if (withoutComment.includes('{{name}}')) {
188
215
  const staticPrefix = withoutComment.replace('{{name}}', '').trim();
189
216
  if (baseEn.startsWith(staticPrefix)) {
190
- const namePart = baseEn.slice(staticPrefix.length).trim();
191
- return interpolate(tpl.ar, { name: namePart, comment: commentSuffix });
217
+ const fallbackKey = Object.entries(WORKFLOW_TEMPLATES).find(([, t]) => t === tpl)?.[0] ?? '';
218
+ if (fallbackKey.includes('_by')) {
219
+ const genericKey = fallbackKey.replace('_by', '');
220
+ if (WORKFLOW_TEMPLATES[genericKey]) {
221
+ return WORKFLOW_TEMPLATES[genericKey].ar;
222
+ }
223
+ }
224
+ return tpl.ar.split('{{name}}')[0].trim();
192
225
  }
193
226
  }
194
227
  else if (withoutComment.trim() === baseEn) {
195
- return interpolate(tpl.ar, { comment: commentSuffix });
228
+ return interpolate(tpl.ar, { comment: '' });
196
229
  }
197
230
  }
198
231
  }
232
+ const inProgressPrefix = WORKFLOW_TEMPLATES.request_approval_in_progress_from?.en.split('{{name}}')[0].trim();
233
+ if (inProgressPrefix && normalized.startsWith(inProgressPrefix)) {
234
+ return WORKFLOW_TEMPLATES.request_approval_in_progress.ar;
235
+ }
236
+ const pendingPrefix = WORKFLOW_TEMPLATES.request_approval_pending_from?.en.split('{{name}}')[0].trim();
237
+ if (pendingPrefix && normalized.startsWith(pendingPrefix)) {
238
+ return WORKFLOW_TEMPLATES.request_approval_pending.ar;
239
+ }
199
240
  return null;
200
241
  }
201
242
  /** Resolve Arabic chat message from a stored English message string (legacy rows). */
@@ -212,18 +253,26 @@ function resolveChatMessageAr(message) {
212
253
  function enrichCancellationWorkflowLog(log) {
213
254
  const status = log.status != null ? String(log.status) : null;
214
255
  const content = log.content != null ? String(log.content) : null;
256
+ const storedAr = log.content_ar != null ? String(log.content_ar).trim() : '';
257
+ const hasEnglishInAr = storedAr && /[A-Za-z]/.test(storedAr);
215
258
  return {
216
259
  ...log,
217
- content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
260
+ content_ar: hasEnglishInAr
261
+ ? (resolveWorkflowContentAr(content) ?? storedAr)
262
+ : (log.content_ar ?? resolveWorkflowContentAr(content)),
218
263
  status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
219
264
  };
220
265
  }
221
266
  function enrichCancellationChatMessage(chat) {
222
267
  const status = chat.status != null ? String(chat.status) : null;
223
268
  const message = chat.message != null ? String(chat.message) : null;
269
+ const storedAr = chat.message_ar != null ? String(chat.message_ar).trim() : '';
270
+ const hasEnglishInAr = storedAr && /[A-Za-z]/.test(storedAr);
224
271
  return {
225
272
  ...chat,
226
- message_ar: chat.message_ar ?? resolveChatMessageAr(message),
273
+ message_ar: hasEnglishInAr
274
+ ? (resolveChatMessageAr(message) ?? storedAr)
275
+ : (chat.message_ar ?? resolveChatMessageAr(message)),
227
276
  status_ar: chat.status_ar ?? resolveChatStatusAr(status),
228
277
  };
229
278
  }
@@ -58,6 +58,10 @@
58
58
  "en": "Cancellation request submitted by {{userName}} ({{employeeId}})",
59
59
  "ar": "تم تقديم طلب الإلغاء بواسطة {{userName}} ({{employeeId}})"
60
60
  },
61
+ "cancellation_submitted": {
62
+ "en": "Cancellation request submitted",
63
+ "ar": "تم تقديم طلب الإلغاء"
64
+ },
61
65
  "request_received": {
62
66
  "en": "Request Received",
63
67
  "ar": "تم استلام الطلب"
@@ -73,6 +77,18 @@
73
77
  "request_returned_for_modification_by_role": {
74
78
  "en": "Request Returned for Modification by: {{roleName}}{{comment}}",
75
79
  "ar": "تم إرجاع الطلب للتعديل من قبل: {{roleName}}{{comment}}"
80
+ },
81
+ "request_approved": {
82
+ "en": "Request Approved{{comment}}",
83
+ "ar": "تمت الموافقة على الطلب{{comment}}"
84
+ },
85
+ "request_rejected": {
86
+ "en": "Request Rejected{{comment}}",
87
+ "ar": "تم رفض الطلب{{comment}}"
88
+ },
89
+ "request_returned_for_modification": {
90
+ "en": "Request Returned for Modification{{comment}}",
91
+ "ar": "تم إرجاع الطلب للتعديل{{comment}}"
76
92
  }
77
93
  },
78
94
  "workflowStatusLabels": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.279",
3
+ "version": "2.3.280",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -58,6 +58,10 @@
58
58
  "en": "Cancellation request submitted by {{userName}} ({{employeeId}})",
59
59
  "ar": "تم تقديم طلب الإلغاء بواسطة {{userName}} ({{employeeId}})"
60
60
  },
61
+ "cancellation_submitted": {
62
+ "en": "Cancellation request submitted",
63
+ "ar": "تم تقديم طلب الإلغاء"
64
+ },
61
65
  "request_received": {
62
66
  "en": "Request Received",
63
67
  "ar": "تم استلام الطلب"
@@ -73,6 +77,18 @@
73
77
  "request_returned_for_modification_by_role": {
74
78
  "en": "Request Returned for Modification by: {{roleName}}{{comment}}",
75
79
  "ar": "تم إرجاع الطلب للتعديل من قبل: {{roleName}}{{comment}}"
80
+ },
81
+ "request_approved": {
82
+ "en": "Request Approved{{comment}}",
83
+ "ar": "تمت الموافقة على الطلب{{comment}}"
84
+ },
85
+ "request_rejected": {
86
+ "en": "Request Rejected{{comment}}",
87
+ "ar": "تم رفض الطلب{{comment}}"
88
+ },
89
+ "request_returned_for_modification": {
90
+ "en": "Request Returned for Modification{{comment}}",
91
+ "ar": "تم إرجاع الطلب للتعديل{{comment}}"
76
92
  }
77
93
  },
78
94
  "workflowStatusLabels": {
@@ -66,18 +66,9 @@ export function resolveChatStatusAr(status: string | null | undefined): string |
66
66
  return CHAT_STATUS_LABELS[status] ?? status;
67
67
  }
68
68
 
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
-
79
- export function pickArabicName(arabicName?: string | null, fallback = ''): string {
80
- return arabicName?.trim() || fallback;
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() || '';
81
72
  }
82
73
 
83
74
  export function pickLocalizedName(
@@ -99,19 +90,17 @@ function joinDeptSectionLabels(
99
90
  return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
100
91
  }
101
92
 
102
- /** Parallel EN/AR dept-section labels (Arabic columns preferred; English fallback per field). */
93
+ /** EN from English columns; AR from Arabic columns only (no English in ar). */
103
94
  export function joinDeptSectionBilingualLabels(
104
95
  deptEn?: string | null,
105
96
  deptAr?: string | null,
106
97
  sectEn?: string | null,
107
98
  sectAr?: string | null,
108
99
  ): { 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 };
100
+ return {
101
+ en: joinDeptSectionLabels(deptEn, sectEn),
102
+ ar: joinDeptSectionLabels(deptAr, sectAr),
103
+ };
115
104
  }
116
105
 
117
106
  /** @deprecated Use joinDeptSectionBilingualLabels */
@@ -132,30 +121,55 @@ function formatChatCommentSuffix(comment?: string | null): string {
132
121
  return text ? ` - ${text}` : '';
133
122
  }
134
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
+
135
134
  function buildWorkflowApprovalByName(
136
- templateKey: 'request_approved_by' | 'request_rejected_by' | 'request_returned_for_modification_by',
135
+ templateKey: WorkflowByNameKey,
136
+ fallbackKey: WorkflowWithoutNameKey,
137
137
  nameEn: string,
138
138
  nameAr: string,
139
139
  comment?: string | null,
140
140
  ): BilingualText {
141
141
  const commentSuffix = formatWorkflowCommentSuffix(comment);
142
- return workflowTemplate(
143
- templateKey,
144
- { name: nameEn, comment: commentSuffix },
145
- { name: nameAr, comment: commentSuffix },
146
- );
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
+ };
147
148
  }
148
149
 
149
150
  function buildWorkflowApprovalWithoutName(
150
- templateKey: 'request_approved' | 'request_rejected' | 'request_returned_for_modification',
151
+ templateKey: WorkflowWithoutNameKey,
151
152
  comment?: string | null,
152
153
  ): BilingualText {
153
154
  const commentSuffix = formatWorkflowCommentSuffix(comment);
154
- return workflowTemplate(
155
- templateKey,
156
- { comment: commentSuffix },
157
- { comment: commentSuffix },
158
- );
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
+ };
159
173
  }
160
174
 
161
175
  export function buildApprovalWorkflowContent(params: {
@@ -165,40 +179,46 @@ export function buildApprovalWorkflowContent(params: {
165
179
  comment?: string | null;
166
180
  }): BilingualText {
167
181
  const nameEn = params.roleName?.trim() || '';
168
- const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
182
+ const nameAr = pickArabicName(params.roleArabicName);
169
183
 
170
184
  if (params.status === 'Approved') {
171
185
  return nameEn
172
- ? buildWorkflowApprovalByName('request_approved_by', nameEn, nameAr, params.comment)
186
+ ? buildWorkflowApprovalByName('request_approved_by', 'request_approved', nameEn, nameAr, params.comment)
173
187
  : buildWorkflowApprovalWithoutName('request_approved', params.comment);
174
188
  }
175
189
 
176
190
  if (params.status === 'Rejected') {
177
191
  return nameEn
178
- ? buildWorkflowApprovalByName('request_rejected_by', nameEn, nameAr, params.comment)
192
+ ? buildWorkflowApprovalByName('request_rejected_by', 'request_rejected', nameEn, nameAr, params.comment)
179
193
  : buildWorkflowApprovalWithoutName('request_rejected', params.comment);
180
194
  }
181
195
 
182
196
  if (params.status === 'Returned for Modification') {
183
197
  return nameEn
184
- ? buildWorkflowApprovalByName('request_returned_for_modification_by', nameEn, nameAr, params.comment)
198
+ ? buildWorkflowApprovalByName(
199
+ 'request_returned_for_modification_by',
200
+ 'request_returned_for_modification',
201
+ nameEn,
202
+ nameAr,
203
+ params.comment,
204
+ )
185
205
  : buildWorkflowApprovalWithoutName('request_returned_for_modification', params.comment);
186
206
  }
187
207
 
188
208
  const commentSuffix = formatWorkflowCommentSuffix(params.comment);
189
209
  if (nameEn) {
190
- return workflowTemplate(
191
- 'request_approval_pending_from',
192
- { name: nameEn, comment: commentSuffix },
193
- { name: nameAr, comment: commentSuffix },
194
- );
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
+ };
195
216
  }
196
217
 
197
- return workflowTemplate(
198
- 'request_approval_pending',
199
- { comment: commentSuffix },
200
- { comment: commentSuffix },
201
- );
218
+ return {
219
+ en: workflowTemplate('request_approval_pending', { comment: commentSuffix }).en,
220
+ ar: workflowTemplate('request_approval_pending', { comment: '' }).ar,
221
+ };
202
222
  }
203
223
 
204
224
  export function buildHumanApprovalWorkflowContent(params: {
@@ -210,12 +230,13 @@ export function buildHumanApprovalWorkflowContent(params: {
210
230
  sectionName?: string | null;
211
231
  sectionArabicName?: string | null;
212
232
  }): BilingualText {
213
- const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
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';
214
235
 
215
236
  if (params.roleName?.trim()) {
216
237
  const nameEn = params.roleName.trim();
217
- const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
218
- return workflowTemplate(key, { name: nameEn }, { name: nameAr });
238
+ const nameAr = pickArabicName(params.roleArabicName);
239
+ return buildWorkflowFromNamePair(withNameKey, withoutNameKey, nameEn, nameAr);
219
240
  }
220
241
 
221
242
  const { en, ar } = joinDeptSectionBilingualLabels(
@@ -225,7 +246,7 @@ export function buildHumanApprovalWorkflowContent(params: {
225
246
  params.sectionArabicName,
226
247
  );
227
248
  if (en) {
228
- return workflowTemplate(key, { name: en }, { name: ar });
249
+ return buildWorkflowFromNamePair(withNameKey, withoutNameKey, en, ar);
229
250
  }
230
251
 
231
252
  return params.isFirst
@@ -240,25 +261,31 @@ export function buildApprovalChatMessage(params: {
240
261
  comment?: string | null;
241
262
  }): BilingualText {
242
263
  const roleEn = params.roleName?.trim() || 'Unknown Role';
243
- const roleAr = resolveBilingualName(roleEn, params.roleArabicName);
264
+ const roleAr = pickArabicName(params.roleArabicName);
244
265
  const commentSuffix = formatChatCommentSuffix(params.comment);
245
266
 
246
- let key: string;
267
+ let byRoleKey: string;
268
+ let withoutRoleKey: string;
247
269
  if (params.status === 'Approved') {
248
- key = 'request_approved_by_role';
270
+ byRoleKey = 'request_approved_by_role';
271
+ withoutRoleKey = 'request_approved';
249
272
  } else if (params.status === 'Rejected') {
250
- key = 'request_rejected_by_role';
273
+ byRoleKey = 'request_rejected_by_role';
274
+ withoutRoleKey = 'request_rejected';
251
275
  } else if (params.status === 'Returned for Modification') {
252
- key = 'request_returned_for_modification_by_role';
276
+ byRoleKey = 'request_returned_for_modification_by_role';
277
+ withoutRoleKey = 'request_returned_for_modification';
253
278
  } else {
254
- key = 'request_approved_by_role';
279
+ byRoleKey = 'request_approved_by_role';
280
+ withoutRoleKey = 'request_approved';
255
281
  }
256
282
 
257
- return chatTemplate(
258
- key,
259
- { roleName: roleEn, comment: commentSuffix },
260
- { roleName: roleAr, comment: commentSuffix },
261
- );
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
+ };
262
289
  }
263
290
 
264
291
  /** Resolve Arabic workflow content from a stored English content string (legacy rows). */
@@ -273,8 +300,6 @@ export function resolveWorkflowContentAr(content: string | null | undefined): st
273
300
  const commentIdx = normalized.indexOf(': ');
274
301
  if (commentIdx > 0) {
275
302
  const baseEn = normalized.slice(0, commentIdx).trim();
276
- const commentText = normalized.slice(commentIdx + 2).trim();
277
- const commentSuffix = formatWorkflowCommentSuffix(commentText);
278
303
 
279
304
  for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
280
305
  if (!tpl.en.includes('{{comment}}')) continue;
@@ -282,15 +307,30 @@ export function resolveWorkflowContentAr(content: string | null | undefined): st
282
307
  if (withoutComment.includes('{{name}}')) {
283
308
  const staticPrefix = withoutComment.replace('{{name}}', '').trim();
284
309
  if (baseEn.startsWith(staticPrefix)) {
285
- const namePart = baseEn.slice(staticPrefix.length).trim();
286
- return interpolate(tpl.ar, { name: namePart, comment: commentSuffix });
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();
287
318
  }
288
319
  } else if (withoutComment.trim() === baseEn) {
289
- return interpolate(tpl.ar, { comment: commentSuffix });
320
+ return interpolate(tpl.ar, { comment: '' });
290
321
  }
291
322
  }
292
323
  }
293
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
+
294
334
  return null;
295
335
  }
296
336
 
@@ -309,9 +349,13 @@ export function resolveChatMessageAr(message: string | null | undefined): string
309
349
  export function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown> {
310
350
  const status = log.status != null ? String(log.status) : null;
311
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);
312
354
  return {
313
355
  ...log,
314
- content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
356
+ content_ar: hasEnglishInAr
357
+ ? (resolveWorkflowContentAr(content) ?? storedAr)
358
+ : (log.content_ar ?? resolveWorkflowContentAr(content)),
315
359
  status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
316
360
  };
317
361
  }
@@ -319,9 +363,13 @@ export function enrichCancellationWorkflowLog(log: Record<string, unknown>): Rec
319
363
  export function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown> {
320
364
  const status = chat.status != null ? String(chat.status) : null;
321
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);
322
368
  return {
323
369
  ...chat,
324
- message_ar: chat.message_ar ?? resolveChatMessageAr(message),
370
+ message_ar: hasEnglishInAr
371
+ ? (resolveChatMessageAr(message) ?? storedAr)
372
+ : (chat.message_ar ?? resolveChatMessageAr(message)),
325
373
  status_ar: chat.status_ar ?? resolveChatStatusAr(status),
326
374
  };
327
375
  }