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.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,17 +59,20 @@ 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") { - return
Loading...
; - } - if (hasError) { return
Error fetching data
; } @@ -73,11 +82,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 +96,24 @@ 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", + }) + ); } }; return ( -
+
{chartType === "volume" @@ -113,19 +133,24 @@ export default function CoinChart({ {chartType === "volume" ? ( - + - + formatDateGraphs(tick, days)} + /> } /> - + ) : ( - + @@ -134,10 +159,15 @@ export default function CoinChart({ dataKey="value" stroke="#7878FA" strokeWidth="3" - fill="url(#color)" + fill="url(#colorAreaChart)" /> - + formatDateGraphs(tick, days)} + /> } /> diff --git a/src/app/components/Timeline.tsx b/src/app/components/Timeline.tsx new file mode 100644 index 0000000..e9cb787 --- /dev/null +++ b/src/app/components/Timeline.tsx @@ -0,0 +1,14 @@ +"use client"; +import { useAppSelector } from "../../redux/store"; +import TimelineDays from "./TimelineDays"; +export default function Timeline() { + 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 ( + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 11fffb5..ee60a92 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,10 +1,11 @@ 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,9 @@ export default function Home() {
+
+ +
); } 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; diff --git a/src/redux/features/timelineSlice.ts b/src/redux/features/timelineSlice.ts new file mode 100644 index 0000000..e0a04cb --- /dev/null +++ b/src/redux/features/timelineSlice.ts @@ -0,0 +1,93 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +export 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, }, });