Compare commits

..

No commits in common. "3e5afe0bc60126b153481797bdcfc096036374ba" and "a1a935b0d5be911147f25a446d78ef4ed82ed4f1" have entirely different histories.

8 changed files with 63 additions and 124 deletions

View File

@ -1,39 +0,0 @@
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.
})
}

View File

@ -6,7 +6,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import { AuthWrapper } from "./AuthWrapper"; import { AuthWrapper } from "./AuthWrapper";
import { setOrgToken } from "../../services/tenantService";
const LoginPage = () => { const LoginPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -16,13 +16,13 @@ const LoginPage = () => {
const loginSchema = IsLoginWithOTP const loginSchema = IsLoginWithOTP
? z.object({ ? z.object({
username: z.string().trim().email({ message: "Valid email required" }), username: z.string().trim().email({ message: "Valid email required" }),
}) })
: z.object({ : z.object({
username: z.string().trim().email({ message: "Valid email required" }), username: z.string().trim().email({ message: "Valid email required" }),
password: z.string().trim().min(1, { message: "Password required" }), password: z.string().trim().min(1, { message: "Password required" }),
rememberMe: z.boolean(), rememberMe: z.boolean(),
}); });
const { const {
register, register,
@ -41,11 +41,10 @@ const LoginPage = () => {
password: data.password, password: data.password,
}; };
const response = await AuthRepository.login(userCredential); const response = await AuthRepository.login(userCredential);
// localStorage.setItem("jwtToken", response.data.token); localStorage.setItem("jwtToken", response.data.token);
// localStorage.setItem("refreshToken", response.data.refreshToken); localStorage.setItem("refreshToken", response.data.refreshToken);
setLoading(false); setLoading(false);
setOrgToken(response.data.token, response.data.refreshToken); navigate("/dashboard");
navigate("/auth/user");
} else { } else {
await AuthRepository.sendOTP({ email: data.username }); await AuthRepository.sendOTP({ email: data.username });
showToast("OTP has been sent to your email.", "success"); showToast("OTP has been sent to your email.", "success");
@ -147,9 +146,8 @@ const LoginPage = () => {
type={hidepass ? "password" : "text"} type={hidepass ? "password" : "text"}
autoComplete="new-password" autoComplete="new-password"
id="password" id="password"
className={`form-control form-control-xl shadow-none ${ className={`form-control form-control-xl shadow-none ${errors.password ? "is-invalid" : ""
errors.password ? "is-invalid" : "" }`}
}`}
name="password" name="password"
{...register("password")} {...register("password")}
placeholder="••••••••••••" placeholder="••••••••••••"
@ -172,15 +170,13 @@ const LoginPage = () => {
{/* ✅ Error message */} {/* ✅ Error message */}
{errors.password && ( {errors.password && (
<div <div className="invalid-feedback text-start" style={{ fontSize: "12px" }}>
className="invalid-feedback text-start"
style={{ fontSize: "12px" }}
>
{errors.password.message} {errors.password.message}
</div> </div>
)} )}
</div> </div>
{/* Remember Me + Forgot Password */} {/* Remember Me + Forgot Password */}
<div className="mb-3 d-flex justify-content-between align-items-center"> <div className="mb-3 d-flex justify-content-between align-items-center">
<div className="form-check"> <div className="form-check">
@ -213,8 +209,8 @@ const LoginPage = () => {
{loading {loading
? "Please Wait..." ? "Please Wait..."
: IsLoginWithOTP : IsLoginWithOTP
? "Send OTP" ? "Send OTP"
: "Sign In"} : "Sign In"}
</button> </button>
{/* Login With OTP Button */} {/* Login With OTP Button */}

View File

@ -1,10 +1,13 @@
import { api } from "../utils/axiosClient"; import { api } from "../utils/axiosClient";
const AttendanceRepository = {
markAttendance: (data) => api.post("/api/attendance/record", data), const AttendanceRepository = {
getAttendance: (id) => api.get(`api/attendance/project/team?projectId=${id}`), markAttendance:(data)=>api.post("/api/attendance/record",data),
getAttendanceFilteredByDate: (projectId, fromDate, toDate) => { getAttendance:(id)=>api.get(`api/attendance/project/team?projectId=${id}`),
let url = `api/Attendance/project/log?projectId=${projectId}`; getAttendanceFilteredByDate: ( projectId, fromDate, toDate ) =>
{
let url = `api/Attendance/project/log?projectId=${ projectId }`
if (fromDate) { if (fromDate) {
url += `&dateFrom=${fromDate}`; url += `&dateFrom=${fromDate}`;
} }
@ -12,24 +15,27 @@ const AttendanceRepository = {
if (toDate) { if (toDate) {
url += `&dateTo=${toDate}`; url += `&dateTo=${toDate}`;
} }
return api.get(url); return api.get(url)
}, },
getAttendanceLogs: (id) => api.get(`api/attendance/log/attendance/${id}`), getAttendanceLogs: ( id ) => api.get( `api/attendance/log/attendance/${ id }` ),
getRegularizeList: (id) => getRegularizeList: ( id ) => api.get( `api/attendance/regularize?projectId=${ id }` ),
api.get(`api/attendance/regularize?projectId=${id}`),
getAttendanceByEmployee: (employeeId, fromDate, toDate) => { getAttendanceByEmployee: ( employeeId, fromDate, toDate ) =>
let url = `api/Attendance/log/employee/${employeeId}?`; {
if (fromDate) {
url += `&dateFrom=${fromDate}`;
}
if (toDate) { let url = `api/Attendance/log/employee/${ employeeId }?`
url += `&dateTo=${toDate}`; if (fromDate) {
} url += `&dateFrom=${fromDate}`;
return api.get(url); }
},
}; if (toDate) {
url += `&dateTo=${toDate}`;
}
return api.get(url)
},
}
export default AttendanceRepository; export default AttendanceRepository;

View File

@ -16,22 +16,6 @@ const AuthRepository = {
profile: () => api.get("/api/user/profile"), profile: () => api.get("/api/user/profile"),
changepassword: (data) => api.post("/api/auth/change-password", data), changepassword: (data) => api.post("/api/auth/change-password", data),
appmenu: () => api.get("/api/appmenu/get/menu"), 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; export default AuthRepository;

View File

@ -49,6 +49,7 @@ import MainResetPasswordPage from "../pages/authentication/MainResetPasswordPage
import TenantPage from "../pages/Tenant/TenantPage"; import TenantPage from "../pages/Tenant/TenantPage";
import { Navigate } from "react-router-dom"; import { Navigate } from "react-router-dom";
import CreateTenant from "../pages/Tenant/CreateTenant"; import CreateTenant from "../pages/Tenant/CreateTenant";
;
import OrganizationPage from "../pages/Organization/OrganizationPage"; import OrganizationPage from "../pages/Organization/OrganizationPage";
import LandingPage from "../pages/Home/LandingPage"; import LandingPage from "../pages/Home/LandingPage";
const router = createBrowserRouter( const router = createBrowserRouter(

View File

@ -66,7 +66,7 @@ const attemptTokenRefresh = async (storedRefreshToken) => {
localStorage.setItem("jwtToken", response.data.token); localStorage.setItem("jwtToken", response.data.token);
localStorage.setItem("refreshToken", response.data.refreshToken); localStorage.setItem("refreshToken", response.data.refreshToken);
return true; return true;
// api // api
// .post("/api/auth/refresh-token", { // .post("/api/auth/refresh-token", {
// token: localStorage.getItem("jwtToken"), // token: localStorage.getItem("jwtToken"),
// refreshToken: refreshToken, // refreshToken: refreshToken,

View File

@ -3,23 +3,23 @@ import { createSlice } from "@reduxjs/toolkit";
const globalVariablesSlice = createSlice({ const globalVariablesSlice = createSlice({
name: "globalVariables", name: "globalVariables",
initialState: { initialState: {
loginUser: null, loginUser:null,
currentTenant: null, currentTenant:null
}, },
reducers: { reducers: {
setGlobalVariable: (state, action) => { setGlobalVariable: (state, action) => {
const { key, value } = action.payload; const { key, value } = action.payload;
state[key] = value; state[key] = value;
}, },
setLoginUserPermmisions: (state, action) => { setLoginUserPermmisions: ( state, action ) =>
state.loginUser = action.payload; {
}, state.loginUser = action.payload
setCurrentTenant: (state, action) => {
state.currentTenant = action.payload;
}, },
setCurrentTenant:(state,action)=>{
state.currentTenant = action.payload
}
}, },
}); });
export const { setGlobalVariable, setLoginUserPermmisions, setCurrentTenant } = export const { setGlobalVariable,setLoginUserPermmisions,setCurrentTenant } = globalVariablesSlice.actions;
globalVariablesSlice.actions;
export default globalVariablesSlice.reducer; export default globalVariablesSlice.reducer;

View File

@ -4,7 +4,7 @@ import axiosRetry from "axios-retry";
import showToast from "../services/toastService"; import showToast from "../services/toastService";
import { startSignalR, stopSignalR } from "../services/signalRService"; import { startSignalR, stopSignalR } from "../services/signalRService";
import { BASE_URL } from "./constants"; import { BASE_URL } from "./constants";
const base_Url = BASE_URL; const base_Url = BASE_URL
export const axiosClient = axios.create({ export const axiosClient = axios.create({
baseURL: base_Url, baseURL: base_Url,
@ -44,10 +44,7 @@ axiosClient.interceptors.response.use(
const originalRequest = error.config; const originalRequest = error.config;
// Skip retry for public requests or already retried ones // Skip retry for public requests or already retried ones
if ( if (!originalRequest && originalRequest._retry || originalRequest.authRequired === false) {
(!originalRequest && originalRequest._retry) ||
originalRequest.authRequired === false
) {
return Promise.reject(error); return Promise.reject(error);
} }
@ -56,10 +53,7 @@ axiosClient.interceptors.response.use(
originalRequest._toastShown = true; originalRequest._toastShown = true;
if (error.code === "ERR_CONNECTION_REFUSED") { if (error.code === "ERR_CONNECTION_REFUSED") {
showToast( showToast("Unable to connect to the server. Please try again later.", "error");
"Unable to connect to the server. Please try again later.",
"error"
);
} else if (error.code === "ERR_NETWORK") { } else if (error.code === "ERR_NETWORK") {
showToast("Network error. Please check your connection.", "error"); showToast("Network error. Please check your connection.", "error");
redirectToLogin(); redirectToLogin();
@ -74,10 +68,7 @@ axiosClient.interceptors.response.use(
const refreshToken = localStorage.getItem("refreshToken"); const refreshToken = localStorage.getItem("refreshToken");
if ( if (!refreshToken || error.response.data?.errors === "Invalid or expired refresh token.") {
!refreshToken ||
error.response.data?.errors === "Invalid or expired refresh token."
) {
redirectToLogin(); redirectToLogin();
return Promise.reject(error); return Promise.reject(error);
} }
@ -97,7 +88,7 @@ axiosClient.interceptors.response.use(
localStorage.setItem("jwtToken", token); localStorage.setItem("jwtToken", token);
localStorage.setItem("refreshToken", newRefreshToken); localStorage.setItem("refreshToken", newRefreshToken);
startSignalR(); startSignalR()
// Set Authorization header // Set Authorization header
originalRequest.headers["Authorization"] = `Bearer ${token}`; originalRequest.headers["Authorization"] = `Bearer ${token}`;
return axiosClient(originalRequest); return axiosClient(originalRequest);