diff --git a/src/hooks/useAuth.jsx b/src/hooks/useAuth.jsx new file mode 100644 index 00000000..94b9f2c5 --- /dev/null +++ b/src/hooks/useAuth.jsx @@ -0,0 +1,39 @@ +import { useState, useEffect, useCallback } from "react"; + +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 =()=>{ + get + return useQueery({ + queryKey:["tenantlist",localhost.get("orgJwtToken")], + queryFun:async()=> await AuthRepository. + }) +} \ No newline at end of file diff --git a/src/pages/authentication/LoginPage.jsx b/src/pages/authentication/LoginPage.jsx index 3e331376..e75b9f0c 100644 --- a/src/pages/authentication/LoginPage.jsx +++ b/src/pages/authentication/LoginPage.jsx @@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { AuthWrapper } from "./AuthWrapper"; - +import { setOrgToken } from "../../services/tenantService"; const LoginPage = () => { const navigate = useNavigate(); const [loading, setLoading] = useState(false); @@ -16,13 +16,13 @@ const LoginPage = () => { const loginSchema = IsLoginWithOTP ? z.object({ - username: z.string().trim().email({ message: "Valid email required" }), - }) + username: z.string().trim().email({ message: "Valid email required" }), + }) : z.object({ - username: z.string().trim().email({ message: "Valid email required" }), - password: z.string().trim().min(1, { message: "Password required" }), - rememberMe: z.boolean(), - }); + username: z.string().trim().email({ message: "Valid email required" }), + password: z.string().trim().min(1, { message: "Password required" }), + rememberMe: z.boolean(), + }); const { register, @@ -41,10 +41,11 @@ const LoginPage = () => { password: data.password, }; const response = await AuthRepository.login(userCredential); - localStorage.setItem("jwtToken", response.data.token); - localStorage.setItem("refreshToken", response.data.refreshToken); + // localStorage.setItem("jwtToken", response.data.token); + // localStorage.setItem("refreshToken", response.data.refreshToken); setLoading(false); - navigate("/dashboard"); + setOrgToken(response.data.token, response.data.refreshToken); + navigate("/auth/user"); } else { await AuthRepository.sendOTP({ email: data.username }); showToast("OTP has been sent to your email.", "success"); @@ -146,8 +147,9 @@ const LoginPage = () => { type={hidepass ? "password" : "text"} autoComplete="new-password" id="password" - className={`form-control form-control-xl shadow-none ${errors.password ? "is-invalid" : "" - }`} + className={`form-control form-control-xl shadow-none ${ + errors.password ? "is-invalid" : "" + }`} name="password" {...register("password")} placeholder="••••••••••••" @@ -170,13 +172,15 @@ const LoginPage = () => { {/* ✅ Error message */} {errors.password && ( -
+
{errors.password.message}
)}
- {/* Remember Me + Forgot Password */}
@@ -209,8 +213,8 @@ const LoginPage = () => { {loading ? "Please Wait..." : IsLoginWithOTP - ? "Send OTP" - : "Sign In"} + ? "Send OTP" + : "Sign In"} {/* Login With OTP Button */} @@ -254,4 +258,4 @@ const LoginPage = () => { ); }; -export default LoginPage; \ No newline at end of file +export default LoginPage; diff --git a/src/repositories/AttendanceRepository.jsx b/src/repositories/AttendanceRepository.jsx index 679001f8..a8f1a0b1 100644 --- a/src/repositories/AttendanceRepository.jsx +++ b/src/repositories/AttendanceRepository.jsx @@ -1,13 +1,10 @@ import { api } from "../utils/axiosClient"; - -const AttendanceRepository = { - markAttendance:(data)=>api.post("/api/attendance/record",data), - getAttendance:(id)=>api.get(`api/attendance/project/team?projectId=${id}`), - getAttendanceFilteredByDate: ( projectId, fromDate, toDate ) => - { - - let url = `api/Attendance/project/log?projectId=${ projectId }` +const AttendanceRepository = { + markAttendance: (data) => api.post("/api/attendance/record", data), + getAttendance: (id) => api.get(`api/attendance/project/team?projectId=${id}`), + getAttendanceFilteredByDate: (projectId, fromDate, toDate) => { + let url = `api/Attendance/project/log?projectId=${projectId}`; if (fromDate) { url += `&dateFrom=${fromDate}`; } @@ -15,27 +12,24 @@ const AttendanceRepository = { if (toDate) { url += `&dateTo=${toDate}`; } - return api.get(url) + return api.get(url); }, - - getAttendanceLogs: ( id ) => api.get( `api/attendance/log/attendance/${ id }` ), - getRegularizeList: ( id ) => api.get( `api/attendance/regularize?projectId=${ id }` ), - - getAttendanceByEmployee: ( employeeId, fromDate, toDate ) => - { - - let url = `api/Attendance/log/employee/${ employeeId }?` - if (fromDate) { - url += `&dateFrom=${fromDate}`; - } - - if (toDate) { - url += `&dateTo=${toDate}`; - } - return api.get(url) - }, -} + getAttendanceLogs: (id) => api.get(`api/attendance/log/attendance/${id}`), + getRegularizeList: (id) => + api.get(`api/attendance/regularize?projectId=${id}`), + + getAttendanceByEmployee: (employeeId, fromDate, toDate) => { + let url = `api/Attendance/log/employee/${employeeId}?`; + if (fromDate) { + url += `&dateFrom=${fromDate}`; + } + + if (toDate) { + url += `&dateTo=${toDate}`; + } + return api.get(url); + }, +}; export default AttendanceRepository; - diff --git a/src/repositories/AuthRepository.jsx b/src/repositories/AuthRepository.jsx index 5004c7ec..e6c6411a 100644 --- a/src/repositories/AuthRepository.jsx +++ b/src/repositories/AuthRepository.jsx @@ -16,6 +16,22 @@ const AuthRepository = { profile: () => api.get("/api/user/profile"), changepassword: (data) => api.post("/api/auth/change-password", data), appmenu: () => api.get("/api/appmenu/get/menu"), + // getTenantList: () => + // api.get("/api/Auth/get/user/tenants", {}, { useTenantToken: false }), + // selectTenant: (tenantId) => + // api.post(`/api/Auth/select-tenant/${tenantId}`), + + // ---------------- Org-level routes (use org token) --------------- + // Note: pass `orgToken: true` (4th arg) so axios interceptor uses orgJwtToken. + getTenantList: () => api.get("/api/Auth/get/user/tenants", {}, {}, true), + + // Exchange tenantId (with org token) -> server returns tenant JWT + // Using POST with body { tenantId } (adjust if your API expects path param). + selectTenant: (tenantId) => + api.post("/api/Auth/select-tenant", { tenantId }, {}, true), + + // If your backend expects tenantId in URL instead of body, use this variant: + // selectTenant: (tenantId) => api.post(`/api/Auth/select-tenant/${tenantId}`, {}, {}, true), }; export default AuthRepository; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 8e32e0e1..66943fb0 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -49,7 +49,6 @@ import MainResetPasswordPage from "../pages/authentication/MainResetPasswordPage import TenantPage from "../pages/Tenant/TenantPage"; import { Navigate } from "react-router-dom"; import CreateTenant from "../pages/Tenant/CreateTenant"; -; import OrganizationPage from "../pages/Organization/OrganizationPage"; import LandingPage from "../pages/Home/LandingPage"; const router = createBrowserRouter( diff --git a/src/router/ProtectedRoute.jsx b/src/router/ProtectedRoute.jsx index a1879e85..68dd847f 100644 --- a/src/router/ProtectedRoute.jsx +++ b/src/router/ProtectedRoute.jsx @@ -8,7 +8,7 @@ const ProtectedRoute = () => { // // const isAuthenticated = true; // isTokenValid(); // return isAuthenticated ? : - + const [isAuthenticated, setIsAuthenticated] = useState(null); useEffect(() => { @@ -66,7 +66,7 @@ const attemptTokenRefresh = async (storedRefreshToken) => { localStorage.setItem("jwtToken", response.data.token); localStorage.setItem("refreshToken", response.data.refreshToken); return true; - // api + // api // .post("/api/auth/refresh-token", { // token: localStorage.getItem("jwtToken"), // refreshToken: refreshToken, diff --git a/src/slices/globalVariablesSlice.jsx b/src/slices/globalVariablesSlice.jsx index 7bdadd51..8e656000 100644 --- a/src/slices/globalVariablesSlice.jsx +++ b/src/slices/globalVariablesSlice.jsx @@ -3,23 +3,23 @@ import { createSlice } from "@reduxjs/toolkit"; const globalVariablesSlice = createSlice({ name: "globalVariables", initialState: { - loginUser:null, - currentTenant:null + loginUser: null, + currentTenant: null, }, reducers: { setGlobalVariable: (state, action) => { const { key, value } = action.payload; state[key] = value; }, - setLoginUserPermmisions: ( state, action ) => - { - state.loginUser = action.payload + setLoginUserPermmisions: (state, action) => { + state.loginUser = action.payload; + }, + setCurrentTenant: (state, action) => { + state.currentTenant = action.payload; }, - setCurrentTenant:(state,action)=>{ - state.currentTenant = action.payload - } }, }); -export const { setGlobalVariable,setLoginUserPermmisions,setCurrentTenant } = globalVariablesSlice.actions; +export const { setGlobalVariable, setLoginUserPermmisions, setCurrentTenant } = + globalVariablesSlice.actions; export default globalVariablesSlice.reducer; diff --git a/src/utils/axiosClient.jsx b/src/utils/axiosClient.jsx index 9c4bd3ae..62e9b3bb 100644 --- a/src/utils/axiosClient.jsx +++ b/src/utils/axiosClient.jsx @@ -4,7 +4,7 @@ import axiosRetry from "axios-retry"; import showToast from "../services/toastService"; import { startSignalR, stopSignalR } from "../services/signalRService"; import { BASE_URL } from "./constants"; -const base_Url = BASE_URL +const base_Url = BASE_URL; export const axiosClient = axios.create({ baseURL: base_Url, @@ -44,7 +44,10 @@ axiosClient.interceptors.response.use( const originalRequest = error.config; // Skip retry for public requests or already retried ones - if (!originalRequest && originalRequest._retry || originalRequest.authRequired === false) { + if ( + (!originalRequest && originalRequest._retry) || + originalRequest.authRequired === false + ) { return Promise.reject(error); } @@ -53,7 +56,10 @@ axiosClient.interceptors.response.use( originalRequest._toastShown = true; if (error.code === "ERR_CONNECTION_REFUSED") { - showToast("Unable to connect to the server. Please try again later.", "error"); + showToast( + "Unable to connect to the server. Please try again later.", + "error" + ); } else if (error.code === "ERR_NETWORK") { showToast("Network error. Please check your connection.", "error"); redirectToLogin(); @@ -68,7 +74,10 @@ axiosClient.interceptors.response.use( const refreshToken = localStorage.getItem("refreshToken"); - if (!refreshToken || error.response.data?.errors === "Invalid or expired refresh token.") { + if ( + !refreshToken || + error.response.data?.errors === "Invalid or expired refresh token." + ) { redirectToLogin(); return Promise.reject(error); } @@ -88,7 +97,7 @@ axiosClient.interceptors.response.use( localStorage.setItem("jwtToken", token); localStorage.setItem("refreshToken", newRefreshToken); - startSignalR() + startSignalR(); // Set Authorization header originalRequest.headers["Authorization"] = `Bearer ${token}`; return axiosClient(originalRequest); @@ -160,4 +169,4 @@ export const api = { // Redirect helper function redirectToLogin() { window.location.href = "/auth/login"; -} \ No newline at end of file +}