DEV Community

Datravous Odds
Datravous Odds

Posted on

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 = `
                   <div class="activity-item-wrapper">
                    <label class="details-label font-500">${act.type}</label>
                    <p class="detail-date">${formatFirebaseDate(act.date)}</p>
                    </div>
                    <div class="activity-item-wrapper">
                    <label class="detail-value font-500 deposit">
                       +$${act.amount} ${act.currency}
                      </label>
                      <p class="status-indicator ${getStatusClass(
                        act.status
                      )}" >${act.status}</p>
                    </div>`;
        } else {
          activityItem.className = "activity-item";
          activityItem.innerHTML = `
                  <div class="activity-item-wrapper">
                    <label class="details-label font-500">${act.type}</label>
                    <p class="detail-date">${formatFirebaseDate(act.date)}</p>

                  </div>
                  <div class="activity-item-wrapper">
                    <label class="detail-value font-500 withdraw">$${
                      act.amount
                    } ${act.currency}</label>
                    <p class="status-indicator ${getStatusClass(act.status)}">${
            act.status
          }</p>
                  </div>`;
        }
        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 = `
            <p>${tran.itemName}</p>
            <p>$${tran.amount}</p>
            `;
        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
  }
}
Enter fullscreen mode Exit fullscreen mode

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()
}));
Enter fullscreen mode Exit fullscreen mode

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 };
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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 = `
      <div class="activity-item-wrapper">
        <label class="details-label font-500">${act.type}</label>
        <p class="detail-date">${formatFirebaseDate(act.date)}</p>
      </div>
      <div class="activity-item-wrapper">
        <label class="detail-value font-500 ${act.type === "Deposit" ? "deposit" : "withdraw"}">
          ${act.type === "Deposit" ? "+" : ""}$${act.amount} ${act.currency}
        </label>
        <p class="status-indicator ${getStatusClass(act.status)}">${act.status}</p>
      </div>`;
    return activityItem;
  });
});
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)