@platform-modules/foreign-ministry 1.3.338 → 1.3.340

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.
Files changed (80) hide show
  1. package/.env +3 -3
  2. package/dist/auth/authenticate-jwt-session.d.ts +19 -0
  3. package/dist/auth/authenticate-jwt-session.js +35 -0
  4. package/dist/auth/jwt-token.helper.d.ts +18 -0
  5. package/dist/auth/jwt-token.helper.js +58 -0
  6. package/dist/auth/user-session-validation.d.ts +42 -0
  7. package/dist/auth/user-session-validation.js +95 -0
  8. package/dist/i18n/workflow-chat-i18n.d.ts +5 -0
  9. package/dist/i18n/workflow-chat-i18n.js +21 -0
  10. package/dist/i18n/workflow-chat-i18n.json +320 -0
  11. package/dist/i18n/workflow-chat-message.builder.d.ts +325 -0
  12. package/dist/i18n/workflow-chat-message.builder.js +924 -0
  13. package/dist/models/CurrencyModel.d.ts +1 -0
  14. package/dist/models/CurrencyModel.js +4 -0
  15. package/dist/models/SlaApprovalsViewModel.js +47 -47
  16. package/dist/models/SlaMyRequestsViewModel.js +52 -52
  17. package/package.json +2 -1
  18. package/scripts/check-column-types.js +38 -38
  19. package/scripts/check-service-tables.js +42 -42
  20. package/scripts/sync-sla-reports-sql.js +92 -92
  21. package/sql/add_lms_external_entity_trained_people_dates.sql +4 -4
  22. package/sql/sla-reports-sync.manifest.json +9 -9
  23. package/sql/sla_reports_admin_procedures.sql +304 -304
  24. package/sql/sla_reports_approvals_workbook.sql +383 -383
  25. package/sql/sla_reports_procedures.sql +886 -886
  26. package/sql/sla_requests_status_to_varchar.sql +28 -28
  27. package/sql/vw_sla_approvals.sql +108 -108
  28. package/sql/vw_sla_my_requests.sql +55 -55
  29. package/src/data-source.ts +686 -686
  30. package/src/helpers/employee-evaluation-request.utils.ts +178 -178
  31. package/src/helpers/evaluation-eligibility.utils.ts +126 -126
  32. package/src/index.ts +617 -617
  33. package/src/models/CurrencyModel.ts +5 -0
  34. package/src/models/DiplomaticAcademyRequestModel.ts +80 -80
  35. package/src/models/DocumentationFileModel.ts +40 -40
  36. package/src/models/EmbassyEvaluationCycleModel.ts +45 -45
  37. package/src/models/EmbassyEvaluationRequestModel.ts +59 -59
  38. package/src/models/EmployeeEvaluationAnswerModel.ts +26 -26
  39. package/src/models/EvaluationEligibilitySettingModel.ts +51 -51
  40. package/src/models/EvaluationFormModel.ts +44 -44
  41. package/src/models/LMSExternalEntityTrainedPersonModel.ts +51 -51
  42. package/src/models/LanguageCourseRequestModel.ts +67 -67
  43. package/src/models/LeaveApprovalsModel.ts +66 -66
  44. package/src/models/LeaveConfigModel.ts +71 -71
  45. package/src/models/MissionTravelApprovalModel.ts +101 -101
  46. package/src/models/MissionTravelAttachmentModel.ts +56 -56
  47. package/src/models/MissionTravelChatModel.ts +52 -52
  48. package/src/models/MissionTravelPersonModel.ts +105 -105
  49. package/src/models/MissionTravelWorkflowModel.ts +54 -54
  50. package/src/models/ProjectContactsModel.ts +51 -51
  51. package/src/models/ProjectFaqModel.ts +36 -36
  52. package/src/models/ProjectInvoicesModel.ts +41 -41
  53. package/src/models/ProjectModel.ts +75 -75
  54. package/src/models/ProjectTasksModel.ts +75 -75
  55. package/src/models/ServiceSlaApprovalModel.ts +63 -63
  56. package/src/models/ServicesNotificationConfigsModel.ts +55 -55
  57. package/src/models/SlaApprovalsViewModel.ts +135 -135
  58. package/src/models/SlaMyRequestsViewModel.ts +172 -172
  59. package/src/models/SlaRequestModel.ts +57 -57
  60. package/src/sla-report-views.ts +3 -3
  61. package/dist/helpers/sla-request-status.helper.d.ts +0 -3
  62. package/dist/helpers/sla-request-status.helper.js +0 -36
  63. package/dist/models/EmbassyEvaluationDepartmentSettingModel.d.ts +0 -14
  64. package/dist/models/EmbassyEvaluationDepartmentSettingModel.js +0 -56
  65. package/dist/models/EvaluationMastersModel.d.ts +0 -16
  66. package/dist/models/EvaluationMastersModel.js +0 -67
  67. package/dist/models/EvaluationQuestionMasterModel.d.ts +0 -10
  68. package/dist/models/EvaluationQuestionMasterModel.js +0 -42
  69. package/dist/models/EvaluationSectionMasterModel.d.ts +0 -7
  70. package/dist/models/EvaluationSectionMasterModel.js +0 -36
  71. package/dist/models/EvaluationWorkflowModel.d.ts +0 -60
  72. package/dist/models/EvaluationWorkflowModel.js +0 -215
  73. package/dist/models/IneligibleForEvaluationModel.d.ts +0 -8
  74. package/dist/models/IneligibleForEvaluationModel.js +0 -44
  75. package/dist/models/MissionTravelClassConfigModel.d.ts +0 -10
  76. package/dist/models/MissionTravelClassConfigModel.js +0 -50
  77. package/dist/models/MissionTravelPerdiemModel.d.ts +0 -10
  78. package/dist/models/MissionTravelPerdiemModel.js +0 -54
  79. package/dist/models/UuidAuditModel.d.ts +0 -9
  80. package/dist/models/UuidAuditModel.js +0 -41
package/.env CHANGED
@@ -1,5 +1,5 @@
1
- DB_HOST=localhost
1
+ DB_HOST=169.58.34.60
2
2
  DB_PORT=5432
3
- DB_USER=postgres
4
- DB_PASS=Fa@123
3
+ DB_USER=postgres_admin_user
4
+ DB_PASS=pg_admin_user_pwd_caa_fa_$%^&OIukhjgcvbn
5
5
  DB_NAME=FM
@@ -0,0 +1,19 @@
1
+ import type { UserSessionReader } from './user-session-validation';
2
+ export type JwtAuthFailure = {
3
+ ok: false;
4
+ statusCode: 401;
5
+ message: string;
6
+ };
7
+ export type JwtAuthSuccess = {
8
+ ok: true;
9
+ claims: Record<string, unknown>;
10
+ userId: number;
11
+ sessionId: number | null;
12
+ };
13
+ export type JwtAuthResult = JwtAuthSuccess | JwtAuthFailure;
14
+ /**
15
+ * After jwt.verify(), enforce server-side session binding (logout revocation).
16
+ */
17
+ export declare function authorizeVerifiedJwtSession(verified: Record<string, unknown>, sessionReader: UserSessionReader, options?: {
18
+ requireActiveSession?: boolean;
19
+ }): Promise<JwtAuthResult>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authorizeVerifiedJwtSession = authorizeVerifiedJwtSession;
4
+ const jwt_token_helper_1 = require("./jwt-token.helper");
5
+ const user_session_validation_1 = require("./user-session-validation");
6
+ /**
7
+ * After jwt.verify(), enforce server-side session binding (logout revocation).
8
+ */
9
+ async function authorizeVerifiedJwtSession(verified, sessionReader, options) {
10
+ const parsed = (0, jwt_token_helper_1.parseVerifiedJwtClaims)(verified);
11
+ if (!parsed) {
12
+ return { ok: false, statusCode: 401, message: 'Invalid Token' };
13
+ }
14
+ const requireActiveSession = options?.requireActiveSession !== false;
15
+ if (requireActiveSession) {
16
+ const sessionValid = await (0, user_session_validation_1.validateUserSessionBinding)(parsed.userId, parsed.sessionId, sessionReader);
17
+ if (!sessionValid) {
18
+ return { ok: false, statusCode: 401, message: 'Session Expired please login again' };
19
+ }
20
+ if (parsed.sessionId != null) {
21
+ const isCurrent = await (0, user_session_validation_1.validateJwtSessionIsCurrent)(parsed.userId, parsed.sessionId, sessionReader);
22
+ if (!isCurrent) {
23
+ return { ok: false, statusCode: 401, message: 'Session Expired please login again' };
24
+ }
25
+ }
26
+ }
27
+ const sessionId = await (0, user_session_validation_1.resolveAuthorizedSessionId)(parsed.userId, parsed.sessionId, sessionReader);
28
+ const { sessionId: _jwtSessionId, ...claimsWithoutSession } = parsed.raw;
29
+ return {
30
+ ok: true,
31
+ claims: claimsWithoutSession,
32
+ userId: parsed.userId,
33
+ sessionId,
34
+ };
35
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Extract portal API JWT from standard request headers.
3
+ * Supports `jwt` header (legacy) and `Authorization: Bearer <token>`.
4
+ */
5
+ export declare function extractJwtToken(headers: Record<string, string | string[] | undefined> | undefined): string | null;
6
+ export type JwtTokenRequest = {
7
+ headers: Record<string, string | string[] | undefined>;
8
+ body?: unknown;
9
+ };
10
+ /** JWT from headers (`jwt`, `Authorization: Bearer`) or JSON body (`token`, `jwt`, `access_token`). */
11
+ export declare function extractJwtTokenFromRequest(request: JwtTokenRequest): string | null;
12
+ export type VerifiedJwtClaims = {
13
+ userId: number;
14
+ sessionId: number | null;
15
+ raw: Record<string, unknown>;
16
+ };
17
+ /** Parse verified JWT payload into userId + sessionId. */
18
+ export declare function parseVerifiedJwtClaims(verified: Record<string, unknown>): VerifiedJwtClaims | null;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractJwtToken = extractJwtToken;
4
+ exports.extractJwtTokenFromRequest = extractJwtTokenFromRequest;
5
+ exports.parseVerifiedJwtClaims = parseVerifiedJwtClaims;
6
+ /**
7
+ * Extract portal API JWT from standard request headers.
8
+ * Supports `jwt` header (legacy) and `Authorization: Bearer <token>`.
9
+ */
10
+ function extractJwtToken(headers) {
11
+ if (!headers)
12
+ return null;
13
+ const pick = (value) => {
14
+ const raw = Array.isArray(value) ? value[0] : value;
15
+ const trimmed = raw?.trim();
16
+ return trimmed ? trimmed : null;
17
+ };
18
+ const fromJwtHeader = pick(headers.jwt ?? headers.JWT);
19
+ if (fromJwtHeader)
20
+ return fromJwtHeader;
21
+ const authorization = pick(headers.authorization ?? headers.Authorization);
22
+ if (!authorization)
23
+ return null;
24
+ const bearerMatch = /^Bearer\s+(.+)$/i.exec(authorization);
25
+ if (bearerMatch?.[1]?.trim())
26
+ return bearerMatch[1].trim();
27
+ return authorization;
28
+ }
29
+ /** JWT from headers (`jwt`, `Authorization: Bearer`) or JSON body (`token`, `jwt`, `access_token`). */
30
+ function extractJwtTokenFromRequest(request) {
31
+ const fromHeaders = extractJwtToken(request.headers);
32
+ if (fromHeaders)
33
+ return fromHeaders;
34
+ const body = request.body;
35
+ if (!body || typeof body !== 'object')
36
+ return null;
37
+ for (const key of ['token', 'jwt', 'access_token']) {
38
+ const value = body[key];
39
+ if (typeof value === 'string' && value.trim())
40
+ return value.trim();
41
+ }
42
+ return null;
43
+ }
44
+ /** Parse verified JWT payload into userId + sessionId. */
45
+ function parseVerifiedJwtClaims(verified) {
46
+ const userId = Number(verified.userId);
47
+ if (!Number.isFinite(userId) || userId <= 0)
48
+ return null;
49
+ const sessionIdRaw = verified.sessionId;
50
+ const parsedSessionId = sessionIdRaw == null || sessionIdRaw === '' ? null : Number(sessionIdRaw);
51
+ return {
52
+ userId,
53
+ sessionId: parsedSessionId != null && Number.isFinite(parsedSessionId) && parsedSessionId > 0
54
+ ? parsedSessionId
55
+ : null,
56
+ raw: verified,
57
+ };
58
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Server-side JWT session binding (VAPT M003/M004).
3
+ * JWT remains valid cryptographically until exp; access is allowed only while
4
+ * the bound user_sessions row is active and not expired.
5
+ */
6
+ export type UserSessionRow = {
7
+ id?: number;
8
+ };
9
+ export type UserSessionReader = {
10
+ isSessionActiveById(sessionId: number, userId: number): Promise<boolean>;
11
+ /** Legacy fallback when REQUIRE_JWT_SESSION_ID=false and token has no sessionId. */
12
+ isSessionActive?(userId: number): Promise<UserSessionRow | null | undefined>;
13
+ /** Latest active session row id for user (highest id). */
14
+ getLatestActiveSessionId?(userId: number): Promise<number | null>;
15
+ };
16
+ /** Default true — only the newest active session may use the API when JWT carries sessionId. */
17
+ export declare function isSingleSessionOnLogin(): boolean;
18
+ /** When true (default), JWT must include sessionId and that session must be active. */
19
+ export declare function isRequireJwtSessionId(): boolean;
20
+ export declare function resolveJwtSessionId(sessionId: unknown): number | null;
21
+ /**
22
+ * Validate that a verified JWT is still authorized (not logged out / expired server-side).
23
+ */
24
+ export declare function validateUserSessionBinding(userId: number, sessionId: number | null | undefined, reader: UserSessionReader): Promise<boolean>;
25
+ /** After row-level active check, enforce single-session policy (reject superseded JWTs). */
26
+ export declare function validateJwtSessionIsCurrent(userId: number, sessionId: number, reader: UserSessionReader): Promise<boolean>;
27
+ /** Resolve session id for request meta: JWT claim first, else latest active DB row. */
28
+ export declare function resolveAuthorizedSessionId(userId: number, jwtSessionId: number | null, reader: UserSessionReader): Promise<number | null>;
29
+ /** Shared TypeORM filters: active, not deleted, not past expires_at. */
30
+ export declare function applyActiveUserSessionFilters(qb: {
31
+ andWhere: (clause: string, params?: Record<string, unknown>) => unknown;
32
+ orderBy?: (sort: string, order?: 'ASC' | 'DESC') => unknown;
33
+ }, alias?: string): void;
34
+ /** Prefer newest active session when multiple rows match (legacy path). */
35
+ export declare function applyLatestActiveUserSessionOrdering(qb: {
36
+ orderBy: (sort: string, order?: 'ASC' | 'DESC') => unknown;
37
+ }, alias?: string): void;
38
+ /** @deprecated Use applyActiveUserSessionFilters — kept for backward compatibility. */
39
+ export declare function activeUserSessionSql(alias?: string): {
40
+ clause: string;
41
+ params: Record<string, unknown>;
42
+ };
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Server-side JWT session binding (VAPT M003/M004).
4
+ * JWT remains valid cryptographically until exp; access is allowed only while
5
+ * the bound user_sessions row is active and not expired.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.isSingleSessionOnLogin = isSingleSessionOnLogin;
9
+ exports.isRequireJwtSessionId = isRequireJwtSessionId;
10
+ exports.resolveJwtSessionId = resolveJwtSessionId;
11
+ exports.validateUserSessionBinding = validateUserSessionBinding;
12
+ exports.validateJwtSessionIsCurrent = validateJwtSessionIsCurrent;
13
+ exports.resolveAuthorizedSessionId = resolveAuthorizedSessionId;
14
+ exports.applyActiveUserSessionFilters = applyActiveUserSessionFilters;
15
+ exports.applyLatestActiveUserSessionOrdering = applyLatestActiveUserSessionOrdering;
16
+ exports.activeUserSessionSql = activeUserSessionSql;
17
+ /** Default true — only the newest active session may use the API when JWT carries sessionId. */
18
+ function isSingleSessionOnLogin() {
19
+ const raw = (process.env.SINGLE_SESSION_ON_LOGIN ?? 'true').trim().toLowerCase();
20
+ return raw !== 'false' && raw !== '0' && raw !== 'no';
21
+ }
22
+ /** When true (default), JWT must include sessionId and that session must be active. */
23
+ function isRequireJwtSessionId() {
24
+ const raw = (process.env.REQUIRE_JWT_SESSION_ID ?? 'true').trim().toLowerCase();
25
+ return raw !== 'false' && raw !== '0' && raw !== 'no';
26
+ }
27
+ function resolveJwtSessionId(sessionId) {
28
+ if (sessionId == null || sessionId === '')
29
+ return null;
30
+ const sid = Number(sessionId);
31
+ if (!Number.isFinite(sid) || sid <= 0)
32
+ return null;
33
+ return sid;
34
+ }
35
+ /**
36
+ * Validate that a verified JWT is still authorized (not logged out / expired server-side).
37
+ */
38
+ async function validateUserSessionBinding(userId, sessionId, reader) {
39
+ if (!Number.isFinite(userId) || userId <= 0)
40
+ return false;
41
+ const sid = resolveJwtSessionId(sessionId);
42
+ if (sid != null) {
43
+ return reader.isSessionActiveById(sid, userId);
44
+ }
45
+ if (isRequireJwtSessionId()) {
46
+ return false;
47
+ }
48
+ if (!reader.isSessionActive)
49
+ return false;
50
+ const legacy = await reader.isSessionActive(userId);
51
+ return !!legacy;
52
+ }
53
+ /** After row-level active check, enforce single-session policy (reject superseded JWTs). */
54
+ async function validateJwtSessionIsCurrent(userId, sessionId, reader) {
55
+ if (!isSingleSessionOnLogin() || !reader.getLatestActiveSessionId)
56
+ return true;
57
+ const latest = await reader.getLatestActiveSessionId(userId);
58
+ if (latest == null)
59
+ return true;
60
+ return latest === sessionId;
61
+ }
62
+ /** Resolve session id for request meta: JWT claim first, else latest active DB row. */
63
+ async function resolveAuthorizedSessionId(userId, jwtSessionId, reader) {
64
+ if (jwtSessionId != null)
65
+ return jwtSessionId;
66
+ if (reader.getLatestActiveSessionId) {
67
+ return reader.getLatestActiveSessionId(userId);
68
+ }
69
+ if (!reader.isSessionActive)
70
+ return null;
71
+ const legacy = await reader.isSessionActive(userId);
72
+ const legacyId = Number(legacy?.id);
73
+ return Number.isFinite(legacyId) && legacyId > 0 ? legacyId : null;
74
+ }
75
+ /** Shared TypeORM filters: active, not deleted, not past expires_at. */
76
+ function applyActiveUserSessionFilters(qb, alias = 'user_sessions') {
77
+ qb.andWhere(`${alias}.is_active = :is_active`, { is_active: true });
78
+ qb.andWhere(`(${alias}.is_deleted IS NULL OR ${alias}.is_deleted = :is_deleted)`, { is_deleted: false });
79
+ qb.andWhere(`(${alias}.expires_at IS NULL OR ${alias}.expires_at > :session_now)`, { session_now: new Date() });
80
+ }
81
+ /** Prefer newest active session when multiple rows match (legacy path). */
82
+ function applyLatestActiveUserSessionOrdering(qb, alias = 'user_sessions') {
83
+ qb.orderBy(`${alias}.id`, 'DESC');
84
+ }
85
+ /** @deprecated Use applyActiveUserSessionFilters — kept for backward compatibility. */
86
+ function activeUserSessionSql(alias = 'user_sessions') {
87
+ return {
88
+ clause: `${alias}.is_active = :is_active AND (${alias}.is_deleted IS NULL OR ${alias}.is_deleted = :is_deleted) AND (${alias}.expires_at IS NULL OR ${alias}.expires_at > :session_now)`,
89
+ params: {
90
+ is_active: true,
91
+ is_deleted: false,
92
+ session_now: new Date(),
93
+ },
94
+ };
95
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Re-exports the FM standard bilingual message builder.
3
+ * Import from '@platform-modules/foreign-ministry' in all services.
4
+ */
5
+ export * from './workflow-chat-message.builder';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Re-exports the FM standard bilingual message builder.
19
+ * Import from '@platform-modules/foreign-ministry' in all services.
20
+ */
21
+ __exportStar(require("./workflow-chat-message.builder"), exports);
@@ -0,0 +1,320 @@
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}}{{comment}}",
33
+ "ar": "تمت الموافقة على الطلب من قبل {{name}}{{comment}}"
34
+ },
35
+ "request_rejected_by": {
36
+ "en": "Request Rejected by {{name}}{{comment}}",
37
+ "ar": "تم رفض الطلب من قبل {{name}}{{comment}}"
38
+ },
39
+ "request_approved": {
40
+ "en": "Request Approved{{comment}}",
41
+ "ar": "تمت الموافقة على الطلب{{comment}}"
42
+ },
43
+ "request_rejected": {
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}}"
54
+ },
55
+ "request_closed_by": {
56
+ "en": "Request Closed by: {{name}}{{comment}}",
57
+ "ar": "تم إغلاق الطلب من قبل: {{name}}{{comment}}"
58
+ },
59
+ "request_closed": {
60
+ "en": "Request Closed{{comment}}",
61
+ "ar": "تم إغلاق الطلب{{comment}}"
62
+ },
63
+ "it_helpdesk_wf_hos_assigned": {
64
+ "en": "{{approverRoleName}} assigned the request to {{assignedRoleName}}{{comment}}",
65
+ "ar": "تم تعيين الطلب إلى {{assignedRoleName}} من قبل {{approverRoleName}}{{comment}}"
66
+ },
67
+ "it_helpdesk_wf_technician_assigned": {
68
+ "en": "IT technician {{level}} assigned by {{approverRoleName}}{{comment}}",
69
+ "ar": "تم تعيين فني تقنية المعلومات {{level}} من قبل {{approverRoleName}}{{comment}}"
70
+ },
71
+ "it_helpdesk_wf_reassigned_technician": {
72
+ "en": "{{approverRoleName}} reassigned request to Technician{{comment}}{{modifiers}}",
73
+ "ar": "{{approverRoleName}} تمت إعادة تعيين الطلب إلى الفني{{comment}}{{modifiers}}"
74
+ },
75
+ "it_helpdesk_wf_reassigned_l3": {
76
+ "en": "{{approverRoleName}} reassigned request to L3{{comment}}{{modifiers}}",
77
+ "ar": "{{approverRoleName}} تمت إعادة تعيين الطلب إلى المستوى الثالث (L3){{comment}}{{modifiers}}"
78
+ },
79
+ "it_helpdesk_wf_reassigned_generic": {
80
+ "en": "{{approverRoleName}} {{level}} reassigned{{comment}}{{modifiers}}",
81
+ "ar": "{{approverRoleName}} {{level}} إعادة التعيين{{comment}}{{modifiers}}"
82
+ },
83
+ "it_helpdesk_wf_closed_request": {
84
+ "en": "{{approverRoleName}} closed request{{comment}}",
85
+ "ar": "تم إغلاق الطلب من قبل {{approverRoleName}}{{comment}}"
86
+ },
87
+ "it_helpdesk_wf_closed_request_cap": {
88
+ "en": "{{approverRoleName}} Closed request{{comment}}",
89
+ "ar": "تم إغلاق الطلب من قبل {{approverRoleName}}{{comment}}"
90
+ },
91
+ "it_helpdesk_wf_l3_approved_by": {
92
+ "en": "{{roleName}} approved the request by {{userName}}{{comment}}",
93
+ "ar": "{{roleName}} تمت الموافقة على الطلب من قبل {{userName}}{{comment}}"
94
+ },
95
+ "it_helpdesk_wf_l3_rejected_by": {
96
+ "en": "{{roleName}} rejected the request by {{userName}}{{comment}}",
97
+ "ar": "{{roleName}} تم رفض الطلب من قبل {{userName}}{{comment}}"
98
+ },
99
+ "it_helpdesk_wf_level_approved": {
100
+ "en": "Level {{level}} approved by {{approverRoleName}}{{comment}}",
101
+ "ar": "تمت الموافقة من قبل {{approverRoleName}} للمستوى {{level}}{{comment}}"
102
+ },
103
+ "it_helpdesk_wf_rejected": {
104
+ "en": "{{approverRoleName}} rejected the request{{comment}}",
105
+ "ar": "{{approverRoleName}} تم رفض الطلب{{comment}}"
106
+ },
107
+ "it_helpdesk_wf_status_by_role": {
108
+ "en": "Request {{status}} by {{approverRoleName}}{{comment}}",
109
+ "ar": "تم {{statusAr}} الطلب من قبل {{approverRoleName}}{{comment}}"
110
+ },
111
+ "it_helpdesk_wf_l3_step_rejected": {
112
+ "en": "{{roleName}} rejected the request{{comment}}",
113
+ "ar": "{{roleName}} تم رفض الطلب{{comment}}"
114
+ },
115
+ "it_helpdesk_wf_technician_completed": {
116
+ "en": "Technician action completed - request closed by {{roleName}}{{comment}}",
117
+ "ar": "تم استكمال إجراء الفني - تم إغلاق الطلب من قبل {{roleName}}{{comment}}"
118
+ },
119
+ "it_helpdesk_wf_routing_rejected": {
120
+ "en": "Workflow routing completed - request rejected at L3{{comment}}",
121
+ "ar": "تم استكمال مسار سير العمل - تم رفض الطلب في المستوى الثالث (L3){{comment}}"
122
+ },
123
+ "it_helpdesk_wf_routing_resolved": {
124
+ "en": "Workflow routing completed - request resolved at L3",
125
+ "ar": "تم استكمال مسار سير العمل - تم حل الطلب في المستوى الثالث (L3)"
126
+ }
127
+ },
128
+ "chatTemplates": {
129
+ "request_submitted_by": {
130
+ "en": "Request Submitted by {{userName}} ({{employeeId}})",
131
+ "ar": "تم تقديم الطلب من قبل {{userName}} ({{employeeId}})"
132
+ },
133
+ "cancellation_submitted_by": {
134
+ "en": "Cancellation request submitted by {{userName}} ({{employeeId}})",
135
+ "ar": "تم تقديم طلب الإلغاء من قبل {{userName}} ({{employeeId}})"
136
+ },
137
+ "cancellation_submitted": {
138
+ "en": "Cancellation request submitted",
139
+ "ar": "تم تقديم طلب الإلغاء"
140
+ },
141
+ "request_received": {
142
+ "en": "Request Received",
143
+ "ar": "تم استلام الطلب"
144
+ },
145
+ "request_approved_by_role": {
146
+ "en": "Request Approved by: {{roleName}}{{comment}}",
147
+ "ar": "تمت الموافقة على الطلب من قبل: {{roleName}}{{comment}}"
148
+ },
149
+ "request_rejected_by_role": {
150
+ "en": "Request Rejected by: {{roleName}}{{comment}}",
151
+ "ar": "تم رفض الطلب من قبل: {{roleName}}{{comment}}"
152
+ },
153
+ "request_returned_for_modification_by_role": {
154
+ "en": "Request Returned for Modification by: {{roleName}}{{comment}}",
155
+ "ar": "تمت إعادة الطلب للتعديل من قبل: {{roleName}}{{comment}}"
156
+ },
157
+ "request_approved": {
158
+ "en": "Request Approved{{comment}}",
159
+ "ar": "تمت الموافقة على الطلب{{comment}}"
160
+ },
161
+ "request_rejected": {
162
+ "en": "Request Rejected{{comment}}",
163
+ "ar": "تم رفض الطلب{{comment}}"
164
+ },
165
+ "request_returned_for_modification": {
166
+ "en": "Request Returned for Modification{{comment}}",
167
+ "ar": "تمت إعادة الطلب للتعديل{{comment}}"
168
+ },
169
+ "request_closed_by_role": {
170
+ "en": "Request Closed by: {{roleName}}{{comment}}",
171
+ "ar": "تم إغلاق الطلب من قبل: {{roleName}}{{comment}}"
172
+ },
173
+ "request_for_change_by_role": {
174
+ "en": "Request For Change by: {{roleName}}{{comment}}",
175
+ "ar": "طلب تغيير من قبل: {{roleName}}{{comment}}"
176
+ },
177
+ "request_for_change_requested_by_role": {
178
+ "en": "Request For Change requested by: {{roleName}}{{comment}}",
179
+ "ar": "تم طلب التغيير من قبل: {{roleName}}{{comment}}"
180
+ },
181
+ "attachment_uploaded": {
182
+ "en": "Attachment uploaded: {{fileName}}",
183
+ "ar": "تم رفع المرفق: {{fileName}}"
184
+ },
185
+ "attachment_placeholder": {
186
+ "en": "[Attachment]",
187
+ "ar": "[مرفق]"
188
+ },
189
+ "task_status_in_progress_by_role": {
190
+ "en": "Task status updated to \"In Progress\" by: {{roleName}}",
191
+ "ar": "تم تحديث حالة المهمة إلى \"قيد التنفيذ\" من قبل: {{roleName}}"
192
+ },
193
+ "task_completed_by_role": {
194
+ "en": "Task Completed by: {{roleName}}{{comment}}",
195
+ "ar": "اكتملت المهمة من قبل: {{roleName}}{{comment}}"
196
+ },
197
+ "task_status_updated": {
198
+ "en": "Task status updated to {{status}}",
199
+ "ar": "تم تحديث حالة المهمة إلى {{status}}"
200
+ },
201
+ "it_helpdesk_hos_assigned": {
202
+ "en": "Head Of The Section assigned request to {{assignedRoleName}}: {{assignedUserName}}{{comment}}",
203
+ "ar": "قام رئيس القسم بتعيين الطلب إلى {{assignedRoleName}}: {{assignedUserName}}{{comment}}"
204
+ },
205
+ "it_helpdesk_technician_assigned": {
206
+ "en": "IT technician {{level}} assigned to {{assignedRoleName}}: {{assignedUserName}}{{comment}}",
207
+ "ar": "تم تعيين فني تقنية المعلومات {{level}} إلى {{assignedRoleName}}: {{assignedUserName}}{{comment}}"
208
+ },
209
+ "it_helpdesk_reassigned_to_role": {
210
+ "en": "{{approverRoleName}} reassigned request to {{reassignedRoleName}}{{comment}}",
211
+ "ar": "{{approverRoleName}} تمت إعادة تعيين الطلب إلى {{reassignedRoleName}}{{comment}}"
212
+ },
213
+ "it_helpdesk_reassigned_to_l3": {
214
+ "en": "{{approverRoleName}} reassigned request to L3{{comment}}",
215
+ "ar": "{{approverRoleName}} تمت إعادة تعيين الطلب إلى المستوى الثالث (L3){{comment}}"
216
+ },
217
+ "it_helpdesk_reassigned_generic": {
218
+ "en": "{{approverRoleName}} {{level}} reassigned the request{{comment}}",
219
+ "ar": "{{approverRoleName}} {{level}} تمت إعادة تعيين الطلب{{comment}}"
220
+ },
221
+ "request_submitted_with_ref": {
222
+ "en": "{{requestLabel}} #{{requestId}} has been submitted by {{userName}} ({{employeeId}})",
223
+ "ar": "تم تقديم {{requestLabel}} رقم {{requestId}} من قبل {{userName}} ({{employeeId}})"
224
+ },
225
+ "security_access_approval_step": {
226
+ "en": "{{productLabel}} request #{{requestId}} was {{statusWord}} (approval step {{approvalLevel}}).{{comment}}",
227
+ "ar": "تم {{statusWordAr}} طلب {{productLabel}} رقم {{requestId}} (خطوة الموافقة {{approvalLevel}}).{{comment}}"
228
+ },
229
+ "logistics_actual_return_updated": {
230
+ "en": "{{roleName}} updated the Vehicle Actual Return Date, Time, and Reason. Actual Return Date: {{date}}, Actual Return Time: {{time}}, Reason: {{reason}}",
231
+ "ar": "قام {{roleName}} بتحديث تاريخ الإرجاع الفعلي للمركبة والوقت والسبب. تاريخ الإرجاع الفعلي: {{date}}، وقت الإرجاع الفعلي: {{time}}، السبب: {{reason}}"
232
+ }
233
+ },
234
+ "notificationTemplates": {
235
+ "logistics_actual_return_title": {
236
+ "en": "Vehicle Actual Return Updated - {{requestLabel}} Request #{{requestId}}",
237
+ "ar": "تم تحديث الإرجاع الفعلي للمركبة - طلب {{requestLabel}} رقم {{requestId}}"
238
+ },
239
+ "it_helpdesk_notif_request_closed": {
240
+ "en": "IT request #{{requestId}} has been closed",
241
+ "ar": "تم إغلاق طلب تقنية المعلومات رقم {{requestId}}"
242
+ },
243
+ "it_helpdesk_notif_request_rejected": {
244
+ "en": "IT request #{{requestId}} has been rejected",
245
+ "ar": "تم رفض طلب تقنية المعلومات رقم {{requestId}}"
246
+ },
247
+ "fm_request_submitted_title": {
248
+ "en": "Request Submitted",
249
+ "ar": "تم تقديم الطلب"
250
+ },
251
+ "fm_request_submitted_body": {
252
+ "en": "Your {{serviceLabel}} request #{{requestId}} has been submitted successfully",
253
+ "ar": "تم تقديم طلب {{serviceLabelAr}} رقم {{requestId}} بنجاح"
254
+ },
255
+ "fm_request_received_title": {
256
+ "en": "Request Received for Approval",
257
+ "ar": "تم استلام الطلب للموافقة"
258
+ },
259
+ "fm_request_received_body": {
260
+ "en": "{{serviceLabel}} request #{{requestId}} has been received for your approval",
261
+ "ar": "تم استلام طلب {{serviceLabelAr}} رقم {{requestId}} للموافقة عليه"
262
+ },
263
+ "fm_request_approved_title": {
264
+ "en": "Request Approved",
265
+ "ar": "تمت الموافقة على الطلب"
266
+ },
267
+ "fm_request_approved_body": {
268
+ "en": "Your {{serviceLabel}} request #{{requestId}} has been approved and is now completed{{comment}}",
269
+ "ar": "تمت الموافقة على طلب {{serviceLabelAr}} رقم {{requestId}} واكتمل الآن{{comment}}"
270
+ },
271
+ "fm_request_approved_intermediate_body": {
272
+ "en": "Your {{serviceLabel}} request #{{requestId}} has been approved at level {{level}}. {{pendingCount}} approval(s) remaining.{{comment}}",
273
+ "ar": "تمت الموافقة على طلب {{serviceLabelAr}} رقم {{requestId}} في المستوى {{level}}. {{pendingCount}} موافقة متبقية.{{comment}}"
274
+ },
275
+ "fm_level_approved_title": {
276
+ "en": "Level {{level}} Approved",
277
+ "ar": "تمت الموافقة على المستوى {{level}}"
278
+ },
279
+ "fm_request_rejected_title": {
280
+ "en": "Request Rejected",
281
+ "ar": "تم رفض الطلب"
282
+ },
283
+ "fm_request_rejected_body": {
284
+ "en": "Your {{serviceLabel}} request #{{requestId}} has been rejected{{comment}}",
285
+ "ar": "تم رفض طلب {{serviceLabelAr}} رقم {{requestId}}{{comment}}"
286
+ },
287
+ "fm_approval_required_title": {
288
+ "en": "Approval Required",
289
+ "ar": "مطلوب الموافقة"
290
+ },
291
+ "fm_approval_required_body": {
292
+ "en": "{{serviceLabel}} request #{{requestId}} requires your approval at level {{level}}",
293
+ "ar": "يتطلب طلب {{serviceLabelAr}} رقم {{requestId}} موافقتك في المستوى {{level}}"
294
+ },
295
+ "fm_approval_required_level_title": {
296
+ "en": "Approval Required - Level {{level}}",
297
+ "ar": "مطلوب الموافقة - المستوى {{level}}"
298
+ }
299
+ },
300
+ "workflowStatusLabels": {
301
+ "Completed": "مكتمل",
302
+ "Pending": "قيد الانتظار",
303
+ "Not Yet Started": "لم يبدأ بعد"
304
+ },
305
+ "chatStatusLabels": {
306
+ "Submitted": "تم التقديم",
307
+ "Received": "تم الاستلام",
308
+ "Approved": "تم الموافقة",
309
+ "Rejected": "تم الرفض",
310
+ "Pending": "قيد الانتظار",
311
+ "Returned for Modification": "تمت إعادته للتعديل",
312
+ "Request For Change": "طلب تغيير",
313
+ "Closed": "مغلق",
314
+ "Completed": "مكتمل",
315
+ "Assigned": "تم التعيين",
316
+ "Reassigned": "تمت إعادة التعيين",
317
+ "In Progress": "قيد التنفيذ",
318
+ "Attachment uploaded": "تم رفع المرفق"
319
+ }
320
+ }