feat(editor,quick-entry): page editor with splits, links, attachments and one-line quick entry

This commit is contained in:
hh committed 2026-10-16 03:25:02 +00:00
1 parent 135a2d9328
commit 7e19684cff
22 files changed
+4067 -10

No files matched your search

@@ -0,0 +1,47 @@
<script lang="ts">
import { createQuery } from "@tanstack/svelte-query";
import { api } from "#lib/api/client.js";
import type { One, TransactionGroup } from "#lib/api/types.js";
import { takeDraft } from "#lib/components/editor/draft.js";
import Editor from "#lib/components/editor/Editor.svelte";
import {
emptyForm,
fromGroup,
isEditableType,
} from "#lib/components/editor/model.js";
import { page } from "$app/state";
const raw = $derived(
page.params.type === "transfers" ? "transfer" : (page.params.type ?? "")
);
const type = $derived(isEditableType(raw) ? raw : "withdrawal");
const cloneId = $derived(page.url.searchParams.get("clone"));
const prefill = $derived({
destination: page.url.searchParams.get("destination"),
source: page.url.searchParams.get("source"),
});
const draftKey = $derived(page.url.searchParams.get("draft"));
const clone = createQuery(() => ({
enabled: Boolean(cloneId),
queryFn: () => api<One<TransactionGroup>>(`/v1/transactions/${cloneId}`),
queryKey: ["transaction", cloneId],
}));
</script>
{#key `${type}:${cloneId}:${draftKey}`}
{#if cloneId && clone.isPending}
<p class="py-8 text-cladd-fg-soft">Loading the transaction to clone</p>
{:else if cloneId && clone.data}
<Editor initial={fromGroup(clone.data.data, true)} />
{:else}
{@const draft = takeDraft()}
{#if cloneId && clone.isError}
<p class="pb-3 text-[13px] text-expense">
Transaction #{cloneId}
could not be loaded, so this is an empty form.
</p>
{/if}
<Editor initial={draft ?? emptyForm(type)} {prefill} />
{/if}
{/key}
@@ -0,0 +1,46 @@
<script lang="ts">
import { createQuery } from "@tanstack/svelte-query";
import { api } from "#lib/api/client.js";
import type { One, TransactionGroup } from "#lib/api/types.js";
import Editor from "#lib/components/editor/Editor.svelte";
import { fromGroup, isEditableType } from "#lib/components/editor/model.js";
import PageHeader from "#lib/components/shell/PageHeader.svelte";
import { page } from "$app/state";
const id = $derived(page.params.id ?? "");
const group = createQuery(() => ({
queryFn: () => api<One<TransactionGroup>>(`/v1/transactions/${id}`),
queryKey: ["transaction", id],
staleTime: 0,
}));
const type = $derived(
group.data?.data.attributes.transactions[0]?.type ?? ""
);
</script>
{#key id}
{#if group.isPending}
<p class="py-8 text-cladd-fg-soft">Loading transaction #{id}</p>
{:else if group.isError}
<PageHeader subtitle="Edit transaction" title="Transactions" />
<p class="text-expense">
Transaction #{id}
could not be loaded: {group.error.message}
</p>
{:else if group.data && !isEditableType(type)}
<PageHeader subtitle="Edit transaction" title="Transactions" />
<p class="text-[13px]">
This is a {type}, which this editor does not change.
<a
class="text-brand underline"
data-sveltekit-reload
href="/transactions/edit/{id}?ffui=old"
>Edit it in the old interface</a
>
</p>
{:else if group.data}
<Editor group={group.data.data} initial={fromGroup(group.data.data)} />
{/if}
{/key}