Implementing loadPaymentMethods(): Loading Credit Card and Bank Account Information

This weekend, I finished implementing the loadPaymentMethods() function to load credit card and bank account information. Below, I'll walk you through the steps I used to solve problems and break down the solution. Full Implementation async function loadPaymentMethods(userData) { if (!userData) return null; try { // user profile reference const userProfileRef = doc(db, "userProfiles", userData.email); // references to bank and credit card documents const bankAccountsRef = collection(userProfileRef, "bankAccounts"); const creditCardsRef = collection(userProfileRef, "creditCards"); // fetch bank and credit card information at the same time const [bankAccountSnapshot, creditCardsSnapshot] = await Promise.all([ getDocs(bankAccountsRef), getDocs(creditCardsRef) ]); // map bank information to an object const bankAccounts = bankAccountSnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() })); // map credit card information to an object const creditCardAccounts = creditCardsSnapshot.docs.map((doc) => ({ id: doc.id, // credit card id ...doc.data() })); // load credit card transactions const transactionPromises = creditCardAccounts.map(async (card) => { const cardId = card.id; // console.log("Card id:", cardId); const transactions = await loadCreditCardTransactions(userData, cardId); // console.log("transactions: ", transactions); return { cardId, transactions }; }); // load bank transactions const bankTransactionsPromise = bankAccounts.map(async (acc) => { const bankId = acc.id; // console.log("bank id: ", bankId); const transactions = await loadBankTransactions(userData, bankId); console.log("Bank Account transactions: ", transactions); return { bankId, transactions }; }); const allBankActivity = await Promise.all(bankTransactionsPromise); // console.log(allBankActivity); const allTransactions = await Promise.all(transactionPromises); // console.log(allTransactions); const activityElements = allBankActivity.flatMap((bankData) => { // Return the map array return bankData.transactions.map((act) => { const activityItem = document.createElement("div"); activityItem.className = "activity-item"; if (act.type === "Deposit") { activityItem.innerHTML = ` ${act.type} ${formatFirebaseDate(act.date)} +$${act.amount} ${act.currency} ${act.status} `; } else { activityItem.className = "activity-item"; activityItem.innerHTML = ` ${act.type} ${formatFirebaseDate(act.date)} $${ act.amount } ${act.currency} ${ act.status } `; } return activityItem; // Return the element }); }); const recentActivity = document.querySelector("#recent-activity"); recentActivity.innerHTML = ""; // add all elements activityElements.forEach((element) => { recentActivity.appendChild(element); }); // credit cards transactions const transactionElements = allTransactions.flatMap((cardData) => cardData.transactions.map((tran) => { const transDiv = document.createElement("div"); transDiv.className = "trans"; transDiv.innerHTML = ` ${tran.itemName} $${tran.amount} `; return transDiv; }) ); const recentTransactions = document.querySelector("#r-trans"); // clear existing content recentTransactions.innerHTML = ""; // add all transaction elements transactionElements.forEach((element) => { recentTransactions.appendChild(element); }); return { bankAccounts, creditCardAccounts }; } catch (error) { console.error("Error happened when fetching payment information"); throw error; // Propagate error to caller } } Breaking Down the Implementation Mapping Bank and Credit Card Information: Here's how we map the data to objects containing the ID and the best rest of the data from Firebase: const bankAccounts = bankAccountSnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() })); const creditCardAccounts = creditCardsSnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() })); This helps in better formatting and using the data. Loading Transactions We create promises to load all transactions: const transactionPromises = creditCardAccounts.map(async (card) => { const cardId = card.id; const transactions = await loadCreditCardTransactions(userData,

Mar 5, 2025 - 18:52
 0
Implementing loadPaymentMethods(): Loading Credit Card and Bank Account Information

This weekend, I finished implementing the loadPaymentMethods() function to load credit card and bank account information. Below, I'll walk you through the steps I used to solve problems and break down the solution.

Full Implementation

async function loadPaymentMethods(userData) {
  if (!userData) return null;
  try {
    // user profile reference
    const userProfileRef = doc(db, "userProfiles", userData.email);

    // references to bank and credit card documents
    const bankAccountsRef = collection(userProfileRef, "bankAccounts");
    const creditCardsRef = collection(userProfileRef, "creditCards");

    // fetch bank and credit card information at the same time
    const [bankAccountSnapshot, creditCardsSnapshot] = await Promise.all([
      getDocs(bankAccountsRef),
      getDocs(creditCardsRef)
    ]);

    // map bank information to an object
    const bankAccounts = bankAccountSnapshot.docs.map((doc) => ({
      id: doc.id,
      ...doc.data()
    }));

    // map credit card information to an object
    const creditCardAccounts = creditCardsSnapshot.docs.map((doc) => ({
      id: doc.id, // credit card id
      ...doc.data()
    }));

    // load credit card transactions
    const transactionPromises = creditCardAccounts.map(async (card) => {
      const cardId = card.id;
      // console.log("Card id:", cardId);
      const transactions = await loadCreditCardTransactions(userData, cardId);
      // console.log("transactions: ", transactions);
      return { cardId, transactions };
    });

    // load bank transactions
    const bankTransactionsPromise = bankAccounts.map(async (acc) => {
      const bankId = acc.id;
      // console.log("bank id: ", bankId);
      const transactions = await loadBankTransactions(userData, bankId);
      console.log("Bank Account transactions: ", transactions);
      return { bankId, transactions };
    });

    const allBankActivity = await Promise.all(bankTransactionsPromise);
    // console.log(allBankActivity);

    const allTransactions = await Promise.all(transactionPromises);
    // console.log(allTransactions);

    const activityElements = allBankActivity.flatMap((bankData) => {
      // Return the map array
      return bankData.transactions.map((act) => {
        const activityItem = document.createElement("div");
        activityItem.className = "activity-item";
        if (act.type === "Deposit") {
          activityItem.innerHTML = `
                   

${formatFirebaseDate(act.date)}

${getStatusClass( act.status )}" >${act.status}

`
; } else { activityItem.className = "activity-item"; activityItem.innerHTML = `

${formatFirebaseDate(act.date)}

${getStatusClass(act.status)}">${ act.status }

`
; } return activityItem; // Return the element }); }); const recentActivity = document.querySelector("#recent-activity"); recentActivity.innerHTML = ""; // add all elements activityElements.forEach((element) => { recentActivity.appendChild(element); }); // credit cards transactions const transactionElements = allTransactions.flatMap((cardData) => cardData.transactions.map((tran) => { const transDiv = document.createElement("div"); transDiv.className = "trans"; transDiv.innerHTML = `

${tran.itemName}

$${tran.amount} `; return transDiv; }) ); const recentTransactions = document.querySelector("#r-trans"); // clear existing content recentTransactions.innerHTML = ""; // add all transaction elements transactionElements.forEach((element) => { recentTransactions.appendChild(element); }); return { bankAccounts, creditCardAccounts }; } catch (error) { console.error("Error happened when fetching payment information"); throw error; // Propagate error to caller } }

Breaking Down the Implementation

  1. Mapping Bank and Credit Card Information:

Here's how we map the data to objects containing the ID and the best rest of the data from Firebase:

const bankAccounts = bankAccountSnapshot.docs.map((doc) => ({
  id: doc.id,
  ...doc.data()
}));
const creditCardAccounts = creditCardsSnapshot.docs.map((doc) => ({
  id: doc.id,
  ...doc.data()
}));

This helps in better formatting and using the data.

  1. Loading Transactions

We create promises to load all transactions:

const transactionPromises = creditCardAccounts.map(async (card) => {
  const cardId = card.id;
  const transactions = await loadCreditCardTransactions(userData, cardId);
  return { cardId, transactions };
});
const bankTransactionsPromise = bankAccounts.map(async (acc) => {
  const bankId = acc.id;
  const transactions = await loadBankTransactions(userData, bankId);
  return { bankId, transactions };
});

We use async before the callback function because these are API calls. We wait for all transactions to be loaded and then map them to their respective accounts.

Handling Promises:

const allBankActivity = await Promise.all(bankTransactionsPromise);
const allTransactions = await Promise.all(transactionPromises);

We use Promise.all() to wait for all promises to resolve, retrieving transactions from both credit cards and bank accounts.

Creating Activity Elements:

const activityElements = allBankActivity.flatMap((bankData) => {
  return bankData.transactions.map((act) => {
    const activityItem = document.createElement("div");
    activityItem.className = "activity-item";
    activityItem.innerHTML = `
      

${formatFirebaseDate(act.date)}

${getStatusClass(act.status)}">${act.status}

`
; return activityItem; }); });

We create elements to display recent activity for both bank accounts and credit cards, handling different types of transactions (deposits vs. withdrawals) with appropriate styling.

Stay tuned for the next post where we'll discuss the view detail UI!