Merge branch 'organization_management' of https://git.marcoaiot.com/admin/marco.pms.web into Organization_Management
This commit is contained in:
commit
7e6020e3db
39
src/hooks/useAuth.jsx
Normal file
39
src/hooks/useAuth.jsx
Normal file
@ -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.
|
||||
})
|
||||
}
|
@ -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);
|
||||
@ -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,7 +147,8 @@ 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")}
|
||||
@ -170,13 +172,15 @@ const LoginPage = () => {
|
||||
|
||||
{/* ✅ Error message */}
|
||||
{errors.password && (
|
||||
<div className="invalid-feedback text-start" style={{ fontSize: "12px" }}>
|
||||
<div
|
||||
className="invalid-feedback text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.password.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
{/* Remember Me + Forgot Password */}
|
||||
<div className="mb-3 d-flex justify-content-between align-items-center">
|
||||
<div className="form-check">
|
||||
|
@ -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 }`
|
||||
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,16 +12,15 @@ 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 }` ),
|
||||
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 }?`
|
||||
getAttendanceByEmployee: (employeeId, fromDate, toDate) => {
|
||||
let url = `api/Attendance/log/employee/${employeeId}?`;
|
||||
if (fromDate) {
|
||||
url += `&dateFrom=${fromDate}`;
|
||||
}
|
||||
@ -32,10 +28,8 @@ const AttendanceRepository = {
|
||||
if (toDate) {
|
||||
url += `&dateTo=${toDate}`;
|
||||
}
|
||||
return api.get(url)
|
||||
return api.get(url);
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export default AttendanceRepository;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user