From cccebbb6f3352b9a42d260e57a414fa1841495d2 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 09:43:22 -0700 Subject: [PATCH 1/9] feat: create amount input --- src/components/inputs/AmountInput.svelte | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/components/inputs/AmountInput.svelte diff --git a/src/components/inputs/AmountInput.svelte b/src/components/inputs/AmountInput.svelte new file mode 100644 index 0000000..95606cc --- /dev/null +++ b/src/components/inputs/AmountInput.svelte @@ -0,0 +1,17 @@ + + +
+ + +
From 5389d3877cebf0b6727d42ff5083bb67641f5d80 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 09:43:42 -0700 Subject: [PATCH 2/9] feat: create category input --- src/components/inputs/CategoryInput.svelte | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/components/inputs/CategoryInput.svelte diff --git a/src/components/inputs/CategoryInput.svelte b/src/components/inputs/CategoryInput.svelte new file mode 100644 index 0000000..f69d0c2 --- /dev/null +++ b/src/components/inputs/CategoryInput.svelte @@ -0,0 +1,47 @@ + + +
+ Category +
+
+ + +
+ {#each categories as { category }, index} +
+ + +
+ {/each} +
+
+{#if selectedCategory === 'NEW_CATEGORY'} +
+ + +
+{/if} From 8b6354beaa670dedaf8cc5f71c92f7c312635095 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 09:43:58 -0700 Subject: [PATCH 3/9] feat: create description input --- src/components/inputs/DescriptionInput.svelte | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/components/inputs/DescriptionInput.svelte diff --git a/src/components/inputs/DescriptionInput.svelte b/src/components/inputs/DescriptionInput.svelte new file mode 100644 index 0000000..164afc3 --- /dev/null +++ b/src/components/inputs/DescriptionInput.svelte @@ -0,0 +1,8 @@ + + +
+ + +
From 01136dcd37a92a9c0c933d0267db0a9656c9d26c Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 09:44:10 -0700 Subject: [PATCH 4/9] feat: create date input --- src/components/inputs/DateInputs.svelte | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/components/inputs/DateInputs.svelte diff --git a/src/components/inputs/DateInputs.svelte b/src/components/inputs/DateInputs.svelte new file mode 100644 index 0000000..ad7ade5 --- /dev/null +++ b/src/components/inputs/DateInputs.svelte @@ -0,0 +1,52 @@ + + +
+ Date +
+
+ + +
+
+ + +
+
+ + +
+
+
From 605ed53375f7b8d2a224a1c66697cd2ca598b7f1 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 09:44:44 -0700 Subject: [PATCH 5/9] feat: load the add expense page with category data --- src/routes/expense/+page.server.js | 16 ++++++++++++++++ src/routes/expense/+page.svelte | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/routes/expense/+page.server.js create mode 100644 src/routes/expense/+page.svelte diff --git a/src/routes/expense/+page.server.js b/src/routes/expense/+page.server.js new file mode 100644 index 0000000..dd45e64 --- /dev/null +++ b/src/routes/expense/+page.server.js @@ -0,0 +1,16 @@ +import { fail } from '@sveltejs/kit'; + +export const load = async ({ locals: { supabase } }) => { + try { + const { data: categories } = await supabase + .from('expense_categories') + .select('category') + .order('category'); + + return { + categories, + }; + } catch (error) { + return fail(500, { message: 'Server error. Try again later.', success: false }); + } +}; diff --git a/src/routes/expense/+page.svelte b/src/routes/expense/+page.svelte new file mode 100644 index 0000000..db1d8b3 --- /dev/null +++ b/src/routes/expense/+page.svelte @@ -0,0 +1,20 @@ + + +

Add Expense

+ +
+ + + + +
+ +
+ From 92c5ec0d6574d25ffa80f8dc438cc4e94fc40ab9 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 10:06:48 -0700 Subject: [PATCH 6/9] fix: adjust pattern to reject invalid dollar amounts --- src/components/inputs/AmountInput.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/inputs/AmountInput.svelte b/src/components/inputs/AmountInput.svelte index 95606cc..4aa6ba9 100644 --- a/src/components/inputs/AmountInput.svelte +++ b/src/components/inputs/AmountInput.svelte @@ -9,7 +9,7 @@ type="text" name="amount" inputmode="numeric" - pattern={'^(\\s{1,})?\\$?\\d{1,3}(,?\\d{3})*(\\.\\d+)?(\\s{1,})?$'} + pattern={'^(\\s{1,})?\\$?\\d{1,3}(,?\\d{3})*(\\.\\d{1,2})?(\\s{1,})?$'} required aria-required="true" value={amount} From 618e87150a9ff2beba5425584aaa3106481eab6a Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 10:07:14 -0700 Subject: [PATCH 7/9] feat: add helper functions for formatting input data --- src/lib/format-inputs.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/lib/format-inputs.js diff --git a/src/lib/format-inputs.js b/src/lib/format-inputs.js new file mode 100644 index 0000000..1426e2d --- /dev/null +++ b/src/lib/format-inputs.js @@ -0,0 +1,9 @@ +export const formatAmount = (value) => Number(value.toString().replace(/[^0-9|.]/g, '')); + +export const formatDate = (year, month, day) => { + const numericYear = Number(year); + const numericMonth = Number(month) - 1; + const numericDay = Number(day); + + return new Date(numericYear, numericMonth, numericDay); +}; From b7f4628673027a3459347c2e7d9c38003b1dd486 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 10:16:35 -0700 Subject: [PATCH 8/9] feat: save expense to DB --- src/routes/expense/+page.server.js | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/routes/expense/+page.server.js b/src/routes/expense/+page.server.js index dd45e64..20241dc 100644 --- a/src/routes/expense/+page.server.js +++ b/src/routes/expense/+page.server.js @@ -1,4 +1,5 @@ -import { fail } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; +import { formatAmount, formatDate } from '$lib/format-inputs.js'; export const load = async ({ locals: { supabase } }) => { try { @@ -14,3 +15,34 @@ export const load = async ({ locals: { supabase } }) => { return fail(500, { message: 'Server error. Try again later.', success: false }); } }; + +export const actions = { + default: async ({ request, locals: { supabase } }) => { + const formData = await request.formData(); + const amount = formatAmount(formData.get('amount')); + + const selectedCategory = formData.get('category'); + const newCategory = formData.get('new-category'); + const category = newCategory ?? selectedCategory; + + const description = formData.get('description') || category; + + const year = formData.get('year'); + const month = formData.get('month'); + const day = formData.get('day'); + const date = formatDate(year, month, day); + + const { + data: { user }, + } = await supabase.auth.getUser(); + const { error } = await supabase + .from('expenses') + .insert({ user_id: user.id, date, category, description, amount }); + + if (error) { + return fail(500, { message: 'Server error. Try again later.', success: false }); + } + + throw redirect(303, '/overview'); + }, +}; From 088ca37663fe728c7c3d25c8d68d948dadc66537 Mon Sep 17 00:00:00 2001 From: Dustin Whisman Date: Tue, 26 Dec 2023 10:18:22 -0700 Subject: [PATCH 9/9] feat: link to add expense page --- src/components/MonthlyOverview.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/MonthlyOverview.svelte b/src/components/MonthlyOverview.svelte index 91feb3c..9ab864a 100644 --- a/src/components/MonthlyOverview.svelte +++ b/src/components/MonthlyOverview.svelte @@ -23,6 +23,7 @@ {:else}

No expenses found.

{/if} +Add Expense

Income

{#if data.income.length}