@@ -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/pages/authentication/TenantSelection.jsx b/src/pages/authentication/TenantSelection.jsx
new file mode 100644
index 00000000..04223282
--- /dev/null
+++ b/src/pages/authentication/TenantSelection.jsx
@@ -0,0 +1,61 @@
+import React from "react";
+
+const TenantSelection = () => {
+ return (
+
+ {/* Header */}
+
+
Welcome Pramod
+
+ Please select which dashboard you want to explore!!!
+
+
+
+ {/* Card Section */}
+
+
+
+ {/* Image */}
+
+
+
+
+ {/* Content */}
+
+
+ Marco Secure Solution Pvt Limited
+
+
+
+
+
Industry:
+
Information Technology (IT) Service
+
+
+
+ Pune has emerged as a major IT hub in India, home to a variety
+ of global technology companies providing cutting-edge
+ solutions. In this blog, we cover the top 50 IT companies in
+ Pune, highlighting their addresses, key services, and
+ specialties. Whether you’re looking for career opportunities,
+ partnerships, or business solutions,
+
+
+
+ Go To Dashboard
+
+
+
+
+
+
+
+ );
+};
+
+export default TenantSelection;
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..423e57dd 100644
--- a/src/router/AppRoutes.jsx
+++ b/src/router/AppRoutes.jsx
@@ -49,9 +49,9 @@ 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";
+import TenantSelection from "../pages/authentication/TenantSelection";
const router = createBrowserRouter(
[
{
@@ -62,6 +62,7 @@ const router = createBrowserRouter(
element:
,
children: [
{ path: "/auth/login", element:
},
+ { path: "/auth/user", element:
},
{ path: "/auth/login-otp", element:
},
{ path: "/auth/reqest/demo", element:
},
{ path: "/auth/forgot-password", element:
},
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/services/tenantService.js b/src/services/tenantService.js
new file mode 100644
index 00000000..5d08b577
--- /dev/null
+++ b/src/services/tenantService.js
@@ -0,0 +1,30 @@
+// Save org-level token
+export const setOrgToken = (token, refreshToken) => {
+ localStorage.setItem("orgJwtToken", token);
+ localStorage.setItem("orgRefreshToken", refreshToken);
+};
+
+// Save selected tenant token
+export const setTenantToken = (token, refreshToken, tenantId) => {
+ localStorage.setItem("jwtToken", token); // tenant JWT
+ localStorage.setItem("refreshToken", refreshToken);
+ localStorage.setItem("tenantId", tenantId);
+};
+
+// Get tenant token
+export const getTenantToken = () => {
+ return {
+ token: localStorage.getItem("jwtToken"),
+ refreshToken: localStorage.getItem("refreshToken"),
+ tenantId: localStorage.getItem("tenantId"),
+ };
+};
+
+// Clear all tokens
+export const clearAllTokens = () => {
+ localStorage.removeItem("jwtToken");
+ localStorage.removeItem("refreshToken");
+ localStorage.removeItem("orgJwtToken");
+ localStorage.removeItem("orgRefreshToken");
+ localStorage.removeItem("tenantId");
+};
diff --git a/src/slices/globalVariablesSlice.jsx b/src/slices/globalVariablesSlice.jsx
index 7bdadd51..70e6a479 100644
--- a/src/slices/globalVariablesSlice.jsx
+++ b/src/slices/globalVariablesSlice.jsx
@@ -3,23 +3,32 @@ import { createSlice } from "@reduxjs/toolkit";
const globalVariablesSlice = createSlice({
name: "globalVariables",
initialState: {
- loginUser:null,
- currentTenant:null
+ loginUser: null,
+ tenantList: [],
+ currentTenant: null,
+ permissions: [],
},
reducers: {
setGlobalVariable: (state, action) => {
const { key, value } = action.payload;
state[key] = value;
},
- setLoginUserPermmisions: ( state, action ) =>
- {
- state.loginUser = action.payload
+ setLoginUserPermmisions: (state, action) => {
+ state.permissions = action.payload;
+ },
+ setLoginUserTenants: (state, action) => {
+ state.tenantList = 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,
+ setLoginUserTenants,
+} = globalVariablesSlice.actions;
export default globalVariablesSlice.reducer;
diff --git a/src/utils/axiosClient.jsx b/src/utils/axiosClient.jsx
index 9c4bd3ae..427b596a 100644
--- a/src/utils/axiosClient.jsx
+++ b/src/utils/axiosClient.jsx
@@ -1,10 +1,10 @@
import axios from "axios";
-import { useNavigate } from "react-router-dom";
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,
@@ -14,16 +14,15 @@ export const axiosClient = axios.create({
},
});
-// Auto retry failed requests (e.g., network issues)
-axiosRetry(axiosClient, { retries: 3 });
-
-// Request Interceptor — Add Bearer token if required
+// Retry failed requests
axiosClient.interceptors.request.use(
async (config) => {
- const requiresAuth = config.authRequired !== false; // default to true
+ const requiresAuth = config.authRequired !== false;
if (requiresAuth) {
- const token = localStorage.getItem("jwtToken");
+ let token = localStorage.getItem("jwtToken"); // tenant token by default
+ if (config.orgToken) token = localStorage.getItem("orgJwtToken"); // org token if requested
+
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
config._retry = true;
@@ -37,23 +36,27 @@ axiosClient.interceptors.request.use(
(error) => Promise.reject(error)
);
-// 🔄 Response Interceptor — Handle 401, refresh token, etc.
+// Response interceptor
axiosClient.interceptors.response.use(
(response) => response,
async (error) => {
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);
}
- // Avoid showing multiple toasts
if (!originalRequest._toastShown) {
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();
@@ -65,31 +68,25 @@ axiosClient.interceptors.response.use(
if (status === 401 && !isRefreshRequest) {
originalRequest._retry = true;
-
const refreshToken = localStorage.getItem("refreshToken");
- if (!refreshToken || error.response.data?.errors === "Invalid or expired refresh token.") {
+ if (!refreshToken) {
redirectToLogin();
return Promise.reject(error);
}
stopSignalR();
-
try {
- // Refresh token call
const res = await axiosClient.post("/api/Auth/refresh-token", {
token: localStorage.getItem("jwtToken"),
refreshToken,
});
const { token, refreshToken: newRefreshToken } = res.data.data;
-
- // Save updated tokens
localStorage.setItem("jwtToken", token);
localStorage.setItem("refreshToken", newRefreshToken);
- startSignalR()
- // Set Authorization header
+ startSignalR();
originalRequest.headers["Authorization"] = `Bearer ${token}`;
return axiosClient(originalRequest);
} catch (refreshError) {
@@ -124,40 +121,41 @@ const apiRequest = async (method, url, data = {}, config = {}) => {
// Exported API wrapper
export const api = {
- // Public routes (no token required)
postPublic: (url, data = {}, customHeaders = {}) =>
apiRequest("post", url, data, {
headers: { ...customHeaders },
authRequired: false,
}),
- // Authenticated routes
- get: (url, params = {}, customHeaders = {}) =>
+ get: (url, params = {}, customHeaders = {}, orgToken = false) =>
apiRequest("get", url, params, {
headers: { ...customHeaders },
authRequired: true,
+ orgToken,
}),
- post: (url, data = {}, customHeaders = {}) =>
+ post: (url, data = {}, customHeaders = {}, orgToken = false) =>
apiRequest("post", url, data, {
headers: { ...customHeaders },
authRequired: true,
+ orgToken,
}),
- put: (url, data = {}, customHeaders = {}) =>
+ put: (url, data = {}, customHeaders = {}, orgToken = false) =>
apiRequest("put", url, data, {
headers: { ...customHeaders },
authRequired: true,
+ orgToken,
}),
- delete: (url, data = {}, customHeaders = {}) =>
+ delete: (url, data = {}, customHeaders = {}, orgToken = false) =>
apiRequest("delete", url, data, {
headers: { ...customHeaders },
authRequired: true,
+ orgToken,
}),
};
-// Redirect helper
function redirectToLogin() {
window.location.href = "/auth/login";
-}
\ No newline at end of file
+}