nexus-1/frontend/modules/ReportsModule.jsx
2026-01-26 09:45:31 -05:00

244 lines
10 KiB
JavaScript

import React from "react";
import api from "../api.js";
import {useState, useEffect} from "react";
export default function ReportsModule() {
const username = localStorage.getItem('username');
const [reports, setReports] = useState([]);
const [month, setMonth] = useState("");
const [year, setYear] = useState("");
const [account, setAccount] = useState("");
const [accounts, setAccounts] = useState([]);
const [loading, setLoading] = useState(false);
const [rates, setRates] = useState({});
const [loadingRates, setLoadingRates] = useState(false);
const [grandTotal, setGrandTotal] = useState(0);
useEffect(() => {
const fetchAccounts = async () => {
setLoading(true);
try {
const response = await api.get('/accounts/');
if (response.status === 200) {
const sortedAccounts = [...response.data].sort((a, b) => a.store - b.store);
setAccounts(sortedAccounts);
}
} catch (error) {
alert(error);
} finally {
setLoading(false);
}
};
fetchAccounts();
}, []);
const fetchServiceVisits = async () => {
if (month !== "" && year !== "" && account !== "") {
setLoading(true);
try {
const response = await api.get(
`visits/?username=${username}&month=${month}&year=${year}&account=${account}`
);
if (response.status === 200) {
const sortedVisits = [...response.data].sort((a, b) => {
if (a.short_name < b.short_name) return -1;
if (a.short_name > b.short_name) return 1;
if (a.date < b.date) return -1;
if (a.date > b.date) return 1;
return 0;
});
setReports(sortedVisits);
setGrandTotal(null); // Reset to null when fetching
setRates({}); // Reset rates
}
} catch (error) {
alert(error);
} finally {
setLoading(false);
}
} else {
alert("All fields are required!!")
}
};
const fetchRate = async (account) => {
setLoading(true);
try {
const response = await api.post('/accounts/rate/', {'account': account});
if (response.status === 200) {
return response.data['rate'];
}
} catch (error) {
alert(error);
} finally {
setLoading(false);
}
}
useEffect(() => {
const fetchAndSetRates = async () => {
if (reports.length > 0) {
setLoadingRates(true);
try {
const uniqueAccounts = [...new Set(reports.map(report => report.short_name))];
const ratePromises = uniqueAccounts.map(account => fetchRate(account));
const fetchedRates = await Promise.all(ratePromises);
const ratesObject = {};
uniqueAccounts.forEach((account, index) => {
ratesObject[account] = fetchedRates[index];
});
setRates(ratesObject);
} catch (error) {
alert(error); // Handle errors appropriately
} finally {
setLoadingRates(false);
}
}
};
fetchAndSetRates();
}, [reports]);
useEffect(() => {
if (!loadingRates && Object.keys(rates).length > 0) {
const closedVisitsData = reports.reduce((count, visit) => {
if (visit.status === "Closed") {
count[visit.short_name] = {
closedVisits: (count[visit.short_name]?.closedVisits || 0) + 1,
fullName: visit.full_name,
};
}
return count;
}, {});
let totalByAccount = 0;
for (let key in closedVisitsData) {
if (rates[key] && rates[key] !== "N/A") {
totalByAccount += closedVisitsData[key].closedVisits * rates[key];
}
}
setGrandTotal(totalByAccount);
} else if (!loadingRates && reports.length === 0) {
setGrandTotal(0);
}
}, [reports, rates, loadingRates]);
const handleYear = (e) => {
setYear(e.target.value)
}
const handleMonth = (e) => {
setMonth(e.target.value)
}
const handleAccount = (e) => {
setAccount(e.target.value)
}
// Generate year options dynamically
const currentYear = new Date().getFullYear();
const yearOptions = [];
for (let y = 2024; y <= currentYear + 1; y++) {
yearOptions.push(y);
}
return (
<div className="d-flex flex-column">
<div className="d-flex align-items-center justify-content-center p-2">
<h4>Reports Module</h4>
</div>
<div>
<form className="form-control">
<select defaultValue="" onChange={handleYear} className="form-select">
<option value="">Select a year...</option>
{yearOptions.map(y => (
<option key={y} value={y}>{y}</option>
))}
</select>
<select defaultValue="" required onChange={handleMonth} className="form-select">
<option value="">Select a month...</option>
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select defaultValue="" required onChange={handleAccount} className="form-select">
<option value="">Select an account...</option>
<option value="*">*Any*</option>
{accounts.map((acc, index) => (<option key={index} value={acc.short_name}>{acc.full_name}</option>))}
</select>
<button type="button" onClick={fetchServiceVisits} className="btn btn-primary">Fetch Visits</button>
</form>
{loading ? <div className="d-flex align-items-center"><h4 className="m-2 p-2">Loading Reports...</h4><div className="m-2 spinner-border"></div></div> : (
<div className="d-flex m-2 p-2 flex-column align-items-center justify-content-center">
<h4>Closed Visits Detail</h4>
<table className="table">
<thead>
<tr>
<th scope="col" className="w-25">Account</th>
<th scope="col" className="w-25">Visits</th>
<th scope="col" className="w-25">Rate</th>
<th scope="col" className="w-25">Total</th>
</tr>
</thead>
<tbody>
{Object.entries(
reports.reduce(
(count, visit) => {
if (visit.status === "Closed") {
count[visit.short_name] = {
closedVisits: (count[visit.short_name]?.closedVisits || 0) + 1,
fullName: visit.full_name
}
}
return count;
}, {})
).map(
([short_name, count]) => (
<tr key={short_name}>
<th scope="row">{count.fullName}</th>
<td>{count.closedVisits}</td>
<td>{loadingRates ? "Loading..." : (rates[short_name] || "N/A")}</td>
<td>{loadingRates ? "Loading..." : rates[short_name] !== "N/A" ? count.closedVisits * rates[short_name] : "N/A"}</td>
</tr>
)
)}
<tr>
<th colSpan="3">Grand Total:</th>
<td>{grandTotal}</td>
</tr>
</tbody>
</table>
<h4>Open Visits Detail</h4>
<table className="table">
<thead>
<tr>
<th scope="col" className="w-50">Account</th>
<th scope="col" className="w-20">Date</th>
<th scope="col" className="w-20">Notes</th>
</tr>
</thead>
<tbody>
{reports.filter(report => report.status !== 'Closed').map(report => (
<tr key={report.id}> {/* Assuming you have an 'id' field */}
<th scope="row">{report.full_name}</th>
<td>{report.date}</td>
<td>{report.notes ? report.notes : null}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
)
}