@apptimate/core-lib 5.2.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.2.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 ──
@@ -169,7 +171,6 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
169
171
  { key: 'jewelry__party_transactions', label: 'Party Transactions', path: '/jewelry/party-transactions', module: 'Jewelry', defaultGroup: 'Karigar', permission: 'jewelry.karigar_transactions.view' },
170
172
  { key: 'jewelry__karigar_orders', label: 'Karigar Orders', path: '/jewelry/karigar-orders', module: 'Jewelry', defaultGroup: 'Karigar', permission: 'jewelry.karigar_orders.view' },
171
173
 
172
- { key: 'jewelry__karigar_making_charges', label: 'Making Charges', path: '/jewelry/karigar-making-charges', module: 'Jewelry', defaultGroup: 'Karigar', permission: 'jewelry.karigar_making_charges.view' },
173
174
  { key: 'jewelry__party_metal_balances', label: 'Party Metal Balances', path: '/jewelry/party-metal-balances', module: 'Jewelry', defaultGroup: 'Karigar', permission: 'jewelry.party_metal_balances.view' },
174
175
  { key: 'jewelry__metal_settlements', label: 'Metal Settlements', path: '/jewelry/metal-settlements', module: 'Jewelry', defaultGroup: 'Karigar', permission: 'metal_settlement.view' },
175
176
  { key: 'jewelry__settings', label: 'Settings', path: '/jewelry/settings', module: 'Jewelry', defaultGroup: 'Configuration', permission: 'jewelry.settings.view' },
@@ -241,31 +242,42 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
241
242
  // ── Construction Module ──
242
243
  { key: 'construction__dashboard', label: 'Overview', path: '/construction', module: 'Construction', defaultGroup: 'Overview', permission: null },
243
244
 
244
- { key: 'construction__projects', label: 'Projects', path: '/construction/projects', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
245
- { key: 'construction__rate_cards', label: 'Rate Cards', path: '/construction/rate-cards', module: 'Construction', defaultGroup: 'Core Setup & Planning', permission: null },
246
- { key: 'construction__schedule_calendar', label: 'Resource Calendar', path: '/construction/scheduling/calendar', 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 },
247
253
 
248
254
  { key: 'construction__main_contract', label: 'Main Contract', path: '/construction/contracts/main-contract', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
249
255
  { key: 'construction__subcontractors', label: 'Subcontractors', path: '/construction/contracts/subcontractors', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
250
- { key: 'construction__ipcs', label: 'Progress Billing', path: '/construction/contracts/ipcs', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
256
+ { key: 'construction__ipcs', label: 'Progress Billing', path: '/construction/contracts/progress-billing', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
251
257
  { key: 'construction__variations', label: 'Subcontractor Variations', path: '/construction/variations', module: 'Construction', defaultGroup: 'Contracts & Billing', 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 },
252
262
 
253
263
 
254
264
 
255
- { key: 'construction__workforce', label: 'Workforce Master', path: '/construction/workforce', module: 'Construction', defaultGroup: 'Workforce', permission: null },
256
- { key: 'construction__workforce_allocation', label: 'Workforce Allocation', path: '/construction/workforce/allocation', module: 'Construction', defaultGroup: 'Workforce', permission: null },
257
- { key: 'construction__shifts', label: 'Shift Master', path: '/construction/shifts', module: 'Construction', defaultGroup: 'Workforce', permission: null },
258
- { 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 },
259
269
 
260
- { key: 'construction__equipment_types', label: 'Equipment Types', path: '/construction/equipment/types', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
261
- { key: 'construction__equipment', label: 'Equipments', path: '/construction/equipment', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
262
- { key: 'construction__equipment_allocation', label: 'Equipment Allocation', path: '/construction/equipment/allocation', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
263
- { key: 'construction__fuel_logs', label: 'Fuel Logs', path: '/construction/equipment/fuel-logs', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
264
- { 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 },
265
275
 
276
+ { key: 'construction__site_engineer', label: 'Site Engineer', path: '/construction/dashboard/site-engineer', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
266
277
  { key: 'construction__dsr', label: 'Daily Site Reports', path: '/construction/site-execution/dsr', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
267
278
  { key: 'construction__progress', label: 'Progress Measurement', path: '/construction/site-execution/progress', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
268
279
  { key: 'construction__subcontractor_progress', label: 'Subcontractor Progress', path: '/construction/site-execution/subcontractor-progress', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
280
+ { key: 'construction__change_orders', label: 'Change Orders', path: '/construction/change-orders', module: 'Construction', defaultGroup: 'Site Execution', permission: null },
269
281
 
270
282
  { key: 'construction__quality_ir', label: 'Inspection Requests', path: '/construction/quality/ir', module: 'Construction', defaultGroup: 'Quality & Safety', permission: null },
271
283
  { key: 'construction__quality_ncr', label: 'Non-Conformance Reports', path: '/construction/quality/ncr', module: 'Construction', defaultGroup: 'Quality & Safety', permission: null },
@@ -373,7 +385,7 @@ export const MENU_PRESETS: MenuPreset[] = [
373
385
  'core__warehouses', 'core__terminals',
374
386
  'core__activity_logs', 'core__api_requests', 'core__feature_access_logs',
375
387
  'core__issue_logs', 'core__feature_registry', 'core__menu_config',
376
- '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',
377
389
  ],
378
390
  },
379
391
  {
@@ -433,7 +445,6 @@ export const MENU_PRESETS: MenuPreset[] = [
433
445
  'jewelry__party_transactions',
434
446
  'jewelry__karigar_orders',
435
447
 
436
- 'jewelry__karigar_making_charges',
437
448
  'jewelry__party_metal_balances',
438
449
  'jewelry__metal_settlements',
439
450
  'jewelry__reports',
@@ -500,46 +511,65 @@ export const MENU_PRESETS: MenuPreset[] = [
500
511
  {
501
512
  id: 'construction',
502
513
  label: 'Construction',
503
- iconName: 'Crane',
514
+ iconName: 'Hammer',
504
515
  secondaryItems: [
516
+ // 1. Overview
505
517
  'construction__dashboard',
518
+ // 2. Project Planning
506
519
  'construction__projects',
520
+ 'construction__schedule_calendar',
521
+ 'construction__hierarchy',
522
+ 'construction__budget',
523
+ 'construction__team',
524
+ // 3. Estimation & BOQ
525
+ 'construction__boq',
526
+ 'construction__boq_revisions',
507
527
  'construction__rate_cards',
508
- 'construction__mtrs',
509
- 'construction__prs',
510
- 'construction__pos',
511
- 'construction__grns',
512
- 'construction__site_ledger',
513
- 'construction__site_issues',
514
- 'construction__wastage_returns',
528
+ 'construction__assemblies',
529
+ 'construction__assembly_groups',
530
+ // 4. Contracts & Billing
531
+ 'construction__main_contract',
532
+ 'construction__subcontractors',
533
+ 'construction__ipcs',
515
534
  'construction__variations',
535
+ // 5. Workforce Management
516
536
  'construction__workforce',
517
537
  'construction__workforce_allocation',
518
538
  'construction__shifts',
519
539
  'construction__attendance',
540
+ // 6. Plant & Equipment
520
541
  'construction__equipment_types',
521
542
  'construction__equipment',
522
543
  'construction__equipment_allocation',
523
544
  'construction__fuel_logs',
524
545
  'construction__maintenance',
525
- 'construction__schedule_calendar',
526
- 'construction__main_contract',
527
- 'construction__subcontractors',
528
- 'construction__ipcs',
546
+ // 7. Site Execution
547
+ 'construction__site_engineer',
529
548
  'construction__dsr',
530
549
  'construction__progress',
531
550
  'construction__subcontractor_progress',
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
532
560
  'construction__drawings',
533
- 'construction__transmittals',
534
561
  'construction__rfis',
562
+ 'construction__transmittals',
563
+ // 9. Quality & Safety
535
564
  'construction__quality_ir',
536
565
  'construction__quality_ncr',
537
566
  'construction__punch_list',
538
567
  'construction__material_testing',
539
568
  'construction__safety_incidents',
540
569
  'construction__ptw',
541
- 'construction__test_types',
570
+ // 10. Configuration
542
571
  'construction__project_categories',
572
+ 'construction__test_types',
543
573
  'construction__settings',
544
574
  ],
545
575
  },
@@ -550,6 +580,14 @@ export const MENU_PRESETS: MenuPreset[] = [
550
580
  secondaryItems: [
551
581
  ...CRM_SECONDARY_ITEM_KEYS,
552
582
  ],
583
+ },
584
+ {
585
+ id: 'site_engineer',
586
+ label: 'Site Engineer',
587
+ iconName: 'Wrench',
588
+ secondaryItems: [
589
+ 'construction__site_engineer',
590
+ ],
553
591
  }
554
592
  ],
555
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';
@@ -51,7 +51,7 @@ export interface ClientResponse<T = any> {
51
51
  responseData: IApiResponse<T>;
52
52
  }
53
53
 
54
- function buildBrowserProxyUrl(rawUrl: string): string {
54
+ export function buildBrowserProxyUrl(rawUrl: string): string {
55
55
  if (typeof window === 'undefined') {
56
56
  return rawUrl;
57
57
  }