better info response types

This commit is contained in:
Ste Vaidis 2022-12-11 21:12:54 +02:00
parent 496c096531
commit 24c99868e1
7 changed files with 109 additions and 56 deletions

View File

@ -1,3 +1,5 @@
import { ReactNode } from "react";
export interface IGetGlobalResponse { export interface IGetGlobalResponse {
active_cryptocurrencies: number; active_cryptocurrencies: number;
markets: number; markets: number;
@ -17,18 +19,37 @@ export interface IGetCoinsCountResponse {
} }
export interface IGetCoinInfoResponse { export interface IGetCoinInfoResponse {
[key: string]: any; name: string;
id: string;
image: {
large: string;
};
description: {
en: string;
}
market_data: {
low_24h: { usd: number };
high_24h: { usd: number };
ath: { usd: number };
atl: { usd: number };
price_change_percentage_24h: number;
price_change_percentage_7d: number;
price_change_percentage_14d: number;
price_change_percentage_30d: number;
price_change_percentage_60d: number;
price_change_percentage_200d: number;
price_change_percentage_1y: number;
}
}
export interface IGetCoinInfoData {
data: IGetCoinInfoResponse
} }
export interface IGetCoinInfoRequest { export interface IGetCoinInfoRequest {
id: string; id: string;
} }
// export interface IGetCoinChartRequest {
// page: number;
// per_page: number;
// }
export interface IChartDataItem { export interface IChartDataItem {
[index: number]: number, [index: number]: number,
} }

View File

@ -1,3 +1,4 @@
import React from 'react'
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useGetCoinInfoQuery } from '../coinApi'; import { useGetCoinInfoQuery } from '../coinApi';
@ -12,22 +13,25 @@ import CoinInfo from './coinInfo';
const CoinDetails = (): JSX.Element => { const CoinDetails = (): JSX.Element => {
const { id } = useParams(); const { id } = useParams();
const { const {
data: data, data,
error: errorInfo, error,
isLoading: isLoadingInfo, isLoading,
isSuccess: isSuccessInfo, isSuccess,
refetch: refetchInfo
} = useGetCoinInfoQuery(id ? id : ''); } = useGetCoinInfoQuery(id ? id : '');
React.useEffect(() => {
console.log(data)
}, [])
return ( return (
<div className="details"> <div className="details">
{isLoading && <div>Loading...</div>}
{ {
data && isSuccessInfo && <> data && isSuccess && <>
<div className="details-title"> <div className="details-title">
<img src={data.image.large} width="28" height="28" /> <img src={data.image.large} width="28" height="28" />
<h1>{data.name}</h1> <h1>{data.name}</h1>
</div> </div>
<Box> <Box>
<Grid container> <Grid container>
<Grid item xs={12} sm={12} md={8}> <Grid item xs={12} sm={12} md={8}>
@ -39,8 +43,8 @@ const CoinDetails = (): JSX.Element => {
<Grid item xs={12} style={{marginTop: 20}}> <Grid item xs={12} style={{marginTop: 20}}>
<CoinChart coin={data.id} /> <CoinChart coin={data.id} />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12} style={{marginTop: 40}}>
<div dangerouslySetInnerHTML={{ __html: data.description.en }} /> <div style={{lineHeight: 2}} dangerouslySetInnerHTML={{ __html: data.description.en }} />
</Grid> </Grid>
</Grid> </Grid>
</Box> </Box>

View File

@ -27,7 +27,7 @@ const CoinInfo = (props: ICoinInfoProps): JSX.Element => {
Current price: ${data.tickers[0].converted_last.usd} Current price: ${data.tickers[0].converted_last.usd}
</Grid> </Grid>
<Grid item xs={12} style={{ marginTop: 20 }}> <Grid item xs={12} style={{ marginTop: 20 }}>
Homepage: <a target="_blank" href={data.links.homepage}>{data.links.homepage}</a> Homepage: <a target="_blank" href={data.links.homepage[0]}>{data.links.homepage[0]}</a>
</Grid> </Grid>
<Grid item style={{ marginTop: 20 }}> <Grid item style={{ marginTop: 20 }}>
Votes: {data.sentiment_votes_up_percentage}% Votes: {data.sentiment_votes_up_percentage}%
@ -38,27 +38,35 @@ const CoinInfo = (props: ICoinInfoProps): JSX.Element => {
</Grid> </Grid>
<Grid container justifyContent="flex-start" style={{ marginTop: 30, marginBottom: 30 }}> <Grid container justifyContent="flex-start" style={{ marginTop: 30, marginBottom: 30 }}>
{data.links.subreddit_url && {data.links.subreddit_url &&
<Button style={{ marginRight: 10 }} variant="outlined" startIcon={<RedditIcon />} href={data.links.subreddit_url}> <Button style={{ marginRight: 10, marginTop: 10 }} variant="outlined" startIcon={<RedditIcon />} href={data.links.subreddit_url}>
Reddit Reddit
</Button> </Button>
} }
{data.links.twitter_screen_name && {data.links.twitter_screen_name &&
<Button style={{ marginRight: 10 }} variant="outlined" startIcon={<TwitterIcon />} href={`https://twitter.com/${data.links.twitter_screen_name}`}> <Button style={{ marginRight: 10, marginTop: 10 }} variant="outlined" startIcon={<TwitterIcon />} href={`https://twitter.com/${data.links.twitter_screen_name}`}>
Twitter Twitter
</Button> </Button>
} }
{ {
Object.entries(data.links.chat_url).map( Object.entries(data.links.chat_url).map(
([key, value]) => Boolean(value) ([key, value]) => {
&& <Button if (Boolean(value)) {
key={key} console.log(value);
style={{ marginRight: 10 }} const domain = new URL(String(value));
variant="outlined" const label = domain.hostname.split(".")[0]
href={String(value)} return (
startIcon={<LinkIcon />} <Button
key={key}
style={{ marginRight: 10, marginTop: 10 }}
variant="outlined"
href={String(value)}
startIcon={<LinkIcon />}
> >
Discord {label}
</Button> </Button>
)
}
}
) )
} }
</Grid> </Grid>

View File

@ -5,12 +5,17 @@ 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';
export interface ICoinStatsProps { import { IGetCoinInfoResponse, IGetCoinInfoData } from '../coinApi-types'
[key: string]: any;
interface ICoinStatsProps {
data: IGetCoinInfoResponse;
} }
const CoinStats = (props: ICoinStatsProps): JSX.Element => { const CoinStats = (props: ICoinStatsProps): JSX.Element => {
console.log("CoinStats props", props)
const { data } = props; const { data } = props;
const tableData = [ const tableData = [
{title: "24h Hight", value: `$${data.market_data.low_24h.usd}`}, {title: "24h Hight", value: `$${data.market_data.low_24h.usd}`},
{title: "24h Low", value: `$${data.market_data.high_24h.usd}`}, {title: "24h Low", value: `$${data.market_data.high_24h.usd}`},

View File

@ -12,7 +12,17 @@ import { useGetCoinsListQuery, useGetGlobalQuery } from '../coinApi';
import Pager from './coinListPager'; import Pager from './coinListPager';
export interface IData {
id: string;
image: string;
name: string;
market_cap_rank: number;
symbol: string;
current_price: string;
high_24h: number;
low_24h: number;
price_change_percentage_24h: number;
}
const CoinList = (): JSX.Element => { const CoinList = (): JSX.Element => {
@ -74,7 +84,7 @@ const CoinList = (): JSX.Element => {
</TableHead> </TableHead>
<TableBody> <TableBody>
{ {
data.map((row: any, index: any) => ( data.map((row: IData, index: number) => (
<TableRow <TableRow
key={row.name} key={row.name}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
@ -119,7 +129,7 @@ const CoinList = (): JSX.Element => {
</Table> </Table>
</TableContainer> </TableContainer>
} }
<Pager page={page} per_page={per_page} setSearchParams={setSearchParams} /> <Pager page={page} per_page={per_page}/>
</div> </div>
) )
} }

View File

@ -6,32 +6,37 @@ import ButtonGroup from '@mui/material/ButtonGroup';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
const Pager = (props:any): JSX.Element => { export interface IPagerProps {
const { page, per_page, } = props; page: number;
const { data, error, isLoading, isSuccess, refetch } = useGetCoinsCountQuery(); per_page: number;
const navigate = useNavigate(); }
const lastPage = (data: number) => Math.ceil(data / per_page)
function handlePager(page: number) { const Pager = (props: IPagerProps): JSX.Element => {
const url = `/?page=${page}` const { page, per_page, } = props;
navigate(url) const { data, error, isLoading, isSuccess, refetch } = useGetCoinsCountQuery();
const navigate = useNavigate();
const lastPage = (data: number) => Math.ceil(data / per_page)
function handlePager(page: number) {
const url = `/?page=${page}`
navigate(url)
}
return (<>
{
data && isSuccess &&
<Grid container justifyContent="center" style={{ marginTop: 60 }}>
<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(data.count)}</Typography>
<Button disabled={page === lastPage(data.count)} onClick={() => handlePager(page + 1)}>Next</Button>
<Button disabled={page === lastPage(data.count)} onClick={() => handlePager(lastPage(data.count))}>Last</Button>
</ButtonGroup>
</Grid>
} }
</>
return (<> )
{
data && isSuccess &&
<Grid container justifyContent="center" style={{marginTop: 60}}>
<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(data.count)}</Typography>
<Button disabled={page === lastPage(data.count)} onClick={() => handlePager(page + 1)}>Next</Button>
<Button disabled={page === lastPage(data.count)} onClick={() => handlePager(lastPage(data.count))}>Last</Button>
</ButtonGroup>
</Grid>
}
</>
)
} }
export default Pager; export default Pager;

View File

@ -3,7 +3,7 @@
## What's inside ## What's inside
- **Frontend:** Typescript using React, Redux-Toolkit, MaterialUI, react-charts-2 - **Frontend:** Typescript using React, Redux-Toolkit, Material-UI, react-chartjs-2
- **Backend:** Javascript using Express - **Backend:** Javascript using Express
![Screenshot](./screenshot.png) ![Screenshot](./screenshot.png)