Merge pull request #5 from Invest-Bot/dev

Merge Dev: ranking init
This commit is contained in:
zach 2022-12-22 08:26:54 -08:00 committed by GitHub
commit 49f6a2685c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 395 additions and 53 deletions

View file

@ -169,7 +169,7 @@ const logoContainerVariants: Variants = {
transition: { transition: {
duration: 4, duration: 4,
type: "spring", type: "spring",
stiffness: 20, stiffness: 15,
}, },
}, },
}; };

View file

@ -1,35 +1,54 @@
import { m, Variants } from "framer-motion"; import { m, Variants } from "framer-motion";
import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
function NavBar() { function NavBar() {
return ( return (
<div className="m-3">
<m.div <m.div
className="mr-2 flex h-full w-24 flex-col items-center justify-between bg-zinc-800 p-1" className="flex min-h-[5rem] w-full flex-row items-center justify-between rounded-2xl bg-zinc-800 p-1 lg:h-full lg:w-24 lg:flex-col"
variants={navContainerVariants} variants={navContainerVariants}
initial="initial" initial="initial"
animate="animate" animate="animate"
> >
<m.div className="flex flex-col pt-5" variants={navStripVariants}> <m.div
<m.div variants={navIconVariants} className="pb-5"> className="flex flex-row items-center justify-center pl-5 lg:flex-col lg:pl-0 lg:pt-5"
<Link href="/dashboard"> variants={navStripVariants}
>
<m.div variants={navIconVariants} className="pr-5 lg:pr-0 lg:pb-3">
<ActiveLink href="/dashboard">
<DashIcon /> <DashIcon />
</Link> </ActiveLink>
</m.div> </m.div>
<m.div variants={navIconVariants} className="pb-5"> <m.div
<Link href="/dashboard/ranking"> variants={navIconVariants}
className="pr-5 lg:pr-0 lg:pt-3 lg:pb-3"
>
<ActiveLink href="/dashboard/ranking">
<RankingIcon /> <RankingIcon />
</Link> </ActiveLink>
</m.div> </m.div>
</m.div> </m.div>
<m.div <m.div
className="flex w-full flex-col items-center justify-center pb-5" className="flex flex-row items-center justify-center pr-5 lg:w-full lg:flex-col lg:pr-0 lg:pb-5"
variants={navStripVariants} variants={navStripVariants}
>
<m.div
className="fill-white stroke-white"
whileHover={{
color: "#fca311",
}}
whileTap={{
color: "#dd4444",
}}
> >
<Link href="/"> <Link href="/">
<ExitIcon /> <ExitIcon />
</Link> </Link>
</m.div> </m.div>
</m.div> </m.div>
</m.div>
</div>
); );
} }
@ -43,6 +62,12 @@ const NavSvgWrap = (props: { children: React.ReactNode }) => {
x={0} x={0}
y={0} y={0}
origin="center" origin="center"
whileHover={{
scale: 1.1,
}}
whileTap={{
scale: 0.9,
}}
> >
{props.children} {props.children}
</m.svg> </m.svg>
@ -54,9 +79,9 @@ const DashIcon = () => {
<NavSvgWrap> <NavSvgWrap>
<m.path <m.path
d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z" d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"
fill="white"
stroke="white"
strokeWidth="1" strokeWidth="1"
stroke="currentColor"
fill="currentColor"
/> />
</NavSvgWrap> </NavSvgWrap>
); );
@ -67,9 +92,9 @@ const ExitIcon = () => {
<NavSvgWrap> <NavSvgWrap>
<m.path <m.path
d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"
fill="white"
stroke="white"
strokeWidth="1" strokeWidth="1"
stroke="currentColor"
fill="currentColor"
/> />
</NavSvgWrap> </NavSvgWrap>
); );
@ -80,14 +105,29 @@ const RankingIcon = () => {
<NavSvgWrap> <NavSvgWrap>
<m.path <m.path
d="M7.5 21H2V9h5.5v12zm7.25-18h-5.5v18h5.5V3zM22 11h-5.5v10H22V11z" d="M7.5 21H2V9h5.5v12zm7.25-18h-5.5v18h5.5V3zM22 11h-5.5v10H22V11z"
fill="white"
stroke="white"
strokeWidth="1" strokeWidth="1"
stroke="currentColor"
fill="currentColor"
/> />
</NavSvgWrap> </NavSvgWrap>
); );
}; };
const ActiveLink = (props: { href: string; children: React.ReactNode }) => {
const router = useRouter();
let styling = "text-white";
console.log(router.pathname);
console.log(props.href);
if (router.pathname === props.href) {
styling = "text-[#a855f7]";
}
return (
<Link href={props.href} className={styling}>
{props.children}
</Link>
);
};
const navContainerVariants: Variants = { const navContainerVariants: Variants = {
initial: { initial: {
opacity: 0, opacity: 0,

View file

@ -34,7 +34,7 @@ function DashLayout(props: DashLayoutProps) {
<meta property="og:site_name" content="InvestBot" /> <meta property="og:site_name" content="InvestBot" />
</Head> </Head>
<div className="flex h-screen w-screen flex-row overflow-hidden"> <div className="flex h-screen w-screen flex-col overflow-hidden lg:flex-row">
{/* dashboard nav bar */} {/* dashboard nav bar */}
<LazyMotion features={domAnimation}> <LazyMotion features={domAnimation}>
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
@ -46,7 +46,7 @@ function DashLayout(props: DashLayoutProps) {
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
<m.div <m.div
key={router.route.concat("layout-fade")} key={router.route.concat("layout-fade")}
className="h-screen w-screen" className="w-screen overflow-y-scroll"
variants={containerVariants} variants={containerVariants}
initial="initial" initial="initial"
animate="animate" animate="animate"

View file

@ -5,8 +5,6 @@ type Data = {
[key: string]: any; [key: string]: any;
}; };
const secret = process.env.NEXTAUTH_SECRET;
export default async function handler( export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse<Data> res: NextApiResponse<Data>

208
pages/api/fakeRanking.ts Normal file
View file

@ -0,0 +1,208 @@
import type { NextApiRequest, NextApiResponse } from "next";
type Data = {
[key: string]: any;
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const sortBy = req.query.s ? (req.query.s as string) : undefined;
let data = fakeData;
if (sortBy) {
if (sortBy === "netWorth") {
data = data.sort((a, b) => b.netWorth - a.netWorth);
} else if (sortBy === "dailyChange") {
data = data.sort((a, b) => b.dailyChange - a.dailyChange);
} else if (sortBy === "dailyChangePercent") {
data = data.sort((a, b) => b.dailyChangePercent - a.dailyChangePercent);
} else if (sortBy === "shares") {
data = data.sort((a, b) => b.shares - a.shares);
} else if (sortBy === "points") {
data = data.sort((a, b) => b.points - a.points);
} else if (sortBy === "name") {
data = data.sort((a, b) => a.name.localeCompare(b.name));
}
}
res.status(200).json({ data });
}
interface fakeDataEntry {
id: number;
name: string;
netWorth: number;
points: number;
shares: number;
dailyChange: number;
dailyChangePercent: number;
}
const fakeData: fakeDataEntry[] = [
{
id: 4,
name: "3zachm",
netWorth: 10030, // stocks + points
points: 70, /// uninvested points
shares: 20,
dailyChange: -500,
dailyChangePercent: -0.523,
},
{
id: 1,
name: "ModulatingForce",
netWorth: 142910,
points: 10020,
shares: 200,
dailyChange: 5420,
dailyChangePercent: 0.14,
},
{
id: 2,
name: "notohh",
netWorth: 153495392,
points: 10020,
shares: 2432,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 3,
name: "SecondSock",
netWorth: 153495,
points: 15020,
shares: 20,
dailyChange: 5432,
dailyChangePercent: 0.104,
},
{
id: 0,
name: "Ente",
netWorth: 429481824,
points: 1002022,
shares: 94214,
dailyChange: 3294444224,
dailyChangePercent: 0.94,
},
{
id: 5,
name: "ObnoxiouslyLongNameWICKED",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 6,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 7,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 8,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 9,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 10,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 11,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 12,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 13,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 14,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 15,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 16,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
{
id: 17,
name: "User",
netWorth: 0,
points: 100,
shares: 0,
dailyChange: 0,
dailyChangePercent: 0,
},
];
export type { fakeDataEntry };

View file

@ -11,37 +11,37 @@ function Dashboard() {
<title>Dashboard - InvestBot</title> <title>Dashboard - InvestBot</title>
</Head> </Head>
<m.div <m.div
className="inline-grid h-full w-full grid-cols-2 pt-2 pr-2 xl:grid-cols-5" className="inline-grid w-full grid-cols-1 pt-2 pl-2 lg:h-full lg:grid-cols-5 lg:pl-0 lg:pr-2"
variants={gridContainerVariants} variants={gridContainerVariants}
initial="initial" initial="initial"
animate="animate" animate="animate"
> >
<m.div <m.div
className="col-span-1 m-2 bg-zinc-800" className="col-span-1 m-2 rounded-2xl bg-zinc-800 p-3"
variants={gridItemVariants} variants={gridItemVariants}
> >
1 1
</m.div> </m.div>
<m.div <m.div
className="col-span-3 m-2 bg-zinc-800" className="col-span-1 row-span-3 m-2 rounded-2xl bg-zinc-800 p-3 lg:col-span-3 lg:row-span-1"
variants={gridItemVariants} variants={gridItemVariants}
> >
2 2
</m.div> </m.div>
<m.div <m.div
className="col-span-1 m-2 bg-zinc-800" className="col-span-1 m-2 rounded-2xl bg-zinc-800 p-3"
variants={gridItemVariants} variants={gridItemVariants}
> >
3 3
</m.div> </m.div>
<m.div <m.div
className="col-span-4 m-2 bg-zinc-800" className="col-span-1 row-span-4 m-2 rounded-2xl bg-zinc-800 p-3 lg:col-span-4 lg:row-span-1"
variants={gridItemVariants} variants={gridItemVariants}
> >
4 4
</m.div> </m.div>
<m.div <m.div
className="col-span-1 m-2 bg-zinc-800" className="col-span-1 m-2 rounded-2xl bg-zinc-800 p-3"
variants={gridItemVariants} variants={gridItemVariants}
> >
5 5

View file

@ -1,26 +1,101 @@
import { m, Variants } from "framer-motion"; import { m, Variants } from "framer-motion";
import Head from "next/head"; import Head from "next/head";
import { ReactElement } from "react"; import { ReactElement, useEffect, useState } from "react";
import DashLayout from "../../layouts/DashLayout"; import DashLayout from "../../layouts/DashLayout";
import { fakeDataEntry } from "../api/fakeRanking";
function Ranking() {
const [sortBy, setSortBy] = useState("netWorth");
const [fakeData, setFakeData] = useState([]);
useEffect(() => {
fetch(`/api/fakeRanking?s=${sortBy}`)
.then((res) => res.json())
.then((data) => {
setFakeData(data.data);
});
}, [sortBy]);
function Dashboard() {
return ( return (
<> <>
<Head> <Head>
<title>Ranking - InvestBot</title> <title>Ranking - InvestBot</title>
</Head> </Head>
<div className="flex min-h-screen flex-col items-center justify-start py-2"> <div className="flex w-full justify-center">
<m.div <m.div
className="grid w-[90vw] grid-cols-1 py-2 sm:grid-cols-2 md:grid-cols-4 lg:w-[75vw]" className="ml-3 flex w-full flex-col items-center justify-start font-spaceMono font-semibold lg:ml-0"
variants={containerVariants} variants={containerVariants}
initial="initial" initial="initial"
animate="animate" animate="animate"
> >
<m.div <m.h1
className="col-span-1 flex w-full items-center justify-center bg-gradient-to-r from-purple-400 to-pink-600 bg-clip-text pt-[200px] pb-[100px] font-plusJakarta text-transparent sm:col-span-2 md:col-span-4" className="hidden bg-gradient-to-tr from-purple-500 to-purple-100 bg-clip-text py-10 text-center font-plusJakarta text-5xl font-bold text-white text-transparent lg:block lg:text-6xl"
variants={headerVariants} variants={headerVariants}
> >
<m.h1 className="text-6xl">rankings</m.h1> Top Investors
</m.h1>
<m.div
className="inline-grid w-full rounded-t-2xl bg-zinc-800 bg-opacity-70 p-3 pt-4 text-xl backdrop-blur lg:text-2xl"
variants={rankingCardVariants}
initial="initial"
animate="animate"
>
<m.div className="inline-grid w-full grid-flow-col grid-cols-[0.75fr_4fr_3fr_2fr] gap-2 border-b-2 border-zinc-400 px-5 pb-3 text-right text-gray-300 md:grid-cols-[0.5fr_4fr_repeat(3,_2fr)_1.5fr]">
<m.h1 className="text-left md:text-center">#</m.h1>
<m.h1
className="overflow-hidden overflow-ellipsis whitespace-nowrap text-left"
onClick={() => setSortBy("name")}
>
Name
</m.h1>
<m.h1 onClick={() => setSortBy("netWorth")}>Assets</m.h1>
<m.h1
className="hidden md:block"
onClick={() => setSortBy("points")}
>
Points
</m.h1>
<m.h1
className="hidden md:block"
onClick={() => setSortBy("shares")}
>
Shares
</m.h1>
<m.h1 onClick={() => setSortBy("dailyChange")}>Daily</m.h1>
</m.div>
{
// TODO: add arrow to show which column is being sorted by and which direction
fakeData.map((entry: fakeDataEntry, index) => {
let changeClass = " text-lime-500";
if (entry.dailyChangePercent < 0) {
changeClass = " text-red-500";
}
return (
<m.div key={entry.id}>
<m.div className="inline-grid w-full grid-flow-col grid-cols-[1fr_4fr_3fr_2fr] gap-2 border-b-2 border-zinc-700 px-5 py-2 text-right md:grid-cols-[0.5fr_4fr_repeat(3,_2fr)_1.5fr]">
<m.h1 className="text-left md:text-center">
{index + 1}
</m.h1>
<m.h1 className="overflow-hidden overflow-ellipsis whitespace-nowrap text-left">
{entry.name}
</m.h1>
<m.h1>{entry.netWorth.toLocaleString("en-US")}</m.h1>
<m.h1 className="hidden md:block">
{entry.points.toLocaleString("en-US")}
</m.h1>
<m.h1 className="hidden md:block">
{entry.shares.toLocaleString("en-US")}
</m.h1>
<m.h1 className={changeClass}>
{(
Math.round(entry.dailyChangePercent * 1000) / 10
).toFixed(1) + "%"}
</m.h1>
</m.div>
</m.div>
);
})
}
</m.div> </m.div>
</m.div> </m.div>
</div> </div>
@ -48,23 +123,42 @@ const containerVariants: Variants = {
const headerVariants: Variants = { const headerVariants: Variants = {
initial: { initial: {
opacity: 0, opacity: 0,
y: 100, y: -100,
}, },
animate: { animate: {
opacity: 1, opacity: 1,
y: 0, y: 0,
transition: { transition: {
delay: 0.5, delay: 1.0,
duration: 1.0, duration: 1.0,
type: "spring", type: "spring",
bounce: 0.5, bounce: 0.5,
stiffness: 80, stiffness: 60,
}, },
}, },
}; };
Dashboard.getLayout = function getLayout(page: ReactElement) { const rankingCardVariants: Variants = {
initial: {
opacity: 0,
y: 300,
},
animate: {
opacity: 1,
y: 0,
transition: {
duration: 3,
delayChildren: 0.5,
staggerChildren: 0.25,
type: "spring",
bounce: 0.5,
stiffness: 40,
},
},
};
Ranking.getLayout = function getLayout(page: ReactElement) {
return <DashLayout>{page}</DashLayout>; return <DashLayout>{page}</DashLayout>;
}; };
export default Dashboard; export default Ranking;

View file

@ -3,6 +3,7 @@
@tailwind utilities; @tailwind utilities;
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"); @import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap");
@import url("https://fonts.googleapis.com/css?family=Plus+Jakarta+Sans:300,400,500,700&display=swap"); @import url("https://fonts.googleapis.com/css?family=Plus+Jakarta+Sans:300,400,500,700&display=swap");
@import url("https://fonts.googleapis.com/css?family=Space+Mono:300,400,500,700&display=swap");
html, html,
body { body {

View file

@ -10,6 +10,7 @@ module.exports = {
fontFamily: { fontFamily: {
roboto: ["Roboto", "sans-serif"], roboto: ["Roboto", "sans-serif"],
plusJakarta: ["Plus Jakarta Sans", "sans-serif"], plusJakarta: ["Plus Jakarta Sans", "sans-serif"],
spaceMono: ["Space Mono", "monospace"],
}, },
}, },
}, },