diff --git a/lib/data-utils.ts b/lib/data-utils.ts index 91a17f6..5f720c8 100644 --- a/lib/data-utils.ts +++ b/lib/data-utils.ts @@ -378,6 +378,27 @@ export async function getExpensesByMonth(userId: string, month: string) { return expenses; } +export async function getExpensesForLastYear(userId: string) { + const twelveMonthsAgoMonth = new Date(new Date().setUTCMonth(new Date().getUTCMonth() - 12)).toISOString().substring( + 0, + 7, + ); + const currentMonth = new Date().toISOString().substring(0, 7); + + const expenses = await db.query( + sql`SELECT * FROM "budgetzen_expenses" + WHERE "user_id" = $1 AND + "date" >= '${twelveMonthsAgoMonth}-01' AND + "date" <= '${currentMonth}-31' + ORDER BY "date" DESC, "id" DESC`, + [ + userId, + ], + ); + + return expenses; +} + export async function createExpense(expense: Omit) { const newExpense = (await db.query( sql`INSERT INTO "budgetzen_expenses" ( diff --git a/pages/api/expenses.ts b/pages/api/expenses.ts index de937d2..48f6e0c 100644 --- a/pages/api/expenses.ts +++ b/pages/api/expenses.ts @@ -3,6 +3,7 @@ import { deleteExpense, getAllExpenses, getExpensesByMonth, + getExpensesForLastYear, monthRegExp, updateExpense, validateUserAndSession, @@ -80,13 +81,21 @@ export async function pageContent(request: Request, _match: URLPatternResult) { const userId = urlSearchParams.get('user_id'); const month = urlSearchParams.get('month'); - if (!sessionId || !userId || !month || (!monthRegExp.test(month) && month !== 'all')) { + if (!sessionId || !userId || !month || (!monthRegExp.test(month) && month !== 'all' && month !== 'year')) { return new Response('Bad Request', { status: 400 }); } const { user } = await validateUserAndSession(userId, sessionId); - const events = await (month === 'all' ? getAllExpenses(user.id) : getExpensesByMonth(user.id, month)); + let expenses: Expense[] = []; - return new Response(JSON.stringify(events), { headers: { 'Content-Type': 'application/json; charset=utf-8' } }); + if (month === 'all') { + expenses = await getAllExpenses(user.id); + } else if (month === 'year') { + expenses = await getExpensesForLastYear(user.id); + } else { + expenses = await getExpensesByMonth(user.id, month); + } + + return new Response(JSON.stringify(expenses), { headers: { 'Content-Type': 'application/json; charset=utf-8' } }); } diff --git a/public/css/style.css b/public/css/style.css index b85319e..d4ef83a 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -555,6 +555,7 @@ a.button.secondary:focus { .expenses-wrapper .expenses-filter-wrapper { display: flex; margin: 20px 0.5rem; + justify-content: space-between; } #expenses-filter .input-wrapper { diff --git a/public/ts/index.ts b/public/ts/index.ts index 5ece9fa..7351266 100644 --- a/public/ts/index.ts +++ b/public/ts/index.ts @@ -207,11 +207,11 @@ document.addEventListener('app-loaded', async () => { // IIFE because we don't want to wait for all expenses in the main event-loop, but need it to get unique names (async () => { - const allExpenses = await fetchExpenses('all'); - const allExpenseNames: string[] = []; // This is necessary because you can't sort a Set, but it follows insertion order - allExpenses.forEach((expense) => allExpenseNames.push(expense.description)); - allExpenseNames.sort(); - allExpenseNames.forEach((expenseName) => uniqueExpenseNames.add(expenseName)); + const yearExpenses = await fetchExpenses('year'); + const recentExpenseNames: string[] = []; // This is necessary because you can't sort a Set, but it follows insertion order + yearExpenses.forEach((expense) => recentExpenseNames.push(expense.description)); + recentExpenseNames.sort(); + recentExpenseNames.forEach((expenseName) => uniqueExpenseNames.add(expenseName)); })(); const budgetOptions = new Set([{ name: 'Misc' }, ...allBudgets].map((budget) => budget.name));