fix(transactions,editor,reports): v1 period column, one amount edge, unclipped report numbers, solid save, mobile headers
This commit is contained in:
1 parent
fa322d22e9
commit
e279d3948a
8 files changed
+221
-76
No files matched your search
@@ -10,6 +10,7 @@
|
||||
import Field from "#lib/components/inputs/Field.svelte";
|
||||
import { INPUT } from "#lib/components/inputs/styles.js";
|
||||
import PageHeader from "#lib/components/shell/PageHeader.svelte";
|
||||
import Button from "#lib/components/ui/button/button.svelte";
|
||||
import { formatMoney } from "#lib/format.js";
|
||||
import { prettyKeys, useKeys } from "#lib/keys.svelte.js";
|
||||
import { ui } from "#lib/ui.svelte.js";
|
||||
@@ -626,20 +627,29 @@
|
||||
</Box>
|
||||
{/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
|
||||
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
|
||||
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"
|
||||
<Button
|
||||
class="h-9 px-3.5 text-[13px] sm:h-cladd-md"
|
||||
color="brand"
|
||||
disabled={saving}
|
||||
onclick={save}
|
||||
type="button"
|
||||
variant="fill"
|
||||
>
|
||||
{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
|
||||
>
|
||||
</button>
|
||||
</Button>
|
||||
<label class="flex items-center gap-2 text-[13px] text-cladd-fg-soft">
|
||||
<span class="hidden sm:inline">After saving</span>
|
||||
<select
|
||||
@@ -685,11 +695,4 @@
|
||||
</p>
|
||||
{/if}
|
||||
</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>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Calendar from "@lucide/svelte/icons/calendar";
|
||||
import { untrack } from "svelte";
|
||||
import { tick, untrack } from "svelte";
|
||||
import type { HTMLInputAttributes } from "svelte/elements";
|
||||
import { addDays, today } from "#lib/dates.js";
|
||||
import { formatDate } from "#lib/format.js";
|
||||
@@ -11,16 +11,21 @@
|
||||
value = $bindable(""),
|
||||
ref = $bindable(null),
|
||||
invalid = false,
|
||||
compact = false,
|
||||
oncommit,
|
||||
class: className = "",
|
||||
...rest
|
||||
}: Omit<HTMLInputAttributes, "value"> & {
|
||||
value?: string;
|
||||
ref?: HTMLInputElement | null;
|
||||
invalid?: boolean;
|
||||
compact?: boolean;
|
||||
oncommit?: (value: string) => void;
|
||||
} = $props();
|
||||
|
||||
let text = $state(value);
|
||||
let picker = $state<HTMLInputElement | null>(null);
|
||||
let focused = $state(false);
|
||||
|
||||
const parsed = $derived(parseDate(text, today()));
|
||||
const shown = $derived(parsed ?? value);
|
||||
@@ -29,6 +34,9 @@
|
||||
? `${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(() => {
|
||||
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() {
|
||||
focused = false;
|
||||
if (parsed) {
|
||||
text = parsed;
|
||||
value = parsed;
|
||||
commit(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
function handleKey(event: KeyboardEvent) {
|
||||
if (event.key === "Enter" && parsed) {
|
||||
commit(parsed);
|
||||
}
|
||||
if (
|
||||
(event.key === "ArrowUp" || event.key === "ArrowDown") &&
|
||||
!(event.altKey || event.ctrlKey || event.metaKey)
|
||||
@@ -62,8 +89,7 @@
|
||||
event.preventDefault();
|
||||
const step =
|
||||
(event.key === "ArrowUp" ? 1 : -1) * (event.shiftKey ? 7 : 1);
|
||||
value = addDays(parsed ?? value ?? today(), step);
|
||||
text = value;
|
||||
commit(addDays(parsed ?? value ?? today(), step));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,17 +104,20 @@
|
||||
{...rest}
|
||||
aria-invalid={invalid || (text.trim() !== "" && !parsed) || undefined}
|
||||
autocomplete="off"
|
||||
class="{INPUT} num pr-28"
|
||||
class="{INPUT} num {compact ? "pr-8" : "pr-28"}"
|
||||
onblur={handleBlur}
|
||||
onfocus={handleFocus}
|
||||
oninput={handleInput}
|
||||
onkeydown={handleKey}
|
||||
spellcheck="false"
|
||||
value={text}
|
||||
value={display}
|
||||
>
|
||||
{#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
|
||||
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"
|
||||
@@ -101,10 +130,7 @@
|
||||
<input
|
||||
aria-hidden="true"
|
||||
class="pointer-events-none absolute right-0 bottom-0 h-0 w-0 opacity-0"
|
||||
onchange={({ currentTarget }) => {
|
||||
({ value } = currentTarget);
|
||||
text = value;
|
||||
}}
|
||||
onchange={({ currentTarget }) => commit(currentTarget.value)}
|
||||
tabindex="-1"
|
||||
type="date"
|
||||
{value}
|
||||
|
||||
@@ -73,17 +73,17 @@
|
||||
title="Account balances"
|
||||
>
|
||||
<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>
|
||||
<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">
|
||||
<th class="h-7 w-full px-3 text-left font-medium">Name</th>
|
||||
<th class="h-7 w-32 px-3 text-right font-medium">
|
||||
Balance at start of period
|
||||
</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
|
||||
</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>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -95,7 +95,7 @@
|
||||
? "bg-row-selected"
|
||||
: ""}"
|
||||
>
|
||||
<td class="max-w-[16rem] px-3">
|
||||
<td class="max-w-0 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"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ChevronLeft from "@lucide/svelte/icons/chevron-left";
|
||||
import ChevronRight from "@lucide/svelte/icons/chevron-right";
|
||||
import DateInput from "#lib/components/inputs/DateInput.svelte";
|
||||
import { useKeys } from "#lib/keys.svelte.js";
|
||||
import { recall, remember } from "#lib/reports/memory.js";
|
||||
import { listOptions, listReady } from "#lib/reports/options.js";
|
||||
@@ -25,7 +26,7 @@
|
||||
let accountsOpen = $state(false);
|
||||
let itemsOpen = $state(false);
|
||||
let typeSelect = $state<HTMLSelectElement>();
|
||||
let startInput = $state<HTMLInputElement>();
|
||||
let startInput = $state<HTMLInputElement | null>(null);
|
||||
|
||||
const p = $derived(report.params);
|
||||
const presets = rangePresets();
|
||||
@@ -82,8 +83,9 @@
|
||||
go({ items: defaultItems(type), type });
|
||||
}
|
||||
|
||||
function setRange(start: string, end: string) {
|
||||
if (start && end && start <= end) {
|
||||
function setRange(a: string, b: string) {
|
||||
const [start, end] = a <= b ? [a, b] : [b, a];
|
||||
if (start && end && (start !== p.start || end !== p.end)) {
|
||||
go({ end, start });
|
||||
}
|
||||
}
|
||||
@@ -159,11 +161,13 @@
|
||||
<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"
|
||||
>
|
||||
<div class="flex min-w-0 flex-1 flex-wrap items-end gap-x-3 gap-y-2">
|
||||
<label class="flex flex-col gap-0.5">
|
||||
<div
|
||||
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>
|
||||
<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)}
|
||||
value={p.type}
|
||||
bind:this={typeSelect}
|
||||
@@ -173,12 +177,12 @@
|
||||
{/each}
|
||||
</select>
|
||||
</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"
|
||||
>Accounts</label
|
||||
>
|
||||
<MultiSelect
|
||||
class="w-48"
|
||||
class="w-44 max-sm:w-full"
|
||||
id="report-accounts"
|
||||
label="Accounts"
|
||||
loading={!report.accountsReady}
|
||||
@@ -189,12 +193,12 @@
|
||||
/>
|
||||
</div>
|
||||
{#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"
|
||||
>{LIST_LABELS[list].many}</label
|
||||
>
|
||||
<MultiSelect
|
||||
class="w-48"
|
||||
class="w-48 max-sm:w-full"
|
||||
id="report-items"
|
||||
label={LIST_LABELS[list].many}
|
||||
loading={!listReady(list, sources)}
|
||||
@@ -205,48 +209,50 @@
|
||||
/>
|
||||
</div>
|
||||
{/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>
|
||||
<div class="flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="flex flex-wrap items-center gap-x-1 gap-y-2 sm:gap-x-2">
|
||||
<button
|
||||
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"
|
||||
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"
|
||||
onclick={() => shift(-1)}
|
||||
title="Previous period ["
|
||||
type="button"
|
||||
>
|
||||
<ChevronLeft class="size-4" />
|
||||
</button>
|
||||
<input
|
||||
<div
|
||||
class="flex items-center gap-2 max-sm:w-full max-sm:flex-col max-sm:items-stretch"
|
||||
>
|
||||
<DateInput
|
||||
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"
|
||||
onchange={(event) => setRange(event.currentTarget.value, p.end)}
|
||||
type="date"
|
||||
class="w-[8.75rem] max-sm:w-full [&>input]:h-8"
|
||||
compact
|
||||
oncommit={(value) => setRange(value, p.end)}
|
||||
value={p.start}
|
||||
bind:this={startInput}
|
||||
>
|
||||
<span class="text-cladd-fg-soft">to</span>
|
||||
<input
|
||||
bind:ref={startInput}
|
||||
/>
|
||||
<span class="text-cladd-fg-soft max-sm:hidden">to</span>
|
||||
<DateInput
|
||||
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"
|
||||
onchange={(event) => setRange(p.start, event.currentTarget.value)}
|
||||
type="date"
|
||||
class="w-[8.75rem] max-sm:w-full [&>input]:h-8"
|
||||
compact
|
||||
oncommit={(value) => setRange(p.start, value)}
|
||||
value={p.end}
|
||||
>
|
||||
/>
|
||||
</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"
|
||||
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>
|
||||
</div>
|
||||
<select
|
||||
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) => {
|
||||
const found = presets.find(
|
||||
(x) => x.key === event.currentTarget.value
|
||||
|
||||
@@ -24,14 +24,14 @@
|
||||
</svelte:head>
|
||||
|
||||
<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
|
||||
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}
|
||||
</h1>
|
||||
{#if subtitle}
|
||||
<span class="truncate text-[13px] text-cladd-fg-soft">{subtitle}</span>
|
||||
<span class="text-[13px] text-cladd-fg-soft">{subtitle}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<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}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div class="flex flex-wrap justify-end gap-x-1">
|
||||
{@render money(amount)}
|
||||
{#if foreign}
|
||||
<span class="num text-[12px] text-cladd-fg-soft"
|
||||
>({formatMoney(foreign)})</span
|
||||
>
|
||||
{/if}
|
||||
<div class="num text-[12px] text-cladd-fg-soft">
|
||||
{formatMoney(foreign)}
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
{#if accountId}
|
||||
<td class="text-right">{@render plain(balanceAfter(split, accountId))}</td>
|
||||
@@ -411,15 +409,10 @@
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div class="flex flex-wrap justify-end gap-x-1 font-semibold">
|
||||
{#each sums as sum, i (sum.code)}
|
||||
{@render money(sum)}
|
||||
{#if i < sums.length - 1}
|
||||
,
|
||||
{/if}
|
||||
<td class="text-right font-semibold">
|
||||
{#each sums as sum (sum.code)}
|
||||
<div>{@render money(sum)}</div>
|
||||
{/each}
|
||||
</div>
|
||||
</td>
|
||||
{#if accountId}
|
||||
<td></td>
|
||||
@@ -484,7 +477,7 @@
|
||||
{/if}
|
||||
<th class="w-6 max-sm:hidden"><span class="sr-only">Type</span></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}
|
||||
<th class="w-32 text-right">Balance</th>
|
||||
{/if}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import PageHeader from "#lib/components/shell/PageHeader.svelte";
|
||||
import Pager from "#lib/components/transactions/Pager.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 TransactionTable from "#lib/components/transactions/TransactionTable.svelte";
|
||||
import Button from "#lib/components/ui/button/button.svelte";
|
||||
@@ -191,6 +192,9 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="grid items-start gap-4 min-[1600px]:grid-cols-[minmax(0,1fr)_13rem]"
|
||||
>
|
||||
<Box body="p-0" title={LIST_TITLE[type] ?? "Transactions"}>
|
||||
{#snippet tools()}
|
||||
<span class="max-sm:hidden">
|
||||
@@ -203,7 +207,9 @@
|
||||
</span>
|
||||
{/snippet}
|
||||
<TransactionTable
|
||||
empty={everything ? "No transactions." : "No transactions in this period."}
|
||||
empty={everything
|
||||
? "No transactions."
|
||||
: "No transactions in this period."}
|
||||
groups={list.data?.data ?? []}
|
||||
id="tx-list"
|
||||
loading={list.isFetching}
|
||||
@@ -227,3 +233,7 @@
|
||||
</span>
|
||||
{/snippet}
|
||||
</Box>
|
||||
{#if !everything}
|
||||
<Periods class="order-first min-[1600px]:order-none" {type} />
|
||||
{/if}
|
||||
</div>
|
||||
Reference in new issue
Block a user