fix(budgets,tags,piggy-banks,dashboard): live buttons that answer in place, lucide icons, mobile wrapping

This commit is contained in:
hh committed 2026-10-16 04:26:07 +00:00
1 parent aba2509399
commit f3a603648e
10 files changed
+102 -71

No files matched your search

+18 -5
View File
@@ -56,6 +56,7 @@
let busy = $state(false);
let status = $state("");
let failure = $state("");
let pressed = $state(false);
const account = createQuery(() => ({
queryFn: () => api<One<Account>>(`/v1/accounts/${id}`),
@@ -179,6 +180,17 @@
const canCorrect = $derived(Boolean(reconciliationId));
const correcting = $derived(correct && canCorrect && difference !== 0);
const ready = $derived(before.isSuccess && after.isSuccess && list.isSuccess);
const hint = $derived.by(() => {
if (!pressed) {
return "";
}
if (!ready) {
return "Balances are still loading";
}
return selected.length === 0 && !correcting
? "Tick the transactions to clear first"
: "";
});
function setDates(nextStart: string, nextEnd: string) {
if (!(nextStart && nextEnd) || nextStart > nextEnd) {
@@ -241,7 +253,8 @@
}
async function store() {
if (busy || !ready || (selected.length === 0 && !correcting)) {
pressed = true;
if (busy || hint) {
return;
}
busy = true;
@@ -525,10 +538,7 @@
</fieldset>
{/if}
<div class="flex flex-wrap items-center gap-2">
<Button
disabled={busy || !ready || (selected.length === 0 && !correcting)}
onclick={store}
type="button"
<Button disabled={busy} onclick={store} type="button"
>Store reconciliation</Button
>
<span class="flex gap-1">
@@ -536,6 +546,9 @@
<span class="kbd">{key}</span>
{/each}
</span>
{#if hint}
<span class="text-[12px] text-expense" role="alert">{hint}</span>
{/if}
</div>
{#if status}
<p class="text-[12px] text-cladd-fg-soft" role="status">{status}</p>
+10 -8
View File
@@ -114,7 +114,7 @@
</div>
{:else}
<div
class="truncate font-semibold text-[18px] text-cladd-fg tracking-[-0.02em] sm:text-[22px]"
class="wrap-anywhere font-semibold text-[18px] text-cladd-fg tracking-[-0.02em] sm:text-[22px]"
>
{fmt(list[0], abs)}
</div>
@@ -142,7 +142,7 @@
{:else}
<ArrowDown class="size-3.5 shrink-0" />
{/if}
<span class="truncate"
<span
><span class="num">{delta.text}</span>
<span class="text-cladd-fg-soft">than {prevName}</span></span
>
@@ -203,18 +203,20 @@
segments={[{ color: "var(--ff-series-3)", value: paidShare.paid }]}
/>
{/if}
<div class="truncate text-[12px] text-cladd-fg-soft">
Paid:
<span class="num"
>{paid.length ? paid.map((e) => fmt(e, true)).join(", ") : "0"}</span
>
<div class="flex flex-wrap gap-x-2 text-[12px] text-cladd-fg-soft">
<span>Paid:</span>
{#each paid as e (e.code)}
<span class="num">{fmt(e, true)}</span>
{:else}
<span class="num">0</span>
{/each}
</div>
{/snippet}
{#snippet leftToSpend()}
{@render values(left)}
{#if left[0]?.sub}
<div class="truncate text-[12px] text-cladd-fg-soft">
<div class="text-[12px] text-cladd-fg-soft">
{leftState === "none" ? "" : "Per day: "}<span class="num"
>{left[0].sub}</span
>
@@ -30,6 +30,7 @@
const action = new Action();
const values = $state<Record<string, string>>({});
const fields: Record<string, HTMLElement> = {};
const links = $derived(
(piggy.attributes.accounts ?? []).map((link) => {
@@ -50,9 +51,10 @@
}
}
function fail(message: string) {
function fail(message: string, id = open[0]?.id ?? "") {
action.failed = true;
action.message = message;
fields[id]?.focus();
}
async function save() {
@@ -64,11 +66,11 @@
}
const amount = evalAmount(raw);
if (amount === null || amount <= 0) {
return fail("Enter an amount above zero");
return fail("Enter an amount above zero", link.id);
}
if (amount > link.max + 1e-9) {
const max = formatMoney(piggyMoney(piggy.attributes, link.max));
return fail(`${link.name} allows at most ${max}`);
return fail(`${link.name} allows at most ${max}`, link.id);
}
deltas[link.id] = mode === "add" ? amount : -amount;
}
@@ -109,6 +111,7 @@
onkeydown={onEscape}
bind:value={values[link.id]}
{@attach (node) => {
fields[link.id] = node;
if (i === 0) {
node.focus();
}
@@ -1,4 +1,5 @@
<script lang="ts">
import Check from "@lucide/svelte/icons/check";
import { formatDate } from "#lib/format.js";
import { STATUS_TONE, type Status, statusText } from "./bills.js";
@@ -9,9 +10,9 @@
<span class="inline-flex flex-wrap gap-x-2">
{#each state.paid as paid (paid.transaction_journal_id)}
<a
class="text-income hover:underline"
class="inline-flex items-center gap-1 text-income hover:underline"
href="/transactions/show/{paid.transaction_group_id}"
>✓ {formatDate(paid.date, "short")}</a
><Check class="size-3.5 shrink-0" />{formatDate(paid.date, "short")}</a
>
{/each}
</span>