nexus-shared 1.1.18 → 1.1.19

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 (52) hide show
  1. package/package.json +1 -1
  2. package/src/client.ts +2 -1
  3. package/src/components/documents/button.tsx +5 -1
  4. package/src/components/inputs/input-box.tsx +4 -2
  5. package/src/components/inputs/input-form.tsx +126 -14
  6. package/src/helpers/browser-helpers.tsx +41 -1
  7. package/src/helpers/input-helpers.tsx +34 -4
  8. package/src/helpers/validation-helpers.tsx +302 -0
  9. package/src/index.ts +2 -18
  10. package/src/interface.ts +4 -1
  11. package/src/interfaces/button-interfaces.tsx +2 -2
  12. package/src/interfaces/http-interfaces.tsx +89 -8
  13. package/src/interfaces/input-interfaces.tsx +61 -11
  14. package/src/interfaces/message-interfaces.tsx +32 -0
  15. package/src/interfaces/storage-interfaces.tsx +1 -0
  16. package/src/interfaces/type-interfaces.tsx +8 -2
  17. package/src/interfaces/user-interfaces.tsx +17 -0
  18. package/src/nexus.environments.tsx +6 -1
  19. package/src/proxy-api/proxy-backend.tsx +60 -0
  20. package/src/proxy-api/proxy-constants.tsx +28 -0
  21. package/src/proxy-api/proxy-helpers.tsx +91 -0
  22. package/src/proxy-api-client.tsx +1 -0
  23. package/src/proxy-api-server.tsx +2 -0
  24. package/src/services/http-client-services.tsx +26 -11
  25. package/src/services/http-services.tsx +37 -22
  26. package/src/services/loader-service.tsx +5 -4
  27. package/src/services/localstorage-service.tsx +59 -0
  28. package/src/services/message-services.tsx +383 -0
  29. package/src/sso-config/auth-token-validation.ts +20 -0
  30. package/src/sso-config/callback-route.tsx +34 -0
  31. package/src/sso-config/cookie-encryption.ts +51 -0
  32. package/src/sso-config/cookie-helpers.tsx +77 -0
  33. package/src/sso-config/forgot-password-route.tsx +36 -0
  34. package/src/sso-config/oauth-callback-state.ts +58 -0
  35. package/src/sso-config/pkce-helpers.ts +16 -0
  36. package/src/sso-config/provider-complete-route.tsx +15 -0
  37. package/src/sso-config/redirect-context-actions.tsx +46 -0
  38. package/src/sso-config/redirect-context-helpers.tsx +144 -0
  39. package/src/sso-config/redirect-context-persist.tsx +18 -0
  40. package/src/sso-config/redirect-context-route.tsx +45 -0
  41. package/src/sso-config/refresh-route.tsx +22 -0
  42. package/src/sso-config/sign-in-route.tsx +69 -0
  43. package/src/sso-config/sign-out-route.tsx +53 -0
  44. package/src/sso-config/sign-up-route.tsx +46 -0
  45. package/src/sso-config/sso-backend-flow.ts +45 -0
  46. package/src/sso-config/sso-complete-route.tsx +46 -0
  47. package/src/sso-config/sso-config.ts +57 -0
  48. package/src/{interfaces → sso-config}/sso-interfaces.tsx +17 -1
  49. package/src/sso-config/sso-response-helpers.tsx +20 -0
  50. package/src/sso-server.tsx +21 -1
  51. package/src/styles/nexus.dialog.css +3 -0
  52. package/src/load-env.ts +0 -2
@@ -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, 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
18
  if (token && token.length > 50) headers.append("authorization", `${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)));
67
70
  };
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)));
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)));
70
73
  };
71
74
 
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)));
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)));
74
77
  };
75
78
 
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)));
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)));
78
81
  };
82
+
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
+ }
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:
@@ -122,14 +137,14 @@ const handleResponse = async (response: Response): Promise<IErrorRespose> => {
122
137
  export const handleError = (error: any): IErrorRespose => {
123
138
  return {
124
139
  isSuccess: false,
125
- exception: error,
126
140
  code: error?.code,
127
141
  message: error?.message || "An unknown error occurred.",
128
- messageCode: error?.key || "GENERIC_ERROR",
142
+ messageCode: error?.messageCode || "GENERIC_ERROR",
129
143
  exceptionCategory: error?.exceptionCategory || EExceptionCategories.Application,
130
144
  errorCode: error?.errorCode || EErrorCodes.Unknown,
131
145
  inputName: error?.inputName || undefined,
132
- result: null,
146
+ exception: error?.exception || undefined,
147
+ result: error?.result || undefined,
133
148
  };
134
149
  };
135
150
  const handleNetworkError = (error: any): IErrorRespose => {
@@ -74,7 +74,7 @@ const _updateLoaderAttributes = (loaderElement: HTMLElement, type: EAnimationCla
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): 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;
@@ -86,6 +86,7 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
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
+ if (defaultClass) newLoaderElement.classList.add(defaultClass);
89
90
  _updateLoaderAttributes(newLoaderElement, type, size, disabledInputs, 1);
90
91
 
91
92
  const loader = document.createElement("div");
@@ -97,7 +98,7 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
97
98
  loader.innerText = "Loading...";
98
99
  loader.classList.add("tx52");
99
100
  loader.style.fontSize = "1.5rem";
100
- } else if (showLogo) {
101
+ } else if (showLogo && type !== EAnimationClasses.SLIDE_BAR && type !== EAnimationClasses.BOUNCE_BAR) {
101
102
  _updateLoaderProperties(newLoaderElement, type, size);
102
103
 
103
104
  const logoElement = document.createElement("img");
@@ -123,7 +124,7 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
123
124
  return newLoaderElement;
124
125
  };
125
126
 
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> => {
127
+ 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): Promise<HTMLElement | null> => {
127
128
  if (!IsBrowser()) return Promise.resolve(null);
128
129
  type = type ?? DEFAULT_LOADER_TYPE;
129
130
  size = size ?? DEFAULT_LOADER_SIZE;
@@ -133,7 +134,7 @@ export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAni
133
134
  const timeout = setTimeout(() => {
134
135
  clearTimeout(timeout);
135
136
  const parentElement = _findParentElement(parent);
136
- const loaderElement = _createLoaderElement(parentElement, type, size, showLogo ?? true, disabledInputs ?? false, background);
137
+ const loaderElement = _createLoaderElement(parentElement, type, size, showLogo ?? true, disabledInputs ?? false, background, defaultClass);
137
138
  resolve(loaderElement);
138
139
  }, delay ?? DEFAULT_LOADER_DELAY);
139
140
  });
@@ -1,30 +1,67 @@
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 } 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
 
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
+ */
11
27
  const _syncStorageToServer = async (key: string, value: string | null, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
12
28
  if (!IsBrowser() || !isSyncWithServer || !target) return;
13
29
 
14
30
  await _createUpdatePreferenceDebounce(target, key, value);
15
31
  };
16
32
 
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
+ */
17
41
  export const SetLocalStorage = (key: string, value: string, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
18
42
  if (!IsBrowser()) return;
19
43
 
20
44
  _syncStorageToServer(key, value, isSyncWithServer, target);
21
45
  localStorage.setItem(key, value);
22
46
  };
47
+ /**
48
+ * Remove the value of the local storage
49
+ * This is used to remove the value of the local storage
50
+ * @param key The key of the local storage
51
+ * @param isSyncWithServer Whether to sync with the server
52
+ * @param target The target of the local storage
53
+ */
23
54
  export const RemoveLocalStorage = (key: string, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
24
55
  _syncStorageToServer(key, null, isSyncWithServer, target);
25
56
  localStorage.removeItem(key);
26
57
  };
27
58
 
59
+ /**
60
+ * Get the value of the local storage
61
+ * This is used to get the value of the local storage
62
+ * @param key The key of the local storage
63
+ * @returns The value of the local storage
64
+ */
28
65
  export function GetLocalStorage(key: string): string | null {
29
66
  if (!IsBrowser()) return null;
30
67
  const data = localStorage.getItem(key);
@@ -32,6 +69,11 @@ export function GetLocalStorage(key: string): string | null {
32
69
  return data;
33
70
  }
34
71
 
72
+ /**
73
+ * Set the current theme in the local storage
74
+ * This is used to determine the current theme in the application
75
+ * @param themeCode The code of the theme to set
76
+ */
35
77
  export const SetCurrentTheme = (themeCode: string) => SetLocalStorage(LOCAL_STORAGE_KEYS.ACTIVE_THEME, themeCode, true, EPreferenceTargets.User);
36
78
  export const GetCurrentTheme = (): IThemeInformation => {
37
79
  const themeInformations = O.systemThemes;
@@ -41,3 +83,20 @@ export const GetCurrentTheme = (): IThemeInformation => {
41
83
  return themeInformations.find((theme) => theme.systemName === activeTheme) || themeInformations[0];
42
84
  };
43
85
  export const GetCurrentThemeName = (): string => GetCurrentTheme().systemName;
86
+
87
+ /**
88
+ * Get the position of the form buttons from the local storage
89
+ * This is used to determine the position of the form buttons in the form
90
+ * @returns The position of the form buttons
91
+ */
92
+ export const GetFormButtonPosition = (): EFlexPositions => {
93
+ const position = GetLocalStorage(LOCAL_STORAGE_KEYS.FORM_BUTTON_POSITION);
94
+ if (!position) return EFlexPositions.Left;
95
+ return position as EFlexPositions;
96
+ };
97
+ /**
98
+ * Set the position of the form buttons in the local storage
99
+ * This is used to determine the position of the form buttons in the form
100
+ * @param position The position of the form buttons
101
+ */
102
+ export const SetFormButtonPosition = (position: EFlexPositions) => SetLocalStorage(LOCAL_STORAGE_KEYS.FORM_BUTTON_POSITION, position, true, EPreferenceTargets.User);