Core Components - BojkovaA/CryptoTracker GitHub Wiki
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.
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.
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.
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 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"
<template #addons>
<Navigation />
<Pagination />
</template>
These are extra UI features provided by vue3-carousel to show navigation arrows and dots for switching between slides.
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
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
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.
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>
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.
- 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();
}
});
});
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(...);
};
- 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);
}
};
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.