244 lines
10 KiB
JavaScript
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>
|
|
)
|
|
}
|