feat(dashboard,accounts,charts): v1 dashboard redrawn, account lists and pages, svg chart kit
This commit is contained in:
1 parent
62cd17ecde
commit
9e89829bae
36 files changed
+5678
-16
No files matched your search
@@ -1,5 +1,96 @@
|
||||
<script lang="ts">
|
||||
import { createQuery, keepPreviousData } from "@tanstack/svelte-query";
|
||||
import AccountBox from "#lib/components/dashboard/AccountBox.svelte";
|
||||
import AccountsChart from "#lib/components/dashboard/AccountsChart.svelte";
|
||||
import BudgetsChart from "#lib/components/dashboard/BudgetsChart.svelte";
|
||||
import CategoriesChart from "#lib/components/dashboard/CategoriesChart.svelte";
|
||||
import DebtsBox from "#lib/components/dashboard/DebtsBox.svelte";
|
||||
import {
|
||||
accountsChartQuery,
|
||||
accountsQuery,
|
||||
seriesPoints,
|
||||
} from "#lib/components/dashboard/data.js";
|
||||
import InfoBoxes from "#lib/components/dashboard/InfoBoxes.svelte";
|
||||
import InsightBox from "#lib/components/dashboard/InsightBox.svelte";
|
||||
import PiggyBanksBox from "#lib/components/dashboard/PiggyBanksBox.svelte";
|
||||
import SubscriptionsBox from "#lib/components/dashboard/SubscriptionsBox.svelte";
|
||||
import PageHeader from "#lib/components/shell/PageHeader.svelte";
|
||||
import { today } from "#lib/dates.js";
|
||||
import { period } from "#lib/period.svelte.js";
|
||||
import { session } from "#lib/session.svelte.js";
|
||||
|
||||
const assets = createQuery(() => ({
|
||||
...accountsQuery("asset", period.start, period.end),
|
||||
placeholderData: keepPreviousData,
|
||||
}));
|
||||
const chart = createQuery(() => ({
|
||||
...accountsChartQuery(period.start, period.end),
|
||||
placeholderData: keepPreviousData,
|
||||
}));
|
||||
|
||||
const frontpage = $derived.by(() => {
|
||||
const list = (assets.data ?? [])
|
||||
.filter((a) => a.attributes.active !== false)
|
||||
.sort((a, b) => (a.attributes.order ?? 0) - (b.attributes.order ?? 0));
|
||||
if (!("frontpageAccounts" in session.prefs)) {
|
||||
return list;
|
||||
}
|
||||
const ids = new Set(session.frontpageAccounts);
|
||||
return list.filter((a) => ids.has(a.id));
|
||||
});
|
||||
|
||||
const trends = $derived(
|
||||
new Map(
|
||||
(chart.data ?? []).map((set) => {
|
||||
const points = seriesPoints(set, false);
|
||||
return [
|
||||
set.label,
|
||||
{
|
||||
marker: points.findIndex((p) => p.x === today()),
|
||||
values: points.map((p) => p.y),
|
||||
},
|
||||
];
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
if (
|
||||
assets.isSuccess &&
|
||||
!assets.isPlaceholderData &&
|
||||
assets.data.length === 0
|
||||
) {
|
||||
location.assign("/new-user");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<PageHeader subtitle="What's playing?" title="Dashboard" />
|
||||
|
||||
<InfoBoxes />
|
||||
|
||||
<div class="mt-3 grid gap-3 lg:grid-cols-12">
|
||||
<div class="flex min-w-0 flex-col gap-3 lg:col-span-8">
|
||||
<AccountsChart />
|
||||
<BudgetsChart />
|
||||
<CategoriesChart />
|
||||
</div>
|
||||
<div class="flex min-w-0 flex-col gap-3 lg:col-span-4">
|
||||
{#each frontpage as account (account.id)}
|
||||
{@const trend = trends.get(account.attributes.name)}
|
||||
<AccountBox
|
||||
{account}
|
||||
marker={trend?.marker ?? -1}
|
||||
trend={trend?.values ?? []}
|
||||
/>
|
||||
{/each}
|
||||
<SubscriptionsBox />
|
||||
<PiggyBanksBox />
|
||||
<DebtsBox />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex flex-col gap-3">
|
||||
<InsightBox kind="expense" />
|
||||
<InsightBox kind="revenue" />
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
import { redirect } from "@sveltejs/kit";
|
||||
|
||||
export function load() {
|
||||
redirect(307, "/accounts/asset");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { AccountType } from "#lib/api/types.js";
|
||||
import AccountsIndex from "#lib/components/accounts/AccountsIndex.svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const type = $derived(page.params.type as AccountType);
|
||||
</script>
|
||||
|
||||
{#key type}
|
||||
<AccountsIndex {type} />
|
||||
{/key}
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import type { AccountType } from "#lib/api/types.js";
|
||||
import AccountForm from "#lib/components/accounts/AccountForm.svelte";
|
||||
import {
|
||||
indexUrl,
|
||||
TYPE_ONE,
|
||||
TYPE_TITLE,
|
||||
} from "#lib/components/accounts/links.js";
|
||||
import PageHeader from "#lib/components/shell/PageHeader.svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const type = $derived(page.params.type as AccountType);
|
||||
</script>
|
||||
|
||||
<PageHeader
|
||||
crumbs={[
|
||||
{ href: indexUrl(type), label: TYPE_TITLE[type] },
|
||||
{ label: `Create a new ${TYPE_ONE[type]}` },
|
||||
]}
|
||||
title="Create a new {TYPE_ONE[type]}"
|
||||
/>
|
||||
|
||||
{#key type}
|
||||
<AccountForm {type} />
|
||||
{/key}
|
||||
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { createQuery } from "@tanstack/svelte-query";
|
||||
import { api } from "#lib/api/client.js";
|
||||
import type { Account, One } from "#lib/api/types.js";
|
||||
import AccountForm from "#lib/components/accounts/AccountForm.svelte";
|
||||
import {
|
||||
indexUrl,
|
||||
showUrl,
|
||||
TYPE_TITLE,
|
||||
typeOf,
|
||||
} from "#lib/components/accounts/links.js";
|
||||
import PageHeader from "#lib/components/shell/PageHeader.svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const id = $derived(page.params.id ?? "");
|
||||
const account = createQuery(() => ({
|
||||
queryFn: () => api<One<Account>>(`/v1/accounts/${id}`),
|
||||
queryKey: ["account-edit", id],
|
||||
staleTime: 0,
|
||||
}));
|
||||
const item = $derived(account.data?.data);
|
||||
const type = $derived(item ? typeOf(item.attributes) : "asset");
|
||||
</script>
|
||||
|
||||
<PageHeader
|
||||
crumbs={[
|
||||
{ href: indexUrl(type), label: TYPE_TITLE[type] },
|
||||
{ href: showUrl(id), label: item?.attributes.name ?? "" },
|
||||
{ label: "Edit" },
|
||||
]}
|
||||
title={item ? `Edit ${item.attributes.name}` : "Edit account"}
|
||||
/>
|
||||
|
||||
{#if account.isError}
|
||||
<p class="text-expense">{account.error.message}</p>
|
||||
{:else if item}
|
||||
{#key item.id}
|
||||
<AccountForm account={item} {type} />
|
||||
{/key}
|
||||
{/if}
|
||||
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import Reconcile from "#lib/components/accounts/Reconcile.svelte";
|
||||
import { period } from "#lib/period.svelte.js";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const id = $derived(page.params.id ?? "");
|
||||
const start = $derived(page.params.start ?? period.start);
|
||||
const end = $derived(page.params.end ?? period.end);
|
||||
</script>
|
||||
|
||||
{#key `${id}-${start}-${end}`}
|
||||
<Reconcile {end} {id} {start} />
|
||||
{/key}
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { untrack } from "svelte";
|
||||
import AccountShow from "#lib/components/accounts/AccountShow.svelte";
|
||||
import { period } from "#lib/period.svelte.js";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const id = $derived(page.params.id ?? "");
|
||||
const start = $derived(page.params.start);
|
||||
const end = $derived(page.params.end);
|
||||
|
||||
let applied = "";
|
||||
|
||||
$effect(() => {
|
||||
const key = `${start}|${end}`;
|
||||
if (start && end && key !== applied) {
|
||||
applied = key;
|
||||
period.set(start <= end ? { end, start } : { end: start, start: end });
|
||||
return;
|
||||
}
|
||||
const moved = period.start !== start || period.end !== end;
|
||||
untrack(() => {
|
||||
if (start && end && moved) {
|
||||
goto(`/accounts/show/${id}`, { replace: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{#key id}
|
||||
<AccountShow {id} />
|
||||
{/key}
|
||||
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import AccountShow from "#lib/components/accounts/AccountShow.svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const id = $derived(page.params.id ?? "");
|
||||
</script>
|
||||
|
||||
{#key id}
|
||||
<AccountShow everything {id} />
|
||||
{/key}
|
||||
@@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { AccountType } from "#lib/api/types.js";
|
||||
import AccountsIndex from "#lib/components/accounts/AccountsIndex.svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const type = $derived(page.params.type as AccountType);
|
||||
</script>
|
||||
|
||||
{#key type}
|
||||
<AccountsIndex active={false} {type} />
|
||||
{/key}
|
||||
+16
-16
@@ -16,14 +16,14 @@
|
||||
--ff-row-hover: oklch(0.96 0.012 240);
|
||||
--ff-row-cursor: oklch(0.93 0.03 240);
|
||||
--ff-row-selected: oklch(0.9 0.05 240);
|
||||
--ff-series-1: oklch(0.57 0.105 240);
|
||||
--ff-series-2: oklch(0.62 0.13 160);
|
||||
--ff-series-3: oklch(0.7 0.14 70);
|
||||
--ff-series-4: oklch(0.58 0.15 20);
|
||||
--ff-series-5: oklch(0.55 0.12 300);
|
||||
--ff-series-6: oklch(0.65 0.1 200);
|
||||
--ff-series-7: oklch(0.6 0.13 110);
|
||||
--ff-series-8: oklch(0.6 0.08 260);
|
||||
--ff-series-1: #1777b8;
|
||||
--ff-series-2: #cb6620;
|
||||
--ff-series-3: #008f7d;
|
||||
--ff-series-4: #b08000;
|
||||
--ff-series-5: #bc4b87;
|
||||
--ff-series-6: #3f842e;
|
||||
--ff-series-7: #6b4cae;
|
||||
--ff-series-8: #b63132;
|
||||
}
|
||||
|
||||
.cladd-color-brand {
|
||||
@@ -41,14 +41,14 @@
|
||||
--ff-row-hover: oklch(0.25 0.012 240);
|
||||
--ff-row-cursor: oklch(0.29 0.03 240);
|
||||
--ff-row-selected: oklch(0.33 0.06 240);
|
||||
--ff-series-1: oklch(0.7 0.1 240);
|
||||
--ff-series-2: oklch(0.72 0.12 160);
|
||||
--ff-series-3: oklch(0.8 0.13 75);
|
||||
--ff-series-4: oklch(0.7 0.13 25);
|
||||
--ff-series-5: oklch(0.7 0.11 300);
|
||||
--ff-series-6: oklch(0.75 0.09 200);
|
||||
--ff-series-7: oklch(0.75 0.12 115);
|
||||
--ff-series-8: oklch(0.72 0.07 260);
|
||||
--ff-series-1: #348dcf;
|
||||
--ff-series-2: #d5753a;
|
||||
--ff-series-3: #0f9b89;
|
||||
--ff-series-4: #b68b16;
|
||||
--ff-series-5: #cd6199;
|
||||
--ff-series-6: #4e943e;
|
||||
--ff-series-7: #876cca;
|
||||
--ff-series-8: #c44844;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
|
||||
Reference in new issue
Block a user