Adding Spinner in Dashbard.
This commit is contained in:
parent
9fbe3cf6a5
commit
86f931929a
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import ReactApexChart from "react-apexcharts";
|
||||
import PropTypes from "prop-types";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const HorizontalBarChart = ({
|
||||
seriesData = [],
|
||||
@ -23,7 +24,12 @@ const HorizontalBarChart = ({
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="w-full h-[380px] flex items-center justify-center bg-gray-100 rounded-xl">
|
||||
<span className="text-gray-500">Loading chart...</span>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ minHeight: "50vh" }}
|
||||
>
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
{/* Replace this with a skeleton or spinner if you prefer */}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import ReactApexChart from "react-apexcharts";
|
||||
import PropTypes from "prop-types";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const LineChart = ({
|
||||
seriesData = [],
|
||||
@ -9,24 +10,28 @@ const LineChart = ({
|
||||
loading = false,
|
||||
lineChartCategoriesDates = [],
|
||||
}) => {
|
||||
const hasValidData =
|
||||
Array.isArray(seriesData) &&
|
||||
seriesData.length > 0 &&
|
||||
Array.isArray(categories) &&
|
||||
categories.length > 0;
|
||||
const hasValidData =
|
||||
Array.isArray(seriesData) &&
|
||||
seriesData.length > 0 &&
|
||||
Array.isArray(categories) &&
|
||||
categories.length > 0;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-[350px] text-gray-500">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500 mr-2" />
|
||||
Loading chart...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-[350px] text-gray-500">
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ minHeight: "50vh" }}
|
||||
>
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasValidData) {
|
||||
return <div className="text-center text-gray-500">No data to display</div>;
|
||||
}
|
||||
if (!hasValidData) {
|
||||
return <div className="text-center text-gray-500">No data to display</div>;
|
||||
}
|
||||
|
||||
const chartOptions = {
|
||||
chart: {
|
||||
@ -129,16 +134,16 @@ const LineChart = ({
|
||||
};
|
||||
|
||||
LineChart.propTypes = {
|
||||
seriesData: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
data: PropTypes.arrayOf(PropTypes.number).isRequired
|
||||
})
|
||||
),
|
||||
categories: PropTypes.arrayOf(PropTypes.string),
|
||||
colors: PropTypes.arrayOf(PropTypes.string),
|
||||
title: PropTypes.string,
|
||||
loading: PropTypes.bool
|
||||
seriesData: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
data: PropTypes.arrayOf(PropTypes.number).isRequired
|
||||
})
|
||||
),
|
||||
categories: PropTypes.arrayOf(PropTypes.string),
|
||||
colors: PropTypes.arrayOf(PropTypes.string),
|
||||
title: PropTypes.string,
|
||||
loading: PropTypes.bool
|
||||
};
|
||||
|
||||
export default LineChart;
|
||||
|
||||
@ -6,6 +6,7 @@ import { DateRangePicker1 } from "../common/DateRangePicker";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import { formatCurrency, localToUtc } from "../../utils/appUtils";
|
||||
import { useProjectName } from "../../hooks/useProjects";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const ExpenseAnalysis = () => {
|
||||
const projectId = useSelectedProject();
|
||||
@ -78,86 +79,92 @@ const ExpenseAnalysis = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="card-header d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center gap-2">
|
||||
<div className="text-start w-100">
|
||||
<h5 className="mb-1 card-title">Expense Breakdown</h5>
|
||||
{/* <p className="card-subtitle mb-0">Category Wise Expense Breakdown</p> */}
|
||||
<p className="card-subtitle m-0">{projectName}</p>
|
||||
</div>
|
||||
|
||||
<div className="text-start text-sm-end w-75">
|
||||
<FormProvider {...methods}>
|
||||
<DateRangePicker1 />
|
||||
</FormProvider>
|
||||
</div>
|
||||
<div className="card-header d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center gap-2">
|
||||
<div className="text-start w-100">
|
||||
<h5 className="mb-1 card-title">Expense Breakdown</h5>
|
||||
{/* <p className="card-subtitle mb-0">Category Wise Expense Breakdown</p> */}
|
||||
<p className="card-subtitle m-0">{projectName}</p>
|
||||
</div>
|
||||
|
||||
{/* Card body */}
|
||||
<div className="card-body position-relative">
|
||||
{isLoading && (
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ height: "200px" }}
|
||||
>
|
||||
<span>Loading...</span>
|
||||
<div className="text-start text-sm-end w-75">
|
||||
<FormProvider {...methods}>
|
||||
<DateRangePicker1 />
|
||||
</FormProvider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card body */}
|
||||
<div className="card-body position-relative">
|
||||
{isLoading && (
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ height: "200px" }}
|
||||
>
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && report.length === 0 && (
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center text-muted"
|
||||
style={{ height: "300px" }}
|
||||
>
|
||||
No data found
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{!isLoading && report.length > 0 && (
|
||||
<>
|
||||
{isFetching && (
|
||||
<div className="position-absolute top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-white bg-opacity-75">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-center mb-3">
|
||||
<Chart
|
||||
options={donutOptions}
|
||||
series={series}
|
||||
type="donut"
|
||||
width="100%"
|
||||
height={320}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && report.length === 0 && (
|
||||
<div className="text-center py-5 text-muted">No data found</div>
|
||||
)}
|
||||
|
||||
{!isLoading && report.length > 0 && (
|
||||
<>
|
||||
{isFetching && (
|
||||
<div className="position-absolute top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-white bg-opacity-75">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-center mb-3">
|
||||
<Chart
|
||||
options={donutOptions}
|
||||
series={series}
|
||||
type="donut"
|
||||
width="100%"
|
||||
height={320}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 w-100">
|
||||
<div className="row g-2">
|
||||
{report.map((item, idx) => (
|
||||
<div
|
||||
className="col-12 col-sm-6 d-flex align-items-start"
|
||||
key={idx}
|
||||
>
|
||||
<div className="avatar me-2">
|
||||
<span
|
||||
className="avatar-initial rounded-2"
|
||||
style={{
|
||||
backgroundColor:
|
||||
donutOptions.colors[idx % donutOptions.colors.length],
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-receipt fs-4"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div className="d-flex flex-column gap-1 text-start">
|
||||
<small className="fw-semibold">{item.projectName}</small>
|
||||
<span className="fw-semibold text-muted ms-1">
|
||||
{formatCurrency(item.totalApprovedAmount)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mb-2 w-100">
|
||||
<div className="row g-2">
|
||||
{report.map((item, idx) => (
|
||||
<div
|
||||
className="col-12 col-sm-6 d-flex align-items-start"
|
||||
key={idx}
|
||||
>
|
||||
<div className="avatar me-2">
|
||||
<span
|
||||
className="avatar-initial rounded-2"
|
||||
style={{
|
||||
backgroundColor:
|
||||
donutOptions.colors[idx % donutOptions.colors.length],
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-receipt fs-4"></i>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="d-flex flex-column gap-1 text-start">
|
||||
<small className="fw-semibold">{item.projectName}</small>
|
||||
<span className="fw-semibold text-muted ms-1">
|
||||
{formatCurrency(item.totalApprovedAmount)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
|
||||
</>
|
||||
|
||||
@ -19,3 +19,13 @@ const Loader = () => {
|
||||
|
||||
export default Loader;
|
||||
|
||||
export const SpinnerLoader = () => {
|
||||
return (
|
||||
<div className="text-center">
|
||||
<div className="spinner-border text-primary mb-3" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p className="text-secondary mb-0">Loading data...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user