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.
Files changed (74) hide show
  1. package/package.json +1 -1
  2. package/src/api-services/authentication-service.tsx +23 -0
  3. package/src/api-services/system-service.tsx +28 -315
  4. package/src/client.ts +2 -2
  5. package/src/components/documents/button.tsx +17 -11
  6. package/src/components/documents/icon-box.tsx +1 -1
  7. package/src/components/documents/tab-button.tsx +2 -2
  8. package/src/components/documents/tag.tsx +30 -0
  9. package/src/components/inputs/input-box.tsx +4 -2
  10. package/src/components/inputs/input-element.tsx +1 -1
  11. package/src/components/inputs/input-form.tsx +126 -14
  12. package/src/components/layouts/global-dialogbox.tsx +5 -9
  13. package/src/components/layouts/global-layout.tsx +3 -0
  14. package/src/components/layouts/layout-helpers.tsx +1 -4
  15. package/src/components/layouts/panels/user-panel.tsx +112 -0
  16. package/src/components/layouts/utility-menu.tsx +9 -29
  17. package/src/components/panels/theme-panel.tsx +2 -2
  18. package/src/helpers/browser-helpers.tsx +88 -3
  19. package/src/helpers/input-helpers.tsx +34 -4
  20. package/src/helpers/validation-helpers.tsx +302 -0
  21. package/src/index.ts +2 -18
  22. package/src/interface.ts +5 -1
  23. package/src/interfaces/auth-token-interfaces.tsx +6 -0
  24. package/src/interfaces/browser-interfaces.tsx +13 -0
  25. package/src/interfaces/button-interfaces.tsx +2 -2
  26. package/src/interfaces/http-interfaces.tsx +86 -8
  27. package/src/interfaces/icon-interfaces.tsx +3 -0
  28. package/src/interfaces/input-interfaces.tsx +61 -11
  29. package/src/interfaces/menu-interfaces.tsx +13 -0
  30. package/src/interfaces/message-interfaces.tsx +32 -0
  31. package/src/interfaces/storage-interfaces.tsx +2 -0
  32. package/src/interfaces/theme-interfaces.tsx +43 -129
  33. package/src/interfaces/type-interfaces.tsx +8 -2
  34. package/src/interfaces/user-interfaces.tsx +47 -0
  35. package/src/nexus.environments.tsx +41 -24
  36. package/src/proxy-api/proxy-backend.tsx +60 -0
  37. package/src/proxy-api/proxy-constants.tsx +28 -0
  38. package/src/proxy-api/proxy-helpers.tsx +91 -0
  39. package/src/proxy-api-client.tsx +1 -0
  40. package/src/proxy-api-server.tsx +2 -0
  41. package/src/services/http-client-services.tsx +26 -11
  42. package/src/services/http-services.tsx +42 -27
  43. package/src/services/loader-service.tsx +34 -17
  44. package/src/services/localstorage-service.tsx +81 -5
  45. package/src/services/message-services.tsx +383 -0
  46. package/src/services/theme-service.tsx +30 -16
  47. package/src/services/user-services.tsx +10 -0
  48. package/src/sso-config/auth-token-validation.ts +16 -0
  49. package/src/sso-config/callback-route.tsx +35 -0
  50. package/src/sso-config/cookie-encryption.ts +51 -0
  51. package/src/sso-config/cookie-helpers.tsx +102 -0
  52. package/src/sso-config/forgot-password-route.tsx +36 -0
  53. package/src/sso-config/oauth-callback-state.ts +61 -0
  54. package/src/sso-config/pkce-helpers.ts +9 -0
  55. package/src/sso-config/provider-complete-route.tsx +18 -0
  56. package/src/sso-config/redirect-context-actions.tsx +45 -0
  57. package/src/sso-config/redirect-context-helpers.tsx +159 -0
  58. package/src/sso-config/redirect-context-persist.tsx +18 -0
  59. package/src/sso-config/redirect-context-route.tsx +45 -0
  60. package/src/sso-config/refresh-route.tsx +23 -0
  61. package/src/sso-config/sign-in-route.tsx +74 -0
  62. package/src/sso-config/sign-out-route.tsx +57 -0
  63. package/src/sso-config/sign-up-route.tsx +57 -0
  64. package/src/sso-config/sso-backend-flow.ts +38 -0
  65. package/src/sso-config/sso-complete-route.tsx +50 -0
  66. package/src/sso-config/sso-config.ts +59 -0
  67. package/src/sso-config/sso-interfaces.tsx +99 -0
  68. package/src/sso-config/sso-response-helpers.tsx +20 -0
  69. package/src/sso-server.tsx +21 -1
  70. package/src/styles/nexus.dialog.css +3 -0
  71. package/src/styles/nexus.icon.css +1 -0
  72. package/src/styles/nexus.logic.css +34 -5
  73. package/src/interfaces/sso-interfaces.tsx +0 -52
  74. 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";
@@ -0,0 +1,2 @@
1
+ export * from "./proxy-api/proxy-backend";
2
+ export * from "./proxy-api/proxy-constants";
@@ -1,7 +1,13 @@
1
1
  "use client";
2
2
 
3
- import { EBody, IErrorRespose, ISuccessResponse } from "../interface";
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, revalidate?: number): Promise<ISuccessResponse | IErrorRespose> => {
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 | null, bodyType?: EBody, successMessage?: boolean, errorMessage?: boolean, revalidate?: number) => {
27
- return await handleResponse(await HttpPut(apiUrl, body, bodyType, null, revalidate, null), successMessage ?? true, errorMessage ?? true);
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 | null, bodyType?: EBody, successMessage?: boolean, errorMessage?: boolean, revalidate?: number) => {
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, revalidate, null), successMessage ?? true, errorMessage ?? true);
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, revalidate, 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, revalidate?: number) => {
50
- return await handleResponse(await HttpDelete(apiUrl, null, revalidate, null), successMessage ?? true, errorMessage ?? true);
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
- // TODO: if (successMessage) SuccessMessage("Request Success!", result.message ?? "Success");
70
+ if (successMessage) SuccessMessage("Request Success!", result.message ?? "Success");
56
71
  return result;
57
72
  }
58
- // TODO: if (errorMessage) ErrorMessage("Request Failed!", result.message ?? "Failed");
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 NEXUS_HEADERS = (formdata?: any, httpMethod: EActions = EActions.Get, bodyType: EBody = EBody.JSON, token?: string | null, revalidate?: number | null, browserId?: string | null): RequestInit & { next?: { revalidate: number } } => {
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", `${token}`);
18
+ if (token && token.length > 50) headers.append("authorization", token.startsWith("Bearer ") ? token : `Bearer ${token}`);
12
19
  if (browserId) headers.append("bid", browserId);
13
- if (bodyType === EBody.JSON && (httpMethod === EActions.Post || httpMethod === EActions.Put)) {
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 | null, revalidate?: number, browserId?: string | null) => {
66
- return await safeFetch(() => fetchWithTimeout(apiUrl, NEXUS_HEADERS(undefined, EActions.Get, EBody.JSON, token, revalidate, browserId)));
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
- export const HttpPost = async (apiUrl: string, body?: any, bodyType?: EBody, token?: string | null, revalidate?: number, browserId?: string | null) => {
69
- return await safeFetch(() => fetchWithTimeout(apiUrl, NEXUS_HEADERS(body, EActions.Post, bodyType, token, revalidate, browserId)));
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 HttpPut = async (apiUrl: string, body?: any, bodyType?: EBody, token?: string | null, revalidate?: number, browserId?: string | null) => {
73
- return await safeFetch(() => fetchWithTimeout(apiUrl, NEXUS_HEADERS(body, EActions.Put, bodyType, token, revalidate, browserId)));
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 HttpDelete = async (apiUrl: string, token?: string | null, revalidate?: number, browserId?: string | null) => {
77
- return await safeFetch(() => fetchWithTimeout(apiUrl, NEXUS_HEADERS(undefined, EActions.Delete, undefined, token, revalidate, browserId)));
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
- return handleError({ ...errorData, status });
104
- } catch {
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({ status, message });
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?.key || "GENERIC_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
- result: null,
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
- _updateLoaderAttributes(newLoaderElement, type, size, disabledInputs, 1);
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
- const _syncStorageToServer = async (key: string, value: string | null, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
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
- export const SetLocalStorage = (key: string, value: string, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
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
- localStorage.setItem(key, value);
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 RemoveLocalStorage = (key: string, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
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);