feat: add statistics

This commit is contained in:
Nicola Zambello 2022-02-21 19:31:02 +01:00
parent 60b551801a
commit b6674bccd7

View file

@ -1,21 +1,99 @@
import type { User, Team } from "@prisma/client"; import type { User, Team, Expense } from "@prisma/client";
import type { LoaderFunction } from "remix"; import type { LoaderFunction } from "remix";
import { redirect, Link, useLoaderData, useCatch } from "remix"; import { redirect, useLoaderData, useCatch, Link } from "remix";
import { getUser } from "~/utils/session.server"; import { db } from "~/utils/db.server";
import { getUser, requireUserId } from "~/utils/session.server";
import Header from "../components/Header"; import Header from "../components/Header";
type LoaderData = { type LoaderData = {
user: (User & { team: Team & { members: User[] } }) | null; user: (User & { team: Team & { members: User[] } }) | null;
thisMonth: {
count: number;
amount: number;
};
count: number;
avg: number;
statsByMonth: {
[month: string]: {
count: number;
amount: number;
};
};
}; };
export const loader: LoaderFunction = async ({ request }) => { export const loader: LoaderFunction = async ({ request }) => {
const userId = requireUserId(request);
const user = await getUser(request); const user = await getUser(request);
if (!user?.id) { if (!user?.id || !userId) {
return redirect("/login"); return redirect("/login");
} }
const expenses = await db.expense.aggregate({
_avg: {
amount: true,
},
_count: {
_all: true,
},
where: { userId: user.id },
orderBy: {
createdAt: "asc",
},
});
let thisMonth = new Date();
thisMonth.setDate(0);
const thisMonthExp = await db.expense.aggregate({
_avg: {
amount: true,
},
_count: {
_all: true,
},
where: { userId: user.id, createdAt: { gt: thisMonth } },
orderBy: {
createdAt: "asc",
},
});
const allExpenses = await db.expense.findMany({
where: { userId: user.id },
});
const statsByMonth = allExpenses.reduce(
(
acc: { [key: string]: { count: number; amount: number } },
exp: Expense
) => {
const month = new Intl.DateTimeFormat("it", {
month: "2-digit",
year: "numeric",
}).format(new Date(exp.createdAt));
if (!acc[month]) {
acc[month] = {
count: 0,
amount: 0,
};
}
acc[month].count += 1;
acc[month].amount += exp.amount;
return acc;
},
{}
);
console.log(statsByMonth);
const data: LoaderData = { const data: LoaderData = {
user, user,
thisMonth: {
count: thisMonthExp?._count?._all ?? 0,
amount: thisMonthExp?._avg?.amount ?? 0,
},
count: expenses._count._all ?? 0,
avg: expenses._avg?.amount ?? 0,
statsByMonth,
}; };
return data; return data;
}; };
@ -26,33 +104,52 @@ export default function ListExpensesRoute() {
return ( return (
<> <>
<Header user={data.user} route="/expenses" /> <Header user={data.user} route="/expenses" />
<div className="hero py-40 bg-base-200 my-8 rounded-box"> <main className="container mx-auto">
<div className="text-center hero-content"> <h1 className="mb-10 mt-6 text-4xl font-bold">Statistics</h1>
<div className="max-w-md">
<h1 className="mb-5 text-5xl font-bold">Work in progress</h1> <div className="shadow-xl flex flex-wrap w-full rounded-box bg-base-100 overflow-hidden">
<p className="mb-5"> <div className="stat w-full sm:w-1/2 md:w-1/3">
<button className="btn btn-lg loading"></button> <div className="stat-title">Expenses count</div>
This page is under construction. <div className="stat-value">{data.count}</div>
</p> <div className="stat-desc"></div>
<Link to="/expenses" className="btn btn-primary"> </div>
<svg
xmlns="http://www.w3.org/2000/svg" <div className="stat w-full sm:w-1/2 md:w-1/3">
fill="none" <div className="stat-title">Average per month</div>
viewBox="0 0 24 24" <div className="stat-value">{data.avg} </div>
className="inline-block w-6 h-6 mr-2 stroke-current rotate-180" </div>
>
<path <div className="stat w-full sm:w-1/2 md:w-1/3">
stroke-linecap="round" <div className="stat-title">This month</div>
stroke-linejoin="round" <div className="stat-value">{data.thisMonth.amount} </div>
stroke-width="2" <div className="stat-desc">{data.thisMonth.count} expenses</div>
d="M9 5l7 7-7 7"
></path>
</svg>
Back
</Link>
</div> </div>
</div> </div>
</div>
<h2 className="mt-12 mb-8 text-2xl font-bold">Expenses by month</h2>
<div className="shadow-xl bg-base-100 rounded-box overflow-hidden">
<div className="overflow-x-auto">
<table className="table table-zebra w-full">
<thead>
<tr>
<th>Month</th>
<th>Total</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{Object.keys(data.statsByMonth)?.map((month) => (
<tr key={month}>
<td className="capitalize">{month}</td>
<td>{data.statsByMonth[month].amount} </td>
<td>{data.statsByMonth[month].count}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</main>
</> </>
); );
} }