165 lines
5.2 KiB
JavaScript
165 lines
5.2 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import Chart from "react-apexcharts";
|
|
import { useExpenseType } from "../../hooks/masterHook/useMaster";
|
|
import { useSelector } from "react-redux";
|
|
import { useExpenseDataByProject } from "../../hooks/useDashboard_Data";
|
|
import { formatCurrency } from "../../utils/appUtils";
|
|
import { formatDate_DayMonth } from "../../utils/dateUtils";
|
|
|
|
const ExpenseByProject = () => {
|
|
const projectId = useSelector((store) => store.localVariables.projectId);
|
|
const [range, setRange] = useState("12M");
|
|
const [selectedType, setSelectedType] = useState("");
|
|
const [viewMode, setViewMode] = useState("Category");
|
|
const [chartData, setChartData] = useState({ categories: [], data: [] });
|
|
|
|
const { ExpenseTypes, loading: typeLoading } = useExpenseType();
|
|
|
|
const { data: expenseApiData, isLoading } = useExpenseDataByProject(
|
|
projectId,
|
|
selectedType,
|
|
range === "All" ? null : parseInt(range)
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (expenseApiData) {
|
|
const categories = expenseApiData.map((item) =>
|
|
formatDate_DayMonth(item.monthName, item.year)
|
|
);
|
|
const data = expenseApiData.map((item) => item.total);
|
|
setChartData({ categories, data });
|
|
} else {
|
|
setChartData({ categories: [], data: [] });
|
|
}
|
|
}, [expenseApiData]);
|
|
|
|
const getSelectedTypeName = () => {
|
|
if (!selectedType) return "All Types";
|
|
const found = ExpenseTypes.find((t) => t.id === selectedType);
|
|
return found ? found.name : "All Types";
|
|
};
|
|
|
|
const options = {
|
|
chart: { type: "bar", toolbar: { show: false } },
|
|
plotOptions: {
|
|
bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 },
|
|
},
|
|
dataLabels: { enabled: true, formatter: (val) => formatCurrency(val) },
|
|
xaxis: {
|
|
categories: chartData.categories,
|
|
labels: { style: { fontSize: "12px" }, rotate: -45 },
|
|
},
|
|
tooltip: {
|
|
y: {
|
|
formatter: (val) => `${formatCurrency(val)} (${getSelectedTypeName()})`,
|
|
},
|
|
},
|
|
|
|
annotations: { xaxis: [{ x: 0, strokeDashArray: 0 }] },
|
|
fill: { opacity: 1 },
|
|
colors: ["#2196f3"],
|
|
};
|
|
|
|
const series = [
|
|
{
|
|
name: getSelectedTypeName(),
|
|
data: chartData.data,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="card shadow-sm rounded ">
|
|
{/* Header */}
|
|
<div className="card-header">
|
|
<div className="d-flex justify-content-start align-items-center mb-3 mt-3">
|
|
<div>
|
|
<h5 className="mb-1 me-6 card-title">Monthly Expense -</h5>
|
|
<p className="card-subtitle me-5 mb-0">Detailed project expenses</p>
|
|
</div>
|
|
<div className="btn-group mb-4 ms-n8">
|
|
<button
|
|
className="btn btn-sm dropdown-toggle fs-5"
|
|
type="button"
|
|
data-bs-toggle="dropdown"
|
|
aria-expanded="false"
|
|
>
|
|
{viewMode}
|
|
</button>
|
|
<ul className="dropdown-menu dropdown-menu-end ">
|
|
<li>
|
|
<button
|
|
className="dropdown-item"
|
|
onClick={() => {
|
|
setViewMode("Category");
|
|
setSelectedType("");
|
|
}}
|
|
>
|
|
Category
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button
|
|
className="dropdown-item"
|
|
onClick={() => {
|
|
setViewMode("Project");
|
|
setSelectedType("");
|
|
}}
|
|
>
|
|
Project
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Range Buttons + Expense Dropdown */}
|
|
<div className="d-flex align-items-center flex-wrap ">
|
|
{["1M", "3M", "6M", "12M", "All"].map((item) => (
|
|
<button
|
|
key={item}
|
|
className={`border-0 px-2 py-1 text-sm rounded ${range === item
|
|
? "text-white bg-primary"
|
|
: "text-body bg-transparent"
|
|
}`}
|
|
style={{ cursor: "pointer", transition: "all 0.2s ease" }}
|
|
onClick={() => setRange(item)}
|
|
>
|
|
{item}
|
|
</button>
|
|
))}
|
|
{viewMode === "Category" && (
|
|
<select
|
|
className="form-select form-select-sm ms-auto mb-3 mt-1 mt-sm-0"
|
|
value={selectedType}
|
|
onChange={(e) => setSelectedType(e.target.value)}
|
|
disabled={typeLoading}
|
|
style={{ maxWidth: "200px" }}
|
|
>
|
|
<option value="">All Types</option>
|
|
{ExpenseTypes.map((type) => (
|
|
<option key={type.id} value={type.id}>
|
|
{type.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Chart */}
|
|
<div className="card-body bg-white text-dark p-3 rounded" style={{ minHeight: "210px" }}>
|
|
{isLoading ? (
|
|
<p>Loading chart...</p>
|
|
) : !expenseApiData || expenseApiData.length === 0 ? (
|
|
<div className="text-center text-muted py-5">No data found</div>
|
|
) : (
|
|
<Chart options={options} series={series} type="bar" height={235} />
|
|
)}
|
|
</div>
|
|
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ExpenseByProject;
|