@platform-modules/civil-aviation-authority 2.3.277 → 2.3.278

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env CHANGED
@@ -2,7 +2,7 @@ DB_HOST=164.52.222.169
2
2
  DB_PORT=5432
3
3
  DB_USER=postgres_admin_user
4
4
  DB_PASS=pg_admin_user_pwd_caa_fa_$%^&OIukhjgcvbn
5
- DB_NAME=CAA
5
+ DB_NAME=CAA_DEV
6
6
 
7
7
 
8
8
  # DB_HOST=localhost
@@ -0,0 +1,37 @@
1
+ import type { EntityManager } from 'typeorm';
2
+ import { CancellationMessageType } from '../models/CancellationChatModel';
3
+ import { CancellationWorkFlowStatus } from '../models/CancellationWorkflowModel';
4
+ export type CancellationWorkflowRowInput = {
5
+ request_id: number;
6
+ service_id?: number | null;
7
+ sub_service_id?: number | null;
8
+ content: string;
9
+ content_ar?: string | null;
10
+ status: CancellationWorkFlowStatus;
11
+ status_ar?: string | null;
12
+ user_id?: number | null;
13
+ role_id?: number | null;
14
+ department_id?: number | null;
15
+ section_id?: number | null;
16
+ step_order?: number | null;
17
+ created_by?: number | null;
18
+ };
19
+ export type CancellationChatRowInput = {
20
+ request_id: number;
21
+ service_id?: number | null;
22
+ sub_service_id?: number | null;
23
+ user_id: number;
24
+ role_id?: number | null;
25
+ message: string;
26
+ message_ar?: string | null;
27
+ messageType?: CancellationMessageType;
28
+ status?: string | null;
29
+ status_ar?: string | null;
30
+ created_by?: number | string | null;
31
+ };
32
+ /** Force-persist Arabic workflow columns (guards against stale TypeORM entity metadata). */
33
+ export declare function patchCancellationWorkflowArabic(manager: EntityManager, workflowId: number, contentAr: string | null, statusAr: string | null): Promise<void>;
34
+ /** Force-persist Arabic chat columns (guards against stale TypeORM entity metadata). */
35
+ export declare function patchCancellationChatArabic(manager: EntityManager, chatId: number, messageAr: string | null, statusAr: string | null): Promise<void>;
36
+ export declare function insertCancellationWorkflowRow(manager: EntityManager, input: CancellationWorkflowRowInput): Promise<number>;
37
+ export declare function insertCancellationChatRow(manager: EntityManager, input: CancellationChatRowInput): Promise<number>;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patchCancellationWorkflowArabic = patchCancellationWorkflowArabic;
4
+ exports.patchCancellationChatArabic = patchCancellationChatArabic;
5
+ exports.insertCancellationWorkflowRow = insertCancellationWorkflowRow;
6
+ exports.insertCancellationChatRow = insertCancellationChatRow;
7
+ const CancellationChatModel_1 = require("../models/CancellationChatModel");
8
+ const CancellationWorkflowModel_1 = require("../models/CancellationWorkflowModel");
9
+ const workflow_chat_i18n_1 = require("../i18n/workflow-chat-i18n");
10
+ function resolveWorkflowRowId(insertResult) {
11
+ const id = insertResult.raw?.[0]?.id ?? insertResult.identifiers?.[0]?.id;
12
+ if (!id) {
13
+ throw new Error('Failed to insert cancellation workflow row');
14
+ }
15
+ return id;
16
+ }
17
+ function resolveChatRowId(insertResult) {
18
+ const id = insertResult.raw?.[0]?.id ?? insertResult.identifiers?.[0]?.id;
19
+ if (!id) {
20
+ throw new Error('Failed to insert cancellation chat row');
21
+ }
22
+ return id;
23
+ }
24
+ /** Force-persist Arabic workflow columns (guards against stale TypeORM entity metadata). */
25
+ async function patchCancellationWorkflowArabic(manager, workflowId, contentAr, statusAr) {
26
+ await manager.query(`UPDATE cancellation_workflows
27
+ SET content_ar = $1,
28
+ status_ar = $2,
29
+ updated_at = CURRENT_TIMESTAMP
30
+ WHERE id = $3`, [contentAr, statusAr, workflowId]);
31
+ }
32
+ /** Force-persist Arabic chat columns (guards against stale TypeORM entity metadata). */
33
+ async function patchCancellationChatArabic(manager, chatId, messageAr, statusAr) {
34
+ await manager.query(`UPDATE cancellation_chats
35
+ SET message_ar = $1,
36
+ status_ar = $2,
37
+ updated_at = CURRENT_TIMESTAMP
38
+ WHERE id = $3`, [messageAr, statusAr, chatId]);
39
+ }
40
+ async function insertCancellationWorkflowRow(manager, input) {
41
+ const contentAr = input.content_ar ?? null;
42
+ const statusAr = input.status_ar ?? (0, workflow_chat_i18n_1.resolveWorkflowStatusAr)(input.status);
43
+ const insertResult = await manager
44
+ .createQueryBuilder()
45
+ .insert()
46
+ .into(CancellationWorkflowModel_1.CancellationWorkFlow)
47
+ .values({
48
+ ...input,
49
+ content_ar: contentAr,
50
+ status_ar: statusAr,
51
+ })
52
+ .returning('id')
53
+ .execute();
54
+ const id = resolveWorkflowRowId(insertResult);
55
+ await patchCancellationWorkflowArabic(manager, id, contentAr, statusAr);
56
+ return id;
57
+ }
58
+ async function insertCancellationChatRow(manager, input) {
59
+ const messageAr = input.message_ar ?? null;
60
+ const statusAr = input.status_ar ?? (0, workflow_chat_i18n_1.resolveChatStatusAr)(input.status ?? null);
61
+ const messageType = input.messageType ?? CancellationChatModel_1.CancellationMessageType.TEXT;
62
+ const insertResult = await manager
63
+ .createQueryBuilder()
64
+ .insert()
65
+ .into(CancellationChatModel_1.CancellationRequestChat)
66
+ .values({
67
+ ...input,
68
+ message_ar: messageAr,
69
+ status_ar: statusAr,
70
+ messageType,
71
+ })
72
+ .returning('id')
73
+ .execute();
74
+ const id = resolveChatRowId(insertResult);
75
+ await patchCancellationChatArabic(manager, id, messageAr, statusAr);
76
+ return id;
77
+ }
@@ -10,12 +10,17 @@ type WorkflowChatI18nCatalog = {
10
10
  };
11
11
  /** Read-only view of the shared workflow/chat translation catalog. */
12
12
  export declare function getWorkflowChatI18nCatalog(): WorkflowChatI18nCatalog;
13
- export declare function buildBilingualFromTemplate(templateCatalog: Record<string, BilingualText>, key: string, params?: Record<string, string>): BilingualText;
14
- export declare function workflowTemplate(key: string, params?: Record<string, string>): BilingualText;
15
- export declare function chatTemplate(key: string, params?: Record<string, string>): BilingualText;
13
+ export declare function buildBilingualFromTemplate(templateCatalog: Record<string, BilingualText>, key: string, params?: Record<string, string>, arParams?: Record<string, string>): BilingualText;
14
+ export declare function workflowTemplate(key: string, params?: Record<string, string>, arParams?: Record<string, string>): BilingualText;
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. */
19
+ export declare function pickArabicName(arabicName?: string | null, fallback?: string): string;
20
+ /** Prefer Arabic for Arabic locale; English for English locale. */
18
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). */
23
+ export declare function joinDeptSectionArabicLabels(deptArabic?: string | null, sectionArabic?: string | null): string;
19
24
  export declare function buildApprovalWorkflowContent(params: {
20
25
  status: 'Approved' | 'Rejected' | 'Pending' | string;
21
26
  roleName?: string | null;
@@ -35,6 +40,10 @@ export declare function buildApprovalChatMessage(params: {
35
40
  roleArabicName?: string | null;
36
41
  comment?: string | null;
37
42
  }): BilingualText;
43
+ /** Resolve Arabic workflow content from a stored English content string. */
44
+ export declare function resolveWorkflowContentAr(content: string | null | undefined): string | null;
45
+ /** Resolve Arabic chat message from a stored English message string. */
46
+ export declare function resolveChatMessageAr(message: string | null | undefined): string | null;
38
47
  export declare function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown>;
39
48
  export declare function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown>;
40
49
  export {};
@@ -9,10 +9,14 @@ exports.workflowTemplate = workflowTemplate;
9
9
  exports.chatTemplate = chatTemplate;
10
10
  exports.resolveWorkflowStatusAr = resolveWorkflowStatusAr;
11
11
  exports.resolveChatStatusAr = resolveChatStatusAr;
12
+ exports.pickArabicName = pickArabicName;
12
13
  exports.pickLocalizedName = pickLocalizedName;
14
+ exports.joinDeptSectionArabicLabels = joinDeptSectionArabicLabels;
13
15
  exports.buildApprovalWorkflowContent = buildApprovalWorkflowContent;
14
16
  exports.buildHumanApprovalWorkflowContent = buildHumanApprovalWorkflowContent;
15
17
  exports.buildApprovalChatMessage = buildApprovalChatMessage;
18
+ exports.resolveWorkflowContentAr = resolveWorkflowContentAr;
19
+ exports.resolveChatMessageAr = resolveChatMessageAr;
16
20
  exports.enrichCancellationWorkflowLog = enrichCancellationWorkflowLog;
17
21
  exports.enrichCancellationChatMessage = enrichCancellationChatMessage;
18
22
  const workflow_chat_i18n_json_1 = __importDefault(require("./workflow-chat-i18n.json"));
@@ -27,21 +31,22 @@ function getWorkflowChatI18nCatalog() {
27
31
  function interpolate(template, params = {}) {
28
32
  return template.replace(/\{\{(\w+)\}\}/g, (_, key) => params[key] ?? '');
29
33
  }
30
- function buildBilingualFromTemplate(templateCatalog, key, params = {}) {
34
+ function buildBilingualFromTemplate(templateCatalog, key, params = {}, arParams) {
31
35
  const tpl = templateCatalog[key];
32
36
  if (!tpl) {
33
37
  return { en: key, ar: key };
34
38
  }
39
+ const arabicParams = arParams ?? params;
35
40
  return {
36
41
  en: interpolate(tpl.en, params),
37
- ar: interpolate(tpl.ar, params),
42
+ ar: interpolate(tpl.ar, arabicParams),
38
43
  };
39
44
  }
40
- function workflowTemplate(key, params = {}) {
41
- return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params);
45
+ function workflowTemplate(key, params = {}, arParams) {
46
+ return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params, arParams);
42
47
  }
43
- function chatTemplate(key, params = {}) {
44
- return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params);
48
+ function chatTemplate(key, params = {}, arParams) {
49
+ return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params, arParams);
45
50
  }
46
51
  function resolveWorkflowStatusAr(status) {
47
52
  if (status == null || status === '')
@@ -53,6 +58,11 @@ function resolveChatStatusAr(status) {
53
58
  return null;
54
59
  return CHAT_STATUS_LABELS[status] ?? status;
55
60
  }
61
+ /** Prefer Arabic name for Arabic content; never fall back to English. */
62
+ function pickArabicName(arabicName, fallback = '') {
63
+ return arabicName?.trim() || fallback;
64
+ }
65
+ /** Prefer Arabic for Arabic locale; English for English locale. */
56
66
  function pickLocalizedName(englishName, arabicName, fallback = '') {
57
67
  const ar = arabicName?.trim();
58
68
  if (ar)
@@ -62,52 +72,63 @@ function pickLocalizedName(englishName, arabicName, fallback = '') {
62
72
  return en;
63
73
  return fallback;
64
74
  }
75
+ function joinDeptSectionLabels(dept, section) {
76
+ return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
77
+ }
78
+ /** Build dept/section label from Arabic columns only (for content_ar). */
79
+ function joinDeptSectionArabicLabels(deptArabic, sectionArabic) {
80
+ return joinDeptSectionLabels(deptArabic, sectionArabic);
81
+ }
65
82
  function buildApprovalWorkflowContent(params) {
66
83
  const nameEn = params.roleName?.trim() || '';
67
- const nameAr = pickLocalizedName(params.roleName, params.roleArabicName, nameEn || 'Unknown');
84
+ const nameAr = pickArabicName(params.roleArabicName);
68
85
  let base;
69
86
  if (params.status === 'Approved') {
70
87
  base = nameEn
71
88
  ? workflowTemplate('request_approved_by', { name: nameEn })
72
89
  : workflowTemplate('request_approved');
73
- if (nameEn) {
74
- base.ar = workflowTemplate('request_approved_by', { name: nameAr }).ar;
75
- }
90
+ base.ar = nameAr
91
+ ? workflowTemplate('request_approved_by', { name: nameAr }).ar
92
+ : workflowTemplate('request_approved').ar;
76
93
  }
77
94
  else if (params.status === 'Rejected') {
78
95
  base = nameEn
79
96
  ? workflowTemplate('request_rejected_by', { name: nameEn })
80
97
  : workflowTemplate('request_rejected');
81
- if (nameEn) {
82
- base.ar = workflowTemplate('request_rejected_by', { name: nameAr }).ar;
83
- }
98
+ base.ar = nameAr
99
+ ? workflowTemplate('request_rejected_by', { name: nameAr }).ar
100
+ : workflowTemplate('request_rejected').ar;
84
101
  }
85
102
  else {
86
103
  base = nameEn
87
104
  ? workflowTemplate('request_approval_pending_from', { name: nameEn })
88
105
  : workflowTemplate('request_approval_pending');
89
- if (nameEn) {
90
- base.ar = workflowTemplate('request_approval_pending_from', { name: nameAr }).ar;
91
- }
106
+ base.ar = nameAr
107
+ ? workflowTemplate('request_approval_pending_from', { name: nameAr }).ar
108
+ : workflowTemplate('request_approval_pending').ar;
92
109
  }
93
110
  const comment = params.comment?.trim();
94
111
  if (!comment)
95
112
  return base;
96
113
  return {
97
114
  en: `${base.en}: ${comment}`,
98
- ar: `${base.ar}: ${comment}`,
115
+ ar: base.ar,
99
116
  };
100
117
  }
101
118
  function buildHumanApprovalWorkflowContent(params) {
102
119
  const nameEn = params.roleName?.trim() || params.deptSectionEn?.trim() || '';
103
120
  const nameAr = params.roleName
104
- ? pickLocalizedName(params.roleName, params.roleArabicName, nameEn)
105
- : params.deptSectionAr?.trim() || params.deptSectionEn?.trim() || nameEn;
121
+ ? pickArabicName(params.roleArabicName)
122
+ : (params.deptSectionAr?.trim() || '');
106
123
  if (nameEn) {
107
124
  const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
108
125
  return {
109
126
  en: workflowTemplate(key, { name: nameEn }).en,
110
- ar: workflowTemplate(key, { name: nameAr || nameEn }).ar,
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,
111
132
  };
112
133
  }
113
134
  return params.isFirst
@@ -116,25 +137,54 @@ function buildHumanApprovalWorkflowContent(params) {
116
137
  }
117
138
  function buildApprovalChatMessage(params) {
118
139
  const roleEn = params.roleName?.trim() || 'Unknown Role';
119
- const roleAr = pickLocalizedName(params.roleName, params.roleArabicName, roleEn);
140
+ const roleAr = pickArabicName(params.roleArabicName);
120
141
  const commentSuffix = params.comment?.trim() ? ` - ${params.comment.trim()}` : '';
121
142
  const key = params.status === 'Approved' ? 'request_approved_by_role' : 'request_rejected_by_role';
122
143
  return {
123
144
  en: chatTemplate(key, { roleName: roleEn, comment: commentSuffix }).en,
124
- ar: chatTemplate(key, { roleName: roleAr, comment: commentSuffix }).ar,
145
+ ar: chatTemplate(key, { roleName: roleAr, comment: '' }).ar,
125
146
  };
126
147
  }
148
+ /** Resolve Arabic workflow content from a stored English content string. */
149
+ function resolveWorkflowContentAr(content) {
150
+ if (content == null || content.trim() === '')
151
+ return null;
152
+ const normalized = content.trim();
153
+ for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
154
+ if (tpl.en === normalized)
155
+ return tpl.ar;
156
+ }
157
+ return null;
158
+ }
159
+ /** Resolve Arabic chat message from a stored English message string. */
160
+ function resolveChatMessageAr(message) {
161
+ if (message == null || message.trim() === '')
162
+ return null;
163
+ const normalized = message.trim();
164
+ for (const tpl of Object.values(CHAT_TEMPLATES)) {
165
+ if (tpl.en === normalized)
166
+ return tpl.ar;
167
+ }
168
+ if (normalized === 'Request Received') {
169
+ return CHAT_TEMPLATES.request_received?.ar ?? null;
170
+ }
171
+ return null;
172
+ }
127
173
  function enrichCancellationWorkflowLog(log) {
128
174
  const status = log.status != null ? String(log.status) : null;
175
+ const content = log.content != null ? String(log.content) : null;
129
176
  return {
130
177
  ...log,
178
+ content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
131
179
  status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
132
180
  };
133
181
  }
134
182
  function enrichCancellationChatMessage(chat) {
135
183
  const status = chat.status != null ? String(chat.status) : null;
184
+ const message = chat.message != null ? String(chat.message) : null;
136
185
  return {
137
186
  ...chat,
187
+ message_ar: chat.message_ar ?? resolveChatMessageAr(message),
138
188
  status_ar: chat.status_ar ?? resolveChatStatusAr(status),
139
189
  };
140
190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.277",
3
+ "version": "2.3.278",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -26,24 +26,34 @@ function interpolate(template: string, params: Record<string, string> = {}): str
26
26
  export function buildBilingualFromTemplate(
27
27
  templateCatalog: Record<string, BilingualText>,
28
28
  key: string,
29
- params: Record<string, string> = {}
29
+ params: Record<string, string> = {},
30
+ arParams?: Record<string, string>,
30
31
  ): BilingualText {
31
32
  const tpl = templateCatalog[key];
32
33
  if (!tpl) {
33
34
  return { en: key, ar: key };
34
35
  }
36
+ const arabicParams = arParams ?? params;
35
37
  return {
36
38
  en: interpolate(tpl.en, params),
37
- ar: interpolate(tpl.ar, params),
39
+ ar: interpolate(tpl.ar, arabicParams),
38
40
  };
39
41
  }
40
42
 
41
- export function workflowTemplate(key: string, params: Record<string, string> = {}): BilingualText {
42
- return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params);
43
+ export function workflowTemplate(
44
+ key: string,
45
+ params: Record<string, string> = {},
46
+ arParams?: Record<string, string>,
47
+ ): BilingualText {
48
+ return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params, arParams);
43
49
  }
44
50
 
45
- export function chatTemplate(key: string, params: Record<string, string> = {}): BilingualText {
46
- return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params);
51
+ export function chatTemplate(
52
+ key: string,
53
+ params: Record<string, string> = {},
54
+ arParams?: Record<string, string>,
55
+ ): BilingualText {
56
+ return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params, arParams);
47
57
  }
48
58
 
49
59
  export function resolveWorkflowStatusAr(status: string | null | undefined): string | null {
@@ -56,6 +66,12 @@ export function resolveChatStatusAr(status: string | null | undefined): string |
56
66
  return CHAT_STATUS_LABELS[status] ?? status;
57
67
  }
58
68
 
69
+ /** Prefer Arabic name for Arabic content; never fall back to English. */
70
+ export function pickArabicName(arabicName?: string | null, fallback = ''): string {
71
+ return arabicName?.trim() || fallback;
72
+ }
73
+
74
+ /** Prefer Arabic for Arabic locale; English for English locale. */
59
75
  export function pickLocalizedName(
60
76
  englishName?: string | null,
61
77
  arabicName?: string | null,
@@ -68,6 +84,21 @@ export function pickLocalizedName(
68
84
  return fallback;
69
85
  }
70
86
 
87
+ function joinDeptSectionLabels(
88
+ dept?: string | null,
89
+ section?: string | null,
90
+ ): string {
91
+ return [dept, section].map((s) => s?.trim()).filter(Boolean).join(' - ');
92
+ }
93
+
94
+ /** Build dept/section label from Arabic columns only (for content_ar). */
95
+ export function joinDeptSectionArabicLabels(
96
+ deptArabic?: string | null,
97
+ sectionArabic?: string | null,
98
+ ): string {
99
+ return joinDeptSectionLabels(deptArabic, sectionArabic);
100
+ }
101
+
71
102
  export function buildApprovalWorkflowContent(params: {
72
103
  status: 'Approved' | 'Rejected' | 'Pending' | string;
73
104
  roleName?: string | null;
@@ -75,30 +106,30 @@ export function buildApprovalWorkflowContent(params: {
75
106
  comment?: string | null;
76
107
  }): BilingualText {
77
108
  const nameEn = params.roleName?.trim() || '';
78
- const nameAr = pickLocalizedName(params.roleName, params.roleArabicName, nameEn || 'Unknown');
109
+ const nameAr = pickArabicName(params.roleArabicName);
79
110
 
80
111
  let base: BilingualText;
81
112
  if (params.status === 'Approved') {
82
113
  base = nameEn
83
114
  ? workflowTemplate('request_approved_by', { name: nameEn })
84
115
  : workflowTemplate('request_approved');
85
- if (nameEn) {
86
- base.ar = workflowTemplate('request_approved_by', { name: nameAr }).ar;
87
- }
116
+ base.ar = nameAr
117
+ ? workflowTemplate('request_approved_by', { name: nameAr }).ar
118
+ : workflowTemplate('request_approved').ar;
88
119
  } else if (params.status === 'Rejected') {
89
120
  base = nameEn
90
121
  ? workflowTemplate('request_rejected_by', { name: nameEn })
91
122
  : workflowTemplate('request_rejected');
92
- if (nameEn) {
93
- base.ar = workflowTemplate('request_rejected_by', { name: nameAr }).ar;
94
- }
123
+ base.ar = nameAr
124
+ ? workflowTemplate('request_rejected_by', { name: nameAr }).ar
125
+ : workflowTemplate('request_rejected').ar;
95
126
  } else {
96
127
  base = nameEn
97
128
  ? workflowTemplate('request_approval_pending_from', { name: nameEn })
98
129
  : workflowTemplate('request_approval_pending');
99
- if (nameEn) {
100
- base.ar = workflowTemplate('request_approval_pending_from', { name: nameAr }).ar;
101
- }
130
+ base.ar = nameAr
131
+ ? workflowTemplate('request_approval_pending_from', { name: nameAr }).ar
132
+ : workflowTemplate('request_approval_pending').ar;
102
133
  }
103
134
 
104
135
  const comment = params.comment?.trim();
@@ -106,7 +137,7 @@ export function buildApprovalWorkflowContent(params: {
106
137
 
107
138
  return {
108
139
  en: `${base.en}: ${comment}`,
109
- ar: `${base.ar}: ${comment}`,
140
+ ar: base.ar,
110
141
  };
111
142
  }
112
143
 
@@ -119,14 +150,18 @@ export function buildHumanApprovalWorkflowContent(params: {
119
150
  }): BilingualText {
120
151
  const nameEn = params.roleName?.trim() || params.deptSectionEn?.trim() || '';
121
152
  const nameAr = params.roleName
122
- ? pickLocalizedName(params.roleName, params.roleArabicName, nameEn)
123
- : params.deptSectionAr?.trim() || params.deptSectionEn?.trim() || nameEn;
153
+ ? pickArabicName(params.roleArabicName)
154
+ : (params.deptSectionAr?.trim() || '');
124
155
 
125
156
  if (nameEn) {
126
157
  const key = params.isFirst ? 'request_approval_in_progress_from' : 'request_approval_pending_from';
127
158
  return {
128
159
  en: workflowTemplate(key, { name: nameEn }).en,
129
- ar: workflowTemplate(key, { name: nameAr || nameEn }).ar,
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,
130
165
  };
131
166
  }
132
167
 
@@ -142,28 +177,55 @@ export function buildApprovalChatMessage(params: {
142
177
  comment?: string | null;
143
178
  }): BilingualText {
144
179
  const roleEn = params.roleName?.trim() || 'Unknown Role';
145
- const roleAr = pickLocalizedName(params.roleName, params.roleArabicName, roleEn);
180
+ const roleAr = pickArabicName(params.roleArabicName);
146
181
  const commentSuffix = params.comment?.trim() ? ` - ${params.comment.trim()}` : '';
147
182
  const key = params.status === 'Approved' ? 'request_approved_by_role' : 'request_rejected_by_role';
148
183
 
149
184
  return {
150
185
  en: chatTemplate(key, { roleName: roleEn, comment: commentSuffix }).en,
151
- ar: chatTemplate(key, { roleName: roleAr, comment: commentSuffix }).ar,
186
+ ar: chatTemplate(key, { roleName: roleAr, comment: '' }).ar,
152
187
  };
153
188
  }
154
189
 
190
+ /** Resolve Arabic workflow content from a stored English content string. */
191
+ export function resolveWorkflowContentAr(content: string | null | undefined): string | null {
192
+ if (content == null || content.trim() === '') return null;
193
+ const normalized = content.trim();
194
+ for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
195
+ if (tpl.en === normalized) return tpl.ar;
196
+ }
197
+ return null;
198
+ }
199
+
200
+ /** Resolve Arabic chat message from a stored English message string. */
201
+ export function resolveChatMessageAr(message: string | null | undefined): string | null {
202
+ if (message == null || message.trim() === '') return null;
203
+ const normalized = message.trim();
204
+ for (const tpl of Object.values(CHAT_TEMPLATES)) {
205
+ if (tpl.en === normalized) return tpl.ar;
206
+ }
207
+ if (normalized === 'Request Received') {
208
+ return CHAT_TEMPLATES.request_received?.ar ?? null;
209
+ }
210
+ return null;
211
+ }
212
+
155
213
  export function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown> {
156
214
  const status = log.status != null ? String(log.status) : null;
215
+ const content = log.content != null ? String(log.content) : null;
157
216
  return {
158
217
  ...log,
218
+ content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
159
219
  status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
160
220
  };
161
221
  }
162
222
 
163
223
  export function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown> {
164
224
  const status = chat.status != null ? String(chat.status) : null;
225
+ const message = chat.message != null ? String(chat.message) : null;
165
226
  return {
166
227
  ...chat,
228
+ message_ar: chat.message_ar ?? resolveChatMessageAr(message),
167
229
  status_ar: chat.status_ar ?? resolveChatStatusAr(status),
168
230
  };
169
231
  }