diff --git a/my-app/src/routes/groups/graphs/[[id=integer]]/+page.server.ts b/my-app/src/routes/groups/graphs/[[id=integer]]/+page.server.ts index 10b1cd6..d1a71e0 100644 --- a/my-app/src/routes/groups/graphs/[[id=integer]]/+page.server.ts +++ b/my-app/src/routes/groups/graphs/[[id=integer]]/+page.server.ts @@ -1,3 +1,4 @@ +import { formatDateString } from '$lib/formatter'; import { categoryService, groupService, spendingService } from '$lib/server/api'; import type { PageServerLoad } from './$types'; @@ -8,19 +9,73 @@ export const load: PageServerLoad = async ({ params, cookies }) => { : { name: '', description: '', id: 0, owner_id: 0, is_archived: false }; const categories = await categoryService.list(id, cookies); const spendings = await spendingService.list(id, cookies); - const graphData = computePerCategorySpending(categories, spendings); + const graphData = computeGraphData(categories, spendings); return { group, graphData }; }; -function computePerCategorySpending(categories: Category[], spendings: Spending[]) { - const categoryMap = new Map(categories.map((category) => [category.id, 0])); - spendings.forEach(({ amount, category_id }) => { - const acc = categoryMap.get(category_id)!; - categoryMap.set(category_id, acc + amount); +function computeSpendingsByCategory(categories: Category[], spendings: Spending[]) { + const categoryMap = new Map(categories.map((category) => [category.id, []])); + spendings.forEach((spending) => { + const acc = categoryMap.get(spending.category_id)!; + acc.push(spending); }); - const labels = Array.from(categoryMap.keys()).map( + return categoryMap; +} + +function computeGraphData(categories: Category[], spendings: Spending[]) { + const spendingsByCategory = computeSpendingsByCategory(categories, spendings); + const labels = Array.from(spendingsByCategory.keys()).map( (id) => categories.find((c) => c.id === id)!.name ); - const values = Array.from(categoryMap.values()); - return { labels, values }; + const values = Array.from(spendingsByCategory.values()).map((spendings) => + spendings.reduce((acc, s) => acc + s.amount, 0) + ); + const spendingSumByCategory = { labels, values }; + + const spendingsOverTime = computeSpendingsOverTime(categories, spendings, spendingsByCategory); + return { spendingSumByCategory, spendingsOverTime }; +} + +function computeSpendingsOverTime( + categories: Category[], + spendings: Spending[], + spendingsByCategory: Map +) { + const globalSpendingsByDate = computeSpendingsByDate(spendings, new Map()); + + const emptySpendingsByDate = new Map( + Array.from(globalSpendingsByDate.entries()).map(([date, _]) => [date, 0]) + ); + + let datasets = Array.from(spendingsByCategory.entries()).map(([id, spendings]) => { + const label = categories.find((c) => c.id === id)!.name; + const spendingsByDate = computeSpendingsByDate( + spendings, + new Map(emptySpendingsByDate) + ); + const sorted = [...spendingsByDate.entries()].sort( + ([d0, v0], [d1, v1]) => Date.parse(d0) - Date.parse(d1) + ); + const data = sorted.map(([_, v]) => v); + return { label, data }; + }); + const sorted = [...globalSpendingsByDate.entries()].sort( + ([d0, v0], [d1, v1]) => Date.parse(d0) - Date.parse(d1) + ); + const data = sorted.map(([_, v]) => v); + + datasets.push({ label: 'Total', data }); + + const labels = sorted.map(([d, _]) => d); + + return { labels, datasets }; +} + +function computeSpendingsByDate(spendings: Spending[], spendingsByDate: Map) { + spendings.forEach((spending) => { + const date = formatDateString(spending.date); + const acc = spendingsByDate.get(date) || 0; + spendingsByDate.set(date, acc + spending.amount); + }); + return spendingsByDate; } diff --git a/my-app/src/routes/groups/graphs/[[id=integer]]/+page.svelte b/my-app/src/routes/groups/graphs/[[id=integer]]/+page.svelte index 6dd63f6..9968670 100644 --- a/my-app/src/routes/groups/graphs/[[id=integer]]/+page.svelte +++ b/my-app/src/routes/groups/graphs/[[id=integer]]/+page.svelte @@ -6,8 +6,8 @@ export let data: PageData; - export const loadGraph: Action = (canvas) => { - const { labels, values } = data?.graphData; + export const loadSpendingsByCategoryGraph: Action = (canvas) => { + const { labels, values } = data?.graphData.spendingSumByCategory; const label = 'Gastos por categoría'; // NOTE: this is to have each bar be a different color const datasets = values.map((v, i) => { @@ -29,6 +29,25 @@ }); return { destroy: () => chart.destroy() }; }; + + export const loadSpendingsOverTimeGraph: Action = (canvas) => { + const { labels, datasets } = data?.graphData.spendingsOverTime; + + const prettyDatasets = datasets.map((dataset) => { + return { ...dataset, tension: 0.1 }; + }); + + const chart = new Chart(canvas, { + type: 'line', + options: { + plugins: { + colors: { enabled: true, forceOverride: true } + } + }, + data: { labels, datasets: prettyDatasets } + }); + return { destroy: () => chart.destroy() }; + }; @@ -50,4 +69,26 @@ -
+
+
+

Gastos por categoría

+
+
+ + +
+

Consumo a través del tiempo

+
+
+
+ + +
+ +