From a37e36c2e78f797701c011e52603d506f1c50be1 Mon Sep 17 00:00:00 2001 From: Jonathan Zhao Date: Tue, 1 Sep 2026 23:58:04 -0400 Subject: [PATCH 1/4] feat: get_account_by_email --- bank-app/backend/main.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bank-app/backend/main.py b/bank-app/backend/main.py index 29e86f1..a3a5f37 100644 --- a/bank-app/backend/main.py +++ b/bank-app/backend/main.py @@ -217,6 +217,16 @@ def get_account_id( return account +@app.get("/accounts/by-email/{email}") +def get_account_by_email(email: str, db: Session = Depends(get_db)): + accounts = db.query(Account).join(User).filter(User.email == email).all() + + if accounts is None: + raise HTTPException(status_code=404, detail="Recipient not found") + + return [{"account_id": acc.id, "account_type": acc.account_type} for acc in accounts] + + # IMPLEMENT TO ACCOUNTCARD @app.put("/accounts/{id}", response_model=AccountResponse) def update_account( @@ -335,7 +345,7 @@ def transfer_transaction( to_account = db.query(Account).filter(Account.id == transaction.to_account).first() return transfer( - db, from_account, to_account, transaction.amount, transaction.idempotency_key + db, from_account.id, to_account.id, transaction.amount, transaction.idempotency_key ) From 0401fdb52e196b1d3d8870bf94e92035a5730ce8 Mon Sep 17 00:00:00 2001 From: Jonathan Zhao Date: Tue, 1 Sep 2026 23:58:49 -0400 Subject: [PATCH 2/4] frontend: e-transfer UI & backend built --- .../frontend/src/components/AccountCard.jsx | 117 +++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) diff --git a/bank-app/frontend/src/components/AccountCard.jsx b/bank-app/frontend/src/components/AccountCard.jsx index f24f181..bbdad44 100644 --- a/bank-app/frontend/src/components/AccountCard.jsx +++ b/bank-app/frontend/src/components/AccountCard.jsx @@ -4,14 +4,18 @@ import { authFetch } from "../utils/authFetch"; import TransactionList from "./TransactionList"; import { API_URL } from "../config"; -function AccountCard({ account, onDelete, onEdit, onDeposit, onWithdrawal}) { +function AccountCard({ account, onDelete, onEdit, onDeposit, onWithdrawal, onTransfer}) { const [editing, setEditing] = useState(false); const [ownerName, setOwnerName] = useState(account.owner_name); const [accountType, setAccountType] = useState(account.account_type); - + const [recipientAccounts, setRecipientAccounts] = useState([]); + const [selectedAccount, setSelectedAccount] = useState(""); + const [toEmail, setToEmail] = useState("") + const [amount, setAmount] = useState(""); const [showTransactions, setShowTransactions] = useState(false); + const [showTransfer, setShowTransfer] = useState(false); //above -> only use const [..., ...] = useState() when user is interacting with it (input fields, dropdowns checkboxes, temporary UI changes) // DO NOT USE IT when the backend owns it (e.g account.frozen, account.balance, account.account_type) @@ -88,6 +92,62 @@ function AccountCard({ account, onDelete, onEdit, onDeposit, onWithdrawal}) { } }; + const handleTransfer = async () => { //E-Transfer -> SHOW ACCOUNT'S USER NAMES INSTEAD OF account_type + try { + const idempotencyKey = crypto.randomUUID() + + const response = await authFetch( + `${API_URL}/accounts/${account.id}/transfer`, { + method : "PATCH", + headers : { + "Content-Type" : "application/json" + }, + body: JSON.stringify({ + from_account: account.id, + to_account: selectedAccount, + amount: Number(amount), + idempotency_key: idempotencyKey + }), + } + ); + + if (!response.ok) { + throw new Error("Transaction Failed"); + } + + const transactions = await response.json(); + + onTransfer(transactions); + setRecipientAccounts([]); + setSelectedAccount(""); + setAmount(""); + setShowTransfer(false); + + } catch (error) { + alert("Transaction Failed. Try again"); + console.error("TRANSFER ERROR:", error); + } + }; + + const findRecipient = async () => { + try { + const response = await authFetch( + `${API_URL}/accounts/by-email/${encodeURIComponent(toEmail)}` + ); + + if (!response.ok) { + throw new Error("Recipient not found"); + } + + const accounts = await response.json(); + + setRecipientAccounts(accounts); + } catch (error) { + alert("Recipient not found"); + console.error(error); + } + }; + //toggle button so that if your account is frozen then you can only unfreeze it vice versa const handleFreeze = async () => { try { @@ -224,6 +284,59 @@ function AccountCard({ account, onDelete, onEdit, onDeposit, onWithdrawal}) { + + {showTransfer && ( // show this if showTransfer is true (when "E-transfer is pressed") + <> + setToEmail(e.target.value)} + /> + + + + )} + + {recipientAccounts.length > 0 && ( + + )} + + {showTransfer && ( + setAmount(e.target.value)} + + /> + )} + + + + {showTransfer && ( + + )} From cf925c4cf812acbfe51168b9a6c3995e22996c02 Mon Sep 17 00:00:00 2001 From: Jonathan Zhao Date: Tue, 1 Sep 2026 23:59:09 -0400 Subject: [PATCH 3/4] frontend: onTransfer added --- .../frontend/src/components/AccountList.jsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bank-app/frontend/src/components/AccountList.jsx b/bank-app/frontend/src/components/AccountList.jsx index 2c27516..d7a1452 100644 --- a/bank-app/frontend/src/components/AccountList.jsx +++ b/bank-app/frontend/src/components/AccountList.jsx @@ -71,6 +71,24 @@ function AccountList() { ); }; + const handleTransfer = (transactions) => { + setAccounts(prevAccounts => + prevAccounts.map(acc => { + const transaction = transactions.find( + transaction => transaction.account_id === acc.id + ); + + return transaction + ? { + ...acc, + balance: transaction.balance_after + } + : acc; + }) + ); + }; + + return (
@@ -85,6 +103,7 @@ function AccountList() { onEdit={handleEdit} onDeposit={handleDeposit} onWithdrawal={handleWithdrawal} + onTransfer={handleTransfer} /> ))}
From c211a73cb1c0fdccd5ba34de05b8543a389ab7af Mon Sep 17 00:00:00 2001 From: Jonathan Zhao Date: Wed, 2 Sep 2026 00:00:29 -0400 Subject: [PATCH 4/4] chore: fixed flake8 formatting --- bank-app/backend/main.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bank-app/backend/main.py b/bank-app/backend/main.py index a3a5f37..48ca0ff 100644 --- a/bank-app/backend/main.py +++ b/bank-app/backend/main.py @@ -224,7 +224,9 @@ def get_account_by_email(email: str, db: Session = Depends(get_db)): if accounts is None: raise HTTPException(status_code=404, detail="Recipient not found") - return [{"account_id": acc.id, "account_type": acc.account_type} for acc in accounts] + return [ + {"account_id": acc.id, "account_type": acc.account_type} for acc in accounts + ] # IMPLEMENT TO ACCOUNTCARD @@ -345,7 +347,11 @@ def transfer_transaction( to_account = db.query(Account).filter(Account.id == transaction.to_account).first() return transfer( - db, from_account.id, to_account.id, transaction.amount, transaction.idempotency_key + db, + from_account.id, + to_account.id, + transaction.amount, + transaction.idempotency_key, )