@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
- /** Prefer Arabic name for Arabic content; never fall back to English. */
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
- /** Build dept/section label from Arabic columns only (for content_ar). */
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
- deptSectionEn?: string | null;
35
- deptSectionAr?: string | null;
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
- /** Prefer Arabic name for Arabic content; never fall back to English. */
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
- /** Build dept/section label from Arabic columns only (for content_ar). */
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 = pickArabicName(params.roleArabicName);
85
- let base;
113
+ const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
86
114
  if (params.status === 'Approved') {
87
- base = nameEn
88
- ? workflowTemplate('request_approved_by', { name: nameEn })
89
- : workflowTemplate('request_approved');
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
- else if (params.status === 'Rejected') {
95
- base = nameEn
96
- ? workflowTemplate('request_rejected_by', { name: nameEn })
97
- : workflowTemplate('request_rejected');
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
- else {
103
- base = nameEn
104
- ? workflowTemplate('request_approval_pending_from', { name: nameEn })
105
- : workflowTemplate('request_approval_pending');
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 comment = params.comment?.trim();
111
- if (!comment)
112
- return base;
113
- return {
114
- en: `${base.en}: ${comment}`,
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 nameEn = params.roleName?.trim() || params.deptSectionEn?.trim() || '';
120
- const nameAr = params.roleName
121
- ? pickArabicName(params.roleArabicName)
122
- : (params.deptSectionAr?.trim() || '');
123
- if (nameEn) {
124
- const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
125
- return {
126
- en: workflowTemplate(key, { name: nameEn }).en,
127
- ar: nameAr
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 = pickArabicName(params.roleArabicName);
141
- const commentSuffix = params.comment?.trim() ? ` - ${params.comment.trim()}` : '';
142
- const key = params.status === 'Approved' ? 'request_approved_by_role' : 'request_rejected_by_role';
143
- return {
144
- en: chatTemplate(key, { roleName: roleEn, comment: commentSuffix }).en,
145
- ar: chatTemplate(key, { roleName: roleAr, comment: '' }).ar,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.278",
3
+ "version": "2.3.279",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -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
- /** Prefer Arabic name for Arabic content; never fall back to English. */
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
- /** Build dept/section label from Arabic columns only (for content_ar). */
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 = pickArabicName(params.roleArabicName);
168
+ const nameAr = resolveBilingualName(nameEn, params.roleArabicName);
110
169
 
111
- let base: BilingualText;
112
170
  if (params.status === 'Approved') {
113
- base = nameEn
114
- ? workflowTemplate('request_approved_by', { name: nameEn })
115
- : workflowTemplate('request_approved');
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
- const comment = params.comment?.trim();
136
- if (!comment) return base;
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
- return {
139
- en: `${base.en}: ${comment}`,
140
- ar: base.ar,
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
- deptSectionEn?: string | null;
149
- deptSectionAr?: string | null;
208
+ deptName?: string | null;
209
+ deptArabicName?: string | null;
210
+ sectionName?: string | null;
211
+ sectionArabicName?: string | null;
150
212
  }): BilingualText {
151
- const nameEn = params.roleName?.trim() || params.deptSectionEn?.trim() || '';
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 (nameEn) {
157
- const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
158
- return {
159
- en: workflowTemplate(key, { name: nameEn }).en,
160
- ar: nameAr
161
- ? workflowTemplate(key, { name: nameAr }).ar
162
- : params.isFirst
163
- ? workflowTemplate('request_approval_in_progress').ar
164
- : workflowTemplate('request_approval_pending').ar,
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 = pickArabicName(params.roleArabicName);
181
- const commentSuffix = params.comment?.trim() ? ` - ${params.comment.trim()}` : '';
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
- return {
185
- en: chatTemplate(key, { roleName: roleEn, comment: commentSuffix }).en,
186
- ar: chatTemplate(key, { roleName: roleAr, comment: '' }).ar,
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
- if (normalized === 'Request Received') {
208
- return CHAT_TEMPLATES.request_received?.ar ?? null;
209
- }
305
+
210
306
  return null;
211
307
  }
212
308