Core Components - BojkovaA/CryptoTracker GitHub Wiki

Homepage Component

components/HomePageComponent.vue
Purpose:
The homepage is the landing screen for unauthenticated users. It includes:

  • A promotional hero section with a CTA (Call To Action) to register.

  • Cryptocurrency card previews (CoinCardsComponent).

  • User portfolio display (MyPortfolio) and a chart of popular coins (PopularCoinsChart).

  • Live market section (LiveMarketComponent).

Main Features:

  • Authentication State Check using Firebase Auth to determine if the user is logged in.

  • Conditionally renders content based on the authentication state (isLoggedIn).

  • Loads child components that visualize and display market and portfolio data.

onMounted(() =>{
    onAuthStateChanged(auth, (user) => {
        if (user) {
            isLoggedIn.value = true;
        } else {
            isLoggedIn.value = false;
        }
        isLoading.value = false;
    });
});

When the component is mounted, Firebase’s onAuthStateChanged is used to check if the user is logged in. The result updates isLoggedIn which conditionally renders the content on the page.

Navbar Component

components/NavbarComponent.vue
Purpose:
The navigation bar appears on all pages. It includes:

  • Logo and brand name.

  • Search functionality (only on home / route).

  • Navigation links (e.g. to Portfolio).

  • Authenticated user information (greeting, balance, logout).

  • Login/Register buttons for unauthenticated users.

Main Features:

  • Search bar that filters coins dynamically from the store (Pinia).

  • Displays user balance from Firestore in real time using onSnapshot.

  • Dynamic route-based rendering using vue-router.

  • Logout functionality using Firebase Auth.

onMounted(() => {
    onAuthStateChanged(auth, async(firebaseUser) => {
      if (firebaseUser) {
          user.value = firebaseUser; 
          const userDocRef = doc(db, "users", firebaseUser.uid);
          onSnapshot(userDocRef, (docSnap) => {
              if (docSnap.exists()) {
                  userData.value = docSnap.data();
              }
          });
      } else {
          user.value = null;
          userData.value = { balance: 0 };
      }
  });
});

Checks if a user is logged in and listens for real-time updates to their document in Firestore. Automatically updates balance in the navbar when it changes.

Coin Cards Component

components/CoinCardsComponent.vue
Purpose:
This component displays a scrollable carousel of the top 50 cryptocurrencies using a clean, card-based layout.
Each card includes:

  • The coin's name and symbol

  • Coin logo

  • A description (placeholder text for now)

  • An "Invest" button which navigates the user based on their authentication status

Libraries Used:

  • vue3-carousel: Carousel library for Vue 3.
  • primeicons: Icon pack used for the arrow icon on the Invest button.

Carousel Setup

const config = {
  height: 450,
  itemsToShow: 4,
  gap: 5,
  breakpoints: {
    320: { itemsToShow: 1 },
    768: { itemsToShow: 2 },
    1024: { itemsToShow: 3 },
    1280: { itemsToShow: 4 },
  },
  wrapAround: true,

This object defines the configuration for the carousel:

  • Responsive breakpoints: Different number of cards shown per screen size.

  • wrapAround: true: Enables infinite loop scrolling.

  • itemsToShow: Controls how many cards appear at once.

  • gap: Space between cards.

Carousel Rendering

<Carousel v-bind="config" :loop="true" :navigation="true" :pagination="true">
  • The carousel renders with the above config.
  • loop, navigation, and pagination are enabled for better UX.

Each Slide:

<Slide v-for="coin in coinStore.coinDataTop50" :key="coin">
  <div class="...">
    <img :src="`/static/${coin.symbol.toLowerCase()}.png`" />
    <h3>{{ coin.symbol }}</h3>
    <span>{{ coin.name }}</span>
    ...
    <button @click="handleInvest(coin.name)">
      Invest in {{ coin.name }} <span class="pi pi-arrow-right" />
    </button>
  </div>
</Slide>

A Slide is created for each coin from the top 50 list (retrieved from the Pinia store).
Each slide contains:

  • Coin image from a static folder
  • Coin name and symbol
  • Placeholder description
  • Button to "Invest"

Add-ons

<template #addons>
  <Navigation />
  <Pagination />
</template>

These are extra UI features provided by vue3-carousel to show navigation arrows and dots for switching between slides.

Popular Coins Chart Component

components/PopularCoinsChartComponent.vue
Purpose:
This component displays line charts for the top 4 most popular cryptocurrencies, based on their 24-hour trading volume.
The charts are built using the Chart.js library via the vue-chartjs wrapper.

Used Libraries:

  • chart.js – chart rendering library

  • vue-chartjs – Vue 3 wrapper for Chart.js

  • chartService.js – a custom service for fetching chart data from an API

  • pinia store (coinStore) – to access the list of top 50 coins

Chart Configuration (chartOptions)

The chartOptions object defines the look and behavior of each chart:

const chartOptions = {
  responsive: true,
  interaction: {
    mode: "index",
    intersect: true,
  },
  plugins: {
    tooltip: {
      enabled: true,
      mode: "index",
      intersect: true,
      callbacks: {
        label: function (context) {
          return `Price: $${formatPrice(context.parsed.y)}`;
        },
      },
    },
  },
  scales: {
    x: { display: false },
    y: { display: true },
  },
};
  • responsive: true – makes the chart responsive to screen size
  • interaction.mode: "index" – shows tooltips for all data points at the same x-value
  • tooltip.callbacks.label – customizes tooltip to show price
  • scales.x.display: false – hides the x-axis labels
  • scales.y.display: true – shows the y-axis

How Data Is Loaded

The top 4 coins are calculated like this:

const topCoins = computed(() => {
  return [...coinsStore.coinDataTop50]
    .sort((a, b) => parseFloat(b.volumeUsd24Hr) - parseFloat(a.volumeUsd24Hr))
    .slice(0, 4);
});

Each coin’s price history is fetched by ChartService.getChart() and transformed into:

chartData.value[coin.id] = {
  labels: [...],
  datasets: [{
    label: "BTC Цена во USD",
    data: [...],
    backgroundColor: "...",
    borderColor: "...",
    borderWidth: 2,
    fill: true
  }]
}

This format is used directly by the component from vue-chartjs.

What It Shows in the Template

In the template:

  • A title: "Today's most popular coins chart"
  • For each of the top 4 coins:
  • A chart showing the price over time
  • If data is missing, a fallback message is shown
<Line
  v-if="chartData[coin.id] && chartData[coin.id].labels.length > 0"
  :data="chartData[coin.id]"
  :options="chartOptions"
/>
<p v-else class="text-white text-center">
  Error fetching chart data for {{ coin.symbol }}
</p>

Wallet Component

components/WalletComponent.vue
This component handles a simple wallet where authenticated users can view their balance, add funds, and see a transaction history, using Firebase Auth and Firestore.

Authentication-Aware Wallet

  • Uses onAuthStateChanged from Firebase Auth to detect when the user is logged in.
  • Only fetches wallet and transaction data once the user is authenticated.
onMounted(() => {
  onAuthStateChanged(auth, (user) => {
    if (user) {
      fetchWallet();
    }
  });
});

Balance and Transaction Fetching

fetchWallet() retrieves the current user's balance and transaction history from Firestore. It gets:

  • Balance from users/{uid} document.
  • Transactions from users/{uid}/transactions subcollection, sorted by most recent.
const fetchWallet = async () => {
  const userRef = doc(db, "users", user.uid);
  const userSnap = await getDoc(userRef);
  balance.value = data.balance || 0;
  
  const transSnap = await getDocs(transRef);
  transactions.value = transSnap.docs.map(...).sort(...);
};

Add Funds with Confirmation Logic

  • User inputs an amount to add to the wallet.
  • If the amount is less than 00,000, funds are added immediately.
  • If the amount is 00,000 or more, a confirmation popup is shown first.
const confirmAddFunds = () => {
  if (amount >= 100000) {
    showConfirm.value = true;
  } else {
    addFunds(amount);
  }
};

addFunds(amount) Function

This function handles the full logic of adding funds to the user’s wallet:

  • Prevents spamming using isProcessing guard.
  • Updates balance in Firestore.
  • Creates a new transaction in users/{uid}/transactions.
  • Adds visual feedback:
  • Success or error message
  • "Flash" animation for balance change
  • Auto-refresh wallet data after update
await updateDoc(userRef, { balance: newBalance });
await addDoc(transRef, {
  type: "Add Funds",
  description: "Added Funds",
  amount,
  timestamp: now,
});

Includes a 5-second duplication protection so the same transaction can't be spammed.

⚠️ **GitHub.com Fallback** ⚠️