Merge branch 'organization_management' of https://git.marcoaiot.com/admin/marco.pms.web into Organization_Management

This commit is contained in:
Kartik Sharma 2025-09-21 16:42:26 +05:30
commit 7e6020e3db
8 changed files with 124 additions and 63 deletions

39
src/hooks/useAuth.jsx Normal file
View 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.
})
}

View File

@ -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">

View File

@ -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 }`
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 }` ),
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;

View File

@ -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;

View File

@ -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(

View File

@ -4,22 +4,22 @@ const globalVariablesSlice = createSlice({
name: "globalVariables",
initialState: {
loginUser: null,
currentTenant: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
}
state.currentTenant = action.payload;
},
},
});
export const { setGlobalVariable,setLoginUserPermmisions,setCurrentTenant } = globalVariablesSlice.actions;
export const { setGlobalVariable, setLoginUserPermmisions, setCurrentTenant } =
globalVariablesSlice.actions;
export default globalVariablesSlice.reducer;

View File

@ -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);