Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/(auth)/budgets/edit/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import FormSkeleton from "@/components/skeletons/form";

export default function Loading() {
return <FormSkeleton />;
}
28 changes: 6 additions & 22 deletions app/(auth)/budgets/page.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,24 @@
import AddButton from "@/components/add-button";
import BudgetsList from "@/components/budgets/list";
import EmptyList from "@/components/empty-list";
import { BudgetsContainer } from "@/components/budgets/container";
import PageTitle from "@/components/page-title";
import { Card, CardContent } from "@/components/ui/card";
import { getBudgets, getExpensesByCategory } from "@/lib/dal";
import { getCurrentMonthRange } from "@/lib/utils";
import BudgetsSkeleton from "@/components/skeletons/budgets";
import type { Metadata } from "next";
import { Suspense } from "react";

export const metadata: Metadata = {
title: "Budgets"
};

export default async function BudgetsPage() {
const currentDate = new Date();
const monthRange = getCurrentMonthRange(currentDate);

const [budgets, expenses] = await Promise.all([
getBudgets(),
getExpensesByCategory(monthRange.start, monthRange.end)
]);

return (
<>
<div className="flex items-center justify-between">
<PageTitle text="Budgets" />
<AddButton text="Add budget" href="/budgets/new" />
</div>
{budgets.length === 0 ? (
<Card>
<CardContent>
<EmptyList />
</CardContent>
</Card>
) : (
<BudgetsList budgets={budgets} expenses={expenses} />
)}
<Suspense fallback={<BudgetsSkeleton />}>
<BudgetsContainer />
</Suspense>
</>
);
}
76 changes: 19 additions & 57 deletions app/(auth)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { getExpensesByDateRange, getFirstExpense } from "@/lib/dal";
import {
buildChartData,
calculateTotalAmount,
createDateFromDay,
getCurrentMonthRange,
parseSelectedDay,
parseSelectedMonth
} from "@/lib/utils";
import FormattedAmount from "@/components/formatted-amount";
import ExpensesList from "@/components/expenses/list";
import DailyBarChart from "@/components/daily-bar-chart";
import { format, getDate } from "date-fns";
import MonthSelect from "@/components/month-select";
import { filterExpensesByDay } from "@/lib/utils";
import Chart from "@/components/dashboard/chart";
import type { Metadata } from "next";
import EmptyList from "@/components/empty-list";
import PageTitle from "@/components/page-title";
import ChartHeader from "@/components/dashboard/chart-header";
import { getDashboardData } from "@/lib/dashboard-data";

export const metadata: Metadata = {
title: "Dashboard"
Expand All @@ -25,60 +14,33 @@ export const metadata: Metadata = {
export default async function Page({
searchParams
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
searchParams: Promise<{ day?: string; month?: string }>;
}) {
const params = await searchParams;
const currentDate = parseSelectedMonth(params.month) || new Date();
const monthRange = getCurrentMonthRange(currentDate);

const selectedDayParam = parseSelectedDay(params.day);
const highlightedDate = selectedDayParam
? createDateFromDay(selectedDayParam, currentDate)
: null;

const [monthlyExpenses, firstExpense] = await Promise.all([
getExpensesByDateRange(monthRange.start, currentDate),
getFirstExpense()
]);

const totalSpent = calculateTotalAmount(monthlyExpenses);
let selectedExpenses = monthlyExpenses;
let amountSpent = totalSpent / getDate(currentDate);

if (selectedDayParam) {
const dayExpenses = filterExpensesByDay(monthlyExpenses, selectedDayParam);
const dayTotal = calculateTotalAmount(dayExpenses);

selectedExpenses = dayExpenses ?? [];
amountSpent = dayTotal ?? 0;
}

const chartData = buildChartData(monthlyExpenses, getDate(monthRange.end));
const {
totalSpent,
amountSpent,
selectedExpenses,
highlightedDate,
chartData,
monthSelectOptions
} = await getDashboardData(params.month, params.day);

return (
<>
<PageTitle text="Dashboard" />
<Card>
<CardHeader className="flex w-full items-center justify-between">
<div>
<MonthSelect startDate={firstExpense?.createdAt} />
<FormattedAmount
className="text-xl before:text-base"
amount={totalSpent}
negativeValue
/>
</div>
<div>
<p className="text-muted-foreground text-sm font-medium uppercase">
{highlightedDate === null
? "Spent/day"
: format(highlightedDate, "d MMM yyyy")}
</p>
<FormattedAmount className="text-xl before:text-base" amount={amountSpent} />
</div>
<ChartHeader
totalSpent={totalSpent}
amountSpent={amountSpent}
highlightedDate={highlightedDate}
monthSelectOptions={monthSelectOptions}
/>
</CardHeader>
<CardContent className="space-y-8">
<DailyBarChart chartData={chartData} />
<Chart chartData={chartData} />
</CardContent>
</Card>
<Card>
Expand Down
5 changes: 5 additions & 0 deletions app/(auth)/expenses/edit/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import FormSkeleton from "@/components/skeletons/form";

export default function Loading() {
return <FormSkeleton />;
}
37 changes: 28 additions & 9 deletions app/(auth)/expenses/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import PageTitle from "@/components/page-title";
import AddButton from "@/components/add-button";
import PaginatedExpensesList from "@/components/expenses/paginated-list";
import ExpensesListWrapper from "@/components/expenses/list-wrapper";
import { Suspense } from "react";
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
import SearchBar from "@/components/expenses/search-bar";
import { getExpensesPages } from "@/lib/dal";
import PaginationControls from "@/components/pagination-controls";
import ExpensesSkeleton from "@/components/skeletons/expenses";

const PAGE_SIZE = 15;

export const metadata: Metadata = {
title: "Expenses"
Expand All @@ -11,23 +18,35 @@ export const metadata: Metadata = {
export default async function Page({
searchParams
}: {
searchParams: Promise<{ page: string; query: string }>;
searchParams: Promise<{ page?: string; query?: string }>;
}) {
const params = await searchParams;
const page = params.page ? Number(params.page) : 1;
const query = params.query;
const page = Number(params.page) || 1;
const query = params.query || "";

if (isNaN(page) || page < 1) {
notFound();
}
const totalPages = await getExpensesPages(query, PAGE_SIZE);

return (
<>
<div className="flex items-center justify-between">
<PageTitle text="Expenses" />
<AddButton text="Add expense" href="/expenses/new" />
</div>
<PaginatedExpensesList query={query} page={page} />
<Card>
<CardHeader>
<SearchBar />
</CardHeader>
<CardContent className="space-y-8">
<Suspense key={query + page} fallback={<ExpensesSkeleton />}>
<ExpensesListWrapper query={query} currentPage={page} pageSize={PAGE_SIZE} />
</Suspense>
</CardContent>
{totalPages > 1 && (
<CardFooter>
<PaginationControls totalPages={totalPages} />
</CardFooter>
)}
</Card>
</>
);
}
29 changes: 29 additions & 0 deletions components/budgets/container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import BudgetsList from "@/components/budgets/list";
import EmptyList from "@/components/empty-list";
import { Card, CardContent } from "@/components/ui/card";
import { getBudgets, getExpensesByCategory } from "@/lib/dal";
import { getCurrentMonthRange } from "@/lib/utils";

export async function BudgetsContainer() {
const currentDate = new Date();
const monthRange = getCurrentMonthRange(currentDate);

const [budgets, expenses] = await Promise.all([
getBudgets(),
getExpensesByCategory(monthRange.start, monthRange.end)
]);

return (
<>
{budgets.length === 0 ? (
<Card>
<CardContent>
<EmptyList />
</CardContent>
</Card>
) : (
<BudgetsList budgets={budgets} expenses={expenses} />
)}
</>
);
}
36 changes: 36 additions & 0 deletions components/dashboard/chart-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import FormattedAmount from "@/components/formatted-amount";
import { format } from "date-fns";
import MonthSelect from "@/components/dashboard/month-select";

type Props = {
totalSpent: number;
amountSpent: number;
highlightedDate: Date | null;
monthSelectOptions: { value: string; label: string }[];
};

export default async function ChartHeader({
totalSpent,
amountSpent,
highlightedDate,
monthSelectOptions
}: Props) {
return (
<>
<div>
<MonthSelect options={monthSelectOptions} />
<FormattedAmount
className="text-xl before:text-base"
amount={totalSpent}
negativeValue
/>
</div>
<div>
<p className="text-muted-foreground text-sm font-medium uppercase">
{highlightedDate === null ? "Spent/day" : format(highlightedDate, "d MMM yyyy")}
</p>
<FormattedAmount className="text-xl before:text-base" amount={amountSpent} />
</div>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function formatToK(num: number) {
return num.toString();
}

export default function DailyBarChart({ chartData }: Props) {
export default function Chart({ chartData }: Props) {
const searchParams = useSearchParams();
const router = useRouter();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,22 @@ import {
SelectTrigger,
SelectValue
} from "@/components/ui/select";
import { useEffect, useState } from "react";
import { buildMonthSelectOptions } from "@/lib/utils";
import { format } from "date-fns";

const now = new Date();
const defaultOptions = [
{
label: format(now, "MMM yyyy"),
value: format(now, "yyyy-MM")
}
];
type Props = {
options: {
value: string;
label: string;
}[];
};

export default function MonthSelect({ startDate }: { startDate?: Date }) {
const [options, setOptions] =
useState<Array<{ value: string; label: string }>>(defaultOptions);
export default function MonthSelect({ options }: Props) {
const searchParams = useSearchParams();
const router = useRouter();
const selectedMonth = searchParams.get("month");

useEffect(() => {
if (!startDate) {
return;
}
const monthOptions = buildMonthSelectOptions(startDate);
setOptions(monthOptions);
}, [startDate]);

const handleChange = (value: string) => {
const params = new URLSearchParams();
if (value !== defaultOptions[0].value) {
params.set("month", value);
}
params.set("month", value);
router.push(`?${params.toString()}`);
};

Expand Down
21 changes: 21 additions & 0 deletions components/expenses/list-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getPaginatedExpenses } from "@/lib/dal";
import EmptyList from "@/components/empty-list";
import ExpensesList from "@/components/expenses/list";

export default async function ExpensesListWrapper({
query,
currentPage,
pageSize
}: {
query?: string;
currentPage: number;
pageSize: number;
}) {
const expenses = await getPaginatedExpenses(currentPage, pageSize, query);

if (expenses.length === 0) {
return <EmptyList />;
}

return <ExpensesList expenses={expenses} />;
}
Loading