From 3a4532b70ca3b648b2144dda9abf3a4161957081 Mon Sep 17 00:00:00 2001 From: bonz88 Date: Sun, 10 Mar 2024 16:56:31 +0100 Subject: [PATCH 1/7] based on selected timeline day/days chart will display price for bitcoin. Added formating for XAxis on chart based on selected timeline. Added diferent colors for charts --- src/app/components/CoinChart.tsx | 91 ++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/src/app/components/CoinChart.tsx b/src/app/components/CoinChart.tsx index 0283532..74af604 100644 --- a/src/app/components/CoinChart.tsx +++ b/src/app/components/CoinChart.tsx @@ -35,14 +35,19 @@ export default function CoinChart({ const { selectedCoins, loading, hasError } = useAppSelector( (state) => state.selectedCoin ); + const days = useAppSelector((state) => state.timeline.currentTimeline.days); const [currentValue, setCurrentValue] = useState(""); const [currentDate, setCurrentDate] = useState(""); useEffect(() => { dispatch( - getCoinDataGraph({ currency: currencyCode, days: "3", coinId: "bitcoin" }) + getCoinDataGraph({ + currency: currencyCode, + days: days, + coinId: "bitcoin", + }) ); - }, [dispatch, currencyCode]); + }, [dispatch, currencyCode, days]); useEffect(() => { if (selectedCoins.length > 0) { @@ -53,16 +58,23 @@ export default function CoinChart({ selectedCoins[0].total_volumes.length - 1 ]; const lastValue = lastDataPoint[1]; - const lastDate = new Date(lastDataPoint[0]).toLocaleDateString(); + const lastDate = new Date(lastDataPoint[0]).toLocaleDateString( + undefined, + { + month: "long", + day: "numeric", + year: "numeric", + } + ); setCurrentValue(`${lastValue.toFixed(2)}`); setCurrentDate(lastDate); } }, [selectedCoins, chartType, currencyCode]); - if (loading === "pending") { + /* if (loading === "pending") { return
Loading...
; - } + } */ if (hasError) { return
Error fetching data
; @@ -73,11 +85,11 @@ export default function CoinChart({ const formattedData = coin ? chartType === "price" ? coin.prices.map(([time, value]) => ({ - date: new Date(time).toLocaleDateString(), + date: new Date(time), value, })) : coin.total_volumes.map(([time, volume]) => ({ - date: new Date(time).toLocaleDateString(), + date: new Date(time), volume, })) : []; @@ -87,13 +99,52 @@ export default function CoinChart({ const { payload } = e.activePayload[0]; const dataKey = chartType === "price" ? "value" : "volume"; const value = payload[dataKey]?.toFixed(2) ?? "0"; + const date = new Date(payload.date); setCurrentValue(value); - setCurrentDate(payload.date); + setCurrentDate( + date.toLocaleDateString(undefined, { + month: "long", + day: "numeric", + year: "numeric", + }) + ); + } + }; + + const formatDateGraphs = (tick: string): string => { + const date = new Date(tick); + let formattedDate: string; + + if (days === "1") { + formattedDate = date.toLocaleTimeString(undefined, { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + } else if (["7", "14", "30"].includes(days)) { + formattedDate = date.toLocaleDateString(undefined, { + day: "numeric", + month: "short", + }); + } else if (["90", "180", "365", "max"].includes(days)) { + formattedDate = date.toLocaleDateString(undefined, { + day: "numeric", + month: "short", + year: "numeric", + }); + } else { + formattedDate = date.toLocaleDateString(); } + + return formattedDate; }; return ( -
+
{chartType === "volume" @@ -113,19 +164,24 @@ export default function CoinChart({ {chartType === "volume" ? ( - + - + } /> - + ) : ( - + @@ -134,10 +190,15 @@ export default function CoinChart({ dataKey="value" stroke="#7878FA" strokeWidth="3" - fill="url(#color)" + fill="url(#colorAreaChart)" /> - + } /> From d3d4267cf9239b8e191b63dd31d5ebc9e97c1a53 Mon Sep 17 00:00:00 2001 From: bonz88 Date: Sun, 10 Mar 2024 17:32:50 +0100 Subject: [PATCH 2/7] added timeline so you can display chart based on selected timeline --- src/app/components/CarouselCoins.tsx | 2 +- src/app/components/Timeline.tsx | 29 +++++++++ src/app/page.tsx | 5 ++ src/redux/features/timelineSlice.ts | 93 ++++++++++++++++++++++++++++ src/redux/store.ts | 2 + 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/app/components/Timeline.tsx create mode 100644 src/redux/features/timelineSlice.ts diff --git a/src/app/components/CarouselCoins.tsx b/src/app/components/CarouselCoins.tsx index 8ac1ef4..dda7d34 100644 --- a/src/app/components/CarouselCoins.tsx +++ b/src/app/components/CarouselCoins.tsx @@ -47,7 +47,7 @@ export default function CarouselCoins() { className="w-full" > - {data.map((coin) => ( + {data?.map((coin) => ( state.timeline.timeline); + + return ( +
+ {timeline.map((t) => ( + + ))} +
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 11fffb5..63104eb 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,7 @@ import NavHome from "./components/NavHome"; import CarouselCoins from "./components/CarouselCoins"; import GraphCoins from "./components/GraphCoins"; +import Timeline from "./components/Timeline"; export default function Home() { return ( @@ -12,6 +13,10 @@ export default function Home() {
+
+ +
+
); } diff --git a/src/redux/features/timelineSlice.ts b/src/redux/features/timelineSlice.ts new file mode 100644 index 0000000..2438366 --- /dev/null +++ b/src/redux/features/timelineSlice.ts @@ -0,0 +1,93 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +type TimelineItem = { + id: number; + days: string; + display: string; + active: boolean; +}; + +type TimelineState = { + currentTimeline: TimelineItem; + timeline: TimelineItem[]; +}; + +const initialState: TimelineState = { + currentTimeline: { + id: 1, + days: "1", + display: "1D", + active: true, + }, + timeline: [ + { + id: 1, + days: "1", + display: "1D", + active: true, + }, + { id: 2, days: "7", display: "7D", active: false }, + { + id: 3, + days: "14", + display: "14D", + active: false, + }, + { + id: 4, + days: "30", + display: "1M", + active: false, + }, + { + id: 5, + days: "90", + display: "3M", + active: false, + }, + { + id: 6, + days: "180", + display: "6M", + active: false, + }, + { + id: 7, + days: "365", + display: "1Y", + active: false, + }, + { + id: 8, + days: "max", + display: "MAX", + active: false, + }, + ], +}; + +export const timelineSlice = createSlice({ + name: "timeline", + initialState, + reducers: { + activeTimeline: (state, action: PayloadAction) => { + const currentActiveIndex = state.timeline.findIndex( + (item) => item.active + ); + if (currentActiveIndex !== -1) { + state.timeline[currentActiveIndex].active = false; + } + + const newActiveIndex = state.timeline.findIndex( + (item) => item.id === action.payload + ); + if (newActiveIndex !== -1) { + state.timeline[newActiveIndex].active = true; + state.currentTimeline = { ...state.timeline[newActiveIndex] }; + } + }, + }, +}); + +export const { activeTimeline } = timelineSlice.actions; +export default timelineSlice.reducer; diff --git a/src/redux/store.ts b/src/redux/store.ts index 7dd3189..db283e7 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -5,6 +5,7 @@ import globalReducer from "./features/globalSlice"; import currencyReducer from "./features/currencySlice"; import coinReducer from "./features/coinInfoSlice"; import selectedCoinReducer from "./features/selectedCoinSlice"; +import timelineReducer from "./features/timelineSlice"; export const store = configureStore({ reducer: { @@ -12,6 +13,7 @@ export const store = configureStore({ currency: currencyReducer, coinData: coinReducer, selectedCoin: selectedCoinReducer, + timeline: timelineReducer, }, }); From 18d8afa746fa60961e3f42d9932de58c49117942 Mon Sep 17 00:00:00 2001 From: bonz88 Date: Wed, 13 Mar 2024 20:14:26 +0100 Subject: [PATCH 3/7] removed commented code --- src/app/components/CoinChart.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/components/CoinChart.tsx b/src/app/components/CoinChart.tsx index 74af604..6ad34f4 100644 --- a/src/app/components/CoinChart.tsx +++ b/src/app/components/CoinChart.tsx @@ -72,10 +72,6 @@ export default function CoinChart({ } }, [selectedCoins, chartType, currencyCode]); - /* if (loading === "pending") { - return
Loading...
; - } */ - if (hasError) { return
Error fetching data
; } From 5e6c2dcc2b042543d62130a761ef62af44e10d18 Mon Sep 17 00:00:00 2001 From: bonz88 Date: Wed, 13 Mar 2024 20:37:09 +0100 Subject: [PATCH 4/7] moved formatDataGraphs to utils folder as separete function --- src/app/components/CoinChart.tsx | 33 +++----------------------------- src/app/utils/formatDateGraph.ts | 29 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 src/app/utils/formatDateGraph.ts diff --git a/src/app/components/CoinChart.tsx b/src/app/components/CoinChart.tsx index 6ad34f4..3ff3e69 100644 --- a/src/app/components/CoinChart.tsx +++ b/src/app/components/CoinChart.tsx @@ -13,6 +13,7 @@ import { import { AppDispatch, useAppSelector } from "../../redux/store"; import { getCoinDataGraph } from "../../redux/features/selectedCoinSlice"; import formatNumber from "../utils/formatNumber"; +import formatDateGraphs from "../utils/formatDateGraph"; type Payload = { date: string; @@ -107,34 +108,6 @@ export default function CoinChart({ } }; - const formatDateGraphs = (tick: string): string => { - const date = new Date(tick); - let formattedDate: string; - - if (days === "1") { - formattedDate = date.toLocaleTimeString(undefined, { - hour: "2-digit", - minute: "2-digit", - hour12: false, - }); - } else if (["7", "14", "30"].includes(days)) { - formattedDate = date.toLocaleDateString(undefined, { - day: "numeric", - month: "short", - }); - } else if (["90", "180", "365", "max"].includes(days)) { - formattedDate = date.toLocaleDateString(undefined, { - day: "numeric", - month: "short", - year: "numeric", - }); - } else { - formattedDate = date.toLocaleDateString(); - } - - return formattedDate; - }; - return (
formatDateGraphs(tick, days)} /> } /> @@ -193,7 +166,7 @@ export default function CoinChart({ dataKey="date" axisLine={false} tickLine={false} - tickFormatter={formatDateGraphs} + tickFormatter={(tick) => formatDateGraphs(tick, days)} /> } /> diff --git a/src/app/utils/formatDateGraph.ts b/src/app/utils/formatDateGraph.ts new file mode 100644 index 0000000..a1e33eb --- /dev/null +++ b/src/app/utils/formatDateGraph.ts @@ -0,0 +1,29 @@ +const formatDateGraphs = (tick: string, days: string): string => { + const date = new Date(tick); + let formattedDate: string; + + if (days === "1") { + formattedDate = date.toLocaleTimeString(undefined, { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + } else if (["7", "14", "30"].includes(days)) { + formattedDate = date.toLocaleDateString(undefined, { + day: "numeric", + month: "short", + }); + } else if (["90", "180", "365", "max"].includes(days)) { + formattedDate = date.toLocaleDateString(undefined, { + day: "numeric", + month: "short", + year: "numeric", + }); + } else { + formattedDate = date.toLocaleDateString(); + } + + return formattedDate; +}; + +export default formatDateGraphs; From 9f7ef733d5fe2c9956999afa5e85e2e697358015 Mon Sep 17 00:00:00 2001 From: bonz88 Date: Wed, 13 Mar 2024 21:15:40 +0100 Subject: [PATCH 5/7] exported TimelineItem type so it can be reused inside TimelineDays component --- src/redux/features/timelineSlice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/features/timelineSlice.ts b/src/redux/features/timelineSlice.ts index 2438366..e0a04cb 100644 --- a/src/redux/features/timelineSlice.ts +++ b/src/redux/features/timelineSlice.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -type TimelineItem = { +export type TimelineItem = { id: number; days: string; display: string; From dcfa723870994497f24eae117c57f63e1767718c Mon Sep 17 00:00:00 2001 From: bonz88 Date: Wed, 13 Mar 2024 21:16:59 +0100 Subject: [PATCH 6/7] Created separeted component TimelineDays to displays all available days to select --- src/app/components/Timeline.tsx | 21 +++------------------ src/app/components/TimelineDays.tsx | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 src/app/components/TimelineDays.tsx diff --git a/src/app/components/Timeline.tsx b/src/app/components/Timeline.tsx index d96cd95..e9cb787 100644 --- a/src/app/components/Timeline.tsx +++ b/src/app/components/Timeline.tsx @@ -1,28 +1,13 @@ "use client"; -import { useDispatch } from "react-redux"; -import { AppDispatch, useAppSelector } from "../../redux/store"; -import { activeTimeline } from "../../redux/features/timelineSlice"; - +import { useAppSelector } from "../../redux/store"; +import TimelineDays from "./TimelineDays"; export default function Timeline() { - const dispatch: AppDispatch = useDispatch(); const timeline = useAppSelector((state) => state.timeline.timeline); return (
{timeline.map((t) => ( - + ))}
); diff --git a/src/app/components/TimelineDays.tsx b/src/app/components/TimelineDays.tsx new file mode 100644 index 0000000..75c8548 --- /dev/null +++ b/src/app/components/TimelineDays.tsx @@ -0,0 +1,28 @@ +import { useDispatch } from "react-redux"; +import { AppDispatch } from "../../redux/store"; +import { activeTimeline } from "../../redux/features/timelineSlice"; +import { TimelineItem } from "../../redux/features/timelineSlice"; + +type TimelineDaysProps = { + timeline: TimelineItem; +}; + +export default function TimelineDays({ timeline }: TimelineDaysProps) { + const dispatch: AppDispatch = useDispatch(); + + return ( + + ); +} From 082ef7b804f0e53d09b57a2b22087388a67f605f Mon Sep 17 00:00:00 2001 From: bonz88 Date: Wed, 13 Mar 2024 21:20:28 +0100 Subject: [PATCH 7/7] removed empty div, and aplied styling on main tag to get same output --- src/app/page.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 63104eb..ee60a92 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,7 +5,7 @@ import Timeline from "./components/Timeline"; export default function Home() { return ( -
+
@@ -16,7 +16,6 @@ export default function Home() {
-
); }