Making things nicer

This commit is contained in:
2025-08-01 10:18:29 -04:00
parent 416ef3bc37
commit 7ca5f67c1b
11 changed files with 300 additions and 258 deletions

234
src/lib/budgetlist.svelte Normal file
View File

@ -0,0 +1,234 @@
<script>
import { EditSymbol } from '$lib/editSymbol.svelte';
import { TrashBin } from '$lib/trashbin.svelte';
import { getContext } from 'svelte';
import { invalidate, invalidateAll } from '$app/navigation';
import { loadingModal } from '$lib/loadingModal.svelte';
let { transactions } = $props();
const addToast = getContext('addToast');
let newData = $state({
amount: 0,
notes: '',
name: '',
id: null
});
let toDelete = $state('');
let toDeleteName = $state('');
let loading = $state(false);
let newTransaction = $state({
name: '',
id: null
});
let searchString = $state('');
let debounceString = $state('');
let searchResults = $state([]);
let searching = $state(false);
async function search() {
if (searching) return; // Prevent multiple searches at the same time
searching = true;
const res = await fetch(`/api/transactions?c=100&p=${searchString}`);
const data = await res.json();
searchResults = data;
searching = false;
}
async function updateTransactionID() {
loading = true;
EditBudgetTransactionModal.close();
if (!newTransaction.id) {
addToast('Please select a transaction to update', 'warning');
return;
}
if (newTransaction.id == newData.id) {
addToast('No changes made to the transaction', 'info');
return;
}
const response = await fetch(`/api/budget/transaction/${newData.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
transactionId: newTransaction.id
})
});
if (response.ok) {
addToast('Transaction updated successfully', 'success');
invalidateAll();
} else {
addToast('Failed to update transaction', 'error');
}
loading = false;
}
async function saveTransaction() {
loading = true;
EditBudgetTransactionModal.close();
let res = await fetch(`/api/budget/${budget.id}/transaction`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: $state.snapshot(newData.amount),
notes: $state.snapshot(newData.notes),
transactionId: $state.snapshot(newData.id)
})
});
loading = false;
console.log(res.ok);
if (res.ok) {
// Optionally, you can refresh the UI or show a success message
addToast('Transaction updated successfully', 'success');
invalidateAll();
} else {
console.error('Failed to update transaction');
addToast('Failed to update transaction', 'error');
}
}
function edit(transaction) {
newData.amount = transaction.budget_amount;
newData.name = transaction.description;
newData.notes = transaction.notes || '';
newData.id = transaction.budget_transaction_id;
searchString = transaction.notes || '';
search();
EditBudgetTransactionModal.showModal();
}
function deleteTransaction(transaction) {
toDelete = transaction.budget_transaction_id;
DeleteTransactionModal.showModal();
}
</script>
<ul class="list bg-base-100 rounded-box shadow-md">
{#each transactions as tras}
<li class="list-row">
<div>
{#if tras.id == null}
{#if tras.budget_name}
<div>{tras.budget_name}</div>
{:else}
<span class="badge badge-warning">Orphan</span>
{/if}
{:else}
<div>{tras?.description}</div>
<div class="text-xs uppercase font-semibold opacity-60">
{tras?.date?.toDateString()}
</div>
{/if}
</div>
<div class="text-center">{tras.notes}</div>
<div class="text-lg uppercase font-semibold text-right w-32">{tras?.budget_amount}</div>
<div>
<button class="btn btn-square btn-ghost" onclick={() => edit(tras)}
>{@render EditSymbol()}</button
>
</div>
<div>
<button class="btn btn-square btn-ghost" onclick={() => deleteTransaction(tras)}
>{@render TrashBin()}</button
>
</div>
</li>
{/each}
</ul>
<dialog id="EditBudgetTransactionModal" class="modal">
<div class="modal-box">
<h1>{newData.name}</h1>
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-full border p-4">
<legend class="fieldset-legend">Reassign</legend>
<div class="join w-full">
<label class="input">
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g
stroke-linejoin="round"
stroke-linecap="round"
stroke-width="2.5"
fill="none"
stroke="currentColor"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</g>
</svg>
<input type="search" required placeholder="Search" bind:value={searchString} />
</label>
<button class="btn btn-neutral join-item" onclick={() => search()} disabled={searching}
>Search</button
>
</div>
<div class="join w-full">
<select class="select" bind:value={newTransaction.id} disabled={searching}>
<option value="" disabled selected>Select a Transaction</option>
{#each searchResults as res}
<option value={res?.id}
>{res?.description} - {new Date(res?.date).toDateString()} - {res?.amount}}</option
>
{/each}
</select>
<button class="btn btn-neutral join-item" onclick={() => updateTransactionID()}
>Update</button
>
</div>
</fieldset>
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-full border p-4">
<legend class="fieldset-legend">Edit</legend>
<label class="label">Amount</label>
<input
bind:value={newData.amount}
type="text"
placeholder="Amount"
class="input input-bordered w-full"
/>
<label class="label">Notes</label>
<textarea bind:value={newData.notes} class="textarea w-full" placeholder="Budget Notes"
></textarea>
<button onclick={() => saveTransaction()} class="btn btn-primary mt-4">Save</button>
</fieldset>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<dialog id="DeleteTransactionModal" class="modal">
<div class="modal-box">
<p>Are you sure you want to delete</p>
<span>{toDelete}</span>
<button
class="btn btn-error mt-4"
onclick={async () => {
let res = await fetch(`/api/budget/${toDelete}/transaction/`, {
method: 'DELETE'
});
if (res.ok) {
console.log('Rule deleted successfully');
DeleteTransactionModal.close();
invalidateAll();
addToast('Transaction deleted successfully', 'success');
} else {
console.error('Failed to delete transaction');
addToast('Failed to delete transaction', 'error');
}
}}>Delete</button
>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
{#if loading}
{@render loadingModal()}
{/if}