procode-lowcode-core 1.0.18 → 1.0.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.
@@ -0,0 +1,2 @@
1
+ import { EventService } from "../../Services/EventService";
2
+ export declare const hasLastApiCallFailed: (eventService: EventService | undefined) => boolean;
@@ -8,6 +8,7 @@ import { RequestType } from "../Services/helpers/ErrorMessageProvider";
8
8
  import IValidationFactory from "../Validation/IValidationFactory";
9
9
  import { IStorage } from "../StorageManager/IStorage";
10
10
  import { ResponseFormat } from "../Services/helpers/coreResponseFormatter";
11
+ import { FieldErrorMapper } from "../Services/helpers/defaultFieldErrorMapper";
11
12
  export type CoreProps = {
12
13
  LoaderElement?: prcElement;
13
14
  NotificationElement?: any;
@@ -28,6 +29,7 @@ export type CoreProps = {
28
29
  getSchemaBySlugService?: any;
29
30
  requestHeaders?: Record<string, string>;
30
31
  responseFormatter?: ((data: any, status: number) => ResponseFormat) | undefined;
32
+ fieldErrorMapper?: FieldErrorMapper;
31
33
  customStorages?: Record<string, IStorage>;
32
34
  customRoute?: {
33
35
  parentSimblingRoutes: CustomRoute[];
@@ -0,0 +1,13 @@
1
+ import { AlertType } from "../../UIElement/UIElementDefinations/ApiConfig";
2
+ type ApiResult = {
3
+ success: boolean;
4
+ alertType: AlertType;
5
+ };
6
+ type Handler = (result: ApiResult) => void;
7
+ declare class ApiResultDispatcher {
8
+ private static handler?;
9
+ static registerHandler(handler: Handler): void;
10
+ static dispatch(result: ApiResult): void;
11
+ }
12
+ export default ApiResultDispatcher;
13
+ export type { ApiResult };
@@ -1,9 +1,11 @@
1
1
  /// <reference types="react" />
2
+ import type { ApiResult } from "../ApplicationStart/HOC/ApiResultDispatcher";
2
3
  type prcElement = JSX.Element;
3
4
  type AppState = {
4
5
  inProgressCount: number;
5
6
  viewModel: any;
6
7
  validation: Record<string, string[]>;
8
+ lastApiResult: ApiResult | null;
7
9
  };
8
10
  type MetaDataState = {
9
11
  uiSchema: any;
@@ -8,6 +8,7 @@ export declare const appActions: import("@reduxjs/toolkit").CaseReducerActions<{
8
8
  inProgressCount: number;
9
9
  viewModel: any;
10
10
  validation: Record<string, string[]>;
11
+ lastApiResult: import("../../ApplicationStart/HOC/ApiResultDispatcher").ApiResult | null;
11
12
  };
12
13
  loadViewModel: (state: import("immer/dist/internal").WritableDraft<AppState>, action: {
13
14
  payload: any;
@@ -29,6 +30,10 @@ export declare const appActions: import("@reduxjs/toolkit").CaseReducerActions<{
29
30
  payload: any;
30
31
  type: string;
31
32
  }) => void;
33
+ setLastApiResult: (state: import("immer/dist/internal").WritableDraft<AppState>, action: {
34
+ payload: any;
35
+ type: string;
36
+ }) => void;
32
37
  }, "appState">;
33
38
  declare const _default: import("redux").Reducer<AppState, import("redux").AnyAction>;
34
39
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "procode-lowcode-core",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "ProCode Core Library - React framework for low-code applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -6,6 +6,7 @@ import { ActionInvokerProperties } from "../Types";
6
6
  import handleNavigation from "./handleNavigation";
7
7
  import { appActions } from "../../Store/Slices/appSlice";
8
8
  import { hasFieldValidationErrors } from "../helpers/hasFieldValidationErrors";
9
+ import { hasLastApiCallFailed } from "../helpers/hasLastApiCallFailed";
9
10
 
10
11
  const handleCreateAndNavigate = async (
11
12
  actionInvokerProps: ActionInvokerProperties,
@@ -53,6 +54,11 @@ const handleCreateAndNavigate = async (
53
54
  // from a form the user still has to fix.
54
55
  if (hasFieldValidationErrors(eventService)) return;
55
56
 
57
+ // TOAST-alert failures don't populate state.validation, so check the
58
+ // last API call's success flag too — a toast-only error should still
59
+ // keep the user on the current screen.
60
+ if (hasLastApiCallFailed(eventService)) return;
61
+
56
62
  handleNavigation(actionInvokerProps);
57
63
  };
58
64
  export default handleCreateAndNavigate;
@@ -6,6 +6,7 @@ import { ActionInvokerProperties } from "../Types";
6
6
  import handleNavigation from "./handleNavigation";
7
7
  import { appActions } from "../../Store/Slices/appSlice";
8
8
  import { hasFieldValidationErrors } from "../helpers/hasFieldValidationErrors";
9
+ import { hasLastApiCallFailed } from "../helpers/hasLastApiCallFailed";
9
10
 
10
11
  const handleUpdateAndNavigate = async (
11
12
  actionInvokerProps: ActionInvokerProperties
@@ -53,6 +54,11 @@ const handleUpdateAndNavigate = async (
53
54
  // from a form the user still has to fix.
54
55
  if (hasFieldValidationErrors(eventService)) return;
55
56
 
57
+ // TOAST-alert failures don't populate state.validation, so check the
58
+ // last API call's success flag too — a toast-only error should still
59
+ // keep the user on the current screen.
60
+ if (hasLastApiCallFailed(eventService)) return;
61
+
56
62
  handleNavigation(actionInvokerProps);
57
63
  };
58
64
  export default handleUpdateAndNavigate;
@@ -0,0 +1,16 @@
1
+ import { eventServiceType } from "../../Constant";
2
+ import { EventService } from "../../Services/EventService";
3
+ import { ApiResult } from "../../ApplicationStart/HOC/ApiResultDispatcher";
4
+
5
+ // Reads `state.app.lastApiResult` written by CentralService's response
6
+ // interceptor and returns true if the most recent request failed. Used by
7
+ // save/update actions to skip navigation when the server reported a
8
+ // non-success status under any alertType (FIELDVALIDATION already populates
9
+ // state.validation, but TOAST leaves no trace there).
10
+ export const hasLastApiCallFailed = (
11
+ eventService: EventService | undefined,
12
+ ): boolean => {
13
+ const appState = eventService?.emit(eventServiceType.APP_STATE);
14
+ const lastApiResult = appState?.lastApiResult as ApiResult | null | undefined;
15
+ return lastApiResult ? lastApiResult.success === false : false;
16
+ };
@@ -11,6 +11,7 @@ import { RequestType } from "../Services/helpers/ErrorMessageProvider";
11
11
  import IValidationFactory from "../Validation/IValidationFactory";
12
12
  import { IStorage } from "../StorageManager/IStorage";
13
13
  import { ResponseFormat } from "../Services/helpers/coreResponseFormatter";
14
+ import { FieldErrorMapper } from "../Services/helpers/defaultFieldErrorMapper";
14
15
  import LoaderElement from "../Renderer/LoadingRenderer";
15
16
  import Skeleton from "../Renderer/SkelitonRenderer";
16
17
  import { SkeletonType } from "../Renderer/SkelitonRenderer/constant";
@@ -48,6 +49,7 @@ export type CoreProps = {
48
49
  responseFormatter?:
49
50
  | ((data: any, status: number) => ResponseFormat)
50
51
  | undefined;
52
+ fieldErrorMapper?: FieldErrorMapper;
51
53
 
52
54
  customStorages?: Record<string, IStorage>;
53
55
  customRoute?: {
@@ -0,0 +1,23 @@
1
+ import { AlertType } from "../../UIElement/UIElementDefinations/ApiConfig";
2
+
3
+ type ApiResult = { success: boolean; alertType: AlertType };
4
+ type Handler = (result: ApiResult) => void;
5
+
6
+ class ApiResultDispatcher {
7
+ private static handler?: Handler;
8
+
9
+ static registerHandler(handler: Handler) {
10
+ ApiResultDispatcher.handler = handler;
11
+ }
12
+
13
+ static dispatch(result: ApiResult) {
14
+ if (ApiResultDispatcher.handler) {
15
+ ApiResultDispatcher.handler(result);
16
+ } else {
17
+ console.warn("Api result dispatcher handler not registered.");
18
+ }
19
+ }
20
+ }
21
+
22
+ export default ApiResultDispatcher;
23
+ export type { ApiResult };
@@ -5,6 +5,9 @@ import { commonDataActions } from "../../Store/Slices/commonDataSlice";
5
5
  import FieldValidationDispatcher, {
6
6
  FieldErrors,
7
7
  } from "../HOC/FieldValidationDispatcher";
8
+ import ApiResultDispatcher, {
9
+ ApiResult,
10
+ } from "../HOC/ApiResultDispatcher";
8
11
 
9
12
  let globalEventService: any = null;
10
13
 
@@ -30,6 +33,13 @@ const setupStoreAction = (dispatch: any, eventService: any, store: any) => {
30
33
  dispatch(appActions.loadValidation(errors));
31
34
  });
32
35
 
36
+ // Record the latest server response's success/alertType so flow-control
37
+ // helpers (e.g. hasLastApiCallFailed) can decide whether to continue with
38
+ // post-request actions like navigation.
39
+ ApiResultDispatcher.registerHandler((result: ApiResult) => {
40
+ dispatch(appActions.setLastApiResult(result));
41
+ });
42
+
33
43
  // Register loader handlers for external services
34
44
  eventService?.subscribe(eventServiceType.REQUEST_START, () => () => {
35
45
  dispatch(commonDataActions.updatePendingRequests(LoadingStatus.INPROCESS));
@@ -22,6 +22,7 @@ export const useCoreInitialization = (coreProps: CoreProps) => {
22
22
  requestHeaders: coreProps.requestHeaders,
23
23
  getSchemaBySlugService: coreProps.getSchemaBySlugService,
24
24
  responseFormatter: coreProps.responseFormatter,
25
+ fieldErrorMapper: coreProps.fieldErrorMapper,
25
26
  });
26
27
  };
27
28
 
@@ -1,9 +1,12 @@
1
+ import type { ApiResult } from "../ApplicationStart/HOC/ApiResultDispatcher";
2
+
1
3
  type prcElement = JSX.Element;
2
4
 
3
5
  type AppState = {
4
6
  inProgressCount: number;
5
7
  viewModel: any;
6
8
  validation: Record<string, string[]>;
9
+ lastApiResult: ApiResult | null;
7
10
  };
8
11
 
9
12
  type MetaDataState = {
@@ -10,6 +10,7 @@ import {
10
10
  } from "../UIElement/UIElementDefinations/ApiConfig";
11
11
  import NotificationService from "../ApplicationStart/HOC/NotificationService";
12
12
  import FieldValidationDispatcher from "../ApplicationStart/HOC/FieldValidationDispatcher";
13
+ import ApiResultDispatcher from "../ApplicationStart/HOC/ApiResultDispatcher";
13
14
  import { getStatusBaseNotifiType } from "./helpers/getStatusBaseNotifiType";
14
15
 
15
16
  export interface CustomAxiosRequestConfig extends AxiosRequestConfig {
@@ -74,6 +75,15 @@ export class CentralService {
74
75
  const alertType = statusRelatedConfig?.alertType || AlertType.TOAST;
75
76
  response.data.alertType = alertType;
76
77
 
78
+ // Record the response outcome so post-request flow (e.g. navigation)
79
+ // can short-circuit when the server reported failure regardless of
80
+ // alertType. Success is derived from the HTTP-ish status set by the
81
+ // response formatter (2xx = success).
82
+ ApiResultDispatcher.dispatch({
83
+ success: status >= 200 && status < 300,
84
+ alertType,
85
+ });
86
+
77
87
  if (alertType === AlertType.FIELDVALIDATION) {
78
88
  // Structured payload (default convention {propertyName, message}, or
79
89
  // whatever shape the registered fieldErrorMapper recognizes): route to
@@ -11,6 +11,7 @@ const initialState: AppState = {
11
11
  viewModel: { supportiveData: null, screenData: null },
12
12
  inProgressCount: 0,
13
13
  validation: {},
14
+ lastApiResult: null,
14
15
  };
15
16
 
16
17
  const appSlice = createSlice({
@@ -54,6 +55,9 @@ const appSlice = createSlice({
54
55
  [fieldName]: invalidMessages || [],
55
56
  };
56
57
  },
58
+ setLastApiResult: (state, action) => {
59
+ state.lastApiResult = action.payload;
60
+ },
57
61
  },
58
62
  });
59
63
  export const appActions = appSlice.actions;