frontend-plus-react 0.1.16 → 0.1.18

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/dist/index.js CHANGED
@@ -2527,8 +2527,8 @@ var GenericDataGrid = ({
2527
2527
  var GenericDataGrid_default = GenericDataGrid;
2528
2528
 
2529
2529
  // src/App.tsx
2530
- import React11, { useEffect as useEffect7 } from "react";
2531
- import { BrowserRouter, Routes, Route, useLocation as useLocation4, Navigate as Navigate2 } from "react-router-dom";
2530
+ import React12, { useEffect as useEffect7 } from "react";
2531
+ import { BrowserRouter, Routes, Route, useLocation as useLocation5, Navigate as Navigate2, Outlet as Outlet2 } from "react-router-dom";
2532
2532
 
2533
2533
  // src/components/MainLayout.tsx
2534
2534
  import { useEffect as useEffect3 } from "react";
@@ -2547,8 +2547,8 @@ import {
2547
2547
  } from "@mui/material";
2548
2548
 
2549
2549
  // src/contexts/AppContext.tsx
2550
- import { createContext as createContext2, useContext as useContext2, useState as useState6, useEffect as useEffect2, useCallback as useCallback5, useMemo as useMemo3 } from "react";
2551
- import { useNavigate } from "react-router-dom";
2550
+ import { createContext as createContext2, useContext as useContext2, useState as useState6, useEffect as useEffect2, useCallback as useCallback5, useMemo as useMemo3, useRef as useRef2 } from "react";
2551
+ import { useLocation, useNavigate } from "react-router-dom";
2552
2552
  import { useSelector as useSelector2, useDispatch as useDispatch2 } from "react-redux";
2553
2553
  import { jsx as jsx15 } from "react/jsx-runtime";
2554
2554
  var AuthContext = createContext2(void 0);
@@ -2560,16 +2560,33 @@ var useApp = () => {
2560
2560
  const clientContext = useSelector2((state) => state.clientContext);
2561
2561
  return { ...context, clientContext };
2562
2562
  };
2563
- var AppProvider = ({ children }) => {
2563
+ var AppProvider = ({ children, publicPaths = [] }) => {
2564
2564
  console.log("AppProvider rendered/re-rendered");
2565
2565
  const [isLoggedIn, setIsLoggedIn] = useState6(false);
2566
2566
  const [showSessionExpiredMessage, setShowSessionExpiredMessage] = useState6(false);
2567
+ const isRedirectingRef = useRef2(false);
2567
2568
  const dispatch = useDispatch2();
2568
2569
  const clientContext = useSelector2((state) => state.clientContext);
2569
2570
  const navigate = useNavigate();
2571
+ const handleExpiredSessionRedirect = useCallback5(() => {
2572
+ isRedirectingRef.current = true;
2573
+ setShowSessionExpiredMessage(false);
2574
+ setIsLoggedIn(false);
2575
+ navigate("/login", { replace: true });
2576
+ setTimeout(() => {
2577
+ isRedirectingRef.current = false;
2578
+ }, 2e3);
2579
+ }, [navigate]);
2580
+ const location = useLocation();
2581
+ const isCurrentPathPublic = useCallback5(() => {
2582
+ const current = location.pathname;
2583
+ return publicPaths.some((path) => current === path || current.startsWith(`${path}/`));
2584
+ }, [location.pathname, publicPaths]);
2570
2585
  const checkSession = useCallback5(async () => {
2586
+ if (isRedirectingRef.current) return;
2571
2587
  try {
2572
2588
  const response = await fetchApi("/keep-alive.json", { method: "GET" });
2589
+ if (isRedirectingRef.current) return;
2573
2590
  if (response.ok) {
2574
2591
  setIsLoggedIn(true);
2575
2592
  setShowSessionExpiredMessage(false);
@@ -2585,15 +2602,19 @@ var AppProvider = ({ children }) => {
2585
2602
  } else {
2586
2603
  setIsLoggedIn(false);
2587
2604
  dispatch(clearClientContext());
2588
- if (!showSessionExpiredMessage && window.location.pathname !== "/login") {
2605
+ if (!showSessionExpiredMessage && !isCurrentPathPublic()) {
2606
+ console.warn("Sesi\xF3n inv\xE1lida en ruta protegida. Mostrando modal.");
2589
2607
  setShowSessionExpiredMessage(true);
2608
+ } else {
2609
+ console.log("Sesi\xF3n inv\xE1lida, pero estamos en ruta p\xFAblica. Todo bien.");
2590
2610
  }
2591
2611
  }
2592
2612
  } catch (error) {
2593
2613
  console.error("Error checking session:", error);
2594
2614
  setIsLoggedIn(false);
2595
2615
  dispatch(clearClientContext());
2596
- if (!showSessionExpiredMessage && window.location.pathname !== "/login") {
2616
+ const isPublic = publicPaths.some((p) => location.pathname === p || location.pathname.startsWith(p + "/"));
2617
+ if (!showSessionExpiredMessage && !isCurrentPathPublic()) {
2597
2618
  setShowSessionExpiredMessage(true);
2598
2619
  }
2599
2620
  }
@@ -2610,13 +2631,15 @@ var AppProvider = ({ children }) => {
2610
2631
  setIsLoggedIn,
2611
2632
  checkSession,
2612
2633
  showSessionExpiredMessage,
2613
- setShowSessionExpiredMessage
2634
+ setShowSessionExpiredMessage,
2635
+ handleExpiredSessionRedirect
2614
2636
  }), [
2615
2637
  isLoggedIn,
2616
2638
  setIsLoggedIn,
2617
2639
  checkSession,
2618
2640
  showSessionExpiredMessage,
2619
- setShowSessionExpiredMessage
2641
+ setShowSessionExpiredMessage,
2642
+ handleExpiredSessionRedirect
2620
2643
  ]);
2621
2644
  return /* @__PURE__ */ jsx15(AuthContext.Provider, { value: authContextValue, children });
2622
2645
  };
@@ -2638,7 +2661,7 @@ import WarningAmberIcon3 from "@mui/icons-material/WarningAmber";
2638
2661
  import DesktopWindowsIcon from "@mui/icons-material/DesktopWindows";
2639
2662
  import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore";
2640
2663
  import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
2641
- import { useNavigate as useNavigate2, useLocation } from "react-router-dom";
2664
+ import { useNavigate as useNavigate2, useLocation as useLocation2 } from "react-router-dom";
2642
2665
  import { blue, orange, teal, red, grey } from "@mui/material/colors";
2643
2666
 
2644
2667
  // src/pages/WScreens.tsx
@@ -2682,7 +2705,7 @@ var extendWScreens = (newWScreens) => {
2682
2705
  import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
2683
2706
  var MenuListItem = ({ item, level, onMenuItemClick }) => {
2684
2707
  const navigate = useNavigate2();
2685
- const location = useLocation();
2708
+ const location = useLocation2();
2686
2709
  const open = useSubMenuOpenState(item.name);
2687
2710
  const dispatch = useAppDispatch();
2688
2711
  const handleClick = () => {
@@ -2770,7 +2793,7 @@ var MenuListItem = ({ item, level, onMenuItemClick }) => {
2770
2793
  var SideMenu = ({ onMenuItemClick }) => {
2771
2794
  const { clientContext } = useApp();
2772
2795
  const navigate = useNavigate2();
2773
- const location = useLocation();
2796
+ const location = useLocation2();
2774
2797
  const dispatch = useAppDispatch();
2775
2798
  const subMenuOpenStates = useAppSelector((state) => state.menuUi.subMenuOpenStates);
2776
2799
  const allSubMenusKeys = Object.keys(subMenuOpenStates);
@@ -2991,7 +3014,6 @@ var MainLayout_default = MainLayout;
2991
3014
 
2992
3015
  // src/components/SessionExpiredMessage.tsx
2993
3016
  import { Box as Box12, Typography as Typography9, Button as Button5, Modal } from "@mui/material";
2994
- import { useNavigate as useNavigate4 } from "react-router-dom";
2995
3017
  import { jsx as jsx19, jsxs as jsxs10 } from "react/jsx-runtime";
2996
3018
  var style = {
2997
3019
  position: "absolute",
@@ -3007,12 +3029,9 @@ var style = {
3007
3029
  borderRadius: 2
3008
3030
  };
3009
3031
  var SessionExpiredMessage = () => {
3010
- const { showSessionExpiredMessage, setShowSessionExpiredMessage, setIsLoggedIn } = useApp();
3011
- const navigate = useNavigate4();
3032
+ const { showSessionExpiredMessage, setShowSessionExpiredMessage, setIsLoggedIn, handleExpiredSessionRedirect } = useApp();
3012
3033
  const handleClose = () => {
3013
- setShowSessionExpiredMessage(false);
3014
- setIsLoggedIn(false);
3015
- navigate("/login", { replace: true });
3034
+ handleExpiredSessionRedirect();
3016
3035
  };
3017
3036
  return /* @__PURE__ */ jsx19(
3018
3037
  Modal,
@@ -3085,7 +3104,7 @@ import { Button as Button6, TextField, Typography as Typography12, Alert as Aler
3085
3104
  import { useForm, Controller } from "react-hook-form";
3086
3105
  import { zodResolver } from "@hookform/resolvers/zod";
3087
3106
  import * as z from "zod";
3088
- import { useNavigate as useNavigate5 } from "react-router-dom";
3107
+ import { useNavigate as useNavigate4 } from "react-router-dom";
3089
3108
  import { useDispatch as useDispatch4 } from "react-redux";
3090
3109
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
3091
3110
  var loginSchema = z.object({
@@ -3095,7 +3114,7 @@ var loginSchema = z.object({
3095
3114
  // Añadir validación básica
3096
3115
  });
3097
3116
  var LoginPage = () => {
3098
- const navigate = useNavigate5();
3117
+ const navigate = useNavigate4();
3099
3118
  const [loginError, setLoginError] = useState7(null);
3100
3119
  const [isSubmitting, setIsSubmitting] = useState7(false);
3101
3120
  const { setIsLoggedIn, checkSession } = useApp();
@@ -3695,10 +3714,10 @@ import { PersistGate } from "redux-persist/integration/react";
3695
3714
 
3696
3715
  // src/components/PrivateRoute.tsx
3697
3716
  import { useEffect as useEffect5 } from "react";
3698
- import { Navigate, useLocation as useLocation2 } from "react-router-dom";
3717
+ import { Navigate, useLocation as useLocation3 } from "react-router-dom";
3699
3718
  import { Fragment as Fragment4, jsx as jsx25 } from "react/jsx-runtime";
3700
3719
  var PrivateRoute = ({ children }) => {
3701
- const location = useLocation2();
3720
+ const location = useLocation3();
3702
3721
  const dispatch = useAppDispatch();
3703
3722
  const clientContextStatus = useClientContextStatus();
3704
3723
  const isAuthenticated = clientContextStatus === "succeeded";
@@ -3733,10 +3752,10 @@ var PrivateRoute_default = PrivateRoute;
3733
3752
 
3734
3753
  // src/components/InitialRedirectHandler.tsx
3735
3754
  import { useEffect as useEffect6 } from "react";
3736
- import { useLocation as useLocation3, useNavigate as useNavigate6 } from "react-router-dom";
3755
+ import { useLocation as useLocation4, useNavigate as useNavigate5 } from "react-router-dom";
3737
3756
  var InitialRedirectHandler = () => {
3738
- const location = useLocation3();
3739
- const navigate = useNavigate6();
3757
+ const location = useLocation4();
3758
+ const navigate = useNavigate5();
3740
3759
  const dispatch = useAppDispatch();
3741
3760
  const redirectPath = useRedirectPath();
3742
3761
  const clientContextStatus = useClientContextStatus();
@@ -3838,19 +3857,40 @@ var MenuTablePage_default = MenuTablePage;
3838
3857
 
3839
3858
  // src/App.tsx
3840
3859
  import { Box as Box20 } from "@mui/material";
3860
+
3861
+ // src/utils/routeUtils.ts
3862
+ import { Children, isValidElement } from "react";
3863
+ var extractPathsFromRoutes = (node) => {
3864
+ const paths = [];
3865
+ Children.forEach(node, (child) => {
3866
+ if (!isValidElement(child)) return;
3867
+ if (child.props.path) {
3868
+ paths.push(child.props.path);
3869
+ }
3870
+ if (child.props.children) {
3871
+ paths.push(...extractPathsFromRoutes(child.props.children));
3872
+ }
3873
+ });
3874
+ return paths;
3875
+ };
3876
+
3877
+ // src/App.tsx
3841
3878
  import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
3842
3879
  var LocationTracker = () => {
3843
- const location = useLocation4();
3880
+ const location = useLocation5();
3844
3881
  const dispatch = useAppDispatch();
3845
3882
  useEffect7(() => {
3846
3883
  dispatch(setCurrentPath(location.pathname + location.search));
3847
3884
  }, [location, dispatch]);
3848
3885
  return null;
3849
3886
  };
3850
- function FrontendPlusReactRoutes({ myRoutes, layout: Layout = MainLayout_default }) {
3887
+ function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout = MainLayout_default, unloggedLayout: UnloggedLayout }) {
3851
3888
  return /* @__PURE__ */ jsxs18(Routes, { children: [
3852
- /* @__PURE__ */ jsx28(Route, { path: "/login", element: /* @__PURE__ */ jsx28(LoginPage_default, {}) }),
3853
- /* @__PURE__ */ jsx28(Route, { path: "/logout", element: /* @__PURE__ */ jsx28(LogoutPage_default, {}) }),
3889
+ /* @__PURE__ */ jsxs18(Route, { element: UnloggedLayout ? /* @__PURE__ */ jsx28(UnloggedLayout, {}) : /* @__PURE__ */ jsx28(Outlet2, {}), children: [
3890
+ /* @__PURE__ */ jsx28(Route, { path: "/login", element: /* @__PURE__ */ jsx28(LoginPage_default, {}) }),
3891
+ /* @__PURE__ */ jsx28(Route, { path: "/logout", element: /* @__PURE__ */ jsx28(LogoutPage_default, {}) }),
3892
+ myUnloggedRoutes
3893
+ ] }),
3854
3894
  /* @__PURE__ */ jsxs18(Route, { element: /* @__PURE__ */ jsxs18(PrivateRoute_default, { children: [
3855
3895
  /* @__PURE__ */ jsx28(InitialRedirectHandler_default, {}),
3856
3896
  /* @__PURE__ */ jsx28(Layout, {})
@@ -3864,12 +3904,12 @@ function FrontendPlusReactRoutes({ myRoutes, layout: Layout = MainLayout_default
3864
3904
  Route,
3865
3905
  {
3866
3906
  path: `/wScreens/${screenName}`,
3867
- element: React11.createElement(
3907
+ element: React12.createElement(
3868
3908
  Box20,
3869
3909
  {
3870
3910
  sx: { pt: 3, pl: 4 }
3871
3911
  },
3872
- React11.createElement(wScreens[screenName], { screenName })
3912
+ React12.createElement(wScreens[screenName], { screenName })
3873
3913
  )
3874
3914
  },
3875
3915
  screenName
@@ -3881,17 +3921,17 @@ function FrontendPlusReactRoutes({ myRoutes, layout: Layout = MainLayout_default
3881
3921
  element: /* @__PURE__ */ jsx28(Box20, { sx: { p: 3 }, children: /* @__PURE__ */ jsx28(FallbackWScreen, { screenName: ":screenName" }) })
3882
3922
  }
3883
3923
  ),
3924
+ myRoutes,
3884
3925
  /* @__PURE__ */ jsx28(Route, { path: "*", element: /* @__PURE__ */ jsx28("div", { style: { marginTop: "20px", marginLeft: "10px" }, children: "404 - Recurso No Encontrado" }) })
3885
3926
  ] }),
3886
- myRoutes,
3887
3927
  /* @__PURE__ */ jsx28(Route, { path: "*", element: /* @__PURE__ */ jsx28(Navigate2, { to: "/", replace: true }) })
3888
3928
  ] });
3889
3929
  }
3890
- var FrontendPlusProviders = ({ children }) => {
3930
+ var FrontendPlusProviders = ({ children, publicPaths }) => {
3891
3931
  return /* @__PURE__ */ jsx28(Provider, { store, children: /* @__PURE__ */ jsx28(PersistGate, { loading: null, persistor, children: /* @__PURE__ */ jsxs18(BrowserRouter, { children: [
3892
3932
  /* @__PURE__ */ jsx28(LocationTracker, {}),
3893
3933
  " ",
3894
- /* @__PURE__ */ jsx28(AppProvider, { children: /* @__PURE__ */ jsxs18(SnackbarProvider, { children: [
3934
+ /* @__PURE__ */ jsx28(AppProvider, { publicPaths, children: /* @__PURE__ */ jsxs18(SnackbarProvider, { children: [
3895
3935
  children,
3896
3936
  /* @__PURE__ */ jsx28(SessionExpiredMessage_default, {})
3897
3937
  ] }) })
@@ -3899,6 +3939,8 @@ var FrontendPlusProviders = ({ children }) => {
3899
3939
  };
3900
3940
  var App = ({
3901
3941
  myRoutes,
3942
+ myUnloggedRoutes,
3943
+ unloggedLayout,
3902
3944
  myWScreens,
3903
3945
  myClientSides,
3904
3946
  myResultsOk
@@ -3914,7 +3956,18 @@ var App = ({
3914
3956
  extendWScreens(myWScreens);
3915
3957
  }
3916
3958
  }, [myClientSides, myResultsOk, myWScreens]);
3917
- return /* @__PURE__ */ jsx28(FrontendPlusProviders, { children: /* @__PURE__ */ jsx28(FrontendPlusReactRoutes, { myRoutes }) });
3959
+ const safePaths = React12.useMemo(() => {
3960
+ const extracted = extractPathsFromRoutes(myUnloggedRoutes);
3961
+ return ["/login", "/logout", ...extracted];
3962
+ }, [myUnloggedRoutes]);
3963
+ return /* @__PURE__ */ jsx28(FrontendPlusProviders, { publicPaths: safePaths, children: /* @__PURE__ */ jsx28(
3964
+ FrontendPlusReactRoutes,
3965
+ {
3966
+ myRoutes,
3967
+ myUnloggedRoutes,
3968
+ unloggedLayout
3969
+ }
3970
+ ) });
3918
3971
  };
3919
3972
  var App_default = App;
3920
3973
  export {
@@ -3931,6 +3984,7 @@ export {
3931
3984
  extendClientSides,
3932
3985
  extendResultsOk,
3933
3986
  extendWScreens,
3987
+ extractPathsFromRoutes,
3934
3988
  resultsOk as resultComponents,
3935
3989
  useApiCall,
3936
3990
  useApiCallWithoutSnackbar,