0 ? "text-error" : "text-success"
}`}
>
- {Math.abs(user.dueAmount)} €
+ {user.dueAmount !== Math.round(user.dueAmount)
+ ? Math.abs(user.dueAmount).toFixed(2)
+ : Math.abs(user.dueAmount)}{" "}
+ €
diff --git a/app/routes/expenses/list.tsx b/app/routes/expenses/list.tsx
index c787b8f..b711a8e 100644
--- a/app/routes/expenses/list.tsx
+++ b/app/routes/expenses/list.tsx
@@ -1,33 +1,262 @@
-import { Link } from "remix";
+import type { User, Team, Expense } from "@prisma/client";
+import type { LoaderFunction } from "remix";
+import { redirect, useLoaderData, useCatch, Link, Form } from "remix";
+import Filter from "~/icons/Filter";
+import { db } from "~/utils/db.server";
+import { getUser, requireUserId } from "~/utils/session.server";
+
+type LoaderData = {
+ user: (User & { team: Team & { members: User[] } }) | null;
+ expenses: (Expense & { user: User & { team: Team } })[];
+ expensesCount: number;
+ page: number;
+ filters: {
+ description: string | null | undefined;
+ dateFrom: string | null | undefined;
+ dateTo: string | null | undefined;
+ user: string | null | undefined;
+ };
+};
+
+export const loader: LoaderFunction = async ({ request }) => {
+ const userId = requireUserId(request);
+ const user = await getUser(request);
+ if (!user?.id || !userId) {
+ return redirect("/login");
+ }
+
+ const expensesCount = await db.expense.count({
+ where: { teamId: user.teamId },
+ });
+
+ const searchParams = new URL(request.url)?.searchParams;
+ const page = parseInt(searchParams.get("page") || "1", 10);
+ const description = searchParams.get("description");
+ const dateFrom = searchParams.get("dateFrom");
+ const dateTo = searchParams.get("dateTo");
+ const userIdParam = searchParams.get("user");
+
+ const filters = {
+ description:
+ description && description.length > 0 ? description : undefined,
+ dateFrom: dateFrom && dateFrom.length > 0 ? dateFrom : undefined,
+ dateTo: dateTo && dateTo.length > 0 ? dateTo : undefined,
+ user: userIdParam && userIdParam?.length > 0 ? userIdParam : undefined,
+ };
+ const expensesFilters = {
+ ...(filters.description && {
+ description: { contains: filters.description },
+ }),
+ ...((filters.dateFrom || filters.dateTo) && {
+ createdAt: {
+ ...(filters.dateFrom && {
+ gte: new Date(`${filters.dateFrom}T00:00:00+0100`),
+ }),
+ ...(filters.dateTo && {
+ lte: new Date(`${filters.dateTo}T00:00:00+0100`),
+ }),
+ },
+ }),
+ ...(filters.user && { userId: filters.user }),
+ };
+ console.log("FILTERS", filters);
+
+ const expenses = await db.expense.findMany({
+ where: {
+ teamId: user.teamId,
+ ...expensesFilters,
+ },
+ take: 10,
+ skip: (page - 1) * 10,
+ orderBy: {
+ createdAt: "desc",
+ },
+ include: {
+ user: {
+ include: {
+ team: true,
+ },
+ },
+ },
+ });
+
+ const data: LoaderData = {
+ user,
+ expenses,
+ expensesCount,
+ page,
+ filters,
+ };
+ return data;
+};
export default function ListExpensesRoute() {
+ const data = useLoaderData
();
+
+ const hasFilters = Object.values(data.filters).some(
+ (value) => value !== undefined && value !== null
+ );
+
return (
-
-
-
-
Work in progress
-
-
- This page is under construction.
-
-
-
- Back
-
+ <>
+
List expenses
+
+
+
+
+
-
+
+
+
+
+
+ |
+ Date |
+ User |
+ Amount |
+ Description |
+
+
+
+ {data.expenses?.map((exp) => (
+
+ |
+
+ See
+
+ |
+
+ {new Intl.DateTimeFormat("it", {
+ dateStyle: "short",
+ }).format(new Date(exp.createdAt))}
+ |
+ {exp.user.username} |
+ {exp.amount} € |
+ {exp.description} |
+
+ ))}
+
+
+
+
+ {data.expensesCount > 10 && (
+
+ {[...new Array(Math.ceil(data.expensesCount / 10)).keys()].map(
+ (p) => (
+
+ {p + 1}
+
+ )
+ )}
+
+ )}
+ >
);
}
+
+export function CatchBoundary() {
+ const caught = useCatch();
+
+ if (caught.status === 401) {
+ return redirect("/login");
+ }
+ if (caught.status === 404) {
+ return
There is no data to display.
;
+ }
+ throw new Error(`Unexpected caught response with status: ${caught.status}`);
+}
+
+export function ErrorBoundary() {
+ return
I did a whoopsies.
;
+}
diff --git a/app/routes/statistics.tsx b/app/routes/statistics.tsx
index 07631b4..8968701 100644
--- a/app/routes/statistics.tsx
+++ b/app/routes/statistics.tsx
@@ -83,8 +83,6 @@ export const loader: LoaderFunction = async ({ request }) => {
{}
);
- console.log(statsByMonth);
-
const data: LoaderData = {
user,
thisMonth: {
@@ -116,12 +114,14 @@ export default function ListExpensesRoute() {
Average per month
-
{data.avg} €
+
{data.avg.toFixed(2)} €
This month
-
{data.thisMonth.amount} €
+
+ {data.thisMonth.amount.toFixed(2)} €
+
{data.thisMonth.count} expenses