Making things nicer
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev --host",
|
||||
"dev": "vite dev --host --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
@ -40,4 +40,4 @@
|
||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||
"tailwindcss": "^4.1.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/app.html
23
src/app.html
@ -1,12 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
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}
|
||||
@ -568,6 +568,22 @@ export async function getUnderallocatedTransactions() {
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getOrphanedTransactions() {
|
||||
const result = await db`
|
||||
SELECT budget.name as budget_name,
|
||||
budget_transaction.id as trans_id,
|
||||
budget_transaction.budget_id as budget_id,
|
||||
budget_transaction.transaction_id as transaction_id,
|
||||
budget_transaction.amount as budget_amount,
|
||||
budget_transaction.notes as notes
|
||||
FROM budget_transaction
|
||||
LEFT JOIN budget on budget_id = budget.id
|
||||
WHERE transaction_id IS NULL
|
||||
`;
|
||||
// result = Result [{ id: 1, amount: 100.00, notes: "Unallocated funds" }, ...]
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getLast30DaysTransactionsSums() {
|
||||
let result = await db`
|
||||
SELECT cast(date as date) as date, SUM(amount)
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
let { transaction, close, budgets } = $props();
|
||||
let id = $derived(transaction.id);
|
||||
let description = $derived(transaction.description || 'No description');
|
||||
let amount = $derived(transaction.amount || 0);
|
||||
let amount = $derived(transaction.amount.toFixed(2) || 0);
|
||||
let date = $derived(new Date(transaction.date || Date.now()));
|
||||
let out_of_budget = $derived(transaction.out_of_budget || false);
|
||||
let budget_id = $derived(transaction.budget_id || null);
|
||||
|
||||
@ -21,6 +21,7 @@ export function isNeedCode() {
|
||||
export function isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
const browser = await chromium.launchPersistentContext('context', {
|
||||
channel: 'chrome',
|
||||
headless: false,
|
||||
@ -186,9 +187,9 @@ export async function pullData(amount = 100) {
|
||||
|
||||
state.push(`Checking for pending transactions for account ID: ${account.id}`);
|
||||
let currentPend =
|
||||
await db`SELECT id, amount, description, date from transaction where account_id = ${account.id} AND pending=true`;
|
||||
await db`SELECT id, amount, description, date from transaction where account_id = ${account.id} AND pending=true`;
|
||||
for (const pend of currentPend) {
|
||||
const found = transactions.find((t) => `${t.postedDate}:${t.amount}` === pend.id && t.transactionType == "Memo");
|
||||
const found = transactions.find((t) => `${t.description}:${t.amount}` === pend.id && t.transactionType == "Memo");
|
||||
|
||||
if (found && found.transactionType != "Memo")
|
||||
{state.push(
|
||||
@ -200,7 +201,7 @@ export async function pullData(amount = 100) {
|
||||
{
|
||||
state.push(`Orphaning no longer pending budget transaction with no new parent`);
|
||||
|
||||
await db`UPDATE budget_transaction SET transaction_id = null WHERE transaction_id = ${pend.id}`;
|
||||
await db`UPDATE budget_transaction SET transaction_id = null, notes = notes || ${pend.description} WHERE transaction_id = ${pend.id}`;
|
||||
state.push(`Removing pending transaction: ${pend.id}`);
|
||||
await db`DELETE FROM transaction WHERE id = ${pend.id}`;
|
||||
}
|
||||
@ -218,7 +219,7 @@ export async function pullData(amount = 100) {
|
||||
const accountId = transaction.accountId;
|
||||
|
||||
const id = pending
|
||||
? `${transaction.postedDate}:${transaction.amount}`
|
||||
? `${transaction.description}:${transaction.amount}`
|
||||
: transaction.hostTranNumber;
|
||||
|
||||
await db`INSERT INTO transaction (
|
||||
@ -251,7 +252,7 @@ export async function pullData(amount = 100) {
|
||||
pending = EXCLUDED.pending`;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
state.push('Orphaning transactions');
|
||||
|
||||
const orphaned = await db`SELECT bt.id as id
|
||||
@ -262,7 +263,7 @@ export async function pullData(amount = 100) {
|
||||
state.push(`Orphaning transaction: ${orphan.id}`);
|
||||
await db`UPDATE budget_transaction set transaction_id = null where id = ${orphan.id}`;
|
||||
}
|
||||
|
||||
*/
|
||||
state.push('Done');
|
||||
} catch (error) {
|
||||
console.error('Error in pullData:', error);
|
||||
|
||||
@ -91,7 +91,7 @@
|
||||
<li><div class="divider"></div></li>
|
||||
<li>
|
||||
<button
|
||||
class="btn btn-primary btn-square w-100% grow"
|
||||
class="btn btn-primary btn-square w-70 grow"
|
||||
onclick={() =>
|
||||
authClient.signOut({
|
||||
fetchOptions: {
|
||||
|
||||
@ -4,7 +4,8 @@ import {
|
||||
getUnderallocatedTransactions,
|
||||
getTotal,
|
||||
getBudgets,
|
||||
getAllBudgetTransactions
|
||||
getAllBudgetTransactions,
|
||||
getOrphanedTransactions
|
||||
} from '$lib/db';
|
||||
|
||||
|
||||
@ -15,6 +16,7 @@ export async function load({ params }) {
|
||||
const budgets = await getBudgets();
|
||||
const budgetTransactions = await getAllBudgetTransactions();
|
||||
const last30DaysTransactionsSums = await getLast30DaysTransactionsSums();
|
||||
const orphanedTransactions = await getOrphanedTransactions();
|
||||
|
||||
return { unallocatedTrans, underAllocatedTrans, total, budgets, budgetTransactions, last30DaysTransactionsSums };
|
||||
return { unallocatedTrans, underAllocatedTrans, total, budgets, budgetTransactions, last30DaysTransactionsSums, orphanedTransactions };
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import TransactionList from '$lib/transactionList.svelte';
|
||||
import Budgetlist from '$lib/budgetlist.svelte';
|
||||
import { echarts } from '$lib/echarts';
|
||||
|
||||
let { data } = $props();
|
||||
@ -9,23 +10,31 @@
|
||||
let budgets = $derived(data.budgets);
|
||||
let budgetTransactions = $derived(data.budgetTransactions);
|
||||
let last30days = $derived(data.last30DaysTransactionsSums.reverse());
|
||||
let orphanedTransactions = $derived(data.orphanedTransactions);
|
||||
</script>
|
||||
|
||||
<span class="font-sans text-xl"
|
||||
<span class="font-sans text-3xl p-4"
|
||||
>Total Net Worth: <span class="{total > 0 ? 'bg-green-500' : 'bg-red-500'} pl-2 pr-2 rounded-lg"
|
||||
>${total}</span
|
||||
></span
|
||||
>
|
||||
|
||||
{#each budgets as budget}
|
||||
<a
|
||||
href="/budget/{budget.id}"
|
||||
class="block p-4 mb-2 bg-base-200 rounded-lg hover:bg-base-300 transition duration-200"
|
||||
><div class="flex flex-row justify-between items-center text-2xl">
|
||||
<div>{budget.name}</div>
|
||||
<div>{budget.sum}</div>
|
||||
</div></a
|
||||
>
|
||||
{/each}
|
||||
|
||||
<div class="text-xl divider">Unallocated Transactions</div>
|
||||
<TransactionList {budgets} {budgetTransactions} transactions={unallocatedTrans} />
|
||||
|
||||
<div class="text-xl divider">Underallocated Transactions</div>
|
||||
<TransactionList {budgets} {budgetTransactions} transactions={underAllocatedTrans} />
|
||||
|
||||
<style>
|
||||
.container {
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<div class="text-xl divider">Orphans</div>
|
||||
<Budgetlist transactions={orphanedTransactions} />
|
||||
|
||||
@ -4,100 +4,12 @@
|
||||
import { loadingModal } from '$lib/loadingModal.svelte';
|
||||
import { getContext } from 'svelte';
|
||||
import { invalidate, invalidateAll } from '$app/navigation';
|
||||
import BudgetList from '$lib/budgetlist.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
const addToast = getContext('addToast');
|
||||
let budget = $derived(data.budget);
|
||||
let transactions = $derived(data.transactions.transactions || []);
|
||||
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;
|
||||
EditBudgetTransactionModal.showModal();
|
||||
}
|
||||
|
||||
async function restoreBudget() {
|
||||
let res = await fetch(`/api/budget/${budget.id}`, {
|
||||
@ -108,11 +20,6 @@
|
||||
body: JSON.stringify({ delete: false })
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTransaction(transaction) {
|
||||
toDelete = transaction.budget_transaction_id;
|
||||
DeleteTransactionModal.showModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if budget.delete}
|
||||
@ -139,98 +46,7 @@
|
||||
<div class="w-64 grow">{budget.amount}</div>
|
||||
</div>
|
||||
|
||||
<ul class="list bg-base-100 rounded-box shadow-md">
|
||||
{#each transactions as tras}
|
||||
<li class="list-row">
|
||||
<div>
|
||||
{#if tras.id == null}
|
||||
<span class="badge badge-warning">Orphan</span>
|
||||
{: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-xs border p-4">
|
||||
<legend class="fieldset-legend">Reassign</legend>
|
||||
<div class="join">
|
||||
<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">
|
||||
<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-xs 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 max-w-xs"
|
||||
/>
|
||||
|
||||
<label class="label">Notes</label>
|
||||
<textarea bind:value={newData.notes} class="textarea w-100" 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>
|
||||
<BudgetList {transactions} />
|
||||
|
||||
<dialog id="RestoreModal" class="modal">
|
||||
<div class="modal-box">
|
||||
@ -244,46 +60,3 @@
|
||||
<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>
|
||||
<p>Type it in the box to confirm</p>
|
||||
<input
|
||||
bind:value={toDeleteName}
|
||||
type="text"
|
||||
placeholder="Type the name to confirm"
|
||||
class="input input-bordered w-full max-w-xs"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-error mt-4"
|
||||
onclick={async () => {
|
||||
if (toDelete == toDeleteName) {
|
||||
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');
|
||||
}
|
||||
} else {
|
||||
console.error('Name does not match');
|
||||
addToast('Name does not match', 'warning');
|
||||
}
|
||||
}}>Delete</button
|
||||
>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
{#if loading}
|
||||
{@render loadingModal()}
|
||||
{/if}
|
||||
|
||||
@ -5,4 +5,8 @@ import { SvelteKitPWA } from '@vite-pwa/sveltekit';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit(), SvelteKitPWA({registerType: 'autoUpdate', pwaAssets: { config: 'pwa-assets.config.js' }})],
|
||||
server: {
|
||||
allowedHosts: ['budget.caseytimm.com']
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user