Wire up dashboard tiles with live data, keep tables

- Fetch stats from /api/Dashboard/Stats endpoint
- KPI tiles show real counts and navigate on click
- Restore Priority Analysis and Invoice Analysis tables (placeholder data)
This commit is contained in:
Adam Moussa 2026-04-16 19:25:42 -04:00
parent 2477bcf5a6
commit 5aa194c7be
2 changed files with 67 additions and 39 deletions

View file

@ -25,6 +25,12 @@
align-items: center; align-items: center;
gap: 15px; gap: 15px;
min-height: 100px; min-height: 100px;
cursor: pointer;
transition: filter 0.15s;
}
.kpiCard:hover {
filter: brightness(0.9);
} }
.bg-success { background-color: #28a745; } .bg-success { background-color: #28a745; }

View file

@ -1,48 +1,71 @@
import React from "react"; import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { get } from "../apiUtil";
import { API_URL } from "../constants";
import "./Dashboard.css"; import "./Dashboard.css";
const Dashboard = () => { const Dashboard = () => {
const navigate = useNavigate();
const [stats, setStats] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
get(`${API_URL}/Dashboard/Stats`)
.then((data) => setStats(data))
.catch(() => {})
.finally(() => setLoading(false));
}, []);
const kpis = [ const kpis = [
{ title: "Create a Work Order", icon: "✓", color: "success", desc: "Report a problem you need fixed" }, {
{ title: "Open Work Orders", icon: "📂", color: "primary", stat: "885" }, title: "Create a Work Order",
{ title: "Not Dispatched", icon: "⏱", color: "info", stat: "503" }, icon: "fa-plus",
{ title: "Completed Orders", icon: "📋", color: "warning", stat: "4,088" }, color: "success",
]; desc: "Report a problem you need fixed",
onClick: () => navigate("/workorders/new"),
const priorities = [ },
{ priority: "5", total: "665", "0-14": "83", "15-29": "79", "30-59": "62", "60+": "441" }, {
{ priority: "4", total: "327", "0-14": "4", "15-29": "19", "30-59": "3", "60+": "301" }, title: "Open Work Orders",
{ priority: "3", total: "295", "0-14": "69", "15-29": "64", "30-59": "42", "60+": "120" }, icon: "fa-folder-open",
{ priority: "2", total: "95", "0-14": "26", "15-29": "38", "30-59": "11", "60+": "20" }, color: "primary",
]; stat: loading ? "..." : stats?.open ?? 0,
onClick: () => navigate("/workorders"),
const invoices = [ },
{ name: "Draft", count: 0, amount: "0.00" }, {
{ name: "Challenged", count: 0, amount: "0.00" }, title: "Not Dispatched",
{ name: "Pending Approval", count: 0, amount: "0.00" }, icon: "fa-clock-o",
{ name: "Approved", count: 0, amount: "0.00" }, color: "info",
{ name: "Paid", count: 0, amount: "0.00" }, stat: loading ? "..." : stats?.notDispatched ?? 0,
onClick: () => navigate("/workorders"),
},
{
title: "Completed Orders",
icon: "fa-check-circle",
color: "warning",
stat: loading ? "..." : stats?.completed ?? 0,
onClick: () => navigate("/workorders"),
},
]; ];
return ( return (
<section className="dashboardPage"> <section className="dashboardPage">
<div className="pageTitle">Dashboard</div> <div className="pageTitle">Dashboard</div>
{/* KPI Cards */}
<div className="kpiContainer"> <div className="kpiContainer">
{kpis.map((kpi, idx) => ( {kpis.map((kpi, idx) => (
<div key={idx} className={`kpiCard bg-${kpi.color}`}> <div key={idx} className={`kpiCard bg-${kpi.color}`} onClick={kpi.onClick}>
<div className="kpiIcon">{kpi.icon}</div> <div className="kpiIcon">
<i className={`fa ${kpi.icon}`}></i>
</div>
<div className="kpiContent"> <div className="kpiContent">
<h5>{kpi.title}</h5> <h5>{kpi.title}</h5>
{kpi.stat && <div className="kpiStat">{kpi.stat}</div>} {kpi.stat !== undefined && <div className="kpiStat">{kpi.stat?.toLocaleString?.() ?? kpi.stat}</div>}
{kpi.desc && <p>{kpi.desc}</p>} {kpi.desc && <p>{kpi.desc}</p>}
</div> </div>
</div> </div>
))} ))}
</div> </div>
{/* Priority Analysis */}
<div className="card mt-4"> <div className="card mt-4">
<div className="cardHeader"> <div className="cardHeader">
<h3>Priority Analysis</h3> <h3>Priority Analysis</h3>
@ -61,14 +84,14 @@ const Dashboard = () => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{priorities.map((row, idx) => ( {[5, 4, 3, 2, 1].map((p) => (
<tr key={idx}> <tr key={p}>
<td>{row.priority}</td> <td>{p}</td>
<td className="text-right">{row.total}</td> <td className="text-right">—</td>
<td className="text-right">{row["0-14"]}</td> <td className="text-right">—</td>
<td className="text-right">{row["15-29"]}</td> <td className="text-right">—</td>
<td className="text-right">{row["30-59"]}</td> <td className="text-right">—</td>
<td className="text-right">{row["60+"]}</td> <td className="text-right">—</td>
</tr> </tr>
))} ))}
</tbody> </tbody>
@ -76,7 +99,6 @@ const Dashboard = () => {
</div> </div>
</div> </div>
{/* Invoice Analysis */}
<div className="card mt-4"> <div className="card mt-4">
<div className="cardHeader"> <div className="cardHeader">
<h3>Invoice Analysis</h3> <h3>Invoice Analysis</h3>
@ -91,11 +113,11 @@ const Dashboard = () => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{invoices.map((inv, idx) => ( {["Draft", "Challenged", "Pending Approval", "Approved", "Paid"].map((s) => (
<tr key={idx}> <tr key={s}>
<td>{inv.name}</td> <td>{s}</td>
<td className="text-right">{inv.count}</td> <td className="text-right">0</td>
<td className="text-right">${inv.amount}</td> <td className="text-right">$0.00</td>
</tr> </tr>
))} ))}
</tbody> </tbody>