├── crypto-project ├── .firebaserc ├── public │ ├── 1.PNG │ ├── 2.PNG │ ├── 3.PNG │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── index.html │ └── 404.html ├── postcss.config.js ├── firebase.json ├── src │ ├── routes │ │ ├── Home.jsx │ │ ├── Account.jsx │ │ ├── Signin.jsx │ │ ├── Signup.jsx │ │ └── CoinPage.jsx │ ├── index.js │ ├── index.css │ ├── components │ │ ├── ThemeToggle.jsx │ │ ├── Trending.jsx │ │ ├── CoinSearch.jsx │ │ ├── CoinItem.jsx │ │ ├── SavedCoin.jsx │ │ ├── Footer.jsx │ │ └── Navbar.jsx │ ├── firebase.js │ ├── context │ │ ├── ThemeContext.jsx │ │ └── AuthContext.jsx │ └── App.js ├── .env ├── .gitignore ├── .firebase │ ├── hosting.YnVpbGQ.cache │ └── hosting.cHVibGlj.cache ├── tailwind.config.js ├── package.json └── README.md ├── README.md └── .github └── workflows ├── firebase-hosting-merge.yml └── firebase-hosting-pull-request.yml /crypto-project/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "cryptobase-bf85a" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /crypto-project/public/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linh777GKOG/crypto-app/HEAD/crypto-project/public/1.PNG -------------------------------------------------------------------------------- /crypto-project/public/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linh777GKOG/crypto-app/HEAD/crypto-project/public/2.PNG -------------------------------------------------------------------------------- /crypto-project/public/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linh777GKOG/crypto-app/HEAD/crypto-project/public/3.PNG -------------------------------------------------------------------------------- /crypto-project/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /crypto-project/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linh777GKOG/crypto-app/HEAD/crypto-project/public/favicon.ico -------------------------------------------------------------------------------- /crypto-project/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linh777GKOG/crypto-app/HEAD/crypto-project/public/logo192.png -------------------------------------------------------------------------------- /crypto-project/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linh777GKOG/crypto-app/HEAD/crypto-project/public/logo512.png -------------------------------------------------------------------------------- /crypto-project/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /crypto-project/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A multi-page reactjs application. 2 | ![](https://raw.githubusercontent.com/Linh777GKOG/crypto-app/master/crypto-project/public/1.PNG) 3 | 4 | ![](https://raw.githubusercontent.com/Linh777GKOG/crypto-app/master/crypto-project/public/2.PNG) 5 | 6 | ![](https://raw.githubusercontent.com/Linh777GKOG/crypto-app/master/crypto-project/public/3.PNG) 7 | -------------------------------------------------------------------------------- /crypto-project/src/routes/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CoinSearch from '../components/CoinSearch'; 3 | import Trending from '../components/Trending'; 4 | 5 | const Home = ({ coins }) => { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | }; 13 | 14 | export default Home; 15 | -------------------------------------------------------------------------------- /crypto-project/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /crypto-project/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_FIREBASE_API_KEY= AIzaSyAMdjD7pTkXBkGIAPB67zsPI5HU0B4A6V8 2 | REACT_APP_FIREBASE_AUTH_DOMAIN= cryptobase-bf85a.firebaseapp.com 3 | REACT_APP_FIREBASE_PROJECT_ID= cryptobase-bf85a 4 | REACT_APP_FIREBASE_STORAGE_BUCKET= cryptobase-bf85a.appspot.com 5 | REACT_APP_FIREBASE_MESSAGING_SENDER= 783998547168 6 | REACT_APP_FIREBASE_APP_ID= 1:783998547168:web:95247d78a5fc3f66a9cc85 -------------------------------------------------------------------------------- /crypto-project/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .vercel 25 | -------------------------------------------------------------------------------- /crypto-project/.firebase/hosting.YnVpbGQ.cache: -------------------------------------------------------------------------------- 1 | favicon.ico,1660296587293,eae62e993eb980ec8a25058c39d5a51feab118bd2100c4deebb2a9c158ec11f9 2 | logo192.png,1660296587293,3ee59515172ee198f3be375979df15ac5345183e656720a381b8872b2a39dc8b 3 | logo512.png,1660296587293,ee7e2f3fdb8209c4b6fd7bef6ba50d1b9dba30a25bb5c3126df057e1cb6f5331 4 | manifest.json,1660296587293,aff3449bdc238776f5d6d967f19ec491b36aed5fb7f23ccff6500736fd58494a 5 | robots.txt,1660296587293,bfe106a3fb878dc83461c86818bf74fc1bdc7f28538ba613cd3e775516ce8b49 6 | -------------------------------------------------------------------------------- /crypto-project/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-merge.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on merge 5 | 'on': 6 | push: 7 | branches: 8 | - master 9 | jobs: 10 | build_and_deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: FirebaseExtended/action-hosting-deploy@v0 15 | with: 16 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 17 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_CRYPTOBASE_BF85A }}' 18 | channelId: live 19 | projectId: cryptobase-bf85a 20 | -------------------------------------------------------------------------------- /crypto-project/.firebase/hosting.cHVibGlj.cache: -------------------------------------------------------------------------------- 1 | 404.html,1662471464494,daa499dd96d8229e73235345702ba32f0793f0c8e5c0d30e40e37a5872be57aa 2 | index.html,1660296587293,820c850791eeaef190f4564272c4cbe718a2f8ead4973eeacc12aa8473f3b4c5 3 | favicon.ico,1660296587293,eae62e993eb980ec8a25058c39d5a51feab118bd2100c4deebb2a9c158ec11f9 4 | logo192.png,1660296587293,3ee59515172ee198f3be375979df15ac5345183e656720a381b8872b2a39dc8b 5 | manifest.json,1660296587293,aff3449bdc238776f5d6d967f19ec491b36aed5fb7f23ccff6500736fd58494a 6 | robots.txt,1660296587293,bfe106a3fb878dc83461c86818bf74fc1bdc7f28538ba613cd3e775516ce8b49 7 | logo512.png,1660296587293,ee7e2f3fdb8209c4b6fd7bef6ba50d1b9dba30a25bb5c3126df057e1cb6f5331 8 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-pull-request.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on PR 5 | 'on': pull_request 6 | jobs: 7 | build_and_preview: 8 | if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: FirebaseExtended/action-hosting-deploy@v0 13 | with: 14 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 15 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_CRYPTOBASE_BF85A }}' 16 | projectId: cryptobase-bf85a 17 | -------------------------------------------------------------------------------- /crypto-project/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./src/**/*.{js,jsx,ts,tsx}'], 3 | darkMode: 'class', 4 | theme: { 5 | extend: { 6 | backgroundColor: { 7 | primary: 'var(--color-bg-primary)', 8 | secondary: 'var(--color-bg-secondary)', 9 | button: 'var(--color-bg-button)', 10 | }, 11 | textColor: { 12 | accent: 'var(--color-text-accent)', 13 | primary: 'var(--color-text-primary)', 14 | secondary: 'var(--color-text-secondary)', 15 | btnText: 'var(--color-bg-secondary)', 16 | }, 17 | borderColor: { 18 | primary: 'var(--color-bg-primary)', 19 | secondary: 'var(--color-bg-secondary)', 20 | input: 'var(--color-bg-input)', 21 | accent: 'var(--color-text-accent)', 22 | }, 23 | }, 24 | }, 25 | plugins: [], 26 | }; 27 | -------------------------------------------------------------------------------- /crypto-project/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | @apply bg-primary text-primary; 7 | } 8 | 9 | .rounded-div { 10 | @apply border border-secondary rounded-2xl shadow-xl bg-primary px-2 max-w-[1140px] w-full mx-auto; 11 | } 12 | 13 | .dark { 14 | --color-bg-primary: #2d3748; 15 | --color-bg-primary: #2d3748; 16 | --color-bg-secondary: #283141; 17 | --color-text-primary: #dbdbdb; 18 | --color-text-secondary: #e2e8f0; 19 | --color-text-accent: #81e6d9; 20 | --color-bg-input: #4a5361; 21 | --color-bg-button: #81e6d9; 22 | } 23 | 24 | .light { 25 | --color-bg-primary: #ffffff; 26 | --color-bg-secondary: #edf2f7; 27 | --color-text-primary: #2d3748; 28 | --color-text-secondary: #4a5568; 29 | --color-text-accent: #2b6cb0; 30 | --color-bg-input: #edf2f7; 31 | --color-bg-button: #2b6cb0; 32 | } 33 | -------------------------------------------------------------------------------- /crypto-project/src/components/ThemeToggle.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { HiSun, HiMoon } from 'react-icons/hi'; 3 | import { ThemeContext } from '../context/ThemeContext'; 4 | 5 | const ThemeToggle = () => { 6 | const { theme, setTheme } = useContext(ThemeContext); 7 | 8 | return ( 9 |
10 | {theme === 'dark' ? ( 11 |
setTheme(theme === 'dark' ? 'light' : 'dark')} 14 | > 15 | Light Mode 16 |
17 | ) : ( 18 |
setTheme(theme === 'dark' ? 'light' : 'dark')} 21 | > 22 | Dark Mode 23 |
24 | )} 25 |
26 | ); 27 | }; 28 | 29 | export default ThemeToggle; 30 | -------------------------------------------------------------------------------- /crypto-project/src/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from 'firebase/app'; 3 | import { getAuth } from 'firebase/auth'; 4 | import { getFirestore } from 'firebase/firestore'; 5 | // TODO: Add SDKs for Firebase products that you want to use 6 | // https://firebase.google.com/docs/web/setup#available-libraries 7 | 8 | // Your web app's Firebase configuration 9 | const firebaseConfig = { 10 | apiKey: process.env.REACT_APP_FIREBASE_API_KEY, 11 | authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, 12 | projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID, 13 | storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET, 14 | messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER, 15 | appId: process.env.REACT_APP_FIREBASE_APP_ID, 16 | }; 17 | 18 | // Initialize Firebase 19 | const app = initializeApp(firebaseConfig); 20 | export const auth = getAuth(app); 21 | export const db = getFirestore(app); 22 | 23 | export default app; 24 | -------------------------------------------------------------------------------- /crypto-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-project", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^13.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "axios": "^0.27.2", 10 | "dompurify": "^2.3.8", 11 | "firebase": "^9.8.2", 12 | "react": "^18.1.0", 13 | "react-dom": "^18.1.0", 14 | "react-icons": "^4.3.1", 15 | "react-router-dom": "^6.3.0", 16 | "react-scripts": "5.0.1", 17 | "react-sparklines": "^1.7.0", 18 | "web-vitals": "^2.1.0" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "autoprefixer": "^10.4.7", 46 | "postcss": "^8.4.14", 47 | "tailwindcss": "^3.0.24" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crypto-project/src/context/ThemeContext.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, createContext } from 'react'; 2 | 3 | const getInitialTheme = () => { 4 | if (typeof window !== 'undefined' && window.localStorage) { 5 | const storedPrefs = window.localStorage.getItem('color-theme'); 6 | if (typeof storedPrefs === 'string') { 7 | return storedPrefs; 8 | } 9 | 10 | const userMedia = window.matchMedia('(prefers-color-scheme: dark)'); 11 | if (userMedia.matches) { 12 | return 'dark'; 13 | } 14 | } 15 | return 'light'; 16 | }; 17 | 18 | export const ThemeContext = createContext(); 19 | 20 | export const ThemeProvider = ({ initialTheme, children }) => { 21 | const [theme, setTheme] = useState(getInitialTheme); 22 | 23 | const rawSetTheme = (theme) => { 24 | const root = window.document.documentElement; 25 | const isDark = theme === 'dark'; 26 | 27 | root.classList.remove(isDark ? 'light' : 'dark'); 28 | root.classList.add(theme); 29 | 30 | localStorage.setItem('color-theme', theme); 31 | }; 32 | 33 | if (initialTheme) { 34 | rawSetTheme(initialTheme); 35 | } 36 | 37 | useEffect(() => { 38 | rawSetTheme(theme); 39 | }, [theme]); 40 | 41 | return ( 42 | 43 | {children} 44 | 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /crypto-project/src/context/AuthContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useState, useEffect } from 'react'; 2 | import { auth, db } from '../firebase'; 3 | import { 4 | createUserWithEmailAndPassword, 5 | signInWithEmailAndPassword, 6 | signOut, 7 | onAuthStateChanged, 8 | } from 'firebase/auth'; 9 | 10 | import { doc, setDoc } from 'firebase/firestore'; 11 | 12 | const UserContext = createContext(); 13 | 14 | export const AuthContextProvider = ({ children }) => { 15 | const [user, setUser] = useState({}); 16 | 17 | const signUp = (email, password) => { 18 | createUserWithEmailAndPassword(auth, email, password); 19 | return setDoc(doc(db, 'users', email), { 20 | watchList: [], 21 | }); 22 | }; 23 | const signIn = (email, password) => { 24 | return signInWithEmailAndPassword(auth, email, password); 25 | }; 26 | 27 | const logout = () => { 28 | return signOut(auth); 29 | }; 30 | 31 | useEffect(() => { 32 | const unsubscribe = onAuthStateChanged(auth, (currentUser) => { 33 | setUser(currentUser); 34 | }); 35 | return () => { 36 | unsubscribe(); 37 | }; 38 | }, []); 39 | 40 | return ( 41 | 42 | {children} 43 | 44 | ); 45 | }; 46 | 47 | export const UserAuth = () => { 48 | return useContext(UserContext); 49 | }; 50 | -------------------------------------------------------------------------------- /crypto-project/src/routes/Account.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SavedCoin from '../components/SavedCoin'; 3 | import { UserAuth } from '../context/AuthContext'; 4 | import { Navigate, useNavigate } from 'react-router-dom'; 5 | 6 | const Account = () => { 7 | const { user, logout } = UserAuth(); 8 | const navigate = useNavigate(); 9 | 10 | const handleSignOut = async () => { 11 | try { 12 | await logout(); 13 | navigate('/'); 14 | } catch (e) { 15 | console.log(e.message); 16 | } 17 | }; 18 | 19 | if (user) { 20 | return ( 21 |
22 |
23 |
24 |

Account

25 |
26 |

Welcome, {user?.email}

27 |
28 |
29 |
30 | 36 |
37 |
38 |
39 |
40 |

Watch List

41 | 42 |
43 |
44 |
45 | ); 46 | } else { 47 | return ; 48 | } 49 | }; 50 | 51 | export default Account; 52 | -------------------------------------------------------------------------------- /crypto-project/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Route, Routes } from 'react-router-dom'; 3 | import Navbar from './components/Navbar'; 4 | import { ThemeProvider } from './context/ThemeContext'; 5 | import Home from './routes/Home'; 6 | import Signin from './routes/Signin'; 7 | import Signup from './routes/Signup'; 8 | import Account from './routes/Account'; 9 | import axios from 'axios'; 10 | import CoinPage from './routes/CoinPage'; 11 | import Footer from './components/Footer'; 12 | import { AuthContextProvider } from './context/AuthContext'; 13 | 14 | function App() { 15 | const [coins, setCoins] = useState([]); 16 | 17 | const url = 18 | 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=10&page=1&sparkline=true'; 19 | 20 | useEffect(() => { 21 | axios.get(url).then((response) => { 22 | setCoins(response.data); 23 | // console.log(response.data) 24 | }); 25 | }, [url]); 26 | 27 | return ( 28 | 29 | 30 | 31 | 32 | } /> 33 | } /> 34 | } /> 35 | } /> 36 | }> 37 | 38 | 39 | 40 |