From c1bdef340730ba6a20d6553ce56626054a804e05 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Tue, 2 Apr 2024 12:14:09 -0400 Subject: [PATCH] feat: basic transaction editing --- src/lib/components/table/addEditRow.ts | 49 ++++++ .../transactions/TransactionInlineEdit.svelte | 144 ++++++++++++++++++ .../transactions/TransactionsGrid.svelte | 78 +++++++++- src/lib/schemas/transaction.ts | 58 +++++++ 4 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 src/lib/components/table/addEditRow.ts create mode 100644 src/lib/components/transactions/TransactionInlineEdit.svelte create mode 100644 src/lib/schemas/transaction.ts diff --git a/src/lib/components/table/addEditRow.ts b/src/lib/components/table/addEditRow.ts new file mode 100644 index 0000000..be141a3 --- /dev/null +++ b/src/lib/components/table/addEditRow.ts @@ -0,0 +1,49 @@ +import type { + NewTablePropSet, + TablePlugin +} from 'svelte-headless-table/lib/types/TablePlugin'; +import { type Readable, type Writable, derived, writable } from 'svelte/store'; + +export type EditRowState = { + active: Writable; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, unused-imports/no-unused-vars +export interface EditRowColumnOptions {} + +export type EditRowPropSet = NewTablePropSet<{ + 'tbody.tr': { + editing: boolean; + edit: () => void; + cancel: () => void; + }; +}>; + +export function addEditRow(): TablePlugin< + Item, + EditRowState, + EditRowColumnOptions, + EditRowPropSet +> { + return () => { + const active = writable(null); + const pluginState: EditRowState = { active }; + + return { + pluginState, + hooks: { + 'tbody.tr': ({ id }) => { + const props: Readable = derived( + [active], + ([$active]) => ({ + editing: $active === id, + edit: () => active.set(id), + cancel: () => active.set(null) + }) + ); + return { props }; + } + } + }; + }; +} diff --git a/src/lib/components/transactions/TransactionInlineEdit.svelte b/src/lib/components/transactions/TransactionInlineEdit.svelte new file mode 100644 index 0000000..0179b49 --- /dev/null +++ b/src/lib/components/transactions/TransactionInlineEdit.svelte @@ -0,0 +1,144 @@ + + +
+

Edit transaction

+ +
+ + + + + + + + + + + +
+

Attachments

+

Not yet implemented :(

+
+ + + + + diff --git a/src/lib/components/transactions/TransactionsGrid.svelte b/src/lib/components/transactions/TransactionsGrid.svelte index e01b1a6..7041b93 100644 --- a/src/lib/components/transactions/TransactionsGrid.svelte +++ b/src/lib/components/transactions/TransactionsGrid.svelte @@ -3,9 +3,18 @@ import { page } from '$app/stores'; import { createQuery } from '@tanstack/svelte-query'; import dayjs from 'dayjs'; - import { type LinkType, Pagination } from 'flowbite-svelte'; + import { + type LinkType, + Pagination, + Table, + TableBody, + TableBodyCell, + TableBodyRow, + TableHead, + TableHeadCell + } from 'flowbite-svelte'; import { tick } from 'svelte'; - import { createRender, createTable } from 'svelte-headless-table'; + import { Render, Subscribe, createRender, createTable } from 'svelte-headless-table'; import { derived, writable } from 'svelte/store'; import { @@ -13,6 +22,7 @@ TransactionTypeFilter, TransactionsApi } from '$lib/api'; + import { addEditRow } from '$lib/components/table/addEditRow'; import { DateFormat } from '$lib/models/DateFormat'; import { TransactionCategory } from '$lib/models/TransactionCategory'; import { useService } from '$lib/services'; @@ -20,7 +30,8 @@ import { tryParseInt } from '$lib/utils/number'; import Currency from '$lib/components/format/Currency.svelte'; - import DataTable from '$lib/components/table/DataTable.svelte'; + + import TransactionInlineEdit from './TransactionInlineEdit.svelte'; export let category: TransactionCategory; export let initialTransactions: TransactionArray; @@ -70,7 +81,9 @@ const data = writable($transactionQuery.data.data); $: data.set($transactionQuery.data.data); - const table = createTable(data); + const table = createTable(data, { + editRow: addEditRow() + }); const columns = table.createColumns([ table.column({ @@ -110,6 +123,7 @@ return item.id; } }); + const { headerRows, rows, tableAttrs } = vm; $: pagination = $transactionQuery.data.meta.pagination; $: showPagination = pagination && (pagination.totalPages ?? 1) > 1; @@ -139,7 +153,61 @@
{#if $transactionQuery.isSuccess} - + + + {#each $headerRows as headerRow (headerRow.id)} + + {#each headerRow.cells as cell (cell.id)} + + + + + + {/each} + + + {/each} + + + {#each $rows as row (row.id)} + + {#if rowProps.editRow.editing} + + + {#if row.isData()} + + {/if} + + + {:else} + + {#each row.cells as cell (cell.id)} + + + + + + {/each} + + + + + {/if} + + {/each} + +
{#if showPagination}
diff --git a/src/lib/schemas/transaction.ts b/src/lib/schemas/transaction.ts new file mode 100644 index 0000000..b7a2a0c --- /dev/null +++ b/src/lib/schemas/transaction.ts @@ -0,0 +1,58 @@ +import { z } from 'zod'; + +export const transactionSplitSchema = z.object({ + transactionJournalId: z.string(), + // type: z.enum([ + // 'withdrawal', + // 'deposit', + // 'transfer', + // 'reconciliation', + // 'opening balance' + // ]), + date: z.date(), + amount: z.string(), + description: z.string(), + // order: z.number().nullish(), + // currencyId: z.string().nullish(), + // currencyCode: z.string().nullish(), + // foreignAmount: z.string().nullish(), + // foreignCurrencyId: z.string().nullish(), + // foreignCurrencyCode: z.string().nullish(), + budgetId: z.string().nullish(), + // categoryId: z.string().nullish(), + categoryName: z.string().nullish(), + // sourceId: z.string().nullish(), + sourceName: z.string().nullish(), + // sourceIban: z.string().nullish(), + // destinationId: z.string().nullish(), + destinationName: z.string().nullish(), + // destinationIban: z.string().nullish(), + // reconciled: z.boolean(), + billId: z.string().nullish(), + // billName: z.string().nullish(), + tags: z.array(z.string()).nullish(), + notes: z.string().nullish() + // internalReference: z.string().nullish(), + // externalId: z.string().nullish(), + // externalUrl: z.string().nullish(), + // bunqPaymentId: z.string().nullish(), + // sepaCc: z.string().nullish(), + // sepaCtOp: z.string().nullish(), + // sepaCtId: z.string().nullish(), + // sepaDb: z.string().nullish(), + // sepaCountry: z.string().nullish(), + // sepaEp: z.string().nullish(), + // sepaCi: z.string().nullish(), + // sepaBatchId: z.string().nullish(), + // interestDate: z.date().nullish(), + // bookDate: z.date().nullish(), + // processDate: z.date().nullish(), + // dueDate: z.date().nullish(), + // paymentDate: z.date().nullish(), + // invoiceDate: z.date().nullish() +}); + +export const transactionSchema = z.object({ + groupTitle: z.string().optional(), + transactions: z.array(transactionSplitSchema) +});