Skip to content

Commit b5665a2

Browse files
committed
Enhancing the features
1 parent e63fe8e commit b5665a2

10 files changed

Lines changed: 373 additions & 158 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@
2121
npm-debug.log*
2222
yarn-debug.log*
2323
yarn-error.log*
24+
src/.env

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Cryptocurrency Tracker
22

33
This project is a cryptocurrency tracker built with React JS and the CoinGecko API. It allows you to monitor real-time prices of your favorite cryptocurrencies and visualize their historical data.
4-
![image](https://github.com/rollendxavier/crypto-portfolio-tracker-react/assets/42246854/1fb4d23d-e272-442e-aee0-14b2a9145485)
4+
5+
![image](public/crypto-dashboard.jpg)
56

67
## Prerequisites
78

177 KB
Loading

package-lock.json

Lines changed: 215 additions & 122 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
"start": "react-scripts start",
2020
"build": "react-scripts build",
2121
"test": "react-scripts test",
22-
"eject": "react-scripts eject"
22+
"eject": "react-scripts eject",
23+
"predeploy": "npm run build",
24+
"deploy": "gh-pages -d build"
2325
},
2426
"eslintConfig": {
2527
"extends": [
@@ -41,6 +43,8 @@
4143
]
4244
},
4345
"devDependencies": {
44-
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
45-
}
46-
}
46+
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
47+
"gh-pages": "^6.3.0"
48+
},
49+
"homepage": "https://rollendxavier.github.io/crypto-portfolio-tracker-react"
50+
}

public/crypto-dashboard.jpg

114 KB
Loading

src/App.css

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
body {
2-
font-family: Arial, sans-serif;
2+
background: #1F1F1F;
3+
color: #A9A9A9;
4+
font-size: 14px;
5+
margin: 0;
6+
padding: 15px 0 30px;
7+
font-family: 'Roboto', sans-serif;
38
}
49

510
.App {
6-
text-align: center;
11+
margin: 0 auto;
12+
width: 1000px;
713
}
814

915
.App-header {
10-
background-color: #ffffff;
11-
min-height: 100vh;
12-
display: flex;
13-
flex-direction: column;
14-
align-items: center;
15-
justify-content: center;
16-
color: rgb(7, 7, 7);
16+
margin: 0 auto;
17+
width: 100%;
18+
display: inline;
19+
float: left;
1720
}
1821

1922
.news-ticker {
20-
font-size: 0.9em; /* Adjust the font size */
21-
padding: 5px; /* Adjust the padding */
22-
background-color: #f9f9f9;
23+
font-size: 0.9em;
24+
padding: 5px;
25+
background-color: #333;
2326
text-decoration: none;
2427
display: inline-block;
2528
margin-right: 10px;
2629
border-radius: 3px;
2730
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
2831
transition: transform 0.3s ease-in-out;
29-
color: #690c0c; /* Make the text really black */
32+
color: #fff;
3033
}
3134

3235
.news-ticker:hover {
@@ -35,19 +38,20 @@ body {
3538

3639
.news-ticker h1 {
3740
margin: 0;
38-
font-size: 1em; /* Adjust the font size */
41+
font-size: 1em;
3942
font-weight: bold;
43+
color: #fff;
4044
}
4145

4246
.news-ticker p {
4347
margin: 2;
44-
font-size: 0.7em; /* Adjust the font size */
45-
text-align: left; /* Align the text to the left */
46-
color: rgba(25, 10, 245, 0.932);
48+
font-size: 0.7em;
49+
text-align: left;
50+
color: #bbb;
4751
}
4852

4953
h1, h2, h3 {
50-
color: rgba(31, 28, 28, 0.842);
54+
color: #ddd;
5155
}
5256

5357
table {
@@ -56,7 +60,7 @@ table {
5660
}
5761

5862
table, th, td {
59-
border: 1px solid #111111;
63+
border: 1px solid #333;
6064
padding: 8px;
6165
}
6266

@@ -69,7 +73,7 @@ th {
6973
}
7074

7175
tr:nth-child(even) {
72-
background-color: #d4aeae;
76+
background-color: #2f2f2f;
7377
}
7478

7579
.coin-details {
@@ -83,15 +87,15 @@ tr:nth-child(even) {
8387
.ReactDatePicker input {
8488
width: 100%;
8589
padding: 10px;
86-
border: 1px solid #ddd;
90+
border: 1px solid #333;
8791
border-radius: 4px;
8892
margin-bottom: 10px;
8993
}
9094

9195
select {
9296
width: 100%;
9397
padding: 10px;
94-
border: 1px solid #ddd;
98+
border: 1px solid #333;
9599
border-radius: 4px;
96100
margin-bottom: 10px;
97101
}
@@ -100,4 +104,17 @@ select {
100104
display: flex;
101105
justify-content: space-between;
102106
align-items: baseline;
103-
}
107+
}
108+
109+
.crypto-news-analyzer {
110+
width: 71%;
111+
}
112+
113+
.suggested-coin {
114+
font-family: 'Roboto', sans-serif;
115+
color: #FFA600;
116+
text-decoration: none;
117+
border: 1px solid #333;
118+
background-color: #1F1F1F;
119+
color: #A9A9A9;
120+
}

src/App.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import DatePicker from 'react-datepicker';
55
import 'react-datepicker/dist/react-datepicker.css';
66
import './App.css'; // Import the CSS
77
import NewsTicker from './components/News'; // Import the News components
8+
import CryptoNewsAnalyzer from './components/CryptoNewsAnalyzer'; // Import the CryptoNewsAnalyzer components
89

910
function CoinDetails({ coin, history }) {
1011
// Prepare the data for the line chart
@@ -15,7 +16,7 @@ function CoinDetails({ coin, history }) {
1516
<div className="coin-details">
1617
<h2>{coin.name} ({coin.symbol.toUpperCase()})</h2>
1718
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', width: '100%' }}>
18-
<span style={{ color: priceChangeColor, fontSize: '0.8em' }}>
19+
<span style={{ color: priceChangeColor, fontSize: '0.8em', top: 2, right: 1200, }}>
1920
{coin.price_change_24h > 0 && '+'}{coin.price_change_24h?.toFixed(2)}%
2021
</span>
2122
<p style={{ fontSize: '2em', margin: 0 }}>
@@ -43,17 +44,32 @@ const holdingsData = [
4344

4445
function App() {
4546
const [coins, setCoins] = useState([]);
47+
const [allCoins, setAllCoins] = useState([]);
4648
const [selectedCoin, setSelectedCoin] = useState(null);
4749
const [history, setHistory] = useState([]);
4850
const [loading, setLoading] = useState(true);
4951
const [startDate, setStartDate] = useState(new Date());
5052
const [endDate, setEndDate] = useState(new Date());
51-
const api_key = 'YOUR_API_KEY'; // replace this with your own API key
53+
const api_key = process.env.REACT_APP_COIN_GECKP_API_KEY;
5254

5355
const holdings = useMemo(() => holdingsData, []);
5456

5557
const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042']; // add more colors if you have more coins
5658

59+
useEffect(() => {
60+
axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd`, {
61+
headers: {
62+
'X-CoinAPI-Key': api_key
63+
}
64+
})
65+
.then(response => {
66+
setAllCoins(response.data);
67+
})
68+
.catch(error => {
69+
console.error('Error fetching all coins', error);
70+
});
71+
}, []);
72+
5773
useEffect(() => {
5874
axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${holdings.map(holding => holding.id).join(',')}`, {
5975
headers: {
@@ -81,7 +97,7 @@ function App() {
8197

8298
const handleCoinSelect = (id) => {
8399
setLoading(true);
84-
const selected = coins.find(coin => coin.id === id);
100+
const selected = allCoins.find(coin => coin.id === id);
85101
setSelectedCoin(selected);
86102

87103
// Fetch historical data for the selected date range
@@ -90,7 +106,7 @@ function App() {
90106
while (date <= endDate) {
91107
const formattedDate = `${date.getDate()}-${date.getMonth() + 1}-${date.getFullYear()}`;
92108

93-
const promise = axios.get(`http://localhost:3001/api/v3/coins/${id}/history?date=${formattedDate}`, {
109+
const promise = axios.get(`https://api.coingecko.com/api/v3/coins/${id}/history?date=${formattedDate}`, {
94110
headers: {
95111
'X-CoinAPI-Key': api_key
96112
}
@@ -133,7 +149,7 @@ function App() {
133149
) : (
134150
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100vh' }}>
135151
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', width: '100%', height: '100%' }}>
136-
<div style={{ flex: 1, border: '1px solid black', margin: '10px', padding: '20px', borderRadius: '10px', boxSizing: 'border-box' }}>
152+
<div style={{ flex: 1, border: '1px solid #333', margin: '10px', padding: '20px', borderRadius: '10px', boxSizing: 'border-box', backgroundColor: '#1F1F1F', color: '#A9A9A9' }}>
137153
<h2>Portfolio Tracker</h2>
138154
<ResponsiveContainer width="100%" height={400}>
139155
<PieChart>
@@ -180,16 +196,19 @@ function App() {
180196
<h3>ROI: {roi.toFixed(2)}%</h3>
181197
<h3>P&L: ${pnl.toFixed(2)}</h3>
182198
</div>
183-
<div style={{ flex: 1, border: '1px solid black', margin: '10px', padding: '20px', borderRadius: '10px', boxSizing: 'border-box' }}>
199+
<div style={{ flex: 1, border: '1px solid #333', margin: '10px', padding: '20px', borderRadius: '10px', boxSizing: 'border-box', backgroundColor: '#1F1F1F', color: '#A9A9A9' }}>
184200
<h2>Trends</h2>
185201
<select value={selectedCoin ? selectedCoin.id : ''} onChange={e => handleCoinSelect(e.target.value)}>
186-
{coins.map(coin => (
202+
{allCoins.map(coin => (
187203
<option key={coin.id} value={coin.id}>{coin.name} ({coin.symbol.toUpperCase()})</option>
188204
))}
189205
</select>
190206
<DatePicker selected={startDate} onChange={date => setStartDate(date)} />
191207
<DatePicker selected={endDate} onChange={date => setEndDate(date)} />
192208
{selectedCoin && <CoinDetails coin={selectedCoin} history={history} />}
209+
</div>
210+
<div style={{ flex: 1, border: '1px solid #333', margin: '10px', padding: '20px', borderRadius: '10px', boxSizing: 'border-box', backgroundColor: '#1F1F1F', color: '#A9A9A9' }}>
211+
<CryptoNewsAnalyzer coins={allCoins} />
193212
</div>
194213
</div>
195214
</div>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React, { useEffect, useState } from 'react';
2+
import axios from 'axios';
3+
4+
function CryptoNewsAnalyzer({ coins }) {
5+
const [suggestedCoins, setSuggestedCoins] = useState([]);
6+
const [isLoading, setIsLoading] = useState(true);
7+
const [error, setError] = useState(null);
8+
9+
useEffect(() => {
10+
const apiKey = process.env.REACT_APP_NEWS_API_KEY; // Use the environment variable
11+
axios.get(`https://newsapi.org/v2/everything?q=cryptocurrency&apiKey=${apiKey}`)
12+
.then(response => {
13+
const positiveKeywords = ['bull', 'rally', 'rise', 'gain', 'positive', 'buy', 'up'];
14+
const articles = response.data.articles;
15+
const suggestedCoins = [];
16+
17+
articles.forEach(article => {
18+
const title = article.title ? article.title.toLowerCase() : '';
19+
const description = article.description ? article.description.toLowerCase() : '';
20+
21+
positiveKeywords.forEach(keyword => {
22+
if (title.includes(keyword) || description.includes(keyword)) {
23+
// Check if a cryptocurrency name is mentioned in the title or description
24+
coins.forEach(coin => {
25+
if (coin.name && (title.includes(coin.name.toLowerCase()) || description.includes(coin.name.toLowerCase()))) {
26+
suggestedCoins.push(coin);
27+
}
28+
});
29+
}
30+
});
31+
});
32+
33+
// Remove duplicates
34+
const uniqueSuggestedCoins = Array.from(new Set(suggestedCoins.map(coin => coin.id)))
35+
.map(id => suggestedCoins.find(coin => coin.id === id));
36+
37+
// Sort by 24h price change and select top 10
38+
uniqueSuggestedCoins.sort((a, b) => b.price_change_24h - a.price_change_24h);
39+
setSuggestedCoins(uniqueSuggestedCoins.slice(0, 10));
40+
41+
setIsLoading(false);
42+
})
43+
.catch(error => {
44+
console.error(error);
45+
setError('Failed to fetch news');
46+
setIsLoading(false);
47+
});
48+
}, [coins]);
49+
50+
if (isLoading) {
51+
return <div>Loading...</div>;
52+
}
53+
54+
if (error) {
55+
return <div>{error}</div>;
56+
}
57+
58+
return (
59+
<div className="crypto-news-analyzer">
60+
<h2>Top 10 Cryptocurrencies to Buy Based on News Sentiment</h2>
61+
{suggestedCoins.map((coin, index) => (
62+
<div key={index} className="suggested-coin" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '300px' }}>
63+
<div style={{ display: 'flex', alignItems: 'center' }}>
64+
<img src={coin.image} alt={coin.name} style={{ width: '20px', height: '20px', marginRight: '10px' }} />
65+
<h3 style={{ margin: 0 }}>{coin.name} ({coin.symbol.toUpperCase()})</h3>
66+
</div>
67+
<div style={{ position: 'relative', width: '100%' }}>
68+
<p style={{ fontSize: '1.9em' }}>{coin.current_price.toFixed(2)}</p>
69+
<span style={{ position: 'absolute', top: 2, right: 120, color: coin.price_change_24h < 0 ? 'red' : 'green' }}>
70+
{coin.price_change_24h.toFixed(2)}
71+
</span>
72+
</div>
73+
</div>
74+
))}
75+
</div>
76+
);
77+
}
78+
79+
export default CryptoNewsAnalyzer;

src/components/News.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ function NewsTicker() {
88
const [error, setError] = useState(null);
99

1010
useEffect(() => {
11-
axios.get(`https://newsapi.org/v2/everything?q=cryptocurrency&apiKey=YOUR_API_KEY`)
11+
const apiKey = process.env.REACT_APP_NEWS_API_KEY; // Use the environment variable
12+
axios.get(`https://newsapi.org/v2/everything?q=cryptocurrency&apiKey=${apiKey}`)
1213
.then(response => {
1314
setNews(response.data.articles);
1415
setIsLoading(false);

0 commit comments

Comments
 (0)