nexus-shared 1.1.18 → 1.1.20
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 +1 -1
- package/src/api-services/authentication-service.tsx +23 -0
- package/src/api-services/system-service.tsx +28 -315
- package/src/client.ts +2 -2
- package/src/components/documents/button.tsx +17 -11
- package/src/components/documents/icon-box.tsx +1 -1
- package/src/components/documents/tab-button.tsx +2 -2
- package/src/components/documents/tag.tsx +30 -0
- package/src/components/inputs/input-box.tsx +4 -2
- package/src/components/inputs/input-element.tsx +1 -1
- package/src/components/inputs/input-form.tsx +126 -14
- package/src/components/layouts/global-dialogbox.tsx +5 -9
- package/src/components/layouts/global-layout.tsx +3 -0
- package/src/components/layouts/layout-helpers.tsx +1 -4
- package/src/components/layouts/panels/user-panel.tsx +112 -0
- package/src/components/layouts/utility-menu.tsx +9 -29
- package/src/components/panels/theme-panel.tsx +2 -2
- package/src/helpers/browser-helpers.tsx +88 -3
- package/src/helpers/input-helpers.tsx +34 -4
- package/src/helpers/validation-helpers.tsx +302 -0
- package/src/index.ts +2 -18
- package/src/interface.ts +5 -1
- package/src/interfaces/auth-token-interfaces.tsx +6 -0
- package/src/interfaces/browser-interfaces.tsx +13 -0
- package/src/interfaces/button-interfaces.tsx +2 -2
- package/src/interfaces/http-interfaces.tsx +86 -8
- package/src/interfaces/icon-interfaces.tsx +3 -0
- package/src/interfaces/input-interfaces.tsx +61 -11
- package/src/interfaces/menu-interfaces.tsx +13 -0
- package/src/interfaces/message-interfaces.tsx +32 -0
- package/src/interfaces/storage-interfaces.tsx +2 -0
- package/src/interfaces/theme-interfaces.tsx +43 -129
- package/src/interfaces/type-interfaces.tsx +8 -2
- package/src/interfaces/user-interfaces.tsx +47 -0
- package/src/nexus.environments.tsx +41 -24
- package/src/proxy-api/proxy-backend.tsx +60 -0
- package/src/proxy-api/proxy-constants.tsx +28 -0
- package/src/proxy-api/proxy-helpers.tsx +91 -0
- package/src/proxy-api-client.tsx +1 -0
- package/src/proxy-api-server.tsx +2 -0
- package/src/services/http-client-services.tsx +26 -11
- package/src/services/http-services.tsx +42 -27
- package/src/services/loader-service.tsx +34 -17
- package/src/services/localstorage-service.tsx +81 -5
- package/src/services/message-services.tsx +383 -0
- package/src/services/theme-service.tsx +30 -16
- package/src/services/user-services.tsx +10 -0
- package/src/sso-config/auth-token-validation.ts +16 -0
- package/src/sso-config/callback-route.tsx +35 -0
- package/src/sso-config/cookie-encryption.ts +51 -0
- package/src/sso-config/cookie-helpers.tsx +102 -0
- package/src/sso-config/forgot-password-route.tsx +36 -0
- package/src/sso-config/oauth-callback-state.ts +61 -0
- package/src/sso-config/pkce-helpers.ts +9 -0
- package/src/sso-config/provider-complete-route.tsx +18 -0
- package/src/sso-config/redirect-context-actions.tsx +45 -0
- package/src/sso-config/redirect-context-helpers.tsx +159 -0
- package/src/sso-config/redirect-context-persist.tsx +18 -0
- package/src/sso-config/redirect-context-route.tsx +45 -0
- package/src/sso-config/refresh-route.tsx +23 -0
- package/src/sso-config/sign-in-route.tsx +74 -0
- package/src/sso-config/sign-out-route.tsx +57 -0
- package/src/sso-config/sign-up-route.tsx +57 -0
- package/src/sso-config/sso-backend-flow.ts +38 -0
- package/src/sso-config/sso-complete-route.tsx +50 -0
- package/src/sso-config/sso-config.ts +59 -0
- package/src/sso-config/sso-interfaces.tsx +99 -0
- package/src/sso-config/sso-response-helpers.tsx +20 -0
- package/src/sso-server.tsx +21 -1
- package/src/styles/nexus.dialog.css +3 -0
- package/src/styles/nexus.icon.css +1 -0
- package/src/styles/nexus.logic.css +34 -5
- package/src/interfaces/sso-interfaces.tsx +0 -52
- package/src/load-env.ts +0 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { EErrorCodes, EExceptionCategories } from "../interfaces/exception-interfaces";
|
|
3
|
+
import { GetResponse, PROXY_PUBLIC_BASE_PATH } from "../interfaces/http-interfaces";
|
|
4
|
+
import { EModules } from "../interfaces/system-interfaces";
|
|
5
|
+
import { PROXY_REQUEST_HEADERS_TO_SKIP, PROXY_RESPONSE_HEADERS_TO_SKIP } from "./proxy-constants";
|
|
6
|
+
|
|
7
|
+
const BACKEND_NOT_CONFIGURED_ERROR = "Backend API is not configured.";
|
|
8
|
+
|
|
9
|
+
export function BuildBackendUrl(backendBase: string, pathSegments: string[], searchParams: URLSearchParams): string {
|
|
10
|
+
if (pathSegments.some((segment) => segment === ".." || segment === "." || segment.includes("\\"))) throw new Error("Invalid path.");
|
|
11
|
+
else if (!backendBase) throw new Error(BACKEND_NOT_CONFIGURED_ERROR);
|
|
12
|
+
|
|
13
|
+
const path = pathSegments.map(encodeURIComponent).join("/");
|
|
14
|
+
const query = searchParams.toString();
|
|
15
|
+
|
|
16
|
+
return query ? `${backendBase}/${path}?${query}` : `${backendBase}/${path}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function BuildForwardHeaders(req: NextRequest): Headers {
|
|
20
|
+
const headers = new Headers();
|
|
21
|
+
|
|
22
|
+
req.headers.forEach((value, key) => {
|
|
23
|
+
if (PROXY_REQUEST_HEADERS_TO_SKIP.has(key.toLowerCase())) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
headers.set(key, value);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return headers;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function ReadRequestBody(req: NextRequest, method: string): Promise<BodyInit | undefined> {
|
|
34
|
+
if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return await req.arrayBuffer();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function ProxyErrorResponse(message: string, status: number, messageCode = "PROXY_ERROR", errorCode: EErrorCodes = EErrorCodes.Unknown): NextResponse {
|
|
42
|
+
return NextResponse.json(GetResponse.ERROR(message, messageCode, EExceptionCategories.Application, errorCode, undefined, null, status), { status });
|
|
43
|
+
}
|
|
44
|
+
function stripBackendBasePath(pathname: string, backendBaseUrl: string): string {
|
|
45
|
+
try {
|
|
46
|
+
const basePath = new URL(backendBaseUrl).pathname.replace(/\/+$/, "");
|
|
47
|
+
if (pathname.startsWith(basePath)) {
|
|
48
|
+
return pathname.slice(basePath.length).replace(/^\/+/, "");
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
// Fall through to pathname cleanup below.
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return pathname.replace(/^\/+/, "");
|
|
55
|
+
}
|
|
56
|
+
export function SanitizeResponseHeaders(headers: Headers, backendBaseUrl: string, module: EModules): Headers {
|
|
57
|
+
const sanitized = new Headers();
|
|
58
|
+
let backendHost: string | undefined;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
backendHost = new URL(backendBaseUrl).host;
|
|
62
|
+
} catch {
|
|
63
|
+
backendHost = undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
headers.forEach((value, key) => {
|
|
67
|
+
const lowerKey = key.toLowerCase();
|
|
68
|
+
|
|
69
|
+
if (PROXY_RESPONSE_HEADERS_TO_SKIP.has(lowerKey)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (lowerKey === "location" && backendHost) {
|
|
74
|
+
try {
|
|
75
|
+
const location = new URL(value, backendBaseUrl);
|
|
76
|
+
if (location.host === backendHost) {
|
|
77
|
+
const relativePath = stripBackendBasePath(location.pathname, backendBaseUrl);
|
|
78
|
+
const proxyPath = `${PROXY_PUBLIC_BASE_PATH}/${module}/${relativePath}${location.search}`;
|
|
79
|
+
sanitized.set(key, proxyPath);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Relative locations are forwarded as-is.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
sanitized.set(key, value);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return sanitized;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./proxy-api/proxy-constants";
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Client-side HTTP utilities for `"use client"` pages and components.
|
|
5
|
+
* Adds UI-level response handling (loader/toast hooks). For Route Handlers and
|
|
6
|
+
* Server Components use `http-services` (`HttpPost`, `HttpGet`, …) from `nexus-shared` instead.
|
|
7
|
+
*/
|
|
8
|
+
import { DatasourceTypes, EBody, GetApiInformations, GetResponse, IApiInformations, IErrorRespose, IInterface, ISuccessResponse, UN } from "../interface";
|
|
4
9
|
import { HttpDelete, HttpGet, HttpPost, HttpPut } from "./http-services";
|
|
10
|
+
import { ErrorMessage, SuccessMessage } from "./message-services";
|
|
5
11
|
|
|
6
12
|
/** // if same url is called multiple times, it will only send one request and return the same promise
|
|
7
13
|
// this is to prevent multiple requests to the same url at the same time
|
|
@@ -12,7 +18,7 @@ import { HttpDelete, HttpGet, HttpPost, HttpPut } from "./http-services";
|
|
|
12
18
|
// this is useful for example when user is typing in a search box and multiple requests are sent to the server
|
|
13
19
|
*/
|
|
14
20
|
const pendingRequests: { [url: string]: Promise<any> | undefined } = {};
|
|
15
|
-
export const Get = async (apiUrl: string, successMessage?: boolean, errorMessage?: boolean
|
|
21
|
+
export const Get = async (apiUrl: string, successMessage?: boolean | UN, errorMessage?: boolean | UN): Promise<ISuccessResponse | IErrorRespose> => {
|
|
16
22
|
if (pendingRequests[apiUrl] !== undefined) return pendingRequests[apiUrl]!;
|
|
17
23
|
|
|
18
24
|
pendingRequests[apiUrl] = HttpGet(apiUrl)
|
|
@@ -23,21 +29,21 @@ export const Get = async (apiUrl: string, successMessage?: boolean, errorMessage
|
|
|
23
29
|
return pendingRequests[apiUrl]!;
|
|
24
30
|
};
|
|
25
31
|
|
|
26
|
-
export const Put = async (apiUrl: string, body?: any |
|
|
27
|
-
return await handleResponse(await HttpPut(apiUrl, body, bodyType, null,
|
|
32
|
+
export const Put = async (apiUrl: string, body?: any | UN, bodyType?: EBody | UN, successMessage?: boolean | UN, errorMessage?: boolean | UN) => {
|
|
33
|
+
return await handleResponse(await HttpPut(apiUrl, body, bodyType, null, 0, null), successMessage ?? true, errorMessage ?? true);
|
|
28
34
|
};
|
|
29
35
|
|
|
30
|
-
export const Post = async (apiUrl: string, body?: any |
|
|
36
|
+
export const Post = async (apiUrl: string, body?: any | UN, bodyType?: EBody | UN, successMessage?: boolean | UN, errorMessage?: boolean | UN) => {
|
|
31
37
|
const isFindListing = Array.isArray(body) && apiUrl.endsWith("/FindListing");
|
|
32
38
|
|
|
33
39
|
if (!isFindListing) {
|
|
34
|
-
return await handleResponse(await HttpPost(apiUrl, body, bodyType, null,
|
|
40
|
+
return await handleResponse(await HttpPost(apiUrl, body, bodyType, null, 0, null), successMessage ?? true, errorMessage ?? true);
|
|
35
41
|
} else {
|
|
36
42
|
const apiKey = `${apiUrl}__${(body as string[]).sort().join(",")}`;
|
|
37
43
|
|
|
38
44
|
if (pendingRequests[apiKey]) return pendingRequests[apiKey];
|
|
39
45
|
|
|
40
|
-
pendingRequests[apiKey] = HttpPost(apiUrl, body, bodyType, null,
|
|
46
|
+
pendingRequests[apiKey] = HttpPost(apiUrl, body, bodyType, null, 0, null)
|
|
41
47
|
.then((response) => handleResponse(response, successMessage ?? true, errorMessage ?? true))
|
|
42
48
|
.finally(() => {
|
|
43
49
|
delete pendingRequests[apiKey];
|
|
@@ -46,15 +52,24 @@ export const Post = async (apiUrl: string, body?: any | null, bodyType?: EBody,
|
|
|
46
52
|
}
|
|
47
53
|
};
|
|
48
54
|
|
|
49
|
-
export const Delete = async (apiUrl: string, successMessage?: boolean, errorMessage?: boolean
|
|
50
|
-
return await handleResponse(await HttpDelete(apiUrl, null,
|
|
55
|
+
export const Delete = async (apiUrl: string, successMessage?: boolean | UN, errorMessage?: boolean | UN) => {
|
|
56
|
+
return await handleResponse(await HttpDelete(apiUrl, null, 0, null), successMessage ?? true, errorMessage ?? true);
|
|
57
|
+
};
|
|
58
|
+
export const SubmitApi = async (api: DatasourceTypes | UN, parameters: IInterface | IInterface[] | UN, successMessage?: boolean | UN, errorMessage?: boolean | UN): Promise<IErrorRespose | ISuccessResponse> => {
|
|
59
|
+
if (!api || Array.isArray(api) || !GetApiInformations.IS_API(api)) return Promise.resolve(GetResponse.SUCCESS(api));
|
|
60
|
+
const apiInfo = api as IApiInformations;
|
|
61
|
+
|
|
62
|
+
const url = GetApiInformations.BUILD_API(apiInfo, parameters);
|
|
63
|
+
|
|
64
|
+
// TODO:
|
|
65
|
+
return Post(url, parameters, null, successMessage, errorMessage);
|
|
51
66
|
};
|
|
52
67
|
|
|
53
68
|
const handleResponse = async (result: ISuccessResponse | IErrorRespose, successMessage?: boolean, errorMessage?: boolean): Promise<ISuccessResponse | IErrorRespose> => {
|
|
54
69
|
if (result.isSuccess && !(result as IErrorRespose).exception) {
|
|
55
|
-
|
|
70
|
+
if (successMessage) SuccessMessage("Request Success!", result.message ?? "Success");
|
|
56
71
|
return result;
|
|
57
72
|
}
|
|
58
|
-
|
|
73
|
+
if (errorMessage) ErrorMessage("Request Failed!", result.message ?? "Failed");
|
|
59
74
|
return result;
|
|
60
75
|
};
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side HTTP utilities for Route Handlers, Server Components, and Server Actions.
|
|
3
|
+
* For browser/client components use `http-client-services` (`Post`, `Get`, …) via `nexus-shared/client`.
|
|
4
|
+
*/
|
|
1
5
|
import { Delay } from "../helpers/utility-helpers";
|
|
2
|
-
import { EActions, EBody, EModules, GetMethod, IErrorRespose, ISuccessResponse } from "../interface";
|
|
6
|
+
import { EActions, EBody, EModules, GetMethod, GetResponse, IErrorRespose, ISuccessResponse, UN } from "../interface";
|
|
3
7
|
import { EErrorCodes, EExceptionCategories } from "../interfaces/exception-interfaces";
|
|
4
8
|
import { APIS, NEXUS_CONFIG, NEXUS_INFO } from "../nexus.environments";
|
|
5
9
|
|
|
6
10
|
const MAX_RETRIES = NEXUS_INFO.IS_PRODUCTION ? 2 : 1;
|
|
7
11
|
const RETRY_DELAY_MS = NEXUS_INFO.IS_PRODUCTION ? 50 : 1;
|
|
8
12
|
|
|
9
|
-
const
|
|
13
|
+
export const BuildApiHeader = (formdata?: any, httpMethod?: EActions | string, bodyType: EBody | UN = EBody.JSON, token?: string | UN, revalidate?: number | UN, browserId?: string | UN): RequestInit & { next?: { revalidate: number } } => {
|
|
14
|
+
httpMethod = httpMethod ?? "GET";
|
|
15
|
+
bodyType = bodyType ?? EBody.JSON;
|
|
16
|
+
|
|
10
17
|
const headers = new Headers();
|
|
11
|
-
if (token && token.length > 50) headers.append("authorization",
|
|
18
|
+
if (token && token.length > 50) headers.append("authorization", token.startsWith("Bearer ") ? token : `Bearer ${token}`);
|
|
12
19
|
if (browserId) headers.append("bid", browserId);
|
|
13
|
-
|
|
14
|
-
headers.append("Content-Type", "application/json");
|
|
15
|
-
}
|
|
20
|
+
headers.append("Content-Type", bodyType);
|
|
16
21
|
|
|
17
22
|
const options: RequestInit & { next?: { revalidate: number } } = {
|
|
18
|
-
method: GetMethod(httpMethod),
|
|
23
|
+
method: typeof httpMethod === "string" ? httpMethod : GetMethod(httpMethod),
|
|
19
24
|
headers,
|
|
20
25
|
};
|
|
21
26
|
|
|
22
|
-
if (formdata)
|
|
23
|
-
options.body = bodyType === EBody.JSON ? JSON.stringify(formdata) : formdata;
|
|
24
|
-
}
|
|
27
|
+
if (formdata) options.body = bodyType === EBody.JSON ? JSON.stringify(formdata) : formdata;
|
|
25
28
|
|
|
26
29
|
if (!revalidate || revalidate <= 0) {
|
|
27
30
|
// No caching
|
|
@@ -55,29 +58,41 @@ const fetchWithTimeout = async (url: string, options?: RequestInit, timeout = NE
|
|
|
55
58
|
const id = setTimeout(() => controller.abort(), timeout);
|
|
56
59
|
try {
|
|
57
60
|
const response = await fetch(url, { ...options, signal: controller.signal });
|
|
58
|
-
clearTimeout(id);
|
|
59
61
|
return response;
|
|
60
62
|
} catch (error) {
|
|
61
|
-
clearTimeout(id);
|
|
62
63
|
throw error;
|
|
64
|
+
} finally {
|
|
65
|
+
clearTimeout(id);
|
|
63
66
|
}
|
|
64
67
|
};
|
|
65
|
-
export const HttpGet = async (apiUrl: string, token?: string |
|
|
66
|
-
return await safeFetch(() => fetchWithTimeout(apiUrl,
|
|
68
|
+
export const HttpGet = async (apiUrl: string, token?: string | UN, revalidate?: number | UN, browserId?: string | UN) => {
|
|
69
|
+
return await safeFetch(() => fetchWithTimeout(apiUrl, BuildApiHeader(undefined, EActions.Get, EBody.JSON, token, revalidate, browserId)));
|
|
70
|
+
};
|
|
71
|
+
export const HttpPost = async (apiUrl: string, body?: any | UN, bodyType?: EBody | UN, token?: string | UN, revalidate?: number | UN, browserId?: string | UN) => {
|
|
72
|
+
return await safeFetch(() => fetchWithTimeout(apiUrl, BuildApiHeader(body, EActions.Post, bodyType, token, revalidate, browserId)));
|
|
67
73
|
};
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
|
|
75
|
+
export const HttpPut = async (apiUrl: string, body?: any | UN, bodyType?: EBody | UN, token?: string | UN, revalidate?: number | UN, browserId?: string | UN) => {
|
|
76
|
+
return await safeFetch(() => fetchWithTimeout(apiUrl, BuildApiHeader(body, EActions.Put, bodyType, token, revalidate, browserId)));
|
|
70
77
|
};
|
|
71
78
|
|
|
72
|
-
export const
|
|
73
|
-
return await safeFetch(() => fetchWithTimeout(apiUrl,
|
|
79
|
+
export const HttpDelete = async (apiUrl: string, token?: string | UN, revalidate?: number | UN, browserId?: string | UN) => {
|
|
80
|
+
return await safeFetch(() => fetchWithTimeout(apiUrl, BuildApiHeader(undefined, EActions.Delete, undefined, token, revalidate, browserId)));
|
|
74
81
|
};
|
|
75
82
|
|
|
76
|
-
export const
|
|
77
|
-
|
|
83
|
+
export const HttpFetch = async (apiUrl: string, method: EActions | string, body?: any, bodyType?: EBody, token?: string | UN, revalidate?: number, browserId?: string | UN): Promise<Response | Error> => {
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(apiUrl, BuildApiHeader(body, method, bodyType, token, revalidate, browserId));
|
|
86
|
+
return response;
|
|
87
|
+
} catch (error: Error | any) {
|
|
88
|
+
return error;
|
|
89
|
+
}
|
|
78
90
|
};
|
|
91
|
+
|
|
79
92
|
export const GetRootApi = (nexusModule: EModules) => {
|
|
80
93
|
switch (nexusModule) {
|
|
94
|
+
case EModules.None:
|
|
95
|
+
return "/api";
|
|
81
96
|
case EModules.Authentication:
|
|
82
97
|
return APIS.AUTHENTICATION;
|
|
83
98
|
case EModules.Configuration:
|
|
@@ -100,8 +115,9 @@ const handleResponse = async (response: Response): Promise<IErrorRespose> => {
|
|
|
100
115
|
const status = response.status;
|
|
101
116
|
try {
|
|
102
117
|
const errorData = await response.json();
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
|
|
119
|
+
return handleError({ ...errorData, code: status, status: status });
|
|
120
|
+
} catch (error: any) {
|
|
105
121
|
let message = "Unexpected server error.";
|
|
106
122
|
switch (status) {
|
|
107
123
|
case 401:
|
|
@@ -116,24 +132,23 @@ const handleResponse = async (response: Response): Promise<IErrorRespose> => {
|
|
|
116
132
|
message = "Method not allowed.";
|
|
117
133
|
break;
|
|
118
134
|
}
|
|
119
|
-
return handleError(
|
|
135
|
+
return handleError(GetResponse.ERROR(message, "GENERIC_ERROR", EExceptionCategories.Application, EErrorCodes.Unknown, undefined, undefined, status));
|
|
120
136
|
}
|
|
121
137
|
};
|
|
122
138
|
export const handleError = (error: any): IErrorRespose => {
|
|
123
139
|
return {
|
|
124
140
|
isSuccess: false,
|
|
125
|
-
exception: error,
|
|
126
141
|
code: error?.code,
|
|
127
142
|
message: error?.message || "An unknown error occurred.",
|
|
128
|
-
messageCode: error?.
|
|
143
|
+
messageCode: error?.messageCode || "GENERIC_ERROR",
|
|
129
144
|
exceptionCategory: error?.exceptionCategory || EExceptionCategories.Application,
|
|
130
145
|
errorCode: error?.errorCode || EErrorCodes.Unknown,
|
|
131
146
|
inputName: error?.inputName || undefined,
|
|
132
|
-
|
|
147
|
+
exception: error?.exception || undefined,
|
|
148
|
+
result: error?.result || undefined,
|
|
133
149
|
};
|
|
134
150
|
};
|
|
135
151
|
const handleNetworkError = (error: any): IErrorRespose => {
|
|
136
|
-
console.error("\n\nNetwork error encountered:", error);
|
|
137
152
|
return handleError({
|
|
138
153
|
status: 503,
|
|
139
154
|
message: "Unable to connect to the server. Please check your network or backend status.",
|
|
@@ -36,17 +36,17 @@ const _findExistingLoader = (parent: HTMLElement): HTMLElement | null => {
|
|
|
36
36
|
const loaderElement = FindChildElement(parent, `#${LOADER_IDENTITY}`);
|
|
37
37
|
return loaderElement as HTMLElement | null;
|
|
38
38
|
};
|
|
39
|
-
const _updateLoaderProperties = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes) => {
|
|
39
|
+
const _updateLoaderProperties = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes, isInputLoader: boolean) => {
|
|
40
40
|
if (type === EAnimationClasses.SPIN) {
|
|
41
41
|
if (size === ESizes.VeryTiny) {
|
|
42
|
-
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "2.5rem");
|
|
43
|
-
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.34rem");
|
|
42
|
+
loaderElement.style.setProperty("--SPIN_BOX_SIZE", isInputLoader ? "1.5rem" : "2.5rem");
|
|
43
|
+
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", isInputLoader ? "0.28rem" : "0.34rem");
|
|
44
44
|
} else if (size === ESizes.Tiny) {
|
|
45
|
-
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "2.8rem");
|
|
46
|
-
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.36rem");
|
|
45
|
+
loaderElement.style.setProperty("--SPIN_BOX_SIZE", isInputLoader ? "2.0rem" : "2.8rem");
|
|
46
|
+
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", isInputLoader ? "0.31rem" : "0.36rem");
|
|
47
47
|
} else if (size === ESizes.VerySmall) {
|
|
48
|
-
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "3rem");
|
|
49
|
-
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.38rem");
|
|
48
|
+
loaderElement.style.setProperty("--SPIN_BOX_SIZE", isInputLoader ? "2.4rem" : "3rem");
|
|
49
|
+
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", isInputLoader ? "0.34rem" : "0.38rem");
|
|
50
50
|
} else if (size === ESizes.Small) {
|
|
51
51
|
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "3.5rem");
|
|
52
52
|
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.45rem");
|
|
@@ -68,25 +68,30 @@ const _updateLoaderProperties = (loaderElement: HTMLElement, type: EAnimationCla
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
-
const _updateLoaderAttributes = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes, disabledInputs: boolean, count: number) => {
|
|
71
|
+
const _updateLoaderAttributes = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes, disabledInputs: boolean, count: number, isInputLoader: boolean) => {
|
|
72
72
|
loaderElement.setAttribute(LOADER_DATA_COUNT_ATTRIBUTE, count.toString());
|
|
73
73
|
loaderElement.setAttribute(LOADER_DATA_TYPE_ATTRIBUTE, type);
|
|
74
74
|
loaderElement.setAttribute(LOADER_DATA_SIZE_ATTRIBUTE, size);
|
|
75
75
|
loaderElement.setAttribute(LOADER_DATA_DISABLED_INPUTS_ATTRIBUTE, disabledInputs ? "true" : "false");
|
|
76
76
|
};
|
|
77
|
-
const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size: ESizes, showLogo: boolean, disabledInputs: boolean, background: EBackgrounds): HTMLElement | null => {
|
|
77
|
+
const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size: ESizes, showLogo: boolean, disabledInputs: boolean, background: EBackgrounds, defaultClass?: string | UN, isInputLoader?: boolean): HTMLElement | null => {
|
|
78
78
|
const loaderElement = _findExistingLoader(parent);
|
|
79
79
|
if (loaderElement && !loaderElement.classList.contains(DISPOSED_LOADER_CLASS)) {
|
|
80
80
|
const count = parseInt(loaderElement.getAttribute(LOADER_DATA_COUNT_ATTRIBUTE) ?? "0") + 1;
|
|
81
|
-
_updateLoaderAttributes(loaderElement, type, size, disabledInputs, count);
|
|
82
|
-
_updateLoaderProperties(loaderElement, type, size);
|
|
81
|
+
_updateLoaderAttributes(loaderElement, type, size, disabledInputs, count, isInputLoader ?? false);
|
|
82
|
+
_updateLoaderProperties(loaderElement, type, size, isInputLoader ?? false);
|
|
83
83
|
return loaderElement as HTMLElement;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const newLoaderElement = document.createElement("div");
|
|
87
87
|
newLoaderElement.id = LOADER_IDENTITY;
|
|
88
88
|
newLoaderElement.classList.add("nexus-container", "o0", EAnimationClasses.FADE_IN, background);
|
|
89
|
-
|
|
89
|
+
if (defaultClass) newLoaderElement.classList.add(...defaultClass.split(" "));
|
|
90
|
+
_updateLoaderAttributes(newLoaderElement, type, size, disabledInputs, 1, isInputLoader ?? false);
|
|
91
|
+
_updateLoaderProperties(newLoaderElement, type, size, isInputLoader ?? false);
|
|
92
|
+
|
|
93
|
+
newLoaderElement.style.maxHeight = "100%!important";
|
|
94
|
+
newLoaderElement.style.maxWidth = "100%!important";
|
|
90
95
|
|
|
91
96
|
const loader = document.createElement("div");
|
|
92
97
|
newLoaderElement.appendChild(loader);
|
|
@@ -97,9 +102,7 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
|
|
|
97
102
|
loader.innerText = "Loading...";
|
|
98
103
|
loader.classList.add("tx52");
|
|
99
104
|
loader.style.fontSize = "1.5rem";
|
|
100
|
-
} else if (showLogo) {
|
|
101
|
-
_updateLoaderProperties(newLoaderElement, type, size);
|
|
102
|
-
|
|
105
|
+
} else if (showLogo && type !== EAnimationClasses.SLIDE_BAR && type !== EAnimationClasses.BOUNCE_BAR) {
|
|
103
106
|
const logoElement = document.createElement("img");
|
|
104
107
|
logoElement.src = FILES.LOGO.ICON;
|
|
105
108
|
logoElement.alt = "Logo";
|
|
@@ -123,7 +126,7 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
|
|
|
123
126
|
return newLoaderElement;
|
|
124
127
|
};
|
|
125
128
|
|
|
126
|
-
export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAnimationClasses | UN, disabledInputs?: boolean | UN, size?: ESizes | UN, delay?: number | UN, showLogo?: boolean | UN, background?: EBackgrounds | UN): Promise<HTMLElement | null> => {
|
|
129
|
+
export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAnimationClasses | UN, disabledInputs?: boolean | UN, size?: ESizes | UN, delay?: number | UN, showLogo?: boolean | UN, background?: EBackgrounds | UN, defaultClass?: string | UN, isInputLoader?: boolean | UN): Promise<HTMLElement | null> => {
|
|
127
130
|
if (!IsBrowser()) return Promise.resolve(null);
|
|
128
131
|
type = type ?? DEFAULT_LOADER_TYPE;
|
|
129
132
|
size = size ?? DEFAULT_LOADER_SIZE;
|
|
@@ -133,7 +136,7 @@ export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAni
|
|
|
133
136
|
const timeout = setTimeout(() => {
|
|
134
137
|
clearTimeout(timeout);
|
|
135
138
|
const parentElement = _findParentElement(parent);
|
|
136
|
-
const loaderElement = _createLoaderElement(parentElement, type, size, showLogo ?? true, disabledInputs ?? false, background);
|
|
139
|
+
const loaderElement = _createLoaderElement(parentElement, type, size, showLogo ?? true, disabledInputs ?? false, background, defaultClass, isInputLoader ?? false);
|
|
137
140
|
resolve(loaderElement);
|
|
138
141
|
}, delay ?? DEFAULT_LOADER_DELAY);
|
|
139
142
|
});
|
|
@@ -166,3 +169,17 @@ export const HideLoader = (parent?: String | HTMLElement | UN, force?: boolean |
|
|
|
166
169
|
}, delay ?? DEFAULT_LOADER_DELAY);
|
|
167
170
|
});
|
|
168
171
|
};
|
|
172
|
+
|
|
173
|
+
export const InputLoader = async (parent: String | HTMLElement, isShow: boolean, defaultClass?: string | UN): Promise<HTMLElement | null> => {
|
|
174
|
+
if (isShow !== false) {
|
|
175
|
+
const loaderElement = await ShowLoader(parent, EAnimationClasses.SPIN, false, ESizes.VeryTiny, null, false, EBackgrounds.None, defaultClass, true);
|
|
176
|
+
if (loaderElement) {
|
|
177
|
+
loaderElement.style.width = "100%";
|
|
178
|
+
loaderElement.style.height = "100%";
|
|
179
|
+
}
|
|
180
|
+
return loaderElement;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
HideLoader(parent);
|
|
184
|
+
return null;
|
|
185
|
+
};
|
|
@@ -1,30 +1,84 @@
|
|
|
1
1
|
import { CreateUpdatePreference } from "../api-services/preference-service";
|
|
2
2
|
import { IsBrowser } from "../helpers/browser-helpers";
|
|
3
3
|
import { Debounce } from "../helpers/utility-helpers";
|
|
4
|
+
import { EFlexPositions, UN } from "../interface";
|
|
4
5
|
import { LOCAL_STORAGE_KEYS } from "../interfaces/storage-interfaces";
|
|
5
6
|
import { EPreferenceTargets } from "../interfaces/system-interfaces";
|
|
6
7
|
import { IThemeInformation } from "../interfaces/theme-interfaces";
|
|
7
8
|
import { O } from "../nexus-client";
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Create a debounce function to update the preference in the server
|
|
12
|
+
* This is used to update the preference in the server
|
|
13
|
+
* @param CreateUpdatePreference The function to create the update preference
|
|
14
|
+
* @param debounceTime The debounce time in milliseconds
|
|
15
|
+
* @returns The debounce function
|
|
16
|
+
*/
|
|
9
17
|
const _createUpdatePreferenceDebounce = Debounce(CreateUpdatePreference, 1000);
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Sync the value of the local storage to the server
|
|
21
|
+
* This is used to sync the value of the local storage to the server
|
|
22
|
+
* @param key The key of the local storage
|
|
23
|
+
* @param value The value to set
|
|
24
|
+
* @param isSyncWithServer Whether to sync with the server
|
|
25
|
+
* @param target The target of the local storage
|
|
26
|
+
*/
|
|
27
|
+
const _syncStorageToServer = async (key: string, value: string | UN, isSyncWithServer?: boolean | UN, target?: EPreferenceTargets | UN) => {
|
|
12
28
|
if (!IsBrowser() || !isSyncWithServer || !target) return;
|
|
13
29
|
|
|
14
|
-
await _createUpdatePreferenceDebounce(target, key, value);
|
|
30
|
+
await _createUpdatePreferenceDebounce(target, key, value ?? null);
|
|
15
31
|
};
|
|
16
32
|
|
|
17
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Set the value of the local storage
|
|
35
|
+
* This is used to set the value of the local storage
|
|
36
|
+
* @param key The key of the local storage
|
|
37
|
+
* @param value The value to set
|
|
38
|
+
* @param isSyncWithServer Whether to sync with the server
|
|
39
|
+
* @param target The target of the local storage
|
|
40
|
+
*/
|
|
41
|
+
export const SetLocalStorage = (key: string, value: string | UN, isSyncWithServer?: boolean | UN, target?: EPreferenceTargets | UN) => {
|
|
18
42
|
if (!IsBrowser()) return;
|
|
19
43
|
|
|
20
44
|
_syncStorageToServer(key, value, isSyncWithServer, target);
|
|
21
|
-
|
|
45
|
+
|
|
46
|
+
if (value === null || value === undefined) RemoveLocalStorage(key, isSyncWithServer, target);
|
|
47
|
+
else localStorage.setItem(key, value);
|
|
48
|
+
};
|
|
49
|
+
export const SetSessionStorage = (key: string, value: string | UN) => {
|
|
50
|
+
if (!IsBrowser()) return;
|
|
51
|
+
if (value === null || value === undefined) RemoveSessionStorage(key);
|
|
52
|
+
else sessionStorage.setItem(key, value);
|
|
22
53
|
};
|
|
23
|
-
export const
|
|
54
|
+
export const GetSessionStorage = (key: string): string | null => {
|
|
55
|
+
if (!IsBrowser()) return null;
|
|
56
|
+
const data = sessionStorage.getItem(key);
|
|
57
|
+
if (!data) return null;
|
|
58
|
+
return data;
|
|
59
|
+
};
|
|
60
|
+
export const RemoveSessionStorage = (key: string) => {
|
|
61
|
+
if (!IsBrowser()) return;
|
|
62
|
+
sessionStorage.removeItem(key);
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Remove the value of the local storage
|
|
66
|
+
* This is used to remove the value of the local storage
|
|
67
|
+
* @param key The key of the local storage
|
|
68
|
+
* @param isSyncWithServer Whether to sync with the server
|
|
69
|
+
* @param target The target of the local storage
|
|
70
|
+
*/
|
|
71
|
+
export const RemoveLocalStorage = (key: string, isSyncWithServer?: boolean | UN, target?: EPreferenceTargets | UN) => {
|
|
24
72
|
_syncStorageToServer(key, null, isSyncWithServer, target);
|
|
25
73
|
localStorage.removeItem(key);
|
|
26
74
|
};
|
|
27
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Get the value of the local storage
|
|
78
|
+
* This is used to get the value of the local storage
|
|
79
|
+
* @param key The key of the local storage
|
|
80
|
+
* @returns The value of the local storage
|
|
81
|
+
*/
|
|
28
82
|
export function GetLocalStorage(key: string): string | null {
|
|
29
83
|
if (!IsBrowser()) return null;
|
|
30
84
|
const data = localStorage.getItem(key);
|
|
@@ -32,6 +86,11 @@ export function GetLocalStorage(key: string): string | null {
|
|
|
32
86
|
return data;
|
|
33
87
|
}
|
|
34
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Set the current theme in the local storage
|
|
91
|
+
* This is used to determine the current theme in the application
|
|
92
|
+
* @param themeCode The code of the theme to set
|
|
93
|
+
*/
|
|
35
94
|
export const SetCurrentTheme = (themeCode: string) => SetLocalStorage(LOCAL_STORAGE_KEYS.ACTIVE_THEME, themeCode, true, EPreferenceTargets.User);
|
|
36
95
|
export const GetCurrentTheme = (): IThemeInformation => {
|
|
37
96
|
const themeInformations = O.systemThemes;
|
|
@@ -41,3 +100,20 @@ export const GetCurrentTheme = (): IThemeInformation => {
|
|
|
41
100
|
return themeInformations.find((theme) => theme.systemName === activeTheme) || themeInformations[0];
|
|
42
101
|
};
|
|
43
102
|
export const GetCurrentThemeName = (): string => GetCurrentTheme().systemName;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the position of the form buttons from the local storage
|
|
106
|
+
* This is used to determine the position of the form buttons in the form
|
|
107
|
+
* @returns The position of the form buttons
|
|
108
|
+
*/
|
|
109
|
+
export const GetFormButtonPosition = (): EFlexPositions => {
|
|
110
|
+
const position = GetLocalStorage(LOCAL_STORAGE_KEYS.FORM_BUTTON_POSITION);
|
|
111
|
+
if (!position) return EFlexPositions.Left;
|
|
112
|
+
return position as EFlexPositions;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Set the position of the form buttons in the local storage
|
|
116
|
+
* This is used to determine the position of the form buttons in the form
|
|
117
|
+
* @param position The position of the form buttons
|
|
118
|
+
*/
|
|
119
|
+
export const SetFormButtonPosition = (position: EFlexPositions) => SetLocalStorage(LOCAL_STORAGE_KEYS.FORM_BUTTON_POSITION, position, true, EPreferenceTargets.User);
|