Making things nicer
This commit is contained in:
234
src/lib/budgetlist.svelte
Normal file
234
src/lib/budgetlist.svelte
Normal 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}
|
||||
Reference in New Issue
Block a user