@apptimate/core-lib 5.3.0 → 5.4.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apptimate/core-lib",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "publishConfig": {
@@ -0,0 +1,94 @@
1
+ import type { IApiResponse } from "../common/interfaces/ICommon";
2
+ import { sendRequest } from "../utils/httpClient";
3
+
4
+ const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api`;
5
+
6
+ export type EmailProvider = "smtp";
7
+ export type EmailConnectionStatus = "draft" | "connected" | "verified" | "active" | "inactive" | "reauthorization_required" | "error" | "disconnected";
8
+
9
+ export interface EmailConnection {
10
+ id: number;
11
+ connection_name: string;
12
+ provider: EmailProvider;
13
+ display_name?: string | null;
14
+ from_name: string;
15
+ from_email: string;
16
+ reply_to_email?: string | null;
17
+ status: EmailConnectionStatus;
18
+ is_default: boolean;
19
+ verified_at?: string | null;
20
+ last_health_status?: "healthy" | "degraded" | "unhealthy" | "unknown" | null;
21
+ last_error_code?: string | null;
22
+ routes_count?: number;
23
+ has_password?: boolean;
24
+ smtp?: {
25
+ host: string;
26
+ port: number;
27
+ encryption: "tls" | "ssl" | "none";
28
+ username: string;
29
+ } | null;
30
+ }
31
+
32
+ export interface EmailConnectionRoute {
33
+ id: number;
34
+ email_connection_id: number;
35
+ module_code?: string | null;
36
+ message_type?: string | null;
37
+ document_type?: string | null;
38
+ is_active: boolean;
39
+ connection?: Pick<EmailConnection, "id" | "connection_name" | "provider">;
40
+ }
41
+
42
+ export interface EmailDelivery {
43
+ id: number;
44
+ uuid: string;
45
+ email_connection_id?: number | null;
46
+ provider: EmailProvider | "system";
47
+ message_type?: string | null;
48
+ recipient_summary: string[] | Record<string, unknown>;
49
+ subject: string;
50
+ status: "queued" | "processing" | "retrying" | "accepted" | "failed" | "cancelled";
51
+ attempt_count: number;
52
+ queued_at?: string | null;
53
+ accepted_at?: string | null;
54
+ failed_at?: string | null;
55
+ connection?: Pick<EmailConnection, "id" | "connection_name">;
56
+ }
57
+
58
+ export interface SmtpConnectionPayload {
59
+ id?: number;
60
+ connection_name: string;
61
+ from_name: string;
62
+ from_email: string;
63
+ reply_to_email?: string;
64
+ host: string;
65
+ port: number;
66
+ encryption: "tls" | "ssl" | "none";
67
+ username: string;
68
+ password?: string;
69
+ }
70
+
71
+ async function request<T>(url: string, method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", data?: unknown): Promise<IApiResponse<T>> {
72
+ const response = await sendRequest({ url, method, data });
73
+ return response.responseData as IApiResponse<T>;
74
+ }
75
+
76
+ export const listEmailConnections = () => request<EmailConnection[]>(`${BASE}/email-connections`, "GET");
77
+ export const revealSmtpConnectionPassword = (id: number) =>
78
+ request<{ password: string }>(`${BASE}/email-connections/${id}/smtp-secret`, "GET");
79
+ export const saveSmtpConnection = (data: SmtpConnectionPayload) => request<EmailConnection>(`${BASE}/email-connections/smtp`, "PUT", data);
80
+ export const verifyEmailConnection = (id: number) => request<EmailConnection>(`${BASE}/email-connections/${id}/verify`, "POST");
81
+ export const sendConnectionTest = (id: number, test_recipient: string) =>
82
+ request<null>(`${BASE}/email-connections/${id}/send-test`, "POST", { test_recipient });
83
+ export const activateEmailConnection = (id: number) => request<EmailConnection>(`${BASE}/email-connections/${id}/activate`, "POST");
84
+ export const deactivateEmailConnection = (id: number) => request<EmailConnection>(`${BASE}/email-connections/${id}/deactivate`, "POST");
85
+ export const deleteEmailConnection = (id: number) => request<null>(`${BASE}/email-connections/${id}`, "DELETE");
86
+
87
+ export const listEmailConnectionRoutes = () => request<EmailConnectionRoute[]>(`${BASE}/email-connection-routes`, "GET");
88
+ export const saveEmailConnectionRoute = (data: Omit<EmailConnectionRoute, "id" | "connection"> & { id?: number }) =>
89
+ request<EmailConnectionRoute>(`${BASE}/email-connection-routes${data.id ? `/${data.id}` : ""}`, data.id ? "PUT" : "POST", data);
90
+ export const deleteEmailConnectionRoute = (id: number) => request<null>(`${BASE}/email-connection-routes/${id}`, "DELETE");
91
+
92
+ export const listEmailDeliveries = () => request<{ data: EmailDelivery[] } | EmailDelivery[]>(`${BASE}/email-deliveries`, "GET");
93
+ export const retryEmailDelivery = (id: number, data?: { email_connection_id?: number; reason?: string }) =>
94
+ request<EmailDelivery>(`${BASE}/email-deliveries/${id}/retry`, "POST", data);
@@ -0,0 +1,130 @@
1
+ import { IApiResponse } from "../common/interfaces/ICommon";
2
+ import { sendRequest } from "../utils/httpClient";
3
+
4
+ const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/email-templates`;
5
+
6
+ export type EmailTemplateStatus = "active" | "inactive";
7
+ export type EmailTemplateFormat = "html" | "plain_text";
8
+
9
+ export interface EmailTemplate {
10
+ id: number;
11
+ organization_id: number;
12
+ name: string;
13
+ code: string;
14
+ description: string | null;
15
+ category: string;
16
+ subject: string;
17
+ html_body: string;
18
+ plain_text_body: string | null;
19
+ format: EmailTemplateFormat;
20
+ status: EmailTemplateStatus;
21
+ created_at: string;
22
+ updated_at: string;
23
+ creator?: { id: number; name: string } | null;
24
+ updater?: { id: number; name: string } | null;
25
+ }
26
+
27
+ export interface EmailTemplatePayload {
28
+ name: string;
29
+ code: string;
30
+ description?: string | null;
31
+ category: string;
32
+ subject: string;
33
+ html_body: string;
34
+ plain_text_body?: string | null;
35
+ format: EmailTemplateFormat;
36
+ status: EmailTemplateStatus;
37
+ }
38
+
39
+ export interface EmailTemplateListResult {
40
+ data: EmailTemplate[];
41
+ total: number;
42
+ }
43
+
44
+ export interface EmailTemplateToken {
45
+ key: string;
46
+ label: string;
47
+ data_type: string;
48
+ default_value: string | null;
49
+ sample_value: string;
50
+ }
51
+
52
+ export interface EmailTemplateTokenGroup {
53
+ key: string;
54
+ label: string;
55
+ tokens: EmailTemplateToken[];
56
+ }
57
+
58
+ export interface EmailTemplateCategoryTokens {
59
+ category: string;
60
+ groups: EmailTemplateTokenGroup[];
61
+ }
62
+
63
+ export interface RenderedEmailTemplate {
64
+ template_id: number;
65
+ subject: string;
66
+ html_body: string;
67
+ plain_text_body: string;
68
+ missing_tokens: string[];
69
+ unknown_tokens: string[];
70
+ }
71
+
72
+ export async function getEmailTemplates(
73
+ params: Record<string, string | number> = {},
74
+ ): Promise<IApiResponse<EmailTemplateListResult>> {
75
+ const query = new URLSearchParams(
76
+ Object.entries(params).reduce<Record<string, string>>((result, [key, value]) => {
77
+ result[key] = String(value);
78
+ return result;
79
+ }, {}),
80
+ ).toString();
81
+ const response = await sendRequest({ url: `${BASE}${query ? `?${query}` : ""}`, method: "GET" });
82
+ return response.responseData as IApiResponse<EmailTemplateListResult>;
83
+ }
84
+
85
+ export async function getEmailTemplate(id: number): Promise<IApiResponse<EmailTemplate>> {
86
+ const response = await sendRequest({ url: `${BASE}/${id}`, method: "GET" });
87
+ return response.responseData as IApiResponse<EmailTemplate>;
88
+ }
89
+
90
+ export async function createEmailTemplate(
91
+ data: EmailTemplatePayload,
92
+ ): Promise<IApiResponse<EmailTemplate>> {
93
+ const response = await sendRequest({ url: BASE, method: "POST", data });
94
+ return response.responseData as IApiResponse<EmailTemplate>;
95
+ }
96
+
97
+ export async function updateEmailTemplate(
98
+ id: number,
99
+ data: EmailTemplatePayload,
100
+ ): Promise<IApiResponse<EmailTemplate>> {
101
+ const response = await sendRequest({ url: `${BASE}/${id}`, method: "PUT", data });
102
+ return response.responseData as IApiResponse<EmailTemplate>;
103
+ }
104
+
105
+ export async function deleteEmailTemplate(id: number): Promise<IApiResponse<null>> {
106
+ const response = await sendRequest({ url: `${BASE}/${id}`, method: "DELETE" });
107
+ return response.responseData as IApiResponse<null>;
108
+ }
109
+
110
+ export async function getEmailTemplateCategoryTokens(
111
+ category: string,
112
+ ): Promise<IApiResponse<EmailTemplateCategoryTokens>> {
113
+ const response = await sendRequest({
114
+ url: `${process.env.NEXT_PUBLIC_API_URL}/api/email-template-categories/${encodeURIComponent(category)}/tokens`,
115
+ method: "GET",
116
+ });
117
+ return response.responseData as IApiResponse<EmailTemplateCategoryTokens>;
118
+ }
119
+
120
+ export async function previewEmailTemplate(
121
+ id: number,
122
+ tokenData: Record<string, string> = {},
123
+ ): Promise<IApiResponse<RenderedEmailTemplate>> {
124
+ const response = await sendRequest({
125
+ url: `${BASE}/${id}/preview`,
126
+ method: "POST",
127
+ data: { token_data: tokenData },
128
+ });
129
+ return response.responseData as IApiResponse<RenderedEmailTemplate>;
130
+ }
@@ -0,0 +1,90 @@
1
+ import { sendRequest } from "../utils/httpClient";
2
+ import { IApiResponse } from "../common/interfaces/ICommon";
3
+
4
+ const callApi = async (url: string, method: "GET" | "POST", data?: any) => {
5
+ const response = await sendRequest({ url, method, data });
6
+ return response.responseData as IApiResponse;
7
+ };
8
+
9
+ const BASE_URL = `${process.env.NEXT_PUBLIC_API_URL}/api/hr`;
10
+
11
+ export const getEmployeeCalendar = async (employeeId: string | number, startDate: string, endDate: string, projectId?: string | number, workforceId?: string | number): Promise<IApiResponse> => {
12
+ const params: any = { start_date: startDate, end_date: endDate };
13
+ if (projectId) params.project_id = projectId;
14
+ if (workforceId) params.workforce_id = workforceId;
15
+ const queryParams = new URLSearchParams(params);
16
+
17
+ const url = employeeId === 'me'
18
+ ? `${BASE_URL}/ess/me/calendar?${queryParams.toString()}`
19
+ : employeeId === 0
20
+ ? `${BASE_URL}/calendar?${queryParams.toString()}` // For construction gangs
21
+ : `${BASE_URL}/employees/${employeeId}/calendar?${queryParams.toString()}`;
22
+
23
+ const response = await sendRequest({ url, method: "GET" });
24
+ return response.responseData as IApiResponse;
25
+ };
26
+
27
+ export const toggleSegmentCancellation = async (employeeId: string | number, data: { roster_date: string, segment_no: number, reason?: string }): Promise<IApiResponse> => {
28
+ const url = employeeId === 'me'
29
+ ? `${BASE_URL}/ess/me/calendar/cancel-segment`
30
+ : `${BASE_URL}/employees/${employeeId}/calendar/cancel-segment`;
31
+
32
+ return callApi(url, 'POST', { ...data, employee_id: employeeId === 'me' ? undefined : employeeId });
33
+ };
34
+ export const requestSegmentExchange = async (
35
+ employeeId: string | number,
36
+ data: { roster_date: string, segment_no: number, requester_id: number, responder_id: number, reason?: string }
37
+ ): Promise<IApiResponse> => {
38
+ const url = employeeId === 'me'
39
+ ? `${BASE_URL}/ess/me/calendar/exchange-segment`
40
+ : `${BASE_URL}/employees/${employeeId}/calendar/exchange-segment`;
41
+
42
+ return callApi(url, 'POST', { ...data, employee_id: employeeId === 'me' ? undefined : employeeId });
43
+ };
44
+
45
+ export const cancelSegmentExchange = async (employeeId: string | number, exchangeId: number): Promise<IApiResponse> => {
46
+ const url = employeeId === 'me'
47
+ ? `${BASE_URL}/ess/me/calendar/exchange-segment/${exchangeId}`
48
+ : `${BASE_URL}/employees/${employeeId}/calendar/exchange-segment/${exchangeId}`;
49
+
50
+ const response = await sendRequest({ url, method: "DELETE" });
51
+ return response.responseData as IApiResponse;
52
+ };
53
+
54
+ export const getDailyOverview = async (date: string): Promise<IApiResponse> => {
55
+ const response = await sendRequest({ url: `${BASE_URL}/calendar/daily-overview?date=${date}`, method: "GET" });
56
+ return response.responseData as IApiResponse;
57
+ };
58
+
59
+ export const getCalendarSummary = async (employeeId: string | number, month: string): Promise<IApiResponse> => {
60
+ const url = employeeId === 'me'
61
+ ? `${BASE_URL}/ess/me/calendar/summary?month=${month}`
62
+ : `${BASE_URL}/employees/${employeeId}/calendar/summary?month=${month}`;
63
+
64
+ const response = await sendRequest({ url, method: "GET" });
65
+ return response.responseData as IApiResponse;
66
+ };
67
+
68
+ export const getAttendanceDays = async (employeeId: string | number, startDate: string, endDate: string, projectId?: string | number, workforceId?: string | number): Promise<IApiResponse> => {
69
+ const params: any = { start_date: startDate, end_date: endDate };
70
+ if (employeeId !== 0 && employeeId !== 'me') params.employee_id = employeeId;
71
+ if (projectId) params.project_id = projectId;
72
+ if (workforceId) params.workforce_id = workforceId;
73
+ const queryParams = new URLSearchParams(params);
74
+
75
+ const url = employeeId === 'me'
76
+ ? `${BASE_URL}/ess/me/attendance/days?${queryParams.toString()}`
77
+ : `${BASE_URL}/attendance/days?${queryParams.toString()}`;
78
+
79
+ const response = await sendRequest({ url, method: "GET" });
80
+ return response.responseData as IApiResponse;
81
+ };
82
+
83
+ export const getPendingActions = async (employeeId: string | number): Promise<IApiResponse> => {
84
+ const url = employeeId === 'me'
85
+ ? `${BASE_URL}/ess/me/calendar/pending-actions`
86
+ : `${BASE_URL}/employees/${employeeId}/calendar/pending-actions`;
87
+
88
+ const response = await sendRequest({ url, method: "GET" });
89
+ return response.responseData as IApiResponse;
90
+ };
@@ -0,0 +1,19 @@
1
+ import { sendRequest } from "../utils/httpClient";
2
+ import { IApiResponse } from "../common/interfaces/ICommon";
3
+
4
+ const BASE_URL = `${process.env.NEXT_PUBLIC_API_URL}/api/hr/attendance`;
5
+
6
+ export async function punchAttendance(data: any): Promise<IApiResponse> {
7
+ const response = await sendRequest({ url: `${BASE_URL}/punch`, method: "POST", data });
8
+ return response.responseData as IApiResponse;
9
+ }
10
+
11
+ export async function bulkPunchAttendance(data: { punches: any[] }): Promise<IApiResponse> {
12
+ const response = await sendRequest({ url: `${BASE_URL}/punches/bulk`, method: "POST", data });
13
+ return response.responseData as IApiResponse;
14
+ }
15
+
16
+ export async function getWorkforceAttendanceLookup(): Promise<IApiResponse> {
17
+ const response = await sendRequest({ url: `${process.env.NEXT_PUBLIC_API_URL}/api/construction/workforce/attendance-lookup`, method: "GET" });
18
+ return response.responseData as IApiResponse;
19
+ }
@@ -0,0 +1,8 @@
1
+ import { sendRequest, IApiResponse } from "../client";
2
+
3
+ const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/hr/employees`;
4
+
5
+ export async function getEmployeeLookup(): Promise<IApiResponse> {
6
+ const response = await sendRequest({ url: `${BASE}/lookup`, method: "GET" });
7
+ return response.responseData as IApiResponse;
8
+ }
@@ -0,0 +1,68 @@
1
+ import { sendRequest } from "../utils/httpClient";
2
+ import { IApiResponse } from "../common/interfaces/ICommon";
3
+
4
+ const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/hr/shift-templates`;
5
+ const ASSIGN_BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/hr/employees`;
6
+
7
+ export const getShiftTemplates = async (page = 1, search = "", status = ""): Promise<IApiResponse> => {
8
+ const queryParams = new URLSearchParams({ page: page.toString() });
9
+ if (search) queryParams.append("search", search);
10
+ if (status) queryParams.append("status", status);
11
+ const response = await sendRequest({ url: `${BASE}?${queryParams.toString()}`, method: "GET" });
12
+ return response.responseData as IApiResponse;
13
+ };
14
+
15
+ export const getShiftTemplate = async (id: string | number): Promise<IApiResponse> => {
16
+ const response = await sendRequest({ url: `${BASE}/${id}`, method: "GET" });
17
+ return response.responseData as IApiResponse;
18
+ };
19
+
20
+ export const createShiftTemplate = async (data: any): Promise<IApiResponse> => {
21
+ const response = await sendRequest({ url: BASE, method: "POST", data });
22
+ return response.responseData as IApiResponse;
23
+ };
24
+
25
+ export const updateShiftTemplate = async (id: string | number, data: any): Promise<IApiResponse> => {
26
+ const response = await sendRequest({ url: `${BASE}/${id}`, method: "PUT", data });
27
+ return response.responseData as IApiResponse;
28
+ };
29
+
30
+ export const deleteShiftTemplate = async (id: string | number): Promise<IApiResponse> => {
31
+ const response = await sendRequest({ url: `${BASE}/${id}`, method: "DELETE" });
32
+ return response.responseData as IApiResponse;
33
+ };
34
+
35
+ export const getShiftTemplateLookup = async (): Promise<IApiResponse> => {
36
+ const response = await sendRequest({ url: `${BASE}/lookup`, method: "GET" });
37
+ return response.responseData as IApiResponse;
38
+ };
39
+
40
+ export const getEmployeeShiftAssignment = async (employeeId: string | number): Promise<IApiResponse> => {
41
+ const response = await sendRequest({ url: `${ASSIGN_BASE}/${employeeId}/shift-assignment`, method: "GET" });
42
+ return response.responseData as IApiResponse;
43
+ };
44
+
45
+ export const assignShiftToEmployee = async (employeeId: string | number, data: any): Promise<IApiResponse> => {
46
+ const response = await sendRequest({ url: `${ASSIGN_BASE}/${employeeId}/shift-assignment`, method: "POST", data });
47
+ return response.responseData as IApiResponse;
48
+ };
49
+
50
+ export const getTemplateAssignments = async (templateId: string | number): Promise<IApiResponse> => {
51
+ const response = await sendRequest({ url: `${BASE}/${templateId}/assignments`, method: "GET" });
52
+ return response.responseData as IApiResponse;
53
+ };
54
+
55
+ export async function assignEmployeesToTemplate(templateId: number, data: any): Promise<IApiResponse> {
56
+ const response = await sendRequest({ url: `${BASE}/${templateId}/assignments`, method: "POST", data });
57
+ return response.responseData as IApiResponse;
58
+ }
59
+
60
+ export async function updateTemplateAssignment(templateId: number, assignmentId: number, data: any): Promise<IApiResponse> {
61
+ const response = await sendRequest({ url: `${BASE}/${templateId}/assignments/${assignmentId}`, method: "PUT", data });
62
+ return response.responseData as IApiResponse;
63
+ }
64
+
65
+ export const unassignEmployee = async (templateId: string | number, assignmentId: string | number): Promise<IApiResponse> => {
66
+ const response = await sendRequest({ url: `${BASE}/${templateId}/assignments/${assignmentId}`, method: "DELETE" });
67
+ return response.responseData as IApiResponse;
68
+ };
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/inventory/brands`;
4
5
 
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/inventory/categories`;
4
5
 
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
3
+ import { sendRequest } from "../../utils/httpClient";
4
+ import { IApiResponse } from "../../common/interfaces/ICommon";
4
5
 
5
6
  const BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
6
7
 
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/inventory/items`;
4
5
 
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/parties`;
4
5
 
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const INV_BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/inventory`;
4
5
  const FIN_BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/finance`;
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/inventory/uom-groups`;
4
5
 
@@ -1,4 +1,5 @@
1
- import { sendRequest, IApiResponse } from "@apptimate/core-lib";
1
+ import { sendRequest } from "../../utils/httpClient";
2
+ import { IApiResponse } from "../../common/interfaces/ICommon";
2
3
 
3
4
  const BASE = `${process.env.NEXT_PUBLIC_API_URL}/api/inventory/uom`;
4
5
 
package/src/client.ts CHANGED
@@ -13,4 +13,6 @@ export * from './utils/currencyService';
13
13
 
14
14
  export * from './api-services/procurement.client';
15
15
  export * from './api-services/printable-templates.client';
16
+ export * from './api-services/email-templates.client';
17
+ export * from './api-services/email-services.client';
16
18
  export * from './schemas/approvalWorkflowSchema';
@@ -85,11 +85,13 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
85
85
  { key: 'core__feature_registry', label: 'Feature Registry', path: '/system/features', module: 'Core', defaultGroup: 'System Auditing', permission: 'feature.view' },
86
86
  { key: 'core__menu_config', label: 'Menu Configuration', path: '/menu-config', module: 'Core', defaultGroup: 'System', permission: 'menu_config.view' },
87
87
  { key: 'core__printables', label: 'Printable Templates', path: '/system/printables', module: 'Core', defaultGroup: 'System', permission: 'printable_template.view' },
88
+ { key: 'core__email_templates', label: 'Email Templates', path: '/system/email-templates', module: 'Core', defaultGroup: 'System', permission: 'email_template.view' },
88
89
  { key: 'core__template_groups', label: 'Template Groups', path: '/system/template-groups', module: 'Core', defaultGroup: 'System', permission: 'template_group.view' },
89
90
  { key: 'core__warehouses', label: 'Warehouses', path: '/warehouses', module: 'Core', defaultGroup: 'Resources', permission: 'warehouse.view' },
90
91
  { key: 'core__terminals', label: 'Terminals', path: '/terminals', module: 'Core', defaultGroup: 'Resources', permission: 'terminal.view' },
91
92
  { key: 'core__currencies', label: 'Currencies', path: '/currencies', module: 'Core', defaultGroup: 'Resources', permission: 'currency.view' },
92
93
  { key: 'core__settings', label: 'Settings', path: '/settings', module: 'Core', defaultGroup: 'Configuration', permission: 'core_setting.view' },
94
+ { key: 'core__email_services', label: 'Integrations', path: '/settings/integrations', module: 'Core', defaultGroup: 'Configuration', permission: 'email_connection.view' },
93
95
  { key: 'core__approval_workflows', label: 'Approval Workflows', path: '/settings/approvals', module: 'Core', defaultGroup: 'Configuration', permission: null },
94
96
 
95
97
  // ── Inventory Module ──
@@ -240,35 +242,38 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
240
242
  // ── Construction Module ──
241
243
  { key: 'construction__dashboard', label: 'Overview', path: '/construction', module: 'Construction', defaultGroup: 'Overview', permission: null },
242
244
 
243
- { key: 'construction__projects', label: 'Projects', path: '/construction/projects', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
244
- { key: 'construction__rate_cards', label: 'Rate Cards', path: '/construction/rate-cards', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
245
- { key: 'construction__schedule_calendar', label: 'Resource Calendar', path: '/construction/scheduling/calendar', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
246
- { key: 'construction__hierarchy', label: 'Hierarchy', path: '/construction/hierarchy', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
247
- { key: 'construction__team', label: 'Team', path: '/construction/team', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
248
- { key: 'construction__budget', label: 'Budget', path: '/construction/budget', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
245
+ // ── Client Portal Module ──
246
+ { key: 'client_portal__dashboard', label: 'Overview', path: '/construction-customers', module: 'Client Portal', defaultGroup: 'Overview', permission: null },
247
+ { key: 'construction__projects', label: 'Projects', path: '/construction/projects', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
248
+ { key: 'construction__rate_cards', label: 'Rate Cards', path: '/construction/rate-cards', module: 'Construction', defaultGroup: 'Estimation & BOQ', permission: null },
249
+ { key: 'construction__schedule_calendar', label: 'Resource Calendar', path: '/construction/scheduling/calendar', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
250
+ { key: 'construction__hierarchy', label: 'Hierarchy', path: '/construction/hierarchy', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
251
+ { key: 'construction__team', label: 'Team', path: '/construction/team', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
252
+ { key: 'construction__budget', label: 'Budget', path: '/construction/budget', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
249
253
 
250
254
  { key: 'construction__main_contract', label: 'Main Contract', path: '/construction/contracts/main-contract', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
251
255
  { key: 'construction__subcontractors', label: 'Subcontractors', path: '/construction/contracts/subcontractors', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
252
256
  { key: 'construction__ipcs', label: 'Progress Billing', path: '/construction/contracts/progress-billing', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
253
257
  { key: 'construction__variations', label: 'Subcontractor Variations', path: '/construction/variations', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
254
- { key: 'construction__boq', label: 'Bill of Quantities (BOQ)', path: '/construction/boq', module: 'Construction', defaultGroup: 'Commercials & BOQ', permission: null },
255
- { key: 'construction__boq_revisions', label: 'BOQ Revisions', path: '/construction/boq/revisions', module: 'Construction', defaultGroup: 'Commercials & BOQ', permission: null },
256
- { key: 'construction__assemblies', label: 'Assemblies', path: '/construction/assemblies', module: 'Construction', defaultGroup: 'Commercials & BOQ', permission: null },
257
- { key: 'construction__assembly_groups', label: 'Assembly Groups', path: '/construction/assembly-groups', module: 'Construction', defaultGroup: 'Commercials & BOQ', permission: null },
258
+ { key: 'construction__boq', label: 'Bill of Quantities (BOQ)', path: '/construction/boq', module: 'Construction', defaultGroup: 'Estimation & BOQ', permission: null },
259
+ { key: 'construction__boq_revisions', label: 'BOQ Revisions', path: '/construction/boq/revisions', module: 'Construction', defaultGroup: 'Estimation & BOQ', permission: null },
260
+ { key: 'construction__assemblies', label: 'Assemblies', path: '/construction/assemblies', module: 'Construction', defaultGroup: 'Estimation & BOQ', permission: null },
261
+ { key: 'construction__assembly_groups', label: 'Assembly Groups', path: '/construction/assembly-groups', module: 'Construction', defaultGroup: 'Estimation & BOQ', permission: null },
258
262
 
259
263
 
260
264
 
261
- { key: 'construction__workforce', label: 'Workforce Master', path: '/construction/workforce', module: 'Construction', defaultGroup: 'Workforce', permission: null },
262
- { key: 'construction__workforce_allocation', label: 'Workforce Allocation', path: '/construction/workforce/allocation', module: 'Construction', defaultGroup: 'Workforce', permission: null },
263
- { key: 'construction__shifts', label: 'Shift Master', path: '/construction/shifts', module: 'Construction', defaultGroup: 'Workforce', permission: null },
264
- { key: 'construction__attendance', label: 'Attendance & Timesheets', path: '/construction/attendance', module: 'Construction', defaultGroup: 'Workforce', permission: null },
265
+ { key: 'construction__workforce', label: 'Workforce Master', path: '/construction/workforce', module: 'Construction', defaultGroup: 'Workforce Management', permission: null },
266
+ { key: 'construction__workforce_allocation', label: 'Workforce Allocation', path: '/construction/workforce/allocation', module: 'Construction', defaultGroup: 'Workforce Management', permission: null },
267
+ { key: 'construction__shifts', label: 'Shift Master', path: '/construction/shifts', module: 'Construction', defaultGroup: 'Workforce Management', permission: null },
268
+ { key: 'construction__attendance', label: 'Attendance & Timesheets', path: '/construction/attendance', module: 'Construction', defaultGroup: 'Workforce Management', permission: null },
265
269
 
266
- { key: 'construction__equipment_types', label: 'Equipment Types', path: '/construction/equipment/types', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
267
- { key: 'construction__equipment', label: 'Equipments', path: '/construction/equipment', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
268
- { key: 'construction__equipment_allocation', label: 'Equipment Allocation', path: '/construction/equipment/allocation', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
269
- { key: 'construction__fuel_logs', label: 'Fuel Logs', path: '/construction/equipment/fuel-logs', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
270
- { key: 'construction__maintenance', label: 'Maintenance', path: '/construction/equipment/maintenance', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
270
+ { key: 'construction__equipment_types', label: 'Equipment Types', path: '/construction/equipment/types', module: 'Construction', defaultGroup: 'Plant & Equipment', permission: null },
271
+ { key: 'construction__equipment', label: 'Equipments', path: '/construction/equipment', module: 'Construction', defaultGroup: 'Plant & Equipment', permission: null },
272
+ { key: 'construction__equipment_allocation', label: 'Equipment Allocation', path: '/construction/equipment/allocation', module: 'Construction', defaultGroup: 'Plant & Equipment', permission: null },
273
+ { key: 'construction__fuel_logs', label: 'Fuel Logs', path: '/construction/equipment/fuel-logs', module: 'Construction', defaultGroup: 'Plant & Equipment', permission: null },
274
+ { key: 'construction__maintenance', label: 'Maintenance', path: '/construction/equipment/maintenance', module: 'Construction', defaultGroup: 'Plant & Equipment', permission: null },
271
275
 
276
+ { key: 'construction__site_engineer', label: 'Site Engineer', path: '/construction/dashboard/site-engineer', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
272
277
  { key: 'construction__dsr', label: 'Daily Site Reports', path: '/construction/site-execution/dsr', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
273
278
  { key: 'construction__progress', label: 'Progress Measurement', path: '/construction/site-execution/progress', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
274
279
  { key: 'construction__subcontractor_progress', label: 'Subcontractor Progress', path: '/construction/site-execution/subcontractor-progress', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
@@ -380,7 +385,7 @@ export const MENU_PRESETS: MenuPreset[] = [
380
385
  'core__warehouses', 'core__terminals',
381
386
  'core__activity_logs', 'core__api_requests', 'core__feature_access_logs',
382
387
  'core__issue_logs', 'core__feature_registry', 'core__menu_config',
383
- 'core__printables', 'core__template_groups', 'core__currencies', 'core__settings', 'core__approval_workflows',
388
+ 'core__printables', 'core__email_templates', 'core__template_groups', 'core__currencies', 'core__settings', 'core__email_services', 'core__approval_workflows',
384
389
  ],
385
390
  },
386
391
  {
@@ -506,54 +511,65 @@ export const MENU_PRESETS: MenuPreset[] = [
506
511
  {
507
512
  id: 'construction',
508
513
  label: 'Construction',
509
- iconName: 'Crane',
514
+ iconName: 'Hammer',
510
515
  secondaryItems: [
516
+ // 1. Overview
511
517
  'construction__dashboard',
518
+ // 2. Project Planning
512
519
  'construction__projects',
513
- 'construction__rate_cards',
520
+ 'construction__schedule_calendar',
514
521
  'construction__hierarchy',
515
- 'construction__team',
516
522
  'construction__budget',
517
- 'construction__mtrs',
518
- 'construction__prs',
519
- 'construction__pos',
520
- 'construction__grns',
521
- 'construction__site_ledger',
522
- 'construction__site_issues',
523
- 'construction__wastage_returns',
524
- 'construction__variations',
523
+ 'construction__team',
524
+ // 3. Estimation & BOQ
525
525
  'construction__boq',
526
526
  'construction__boq_revisions',
527
+ 'construction__rate_cards',
527
528
  'construction__assemblies',
528
529
  'construction__assembly_groups',
530
+ // 4. Contracts & Billing
531
+ 'construction__main_contract',
532
+ 'construction__subcontractors',
533
+ 'construction__ipcs',
534
+ 'construction__variations',
535
+ // 5. Workforce Management
529
536
  'construction__workforce',
530
537
  'construction__workforce_allocation',
531
538
  'construction__shifts',
532
539
  'construction__attendance',
540
+ // 6. Plant & Equipment
533
541
  'construction__equipment_types',
534
542
  'construction__equipment',
535
543
  'construction__equipment_allocation',
536
544
  'construction__fuel_logs',
537
545
  'construction__maintenance',
538
- 'construction__schedule_calendar',
539
- 'construction__main_contract',
540
- 'construction__subcontractors',
541
- 'construction__ipcs',
546
+ // 7. Site Execution
547
+ 'construction__site_engineer',
542
548
  'construction__dsr',
543
549
  'construction__progress',
544
550
  'construction__subcontractor_progress',
545
551
  'construction__change_orders',
552
+ 'construction__mtrs',
553
+ 'construction__prs',
554
+ 'construction__pos',
555
+ 'construction__grns',
556
+ 'construction__site_ledger',
557
+ 'construction__site_issues',
558
+ 'construction__wastage_returns',
559
+ // 8. Document Control
546
560
  'construction__drawings',
547
- 'construction__transmittals',
548
561
  'construction__rfis',
562
+ 'construction__transmittals',
563
+ // 9. Quality & Safety
549
564
  'construction__quality_ir',
550
565
  'construction__quality_ncr',
551
566
  'construction__punch_list',
552
567
  'construction__material_testing',
553
568
  'construction__safety_incidents',
554
569
  'construction__ptw',
555
- 'construction__test_types',
570
+ // 10. Configuration
556
571
  'construction__project_categories',
572
+ 'construction__test_types',
557
573
  'construction__settings',
558
574
  ],
559
575
  },
@@ -564,6 +580,14 @@ export const MENU_PRESETS: MenuPreset[] = [
564
580
  secondaryItems: [
565
581
  ...CRM_SECONDARY_ITEM_KEYS,
566
582
  ],
583
+ },
584
+ {
585
+ id: 'site_engineer',
586
+ label: 'Site Engineer',
587
+ iconName: 'Wrench',
588
+ secondaryItems: [
589
+ 'construction__site_engineer',
590
+ ],
567
591
  }
568
592
  ],
569
593
  },
@@ -11,5 +11,6 @@ export const cookie = {
11
11
  export const local_storage = {
12
12
  user_info: { name: 'user_info', secretName: 'USER_INFO_SECRET', encrypted: false } as IStorageOptions,
13
13
  selected_organization: { name: 'selected_organization', secretName: 'SELECTED_ORG', encrypted: false } as IStorageOptions,
14
- sales_terminal: { name: 'sales_terminal', secretName: 'SALES_TERMINAL', encrypted: false } as IStorageOptions
14
+ sales_terminal: { name: 'sales_terminal', secretName: 'SALES_TERMINAL', encrypted: false } as IStorageOptions,
15
+ active_project: { name: 'active_project', secretName: 'ACTIVE_PROJECT', encrypted: false } as IStorageOptions
15
16
  };
package/src/index.ts CHANGED
@@ -10,4 +10,8 @@ export * from './api-services/inventory/uom.client';
10
10
  export * from './api-services/inventory/uom-group.client';
11
11
  export * from './api-services/inventory/parties.client';
12
12
  export * from './api-services/inventory/transaction.client';
13
- export * from './utils/PriceCalculationService';
13
+ export * from './utils/PriceCalculationService';
14
+ export * from './api-services/hr-attendance.client';
15
+ export * from './api-services/hr-attendance-shifts.client';
16
+ export * from './api-services/hr-shift-templates.client';
17
+ export * from './api-services/hr-employees.client';
package/src/server.ts CHANGED
@@ -13,3 +13,4 @@ export * from './utils/bootstrapConfig';
13
13
 
14
14
  export * from './api-services/procurement.client';
15
15
  export * from './api-services/printable-templates.client';
16
+ export * from './api-services/email-templates.client';