Skip to content

Commit

Permalink
refactor BestBuyPage and chart style
Browse files Browse the repository at this point in the history
  • Loading branch information
nushydude committed Oct 28, 2023
1 parent 8e2823a commit 2956625
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 108 deletions.
38 changes: 37 additions & 1 deletion src/components/KLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { Line } from 'react-chartjs-2';
import { format } from 'date-fns';

type Props = {
variant?: 'summary' | 'detailed';
data: Array<{
openTime: string;
openPrice: number;
volume: number;
}>;
};

export const KLineChart: React.FC<Props> = ({ data }) => {
export const KLineChart: React.FC<Props> = ({ data, variant = 'detailed' }) => {
const labels = data.map((d) => format(new Date(d.openTime), 'MMM d kk:mm'));
const priceSet = data.map((d) => d.openPrice);

Expand All @@ -28,6 +29,41 @@ export const KLineChart: React.FC<Props> = ({ data }) => {
},
],
}}
options={{
responsive: true,
maintainAspectRatio: variant === 'detailed',
plugins: {
legend: {
display: false,
},
},
elements: {
point: {
radius: variant === 'detailed' ? 4 : 0,
},
},
scales: {
x: {
ticks: {
display: variant === 'detailed',
},
grid: {
drawBorder: false,
display: variant === 'detailed',
},
},
y: {
ticks: {
display: variant === 'detailed',
// beginAtZero: true,
},
grid: {
drawBorder: false,
display: variant === 'detailed',
},
},
},
}}
/>
);
};
29 changes: 29 additions & 0 deletions src/components/TokensSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { ChangeEventHandler } from 'react';
import { useSymbols } from '../hooks/useSymbols';

type Props = {
selectedValue: string;
onChange: ChangeEventHandler<HTMLInputElement>;
};

export const TokensSelector = ({ selectedValue, onChange }: Props) => {
const { symbols } = useSymbols();

return (
<>
<input
data-testid="input-symbol"
id="symbol"
list="symbols"
value={selectedValue}
onChange={onChange}
required
/>
<datalist id="symbols">
{symbols.map((symbol, index) => (
<option key={index} value={symbol} />
))}
</datalist>
</>
);
};
45 changes: 45 additions & 0 deletions src/pages/BestBuyPage/BestBuyPage.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import styled from 'styled-components';

export const Row = styled.div<{ best?: boolean; dca?: boolean }>`
border-radius: 5px;
margin-bottom: 10px;
background: ${(props) => {
if (props.dca && props.best) {
return '#A0F0A0';
} else if (props.dca && !props.best) {
return '#FFF3AD';
}
return '#FFD1DC';
}};
padding: 10px;
border-width: 1px;
border-style: solid;
border-color: ${(props) => {
if (props.dca && props.best) {
return '#90EE90';
} else if (props.dca && !props.best) {
return '#FFEB9C';
}
return '#FFC0CB';
}};
`;

export const DCAInfoContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
@media (max-width: 690px) {
flex-direction: column;
}
`;

export const Column = styled.div`
width: 50%;
@media (max-width: 690px) {
width: 100%;
}
`;
92 changes: 6 additions & 86 deletions src/pages/BestBuyPage/BestBuyPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react';
import styled, { keyframes } from 'styled-components';
import { useLocalStorage } from 'react-use';
import { DCAInfo } from '../../components/DCAInfo';
import { useBinanceKLine } from '../../hooks/useBinanceKline';
Expand All @@ -10,71 +8,12 @@ import { FETCH_STATUS } from '../../consts/FetchStatus';
import { DEFAULT_SYMBOLS } from '../../consts/DefaultSymbols';
import { DEFAULT_SETTINGS } from '../../consts/DefaultSettings';
import { KLineChart } from '../../components/KLineChart';
import { Skeleton } from './Skeleton';
import { Column, DCAInfoContainer, Row } from './BestBuyPage.styles';

const defaultInterval: Interval = '4h';
const defaultLimit = 100;

const Row = styled.div<{ best?: boolean; dca?: boolean }>`
margin-bottom: 10px;
background: ${(props) => {
if (props.dca && props.best) {
return 'lightgreen';
} else if (props.dca && !props.best) {
return '#FFEB9C';
}
return 'pink';
}};
padding: 10px;
`;

const SkeletonRow = styled(Row)`
background: #efefef;
`;

const DCAInfoContainer = styled.div`
display: flex;
flex-direction: row;
@media (max-width: 690px) {
flex-direction: column;
}
`;

const skeletonLoading = keyframes`
0% {
background-color: hsl(200, 20%, 80%);
}
100% {
background-color: hsl(200, 20%, 95%);
}
`;

const Bar = styled.div`
height: 18px;
animation: ${skeletonLoading} 1s linear infinite alternate;
margin-bottom: 8px;
width: 50%;
`;

const ThickBar = styled(Bar)`
height: 37px;
`;

const AspectRatioBox = styled.div<{ aspectRatio: number }>`
width: 100%;
padding-top: ${(props) => props.aspectRatio}%;
animation: ${skeletonLoading} 1s linear infinite alternate;
`;

const Column = styled.div`
width: 50%;
@media (max-width: 690px) {
width: 100%;
}
`;

interface Props {
sdMultiplier?: number;
}
Expand Down Expand Up @@ -111,7 +50,7 @@ export const BestBuyPage = ({ sdMultiplier = 1 }: Props) => {
const standardDeviation = calculateStandardDeviation(prices);
const mean = calculateMean(prices);
const targetPrice = mean - sdMultiplier * standardDeviation;
const shouldDCA = avgPrice < targetPrice;
const shouldDCA: boolean = avgPrice < targetPrice;
const dip = ((avgPrice - targetPrice) / targetPrice) * 100;

return { symbol, shouldDCA, targetPrice, avgPrice, dip, klineData };
Expand All @@ -124,26 +63,7 @@ export const BestBuyPage = ({ sdMultiplier = 1 }: Props) => {
);

if (fetchStatus === FETCH_STATUS.fetching) {
return (
<div>
{new Array(5).fill(0).map((dataItem, index) => (
<SkeletonRow key={index}>
<ThickBar />
<DCAInfoContainer>
<Column>
<Bar />
<Bar />
<Bar />
<Bar />
</Column>
<Column>
<AspectRatioBox aspectRatio={50} />
</Column>
</DCAInfoContainer>
</SkeletonRow>
))}
</div>
);
return <Skeleton rows={5} />;
}

return (
Expand All @@ -154,13 +74,13 @@ export const BestBuyPage = ({ sdMultiplier = 1 }: Props) => {
best={bestDCAIndex === index}
dca={dataItem.shouldDCA}
>
<h1>{dataItem.symbol}</h1>
<h2>{dataItem.symbol}</h2>
<DCAInfoContainer>
<Column>
<DCAInfo {...dataItem} />
</Column>
<Column>
<KLineChart data={dataItem.klineData} />
<KLineChart data={dataItem.klineData} variant="summary" />
</Column>
</DCAInfoContainer>
</Row>
Expand Down
33 changes: 33 additions & 0 deletions src/pages/BestBuyPage/Skeleton.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import styled, { keyframes } from 'styled-components';
import { Row } from './BestBuyPage.styles';

export const SkeletonRow = styled(Row)`
background: #efefef;
border-color: #e0e0e0;
`;

export const skeletonLoading = keyframes`
0% {
background-color: hsl(200, 20%, 80%);
}
100% {
background-color: hsl(200, 20%, 95%);
}
`;

export const Bar = styled.div`
height: 18px;
animation: ${skeletonLoading} 1s linear infinite alternate;
margin-bottom: 8px;
width: 50%;
`;

export const ThickBar = styled(Bar)`
height: 37px;
`;

export const AspectRatioBox = styled.div<{ aspectRatio: number }>`
width: 100%;
padding-top: ${(props) => props.aspectRatio}%;
animation: ${skeletonLoading} 1s linear infinite alternate;
`;
29 changes: 29 additions & 0 deletions src/pages/BestBuyPage/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Column, DCAInfoContainer } from './BestBuyPage.styles';
import { AspectRatioBox, Bar, SkeletonRow, ThickBar } from './Skeleton.styles';

type Props = {
rows?: number;
};

export const Skeleton = ({ rows = 5 }: Props) => {
return (
<div>
{new Array(rows).fill(0).map((dataItem, index) => (
<SkeletonRow key={index}>
<ThickBar />
<DCAInfoContainer>
<Column>
<Bar />
<Bar />
<Bar />
<Bar />
</Column>
<Column>
<AspectRatioBox aspectRatio={50} />
</Column>
</DCAInfoContainer>
</SkeletonRow>
))}
</div>
);
};
1 change: 1 addition & 0 deletions src/pages/BestBuyPage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { BestBuyPage } from './BestBuyPage';
2 changes: 1 addition & 1 deletion src/pages/SingleTokenPage/DCAInfoWithChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const DCAInfoWithChart = ({
avgPrice={avgPrice}
shouldDCA={shouldDCA}
/>
<KLineChart data={klineData} />
<KLineChart data={klineData} variant="detailed" />
</Wrapper>
);
};
20 changes: 2 additions & 18 deletions src/pages/SingleTokenPage/TokenOptionsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useEffect } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { useSymbols } from '../../hooks/useSymbols';
import { FieldValues } from './types';
import { Form, FormGroup } from './TokenOptionsForm.styles';
import { TokensSelector } from '../../components/TokensSelector';

type Props = {
defaultValues: Partial<FieldValues>;
Expand All @@ -17,8 +17,6 @@ export const TokenOptionsForm = ({
onSubmit,
onValueChange,
}: Props) => {
const { symbols } = useSymbols();

const {
register,
handleSubmit,
Expand All @@ -43,21 +41,7 @@ export const TokenOptionsForm = ({
control={control}
name="symbol"
render={({ field: { onChange, value } }) => (
<>
<input
data-testid="input-symbol"
id="symbol"
list="symbols"
value={value}
onChange={(e) => onChange(e.target.value.toUpperCase())}
required
/>
<datalist id="symbols">
{symbols.map((symbol, index) => (
<option key={index} value={symbol} />
))}
</datalist>
</>
<TokensSelector selectedValue={value} onChange={onChange} />
)}
/>

Expand Down
2 changes: 0 additions & 2 deletions src/providers/AppSettingsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ function hydrateInitialStateWithFeaturesFromCookies() {
}
}

console.log('initialState', initialState);

return initialState;
}

Expand Down

0 comments on commit 2956625

Please sign in to comment.