handle one tenant have , directly move to dashboard once logged

This commit is contained in:
Kartik Sharma 2025-09-22 00:09:34 +05:30
parent b9b3788dda
commit 4afe43d116
16 changed files with 387 additions and 313 deletions

View File

@ -1,21 +1,16 @@
import React, { useState, useEffect } from "react";
import LineChart from "../Charts/LineChart";
import React, { useState, useMemo } from "react";
import ApexChart from "../Charts/Circle";
import { useProjects } from "../../hooks/useProjects";
import { useDashboard_AttendanceData } from "../../hooks/useDashboard_Data";
import ApexChart from "../Charts/Circle";
const LOCAL_STORAGE_PROJECT_KEY = "selectedAttendanceProjectId";
import { useSelectedProject } from "../../hooks/useSelectedProject"; // your custom hook
const Attendance = () => {
const { projects } = useProjects();
const today = new Date().toISOString().split("T")[0]; // Format: YYYY-MM-DD
const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
const [selectedDate, setSelectedDate] = useState(today);
const storedProjectId = localStorage.getItem(LOCAL_STORAGE_PROJECT_KEY);
const initialProjectId = storedProjectId || "all";
const [selectedProjectId, setSelectedProjectId] = useState(initialProjectId);
const [displayedProjectName, setDisplayedProjectName] =
useState("Select Project");
const [activeTab, setActiveTab] = useState("Summary");
// central project selection hook
const selectedProjectId = useSelectedProject()
const {
dashboard_Attendancedata: AttendanceData,
@ -23,38 +18,24 @@ const Attendance = () => {
error: isError,
} = useDashboard_AttendanceData(selectedDate, selectedProjectId);
useEffect(() => {
if (selectedProjectId === "all") {
setDisplayedProjectName("All Projects");
} else if (projects) {
const foundProject = projects.find((p) => p.id === selectedProjectId);
setDisplayedProjectName(
foundProject ? foundProject.name : "Select Project"
);
} else {
setDisplayedProjectName("Select Project");
}
localStorage.setItem(LOCAL_STORAGE_PROJECT_KEY, selectedProjectId);
// project name derived once
const displayedProjectName = useMemo(() => {
if (selectedProjectId === "all") return "All Projects";
const found = projects?.find((p) => p.id === selectedProjectId);
return found?.name || "Select Project";
}, [selectedProjectId, projects]);
const handleProjectSelect = (projectId) => {
setSelectedProjectId(projectId);
};
const handleDateChange = (e) => {
setSelectedDate(e.target.value);
};
return (
<div className="card h-100">
<div className="card-header mb-1 pb-0 ">
<div className="d-flex flex-wrap justify-content-between align-items-center mb-0 pb-0 ">
{/* Header */}
<div className="card-header mb-1 pb-0">
<div className="d-flex flex-wrap justify-content-between align-items-center">
<div className="card-title mb-0 text-start">
<h5 className="mb-1">Attendance</h5>
<p className="card-subtitle">Daily Attendance Data</p>
</div>
{/* Project Dropdown */}
<div className="btn-group">
<button
className="btn btn-outline-primary btn-sm dropdown-toggle"
@ -68,7 +49,7 @@ const Attendance = () => {
<li>
<button
className="dropdown-item"
onClick={() => handleProjectSelect("all")}
onClick={() => setSelectedProjectId("all")}
>
All Projects
</button>
@ -77,7 +58,7 @@ const Attendance = () => {
<li key={project.id}>
<button
className="dropdown-item"
onClick={() => handleProjectSelect(project.id)}
onClick={() => setSelectedProjectId(project.id)}
>
{project.name}
</button>
@ -88,52 +69,43 @@ const Attendance = () => {
</div>
</div>
<div className="d-flex flex-wrap justify-content-between align-items-center mb-0 mt-0 me-5 ms-5">
{/* Tabs */}
<div>
<ul className="nav nav-tabs " role="tablist">
<li className="nav-item">
<button
type="button"
className={`nav-link ${
activeTab === "Summary" ? "active" : ""
}`}
onClick={() => setActiveTab("Summary")}
data-bs-toggle="tab"
>
Summary
</button>
</li>
<li className="nav-item">
<button
type="button"
className={`nav-link ${
activeTab === "Details" ? "active" : ""
}`}
onClick={() => setActiveTab("Details")}
data-bs-toggle="tab"
>
Details
</button>
</li>
</ul>
</div>
{/* ✅ Date Picker Aligned Left with Padding */}
<div className="ps-6 mb-3 mt-0">
<div style={{ width: "120px" }}>
<input
type="date"
className="form-control p-1"
// style={{ fontSize: "1rem" }}
value={selectedDate}
onChange={handleDateChange}
/>
</div>
{/* Tabs + Date Picker */}
<div className="d-flex flex-wrap justify-content-between align-items-center me-5 ms-5">
<ul className="nav nav-tabs">
<li className="nav-item">
<button
type="button"
className={`nav-link ${AttendanceData?.activeTab === "Summary" ? "active" : ""}`}
onClick={() => (AttendanceData.activeTab = "Summary")}
>
Summary
</button>
</li>
<li className="nav-item">
<button
type="button"
className={`nav-link ${AttendanceData?.activeTab === "Details" ? "active" : ""}`}
onClick={() => (AttendanceData.activeTab = "Details")}
>
Details
</button>
</li>
</ul>
<div className="ps-6 mb-3">
<input
type="date"
className="form-control p-1"
style={{ width: "120px" }}
value={selectedDate}
onChange={(e) => setSelectedDate(e.target.value)}
/>
</div>
</div>
{/* Body */}
<div className="card-body">
{activeTab === "Summary" && (
{/* Summary */}
{AttendanceData?.activeTab === "Summary" && (
<div className="row justify-content-center">
<div className="col-12 col-md-6 d-flex flex-column align-items-center text-center mb-4">
{isLoading ? (
@ -143,7 +115,7 @@ const Attendance = () => {
) : (
AttendanceData && (
<>
<h5 className="fw-bold mb-0 text-center w-100">
<h5 className="fw-bold mb-0">
<i className="bx bx-task text-info"></i> Attendance
</h5>
<h4 className="mb-0 fw-bold">
@ -164,11 +136,9 @@ const Attendance = () => {
</div>
)}
{activeTab === "Details" && (
<div
className="table-responsive"
style={{ maxHeight: "300px", overflowY: "auto" }}
>
{/* Details */}
{AttendanceData?.activeTab === "Details" && (
<div className="table-responsive" style={{ maxHeight: "300px" }}>
<table className="table table-hover mb-0 text-start">
<thead>
<tr>
@ -178,32 +148,17 @@ const Attendance = () => {
</tr>
</thead>
<tbody>
{AttendanceData?.attendanceTable &&
AttendanceData.attendanceTable.length > 0 ? (
AttendanceData.attendanceTable.map((record, index) => (
<tr key={index}>
<td>
{record.firstName} {record.lastName}
</td>
<td>
{new Date(record.inTime).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
</td>
<td>
{new Date(record.outTime).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
</td>
{AttendanceData?.attendanceTable?.length ? (
AttendanceData.attendanceTable.map((r, i) => (
<tr key={i}>
<td>{r.firstName} {r.lastName}</td>
<td>{r.inTime ? new Date(r.inTime).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "-"}</td>
<td>{r.outTime ? new Date(r.outTime).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "-"}</td>
</tr>
))
) : (
<tr>
<td colSpan="3" className="text-center">
No attendance data available
</td>
<td colSpan="3" className="text-center">No attendance data available</td>
</tr>
)}
</tbody>

View File

@ -132,7 +132,7 @@ const AttendanceOverview = () => {
onClick={() => setView("table")}
title="Table View"
>
<i class="bx bx-list-ul fs-5"></i>
<i className="bx bx-list-ul fs-5"></i>
</button>
</div>
</div>

View File

@ -1,33 +1,28 @@
import React, { useCallback, useEffect, useState } from "react";
import React, { useEffect } from "react";
import { useDashboardProjectsCardData } from "../../hooks/useDashboard_Data";
import eventBus from "../../services/eventBus";
import GlobalRepository from "../../repositories/GlobalRepository";
const Projects = () => {
const { projectsCardData } = useDashboardProjectsCardData();
const [projectData, setProjectsData] = useState(projectsCardData);
const {
data: projectsCardData,
isLoading,
isError,
error,
refetch,
} = useDashboardProjectsCardData();
useEffect(() => {
setProjectsData(projectsCardData);
}, [projectsCardData]);
// When "project" event happens, just refetch
const handler = () => {
refetch();
};
const handler = useCallback(
async (msg) => {
try {
const response =
await GlobalRepository.getDashboardProjectsCardData();
setProjectsData(response.data);
} catch (err) {
console.error(err);
}
},
[GlobalRepository]
);
useEffect(() => {
eventBus.on("project", handler);
return () => eventBus.off("project", handler);
}, [handler]);
}, [refetch]);
const totalProjects = projectsCardData?.totalProjects ?? 0;
const ongoingProjects = projectsCardData?.ongoingProjects ?? 0;
return (
<div className="card p-3 h-100 text-center d-flex justify-content-between">
@ -37,20 +32,29 @@ const Projects = () => {
Projects
</h5>
</div>
<div className="d-flex justify-content-around align-items-start mt-n2">
<div>
<h4 className="mb-0 fw-bold">
{projectData.totalProjects?.toLocaleString()}
</h4>
<small className="text-muted">Total</small>
{isLoading ? (
<div className="d-flex justify-content-center align-items-center flex-grow-1">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
<div>
<h4 className="mb-0 fw-bold">
{projectData.ongoingProjects?.toLocaleString()}
</h4>
<small className="text-muted">Ongoing</small>
) : isError ? (
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">
{error?.message || "Error loading data"}
</div>
</div>
) : (
<div className="d-flex justify-content-around align-items-start mt-n2">
<div>
<h4 className="mb-0 fw-bold">{totalProjects.toLocaleString()}</h4>
<small className="text-muted">Total</small>
</div>
<div>
<h4 className="mb-0 fw-bold">{ongoingProjects.toLocaleString()}</h4>
<small className="text-muted">Ongoing</small>
</div>
</div>
)}
</div>
);
};

View File

@ -1,10 +1,16 @@
import React from "react";
import { useSelector } from "react-redux";
import { useSelectedProject } from "../../slices/apiDataManager";
import { useDashboardTasksCardData } from "../../hooks/useDashboard_Data";
const TasksCard = () => {
const projectId = useSelector((store) => store.localVariables?.projectId);
const { tasksCardData, loading, error } = useDashboardTasksCardData(projectId);
const projectId = useSelectedProject();
const {
data: tasksCardData,
isLoading,
isError,
error,
} = useDashboardTasksCardData(projectId);
return (
<div className="card p-3 h-100 text-center d-flex justify-content-between">
@ -14,28 +20,30 @@ const TasksCard = () => {
</h5>
</div>
{loading ? (
// Loader will be displayed when loading is true
{isLoading ? (
// Loader while fetching
<div className="d-flex justify-content-center align-items-center flex-grow-1">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
) : error ? (
// Error message if there's an error
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">{error}</div>
) : isError ? (
// Show error
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">
{error?.message || "Error loading data"}
</div>
) : (
// Actual data when loaded successfully
// Show data
<div className="d-flex justify-content-around align-items-start mt-n2">
<div>
<h4 className="mb-0 fw-bold">
{tasksCardData?.totalTasks?.toLocaleString()}
{tasksCardData?.totalTasks?.toLocaleString() ?? 0}
</h4>
<small className="text-muted">Total</small>
</div>
<div>
<h4 className="mb-0 fw-bold">
{tasksCardData?.completedTasks?.toLocaleString()}
{tasksCardData?.completedTasks?.toLocaleString() ?? 0}
</h4>
<small className="text-muted">Completed</small>
</div>
@ -45,4 +53,4 @@ const TasksCard = () => {
);
};
export default TasksCard;
export default TasksCard;

View File

@ -1,33 +1,45 @@
import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect } from "react";
import { useSelector } from "react-redux";
import { useDashboardTeamsCardData } from "../../hooks/useDashboard_Data";
import eventBus from "../../services/eventBus";
import { useQueryClient } from "@tanstack/react-query";
import { useSelectedProject } from "../../slices/apiDataManager";
const Teams = () => {
const projectId = useSelector((store) => store.localVariables?.projectId);
const { teamsCardData, loading, error } = useDashboardTeamsCardData(projectId);
const queryClient = useQueryClient();
const projectId = useSelectedProject()
const [totalEmployees, setTotalEmployee] = useState(0);
const [inToday, setInToday] = useState(0);
// Update state when API data arrives
useEffect(() => {
setTotalEmployee(teamsCardData?.totalEmployees || 0);
setInToday(teamsCardData?.inToday || 0);
}, [teamsCardData]);
const {
data: teamsCardData,
isLoading,
isError,
error,
} = useDashboardTeamsCardData(projectId);
// Handle real-time updates via eventBus
const handler = useCallback((msg) => {
if (msg.activity === 1) {
setInToday((prev) => prev + 1);
}
}, []);
const handler = useCallback(
(msg) => {
if (msg.activity === 1) {
queryClient.setQueryData(["dashboardTeams", projectId], (old) => {
if (!old) return old;
return {
...old,
inToday: (old.inToday || 0) + 1,
};
});
}
},
[queryClient, projectId]
);
useEffect(() => {
eventBus.on("attendance", handler);
return () => eventBus.off("attendance", handler);
}, [handler]);
const inToday = teamsCardData?.inToday ?? 0;
const totalEmployees = teamsCardData?.totalEmployees ?? 0;
return (
<div className="card p-3 h-100 text-center d-flex justify-content-between">
<div className="d-flex justify-content-start align-items-center mb-3">
@ -36,18 +48,17 @@ const Teams = () => {
</h5>
</div>
{loading ? (
// Blue spinner loader
{isLoading ? (
<div className="d-flex justify-content-center align-items-center flex-grow-1">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
) : error ? (
// Error message if data fetching fails
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">{error}</div>
) : isError ? (
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">
{error?.message || "Error loading data"}
</div>
) : (
// Display data once loaded
<div className="d-flex justify-content-around align-items-start mt-n2">
<div>
<h4 className="mb-0 fw-bold">{totalEmployees.toLocaleString()}</h4>
@ -63,4 +74,4 @@ const Teams = () => {
);
};
export default Teams;
export default Teams;

View File

@ -1,5 +1,4 @@
import React from "react";
import { hasUserPermission } from "../../utils/authUtils";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import {
DIRECTORY_ADMIN,

View File

@ -5,12 +5,12 @@ import GlobalModel from "../common/GlobalModel";
import { useTenantContext } from "../../pages/Tenant/TenantPage";
import { useTenantDetailsContext } from "../../pages/Tenant/TenantDetails";
import IconButton from "../common/IconButton";
import { hasUserPermission } from "../../utils/authUtils";
import { MANAGE_TENANTS } from "../../utils/constants";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
const Profile = ({ data }) => {
const {setEditTenant} = useTenantDetailsContext()
const canUpdateTenant = hasUserPermission(MANAGE_TENANTS)
const canUpdateTenant = useHasUserPermission(MANAGE_TENANTS)
return (
<>
<div className="container-fuid">

View File

@ -28,13 +28,18 @@ export const useSelectTenant = (onSuccessCallBack) => {
const res = await AuthRepository.selectTenant(tenantId);
return res.data;
},
onSuccess: (data) => {
localStorage.setItem("ltkn", data.token);
localStorage.setItem("rtkn", data.refreshToken);
localStorage.setItem("jwtToken", data.token);
localStorage.setItem("refreshToken", data.refreshToken);
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(error.message || "Error while creating project", "error");
localStorage.removeItem("jwtToken");
localStorage.removeItem("refreshToken")
localStorage.removeItem("ctnt")
},
});
};

View File

@ -1,7 +1,8 @@
import { useState, useEffect } from "react";
import GlobalRepository from "../repositories/GlobalRepository";
import { useQuery } from "@tanstack/react-query";
// 🔹 Dashboard Progression Data Hook
export const useDashboard_Data = ({ days, FromDate, projectId }) => {
const [dashboard_data, setDashboard_Data] = useState([]);
const [isLineChartLoading, setLoading] = useState(false);
@ -38,120 +39,120 @@ export const useDashboard_Data = ({ days, FromDate, projectId }) => {
};
export const useDashboard_AttendanceData = (date, projectId) => {
const [dashboard_Attendancedata, setDashboard_AttendanceData] = useState([]);
const [isLineChartLoading, setLoading] = useState(false);
const [error, setError] = useState("");
// export const useDashboard_AttendanceData = (date, projectId) => {
// const [dashboard_Attendancedata, setDashboard_AttendanceData] = useState([]);
// const [isLineChartLoading, setLoading] = useState(false);
// const [error, setError] = useState("");
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError("");
// useEffect(() => {
// const fetchData = async () => {
// setLoading(true);
// setError("");
try {
const response = await GlobalRepository.getDashboardAttendanceData(date, projectId); // date in 2nd param
setDashboard_AttendanceData(response.data);
} catch (err) {
setError("Failed to fetch dashboard data.");
console.error(err);
} finally {
setLoading(false);
}
};
// try {
// const response = await GlobalRepository.getDashboardAttendanceData(date, projectId); // date in 2nd param
// setDashboard_AttendanceData(response.data);
// } catch (err) {
// setError("Failed to fetch dashboard data.");
// console.error(err);
// } finally {
// setLoading(false);
// }
// };
if (date && projectId !== null) {
fetchData();
}
}, [date, projectId]);
// if (date && projectId !== null) {
// fetchData();
// }
// }, [date, projectId]);
return { dashboard_Attendancedata, isLineChartLoading: isLineChartLoading, error };
};
// return { dashboard_Attendancedata, isLineChartLoading: isLineChartLoading, error };
// };
// 🔹 Dashboard Projects Card Data Hook
export const useDashboardProjectsCardData = () => {
const [projectsCardData, setProjectsData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
// export const useDashboardProjectsCardData = () => {
// const [projectsCardData, setProjectsData] = useState([]);
// const [loading, setLoading] = useState(false);
// const [error, setError] = useState("");
useEffect(() => {
const fetchProjectsData = async () => {
setLoading(true);
setError("");
// useEffect(() => {
// const fetchProjectsData = async () => {
// setLoading(true);
// setError("");
try {
const response = await GlobalRepository.getDashboardProjectsCardData();
setProjectsData(response.data);
} catch (err) {
setError("Failed to fetch projects card data.");
console.error(err);
} finally {
setLoading(false);
}
};
// try {
// const response = await GlobalRepository.getDashboardProjectsCardData();
// setProjectsData(response.data);
// } catch (err) {
// setError("Failed to fetch projects card data.");
// console.error(err);
// } finally {
// setLoading(false);
// }
// };
fetchProjectsData();
}, []);
// fetchProjectsData();
// }, []);
return { projectsCardData, loading, error };
};
// return { projectsCardData, loading, error };
// };
// 🔹 Dashboard Teams Card Data Hook
export const useDashboardTeamsCardData = (projectId) => {
const [teamsCardData, setTeamsData] = useState({});
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
// export const useDashboardTeamsCardData = (projectId) => {
// const [teamsCardData, setTeamsData] = useState({});
// const [loading, setLoading] = useState(false);
// const [error, setError] = useState("");
useEffect(() => {
const fetchTeamsData = async () => {
setLoading(true);
setError("");
// useEffect(() => {
// const fetchTeamsData = async () => {
// setLoading(true);
// setError("");
try {
const response = await GlobalRepository.getDashboardTeamsCardData(projectId);
setTeamsData(response.data || {});
} catch (err) {
setError("Failed to fetch teams card data.");
console.error("Error fetching teams card data:", err);
setTeamsData({});
} finally {
setLoading(false);
}
};
// try {
// const response = await GlobalRepository.getDashboardTeamsCardData(projectId);
// setTeamsData(response.data || {});
// } catch (err) {
// setError("Failed to fetch teams card data.");
// console.error("Error fetching teams card data:", err);
// setTeamsData({});
// } finally {
// setLoading(false);
// }
// };
fetchTeamsData();
}, [projectId]);
// fetchTeamsData();
// }, [projectId]);
return { teamsCardData, loading, error };
};
// return { teamsCardData, loading, error };
// };
export const useDashboardTasksCardData = (projectId) => {
const [tasksCardData, setTasksData] = useState({});
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
// export const useDashboardTasksCardData = (projectId) => {
// const [tasksCardData, setTasksData] = useState({});
// const [loading, setLoading] = useState(false);
// const [error, setError] = useState("");
useEffect(() => {
const fetchTasksData = async () => {
setLoading(true);
setError("");
// useEffect(() => {
// const fetchTasksData = async () => {
// setLoading(true);
// setError("");
try {
const response = await GlobalRepository.getDashboardTasksCardData(projectId);
setTasksData(response.data);
} catch (err) {
setError("Failed to fetch tasks card data.");
console.error(err);
setTasksData({});
} finally {
setLoading(false);
}
};
// try {
// const response = await GlobalRepository.getDashboardTasksCardData(projectId);
// setTasksData(response.data);
// } catch (err) {
// setError("Failed to fetch tasks card data.");
// console.error(err);
// setTasksData({});
// } finally {
// setLoading(false);
// }
// };
fetchTasksData();
}, [projectId]);
// fetchTasksData();
// }, [projectId]);
return { tasksCardData, loading, error };
};
// return { tasksCardData, loading, error };
// };
export const useAttendanceOverviewData = (projectId, days) => {
@ -180,3 +181,75 @@ export const useAttendanceOverviewData = (projectId, days) => {
return { attendanceOverviewData, loading, error };
};
// -------------------Query----------------------------
// export const useDashboard_Data = (days, FromDate, projectId)=>{
// return useQuery({
// queryKey:["dashboardProjectProgress"],
// queryFn:async()=> {
// const payload = {
// days,
// FromDate: FromDate || '',
// projectId: projectId || null,
// };
// const resp = await GlobalRepository.getDashboardProgressionData(payload);
// return resp.data;
// }
// })
// }
export const useDashboard_AttendanceData = (date,projectId)=>{
return useQuery({
queryKey:["dashboardAttendances",date,projectId],
queryFn:async()=> {
const resp = await await GlobalRepository.getDashboardAttendanceData(date, projectId)
return resp.data;
}
})
}
export const useDashboardTeamsCardData =(projectId)=>{
return useQuery({
queryKey:["dashboardTeams",projectId],
queryFn:async()=> {
const resp = await GlobalRepository.getDashboardTeamsCardData(projectId)
return resp.data;
}
})
}
export const useDashboardTasksCardData = (projectId) => {
return useQuery({
queryKey:["dashboardTasks",projectId],
queryFn:async()=> {
const resp = await GlobalRepository.getDashboardTasksCardData(projectId)
return resp.data;
}
})
}
// export const useAttendanceOverviewData = (projectId, days) => {
// return useQuery({
// queryKey:["dashboardAttendanceOverView",projectId],
// queryFn:async()=> {
// const resp = await GlobalRepository.getAttendanceOverview(projectId, days);
// return resp.data;
// }
// })
// }
export const useDashboardProjectsCardData = () => {
return useQuery({
queryKey:["dashboardProjects"],
queryFn:async()=> {
const resp = await GlobalRepository.getDashboardProjectsCardData();
return resp.data;
}
})
}

View File

@ -13,7 +13,6 @@ import Regularization from "../../components/Activities/Regularization";
import { useAttendance } from "../../hooks/useAttendance";
import { useDispatch, useSelector } from "react-redux";
import { setProjectId } from "../../slices/localVariablesSlice";
import { hasUserPermission } from "../../utils/authUtils";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { REGULARIZE_ATTENDANCE } from "../../utils/constants";
import eventBus from "../../services/eventBus";

View File

@ -1,16 +1,16 @@
import React, { useEffect, useMemo } from "react";
import { useProfile } from "../../hooks/useProfile";
import TenantDetails from "./TenantDetails";
import { hasUserPermission } from "../../utils/authUtils";
import { VIEW_TENANTS } from "../../utils/constants";
import { useNavigate } from "react-router-dom";
import Loader from "../../components/common/Loader";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
const SelfTenantDetails = () => {
const { profile, loading } = useProfile();
const tenantId = profile?.employeeInfo?.tenantId;
const navigate = useNavigate();
const isSelfTenantView = hasUserPermission(VIEW_TENANTS);
const isSelfTenantView = useHasUserPermission(VIEW_TENANTS);
useEffect(() => {
if (!isSelfTenantView) {

View File

@ -20,7 +20,6 @@ import TenantFilterPanel from "../../components/Tenant/TenantFilterPanel";
import { useDebounce } from "../../utils/appUtils";
import { useFab } from "../../Context/FabContext";
import { setCurrentTenant } from "../../slices/globalVariablesSlice";
import { hasUserPermission } from "../../utils/authUtils";
// ------ Schema -------
import {
@ -35,6 +34,7 @@ import {
VIEW_TENANTS,
} from "../../utils/constants";
import { useProfile } from "../../hooks/useProfile";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
// ---------- Context ----------
export const TenantContext = createContext();
@ -63,9 +63,9 @@ const TenantPage = () => {
const debouncedSearch = useDebounce(searchText, 500);
const { setOffcanvasContent, setShowTrigger } = useFab();
const isSuperTenant = hasUserPermission(SUPPER_TENANT);
const canManageTenants = hasUserPermission(MANAGE_TENANTS);
const isSelfTenant = hasUserPermission(VIEW_TENANTS);
const isSuperTenant = useHasUserPermission(SUPPER_TENANT);
const canManageTenants = useHasUserPermission(MANAGE_TENANTS);
const isSelfTenant = useHasUserPermission(VIEW_TENANTS);
const methods = useForm({
resolver: zodResolver(filterSchema),

View File

@ -4,6 +4,7 @@ import { useAuthModal, useSelectTenant, useTenants } from "../../hooks/useAuth";
import { useProfile } from "../../hooks/useProfile";
import { useQueryClient } from "@tanstack/react-query";
import AuthRepository from "../../repositories/AuthRepository";
import Loader from "../../components/common/Loader";
const SwitchTenant = () => {
const queryClient = useQueryClient();
@ -27,6 +28,8 @@ const SwitchTenant = () => {
localStorage.setItem("ctnt", tenantId);
chooseTenant(tenantId);
};
const contentBody = (
<div className="container text-black">
<p className=" fs-5">Switch Workplace</p>
@ -82,7 +85,7 @@ const SwitchTenant = () => {
<button
className="btn btn-primary btn-sm mt-2 align-self-start"
onClick={() => handleTenantselection(tenant?.id)}
disabled={isPending && pendingTenant === tenant.id}
disabled={isPending && pendingTenant === tenant.id || currentTenant === tenant.id }
>
{isPending && pendingTenant === tenant.id
? "Please Wait.."
@ -95,7 +98,7 @@ const SwitchTenant = () => {
</div>
</div>
);
return <Modal isOpen={isOpen} onClose={onClose} body={contentBody} />;
return <Modal isOpen={isOpen} onClose={onClose} body={isLoading ? <Loader/>:contentBody} />;
};
export default SwitchTenant;

View File

@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { useTenants, useSelectTenant } from "../../hooks/useAuth.jsx";
import { Link, useNavigate } from "react-router-dom";
import Dashboard from "../../components/Dashboard/Dashboard.jsx";
import Loader from "../../components/common/Loader.jsx";
const TenantSelectionPage = () => {
const [pendingTenant, setPendingTenant] = useState(null);
@ -19,26 +20,48 @@ const TenantSelectionPage = () => {
useEffect(() => {
if (localStorage.getItem("ctnt")) {
return navigate("/dashboard");
navigate("/dashboard");
}
}, []);
}, [navigate]);
useEffect(() => {
if (!isLoading && data?.data?.length === 1) {
const tenant = data.data[0];
handleTenantselection(tenant.id);
}
}, [isLoading, data]);
if (isLoading) return <Loader />;
if (isLoading) {
return <Loader />;
}
if (!data?.data?.length) {
return (
<div className="container-fluid py-5 text-center">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading tenants...</span>
</div>
<div className="text-center py-5">
<p>No tenant assigned to your account.</p>
</div>
);
}
return (
<div className="container-fluid py-4">
{/* Header */}
{/* Logo */}
<div className="text-center">
<img
src="/img/brand/marco.png"
alt="marco-logo"
className="app-brand-logo-login img-fluid"
style={{ maxHeight: "80px" }}
/>
</div>
{/* Heading */}
<div className="text-center mb-4">
<p className="fs-4 mb-1">Welcome</p>
<p className="fs-5">
<p className="fs-4 fw-bold mb-1">Welcome</p>
<p className="fs-6 fs-md-5">
Please select which dashboard you want to explore!!!
</p>
</div>
@ -47,40 +70,45 @@ const TenantSelectionPage = () => {
<div className="row justify-content-center g-4 m-auto">
{data?.data.map((tenant) => (
<div key={tenant.id} className="col-12 col-md-10 col-lg-8">
<div className="d-flex flex-column flex-md-row gap-5 align-items-center align-items-md-start p-3 border rounded shadow-sm bg-white h-100">
<div className="d-flex flex-column flex-md-row gap-4 align-items-center align-items-md-start p-3 border rounded shadow-sm bg-white h-100">
{/* Image */}
<div className="flex-shrink-0 text-center">
<img
src={tenant?.logoImage || "/assets/img/SP-Placeholdeer.svg"}
alt={tenant.name}
className="img-fluid"
className="img-fluid rounded"
style={{
maxWidth: "140px",
height: "auto",
aspectRatio: "3 / 2",
objectFit: "contain",
}}
/>
</div>
{/* Content */}
<div className="d-flex flex-column text-start gap-2">
<p className="fs-5 text-muted fw-semibold mb-1">
<div className="d-flex flex-column text-start gap-2 w-100">
{/* Title */}
<p className="fs-5 fs-md-4 text-dark fw-semibold mb-1">
{tenant?.name}
</p>
{/* Industry */}
<div className="d-flex flex-wrap gap-2 align-items-center">
<p className="fw-semibold m-0">Industry:</p>
<p className="m-0">
<p className="m-0 text-muted">
{tenant?.industry?.name || "Not Available"}
</p>
</div>
{/* Description */}
{tenant?.description && (
<p className="text-start text-wrap m-0">
<p className="text-start text-wrap text-muted small m-0">
{tenant?.description}
</p>
)}
{/* Button */}
<button
className="btn btn-primary btn-sm mt-2 align-self-start"
onClick={() => handleTenantselection(tenant?.id)}
@ -96,6 +124,7 @@ const TenantSelectionPage = () => {
))}
</div>
</div>
);
};

View File

@ -11,7 +11,7 @@ import {
} from "../../hooks/useEmployees";
import { useProjectName, useProjects } from "../../hooks/useProjects";
import { useProfile } from "../../hooks/useProfile";
import { hasUserPermission } from "../../utils/authUtils";
import {
ITEMS_PER_PAGE,
MANAGE_EMPLOYEES,
@ -19,7 +19,6 @@ import {
VIEW_TEAM_MEMBERS,
} from "../../utils/constants";
import { clearCacheKey } from "../../slices/apiDataManager";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import SuspendEmp from "../../components/Employee/SuspendEmp"; // Keep if you use SuspendEmp
import {
exportToCSV,
@ -36,6 +35,7 @@ import { newlineChars } from "pdf-lib";
import GlobalModel from "../../components/common/GlobalModel";
import usePagination from "../../hooks/usePagination";
import { setProjectId } from "../../slices/localVariablesSlice";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
const EmployeeList = () => {
const selectedProjectId = useSelector(

View File

@ -1,12 +0,0 @@
import { useProfile } from "../hooks/useProfile";
export const hasUserPermission = (permission) => {
const { profile } = useProfile();
if (profile) {
if (!permission || typeof permission !== "string") {
return false;
}
return profile?.featurePermissions.includes(permission);
}
return false;
};