From 986f7ab88866f3044f8f7fb31df416de0cee6c11 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 10 Apr 2022 01:50:13 +0300 Subject: [PATCH] feat: new dashboards logic, closes #17 --- src/pages/_middleware.ts | 17 ++ src/pages/dashboards.tsx | 211 ------------------ src/pages/dashboards/index.tsx | 88 ++++++++ src/pages/dashboards/view/[id].tsx | 118 ++++++++++ src/web/display/Button.tsx | 10 + src/web/display/InfoBox.tsx | 5 + src/web/display/LineHeader.tsx | 7 + src/web/display/dashboardCreator.tsx | 61 ++--- src/web/display/displayForecastsWrappers.tsx | 8 +- .../display/displayOneForecastForCapture.tsx | 4 +- src/web/hooks.ts | 15 ++ src/web/search/CommonDisplay.tsx | 14 +- 12 files changed, 289 insertions(+), 269 deletions(-) create mode 100644 src/pages/_middleware.ts delete mode 100644 src/pages/dashboards.tsx create mode 100644 src/pages/dashboards/index.tsx create mode 100644 src/pages/dashboards/view/[id].tsx create mode 100644 src/web/display/Button.tsx create mode 100644 src/web/display/InfoBox.tsx create mode 100644 src/web/display/LineHeader.tsx create mode 100644 src/web/hooks.ts diff --git a/src/pages/_middleware.ts b/src/pages/_middleware.ts new file mode 100644 index 0000000..03cbd34 --- /dev/null +++ b/src/pages/_middleware.ts @@ -0,0 +1,17 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function middleware(req: NextRequest) { + const { pathname, searchParams } = req.nextUrl; + + console.log(pathname); + if (pathname === "/dashboards") { + const dashboardId = searchParams.get("dashboardId"); + if (dashboardId) { + return NextResponse.redirect( + new URL(`/dashboards/view/${dashboardId}`, req.url) + ); + } + } + + return NextResponse.next(); +} diff --git a/src/pages/dashboards.tsx b/src/pages/dashboards.tsx deleted file mode 100644 index ca47815..0000000 --- a/src/pages/dashboards.tsx +++ /dev/null @@ -1,211 +0,0 @@ -/* Imports */ -import axios from "axios"; -import { GetServerSideProps, NextPage } from "next"; -import { useRouter } from "next/router"; // https://nextjs.org/docs/api-reference/next/router -import { useState } from "react"; - -import { DashboardItem } from "../backend/dashboards"; -import { getPlatformsConfig, PlatformConfig } from "../backend/platforms"; -import { DashboardCreator } from "../web/display/DashboardCreator"; -import { DisplayForecasts } from "../web/display/DisplayForecasts"; -import { Layout } from "../web/display/Layout"; -import { addLabelsToForecasts, FrontendForecast } from "../web/platforms"; -import { getDashboardForecastsByDashboardId } from "../web/worker/getDashboardForecasts"; - -interface Props { - initialDashboardForecasts: FrontendForecast[]; - initialDashboardId: string | null; - initialDashboardItem: DashboardItem | null; - platformsConfig: PlatformConfig[]; -} - -export const getServerSideProps: GetServerSideProps = async ( - context -) => { - const dashboardIdQ = context.query.dashboardId; - const dashboardId: string | undefined = - typeof dashboardIdQ === "object" ? dashboardIdQ[0] : dashboardIdQ; - - const platformsConfig = getPlatformsConfig({ withGuesstimate: false }); - - if (!dashboardId) { - return { - props: { - platformsConfig, - initialDashboardForecasts: [], - initialDashboardId: null, - initialDashboardItem: null, - }, - }; - } - - const { dashboardForecasts, dashboardItem } = - await getDashboardForecastsByDashboardId({ - dashboardId, - }); - const frontendDashboardForecasts = addLabelsToForecasts( - dashboardForecasts, - platformsConfig - ); - - return { - props: { - initialDashboardForecasts: frontendDashboardForecasts, - initialDashboardId: dashboardId, - initialDashboardItem: dashboardItem, - platformsConfig, - }, - }; -}; - -/* Body */ -const DashboardsPage: NextPage = ({ - initialDashboardForecasts, - initialDashboardItem, - platformsConfig, -}) => { - const router = useRouter(); - const [dashboardForecasts, setDashboardForecasts] = useState( - initialDashboardForecasts - ); - const [dashboardItem, setDashboardItem] = useState(initialDashboardItem); - - const handleSubmit = async (data) => { - console.log(data); - // Send to server to create - // Get back the id - let response = await axios({ - url: "/api/create-dashboard-from-ids", - method: "POST", - headers: { "Content-Type": "application/json" }, - data: JSON.stringify(data), - }).then((res) => res.data); - let dashboardId = response.dashboardId; - if (!!dashboardId) { - console.log("response: ", response); - if (typeof window !== "undefined") { - let urlWithoutDefaultParameters = `/dashboards?dashboardId=${dashboardId}`; - if (!window.location.href.includes(urlWithoutDefaultParameters)) { - window.history.replaceState( - null, - "Metaforecast", - urlWithoutDefaultParameters - ); - } - } - // router.push(`?dashboardId=${dashboardId}`) - // display it - - let { dashboardForecasts, dashboardItem } = - await getDashboardForecastsByDashboardId({ - dashboardId, - }); - setDashboardForecasts( - addLabelsToForecasts(dashboardForecasts, platformsConfig) - ); - setDashboardItem(dashboardItem); - } - }; - - let isGraubardEasterEgg = (name: string) => - name == "Clay Graubard" ? true : false; - - return ( - - {/* Display forecasts */} -
-

- {!!dashboardItem ? dashboardItem.title : ""} -

-

- {!!dashboardItem ? `Created by: ${dashboardItem.creator}` : ""} -

-

- {!!dashboardItem ? `Created by: @` : ""} - - Clay Graubard - -

-

- {!!dashboardItem ? `${dashboardItem.description}` : ""} -

-
- -
- -
- {/* */} -

- - 0 - ? `mx-3 text-md font-medium text-center` - : "hidden" - } - > - Or create your own - - - Create a dashboard! - - -

- -
-
- -
-
-
- ); -}; - -export default DashboardsPage; diff --git a/src/pages/dashboards/index.tsx b/src/pages/dashboards/index.tsx new file mode 100644 index 0000000..b0a2743 --- /dev/null +++ b/src/pages/dashboards/index.tsx @@ -0,0 +1,88 @@ +import axios from "axios"; +import { GetServerSideProps, NextPage } from "next"; +import { useRouter } from "next/router"; // https://nextjs.org/docs/api-reference/next/router + +import { DashboardItem } from "../../backend/dashboards"; +import { getPlatformsConfig, PlatformConfig } from "../../backend/platforms"; +import { DashboardCreator } from "../../web/display/DashboardCreator"; +import { Layout } from "../../web/display/Layout"; +import { LineHeader } from "../../web/display/LineHeader"; +import { addLabelsToForecasts, FrontendForecast } from "../../web/platforms"; +import { getDashboardForecastsByDashboardId } from "../../web/worker/getDashboardForecasts"; + +interface Props { + initialDashboardForecasts: FrontendForecast[]; + initialDashboardId: string | null; + initialDashboardItem: DashboardItem | null; + platformsConfig: PlatformConfig[]; +} + +export const getServerSideProps: GetServerSideProps = async ( + context +) => { + const dashboardIdQ = context.query.dashboardId; + const dashboardId: string | undefined = + typeof dashboardIdQ === "object" ? dashboardIdQ[0] : dashboardIdQ; + + const platformsConfig = getPlatformsConfig({ withGuesstimate: false }); + + if (!dashboardId) { + return { + props: { + platformsConfig, + initialDashboardForecasts: [], + initialDashboardId: null, + initialDashboardItem: null, + }, + }; + } + + const { dashboardForecasts, dashboardItem } = + await getDashboardForecastsByDashboardId({ + dashboardId, + }); + const frontendDashboardForecasts = addLabelsToForecasts( + dashboardForecasts, + platformsConfig + ); + + return { + props: { + initialDashboardForecasts: frontendDashboardForecasts, + initialDashboardId: dashboardId, + initialDashboardItem: dashboardItem, + platformsConfig, + }, + }; +}; + +/* Body */ +const DashboardsPage: NextPage = () => { + const router = useRouter(); + + const handleSubmit = async (data) => { + // Send to server to create + // Get back the id + let response = await axios({ + url: "/api/create-dashboard-from-ids", + method: "POST", + headers: { "Content-Type": "application/json" }, + data: JSON.stringify(data), + }).then((res) => res.data); + await router.push(`/dashboards/view/${response.dashboardId}`); + }; + + return ( + +
+ Create a dashboard! + +
+ +
+
+
+ ); +}; + +export default DashboardsPage; diff --git a/src/pages/dashboards/view/[id].tsx b/src/pages/dashboards/view/[id].tsx new file mode 100644 index 0000000..bce791b --- /dev/null +++ b/src/pages/dashboards/view/[id].tsx @@ -0,0 +1,118 @@ +import { GetServerSideProps, NextPage } from "next"; +import Link from "next/link"; + +import { DashboardItem } from "../../../backend/dashboards"; +import { getPlatformsConfig } from "../../../backend/platforms"; +import { DisplayForecasts } from "../../../web/display/DisplayForecasts"; +import { InfoBox } from "../../../web/display/InfoBox"; +import { Layout } from "../../../web/display/Layout"; +import { LineHeader } from "../../../web/display/LineHeader"; +import { addLabelsToForecasts, FrontendForecast } from "../../../web/platforms"; +import { getDashboardForecastsByDashboardId } from "../../../web/worker/getDashboardForecasts"; + +interface Props { + dashboardForecasts: FrontendForecast[]; + dashboardId: string; + dashboardItem: DashboardItem; +} + +export const getServerSideProps: GetServerSideProps = async ( + context +) => { + const dashboardId = context.query.id as string; + + const platformsConfig = getPlatformsConfig({ withGuesstimate: false }); + + const { dashboardForecasts, dashboardItem } = + await getDashboardForecastsByDashboardId({ + dashboardId, + }); + const frontendDashboardForecasts = addLabelsToForecasts( + dashboardForecasts, + platformsConfig + ); + + return { + props: { + dashboardForecasts: frontendDashboardForecasts, + dashboardId, + dashboardItem, + }, + }; +}; + +const DashboardMetadata: React.FC<{ dashboardItem: DashboardItem }> = ({ + dashboardItem, +}) => ( +
+ {dashboardItem?.title ? ( +

+ {dashboardItem.title} +

+ ) : null} + + {dashboardItem && dashboardItem.creator ? ( +

+ Created by:{" "} + {dashboardItem.creator === "Clay Graubard" ? ( + <> + @ + + Clay Graubard + + + ) : ( + dashboardItem.creator + )} +

+ ) : null} + + {dashboardItem?.description ? ( +

+ {dashboardItem.description} +

+ ) : null} +
+); + +/* Body */ +const ViewDashboardPage: NextPage = ({ + dashboardForecasts, + dashboardItem, + dashboardId, +}) => { + return ( + +
+ {dashboardItem ? ( + + ) : null} + +
+ +
+ +
+ + Dashboards cannot be changed after they are created. + +
+ + + + Create your own dashboard + + +
+
+ ); +}; + +export default ViewDashboardPage; diff --git a/src/web/display/Button.tsx b/src/web/display/Button.tsx new file mode 100644 index 0000000..6508d24 --- /dev/null +++ b/src/web/display/Button.tsx @@ -0,0 +1,10 @@ +interface Props extends React.ButtonHTMLAttributes {} + +export const Button: React.FC = ({ children, ...rest }) => ( + +); diff --git a/src/web/display/InfoBox.tsx b/src/web/display/InfoBox.tsx new file mode 100644 index 0000000..bc69923 --- /dev/null +++ b/src/web/display/InfoBox.tsx @@ -0,0 +1,5 @@ +export const InfoBox: React.FC = ({ children }) => ( +

+ {children} +

+); diff --git a/src/web/display/LineHeader.tsx b/src/web/display/LineHeader.tsx new file mode 100644 index 0000000..585730c --- /dev/null +++ b/src/web/display/LineHeader.tsx @@ -0,0 +1,7 @@ +export const LineHeader: React.FC = ({ children }) => ( +

+

+); diff --git a/src/web/display/dashboardCreator.tsx b/src/web/display/dashboardCreator.tsx index 2996e1f..6f49191 100644 --- a/src/web/display/dashboardCreator.tsx +++ b/src/web/display/dashboardCreator.tsx @@ -1,4 +1,7 @@ -import React, { useState } from "react"; +import React, { EventHandler, SyntheticEvent, useState } from "react"; + +import { Button } from "./Button"; +import { InfoBox } from "./InfoBox"; const exampleInput = `{ "title": "Random example", @@ -13,32 +16,27 @@ interface Props { export const DashboardCreator: React.FC = ({ handleSubmit }) => { const [value, setValue] = useState(exampleInput); - const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false); - const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] = - useState(null); + const [acting, setActing] = useState(false); const handleChange = (event) => { setValue(event.target.value); }; - const handleSubmitInner = (event) => { - clearTimeout(displayingDoneMessageTimer); + const handleSubmitInner: EventHandler = async (event) => { event.preventDefault(); - console.log(value); try { - let newData = JSON.parse(value); + const newData = JSON.parse(value); if (!newData || !newData.ids || newData.ids.length == 0) { throw Error("Not enough objects"); } else { - handleSubmit(newData); - setDisplayingDoneMessage(true); - const timer = setTimeout(() => setDisplayingDoneMessage(false), 3000); - setDisplayingDoneMessageTimer(timer); + setActing(true); + await handleSubmit(newData); + setActing(false); } } catch (error) { - setDisplayingDoneMessage(false); + setActing(false); const substituteText = `Error: ${error.message} Try something like: @@ -50,36 +48,21 @@ Your old input was: ${value}`; }; return ( -
-