@platform-modules/civil-aviation-authority 2.3.274 → 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
@@ -1,8 +1,8 @@
1
- # DB_HOST=164.52.222.169
2
- # DB_PORT=5432
3
- # DB_USER=postgres_admin_user
4
- # DB_PASS=pg_admin_user_pwd_caa_fa_$%^&OIukhjgcvbn
5
- # DB_NAME=CAA
1
+ DB_HOST=164.52.222.169
2
+ DB_PORT=5432
3
+ DB_USER=postgres_admin_user
4
+ DB_PASS=pg_admin_user_pwd_caa_fa_$%^&OIukhjgcvbn
5
+ DB_NAME=CAA_DEV
6
6
 
7
7
 
8
8
  # DB_HOST=localhost
@@ -11,8 +11,8 @@
11
11
  # DB_PASS=stevejobs
12
12
  # DB_NAME=CAA
13
13
 
14
- DB_HOST=10.44.3.201
15
- DB_PORT=5432
16
- DB_USER=appuser
17
- DB_PASS=HamaCaa7489
18
- DB_NAME=caa_prd_db
14
+ # DB_HOST=10.44.3.201
15
+ # DB_PORT=5432
16
+ # DB_USER=appuser
17
+ # DB_PASS=HamaCaa7489
18
+ # DB_NAME=caa_prd_db
@@ -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
+ }
@@ -0,0 +1,49 @@
1
+ export type BilingualText = {
2
+ en: string;
3
+ ar: string;
4
+ };
5
+ type WorkflowChatI18nCatalog = {
6
+ workflowTemplates: Record<string, BilingualText>;
7
+ chatTemplates: Record<string, BilingualText>;
8
+ workflowStatusLabels: Record<string, string>;
9
+ chatStatusLabels: Record<string, string>;
10
+ };
11
+ /** Read-only view of the shared workflow/chat translation catalog. */
12
+ export declare function getWorkflowChatI18nCatalog(): WorkflowChatI18nCatalog;
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
+ export declare function resolveWorkflowStatusAr(status: string | null | undefined): string | null;
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. */
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;
24
+ export declare function buildApprovalWorkflowContent(params: {
25
+ status: 'Approved' | 'Rejected' | 'Pending' | string;
26
+ roleName?: string | null;
27
+ roleArabicName?: string | null;
28
+ comment?: string | null;
29
+ }): BilingualText;
30
+ export declare function buildHumanApprovalWorkflowContent(params: {
31
+ isFirst: boolean;
32
+ roleName?: string | null;
33
+ roleArabicName?: string | null;
34
+ deptSectionEn?: string | null;
35
+ deptSectionAr?: string | null;
36
+ }): BilingualText;
37
+ export declare function buildApprovalChatMessage(params: {
38
+ status: 'Approved' | 'Rejected' | string;
39
+ roleName?: string | null;
40
+ roleArabicName?: string | null;
41
+ comment?: string | null;
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;
47
+ export declare function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown>;
48
+ export declare function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown>;
49
+ export {};
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getWorkflowChatI18nCatalog = getWorkflowChatI18nCatalog;
7
+ exports.buildBilingualFromTemplate = buildBilingualFromTemplate;
8
+ exports.workflowTemplate = workflowTemplate;
9
+ exports.chatTemplate = chatTemplate;
10
+ exports.resolveWorkflowStatusAr = resolveWorkflowStatusAr;
11
+ exports.resolveChatStatusAr = resolveChatStatusAr;
12
+ exports.pickArabicName = pickArabicName;
13
+ exports.pickLocalizedName = pickLocalizedName;
14
+ exports.joinDeptSectionArabicLabels = joinDeptSectionArabicLabels;
15
+ exports.buildApprovalWorkflowContent = buildApprovalWorkflowContent;
16
+ exports.buildHumanApprovalWorkflowContent = buildHumanApprovalWorkflowContent;
17
+ exports.buildApprovalChatMessage = buildApprovalChatMessage;
18
+ exports.resolveWorkflowContentAr = resolveWorkflowContentAr;
19
+ exports.resolveChatMessageAr = resolveChatMessageAr;
20
+ exports.enrichCancellationWorkflowLog = enrichCancellationWorkflowLog;
21
+ exports.enrichCancellationChatMessage = enrichCancellationChatMessage;
22
+ const workflow_chat_i18n_json_1 = __importDefault(require("./workflow-chat-i18n.json"));
23
+ const WORKFLOW_TEMPLATES = workflow_chat_i18n_json_1.default.workflowTemplates;
24
+ const CHAT_TEMPLATES = workflow_chat_i18n_json_1.default.chatTemplates;
25
+ const WORKFLOW_STATUS_LABELS = workflow_chat_i18n_json_1.default.workflowStatusLabels;
26
+ const CHAT_STATUS_LABELS = workflow_chat_i18n_json_1.default.chatStatusLabels;
27
+ /** Read-only view of the shared workflow/chat translation catalog. */
28
+ function getWorkflowChatI18nCatalog() {
29
+ return workflow_chat_i18n_json_1.default;
30
+ }
31
+ function interpolate(template, params = {}) {
32
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => params[key] ?? '');
33
+ }
34
+ function buildBilingualFromTemplate(templateCatalog, key, params = {}, arParams) {
35
+ const tpl = templateCatalog[key];
36
+ if (!tpl) {
37
+ return { en: key, ar: key };
38
+ }
39
+ const arabicParams = arParams ?? params;
40
+ return {
41
+ en: interpolate(tpl.en, params),
42
+ ar: interpolate(tpl.ar, arabicParams),
43
+ };
44
+ }
45
+ function workflowTemplate(key, params = {}, arParams) {
46
+ return buildBilingualFromTemplate(WORKFLOW_TEMPLATES, key, params, arParams);
47
+ }
48
+ function chatTemplate(key, params = {}, arParams) {
49
+ return buildBilingualFromTemplate(CHAT_TEMPLATES, key, params, arParams);
50
+ }
51
+ function resolveWorkflowStatusAr(status) {
52
+ if (status == null || status === '')
53
+ return null;
54
+ return WORKFLOW_STATUS_LABELS[status] ?? status;
55
+ }
56
+ function resolveChatStatusAr(status) {
57
+ if (status == null || status === '')
58
+ return null;
59
+ return CHAT_STATUS_LABELS[status] ?? status;
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. */
66
+ function pickLocalizedName(englishName, arabicName, fallback = '') {
67
+ const ar = arabicName?.trim();
68
+ if (ar)
69
+ return ar;
70
+ const en = englishName?.trim();
71
+ if (en)
72
+ return en;
73
+ return fallback;
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
+ }
82
+ function buildApprovalWorkflowContent(params) {
83
+ const nameEn = params.roleName?.trim() || '';
84
+ const nameAr = pickArabicName(params.roleArabicName);
85
+ let base;
86
+ 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;
93
+ }
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;
101
+ }
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;
109
+ }
110
+ const comment = params.comment?.trim();
111
+ if (!comment)
112
+ return base;
113
+ return {
114
+ en: `${base.en}: ${comment}`,
115
+ ar: base.ar,
116
+ };
117
+ }
118
+ 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
+ };
133
+ }
134
+ return params.isFirst
135
+ ? workflowTemplate('request_approval_in_progress')
136
+ : workflowTemplate('request_approval_pending');
137
+ }
138
+ function buildApprovalChatMessage(params) {
139
+ 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
+ };
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
+ }
173
+ function enrichCancellationWorkflowLog(log) {
174
+ const status = log.status != null ? String(log.status) : null;
175
+ const content = log.content != null ? String(log.content) : null;
176
+ return {
177
+ ...log,
178
+ content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
179
+ status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
180
+ };
181
+ }
182
+ function enrichCancellationChatMessage(chat) {
183
+ const status = chat.status != null ? String(chat.status) : null;
184
+ const message = chat.message != null ? String(chat.message) : null;
185
+ return {
186
+ ...chat,
187
+ message_ar: chat.message_ar ?? resolveChatMessageAr(message),
188
+ status_ar: chat.status_ar ?? resolveChatStatusAr(status),
189
+ };
190
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "workflowTemplates": {
3
+ "request_submitted": {
4
+ "en": "Request Submitted",
5
+ "ar": "تم تقديم الطلب"
6
+ },
7
+ "notification_sent_pending": {
8
+ "en": "Notification sent Pending",
9
+ "ar": "في انتظار إرسال الإشعار"
10
+ },
11
+ "notification_sent_successfully": {
12
+ "en": "Notification sent successfully",
13
+ "ar": "تم إرسال الإشعار بنجاح"
14
+ },
15
+ "request_approval_in_progress": {
16
+ "en": "Request Approval In Progress",
17
+ "ar": "الموافقة على الطلب قيد التنفيذ"
18
+ },
19
+ "request_approval_pending": {
20
+ "en": "Request Approval Pending",
21
+ "ar": "في انتظار الموافقة على الطلب"
22
+ },
23
+ "request_approval_in_progress_from": {
24
+ "en": "Request Approval In Progress from {{name}}",
25
+ "ar": "الموافقة على الطلب قيد التنفيذ من {{name}}"
26
+ },
27
+ "request_approval_pending_from": {
28
+ "en": "Request Approval Pending from {{name}}",
29
+ "ar": "في انتظار موافقة {{name}}"
30
+ },
31
+ "request_approved_by": {
32
+ "en": "Request Approved by {{name}}",
33
+ "ar": "تمت الموافقة على الطلب من قبل {{name}}"
34
+ },
35
+ "request_rejected_by": {
36
+ "en": "Request Rejected by {{name}}",
37
+ "ar": "تم رفض الطلب من قبل {{name}}"
38
+ },
39
+ "request_approved": {
40
+ "en": "Request Approved",
41
+ "ar": "تمت الموافقة على الطلب"
42
+ },
43
+ "request_rejected": {
44
+ "en": "Request Rejected",
45
+ "ar": "تم رفض الطلب"
46
+ }
47
+ },
48
+ "chatTemplates": {
49
+ "cancellation_submitted_by": {
50
+ "en": "Cancellation request submitted by {{userName}} ({{employeeId}})",
51
+ "ar": "تم تقديم طلب الإلغاء بواسطة {{userName}} ({{employeeId}})"
52
+ },
53
+ "request_received": {
54
+ "en": "Request Received",
55
+ "ar": "تم استلام الطلب"
56
+ },
57
+ "request_approved_by_role": {
58
+ "en": "Request Approved by: {{roleName}}{{comment}}",
59
+ "ar": "تمت الموافقة على الطلب من قبل: {{roleName}}{{comment}}"
60
+ },
61
+ "request_rejected_by_role": {
62
+ "en": "Request Rejected by: {{roleName}}{{comment}}",
63
+ "ar": "تم رفض الطلب من قبل: {{roleName}}{{comment}}"
64
+ }
65
+ },
66
+ "workflowStatusLabels": {
67
+ "Completed": "مكتمل",
68
+ "Pending": "قيد الانتظار",
69
+ "Not Yet Started": "لم يبدأ بعد"
70
+ },
71
+ "chatStatusLabels": {
72
+ "Submitted": "تم التقديم",
73
+ "Received": "تم الاستلام",
74
+ "Approved": "موافق عليه",
75
+ "Rejected": "مرفوض",
76
+ "Pending": "قيد الانتظار"
77
+ }
78
+ }
package/dist/index.d.ts CHANGED
@@ -415,3 +415,4 @@ export * from './models/SlaApprovalsViewModel';
415
415
  export * from './models/ServiceSlaApprovalModel';
416
416
  export * from './sla/sla-table-sync.service';
417
417
  export * from './sla/sla-approval-mirror';
418
+ export * from './i18n/workflow-chat-i18n';
package/dist/index.js CHANGED
@@ -604,3 +604,4 @@ __exportStar(require("./models/SlaApprovalsViewModel"), exports);
604
604
  __exportStar(require("./models/ServiceSlaApprovalModel"), exports);
605
605
  __exportStar(require("./sla/sla-table-sync.service"), exports);
606
606
  __exportStar(require("./sla/sla-approval-mirror"), exports);
607
+ __exportStar(require("./i18n/workflow-chat-i18n"), exports);
@@ -13,6 +13,8 @@ export declare class CancellationRequestChat extends BaseModel {
13
13
  user_id: number;
14
14
  role_id: number | null;
15
15
  message: string;
16
+ message_ar: string | null;
16
17
  messageType: CancellationMessageType;
17
18
  status: string | null;
19
+ status_ar: string | null;
18
20
  }
@@ -47,6 +47,10 @@ __decorate([
47
47
  (0, typeorm_1.Column)({ type: "text", nullable: false }),
48
48
  __metadata("design:type", String)
49
49
  ], CancellationRequestChat.prototype, "message", void 0);
50
+ __decorate([
51
+ (0, typeorm_1.Column)({ type: "text", nullable: true }),
52
+ __metadata("design:type", Object)
53
+ ], CancellationRequestChat.prototype, "message_ar", void 0);
50
54
  __decorate([
51
55
  (0, typeorm_1.Column)({
52
56
  type: "enum",
@@ -60,6 +64,10 @@ __decorate([
60
64
  (0, typeorm_1.Column)({ type: "text", nullable: true }),
61
65
  __metadata("design:type", Object)
62
66
  ], CancellationRequestChat.prototype, "status", void 0);
67
+ __decorate([
68
+ (0, typeorm_1.Column)({ type: "varchar", length: 100, nullable: true }),
69
+ __metadata("design:type", Object)
70
+ ], CancellationRequestChat.prototype, "status_ar", void 0);
63
71
  exports.CancellationRequestChat = CancellationRequestChat = __decorate([
64
72
  (0, typeorm_1.Entity)({ name: "cancellation_chats" })
65
73
  ], CancellationRequestChat);
@@ -9,7 +9,9 @@ export declare class CancellationWorkFlow extends BaseModel {
9
9
  service_id: number | null;
10
10
  sub_service_id: number | null;
11
11
  content: string;
12
+ content_ar: string | null;
12
13
  status: CancellationWorkFlowStatus;
14
+ status_ar: string | null;
13
15
  user_id: number | null;
14
16
  role_id: number | null;
15
17
  department_id: number | null;
@@ -37,10 +37,18 @@ __decorate([
37
37
  (0, typeorm_1.Column)({ type: "varchar", length: 500, nullable: false }),
38
38
  __metadata("design:type", String)
39
39
  ], CancellationWorkFlow.prototype, "content", void 0);
40
+ __decorate([
41
+ (0, typeorm_1.Column)({ type: "varchar", length: 500, nullable: true }),
42
+ __metadata("design:type", Object)
43
+ ], CancellationWorkFlow.prototype, "content_ar", void 0);
40
44
  __decorate([
41
45
  (0, typeorm_1.Column)({ type: "enum", enum: CancellationWorkFlowStatus, default: CancellationWorkFlowStatus.NOT_YET_STARTED, nullable: false }),
42
46
  __metadata("design:type", String)
43
47
  ], CancellationWorkFlow.prototype, "status", void 0);
48
+ __decorate([
49
+ (0, typeorm_1.Column)({ type: "varchar", length: 100, nullable: true }),
50
+ __metadata("design:type", Object)
51
+ ], CancellationWorkFlow.prototype, "status_ar", void 0);
44
52
  __decorate([
45
53
  (0, typeorm_1.Column)({ type: "integer", nullable: true }),
46
54
  __metadata("design:type", Object)
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@platform-modules/civil-aviation-authority",
3
- "version": "2.3.274",
3
+ "version": "2.3.278",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
- "scripts": {
7
- "build": "tsc",
6
+ "scripts": {
7
+ "build": "tsc && node -e \"require('fs').mkdirSync('dist/i18n',{recursive:true}); require('fs').copyFileSync('src/i18n/workflow-chat-i18n.json','dist/i18n/workflow-chat-i18n.json')\"",
8
8
  "dev": "ts-node src/scripts.ts",
9
9
  "sync:sla-sql": "node scripts/sync-sla-reports-sql.js"
10
10
  },
@@ -0,0 +1,10 @@
1
+ -- Pilot: Arabic columns for cancellation workflow and chat tables (CAA).
2
+ -- Run once per environment before deploying updated services.
3
+
4
+ ALTER TABLE IF EXISTS cancellation_workflows
5
+ ADD COLUMN IF NOT EXISTS content_ar VARCHAR(500) NULL,
6
+ ADD COLUMN IF NOT EXISTS status_ar VARCHAR(100) NULL;
7
+
8
+ ALTER TABLE IF EXISTS cancellation_chats
9
+ ADD COLUMN IF NOT EXISTS message_ar TEXT NULL,
10
+ ADD COLUMN IF NOT EXISTS status_ar VARCHAR(100) NULL;
@@ -0,0 +1,78 @@
1
+ {
2
+ "workflowTemplates": {
3
+ "request_submitted": {
4
+ "en": "Request Submitted",
5
+ "ar": "تم تقديم الطلب"
6
+ },
7
+ "notification_sent_pending": {
8
+ "en": "Notification sent Pending",
9
+ "ar": "في انتظار إرسال الإشعار"
10
+ },
11
+ "notification_sent_successfully": {
12
+ "en": "Notification sent successfully",
13
+ "ar": "تم إرسال الإشعار بنجاح"
14
+ },
15
+ "request_approval_in_progress": {
16
+ "en": "Request Approval In Progress",
17
+ "ar": "الموافقة على الطلب قيد التنفيذ"
18
+ },
19
+ "request_approval_pending": {
20
+ "en": "Request Approval Pending",
21
+ "ar": "في انتظار الموافقة على الطلب"
22
+ },
23
+ "request_approval_in_progress_from": {
24
+ "en": "Request Approval In Progress from {{name}}",
25
+ "ar": "الموافقة على الطلب قيد التنفيذ من {{name}}"
26
+ },
27
+ "request_approval_pending_from": {
28
+ "en": "Request Approval Pending from {{name}}",
29
+ "ar": "في انتظار موافقة {{name}}"
30
+ },
31
+ "request_approved_by": {
32
+ "en": "Request Approved by {{name}}",
33
+ "ar": "تمت الموافقة على الطلب من قبل {{name}}"
34
+ },
35
+ "request_rejected_by": {
36
+ "en": "Request Rejected by {{name}}",
37
+ "ar": "تم رفض الطلب من قبل {{name}}"
38
+ },
39
+ "request_approved": {
40
+ "en": "Request Approved",
41
+ "ar": "تمت الموافقة على الطلب"
42
+ },
43
+ "request_rejected": {
44
+ "en": "Request Rejected",
45
+ "ar": "تم رفض الطلب"
46
+ }
47
+ },
48
+ "chatTemplates": {
49
+ "cancellation_submitted_by": {
50
+ "en": "Cancellation request submitted by {{userName}} ({{employeeId}})",
51
+ "ar": "تم تقديم طلب الإلغاء بواسطة {{userName}} ({{employeeId}})"
52
+ },
53
+ "request_received": {
54
+ "en": "Request Received",
55
+ "ar": "تم استلام الطلب"
56
+ },
57
+ "request_approved_by_role": {
58
+ "en": "Request Approved by: {{roleName}}{{comment}}",
59
+ "ar": "تمت الموافقة على الطلب من قبل: {{roleName}}{{comment}}"
60
+ },
61
+ "request_rejected_by_role": {
62
+ "en": "Request Rejected by: {{roleName}}{{comment}}",
63
+ "ar": "تم رفض الطلب من قبل: {{roleName}}{{comment}}"
64
+ }
65
+ },
66
+ "workflowStatusLabels": {
67
+ "Completed": "مكتمل",
68
+ "Pending": "قيد الانتظار",
69
+ "Not Yet Started": "لم يبدأ بعد"
70
+ },
71
+ "chatStatusLabels": {
72
+ "Submitted": "تم التقديم",
73
+ "Received": "تم الاستلام",
74
+ "Approved": "موافق عليه",
75
+ "Rejected": "مرفوض",
76
+ "Pending": "قيد الانتظار"
77
+ }
78
+ }
@@ -0,0 +1,231 @@
1
+ import catalog from './workflow-chat-i18n.json';
2
+
3
+ export type BilingualText = { en: string; ar: string };
4
+
5
+ type WorkflowChatI18nCatalog = {
6
+ workflowTemplates: Record<string, BilingualText>;
7
+ chatTemplates: Record<string, BilingualText>;
8
+ workflowStatusLabels: Record<string, string>;
9
+ chatStatusLabels: Record<string, string>;
10
+ };
11
+
12
+ const WORKFLOW_TEMPLATES = catalog.workflowTemplates as Record<string, BilingualText>;
13
+ const CHAT_TEMPLATES = catalog.chatTemplates as Record<string, BilingualText>;
14
+ const WORKFLOW_STATUS_LABELS = catalog.workflowStatusLabels as Record<string, string>;
15
+ const CHAT_STATUS_LABELS = catalog.chatStatusLabels as Record<string, string>;
16
+
17
+ /** Read-only view of the shared workflow/chat translation catalog. */
18
+ export function getWorkflowChatI18nCatalog(): WorkflowChatI18nCatalog {
19
+ return catalog as WorkflowChatI18nCatalog;
20
+ }
21
+
22
+ function interpolate(template: string, params: Record<string, string> = {}): string {
23
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => params[key] ?? '');
24
+ }
25
+
26
+ export function buildBilingualFromTemplate(
27
+ templateCatalog: Record<string, BilingualText>,
28
+ key: string,
29
+ params: Record<string, string> = {},
30
+ arParams?: Record<string, string>,
31
+ ): BilingualText {
32
+ const tpl = templateCatalog[key];
33
+ if (!tpl) {
34
+ return { en: key, ar: key };
35
+ }
36
+ const arabicParams = arParams ?? params;
37
+ return {
38
+ en: interpolate(tpl.en, params),
39
+ ar: interpolate(tpl.ar, arabicParams),
40
+ };
41
+ }
42
+
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);
49
+ }
50
+
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);
57
+ }
58
+
59
+ export function resolveWorkflowStatusAr(status: string | null | undefined): string | null {
60
+ if (status == null || status === '') return null;
61
+ return WORKFLOW_STATUS_LABELS[status] ?? status;
62
+ }
63
+
64
+ export function resolveChatStatusAr(status: string | null | undefined): string | null {
65
+ if (status == null || status === '') return null;
66
+ return CHAT_STATUS_LABELS[status] ?? status;
67
+ }
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. */
75
+ export function pickLocalizedName(
76
+ englishName?: string | null,
77
+ arabicName?: string | null,
78
+ fallback = ''
79
+ ): string {
80
+ const ar = arabicName?.trim();
81
+ if (ar) return ar;
82
+ const en = englishName?.trim();
83
+ if (en) return en;
84
+ return fallback;
85
+ }
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
+
102
+ export function buildApprovalWorkflowContent(params: {
103
+ status: 'Approved' | 'Rejected' | 'Pending' | string;
104
+ roleName?: string | null;
105
+ roleArabicName?: string | null;
106
+ comment?: string | null;
107
+ }): BilingualText {
108
+ const nameEn = params.roleName?.trim() || '';
109
+ const nameAr = pickArabicName(params.roleArabicName);
110
+
111
+ let base: BilingualText;
112
+ 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;
133
+ }
134
+
135
+ const comment = params.comment?.trim();
136
+ if (!comment) return base;
137
+
138
+ return {
139
+ en: `${base.en}: ${comment}`,
140
+ ar: base.ar,
141
+ };
142
+ }
143
+
144
+ export function buildHumanApprovalWorkflowContent(params: {
145
+ isFirst: boolean;
146
+ roleName?: string | null;
147
+ roleArabicName?: string | null;
148
+ deptSectionEn?: string | null;
149
+ deptSectionAr?: string | null;
150
+ }): BilingualText {
151
+ const nameEn = params.roleName?.trim() || params.deptSectionEn?.trim() || '';
152
+ const nameAr = params.roleName
153
+ ? pickArabicName(params.roleArabicName)
154
+ : (params.deptSectionAr?.trim() || '');
155
+
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
+ };
166
+ }
167
+
168
+ return params.isFirst
169
+ ? workflowTemplate('request_approval_in_progress')
170
+ : workflowTemplate('request_approval_pending');
171
+ }
172
+
173
+ export function buildApprovalChatMessage(params: {
174
+ status: 'Approved' | 'Rejected' | string;
175
+ roleName?: string | null;
176
+ roleArabicName?: string | null;
177
+ comment?: string | null;
178
+ }): BilingualText {
179
+ 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';
183
+
184
+ return {
185
+ en: chatTemplate(key, { roleName: roleEn, comment: commentSuffix }).en,
186
+ ar: chatTemplate(key, { roleName: roleAr, comment: '' }).ar,
187
+ };
188
+ }
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
+
213
+ export function enrichCancellationWorkflowLog(log: Record<string, unknown>): Record<string, unknown> {
214
+ const status = log.status != null ? String(log.status) : null;
215
+ const content = log.content != null ? String(log.content) : null;
216
+ return {
217
+ ...log,
218
+ content_ar: log.content_ar ?? resolveWorkflowContentAr(content),
219
+ status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
220
+ };
221
+ }
222
+
223
+ export function enrichCancellationChatMessage(chat: Record<string, unknown>): Record<string, unknown> {
224
+ const status = chat.status != null ? String(chat.status) : null;
225
+ const message = chat.message != null ? String(chat.message) : null;
226
+ return {
227
+ ...chat,
228
+ message_ar: chat.message_ar ?? resolveChatMessageAr(message),
229
+ status_ar: chat.status_ar ?? resolveChatStatusAr(status),
230
+ };
231
+ }
package/src/index.ts CHANGED
@@ -503,4 +503,5 @@ export * from './models/SlaMyRequestsViewModel';
503
503
  export * from './models/SlaApprovalsViewModel';
504
504
  export * from './models/ServiceSlaApprovalModel';
505
505
  export * from './sla/sla-table-sync.service';
506
- export * from './sla/sla-approval-mirror';
506
+ export * from './sla/sla-approval-mirror';
507
+ export * from './i18n/workflow-chat-i18n';
@@ -29,6 +29,9 @@ export class CancellationRequestChat extends BaseModel {
29
29
  @Column({ type: "text", nullable: false })
30
30
  message: string;
31
31
 
32
+ @Column({ type: "text", nullable: true })
33
+ message_ar: string | null;
34
+
32
35
  @Column({
33
36
  type: "enum",
34
37
  enum: CancellationMessageType,
@@ -39,4 +42,7 @@ export class CancellationRequestChat extends BaseModel {
39
42
 
40
43
  @Column({ type: "text", nullable: true })
41
44
  status: string | null;
45
+
46
+ @Column({ type: "varchar", length: 100, nullable: true })
47
+ status_ar: string | null;
42
48
  }
@@ -21,9 +21,15 @@ export class CancellationWorkFlow extends BaseModel {
21
21
  @Column({ type: "varchar", length: 500, nullable: false })
22
22
  content: string;
23
23
 
24
+ @Column({ type: "varchar", length: 500, nullable: true })
25
+ content_ar: string | null;
26
+
24
27
  @Column({ type: "enum", enum: CancellationWorkFlowStatus, default: CancellationWorkFlowStatus.NOT_YET_STARTED, nullable: false })
25
28
  status: CancellationWorkFlowStatus;
26
29
 
30
+ @Column({ type: "varchar", length: 100, nullable: true })
31
+ status_ar: string | null;
32
+
27
33
  @Column({ type: "integer", nullable: true })
28
34
  user_id: number | null;
29
35
 
package/tsconfig.json CHANGED
@@ -5,6 +5,7 @@
5
5
  "declaration": true,
6
6
  "outDir": "./dist",
7
7
  "strict": true,
8
+ "resolveJsonModule": true,
8
9
  "esModuleInterop": true,
9
10
  "skipLibCheck": true,
10
11
  "strictPropertyInitialization": false,