Changes in Monthly Expense dashboard.

This commit is contained in:
Kartik Sharma 2025-10-03 17:00:57 +05:30
parent 2397be3b8c
commit 80ac18ff26
2 changed files with 91 additions and 37 deletions

View File

@ -1,18 +1,18 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Chart from "react-apexcharts"; import Chart from "react-apexcharts";
import { useExpenseType } from "../../hooks/masterHook/useMaster"; import { useExpenseType } from "../../hooks/masterHook/useMaster";
import { useSelectedProject } from "../../slices/apiDataManager"; import { useSelector } from "react-redux";
import { useExpenseDataByProject } from "../../hooks/useDashboard_Data"; import { useExpenseDataByProject } from "../../hooks/useDashboard_Data";
const ExpenseByProject = () => { const ExpenseByProject = () => {
const projectId = useSelectedProject(); const projectId = useSelector((store) => store.localVariables.projectId);
const [range, setRange] = useState("12M"); const [range, setRange] = useState("12M");
const [selectedType, setSelectedType] = useState(""); const [selectedType, setSelectedType] = useState("");
const [viewMode, setViewMode] = useState("Category");
const [chartData, setChartData] = useState({ categories: [], data: [] }); const [chartData, setChartData] = useState({ categories: [], data: [] });
const { ExpenseTypes, loading: typeLoading } = useExpenseType(); const { ExpenseTypes, loading: typeLoading } = useExpenseType();
// Fetch API data
const { data: expenseApiData, isLoading } = useExpenseDataByProject( const { data: expenseApiData, isLoading } = useExpenseDataByProject(
projectId, projectId,
selectedType, selectedType,
@ -25,46 +25,95 @@ const ExpenseByProject = () => {
(item) => `${item.monthName} ${item.year}` (item) => `${item.monthName} ${item.year}`
); );
const data = expenseApiData.map((item) => item.total); const data = expenseApiData.map((item) => item.total);
setChartData({ categories, data }); setChartData({ categories, data });
} else { } else {
setChartData({ categories: [], data: [] }); setChartData({ categories: [], data: [] });
} }
}, [expenseApiData]); }, [expenseApiData]);
const getSelectedTypeName = () => {
if (!selectedType) return "All Types";
const found = ExpenseTypes.find((t) => t.id === selectedType);
return found ? found.name : "All Types";
};
const options = { const options = {
chart: { type: "bar", toolbar: { show: false } }, chart: { type: "bar", toolbar: { show: false } },
plotOptions: { bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 } }, plotOptions: { bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 } },
dataLabels: { enabled: true, formatter: (val) => val }, dataLabels: { enabled: true, formatter: (val) => val },
xaxis: { categories: chartData.categories, labels: { style: { fontSize: "12px" }, rotate: -45 } }, xaxis: {
categories: chartData.categories,
labels: { style: { fontSize: "12px" }, rotate: -45 },
},
tooltip: {
y: {
formatter: (val) => `${val.toLocaleString()} (${getSelectedTypeName()})`,
},
},
annotations: { xaxis: [{ x: 0, strokeDashArray: 0, }] },
fill: { opacity: 1 }, fill: { opacity: 1 },
colors: ["#2196f3"] colors: ["#2196f3"],
}; };
const series = [ const series = [
{ {
name: selectedType || "Expense", name: getSelectedTypeName(),
data: chartData.data data: chartData.data,
} },
]; ];
return ( return (
<div className="card shadow-sm "> <div className="card shadow-sm ">
{/* Header */} {/* Header */}
<div className="card-header"> <div className="card-header">
<div className="d-flex justify-content-between align-items-center mb-3 mt-3"> <div className="d-flex justify-content-start align-items-center mb-3 mt-3">
<div> <div>
<h5 className="mb-1 fw-bold">Expense Breakdown</h5> <h5 className="mb-1 me-6 fw-bold">Monthly Expense -</h5>
<p className="card-subtitle me-3 mb-0">Detailed project expenses</p> <p className="card-subtitle me-5 mb-0">Detailed project expenses</p>
</div> </div>
<div className="d-flex align-items-center gap-2"> {/* View Mode Dropdown → same style as Header */}
{/* Expense Type Dropdown */} <div className="btn-group mb-4 ms-n4">
<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>
{viewMode === "Category" && (
<select <select
className="form-select form-select-sm mb-2" className="form-select form-select-sm ms-auto mb-3"
value={selectedType} value={selectedType}
onChange={(e) => setSelectedType(e.target.value)} onChange={(e) => setSelectedType(e.target.value)}
disabled={typeLoading} disabled={typeLoading}
style={{ maxWidth: "200px" }}
> >
<option value="">All Types</option> <option value="">All Types</option>
{ExpenseTypes.map((type) => ( {ExpenseTypes.map((type) => (
@ -73,15 +122,19 @@ const ExpenseByProject = () => {
</option> </option>
))} ))}
</select> </select>
</div> )}
</div> </div>
{/* Range Buttons */} {/* Range Buttons + Expense Dropdown */}
<div className="d-flex gap-2"> <div className="d-flex gap-2 align-items-center flex-wrap">
{["6M", "12M", "All"].map((item) => ( {["1M", "3M", "6M", "12M", "All"].map((item) => (
<button <button
key={item} key={item}
className={`btn btn-xs ${range === item ? "btn-primary" : "btn-outline-light"}`} 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)} onClick={() => setRange(item)}
> >
{item} {item}
@ -91,7 +144,10 @@ const ExpenseByProject = () => {
</div> </div>
{/* Chart */} {/* Chart */}
<div className="card-body bg-white text-dark" style={{ minHeight: "440px" }}> <div
className="card-body bg-white text-dark p-3"
style={{ minHeight: "440px" }}
>
{isLoading ? ( {isLoading ? (
<p>Loading chart...</p> <p>Loading chart...</p>
) : ( ) : (
@ -103,5 +159,3 @@ const ExpenseByProject = () => {
}; };
export default ExpenseByProject; export default ExpenseByProject;

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState,useMemo } from "react"; import { useCallback, useEffect, useState, useMemo } from "react";
import getGreetingMessage from "../../utils/greetingHandler"; import getGreetingMessage from "../../utils/greetingHandler";
import { import {
cacheData, cacheData,