fix(transactions,editor,reports): v1 period column, one amount edge, unclipped report numbers, solid save, mobile headers

This commit is contained in:
hh committed 2026-10-16 04:26:07 +00:00
1 parent fa322d22e9
commit e279d3948a
8 files changed
+279 -134

No files matched your search

+15 -12
View File
@@ -10,6 +10,7 @@
import Field from "#lib/components/inputs/Field.svelte"; import Field from "#lib/components/inputs/Field.svelte";
import { INPUT } from "#lib/components/inputs/styles.js"; import { INPUT } from "#lib/components/inputs/styles.js";
import PageHeader from "#lib/components/shell/PageHeader.svelte"; import PageHeader from "#lib/components/shell/PageHeader.svelte";
import Button from "#lib/components/ui/button/button.svelte";
import { formatMoney } from "#lib/format.js"; import { formatMoney } from "#lib/format.js";
import { prettyKeys, useKeys } from "#lib/keys.svelte.js"; import { prettyKeys, useKeys } from "#lib/keys.svelte.js";
import { ui } from "#lib/ui.svelte.js"; import { ui } from "#lib/ui.svelte.js";
@@ -626,20 +627,29 @@
</Box> </Box>
{/if} {/if}
<div class="grid gap-4 lg:grid-cols-2">
<Attachments groupId={saved?.id ?? null} {journals} bind:queued />
{#if editing && journals.length}
<Links {journals} />
{/if}
</div>
<div <div
class="cladd-surface cladd-outline sticky bottom-0 z-20 flex flex-wrap items-center gap-x-3 gap-y-2 rounded-cladd-lg px-3 py-2 sm:gap-x-4 sm:py-2.5 shadow-[0_-6px_16px_-12px_rgb(0_0_0/0.35)]" class="cladd-surface cladd-outline sticky bottom-0 z-20 flex flex-wrap items-center gap-x-3 gap-y-2 rounded-cladd-lg px-3 py-2 sm:gap-x-4 sm:py-2.5 shadow-[0_-6px_16px_-12px_rgb(0_0_0/0.35)]"
> >
<button <Button
class="cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-color-brand inline-flex h-9 items-center gap-2 rounded-cladd-md px-3.5 font-semibold text-[13px] text-cladd-primary disabled:opacity-60 sm:h-cladd-md" class="h-9 px-3.5 text-[13px] sm:h-cladd-md"
color="brand"
disabled={saving} disabled={saving}
onclick={save} onclick={save}
type="button" variant="fill"
> >
{editing ? "Update transaction" : "Save transaction"} {editing ? "Update transaction" : "Save transaction"}
<span class="kbd hidden sm:inline-flex" <span
class="kbd hidden bg-white/20! text-current! shadow-none! sm:inline-flex"
>{prettyKeys("mod+Enter")[0]}</span >{prettyKeys("mod+Enter")[0]}</span
> >
</button> </Button>
<label class="flex items-center gap-2 text-[13px] text-cladd-fg-soft"> <label class="flex items-center gap-2 text-[13px] text-cladd-fg-soft">
<span class="hidden sm:inline">After saving</span> <span class="hidden sm:inline">After saving</span>
<select <select
@@ -685,11 +695,4 @@
</p> </p>
{/if} {/if}
</div> </div>
<div class="grid gap-4 lg:grid-cols-2">
<Attachments groupId={saved?.id ?? null} {journals} bind:queued />
{#if editing && journals.length}
<Links {journals} />
{/if}
</div>
</div> </div>
+41 -15
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Calendar from "@lucide/svelte/icons/calendar"; import Calendar from "@lucide/svelte/icons/calendar";
import { untrack } from "svelte"; import { tick, untrack } from "svelte";
import type { HTMLInputAttributes } from "svelte/elements"; import type { HTMLInputAttributes } from "svelte/elements";
import { addDays, today } from "#lib/dates.js"; import { addDays, today } from "#lib/dates.js";
import { formatDate } from "#lib/format.js"; import { formatDate } from "#lib/format.js";
@@ -11,16 +11,21 @@
value = $bindable(""), value = $bindable(""),
ref = $bindable(null), ref = $bindable(null),
invalid = false, invalid = false,
compact = false,
oncommit,
class: className = "", class: className = "",
...rest ...rest
}: Omit<HTMLInputAttributes, "value"> & { }: Omit<HTMLInputAttributes, "value"> & {
value?: string; value?: string;
ref?: HTMLInputElement | null; ref?: HTMLInputElement | null;
invalid?: boolean; invalid?: boolean;
compact?: boolean;
oncommit?: (value: string) => void;
} = $props(); } = $props();
let text = $state(value); let text = $state(value);
let picker = $state<HTMLInputElement | null>(null); let picker = $state<HTMLInputElement | null>(null);
let focused = $state(false);
const parsed = $derived(parseDate(text, today())); const parsed = $derived(parseDate(text, today()));
const shown = $derived(parsed ?? value); const shown = $derived(parsed ?? value);
@@ -29,6 +34,9 @@
? `${formatDate(shown, "day")}${shown.slice(0, 4) === today().slice(0, 4) ? "" : ` ${shown.slice(0, 4)}`}` ? `${formatDate(shown, "day")}${shown.slice(0, 4) === today().slice(0, 4) ? "" : ` ${shown.slice(0, 4)}`}`
: "" : ""
); );
const display = $derived(
compact && !focused && shown ? formatDate(shown) : text
);
$effect(() => { $effect(() => {
const next = value; const next = value;
@@ -47,14 +55,33 @@
} }
} }
function commit(next: string) {
text = next;
value = next;
oncommit?.(next);
}
function handleFocus(
event: FocusEvent & { currentTarget: HTMLInputElement }
) {
focused = true;
const input = event.currentTarget;
if (compact) {
tick().then(() => input.select());
}
}
function handleBlur() { function handleBlur() {
focused = false;
if (parsed) { if (parsed) {
text = parsed; commit(parsed);
value = parsed;
} }
} }
function handleKey(event: KeyboardEvent) { function handleKey(event: KeyboardEvent) {
if (event.key === "Enter" && parsed) {
commit(parsed);
}
if ( if (
(event.key === "ArrowUp" || event.key === "ArrowDown") && (event.key === "ArrowUp" || event.key === "ArrowDown") &&
!(event.altKey || event.ctrlKey || event.metaKey) !(event.altKey || event.ctrlKey || event.metaKey)
@@ -62,8 +89,7 @@
event.preventDefault(); event.preventDefault();
const step = const step =
(event.key === "ArrowUp" ? 1 : -1) * (event.shiftKey ? 7 : 1); (event.key === "ArrowUp" ? 1 : -1) * (event.shiftKey ? 7 : 1);
value = addDays(parsed ?? value ?? today(), step); commit(addDays(parsed ?? value ?? today(), step));
text = value;
} }
} }
@@ -78,17 +104,20 @@
{...rest} {...rest}
aria-invalid={invalid || (text.trim() !== "" && !parsed) || undefined} aria-invalid={invalid || (text.trim() !== "" && !parsed) || undefined}
autocomplete="off" autocomplete="off"
class="{INPUT} num pr-28" class="{INPUT} num {compact ? "pr-8" : "pr-28"}"
onblur={handleBlur} onblur={handleBlur}
onfocus={handleFocus}
oninput={handleInput} oninput={handleInput}
onkeydown={handleKey} onkeydown={handleKey}
spellcheck="false" spellcheck="false"
value={text} value={display}
>
<span
class="pointer-events-none absolute inset-y-0 right-9 flex items-center text-[12px] text-cladd-fg-soft"
>{label}</span
> >
{#if !compact}
<span
class="pointer-events-none absolute inset-y-0 right-9 flex items-center text-[12px] text-cladd-fg-soft"
>{label}</span
>
{/if}
<button <button
aria-label="Pick a date" aria-label="Pick a date"
class="absolute inset-y-0 right-0 grid w-8 place-items-center text-cladd-fg-soft hover:text-cladd-fg" class="absolute inset-y-0 right-0 grid w-8 place-items-center text-cladd-fg-soft hover:text-cladd-fg"
@@ -101,10 +130,7 @@
<input <input
aria-hidden="true" aria-hidden="true"
class="pointer-events-none absolute right-0 bottom-0 h-0 w-0 opacity-0" class="pointer-events-none absolute right-0 bottom-0 h-0 w-0 opacity-0"
onchange={({ currentTarget }) => { onchange={({ currentTarget }) => commit(currentTarget.value)}
({ value } = currentTarget);
text = value;
}}
tabindex="-1" tabindex="-1"
type="date" type="date"
{value} {value}
@@ -73,17 +73,17 @@
title="Account balances" title="Account balances"
> >
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="w-full border-collapse text-[13px]"> <table class="w-full min-w-[34rem] border-collapse text-[13px]">
<thead> <thead>
<tr class="text-[12px] text-cladd-fg-soft"> <tr class="text-[12px] text-cladd-fg-soft">
<th class="h-7 px-3 text-left font-medium">Name</th> <th class="h-7 w-full px-3 text-left font-medium">Name</th>
<th class="h-7 px-3 text-right font-medium"> <th class="h-7 w-32 px-3 text-right font-medium">
Balance at start of period Balance at start of period
</th> </th>
<th class="h-7 px-3 text-right font-medium"> <th class="h-7 w-32 px-3 text-right font-medium">
Balance at end of period Balance at end of period
</th> </th>
<th class="h-7 px-3 text-right font-medium">Difference</th> <th class="h-7 w-32 px-3 text-right font-medium">Difference</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -95,7 +95,7 @@
? "bg-row-selected" ? "bg-row-selected"
: ""}" : ""}"
> >
<td class="max-w-[16rem] px-3"> <td class="max-w-0 px-3">
<button <button
aria-pressed={active} aria-pressed={active}
class="block max-w-full truncate text-left hover:text-brand dark:hover:text-[var(--ff-series-1)] hover:underline" class="block max-w-full truncate text-left hover:text-brand dark:hover:text-[var(--ff-series-1)] hover:underline"
+50 -44
View File
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import ChevronLeft from "@lucide/svelte/icons/chevron-left"; import ChevronLeft from "@lucide/svelte/icons/chevron-left";
import ChevronRight from "@lucide/svelte/icons/chevron-right"; import ChevronRight from "@lucide/svelte/icons/chevron-right";
import DateInput from "#lib/components/inputs/DateInput.svelte";
import { useKeys } from "#lib/keys.svelte.js"; import { useKeys } from "#lib/keys.svelte.js";
import { recall, remember } from "#lib/reports/memory.js"; import { recall, remember } from "#lib/reports/memory.js";
import { listOptions, listReady } from "#lib/reports/options.js"; import { listOptions, listReady } from "#lib/reports/options.js";
@@ -25,7 +26,7 @@
let accountsOpen = $state(false); let accountsOpen = $state(false);
let itemsOpen = $state(false); let itemsOpen = $state(false);
let typeSelect = $state<HTMLSelectElement>(); let typeSelect = $state<HTMLSelectElement>();
let startInput = $state<HTMLInputElement>(); let startInput = $state<HTMLInputElement | null>(null);
const p = $derived(report.params); const p = $derived(report.params);
const presets = rangePresets(); const presets = rangePresets();
@@ -82,8 +83,9 @@
go({ items: defaultItems(type), type }); go({ items: defaultItems(type), type });
} }
function setRange(start: string, end: string) { function setRange(a: string, b: string) {
if (start && end && start <= end) { const [start, end] = a <= b ? [a, b] : [b, a];
if (start && end && (start !== p.start || end !== p.end)) {
go({ end, start }); go({ end, start });
} }
} }
@@ -159,11 +161,13 @@
<div <div
class="cladd-surface cladd-outline z-20 mb-4 lg:sticky lg:top-14 flex flex-wrap items-end justify-between gap-x-4 gap-y-2 rounded-cladd-lg px-3 py-2" class="cladd-surface cladd-outline z-20 mb-4 lg:sticky lg:top-14 flex flex-wrap items-end justify-between gap-x-4 gap-y-2 rounded-cladd-lg px-3 py-2"
> >
<div class="flex min-w-0 flex-1 flex-wrap items-end gap-x-3 gap-y-2"> <div
<label class="flex flex-col gap-0.5"> class="flex min-w-0 flex-1 flex-wrap items-end gap-x-3 gap-y-2 max-sm:basis-full"
>
<label class="flex flex-col gap-0.5 max-sm:w-full">
<span class="text-[11px] text-cladd-fg-soft">Report type</span> <span class="text-[11px] text-cladd-fg-soft">Report type</span>
<select <select
class="cladd-cut cladd-focusable-input h-8 max-w-[14rem] rounded-cladd-sm px-2 text-[13px] text-cladd-fg" class="cladd-cut cladd-focusable-input h-8 max-w-[14rem] rounded-cladd-sm px-2 text-[13px] text-cladd-fg max-sm:max-w-none"
onchange={(event) => setType(event.currentTarget.value as ReportType)} onchange={(event) => setType(event.currentTarget.value as ReportType)}
value={p.type} value={p.type}
bind:this={typeSelect} bind:this={typeSelect}
@@ -173,12 +177,12 @@
{/each} {/each}
</select> </select>
</label> </label>
<div class="flex min-w-0 flex-col gap-0.5"> <div class="flex min-w-0 flex-col gap-0.5 max-sm:w-full">
<label class="text-[11px] text-cladd-fg-soft" for="report-accounts" <label class="text-[11px] text-cladd-fg-soft" for="report-accounts"
>Accounts</label >Accounts</label
> >
<MultiSelect <MultiSelect
class="w-48" class="w-44 max-sm:w-full"
id="report-accounts" id="report-accounts"
label="Accounts" label="Accounts"
loading={!report.accountsReady} loading={!report.accountsReady}
@@ -189,12 +193,12 @@
/> />
</div> </div>
{#if list} {#if list}
<div class="flex min-w-0 flex-col gap-0.5"> <div class="flex min-w-0 flex-col gap-0.5 max-sm:w-full">
<label class="text-[11px] text-cladd-fg-soft" for="report-items" <label class="text-[11px] text-cladd-fg-soft" for="report-items"
>{LIST_LABELS[list].many}</label >{LIST_LABELS[list].many}</label
> >
<MultiSelect <MultiSelect
class="w-48" class="w-48 max-sm:w-full"
id="report-items" id="report-items"
label={LIST_LABELS[list].many} label={LIST_LABELS[list].many}
loading={!listReady(list, sources)} loading={!listReady(list, sources)}
@@ -205,48 +209,50 @@
/> />
</div> </div>
{/if} {/if}
<div class="flex flex-col gap-0.5"> <div class="flex flex-col gap-0.5 max-sm:w-full">
<span class="text-[11px] text-cladd-fg-soft">Date range</span> <span class="text-[11px] text-cladd-fg-soft">Date range</span>
<div class="flex flex-wrap items-center gap-x-2 gap-y-1"> <div class="flex flex-wrap items-center gap-x-1 gap-y-2 sm:gap-x-2">
<div class="flex items-center gap-1"> <button
<button aria-label="Previous period"
aria-label="Previous period" class="grid size-8 place-items-center rounded-cladd-sm text-cladd-fg-soft hover:bg-row-hover hover:text-cladd-fg max-sm:order-2"
class="grid size-8 place-items-center rounded-cladd-sm text-cladd-fg-soft hover:bg-row-hover hover:text-cladd-fg" onclick={() => shift(-1)}
onclick={() => shift(-1)} title="Previous period ["
title="Previous period [" type="button"
type="button" >
> <ChevronLeft class="size-4" />
<ChevronLeft class="size-4" /> </button>
</button> <div
<input class="flex items-center gap-2 max-sm:w-full max-sm:flex-col max-sm:items-stretch"
>
<DateInput
aria-label="Start date" aria-label="Start date"
class="cladd-cut cladd-focusable-input num h-8 w-[7.4rem] rounded-cladd-sm px-1.5 text-[13px] sm:w-[8.2rem] sm:px-2 text-cladd-fg" class="w-[8.75rem] max-sm:w-full [&>input]:h-8"
onchange={(event) => setRange(event.currentTarget.value, p.end)} compact
type="date" oncommit={(value) => setRange(value, p.end)}
value={p.start} value={p.start}
bind:this={startInput} bind:ref={startInput}
> />
<span class="text-cladd-fg-soft">to</span> <span class="text-cladd-fg-soft max-sm:hidden">to</span>
<input <DateInput
aria-label="End date" aria-label="End date"
class="cladd-cut cladd-focusable-input num h-8 w-[7.4rem] rounded-cladd-sm px-1.5 text-[13px] sm:w-[8.2rem] sm:px-2 text-cladd-fg" class="w-[8.75rem] max-sm:w-full [&>input]:h-8"
onchange={(event) => setRange(p.start, event.currentTarget.value)} compact
type="date" oncommit={(value) => setRange(p.start, value)}
value={p.end} value={p.end}
> />
<button
aria-label="Next period"
class="grid size-8 place-items-center rounded-cladd-sm text-cladd-fg-soft hover:bg-row-hover hover:text-cladd-fg"
onclick={() => shift(1)}
title="Next period ]"
type="button"
>
<ChevronRight class="size-4" />
</button>
</div> </div>
<button
aria-label="Next period"
class="grid size-8 place-items-center rounded-cladd-sm text-cladd-fg-soft hover:bg-row-hover hover:text-cladd-fg max-sm:order-4"
onclick={() => shift(1)}
title="Next period ]"
type="button"
>
<ChevronRight class="size-4" />
</button>
<select <select
aria-label="Preset range" aria-label="Preset range"
class="cladd-cut cladd-focusable-input h-8 rounded-cladd-sm px-2 text-[13px] text-cladd-fg" class="cladd-cut cladd-focusable-input h-8 rounded-cladd-sm px-2 text-[13px] text-cladd-fg max-sm:order-3 max-sm:flex-1"
onchange={(event) => { onchange={(event) => {
const found = presets.find( const found = presets.find(
(x) => x.key === event.currentTarget.value (x) => x.key === event.currentTarget.value
+3 -3
View File
@@ -24,14 +24,14 @@
</svelte:head> </svelte:head>
<header class="flex flex-wrap items-end justify-between gap-x-6 gap-y-2 pb-4"> <header class="flex flex-wrap items-end justify-between gap-x-6 gap-y-2 pb-4">
<div class="flex min-w-0 items-baseline gap-3"> <div class="flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-0.5">
<h1 <h1
class="truncate font-semibold text-[22px] text-cladd-fg tracking-[-0.02em]" class="break-words font-semibold text-[22px] text-cladd-fg tracking-[-0.02em]"
> >
{title} {title}
</h1> </h1>
{#if subtitle} {#if subtitle}
<span class="truncate text-[13px] text-cladd-fg-soft">{subtitle}</span> <span class="text-[13px] text-cladd-fg-soft">{subtitle}</span>
{/if} {/if}
</div> </div>
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
@@ -0,0 +1,107 @@
<script lang="ts">
import { createQuery, keepPreviousData } from "@tanstack/svelte-query";
import { all } from "#lib/api/lists.js";
import type { TransactionGroup } from "#lib/api/types.js";
import MoneyList from "#lib/components/categories/MoneyList.svelte";
import {
byPeriod,
list,
periodRanges,
rows,
SIDEBAR_PERIODS,
} from "#lib/components/categories/sums.js";
import type { Range } from "#lib/dates.js";
import { period, periodLabel } from "#lib/period.svelte.js";
import { listPath } from "#lib/transactions/split.js";
const FIELDS = [
["spent", "Spent", "withdrawal"],
["earned", "Earned", "deposit"],
["transferred", "Transferred", "transfer"],
] as const;
let { type, class: className = "" }: { type: string; class?: string } =
$props();
const ranges = $derived(
periodRanges(period.end, period.view, SIDEBAR_PERIODS)
);
const first = $derived(ranges.at(-1)?.start ?? period.start);
const last = $derived(ranges[0]?.end ?? period.end);
const query = createQuery(() => ({
placeholderData: keepPreviousData,
queryFn: async () => ({
first,
groups: await all<TransactionGroup>("/v1/transactions", {
end: last,
start: first,
type,
}),
last,
}),
queryKey: ["transactions-periods", type, first, last],
}));
const periods = $derived(
byPeriod(
rows(query.data?.groups ?? []),
ranges.filter((range) => !period.first || range.end >= period.first)
)
);
const fields = $derived(
FIELDS.filter(([, , kind]) => type === "all" || type === kind)
);
function covered(range: Range) {
const { data } = query;
return Boolean(data && data.first <= range.start && range.end <= data.last);
}
const href = (range: Range) =>
`${listPath(type)}?start=${range.start}&end=${range.end}`;
</script>
<nav
aria-label="Periods"
class="flex min-w-0 gap-2 overflow-x-auto min-[1600px]:flex-col min-[1600px]:overflow-visible {className}"
>
{#each periods as item (item.range.start)}
{@const current =
item.range.start === period.start && item.range.end === period.end}
<a
aria-current={current ? "page" : undefined}
class="cladd-surface cladd-outline flex w-40 shrink-0 flex-col gap-0.5 rounded-cladd-md px-3 py-2 text-[12px] min-[1600px]:w-auto {current
? "bg-row-cursor outline-2 outline-brand -outline-offset-2"
: "hover:bg-row-hover"}"
href={href(item.range)}
>
<span class="font-semibold text-brand"
>{periodLabel(item.range, period.view)}</span
>
{#if !covered(item.range)}
<span class="text-cladd-fg-softer">Loading...</span>
{:else if item.totals.count === 0}
<span class="text-cladd-fg-softer">No transactions</span>
{:else}
<span class="flex justify-between gap-2">
<span class="text-cladd-fg-soft">Transactions</span>
<span class="num text-cladd-fg">{item.totals.count}</span>
</span>
{#each fields as [field, label] (field)}
{@const sums = list(item.totals[field])}
{#if sums.length > 0}
<span class="flex justify-between gap-2">
<span class="text-cladd-fg-soft">{label}</span>
<MoneyList
class="flex-col items-end"
{sums}
tone={field === "transferred" ? "transfer" : "auto"}
/>
</span>
{/if}
{/each}
{/if}
</a>
{/each}
</nav>
@@ -330,14 +330,12 @@
{/if} {/if}
</td> </td>
<td class="text-right"> <td class="text-right">
<div class="flex flex-wrap justify-end gap-x-1"> {@render money(amount)}
{@render money(amount)} {#if foreign}
{#if foreign} <div class="num text-[12px] text-cladd-fg-soft">
<span class="num text-[12px] text-cladd-fg-soft" {formatMoney(foreign)}
>({formatMoney(foreign)})</span </div>
> {/if}
{/if}
</div>
</td> </td>
{#if accountId} {#if accountId}
<td class="text-right">{@render plain(balanceAfter(split, accountId))}</td> <td class="text-right">{@render plain(balanceAfter(split, accountId))}</td>
@@ -411,15 +409,10 @@
> >
</div> </div>
</td> </td>
<td class="text-right"> <td class="text-right font-semibold">
<div class="flex flex-wrap justify-end gap-x-1 font-semibold"> {#each sums as sum (sum.code)}
{#each sums as sum, i (sum.code)} <div>{@render money(sum)}</div>
{@render money(sum)} {/each}
{#if i < sums.length - 1}
,
{/if}
{/each}
</div>
</td> </td>
{#if accountId} {#if accountId}
<td></td> <td></td>
@@ -484,7 +477,7 @@
{/if} {/if}
<th class="w-6 max-sm:hidden"><span class="sr-only">Type</span></th> <th class="w-6 max-sm:hidden"><span class="sr-only">Type</span></th>
<th>Description</th> <th>Description</th>
<th class="w-28 text-right md:w-44">Amount</th> <th class="w-28 text-right md:w-32">Amount</th>
{#if accountId} {#if accountId}
<th class="w-32 text-right">Balance</th> <th class="w-32 text-right">Balance</th>
{/if} {/if}
@@ -9,6 +9,7 @@
import PageHeader from "#lib/components/shell/PageHeader.svelte"; import PageHeader from "#lib/components/shell/PageHeader.svelte";
import Pager from "#lib/components/transactions/Pager.svelte"; import Pager from "#lib/components/transactions/Pager.svelte";
import PeriodSummary from "#lib/components/transactions/PeriodSummary.svelte"; import PeriodSummary from "#lib/components/transactions/PeriodSummary.svelte";
import Periods from "#lib/components/transactions/Periods.svelte";
import QueryInput from "#lib/components/transactions/QueryInput.svelte"; import QueryInput from "#lib/components/transactions/QueryInput.svelte";
import TransactionTable from "#lib/components/transactions/TransactionTable.svelte"; import TransactionTable from "#lib/components/transactions/TransactionTable.svelte";
import Button from "#lib/components/ui/button/button.svelte"; import Button from "#lib/components/ui/button/button.svelte";
@@ -191,39 +192,48 @@
/> />
{/if} {/if}
<Box body="p-0" title={LIST_TITLE[type] ?? "Transactions"}> <div
{#snippet tools()} class="grid items-start gap-4 min-[1600px]:grid-cols-[minmax(0,1fr)_13rem]"
<span class="max-sm:hidden"> >
<Pager <Box body="p-0" title={LIST_TITLE[type] ?? "Transactions"}>
href={pageHref} {#snippet tools()}
keys <span class="max-sm:hidden">
page={pageNo} <Pager
pages={pagination?.total_pages ?? 1} href={pageHref}
/> keys
</span> page={pageNo}
{/snippet} pages={pagination?.total_pages ?? 1}
<TransactionTable />
empty={everything ? "No transactions." : "No transactions in this period."} </span>
groups={list.data?.data ?? []} {/snippet}
id="tx-list" <TransactionTable
loading={list.isFetching} empty={everything
/> ? "No transactions."
{#snippet footer()} : "No transactions in this period."}
{#if everything} groups={list.data?.data ?? []}
<a class="text-brand hover:underline" href={listPath(type)} id="tx-list"
>Show the current period and overview</a loading={list.isFetching}
> />
{:else} {#snippet footer()}
<a class="text-brand hover:underline" href={`${listPath(type)}/all`} {#if everything}
>Show all transactions without grouping them by date</a <a class="text-brand hover:underline" href={listPath(type)}
> >Show the current period and overview</a
{/if} >
<span class="ml-auto"> {:else}
<Pager <a class="text-brand hover:underline" href={`${listPath(type)}/all`}
href={pageHref} >Show all transactions without grouping them by date</a
page={pageNo} >
pages={pagination?.total_pages ?? 1} {/if}
/> <span class="ml-auto">
</span> <Pager
{/snippet} href={pageHref}
</Box> page={pageNo}
pages={pagination?.total_pages ?? 1}
/>
</span>
{/snippet}
</Box>
{#if !everything}
<Periods class="order-first min-[1600px]:order-none" {type} />
{/if}
</div>