types pending

This commit is contained in:
Ste Vaidis 2022-12-10 23:05:30 +02:00
parent 46b167a8f3
commit a5ed7ade2c
8 changed files with 256 additions and 190 deletions

View File

@ -29,11 +29,11 @@ router.get('/coins/markets', function (req, res) {
router.get('/count', function (req, res) { router.get('/count', function (req, res) {
let url = config.coingecko.api_url + '/global'; let url = config.coingecko.api_url + '/global';
// console.log("url: ", url); console.log("url: ", url);
api_helper.REMOTE_API_call(url) api_helper.REMOTE_API_call(url)
.then(response => { .then(response => {
res.json(response.data.active_cryptocurrencies); res.json({'count': response.data.active_cryptocurrencies});
}) })
.catch(error => { .catch(error => {
console.log("error: ", error); console.log("error: ", error);
@ -71,8 +71,18 @@ router.get('/coin/:id/chart', function (req, res) {
}) })
}) })
router.get('/global', function (req, res) {
let url = config.coingecko.api_url + '/global';
api_helper.REMOTE_API_call(url)
.then(response => {
res.json(response.data);
})
.catch(error => {
console.log("error: ", error);
res.send(error);
})
})

View File

@ -10,7 +10,6 @@ import * as React from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles'; import { ThemeProvider, createTheme } from '@mui/material/styles';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import Container from '@mui/material/Container'; import Container from '@mui/material/Container';
const ColorModeContext = React.createContext({ toggleColorMode: () => { } }); const ColorModeContext = React.createContext({ toggleColorMode: () => { } });

View File

@ -15,7 +15,12 @@ import { Link } from "react-router-dom";
const ColorModeContext = React.createContext({ toggleColorMode: () => {} }); const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
const Header = (props: any): JSX.Element => { export interface HeaderProps {
colorMode: any;
theme: any;
}
const Header = (props: HeaderProps): JSX.Element => {
const theme = useTheme(); const theme = useTheme();
const colorMode = React.useContext(ColorModeContext); const colorMode = React.useContext(ColorModeContext);
return ( return (

View File

@ -8,22 +8,52 @@ const globalUrl = '/global';
const coinInfoUrl = (id:string) => `/coin/${id}`; const coinInfoUrl = (id:string) => `/coin/${id}`;
const coinChartUrl = (id:string) => `/coin/${id}/chart`; const coinChartUrl = (id:string) => `/coin/${id}/chart`;
export interface IGetGlobalResponse {
active_cryptocurrencies: number;
markets: number;
}
export interface IGetCoinsListResponse {
[key: string]: any;
}
export interface IGetCoinsCountResponse {
count: number;
}
export interface IGetCoinsInfoResponse {
[key: string]: any;
}
export interface IGetCoinsInfoRequest {
id: string;
}
export interface IGetCoinsChartRequest {
page: number;
per_page: number;
}
export interface IGetCoinsChartResponse {
[key: string]: any ;
}
export const coinListApi = createApi({ export const coinListApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: baseUrl }), baseQuery: fetchBaseQuery({ baseUrl: baseUrl }),
endpoints: (builder) => ({ endpoints: (builder) => ({
getGlobal: builder.query<any, number | void>({ getGlobal: builder.query<IGetGlobalResponse, void>({
query: () => globalUrl, query: () => globalUrl,
}), }),
getCoinsList: builder.query<any, any>({ getCoinsList: builder.query<IGetCoinsListResponse, IGetCoinsChartRequest>({
query: (payload: {page: number, per_page: number}) => `${marketsUrl}?per_page=${payload.per_page}&page=${payload.page}`, query: (payload: IGetCoinsChartRequest) => `${marketsUrl}?per_page=${payload.per_page}&page=${payload.page}`,
}), }),
getCoinsCount: builder.query<any, number | void>({ getCoinsCount: builder.query<IGetCoinsCountResponse, void>({
query: () => countUrl, query: () => countUrl,
}), }),
getCoinInfo: builder.query<any, string>({ getCoinInfo: builder.query<IGetCoinsInfoResponse, string>({
query: (id: string) => coinInfoUrl(id), query: (id: string) => coinInfoUrl(id),
}), }),
getCoinChart: builder.query<any, string>({ getCoinChart: builder.query<IGetCoinsChartResponse, string>({
query: (id: string) => coinChartUrl(id), query: (id: string) => coinChartUrl(id),
}), }),
}), }),

View File

@ -1,91 +1,116 @@
import { import {
Chart as ChartJS, Chart as ChartJS,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
PointElement, PointElement,
LineElement, LineElement,
Title, Title,
Tooltip, Tooltip,
Filler, Filler,
Legend, Legend,
} from 'chart.js'; } from 'chart.js';
import { Line } from 'react-chartjs-2'; import { Line } from 'react-chartjs-2';
import { useGetCoinChartQuery } from '../coinApi'; import { useGetCoinChartQuery } from '../coinApi';
import { format } from 'date-fns'; import { format } from 'date-fns';
ChartJS.register( ChartJS.register(
CategoryScale, CategoryScale,
LinearScale, LinearScale,
PointElement, PointElement,
LineElement, LineElement,
Title, Title,
Tooltip, Tooltip,
Filler, Filler,
Legend Legend
); );
export const options = { export const options = {
responsive: true, responsive: true,
plugins: { plugins: {
legend: { legend: {
position: 'top' as const, position: 'top' as const,
},
title: {
display: true,
text: 'Chart.js Line Chart',
},
}, },
title: {
display: true,
text: 'Chart.js Line Chart',
},
},
}; };
const CoinChart = (props: any): JSX.Element => {
// console.log("props: ", props);
// get chart data ------------------------------------------- export interface ICoinChartProps {
const { coin: string;
data, }
isSuccess: isSuccessChart
} = useGetCoinChartQuery(props.coin || 'btc'); // Todo: 404
// format chart data ---------------------------------------- // export interface IChartData {
function formatChartData(data: any): any { // [key: string]: any;
const chartData = data['prices'].map(function (value: any) { // }
// return value[1];
return {
x: new Date(value[0]),
y: value[1],
};
});
console.log('formatChartData: ', chartData)
return chartData;
}
// format data ----------------------------------------------
function formatChartLabels(data: any): any {
const labels = data['prices'].map((value: (string | number | Date)[]) => format(new Date(value[0]), 'MM/dd/yyyy'));
console.log('formatChartLabels: ', labels)
return labels;
}
return (
// ---------------------------------------- IN
export interface IChartDataItem {
[index: number]: number,
}
export interface IChartData {
market_caps: Array<IChartDataItem>;
prices: Array<IChartDataItem>;
total_volumes: Array<IChartDataItem>;
}
// ---------------------------------------- OUT
export interface IChartDataFormattedItem {
x: Date;
y: number;
}
export interface IChartDataFormatted extends Array<IChartDataFormattedItem>{};
const CoinChart = (props: ICoinChartProps): JSX.Element => {
console.log("props: ", props);
const { data, isSuccess: isSuccessChart } = useGetCoinChartQuery(props.coin);
function formatChartData(data: IChartData): IChartDataFormatted {
const chartData = data['prices'].map(function (value: IChartDataItem) {
return {
x: new Date(value[0]),
y: value[1],
};
});
console.log('formatChartData: ', chartData)
return chartData;
}
function formatChartLabels(data: any): any {
console.log('data: ', data)
const labels = data['prices'].map((value: (string | number | Date)[]) => format(new Date(value[0]), 'MM/dd/yyyy'));
// console.log('formatChartLabels: ', labels)
return labels;
}
return (
<div>
{
data &&
<div> <div>
{ <Line
data && data={{
<div> labels: formatChartLabels(data),
<Line datasets: [{
data={{ label: 'Dataset',
labels: formatChartLabels(data), data: formatChartData(data)
datasets: [{ }],
label: 'Dataset', }}
data: formatChartData(data) />
}],
}}
/>
</div>
}
{/* <Line options={options} data={data} />; */}
</div> </div>
) }
</div>
)
} }
export default CoinChart; export default CoinChart;

View File

@ -4,34 +4,30 @@ import CoinChart from './coinChart';
const CoinDetails = (): JSX.Element => { const CoinDetails = (): JSX.Element => {
const { id } = useParams(); const { id } = useParams();
console.log("id: ", id)
// get info data
const { const {
data: dataInfo, data: dataInfo,
error: errorInfo, error: errorInfo,
isLoading: isLoadingInfo, isLoading: isLoadingInfo,
isSuccess: isSuccessInfo, isSuccess: isSuccessInfo,
refetch: refetchInfo refetch: refetchInfo
} = useGetCoinInfoQuery(id || 'btc'); // Todo: 404 } = useGetCoinInfoQuery(id ? id : '');
return ( return (
<div> <div>
{ {
dataInfo && isSuccessInfo && dataInfo && isSuccessInfo && <>
<div className="info"> <div className="info">
<div>ID: {dataInfo.id}</div> <div>ID: {dataInfo.id}</div>
<div className="links"> <div className="links">
<div>Homepage: {dataInfo.links.homepage}</div> <div>Homepage: {dataInfo.links.homepage}</div>
</div>
<div>Description{dataInfo.description.en}</div>
</div> </div>
<div>Description{dataInfo.description.en}</div>
</div>
}
{ <div className="chart">
<div className="chart"> <CoinChart coin={dataInfo.id} />
<CoinChart coin={'flow'} /> </div>
</div> </>
} }
</div> </div>

View File

@ -1,8 +1,6 @@
// Route
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
// Material UI
import Table from '@mui/material/Table'; import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody'; import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell'; import TableCell from '@mui/material/TableCell';
@ -10,10 +8,8 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead'; import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow'; import TableRow from '@mui/material/TableRow';
// Redux
import { useGetCoinsListQuery, useGetGlobalQuery } from '../coinApi'; import { useGetCoinsListQuery, useGetGlobalQuery } from '../coinApi';
// components
import Pager from './coinListPager'; import Pager from './coinListPager';
@ -27,98 +23,99 @@ const CoinList = (): JSX.Element => {
// For Query // For Query
const payload = { page, per_page } const payload = { page, per_page }
const { data, error, isLoading, isSuccess } = useGetCoinsListQuery(payload); const { data, isLoading, isSuccess } = useGetCoinsListQuery(payload);
const { data: globalData, error: globalError, isLoading: globalIsLoading, isSuccess: lobalSuccess } = useGetGlobalQuery(); const { data: globalData, isLoading: globalIsLoading, isSuccess: lobalSuccess } = useGetGlobalQuery();
return ( return (
<div> <div>
{isLoading && <div>Loading...</div>} {isLoading || globalIsLoading && <div>Loading...</div>}
{ {
globalData && globalData &&
<span className="globalText"> <span className="globalText">
{`The global cryptocurrency market has currently ${globalData.data.active_cryptocurrencies} active cryptocurrencies, and ${globalData.data.markets} markets`} {`The global cryptocurrency market has currently ${globalData.active_cryptocurrencies} active cryptocurrencies, and ${globalData.markets} markets`}
<br /> <br />
</span> </span>
} }
{data && isSuccess && {
<TableContainer style={{ marginTop: 20 }}> data && isSuccess &&
<Table size="small" aria-label="simple table"> <TableContainer style={{ marginTop: 20 }}>
<TableHead> <Table size="small" aria-label="simple table">
<TableRow> <TableHead>
<TableCell <TableRow>
align="left" <TableCell
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }} 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" </TableCell>
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }} <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 } }}
> >
{row.market_cap_rank} <TableCell
</TableCell> align="right"
<TableCell align="left"> sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
<Link to={`coin/${row.id}`}> >
<img src={row.image} width="20" height="20" /> {row.market_cap_rank}
<span className="coinName">{row.name}</span> </TableCell>
<span className="coinSymbol">{row.symbol}</span> <TableCell align="left">
</Link> <Link to={`coin/${row.id}`}>
</TableCell> <img src={row.image} width="20" height="20" />
<TableCell align="right">${row.current_price}</TableCell> <span className="coinName">{row.name}</span>
<TableCell <span className="coinSymbol">{row.symbol}</span>
align="right" </Link>
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }} </TableCell>
> <TableCell align="right">${row.current_price}</TableCell>
${row.low_24h} <TableCell
</TableCell> align="right"
<TableCell sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
align="right" >
sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }} ${row.low_24h}
> </TableCell>
${row.high_24h} <TableCell
</TableCell> align="right"
<TableCell sx={{ display: { xs: 'none', sm: 'table-cell', md: 'table-cell' } }}
align="right" >
sx={{ display: { xs: 'table-cell', sm: 'none', md: 'table-cell' } }} ${row.high_24h}
> </TableCell>
<span className={row.price_change_percentage_24h > 0 ? 'green' : 'red'}> <TableCell
{row.price_change_percentage_24h}% align="right"
</span> sx={{ display: { xs: 'table-cell', sm: 'none', md: 'table-cell' } }}
</TableCell> >
</TableRow> <span className={row.price_change_percentage_24h > 0 ? 'green' : 'red'}>
))} {row.price_change_percentage_24h}%
</TableBody> </span>
</Table> </TableCell>
</TableContainer> </TableRow>
))}
</TableBody>
</Table>
</TableContainer>
} }
<Pager page={page} per_page={per_page} setSearchParams={setSearchParams} /> <Pager page={page} per_page={per_page} setSearchParams={setSearchParams} />
</div> </div>

View File

@ -15,23 +15,27 @@ const Pager = (props:any): JSX.Element => {
const { data, error, isLoading, isSuccess, refetch } = useGetCoinsCountQuery(); const { data, error, isLoading, isSuccess, refetch } = useGetCoinsCountQuery();
const navigate = useNavigate(); const navigate = useNavigate();
const lastPage = Math.ceil(data / per_page) const lastPage = (data: number) => Math.ceil(data / per_page)
function handlePager(page: number) { function handlePager(page: number) {
const url = `/?page=${page}` const url = `/?page=${page}`
navigate(url) navigate(url)
} }
return ( return (<>
<Grid container justifyContent="center" style={{marginTop: 60}}> {
<ButtonGroup variant="contained" aria-label="outlined primary button group"> data && isSuccess &&
<Button disabled={page === 1} onClick={() => navigate("/")}>First</Button> <Grid container justifyContent="center" style={{marginTop: 60}}>
<Button disabled={page === 1} onClick={() => handlePager(page - 1)}>Prev</Button> <ButtonGroup variant="contained" aria-label="outlined primary button group">
<Typography style={{ padding: 10}}>Page {page} from {lastPage}</Typography> <Button disabled={page === 1} onClick={() => navigate("/")}>First</Button>
<Button disabled={page === lastPage} onClick={() => handlePager(page + 1)}>Next</Button> <Button disabled={page === 1} onClick={() => handlePager(page - 1)}>Prev</Button>
<Button disabled={page === lastPage} onClick={() => handlePager(lastPage)}>Last</Button> <Typography style={{ padding: 10}}>Page {page} from {lastPage(data.count)}</Typography>
</ButtonGroup> <Button disabled={page === lastPage(data.count)} onClick={() => handlePager(page + 1)}>Next</Button>
</Grid> <Button disabled={page === lastPage(data.count)} onClick={() => handlePager(lastPage(data.count))}>Last</Button>
</ButtonGroup>
</Grid>
}
</>
) )
} }