diff --git a/src/ModalProvider.jsx b/src/ModalProvider.jsx index 13db2c2c..b9a2dea9 100644 --- a/src/ModalProvider.jsx +++ b/src/ModalProvider.jsx @@ -1,12 +1,19 @@ -import React, { useEffect } from 'react' -import { useOrganizationModal } from './hooks/useOrganization'; -import OrganizationModal from './components/Organization/OrganizationModal'; +import React, { useEffect } from "react"; +import { useOrganizationModal } from "./hooks/useOrganization"; +import OrganizationModal from "./components/Organization/OrganizationModal"; +import { useAuthModal } from "./hooks/useAuth"; +import SwitchTenant from "./pages/authentication/SwitchTenant"; const ModalProvider = () => { - const { isOpen,onClose } = useOrganizationModal(); - - return <>{isOpen && }; + const { isOpen, onClose } = useOrganizationModal(); + const { isOpen: isAuthOpen } = useAuthModal(); + + return ( + <> + {isOpen && } + {isAuthOpen && } + + ); }; - -export default ModalProvider \ No newline at end of file +export default ModalProvider; \ No newline at end of file diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 94ec39ac..6e05c1e1 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -19,6 +19,7 @@ import { useProjectName } from "../../hooks/useProjects"; import eventBus from "../../services/eventBus"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { MANAGE_PROJECT } from "../../utils/constants"; +import { useAuthModal } from "../../hooks/useAuth"; const Header = () => { const { profile } = useProfile(); @@ -26,6 +27,7 @@ const Header = () => { const dispatch = useDispatch(); const { data, loading } = useMaster(); const navigate = useNavigate(); + const {onOpen} = useAuthModal() const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); // { // console.log(location.pathname); @@ -455,6 +457,16 @@ const Header = () => { Change Password + +
  • onOpen()}> + {" "} + + + Switch Tenant + +
  • diff --git a/src/hooks/useAuth.jsx b/src/hooks/useAuth.jsx index 94b9f2c5..570e0454 100644 --- a/src/hooks/useAuth.jsx +++ b/src/hooks/useAuth.jsx @@ -1,39 +1,51 @@ import { useState, useEffect, useCallback } from "react"; +import { + Mutation, + useMutation, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; +import { Link, useNavigate } from "react-router-dom"; +import AuthRepository from "../repositories/AuthRepository.jsx"; +import { useDispatch, useSelector } from "react-redux"; +import { + closeAuthModal, + openAuthModal, +} from "../slices/localVariablesSlice.jsx"; -export const useTenantList = ({ autoFetch = true } = {}) => { - const [data, setData] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const fetchTenantList = useCallback(async () => { - setLoading(true); - setError(null); - try { - const response = setData(response.data || []); - } catch (err) { - setError(err); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - if (autoFetch) fetchTenantList(); - }, [autoFetch, fetchTenantList]); - - return { - data, - loading, - error, - refetch: fetchTenantList, // manual trigger - }; +export const useTenants = () => { + return useQuery({ + queryKey: ["tenantlist"], + queryFn: async () => await AuthRepository.getTenantList(), + }); }; +export const useSelectTenant = (onSuccessCallBack) => { + const queryClient = useQueryClient(); -export const useTenants =()=>{ - get - return useQueery({ - queryKey:["tenantlist",localhost.get("orgJwtToken")], - queryFun:async()=> await AuthRepository. - }) -} \ No newline at end of file + return useMutation({ + mutationFn: async (tenantId) => { + const res = await AuthRepository.selectTenant(tenantId); + return res.data; + }, + onSuccess: (data) => { + localStorage.setItem("ltkn", data.token); + localStorage.setItem("rtkn", data.refreshToken); + if (onSuccessCallBack) onSuccessCallBack(); + }, + onError: (error) => { + showToast(error.message || "Error while creating project", "error"); + }, + }); +}; + +export const useAuthModal = () => { + const dispatch = useDispatch(); + const { isOpen } = useSelector((state) => state.localVariables.AuthModal); + + return { + isOpen, + onOpen: () => dispatch(openAuthModal()), + onClose: () => dispatch(closeAuthModal()), + }; +}; \ No newline at end of file diff --git a/src/pages/authentication/LoginPage.jsx b/src/pages/authentication/LoginPage.jsx index 3e331376..363de91f 100644 --- a/src/pages/authentication/LoginPage.jsx +++ b/src/pages/authentication/LoginPage.jsx @@ -44,7 +44,7 @@ const LoginPage = () => { localStorage.setItem("jwtToken", response.data.token); localStorage.setItem("refreshToken", response.data.refreshToken); setLoading(false); - navigate("/dashboard"); + navigate("/auth/switch/org"); } else { await AuthRepository.sendOTP({ email: data.username }); showToast("OTP has been sent to your email.", "success"); diff --git a/src/pages/authentication/SwitchTenant.jsx b/src/pages/authentication/SwitchTenant.jsx new file mode 100644 index 00000000..e36ede07 --- /dev/null +++ b/src/pages/authentication/SwitchTenant.jsx @@ -0,0 +1,101 @@ +import React, { useState } from "react"; +import Modal from "../../components/common/Modal"; +import { useAuthModal, useSelectTenant, useTenants } from "../../hooks/useAuth"; +import { useProfile } from "../../hooks/useProfile"; +import { useQueryClient } from "@tanstack/react-query"; +import AuthRepository from "../../repositories/AuthRepository"; + +const SwitchTenant = () => { + const queryClient = useQueryClient(); + const { profile } = useProfile(); + const [pendingTenant, setPendingTenant] = useState(null); + const { isOpen, onClose, onOpen } = useAuthModal(); + const { data, isLoading, isError, error } = useTenants(); + const { mutate: chooseTenant, isPending } = useSelectTenant(() => { + onClose(); + queryClient.clear(); + + // 2. Force fetch profile fresh for the new tenant + queryClient.fetchQuery({ + queryKey: ["profile"], + queryFn: () => AuthRepository.profile(), + }); + }); + const currentTenant = localStorage.getItem("ctnt"); + const handleTenantselection = (tenantId) => { + setPendingTenant(tenantId); + localStorage.setItem("ctnt", tenantId); + chooseTenant(tenantId); + }; + const contentBody = ( +
    +

    Switch Workplace

    +
    + {data?.data.map((tenant) => ( +
    +
    +
    + {tenant.name} +
    + +
    +

    + {tenant?.name} +

    + +
    +

    Industry:

    +

    + {tenant?.industry?.name || "Not Available"} +

    +
    + + {tenant?.description && ( +

    + {tenant?.description} +

    + )} + + +
    +
    +
    + ))} +
    +
    + ); + return ; +}; + +export default SwitchTenant; \ No newline at end of file diff --git a/src/pages/authentication/TenantSelectionPage.jsx b/src/pages/authentication/TenantSelectionPage.jsx new file mode 100644 index 00000000..5e748548 --- /dev/null +++ b/src/pages/authentication/TenantSelectionPage.jsx @@ -0,0 +1,102 @@ +import { useEffect, useState } from "react"; +import { useTenants, useSelectTenant } from "../../hooks/useAuth.jsx"; +import { Link, useNavigate } from "react-router-dom"; +import Dashboard from "../../components/Dashboard/Dashboard.jsx"; + +const TenantSelectionPage = () => { + const [pendingTenant, setPendingTenant] = useState(null); + const navigate = useNavigate(); + + const { data, isLoading, isError, error } = useTenants(); + const { mutate: chooseTenant, isPending } = useSelectTenant(() => { + navigate("/dashboard"); + }); + const handleTenantselection = (tenantId) => { + setPendingTenant(tenantId); + localStorage.setItem("ctnt", tenantId); + chooseTenant(tenantId); + }; + + useEffect(() => { + if (localStorage.getItem("ctnt")) { + return navigate("/dashboard"); + } + }, []); + + if (isLoading) { + return ( +
    +
    + Loading tenants... +
    +
    + ); + } + + return ( +
    + {/* Header */} +
    +

    Welcome

    +

    + Please select which dashboard you want to explore!!! +

    +
    + + {/* Card Section */} +
    + {data?.data.map((tenant) => ( +
    +
    + {/* Image */} +
    + {tenant.name} +
    + + {/* Content */} +
    +

    + {tenant?.name} +

    + +
    +

    Industry:

    +

    + {tenant?.industry?.name || "Not Available"} +

    +
    + + {tenant?.description && ( +

    + {tenant?.description} +

    + )} + + +
    +
    +
    + ))} +
    +
    + ); +}; + +export default TenantSelectionPage; \ No newline at end of file diff --git a/src/repositories/AuthRepository.jsx b/src/repositories/AuthRepository.jsx index b1d28940..cf3f6e31 100644 --- a/src/repositories/AuthRepository.jsx +++ b/src/repositories/AuthRepository.jsx @@ -15,7 +15,9 @@ const AuthRepository = { logout: (data) => api.post("/api/auth/logout", data), profile: () => api.get("/api/user/profile"), changepassword: (data) => api.post("/api/auth/change-password", data), - appmenu:()=>api.get('/api/appmenu/get/menu') + appmenu: () => api.get('/api/appmenu/get/menu'), + selectTenant: (tenantId) => api.post(`/api/Auth/select-tenant/${tenantId}`), + getTenantList: () => api.get("/api/Auth/get/user/tenants"), }; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 66943fb0..70052cad 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -51,6 +51,7 @@ import { Navigate } from "react-router-dom"; import CreateTenant from "../pages/Tenant/CreateTenant"; import OrganizationPage from "../pages/Organization/OrganizationPage"; import LandingPage from "../pages/Home/LandingPage"; +import TenantSelectionPage from "../pages/authentication/TenantSelectionPage"; const router = createBrowserRouter( [ { @@ -69,6 +70,7 @@ const router = createBrowserRouter( { path: "/auth/changepassword", element: }, ], }, + { path: "/auth/switch/org", element: }, { element: , errorElement: , diff --git a/src/slices/localVariablesSlice.jsx b/src/slices/localVariablesSlice.jsx index c78e4da7..784100e0 100644 --- a/src/slices/localVariablesSlice.jsx +++ b/src/slices/localVariablesSlice.jsx @@ -3,54 +3,56 @@ import { createSlice } from "@reduxjs/toolkit"; const localVariablesSlice = createSlice({ name: "localVariables", initialState: { - selectedMaster:"Application Role", - regularizationCount:0, - defaultDateRange: { + selectedMaster: "Application Role", + regularizationCount: 0, + defaultDateRange: { startDate: null, endDate: null, }, projectId: null, - reload:false, + reload: false, - OrganizationModal:{ + OrganizationModal: { isOpen: false, orgData: null, - prevStep:null, + prevStep: null, startStep: 1, - flowType: "default", - } - + flowType: "default", + }, + + AuthModal: { + isOpen: false, + }, }, reducers: { changeMaster: (state, action) => { - state.selectedMaster = action.payload; + state.selectedMaster = action.payload; }, updateRegularizationCount: (state, action) => { state.regularizationCount = action.payload; }, - setProjectId: (state, action) => { - localStorage.setItem("project",null) + setProjectId: (state, action) => { + localStorage.setItem("project", null); state.projectId = action.payload; - localStorage.setItem("project",state.projectId || null) + localStorage.setItem("project", state.projectId || null); }, - refreshData: ( state, action ) => - { - state.reload = action.payload + refreshData: (state, action) => { + state.reload = action.payload; }, setDefaultDateRange: (state, action) => { state.defaultDateRange = action.payload; }, -openOrgModal: (state, action) => { -state.OrganizationModal.isOpen = true; - state.OrganizationModal.orgData = action.payload?.orgData || null; + openOrgModal: (state, action) => { + state.OrganizationModal.isOpen = true; + state.OrganizationModal.orgData = action.payload?.orgData || null; - if (state.OrganizationModal.startStep) { - state.OrganizationModal.prevStep = state.OrganizationModal.startStep; - } + if (state.OrganizationModal.startStep) { + state.OrganizationModal.prevStep = state.OrganizationModal.startStep; + } - state.OrganizationModal.startStep = action.payload?.startStep || 1; - state.OrganizationModal.flowType = action.payload?.flowType || "default"; + state.OrganizationModal.startStep = action.payload?.startStep || 1; + state.OrganizationModal.flowType = action.payload?.flowType || "default"; }, closeOrgModal: (state) => { state.OrganizationModal.isOpen = false; @@ -61,8 +63,26 @@ state.OrganizationModal.isOpen = true; toggleOrgModal: (state) => { state.OrganizationModal.isOpen = !state.OrganizationModal.isOpen; }, + + openAuthModal: (state, action) => { + state.AuthModal.isOpen = true; + }, + closeAuthModal: (state, action) => { + state.AuthModal.isOpen = false; + }, }, }); -export const { changeMaster ,updateRegularizationCount,setProjectId,refreshData,setDefaultDateRange,openOrgModal,closeOrgModal,toggleOrgModal} = localVariablesSlice.actions; -export default localVariablesSlice.reducer; +export const { + changeMaster, + updateRegularizationCount, + setProjectId, + refreshData, + setDefaultDateRange, + openOrgModal, + closeOrgModal, + toggleOrgModal, + openAuthModal, + closeAuthModal, +} = localVariablesSlice.actions; +export default localVariablesSlice.reducer; \ No newline at end of file