added skeleton for dashboard card figures cards

This commit is contained in:
pramod.mahajan 2025-10-25 10:26:41 +05:30
parent 32f16092db
commit a070d23304
7 changed files with 133 additions and 38 deletions

View File

@ -25,6 +25,12 @@ font-size: 2rem;
.text-md-b { .text-md-b {
font-weight: normal; font-weight: normal;
} }
.cursor-wait{
cursor:wait;
}
.cursor-notallowed {
cursor: not-allowed;
}
.text-xxs { font-size: 0.55rem; } /* 8px */ .text-xxs { font-size: 0.55rem; } /* 8px */
.text-xs { font-size: 0.75rem; } /* 12px */ .text-xs { font-size: 0.75rem; } /* 12px */

View File

@ -29,7 +29,9 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
} = methods; } = methods;
const closePanel = () => {
document.querySelector(".offcanvas.show .btn-close")?.click();
};
const onSubmit = (formData) => { const onSubmit = (formData) => {
const filterPayload = { const filterPayload = {
...formData, ...formData,
@ -37,12 +39,14 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
dateTo: localToUtc(formData.dateTo), dateTo: localToUtc(formData.dateTo),
}; };
handleFilter(filterPayload); handleFilter(filterPayload);
closePanel();
}; };
const onClear = () => { const onClear = () => {
setResetKey((prev) => prev + 1); setResetKey((prev) => prev + 1);
handleFilter(TaskReportDefaultValue); handleFilter(TaskReportDefaultValue);
reset(TaskReportDefaultValue); reset(TaskReportDefaultValue);
closePanel();
}; };
return ( return (
<FormProvider {...methods}> <FormProvider {...methods}>

View File

@ -19,8 +19,6 @@ import ExpenseStatus from "./ExpenseStatus";
import ExpenseByProject from "./ExpenseByProject"; import ExpenseByProject from "./ExpenseByProject";
const Dashboard = () => { const Dashboard = () => {
const { teamsCardData } = useDashboardTeamsCardData();
const { tasksCardData } = useDashboardTasksCardData();
// Get the selected project ID from Redux store // Get the selected project ID from Redux store
const projectId = useSelector((store) => store.localVariables.projectId); const projectId = useSelector((store) => store.localVariables.projectId);
@ -36,11 +34,11 @@ const Dashboard = () => {
)} )}
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}> <div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
<Teams teamsCardData={teamsCardData} /> <Teams />
</div> </div>
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}> <div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
<TasksCard tasksCardData={tasksCardData} /> <TasksCard/>
</div> </div>
{isAllProjectsSelected && ( {isAllProjectsSelected && (

View File

@ -52,3 +52,59 @@ export const ProjectCardSkeleton = () => {
); );
}; };
export const TeamsSkeleton = () => {
return (
<>
<style>{skeletonStyle}</style>
<div className="card p-3 h-100 text-center d-flex justify-content-between">
{/* Header */}
<div className="d-flex justify-content-start align-items-center mb-3">
<h5 className="fw-bold mb-0 ms-2">
<i className="bx bx-group text-warning"></i> Teams
</h5>
</div>
{/* Skeleton Body */}
<div className="d-flex justify-content-around align-items-start mt-n2 w-100">
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="90px" className="mx-auto" />
</div>
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="70px" className="mx-auto" />
</div>
</div>
</div>
</>
);
};
export const TasksSkeleton = () => {
return (
<>
<style>{skeletonStyle}</style>
<div className="card p-3 h-100 text-center d-flex justify-content-between">
{/* Header */}
<div className="d-flex justify-content-start align-items-center mb-3">
<h5 className="fw-bold mb-0 ms-2">
<i className="bx bx-task text-success"></i> Tasks
</h5>
</div>
{/* Skeleton Body */}
<div className="d-flex justify-content-around align-items-start mt-n2 w-100">
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="70px" className="mx-auto" />
</div>
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="90px" className="mx-auto" />
</div>
</div>
</div>
</>
);
};

View File

@ -10,6 +10,7 @@ const Projects = () => {
isLoading, isLoading,
isError, isError,
error, error,
isFetching,
refetch, refetch,
} = useDashboardProjectsCardData(); } = useDashboardProjectsCardData();
@ -25,7 +26,7 @@ const Projects = () => {
const totalProjects = projectsCardData?.totalProjects ?? 0; const totalProjects = projectsCardData?.totalProjects ?? 0;
const ongoingProjects = projectsCardData?.ongoingProjects ?? 0; const ongoingProjects = projectsCardData?.ongoingProjects ?? 0;
if(isLoading) return <ProjectCardSkeleton/> if (isLoading) return <ProjectCardSkeleton />;
return ( return (
<div className="card p-3 h-100 text-center d-flex justify-content-between"> <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"> <div className="d-flex justify-content-start align-items-center mb-3">
@ -35,15 +36,23 @@ const Projects = () => {
</h5> </h5>
</div> </div>
{isLoading ? ( {isError ? (
<div className="d-flex justify-content-center align-items-center flex-grow-1"> <div className="d-flex flex-column justify-content-center align-items-center p-1">
<div className="spinner-border text-primary" role="status"> <i className="bx bx-error-circle bx-sm fs-2 "></i>
<span className="visually-hidden">Loading...</span> <small className="text-muted mb-2">
</div> {error?.message || "Unable to load data at the moment."}
</div> </small>
) : isError ? ( <span
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center"> className={`text-muted ${
{error?.message || "Error loading data"} isFetching ? "cursor-wait" : "cursor-pointer"
}`}
onClick={refetch}
>
<i
className={`bx bx-refresh me-1 ${isFetching ? "bx-spin" : ""}`}
></i>{" "}
Retry
</span>
</div> </div>
) : ( ) : (
<div className="d-flex justify-content-around align-items-start mt-n2"> <div className="d-flex justify-content-around align-items-start mt-n2">

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { useSelectedProject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
import { useDashboardTasksCardData } from "../../hooks/useDashboard_Data"; import { useDashboardTasksCardData } from "../../hooks/useDashboard_Data";
import { TasksSkeleton } from "./DashboardSkeleton";
const TasksCard = () => { const TasksCard = () => {
const projectId = useSelectedProject(); const projectId = useSelectedProject();
@ -10,8 +11,10 @@ const TasksCard = () => {
isLoading, isLoading,
isError, isError,
error, error,
isFetching,
refetch,
} = useDashboardTasksCardData(projectId); } = useDashboardTasksCardData(projectId);
if (isLoading) return <TasksSkeleton />;
return ( return (
<div className="card p-3 h-100 text-center d-flex justify-content-between"> <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"> <div className="d-flex justify-content-start align-items-center mb-3">
@ -20,20 +23,26 @@ const TasksCard = () => {
</h5> </h5>
</div> </div>
{isLoading ? ( {isError ? (
// Loader while fetching <div className="d-flex flex-column justify-content-center align-items-center p-1">
<div className="d-flex justify-content-center align-items-center flex-grow-1"> <i className="bx bx-error-circle bx-sm fs-2 "></i>
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span> <small className="text-muted mb-2">
</div> {error?.message || "Unable to load data at the moment."}
</div> </small>
) : isError ? ( <span
// Show error className={`text-muted ${
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center"> isFetching ? "cursor-wait" : "cursor-pointer"
{error?.message || "Error loading data"} }`}
onClick={refetch}
>
<i
className={`bx bx-refresh me-1 ${isFetching ? "bx-spin" : ""}`}
></i>{" "}
Retry
</span>
</div> </div>
) : ( ) : (
// Show data
<div className="d-flex justify-content-around align-items-start mt-n2"> <div className="d-flex justify-content-around align-items-start mt-n2">
<div> <div>
<h4 className="mb-0 fw-bold"> <h4 className="mb-0 fw-bold">

View File

@ -4,16 +4,19 @@ import { useDashboardTeamsCardData } from "../../hooks/useDashboard_Data";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useSelectedProject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
import { TeamsSkeleton } from "./DashboardSkeleton";
const Teams = () => { const Teams = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const projectId = useSelectedProject() const projectId = useSelectedProject();
const { const {
data: teamsCardData, data: teamsCardData,
isLoading, isLoading,
isError, isError,
error, error,
isFetching,
refetch,
} = useDashboardTeamsCardData(projectId); } = useDashboardTeamsCardData(projectId);
// Handle real-time updates via eventBus // Handle real-time updates via eventBus
@ -40,6 +43,7 @@ const Teams = () => {
const inToday = teamsCardData?.inToday ?? 0; const inToday = teamsCardData?.inToday ?? 0;
const totalEmployees = teamsCardData?.totalEmployees ?? 0; const totalEmployees = teamsCardData?.totalEmployees ?? 0;
if (isLoading) return <TeamsSkeleton />;
return ( return (
<div className="card p-3 h-100 text-center d-flex justify-content-between"> <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"> <div className="d-flex justify-content-start align-items-center mb-3">
@ -48,15 +52,24 @@ const Teams = () => {
</h5> </h5>
</div> </div>
{isLoading ? ( {isError ? (
<div className="d-flex justify-content-center align-items-center flex-grow-1"> <div className="d-flex flex-column justify-content-center align-items-center p-1">
<div className="spinner-border text-primary" role="status"> <i className="bx bx-error-circle bx-sm fs-2 "></i>
<span className="visually-hidden">Loading...</span>
</div> <small className="text-muted mb-2">
</div> {error?.message || "Unable to load data at the moment."}
) : isError ? ( </small>
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center"> <span
{error?.message || "Error loading data"} className={`text-muted ${
isFetching ? "cursor-wait" : "cursor-pointer"
}`}
onClick={refetch}
>
<i
className={`bx bx-refresh me-1 ${isFetching ? "bx-spin" : ""}`}
></i>{" "}
Retry
</span>
</div> </div>
) : ( ) : (
<div className="d-flex justify-content-around align-items-start mt-n2"> <div className="d-flex justify-content-around align-items-start mt-n2">