init ranking w/ fake data (redesign later)
This commit is contained in:
parent
eb8a85893d
commit
e0e3e744f3
4 changed files with 387 additions and 48 deletions
|
@ -1,38 +1,54 @@
|
|||
import { m, Variants } from "framer-motion";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
|
||||
function NavBar() {
|
||||
return (
|
||||
<div className="m-3">
|
||||
<m.div
|
||||
className="mr-2 flex h-24 w-full flex-row items-center justify-between bg-zinc-800 p-1 lg:h-screen lg:w-24 lg:flex-col"
|
||||
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}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
>
|
||||
<m.div
|
||||
className="flex flex-row pl-5 lg:flex-col lg:pl-0 lg:pt-5"
|
||||
className="flex flex-row items-center justify-center pl-5 lg:flex-col lg:pl-0 lg:pt-5"
|
||||
variants={navStripVariants}
|
||||
>
|
||||
<m.div variants={navIconVariants} className="pr-5 lg:pr-0 lg:pb-5">
|
||||
<Link href="/dashboard">
|
||||
<m.div variants={navIconVariants} className="pr-5 lg:pr-0 lg:pb-3">
|
||||
<ActiveLink href="/dashboard">
|
||||
<DashIcon />
|
||||
</Link>
|
||||
</ActiveLink>
|
||||
</m.div>
|
||||
<m.div variants={navIconVariants} className="pr-5 lg:pr-0 lg:pb-5">
|
||||
<Link href="/dashboard/ranking">
|
||||
<m.div
|
||||
variants={navIconVariants}
|
||||
className="pr-5 lg:pr-0 lg:pt-3 lg:pb-3"
|
||||
>
|
||||
<ActiveLink href="/dashboard/ranking">
|
||||
<RankingIcon />
|
||||
</Link>
|
||||
</ActiveLink>
|
||||
</m.div>
|
||||
</m.div>
|
||||
<m.div
|
||||
className="flex flex-row items-center justify-center pr-5 lg:w-full lg:flex-col lg:pr-0 lg:pb-5"
|
||||
variants={navStripVariants}
|
||||
>
|
||||
<m.div
|
||||
className="fill-white stroke-white"
|
||||
whileHover={{
|
||||
color: "#fca311",
|
||||
}}
|
||||
whileTap={{
|
||||
color: "#dd4444",
|
||||
}}
|
||||
>
|
||||
<Link href="/">
|
||||
<ExitIcon />
|
||||
</Link>
|
||||
</m.div>
|
||||
</m.div>
|
||||
</m.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -46,6 +62,12 @@ const NavSvgWrap = (props: { children: React.ReactNode }) => {
|
|||
x={0}
|
||||
y={0}
|
||||
origin="center"
|
||||
whileHover={{
|
||||
scale: 1.1,
|
||||
}}
|
||||
whileTap={{
|
||||
scale: 0.9,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</m.svg>
|
||||
|
@ -57,9 +79,9 @@ const DashIcon = () => {
|
|||
<NavSvgWrap>
|
||||
<m.path
|
||||
d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
strokeWidth="1"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</NavSvgWrap>
|
||||
);
|
||||
|
@ -70,9 +92,9 @@ const ExitIcon = () => {
|
|||
<NavSvgWrap>
|
||||
<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"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
strokeWidth="1"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</NavSvgWrap>
|
||||
);
|
||||
|
@ -83,14 +105,29 @@ const RankingIcon = () => {
|
|||
<NavSvgWrap>
|
||||
<m.path
|
||||
d="M7.5 21H2V9h5.5v12zm7.25-18h-5.5v18h5.5V3zM22 11h-5.5v10H22V11z"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
strokeWidth="1"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</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 = {
|
||||
initial: {
|
||||
opacity: 0,
|
||||
|
|
208
pages/api/fakeRanking.ts
Normal file
208
pages/api/fakeRanking.ts
Normal 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 };
|
|
@ -17,31 +17,31 @@ function Dashboard() {
|
|||
animate="animate"
|
||||
>
|
||||
<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}
|
||||
>
|
||||
1
|
||||
</m.div>
|
||||
<m.div
|
||||
className="col-span-1 row-span-3 m-2 bg-zinc-800 lg:col-span-3 lg:row-span-1"
|
||||
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}
|
||||
>
|
||||
2
|
||||
</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}
|
||||
>
|
||||
3
|
||||
</m.div>
|
||||
<m.div
|
||||
className="col-span-1 row-span-4 m-2 bg-zinc-800 lg:col-span-4 lg:row-span-1"
|
||||
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}
|
||||
>
|
||||
4
|
||||
</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}
|
||||
>
|
||||
5
|
||||
|
|
|
@ -1,26 +1,101 @@
|
|||
import { m, Variants } from "framer-motion";
|
||||
import Head from "next/head";
|
||||
import { ReactElement } from "react";
|
||||
import { ReactElement, useEffect, useState } from "react";
|
||||
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 (
|
||||
<>
|
||||
<Head>
|
||||
<title>Ranking - InvestBot</title>
|
||||
</Head>
|
||||
<div className="flex min-h-screen flex-col items-center justify-start py-2">
|
||||
<div className="flex w-full justify-center">
|
||||
<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}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
>
|
||||
<m.div
|
||||
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"
|
||||
<m.h1
|
||||
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}
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
|
@ -48,23 +123,42 @@ const containerVariants: Variants = {
|
|||
const headerVariants: Variants = {
|
||||
initial: {
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
y: -100,
|
||||
},
|
||||
animate: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 0.5,
|
||||
delay: 1.0,
|
||||
duration: 1.0,
|
||||
type: "spring",
|
||||
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>;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
export default Ranking;
|
||||
|
|
Loading…
Reference in a new issue