coin list finished
This commit is contained in:
parent
c7ab55ceb5
commit
5ebf4a1a46
1005
Front/package-lock.json
generated
1005
Front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,12 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@fontsource/roboto": "^4.5.8",
|
||||
"@mui/icons-material": "^5.10.16",
|
||||
"@mui/material": "^5.10.17",
|
||||
"@mui/styled-engine-sc": "^5.10.16",
|
||||
"@reduxjs/toolkit": "^1.9.1",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
@ -21,6 +27,7 @@
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-scripts": "5.0.1",
|
||||
"styled-components": "^5.3.6",
|
||||
"typescript": "^4.9.3",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
@ -47,5 +54,11 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"overrides": {
|
||||
"about": "needed because mui-datatables is not ready for react 18",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"@mui/material": "^5.10.17"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
@ -24,12 +24,13 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
<title>React App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
@ -39,5 +40,6 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,23 +1,65 @@
|
||||
import './App.css';
|
||||
|
||||
import {
|
||||
BrowserRouter,
|
||||
Routes,
|
||||
Route
|
||||
} from "react-router-dom";
|
||||
// Route
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
|
||||
// Components
|
||||
import Header from './Header'
|
||||
import CoinList from './coinList/coinList';
|
||||
import CoinDetails from './coinDetails/coinDetails';
|
||||
|
||||
import * as React from 'react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Box from '@mui/material/Box';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Container from '@mui/material/Container';
|
||||
|
||||
const ColorModeContext = React.createContext({ toggleColorMode: () => { } });
|
||||
|
||||
function App() {
|
||||
const [mode, setMode] = React.useState<'light' | 'dark'>('light');
|
||||
const colorMode = React.useMemo(
|
||||
() => ({
|
||||
toggleColorMode: () => {
|
||||
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const theme = React.useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
palette: {
|
||||
mode,
|
||||
},
|
||||
}),
|
||||
[mode],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<CoinList />} />
|
||||
<Route path="/coin/:id" element={<CoinDetails />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
<ColorModeContext.Provider value={colorMode}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<Header colorMode={colorMode} theme={theme} />
|
||||
<Container maxWidth="md">
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid container
|
||||
spacing={0}
|
||||
direction="column"
|
||||
justifyContent="center"
|
||||
>
|
||||
{/* <Grid item xs={12}> */}
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<CoinList />} />
|
||||
<Route path="/coin/:id" element={<CoinDetails />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
{/* </Grid> */}
|
||||
</Grid>
|
||||
</Box>
|
||||
</Container>
|
||||
</ThemeProvider>
|
||||
</ColorModeContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
37
Front/src/Header.tsx
Normal file
37
Front/src/Header.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
// Material UI
|
||||
import * as React from 'react';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Button from '@mui/material/Button';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Brightness4Icon from '@mui/icons-material/Brightness4';
|
||||
import Brightness7Icon from '@mui/icons-material/Brightness7';
|
||||
import { nodeModuleNameResolver } from 'typescript';
|
||||
|
||||
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
|
||||
|
||||
const Header = (props: any): JSX.Element => {
|
||||
const theme = useTheme();
|
||||
const colorMode = React.useContext(ColorModeContext);
|
||||
return (
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
||||
CoinGecko Data
|
||||
</Typography>
|
||||
<IconButton sx={{ ml: 1 }} onClick={props.colorMode.toggleColorMode} color="inherit">
|
||||
{props.theme.palette.mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@ -1,44 +1,113 @@
|
||||
// Route
|
||||
import {useSearchParams} from 'react-router-dom';
|
||||
|
||||
// Redux
|
||||
import { useGetCoinsListQuery } from '../coinApi';
|
||||
|
||||
// Material UI
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
|
||||
// components
|
||||
import Pager from './coinListPager';
|
||||
|
||||
|
||||
|
||||
const CoinList = (): JSX.Element => {
|
||||
|
||||
// From URL
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const page = Number(searchParams.get('page')) || 1
|
||||
const per_page = Number(searchParams.get('per_page')) || 20
|
||||
const per_page = Number(searchParams.get('per_page')) || 25
|
||||
|
||||
// For Query
|
||||
const payload = {page, per_page}
|
||||
const { data, error, isLoading, isSuccess, refetch } = useGetCoinsListQuery(payload);
|
||||
console.log("payload: ", payload);
|
||||
|
||||
return (
|
||||
<div>
|
||||
CoinList
|
||||
{ isLoading && <div>Loading...</div> }
|
||||
{ data && isSuccess &&
|
||||
<TableContainer >
|
||||
<Table size="small" sx={{ marginTop: 10, marginBottom: 10 }} aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
align="left"
|
||||
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
|
||||
>
|
||||
#
|
||||
</TableCell>
|
||||
<TableCell align="left">Coin</TableCell>
|
||||
<TableCell align="right">Current Price</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
|
||||
>
|
||||
Low (24h)
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
|
||||
>
|
||||
Hight (24)
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{ display: { xs: 'table-cell', sm: 'none', md: 'table-cell' } }}
|
||||
align="right">Change (24h)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{
|
||||
data.map((row: any, index: any) => (
|
||||
<TableRow
|
||||
key={row.name}
|
||||
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||
>
|
||||
<TableCell
|
||||
align="right"
|
||||
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
|
||||
>
|
||||
{row.market_cap_rank}
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<img src={row.image} width="20" height="20" />
|
||||
<span className="CoinName">{row.name}</span>
|
||||
<span className="CoinSymbol">{row.symbol}</span>
|
||||
</TableCell>
|
||||
<TableCell align="right">${row.current_price}</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
|
||||
>
|
||||
${row.low_24h}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
|
||||
>
|
||||
${row.high_24h}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
sx={{ display: { xs: 'table-cell', sm: 'none', md: 'table-cell' } }}
|
||||
>
|
||||
<span className={row.price_change_percentage_24h > 0 ? 'green' : 'red'}>
|
||||
{row.price_change_percentage_24h}%
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
}
|
||||
|
||||
{isLoading && <div>Loading...</div>}
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
{ data && isSuccess &&
|
||||
data.map((item: any, index: any) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td><img src={item.image} width="20" height="20" /></td>
|
||||
<td>{item.symbol}</td>
|
||||
<td>{item.id}</td>
|
||||
<td>{item.name}</td>
|
||||
<td>{item.high_24h}</td>
|
||||
<td>{item.low_24h}</td>
|
||||
<td>{item.price_change_percentage_24h}</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<Pager page={page} per_page={per_page} setSearchParams={setSearchParams}/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { useGetCoinsCountQuery } from '../coinApi';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import Button from '@mui/material/Button';
|
||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
|
||||
const Pager = (props:any): JSX.Element => {
|
||||
|
||||
console.log("page props", props)
|
||||
@ -18,13 +23,13 @@ const Pager = (props:any): JSX.Element => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button disabled={page === 1} onClick={() => navigate("/")}>First</button>
|
||||
<button disabled={page === 1} onClick={() => handlePager(page - 1)}>Prev</button>
|
||||
<div>Page {page} from {lastPage}</div>
|
||||
<button disabled={page === lastPage} onClick={() => handlePager(page + 1)}>Next</button>
|
||||
<button disabled={page === lastPage} onClick={() => handlePager(lastPage)}>Last</button>
|
||||
</div>
|
||||
<ButtonGroup variant="contained" aria-label="outlined primary button group">
|
||||
<Button disabled={page === 1} onClick={() => navigate("/")}>First</Button>
|
||||
<Button disabled={page === 1} onClick={() => handlePager(page - 1)}>Prev</Button>
|
||||
<Typography style={{ padding: 10}}>Page {page} from {lastPage}</Typography>
|
||||
<Button disabled={page === lastPage} onClick={() => handlePager(page + 1)}>Next</Button>
|
||||
<Button disabled={page === lastPage} onClick={() => handlePager(lastPage)}>Last</Button>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -11,3 +11,24 @@ code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: #4eaf0a;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #e15241
|
||||
}
|
||||
|
||||
.CoinName {
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.CoinSymbol {
|
||||
font-weight: normal;
|
||||
font-size: 0.8em;
|
||||
margin-left: 5px;
|
||||
color: #777777;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -4,10 +4,14 @@ import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
import {BrowserRouter as Router} from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './store';
|
||||
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user