diff options
| author | Pinapelz <yukais@pinapelz.com> | 2024-10-05 16:19:16 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2024-10-05 16:19:16 -0700 |
| commit | 1ab5215a4294c2bd80443a14088fcd79cbd97d16 (patch) | |
| tree | ee5dd075c17be63898c2321eb22a8719a140c395 | |
| parent | 73b27fe585ac4fe7dd05f83e8309712c47dd0dfb (diff) | |
add countdown timer to date in next milestone projection
| -rw-r--r-- | src/components/ChannelCard/ChannelCard.tsx | 6 | ||||
| -rw-r--r-- | src/components/Countdown.tsx | 70 | ||||
| -rw-r--r-- | src/pages/stats/[slug].tsx | 144 |
3 files changed, 144 insertions, 76 deletions
diff --git a/src/components/ChannelCard/ChannelCard.tsx b/src/components/ChannelCard/ChannelCard.tsx index 8f4fa07..44541ad 100644 --- a/src/components/ChannelCard/ChannelCard.tsx +++ b/src/components/ChannelCard/ChannelCard.tsx @@ -1,5 +1,6 @@ import React from 'react'; import Image from 'next/image'; +import Countdown from '../Countdown'; type ChannelCardProps = { channel_id: string; @@ -78,8 +79,11 @@ const ChannelCard: React.FC<ChannelCardProps> = ({ Next Milestone: {Number(nextMilestone).toLocaleString()} </p> <p className="text-xs sm:text-sm text-gray-300"> - Estimated in {nextMilestoneDays} days ({nextMilestoneDate}) + Estimated Date: {nextMilestoneDate} </p> + <div className="flex justify-center"> + <Countdown targetDate={nextMilestoneDate} /> + </div> </div> <button onClick={() => window.open(`https://youtube.com/channel/${channel_id}`, '_blank')} diff --git a/src/components/Countdown.tsx b/src/components/Countdown.tsx new file mode 100644 index 0000000..69e4bd0 --- /dev/null +++ b/src/components/Countdown.tsx @@ -0,0 +1,70 @@ +import React, { useEffect, useState } from 'react'; + +interface CountdownProps { + targetDate: string; +} + +const Countdown: React.FC<CountdownProps> = ({ targetDate }) => { + const calculateTimeLeft = () => { + const difference = new Date(targetDate).getTime() - new Date().getTime(); + let timeLeft = { + days: '0', + hours: '0', + minutes: '0', + seconds: '0', + }; + + if (difference > 0) { + timeLeft = { + days: Math.floor(difference / (1000 * 60 * 60 * 24)).toString(), + hours: Math.floor((difference / (1000 * 60 * 60)) % 24).toString(), + minutes: Math.floor((difference / 1000 / 60) % 60).toString(), + seconds: Math.floor((difference / 1000) % 60).toString(), + }; + } + + return timeLeft; + }; + + const [timeLeft, setTimeLeft] = useState({ + days: '--', + hours: '--', + minutes: '--', + seconds: '--', + }); + + useEffect(() => { + setTimeLeft(calculateTimeLeft()); + + const timer = setInterval(() => { + setTimeLeft(calculateTimeLeft()); + }, 1000); + + return () => clearInterval(timer); + }, [targetDate]); + + return ( + <div className="bg-gray-700 text-white font-sans"> + <div className="flex gap-2 text-2xl font-bold"> + <div className="flex flex-col items-center"> + <div className="text-4xl">{timeLeft.days}</div> + <div className="text-xs uppercase">Days</div> + </div> + <div className="flex flex-col items-center"> + <div className="text-4xl">{timeLeft.hours}</div> + <div className="text-xs uppercase">Hours</div> + </div> + <div className="flex flex-col items-center"> + <div className="text-4xl">{timeLeft.minutes}</div> + <div className="text-xs uppercase">Minutes</div> + </div> + <div className="flex flex-col items-center"> + <div className="text-4xl">{timeLeft.seconds}</div> + <div className="text-xs uppercase">Seconds</div> + </div> + </div> + </div> + ); +}; + +export default Countdown;
\ No newline at end of file diff --git a/src/pages/stats/[slug].tsx b/src/pages/stats/[slug].tsx index 8288dbf..f70fd69 100644 --- a/src/pages/stats/[slug].tsx +++ b/src/pages/stats/[slug].tsx @@ -7,6 +7,7 @@ import Footer from "@/components/Footer/Footer"; import ChannelCard from "@/components/ChannelCard/ChannelCard" import Head from "next/head"; import TitleBar from "../../components/TitleBar/TitleBar"; +import Countdown from "@/components/Countdown"; interface ChannelDataProp { channel_id: string; @@ -56,90 +57,83 @@ function Page({ sevenDayGraphData, slug, milestoneData, -}: { + }: { chartData: GraphDataProp; channelData: ChannelDataProp; sevenDayGraphData: GraphDataProp; slug: string; milestoneData: CompactTableProps; -}) { - console.log(milestoneData); + }) { return ( - <> - <Head> - <title>{slug as string} - PhaseTracker</title> - <meta - property="og:title" - content={`${slug as string} - PhaseTracker`} - /> - <meta - name="description" - content={`Belonging to ${channelData.sub_org} with ${channelData.subscribers} subscribers`} - /> - <meta - name="og:description" - content={`${channelData.sub_org} - ${channelData.subscribers}`} - /> - <meta property="og:image" content={`${channelData.profile_pic}`} /> - <meta - name="viewport" - content="width=device-width, initial-scale=1.0" - ></meta> - <meta name="author" content="Pinapelz"></meta> - </Head> - <TitleBar - title={slug as string} - redirectUrl="/" - showHomeButton - backgroundColor="black" - /> - <div className="flex justify-center px-12 "> - <ChannelCard - channel_id={channelData.channel_id} - name={channelData.channel_name} - avatarUrl={channelData.profile_pic} - subscriberCount={channelData.subscribers} - videoCount={channelData.video_count} - viewCount={channelData.view_count} - suborg={channelData.sub_org} - nextMilestone={channelData.next_milestone} - nextMilestoneDays={channelData.days_until_next_milestone} - nextMilestoneDate={channelData.next_milestone_date} - /> + <> + <Head> + <title>{slug as string} - PhaseTracker</title> + <meta property="og:title" content={`${slug as string} - PhaseTracker`} /> + <meta + name="description" + content={`Belonging to ${channelData.sub_org} with ${channelData.subscribers} subscribers`} + /> + <meta + name="og:description" + content={`${channelData.sub_org} - ${channelData.subscribers}`} + /> + <meta property="og:image" content={`${channelData.profile_pic}`} /> + <meta name="viewport" content="width=device-width, initial-scale=1.0"></meta> + <meta name="author" content="Pinapelz"></meta> + </Head> + <TitleBar + title={slug as string} + redirectUrl="/" + showHomeButton + backgroundColor="black" + /> + <div className="flex justify-center px-12"> + <ChannelCard + channel_id={channelData.channel_id} + name={channelData.channel_name} + avatarUrl={channelData.profile_pic} + subscriberCount={channelData.subscribers} + videoCount={channelData.video_count} + viewCount={channelData.view_count} + suborg={channelData.sub_org} + nextMilestone={channelData.next_milestone} + nextMilestoneDays={channelData.days_until_next_milestone} + nextMilestoneDate={channelData.next_milestone_date} + /> + </div> + <div className="hidden sm:block"> + <Divider text="Individual Data" description="Data before collection start date are not shown" /> + <div className="px-48 mb-10 mt-10"> + <div className="mb-12"> + <DataChart + overrideBGColor="black" + overrideBorderColor="black" + chartData={chartData} + /> </div> - <div className="hidden sm:block"> - <Divider text="Individual Data" description="Data before collection start date are not shown" /> - <div className="px-48 mb-10 mt-10"> - <div className="mb-12"> - <DataChart - overrideBGColor="black" - overrideBorderColor="black" - chartData={chartData} - /> - </div> - <div className="mb-12"> - <DataChart - chartData={sevenDayGraphData} - overrideBGColor="black" - overrideBorderColor="black" - graphTitle="7 Day Historical" - /> - </div> - </div> - <Divider text="Milestones" description="Approximations are shown for milestones before data collection started" /> - <div className="mb-12 mx-24"> - <CompactTable - tableData={{ - dates: milestoneData.dates, - milestones: milestoneData.milestones, - }} - /> - </div> + <div className="mb-12"> + <DataChart + chartData={sevenDayGraphData} + overrideBGColor="black" + overrideBorderColor="black" + graphTitle="7 Day Historical" + /> </div> - <Footer /> - </> + </div> + <Divider text="Historical Milestones" description="Approximations are shown for milestones before data collection started" /> + <div className="mb-12 mx-24"> + <CompactTable + tableData={{ + dates: milestoneData.dates, + milestones: milestoneData.milestones, + }} + /> + </div> + </div> + <Footer /> + </> ); -} + } async function getGraphData(slug: string) { const encodedSlug = encodeURIComponent(slug as string); |
