From 4531b3ffe9d446d0cd636809aef0c1197b5b49e2 Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 01:18:07 +0100 Subject: [PATCH 01/10] store uploaded transactions to db --- src/components/transactions/CSVUpload.tsx | 19 ++++++++++++------- .../transactions/InputTransaction.tsx | 10 ++++++---- src/components/transactions/Transaction.ts | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/components/transactions/CSVUpload.tsx b/src/components/transactions/CSVUpload.tsx index d4f3064..501a8db 100644 --- a/src/components/transactions/CSVUpload.tsx +++ b/src/components/transactions/CSVUpload.tsx @@ -1,6 +1,8 @@ import { useState } from "react"; import { Transaction } from "./Transaction"; import { Alert, Button, Form, Modal } from "react-bootstrap"; +import { writeNewTransactionsBatched } from "../../utils/firestore"; +import { auth } from "../../utils/firebase"; export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dispatch> }) { const [error, setError] = useState(); @@ -8,17 +10,19 @@ export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dis const reader = new FileReader(); - function handleUpload() { + async function handleUpload() { setError(null); setSuccessMsg(null); - + const fileElement = document.getElementById("file") as HTMLInputElement | null; if (!fileElement || fileElement.files?.length === 0) return setError("You haven't uploaded a CSV file"); const file = fileElement.files![0]; if (!file) return setError("You haven't uploaded a CSV file"); - reader.onload = (event) => { + reader.onload = async (event) => { + if (!auth.currentUser) return setError("You are not signed in"); + const csvContent = event.target?.result; if (!csvContent || csvContent instanceof ArrayBuffer) return setError("Unable to read uploaded CSV file"); @@ -33,10 +37,11 @@ export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dis const validTransactions = transactions.filter((transaction) => transaction.isValid); if (validTransactions.length === 0) return setError("The uploaded CSV file has no valid transactions"); - - // ------------------------------------------------------------------------------------- - // TODO: STORE "validTransactions" IN THE DATABASE - // ------------------------------------------------------------------------------------- + + await writeNewTransactionsBatched( + auth.currentUser, + validTransactions.map((transaction) => transaction.toDocument(auth.currentUser!.uid)) + ) setSuccessMsg(`${validTransactions.length} valid transactions have been imported out of ${transactions.length} total transactions`); setTimeout(() => setSuccessMsg(null), 10000); diff --git a/src/components/transactions/InputTransaction.tsx b/src/components/transactions/InputTransaction.tsx index 473d7ae..b06abe5 100644 --- a/src/components/transactions/InputTransaction.tsx +++ b/src/components/transactions/InputTransaction.tsx @@ -1,6 +1,8 @@ import { useState } from "react"; import { Transaction, emojis, formatDate, formatTime } from "./Transaction"; import { Button, Modal, Form, Alert } from "react-bootstrap"; +import { auth } from "../../utils/firebase"; +import { writeNewTransaction } from "../../utils/firestore"; export function InputTransaction({ show, setShow}: { show: boolean, setShow: React.Dispatch> }) { const [name, setName] = useState(""); @@ -15,10 +17,12 @@ export function InputTransaction({ show, setShow}: { show: boolean, setShow: Rea const [error, setError] = useState(""); const [successMsg, setSuccessMsg] = useState(""); - function addTransaction() { + async function addTransaction() { setError(null); setSuccessMsg(null); + if (!auth.currentUser) return setError("You are not signed in"); + const date = new Date(); const transaction = new Transaction() @@ -39,9 +43,7 @@ export function InputTransaction({ show, setShow}: { show: boolean, setShow: Rea return; } - // ------------------------------------------------------------------------------------- - // TODO: STORE "transaction" IN THE DATABASE - // ------------------------------------------------------------------------------------- + await writeNewTransaction(auth.currentUser, transaction.toDocument(auth.currentUser.uid)) setSuccessMsg("Transaction has been successfully added"); setTimeout(() => setSuccessMsg(null), 10000); diff --git a/src/components/transactions/Transaction.ts b/src/components/transactions/Transaction.ts index f9a6075..c26f531 100644 --- a/src/components/transactions/Transaction.ts +++ b/src/components/transactions/Transaction.ts @@ -1,3 +1,5 @@ +import { Transaction as TransactionDocument } from "../../utils/firestore"; + export const emojis: { [index: string]: string } = { "Income": "💸", "Transfers": "🏦", @@ -148,6 +150,21 @@ export class Transaction { return this; } + toDocument(uid: string): TransactionDocument { + return new TransactionDocument( + this.address as string, + this.amount as number, + this.category as string, + this.currency as string, + new Date((this.date as string) + (this.time as string)).getTime(), + this.description as string, + this.emoji as string, + this.name as string, + this.notes as string, + uid + ); + } + isNotEmpty(val?: string): val is string { return !!val && val.trim() !== ""; } From ab138cce4b528f4dab11cf2444d1a9934396c6cf Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 01:32:18 +0100 Subject: [PATCH 02/10] optimise transaction validation --- src/components/transactions/CSVUpload.tsx | 14 +++++++------- src/components/transactions/InputTransaction.tsx | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/transactions/CSVUpload.tsx b/src/components/transactions/CSVUpload.tsx index 501a8db..6c92140 100644 --- a/src/components/transactions/CSVUpload.tsx +++ b/src/components/transactions/CSVUpload.tsx @@ -35,15 +35,15 @@ export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dis .filter((row) => row[3] === "Card payment" || row[3] === "Faster payment") // filter by type .map((row) => new Transaction().fromRow(row)); - const validTransactions = transactions.filter((transaction) => transaction.isValid); - if (validTransactions.length === 0) return setError("The uploaded CSV file has no valid transactions"); + const transactionDocuments = transactions + .filter((transaction) => transaction.isValid) + .map((transaction) => transaction.toDocument(auth.currentUser!.uid)); + + if (transactionDocuments.length === 0) return setError("The uploaded CSV file has no valid transactions"); - await writeNewTransactionsBatched( - auth.currentUser, - validTransactions.map((transaction) => transaction.toDocument(auth.currentUser!.uid)) - ) + await writeNewTransactionsBatched(auth.currentUser, transactionDocuments); - setSuccessMsg(`${validTransactions.length} valid transactions have been imported out of ${transactions.length} total transactions`); + setSuccessMsg(`${transactionDocuments.length} valid transactions have been imported out of ${transactions.length} total transactions`); setTimeout(() => setSuccessMsg(null), 10000); fileElement.value = ""; diff --git a/src/components/transactions/InputTransaction.tsx b/src/components/transactions/InputTransaction.tsx index b06abe5..07453e4 100644 --- a/src/components/transactions/InputTransaction.tsx +++ b/src/components/transactions/InputTransaction.tsx @@ -43,7 +43,7 @@ export function InputTransaction({ show, setShow}: { show: boolean, setShow: Rea return; } - await writeNewTransaction(auth.currentUser, transaction.toDocument(auth.currentUser.uid)) + await writeNewTransaction(auth.currentUser, transaction.toDocument(auth.currentUser.uid)); setSuccessMsg("Transaction has been successfully added"); setTimeout(() => setSuccessMsg(null), 10000); From 7766095d8171bca3fccf77bb1c16431f0fa8eb41 Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 01:37:44 +0100 Subject: [PATCH 03/10] remove intermediate variables --- src/components/transactions/CSVUpload.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/transactions/CSVUpload.tsx b/src/components/transactions/CSVUpload.tsx index 6c92140..ba8c373 100644 --- a/src/components/transactions/CSVUpload.tsx +++ b/src/components/transactions/CSVUpload.tsx @@ -26,16 +26,12 @@ export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dis const csvContent = event.target?.result; if (!csvContent || csvContent instanceof ArrayBuffer) return setError("Unable to read uploaded CSV file"); - const rows = csvContent + const transactionDocuments = csvContent .split("\n") .slice(1) - .map((row) => row.split(",")); - - const transactions = rows + .map((row) => row.split(",")) .filter((row) => row[3] === "Card payment" || row[3] === "Faster payment") // filter by type - .map((row) => new Transaction().fromRow(row)); - - const transactionDocuments = transactions + .map((row) => new Transaction().fromRow(row)) .filter((transaction) => transaction.isValid) .map((transaction) => transaction.toDocument(auth.currentUser!.uid)); @@ -43,7 +39,7 @@ export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dis await writeNewTransactionsBatched(auth.currentUser, transactionDocuments); - setSuccessMsg(`${transactionDocuments.length} valid transactions have been imported out of ${transactions.length} total transactions`); + setSuccessMsg(`${transactionDocuments.length} transactions have been imported`); setTimeout(() => setSuccessMsg(null), 10000); fileElement.value = ""; From 9a0ab5032201603066748d7d96ca5749638f4a5a Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 11:54:13 +0100 Subject: [PATCH 04/10] fix conflicting file names --- src/components/transactions/CSVUpload.tsx | 2 +- src/components/transactions/InputTransaction.tsx | 2 +- src/components/transactions/Transaction.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/transactions/CSVUpload.tsx b/src/components/transactions/CSVUpload.tsx index ba8c373..193d88d 100644 --- a/src/components/transactions/CSVUpload.tsx +++ b/src/components/transactions/CSVUpload.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Transaction } from "./Transaction"; import { Alert, Button, Form, Modal } from "react-bootstrap"; -import { writeNewTransactionsBatched } from "../../utils/firestore"; +import { writeNewTransactionsBatched } from "../../utils/firestore.ts"; import { auth } from "../../utils/firebase"; export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dispatch> }) { diff --git a/src/components/transactions/InputTransaction.tsx b/src/components/transactions/InputTransaction.tsx index 07453e4..f5485e4 100644 --- a/src/components/transactions/InputTransaction.tsx +++ b/src/components/transactions/InputTransaction.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { Transaction, emojis, formatDate, formatTime } from "./Transaction"; import { Button, Modal, Form, Alert } from "react-bootstrap"; import { auth } from "../../utils/firebase"; -import { writeNewTransaction } from "../../utils/firestore"; +import { writeNewTransaction } from "../../utils/firestore.ts"; export function InputTransaction({ show, setShow}: { show: boolean, setShow: React.Dispatch> }) { const [name, setName] = useState(""); diff --git a/src/components/transactions/Transaction.ts b/src/components/transactions/Transaction.ts index c26f531..d9c1fa5 100644 --- a/src/components/transactions/Transaction.ts +++ b/src/components/transactions/Transaction.ts @@ -1,4 +1,4 @@ -import { Transaction as TransactionDocument } from "../../utils/firestore"; +import { Transaction as TransactionDocument } from "../../utils/firestore.ts"; export const emojis: { [index: string]: string } = { "Income": "💸", From cba9e25f714c94d557fc787fd4ccce26f2666a6e Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 12:12:05 +0100 Subject: [PATCH 05/10] add baseurl to tsconfig --- tsconfig.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 6138ae3..4006964 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,9 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + + "baseUrl": "." }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] From 6b147875aaa3a348b1bc7fde3848e034d193bce6 Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 12:16:13 +0100 Subject: [PATCH 06/10] fix --- tsconfig.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 4006964..3743d79 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,9 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - - "baseUrl": "." + + "baseUrl": ".", + "importHelpers": true }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] From b52844d221dc1b83a50ce54dc51eb2823305a3d6 Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 12:21:26 +0100 Subject: [PATCH 07/10] move tests to folder to avoid naming conflicts --- src/{utils => tests}/firestore.test.ts | 2 +- tsconfig.json | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) rename src/{utils => tests}/firestore.test.ts (99%) diff --git a/src/utils/firestore.test.ts b/src/tests/firestore.test.ts similarity index 99% rename from src/utils/firestore.test.ts rename to src/tests/firestore.test.ts index f05741c..4430be2 100644 --- a/src/utils/firestore.test.ts +++ b/src/tests/firestore.test.ts @@ -6,7 +6,7 @@ import { Transaction, writeNewTransaction, writeNewTransactionsBatched -} from "./firestore.ts"; +} from "../utils/firestore.ts"; import {faker, fakerEN_GB} from "@faker-js/faker"; import _ from "lodash"; import { describe, expect, test } from "vitest"; diff --git a/tsconfig.json b/tsconfig.json index 3743d79..3831f56 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,9 +18,6 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - - "baseUrl": ".", - "importHelpers": true }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] From f5fc25bb5743df3501f0ed803c9353abaee42b18 Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 12:32:40 +0100 Subject: [PATCH 08/10] fix --- src/{tests => utils}/firestore.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/{tests => utils}/firestore.test.ts (98%) diff --git a/src/tests/firestore.test.ts b/src/utils/firestore.test.ts similarity index 98% rename from src/tests/firestore.test.ts rename to src/utils/firestore.test.ts index 4430be2..ce6186b 100644 --- a/src/tests/firestore.test.ts +++ b/src/utils/firestore.test.ts @@ -6,7 +6,7 @@ import { Transaction, writeNewTransaction, writeNewTransactionsBatched -} from "../utils/firestore.ts"; +} from "./firestore.ts"; import {faker, fakerEN_GB} from "@faker-js/faker"; import _ from "lodash"; import { describe, expect, test } from "vitest"; @@ -36,13 +36,13 @@ describe("Firestore Tests", () => { expect(new_transaction.getDocName(), "New transaction has no docName").toBe(undefined); // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error + // @ts-ignore const written_transaction = await writeNewTransaction(user, new_transaction); expect(written_transaction.getDocName(), "Written transaction has docName").toBeDefined(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error + // @ts-ignore const fetched_transaction = await getTransactionsByDocName(user, written_transaction.forceGetDocName()); expect(fetched_transaction, "Written transaction can be fetched").toBeDefined(); From 5ca6ad9b8b4809c0f061db2c879c391a64424fe1 Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 12:34:29 +0100 Subject: [PATCH 09/10] fix --- src/utils/firestore.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/firestore.test.ts b/src/utils/firestore.test.ts index ce6186b..4430be2 100644 --- a/src/utils/firestore.test.ts +++ b/src/utils/firestore.test.ts @@ -6,7 +6,7 @@ import { Transaction, writeNewTransaction, writeNewTransactionsBatched -} from "./firestore.ts"; +} from "../utils/firestore.ts"; import {faker, fakerEN_GB} from "@faker-js/faker"; import _ from "lodash"; import { describe, expect, test } from "vitest"; @@ -36,13 +36,13 @@ describe("Firestore Tests", () => { expect(new_transaction.getDocName(), "New transaction has no docName").toBe(undefined); // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error const written_transaction = await writeNewTransaction(user, new_transaction); expect(written_transaction.getDocName(), "Written transaction has docName").toBeDefined(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error const fetched_transaction = await getTransactionsByDocName(user, written_transaction.forceGetDocName()); expect(fetched_transaction, "Written transaction can be fetched").toBeDefined(); From fcf1b798c570ec6e0f553427247e31a51235b39f Mon Sep 17 00:00:00 2001 From: Dylan Roskilly Date: Fri, 26 Apr 2024 12:45:00 +0100 Subject: [PATCH 10/10] merge --- src/components/transactions/CSVUpload.tsx | 2 +- src/components/transactions/InputTransaction.tsx | 2 +- src/components/transactions/Transaction.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/transactions/CSVUpload.tsx b/src/components/transactions/CSVUpload.tsx index 193d88d..25be4ba 100644 --- a/src/components/transactions/CSVUpload.tsx +++ b/src/components/transactions/CSVUpload.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Transaction } from "./Transaction"; import { Alert, Button, Form, Modal } from "react-bootstrap"; -import { writeNewTransactionsBatched } from "../../utils/firestore.ts"; +import { writeNewTransactionsBatched } from "../../utils/transaction.ts"; import { auth } from "../../utils/firebase"; export function CSVUpload({ show, setShow }: { show: boolean, setShow: React.Dispatch> }) { diff --git a/src/components/transactions/InputTransaction.tsx b/src/components/transactions/InputTransaction.tsx index f5485e4..27f97f6 100644 --- a/src/components/transactions/InputTransaction.tsx +++ b/src/components/transactions/InputTransaction.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { Transaction, emojis, formatDate, formatTime } from "./Transaction"; import { Button, Modal, Form, Alert } from "react-bootstrap"; import { auth } from "../../utils/firebase"; -import { writeNewTransaction } from "../../utils/firestore.ts"; +import { writeNewTransaction } from "../../utils/transaction.ts"; export function InputTransaction({ show, setShow}: { show: boolean, setShow: React.Dispatch> }) { const [name, setName] = useState(""); diff --git a/src/components/transactions/Transaction.ts b/src/components/transactions/Transaction.ts index d9c1fa5..e70ad34 100644 --- a/src/components/transactions/Transaction.ts +++ b/src/components/transactions/Transaction.ts @@ -1,4 +1,4 @@ -import { Transaction as TransactionDocument } from "../../utils/firestore.ts"; +import { Transaction as TransactionDocument } from "../../utils/transaction.ts"; export const emojis: { [index: string]: string } = { "Income": "💸",