Skip to content

Commit

Permalink
Merge pull request #42 from VaquitApp/spendings-over-time-graph
Browse files Browse the repository at this point in the history
Consumo a través del tiempo [5]
  • Loading branch information
MegaRedHand authored Jun 5, 2024
2 parents f25167c + 228addc commit dab9d73
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 12 deletions.
73 changes: 64 additions & 9 deletions my-app/src/routes/groups/graphs/[[id=integer]]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { formatDateString } from '$lib/formatter';
import { categoryService, groupService, spendingService } from '$lib/server/api';
import type { PageServerLoad } from './$types';

Expand All @@ -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<number, number>(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<Id, Spending[]>(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<Id, Spending[]>
) {
const globalSpendingsByDate = computeSpendingsByDate(spendings, new Map<string, number>());

const emptySpendingsByDate = new Map<string, number>(
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<string, number>(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<string, number>) {
spendings.forEach((spending) => {
const date = formatDateString(spending.date);
const acc = spendingsByDate.get(date) || 0;
spendingsByDate.set(date, acc + spending.amount);
});
return spendingsByDate;
}
47 changes: 44 additions & 3 deletions my-app/src/routes/groups/graphs/[[id=integer]]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
export let data: PageData;
export const loadGraph: Action<HTMLCanvasElement> = (canvas) => {
const { labels, values } = data?.graphData;
export const loadSpendingsByCategoryGraph: Action<HTMLCanvasElement> = (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) => {
Expand All @@ -29,6 +29,25 @@
});
return { destroy: () => chart.destroy() };
};
export const loadSpendingsOverTimeGraph: Action<HTMLCanvasElement> = (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() };
};
</script>

<svelte:head>
Expand All @@ -50,4 +69,26 @@
</div>
</header>

<div style="width: 600px;"><canvas use:loadGraph></canvas></div>
<div class="row">
<div>
<h4>Gastos por categoría</h4>
<div style="width: 600px;"><canvas use:loadSpendingsByCategoryGraph></canvas></div>
</div>
<!-- spacing -->
<span style="padding: 24px"></span>
<div>
<h4>Consumo a través del tiempo</h4>
<div style="width: 600px;"><canvas use:loadSpendingsOverTimeGraph></canvas></div>
</div>
</div>

<!-- to allow scrolling down a bit -->
<div style="padding: 100px"></div>

<style>
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
</style>

0 comments on commit dab9d73

Please sign in to comment.