feat(reports): live firefly reports with drill-down, money flow and spending calendar
This commit is contained in:
1 parent
60caee10dc
commit
62cd17ecde
39 files changed
+6596
No files matched your search
@@ -0,0 +1,135 @@
|
||||
<script lang="ts">
|
||||
import { createQuery } from "@tanstack/svelte-query";
|
||||
import { reportQueries } from "#lib/reports/queries.js";
|
||||
import {
|
||||
drillQuery,
|
||||
money,
|
||||
navigate,
|
||||
useReport,
|
||||
} from "#lib/reports/report.svelte.js";
|
||||
import ReportBox from "./ReportBox.svelte";
|
||||
|
||||
const report = useReport();
|
||||
const p = $derived(report.params);
|
||||
const balances = createQuery(() => reportQueries.balances(p.start, p.end));
|
||||
|
||||
const rows = $derived.by(() => {
|
||||
const { data } = balances;
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
return report.selected
|
||||
.map((id) => {
|
||||
const account = report.byId().get(id);
|
||||
const start = data.before.get(id) ?? 0;
|
||||
const end = data.after.get(id) ?? 0;
|
||||
return {
|
||||
code: account?.currency_code ?? "",
|
||||
end,
|
||||
id,
|
||||
name: account?.name ?? `#${id}`,
|
||||
start,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
});
|
||||
const sums = $derived.by(() => {
|
||||
const map = new Map<string, { start: number; end: number }>();
|
||||
for (const row of rows) {
|
||||
const sum = map.get(row.code) ?? { end: 0, start: 0 };
|
||||
sum.start += row.start;
|
||||
sum.end += row.end;
|
||||
map.set(row.code, sum);
|
||||
}
|
||||
const order = report.codes;
|
||||
return [...map.entries()].sort(
|
||||
(a, b) => order.indexOf(a[0]) - order.indexOf(b[0])
|
||||
);
|
||||
});
|
||||
|
||||
function tone(value: number) {
|
||||
if (value === 0) {
|
||||
return "text-cladd-fg-soft";
|
||||
}
|
||||
return value < 0 ? "text-expense" : "text-income";
|
||||
}
|
||||
|
||||
function open(row: { id: string; name: string }) {
|
||||
const current = report.drill;
|
||||
const same = current?.box === "balances" && current.key === row.id;
|
||||
navigate(
|
||||
drillQuery(
|
||||
same
|
||||
? null
|
||||
: { box: "balances", dim: "asset", key: row.id, label: row.name }
|
||||
)
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<ReportBox
|
||||
id="balances"
|
||||
loading={!(balances.data && report.accountsReady)}
|
||||
title="Account balances"
|
||||
>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full border-collapse text-[13px]">
|
||||
<thead>
|
||||
<tr class="text-[12px] text-cladd-fg-soft">
|
||||
<th class="h-7 px-3 text-left font-medium">Name</th>
|
||||
<th class="h-7 px-3 text-right font-medium">
|
||||
Balance at start of period
|
||||
</th>
|
||||
<th class="h-7 px-3 text-right font-medium">
|
||||
Balance at end of period
|
||||
</th>
|
||||
<th class="h-7 px-3 text-right font-medium">Difference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each rows as row (row.id)}
|
||||
{@const active =
|
||||
report.drill?.box === "balances" && report.drill.key === row.id}
|
||||
<tr
|
||||
class="h-7 border-cladd-outline border-t hover:bg-row-hover {active
|
||||
? "bg-row-selected"
|
||||
: ""}"
|
||||
>
|
||||
<td class="max-w-[16rem] px-3">
|
||||
<button
|
||||
aria-pressed={active}
|
||||
class="block max-w-full truncate text-left hover:text-brand dark:hover:text-[var(--ff-series-1)] hover:underline"
|
||||
onclick={() => open(row)}
|
||||
title="Show transactions"
|
||||
type="button"
|
||||
>
|
||||
{row.name}
|
||||
</button>
|
||||
</td>
|
||||
<td class="num px-3 text-right">{money(row.code, row.start)}</td>
|
||||
<td class="num px-3 text-right">{money(row.code, row.end)}</td>
|
||||
<td class="num px-3 text-right {tone(row.end - row.start)}">
|
||||
{money(row.code, row.end - row.start, true)}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
{#each sums as [code, sum], i (code)}
|
||||
<tr
|
||||
class="h-7 font-semibold {i === 0
|
||||
? "border-cladd-outline border-t-2"
|
||||
: ""}"
|
||||
>
|
||||
<td class="px-3">Sum ({code})</td>
|
||||
<td class="num px-3 text-right">{money(code, sum.start)}</td>
|
||||
<td class="num px-3 text-right">{money(code, sum.end)}</td>
|
||||
<td class="num px-3 text-right {tone(sum.end - sum.start)}">
|
||||
{money(code, sum.end - sum.start, true)}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</ReportBox>
|
||||
Reference in new issue
Block a user