App directory refactoring

This commit is contained in:
Darren Clarke 2023-06-26 10:07:12 +00:00 committed by GitHub
parent a53a26f4c0
commit b312a8c862
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 1532 additions and 1447 deletions

View file

@ -1,3 +1,5 @@
"use client";
import { FC, PropsWithChildren } from "react"; import { FC, PropsWithChildren } from "react";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { useAppContext } from "./AppProvider"; import { useAppContext } from "./AppProvider";

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import { Grid, Box, GridSize } from "@mui/material"; import { Grid, Box, GridSize } from "@mui/material";

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import { signOut } from "next-auth/react"; import { signOut } from "next-auth/react";

View file

@ -1,3 +1,5 @@
"use client";
import { import {
FC, FC,
createContext, createContext,

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Link from "next/link"; import Link from "next/link";
import { Button as MUIButton } from "@mui/material"; import { Button as MUIButton } from "@mui/material";

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import { Container, Grid, Box, Button } from "@mui/material"; import { Container, Grid, Box, Button } from "@mui/material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import { Dialog, Box, Grid, Checkbox, IconButton } from "@mui/material"; import { Dialog, Box, Grid, Checkbox, IconButton } from "@mui/material";
import { Close as CloseIcon } from "@mui/icons-material"; import { Close as CloseIcon } from "@mui/icons-material";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Button } from "@mui/material"; import { Button } from "@mui/material";

View file

@ -1,30 +1,23 @@
import { useEffect } from "react"; "use client";
import { NextPage, GetServerSideProps, GetServerSidePropsContext } from "next";
import { useEffect, FC } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { getSession } from "next-auth/react";
import Head from "next/head";
import Link from "next/link"; import Link from "next/link";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { Grid, Button } from "@mui/material"; import { Grid, Button } from "@mui/material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { useCookies } from "react-cookie"; import { useCookies } from "react-cookie";
import { Layout } from "components/Layout"; import { Welcome } from "@/app/_components/Welcome";
import { getUserVisualizations } from "lib/opensearch"; import { WelcomeDialog } from "@/app/_components/WelcomeDialog";
import { Welcome } from "components/Welcome"; import { VisualizationCard } from "@/app/_components/VisualizationCard";
import { WelcomeDialog } from "components/WelcomeDialog"; import { useAppContext } from "@/app/_components/AppProvider";
import { VisualizationCard } from "components/VisualizationCard";
import { useAppContext } from "components/AppProvider";
import { getEmbedded } from "lib/utils";
type MyVisualizationsProps = { type HomeProps = {
visualizations: any; visualizations: any;
embedded: boolean; embedded: boolean;
}; };
const MyVisualizations: NextPage<MyVisualizationsProps> = ({ export const Home: FC<HomeProps> = ({ visualizations, embedded }) => {
visualizations,
embedded,
}) => {
const router = useRouter(); const router = useRouter();
const cookieName = "homeIntroComplete"; const cookieName = "homeIntroComplete";
const [cookies, setCookie] = useCookies([cookieName]); const [cookies, setCookie] = useCookies([cookieName]);
@ -43,10 +36,7 @@ const MyVisualizations: NextPage<MyVisualizationsProps> = ({
}, [homeIntroComplete, router, setCookie]); }, [homeIntroComplete, router, setCookie]);
return ( return (
<Layout embedded={embedded}> <>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<Welcome /> <Welcome />
<Grid <Grid
container container
@ -105,19 +95,6 @@ const MyVisualizations: NextPage<MyVisualizationsProps> = ({
))} ))}
</Grid> </Grid>
<WelcomeDialog /> <WelcomeDialog />
</Layout> </>
); );
}; };
export default MyVisualizations;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const session = (await getSession(context)) ?? null;
const visualizations = await getUserVisualizations(
session?.user?.email ?? "none",
20
);
return { props: { visualizations, embedded: getEmbedded(context) } };
};

View file

@ -1,3 +1,5 @@
"use client";
import { FC, PropsWithChildren } from "react"; import { FC, PropsWithChildren } from "react";
import getConfig from "next/config"; import getConfig from "next/config";
import { Grid, Container } from "@mui/material"; import { Grid, Container } from "@mui/material";
@ -13,7 +15,7 @@ type LayoutProps = PropsWithChildren<{
embedded?: boolean; embedded?: boolean;
}>; }>;
export const Layout: FC<LayoutProps> = ({ export const InternalLayout: FC<LayoutProps> = ({
embedded = false, embedded = false,
children, children,
}: any) => { }: any) => {

View file

@ -1,3 +1,5 @@
"use client";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { IconButton, Menu, MenuItem, Box } from "@mui/material"; import { IconButton, Menu, MenuItem, Box } from "@mui/material";
import { KeyboardArrowDown as KeyboardArrowDownIcon } from "@mui/icons-material"; import { KeyboardArrowDown as KeyboardArrowDownIcon } from "@mui/icons-material";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useEffect, useState } from "react"; import { FC, useEffect, useState } from "react";
import { useAppContext } from "./AppProvider"; import { useAppContext } from "./AppProvider";
import { RawDataViewer } from "./RawDataViewer"; import { RawDataViewer } from "./RawDataViewer";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import { Card, Grid } from "@mui/material"; import { Card, Grid } from "@mui/material";
import { import {

View file

@ -0,0 +1,47 @@
/* eslint-disable react/jsx-props-no-spreading */
import { FC, PropsWithChildren } from "react";
import { SessionProvider } from "next-auth/react";
import { CssBaseline } from "@mui/material";
import { CookiesProvider } from "react-cookie";
import { I18n } from "react-polyglot";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFns";
("use client");
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
import { AppProvider } from "@/app/_components/AppProvider";
import { NextAppDirEmotionCacheProvider } from "tss-react/next/appDir";
import en from "locales/en.json";
import fr from "locales/fr.json";
import "@fontsource/poppins/400.css";
import "@fontsource/poppins/700.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/700.css";
import "@fontsource/playfair-display/900.css";
import "styles/global.css";
import { LicenseInfo } from "@mui/x-data-grid-pro";
LicenseInfo.setLicenseKey(process.env.MUI_LICENSE_KEY ?? "");
const messages: any = { en, fr };
export const MultiProvider: FC<PropsWithChildren> = ({ children }: any) => {
// const { locale = "en" } = useRouter();
const locale = "en";
return (
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
<SessionProvider>
<CookiesProvider>
<CssBaseline />
<AppProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<I18n locale={locale} messages={messages[locale]}>
{children}
</I18n>
</LocalizationProvider>
</AppProvider>
</CookiesProvider>
</SessionProvider>
</NextAppDirEmotionCacheProvider>
);
};

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Iframe from "react-iframe"; import Iframe from "react-iframe";
import { Box } from "@mui/material"; import { Box } from "@mui/material";

View file

@ -1,3 +1,5 @@
"use client";
/* eslint-disable react/require-default-props */ /* eslint-disable react/require-default-props */
import { FC, PropsWithChildren } from "react"; import { FC, PropsWithChildren } from "react";
import { Box } from "@mui/material"; import { Box } from "@mui/material";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import { import {
Box, Box,
@ -15,14 +17,14 @@ import {
Group as GroupIcon, Group as GroupIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import taxonomy from "config/taxonomy.json"; import taxonomy from "app/_config/taxonomy.json";
import { QueryBuilderSection } from "./QueryBuilderSection"; import { QueryBuilderSection } from "./QueryBuilderSection";
import { QueryListSelector } from "./QueryListSelector"; import { QueryListSelector } from "./QueryListSelector";
import { QueryDateRangeSelector } from "./QueryDateRangeSelector"; import { QueryDateRangeSelector } from "./QueryDateRangeSelector";
import { useAppContext } from "./AppProvider"; import { useAppContext } from "./AppProvider";
import { Tooltip } from "./Tooltip"; import { Tooltip } from "./Tooltip";
interface QueryBuilderProps { } interface QueryBuilderProps {}
export const QueryBuilder: FC<QueryBuilderProps> = () => { export const QueryBuilder: FC<QueryBuilderProps> = () => {
const t = useTranslate(); const t = useTranslate();

View file

@ -1,3 +1,5 @@
"use client";
import { FC, PropsWithChildren, useState } from "react"; import { FC, PropsWithChildren, useState } from "react";
import { import {
Box, Box,

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState, useEffect } from "react"; import { FC, useState, useEffect } from "react";
import { Box, Grid, TextField, Select, MenuItem } from "@mui/material"; import { Box, Grid, TextField, Select, MenuItem } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers-pro"; import { DatePicker } from "@mui/x-date-pickers-pro";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState, useEffect } from "react"; import { FC, useState, useEffect } from "react";
import { Box, Grid, Tooltip } from "@mui/material"; import { Box, Grid, Tooltip } from "@mui/material";
import { DataGridPro, GridColDef } from "@mui/x-data-grid-pro"; import { DataGridPro, GridColDef } from "@mui/x-data-grid-pro";

View file

@ -1,7 +1,9 @@
"use client";
import { FC, useState, useEffect } from "react"; import { FC, useState, useEffect } from "react";
import { Box, Grid } from "@mui/material"; import { Box, Grid } from "@mui/material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import taxonomy from "config/taxonomy.json"; import taxonomy from "app/_config/taxonomy.json";
import { colors } from "styles/theme"; import { colors } from "styles/theme";
import { useAppContext } from "./AppProvider"; import { useAppContext } from "./AppProvider";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { import {

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import { Box, Grid } from "@mui/material"; import { Box, Grid } from "@mui/material";
import { DataGridPro } from "@mui/x-data-grid-pro"; import { DataGridPro } from "@mui/x-data-grid-pro";

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import DashboardMenuIcon from "images/dashboard-menu.png"; import DashboardMenuIcon from "images/dashboard-menu.png";
import AboutMenuIcon from "images/about-menu.png"; import AboutMenuIcon from "images/about-menu.png";
@ -18,8 +20,8 @@ import {
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { useAppContext } from "components/AppProvider"; import { useAppContext } from "@/app/_components/AppProvider";
import { Tooltip } from "components/Tooltip"; import { Tooltip } from "@/app/_components/Tooltip";
// import { ArrowCircleRight as ArrowCircleRightIcon } from "@mui/icons-material"; // import { ArrowCircleRight as ArrowCircleRightIcon } from "@mui/icons-material";
const MenuItem = ({ const MenuItem = ({

View file

@ -1,3 +1,5 @@
"use client";
/* eslint-disable react/require-default-props */ /* eslint-disable react/require-default-props */
import { FC } from "react"; import { FC } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";

View file

@ -1,12 +1,14 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import { AppBar, Grid, Box } from "@mui/material"; import { AppBar, Grid, Box } from "@mui/material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import LeafcutterLogo from "images/leafcutter-logo.png"; import LeafcutterLogo from "images/leafcutter-logo.png";
import { AccountButton } from "components/AccountButton"; import { AccountButton } from "@/app/_components/AccountButton";
import { HelpButton } from "components/HelpButton"; import { HelpButton } from "@/app/_components/HelpButton";
import { Tooltip } from "components/Tooltip"; import { Tooltip } from "@/app/_components/Tooltip";
import { useAppContext } from "./AppProvider"; import { useAppContext } from "./AppProvider";
// import { LanguageSelect } from "./LanguageSelect"; // import { LanguageSelect } from "./LanguageSelect";

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState, useEffect } from "react"; import { FC, useState, useEffect } from "react";
import { import {
Box, Box,
@ -23,11 +25,11 @@ import {
RemoveCircle as RemoveCircleIcon, RemoveCircle as RemoveCircleIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { QueryBuilder } from "components/QueryBuilder"; import { QueryBuilder } from "@/app/_components/QueryBuilder";
import { QueryText } from "components/QueryText"; import { QueryText } from "@/app/_components/QueryText";
import { LiveDataViewer } from "components/LiveDataViewer"; import { LiveDataViewer } from "@/app/_components/LiveDataViewer";
import { Tooltip } from "components/Tooltip"; import { Tooltip } from "@/app/_components/Tooltip";
import visualizationMap from "config/visualizationMap.json"; import visualizationMap from "app/_config/visualizationMap.json";
import { VisualizationSelectCard } from "./VisualizationSelectCard"; import { VisualizationSelectCard } from "./VisualizationSelectCard";
import { MetricSelectCard } from "./MetricSelectCard"; import { MetricSelectCard } from "./MetricSelectCard";
import { useAppContext } from "./AppProvider"; import { useAppContext } from "./AppProvider";

View file

@ -1,8 +1,10 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import { Grid, Card, Box } from "@mui/material"; import { Grid, Card, Box } from "@mui/material";
import Iframe from "react-iframe"; import Iframe from "react-iframe";
import { useAppContext } from "components/AppProvider"; import { useAppContext } from "@/app/_components/AppProvider";
import { VisualizationDetailDialog } from "components/VisualizationDetailDialog"; import { VisualizationDetailDialog } from "@/app/_components/VisualizationDetailDialog";
interface VisualizationCardProps { interface VisualizationCardProps {
id: string; id: string;

View file

@ -1,8 +1,10 @@
"use client";
import { FC } from "react"; import { FC } from "react";
// import Link from "next/link"; // import Link from "next/link";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import Iframe from "react-iframe"; import Iframe from "react-iframe";
import { useAppContext } from "components/AppProvider"; import { useAppContext } from "@/app/_components/AppProvider";
interface VisualizationDetailProps { interface VisualizationDetailProps {
id: string; id: string;

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
// import Link from "next/link"; // import Link from "next/link";
import { import {
@ -9,7 +11,7 @@ import {
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { useAppContext } from "components/AppProvider"; import { useAppContext } from "@/app/_components/AppProvider";
import { VisualizationDetail } from "./VisualizationDetail"; import { VisualizationDetail } from "./VisualizationDetail";
interface VisualizationDetailDialogProps { interface VisualizationDetailDialogProps {

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import { Card, Grid } from "@mui/material"; import { Card, Grid } from "@mui/material";

View file

@ -1,3 +1,5 @@
"use client";
import { Box, Grid } from "@mui/material"; import { Box, Grid } from "@mui/material";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";

View file

@ -1,3 +1,5 @@
"use client";
import { Box, Grid, Dialog, Button } from "@mui/material"; import { Box, Grid, Dialog, Button } from "@mui/material";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// import { useSession } from "next-auth/react"; // import { useSession } from "next-auth/react";

View file

@ -4,18 +4,17 @@ import Head from "next/head";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import Link from "next/link"; import Link from "next/link";
import { Grid, Container, Box, Button } from "@mui/material"; import { Grid, Container, Box, Button } from "@mui/material";
import { Layout } from "components/Layout"; import { useAppContext } from "@/app/_components/AppProvider";
import { useAppContext } from "components/AppProvider"; import { PageHeader } from "@/app/_components/PageHeader";
import { PageHeader } from "components/PageHeader"; import { AboutFeature } from "@/app/_components/AboutFeature";
import { AboutFeature } from "components/AboutFeature"; import { AboutBox } from "@/app/_components/AboutBox";
import { AboutBox } from "components/AboutBox";
import AbstractDiagram from "images/abstract-diagram.png"; import AbstractDiagram from "images/abstract-diagram.png";
import AboutHeader from "images/about-header.png"; import AboutHeader from "images/about-header.png";
import Globe from "images/globe.png"; import Globe from "images/globe.png";
import Controls from "images/controls.png"; import Controls from "images/controls.png";
import CommunityBackground from "images/community-background.png"; import CommunityBackground from "images/community-background.png";
import Bicycle from "images/bicycle.png"; import Bicycle from "images/bicycle.png";
import { getEmbedded } from "lib/utils"; import { getEmbedded } from "@/app/_lib/utils";
type AboutProps = { type AboutProps = {
embedded: boolean; embedded: boolean;
@ -29,10 +28,7 @@ const About: NextPage<AboutProps> = ({ embedded }) => {
} = useAppContext(); } = useAppContext();
return ( return (
<Layout embedded={embedded}> <>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader <PageHeader
backgroundColor={leafcutterElectricBlue} backgroundColor={leafcutterElectricBlue}
sx={{ sx={{
@ -166,7 +162,7 @@ const About: NextPage<AboutProps> = ({ embedded }) => {
</Link> </Link>
</Container> </Container>
</Box> </Box>
</Layout> </>
); );
}; };

View file

@ -2,7 +2,7 @@ import NextAuth from "next-auth";
import Google from "next-auth/providers/google"; import Google from "next-auth/providers/google";
import Apple from "next-auth/providers/apple"; import Apple from "next-auth/providers/apple";
export default NextAuth({ const handler = NextAuth({
providers: [ providers: [
Google({ Google({
clientId: process.env.GOOGLE_CLIENT_ID ?? "", clientId: process.env.GOOGLE_CLIENT_ID ?? "",
@ -15,3 +15,5 @@ export default NextAuth({
], ],
secret: process.env.NEXTAUTH_SECRET, secret: process.env.NEXTAUTH_SECRET,
}); });
export { handler as GET, handler as POST };

View file

@ -1,8 +1,8 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { getUserMetadata, saveUserMetadata } from "lib/opensearch"; import { getUserMetadata, saveUserMetadata } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { export const POST = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({
req, req,
secret: process.env.NEXTAUTH_SECRET, secret: process.env.NEXTAUTH_SECRET,
@ -27,7 +27,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.json(updatedSavedSearches); return res.json(updatedSavedSearches);
}; };
export default handler;

View file

@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { getUserMetadata, saveUserMetadata } from "lib/opensearch"; import { getUserMetadata, saveUserMetadata } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({

View file

@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { getUserMetadata } from "lib/opensearch"; import { getUserMetadata } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({

View file

@ -0,0 +1,20 @@
import { NextResponse } from "next/server";
// import { getToken } from "next-auth/jwt";
import { getTrends } from "@/app/_lib/opensearch";
export const GET = async () => {
/*
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
*/
const results = await getTrends(5);
NextResponse.json(results);
};

View file

@ -2,8 +2,8 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { Client } from "@opensearch-project/opensearch"; import { Client } from "@opensearch-project/opensearch";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import taxonomy from "config/taxonomy.json"; import taxonomy from "app/_config/taxonomy.json";
import unRegions from "config/unRegions.json"; import unRegions from "app/_config/unRegions.json";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { headers: { authorization }, body: { tickets } } = req; const { headers: { authorization }, body: { tickets } } = req;

View file

@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { createUserVisualization } from "lib/opensearch"; import { createUserVisualization } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({

View file

@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { deleteUserVisualization } from "lib/opensearch"; import { deleteUserVisualization } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({

View file

@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { performQuery } from "lib/opensearch"; import { performQuery } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({
@ -16,7 +16,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const rawQuery = await JSON.parse(decodeURI(searchQuery as string)); const rawQuery = await JSON.parse(decodeURI(searchQuery as string));
const results = await performQuery(rawQuery, 1000); const results = await performQuery(rawQuery, 1000);
return res.json(results) return res.json(results);
}; };
export default handler; export default handler;

View file

@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt"; import { getToken } from "next-auth/jwt";
import { updateUserVisualization } from "lib/opensearch"; import { updateUserVisualization } from "@/app/_lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => { const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ const session = await getToken({

View file

@ -1,16 +1,14 @@
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import { GetServerSideProps, GetServerSidePropsContext } from "next"; import { GetServerSideProps, GetServerSidePropsContext } from "next";
import Head from "next/head";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Box, Grid } from "@mui/material"; import { Box, Grid } from "@mui/material";
import { useCookies } from "react-cookie"; import { useCookies } from "react-cookie";
import { getTemplates } from "lib/opensearch"; import { getTemplates } from "@/app/_lib/opensearch";
import { Layout } from "components/Layout"; import { useAppContext } from "@/app/_components/AppProvider";
import { useAppContext } from "components/AppProvider"; import { PageHeader } from "@/app/_components/PageHeader";
import { PageHeader } from "components/PageHeader"; import { VisualizationBuilder } from "@/app/_components/VisualizationBuilder";
import { VisualizationBuilder } from "components/VisualizationBuilder"; import { getEmbedded } from "@/app/_lib/utils";
import { getEmbedded } from "lib/utils";
type CreateProps = { type CreateProps = {
templates: any; templates: any;
@ -36,11 +34,7 @@ const Create: FC<CreateProps> = ({ templates, embedded }) => {
}, [searchIntroComplete, router, setCookie]); }, [searchIntroComplete, router, setCookie]);
return ( return (
<Layout embedded={embedded}> <>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader backgroundColor={cdrLinkOrange}> <PageHeader backgroundColor={cdrLinkOrange}>
<Grid container direction="row" spacing={2} alignItems="center"> <Grid container direction="row" spacing={2} alignItems="center">
{/* <Grid item xs={2} sx={{ textAlign: "center" }}> {/* <Grid item xs={2} sx={{ textAlign: "center" }}>
@ -67,7 +61,7 @@ const Create: FC<CreateProps> = ({ templates, embedded }) => {
</PageHeader> </PageHeader>
<VisualizationBuilder templates={templates} /> <VisualizationBuilder templates={templates} />
</Layout> </>
); );
}; };

View file

@ -1,13 +1,11 @@
import Head from "next/head";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { NextPage, GetServerSideProps, GetServerSidePropsContext } from "next"; import { NextPage, GetServerSideProps, GetServerSidePropsContext } from "next";
import { Box, Grid } from "@mui/material"; import { Box, Grid } from "@mui/material";
import { Layout } from "components/Layout"; import { PageHeader } from "@/app/_components/PageHeader";
import { PageHeader } from "components/PageHeader"; import { Question } from "@/app/_components/Question";
import { Question } from "components/Question"; import { useAppContext } from "@/app/_components/AppProvider";
import { useAppContext } from "components/AppProvider";
import FaqHeader from "images/faq-header.svg"; import FaqHeader from "images/faq-header.svg";
import { getEmbedded } from "lib/utils"; import { getEmbedded } from "@/app/_lib/utils";
type FAQProps = { type FAQProps = {
embedded: boolean; embedded: boolean;
@ -70,10 +68,7 @@ const FAQ: NextPage<FAQProps> = ({ embedded }) => {
]; ];
return ( return (
<Layout embedded={embedded}> <>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader <PageHeader
backgroundColor={lavender} backgroundColor={lavender}
sx={{ sx={{
@ -105,7 +100,7 @@ const FAQ: NextPage<FAQProps> = ({ embedded }) => {
{questions.map((q: any, index: number) => ( {questions.map((q: any, index: number) => (
<Question key={index} question={q.question} answer={q.answer} /> <Question key={index} question={q.question} answer={q.answer} />
))} ))}
</Layout> </>
); );
}; };

View file

@ -0,0 +1,36 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import "./_styles/global.css";
import "@fontsource/poppins/400.css";
import "@fontsource/poppins/700.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/700.css";
import "@fontsource/playfair-display/900.css";
import "styles/global.css";
// import getConfig from "next/config";
// import { LicenseInfo } from "@mui/x-data-grid-pro";
import { MultiProvider } from "app/_components/MultiProvider";
import { InternalLayout } from "app/_components/InternalLayout";
export const metadata: Metadata = {
title: "Leafcutter",
};
type LayoutProps = {
children: ReactNode;
};
export default function Layout({ children }: LayoutProps) {
// const { publicRuntimeConfig } = getConfig();
// LicenseInfo.setLicenseKey(publicRuntimeConfig.muiLicenseKey);
return (
<html lang="en">
<body>
<MultiProvider>
<InternalLayout>{children}</InternalLayout>
</MultiProvider>
</body>
</html>
);
}

View file

@ -5,10 +5,10 @@ import Image from "next/legacy/image";
import { Box, Grid, Container, IconButton } from "@mui/material"; import { Box, Grid, Container, IconButton } from "@mui/material";
import { Apple as AppleIcon, Google as GoogleIcon } from "@mui/icons-material"; import { Apple as AppleIcon, Google as GoogleIcon } from "@mui/icons-material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { LanguageSelect } from "components/LanguageSelect"; import { LanguageSelect } from "@/app/_components/LanguageSelect";
import LeafcutterLogoLarge from "images/leafcutter-logo-large.png"; import LeafcutterLogoLarge from "images/leafcutter-logo-large.png";
import { signIn, getSession } from "next-auth/react"; import { signIn, getSession } from "next-auth/react";
import { useAppContext } from "components/AppProvider"; import { useAppContext } from "@/app/_components/AppProvider";
type LoginProps = { type LoginProps = {
session: any; session: any;

View file

@ -0,0 +1,16 @@
import { getSession } from "next-auth/react";
import { getUserVisualizations } from "@/app/_lib/opensearch";
import { getEmbedded } from "@/app/_lib/utils";
import { Home } from "@/app/_components/Home";
export default async function Page() {
const context = undefined;
const session = (await getSession(context)) ?? null;
const visualizations = await getUserVisualizations(
session?.user?.email ?? "none",
20
);
const embedded = false; // getEmbedded(context);
return <Home visualizations={visualizations} embedded={embedded} />;
}

View file

@ -1,8 +1,8 @@
import { FC } from "react"; import { FC } from "react";
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
// import { Client } from "@opensearch-project/opensearch"; // import { Client } from "@opensearch-project/opensearch";
import { RawDataViewer } from "components/RawDataViewer"; import { RawDataViewer } from "@/app/_components/RawDataViewer";
import { VisualizationDetail } from "components/VisualizationDetail"; import { VisualizationDetail } from "@/app/_components/VisualizationDetail";
// import { createVisualization } from "lib/opensearch"; // import { createVisualization } from "lib/opensearch";
interface PreviewProps { interface PreviewProps {

View file

@ -3,7 +3,7 @@ import { NextPage } from "next";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Grid, CircularProgress } from "@mui/material"; import { Grid, CircularProgress } from "@mui/material";
import Iframe from "react-iframe"; import Iframe from "react-iframe";
import { useAppContext } from "components/AppProvider"; import { useAppContext } from "@/app/_components/AppProvider";
const Setup: NextPage = () => { const Setup: NextPage = () => {
const { const {

View file

@ -2,12 +2,11 @@ import { NextPage, GetServerSideProps, GetServerSidePropsContext } from "next";
import Head from "next/head"; import Head from "next/head";
import { Grid, Box } from "@mui/material"; import { Grid, Box } from "@mui/material";
import { useTranslate } from "react-polyglot"; import { useTranslate } from "react-polyglot";
import { Layout } from "components/Layout"; import { getTrends } from "@/app/_lib/opensearch";
import { getTrends } from "lib/opensearch"; import { PageHeader } from "@/app/_components/PageHeader";
import { PageHeader } from "components/PageHeader"; import { VisualizationCard } from "@/app/_components/VisualizationCard";
import { VisualizationCard } from "components/VisualizationCard"; import { useAppContext } from "@/app/_components/AppProvider";
import { useAppContext } from "components/AppProvider"; import { getEmbedded } from "@/app/_lib/utils";
import { getEmbedded } from "lib/utils";
type TrendsProps = { type TrendsProps = {
visualizations: any; visualizations: any;
@ -22,10 +21,7 @@ const Trends: NextPage<TrendsProps> = ({ visualizations, embedded }) => {
} = useAppContext(); } = useAppContext();
return ( return (
<Layout embedded={embedded}> <>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader backgroundColor={cdrLinkOrange}> <PageHeader backgroundColor={cdrLinkOrange}>
<Grid <Grid
container container
@ -73,7 +69,7 @@ const Trends: NextPage<TrendsProps> = ({ visualizations, embedded }) => {
/> />
))} ))}
</Grid> </Grid>
</Layout> </>
); );
}; };

View file

@ -1,10 +1,8 @@
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
import { NextPage, GetServerSideProps, GetServerSidePropsContext } from "next"; import { NextPage, GetServerSideProps, GetServerSidePropsContext } from "next";
import { Client } from "@opensearch-project/opensearch"; import { Client } from "@opensearch-project/opensearch";
import Head from "next/head"; import { VisualizationDetail } from "@/app/_components/VisualizationDetail";
import { Layout } from "components/Layout"; import { getEmbedded } from "@/app/_lib/utils";
import { VisualizationDetail } from "components/VisualizationDetail";
import { getEmbedded } from "lib/utils";
type VisualizationProps = { type VisualizationProps = {
visualization: any; visualization: any;
@ -14,14 +12,7 @@ type VisualizationProps = {
const Visualization: NextPage<VisualizationProps> = ({ const Visualization: NextPage<VisualizationProps> = ({
visualization, visualization,
embedded, embedded,
}) => ( }) => <VisualizationDetail {...visualization} />;
<Layout embedded={embedded}>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<VisualizationDetail {...visualization} />
</Layout>
);
export default Visualization; export default Visualization;

View file

@ -23,13 +23,13 @@
"@mui/icons-material": "^5", "@mui/icons-material": "^5",
"@mui/lab": "^5.0.0-alpha.134", "@mui/lab": "^5.0.0-alpha.134",
"@mui/material": "^5", "@mui/material": "^5",
"@mui/x-data-grid-pro": "^6.8.0", "@mui/x-data-grid-pro": "^6.9.0",
"@mui/x-date-pickers-pro": "^6.8.0", "@mui/x-date-pickers-pro": "^6.9.0",
"@opensearch-project/opensearch": "^2.0.0", "@opensearch-project/opensearch": "^2.0.0",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"http-proxy-middleware": "^2.0.6", "http-proxy-middleware": "^2.0.6",
"material-ui-popup-state": "^5.0.9", "material-ui-popup-state": "^5.0.9",
"next": "13.4.6", "next": "13.4.7",
"next-auth": "^4.22.1", "next-auth": "^4.22.1",
"next-http-proxy-middleware": "^1.2.5", "next-http-proxy-middleware": "^1.2.5",
"nodemailer": "^6.9.3", "nodemailer": "^6.9.3",
@ -41,18 +41,19 @@
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-polyglot": "^0.7.2", "react-polyglot": "^0.7.2",
"sharp": "^0.32.1", "sharp": "^0.32.1",
"swr": "^2.1.5", "swr": "^2.2.0",
"tss-react": "^4.8.6",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.22.5", "@babel/core": "^7.22.5",
"@types/react": "18.2.13",
"@types/node": "^20.3.1", "@types/node": "^20.3.1",
"@types/react": "18.2.14",
"@types/uuid": "^9.0.2", "@types/uuid": "^9.0.2",
"babel-loader": "^9.1.2", "babel-loader": "^9.1.2",
"eslint": "^8.43.0", "eslint": "^8.43.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-config-next": "^13.4.6", "eslint-config-next": "^13.4.7",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.7.1",

View file

@ -1,63 +0,0 @@
/* eslint-disable react/jsx-props-no-spreading */
import { AppProps } from "next/app";
import { SessionProvider } from "next-auth/react";
import { useRouter } from "next/router";
import Head from "next/head";
import { CssBaseline } from "@mui/material";
import { CacheProvider, EmotionCache } from "@emotion/react";
import { CookiesProvider } from "react-cookie";
import { I18n } from "react-polyglot";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFns";
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
import { AppProvider } from "components/AppProvider";
import createEmotionCache from "lib/createEmotionCache";
import Favicon from "images/favicon.ico";
import en from "locales/en.json";
import fr from "locales/fr.json";
import "@fontsource/poppins/400.css";
import "@fontsource/poppins/700.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/700.css";
import "@fontsource/playfair-display/900.css";
import "styles/global.css";
import { LicenseInfo } from "@mui/x-data-grid-pro";
LicenseInfo.setLicenseKey(process.env.MUI_LICENSE_KEY ?? "");
const clientSideEmotionCache: any = createEmotionCache();
const messages: any = { en, fr };
interface LeafcutterWebProps extends AppProps {
// eslint-disable-next-line react/require-default-props
emotionCache?: EmotionCache;
}
const LeafcutterWeb = (props: LeafcutterWebProps) => {
const { locale = "en" } = useRouter();
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<>
<Head>
<link rel="icon" type="image/png" href={Favicon.src} />
</Head>
<SessionProvider session={(pageProps as any).session}>
<CacheProvider value={emotionCache}>
<CookiesProvider>
<CssBaseline />
<AppProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<I18n locale={locale} messages={messages[locale]}>
<Component {...pageProps} />
</I18n>
</LocalizationProvider>
</AppProvider>
</CookiesProvider>
</CacheProvider>
</SessionProvider>
</>
);
};
export default LeafcutterWeb;

View file

@ -1,50 +0,0 @@
// eslint-disable-next-line no-use-before-define
import * as React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import createEmotionServer from "@emotion/server/create-instance";
import createEmotionCache from "lib/createEmotionCache";
export default class LeafcutterDocument extends Document {
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
LeafcutterDocument.getInitialProps = async (ctx): Promise<any> => {
const originalRenderPage = ctx.renderPage;
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache as any);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) => (props: any) =>
<App emotionCache={cache} {...props} />,
});
const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(" ")}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
...emotionStyleTags,
],
};
};

View file

@ -1,19 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { getTrends } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
const results = await getTrends(5);
return res.json(results)
};
export default handler;

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Link from "next/link"; import Link from "next/link";
import { Button as MUIButton } from "@mui/material"; import { Button as MUIButton } from "@mui/material";

View file

@ -0,0 +1,22 @@
"use client";
import { FC } from "react";
import { Box, Grid } from "@mui/material";
type DisplayErrorProps = {
error: Error;
};
export const DisplayError: FC<DisplayErrorProps> = ({ error }) => (
<Grid
container
direction="column"
justifyContent="space-around"
alignItems="center"
style={{ height: 600, width: "100%", color: "red", fontSize: 20 }}
>
<Grid item>
<Box>{`Error: ${error.message}`}</Box>
</Grid>
</Grid>
);

View file

@ -0,0 +1,6 @@
"use client";
import { FC } from "react";
import { ZammadWrapper } from "@/app/_components/ZammadWrapper";
export const Home: FC = () => <ZammadWrapper path="/#dashboard" hideSidebar />;

View file

@ -1,8 +1,10 @@
import { FC, useState } from "react"; "use client";
import { FC, PropsWithChildren, useState } from "react";
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { Sidebar } from "./Sidebar"; import { Sidebar } from "./Sidebar";
export const Layout = ({ children }) => { export const InternalLayout: FC<PropsWithChildren> = ({ children }) => {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
return ( return (
@ -12,7 +14,7 @@ export const Layout = ({ children }) => {
item item
sx={{ ml: open ? "270px" : "100px", width: "100%", height: "100vh" }} sx={{ ml: open ? "270px" : "100px", width: "100%", height: "100vh" }}
> >
{children} {children as any}
</Grid> </Grid>
</Grid> </Grid>
); );

View file

@ -0,0 +1,57 @@
"use client";
import { FC, PropsWithChildren, useState } from "react";
import { CssBaseline } from "@mui/material";
import { CookiesProvider } from "react-cookie";
import { SessionProvider } from "next-auth/react";
import { NextAppDirEmotionCacheProvider } from "tss-react/next/appDir";
import { SWRConfig } from "swr";
import { GraphQLClient } from "graphql-request";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFns";
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
const [csrfToken, setCsrfToken] = useState("");
const origin =
typeof window !== "undefined" && window.location.origin
? window.location.origin
: null;
const client = new GraphQLClient(`${origin}/proxy/zammad/graphql`, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
const graphQLFetcher = async ({ document, variables }: any) => {
const requestHeaders = {
"X-CSRF-Token": csrfToken,
};
const { data, headers } = await client.rawRequest(
document,
variables,
requestHeaders
);
const token = headers.get("CSRF-Token");
setCsrfToken(token);
return data;
};
return (
<>
<CssBaseline />
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
<SWRConfig value={{ fetcher: graphQLFetcher }}>
<SessionProvider>
<CookiesProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
{children}
</LocalizationProvider>
</CookiesProvider>
</SessionProvider>
</SWRConfig>
</NextAppDirEmotionCacheProvider>
</>
);
};

View file

@ -1,3 +1,5 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import { import {
@ -22,12 +24,12 @@ import {
ExpandCircleDown as ExpandCircleDownIcon, ExpandCircleDown as ExpandCircleDownIcon,
Dvr as DvrIcon, Dvr as DvrIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { useRouter } from "next/router"; import { usePathname } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import LinkLogo from "public/link-logo-small.png"; import LinkLogo from "public/link-logo-small.png";
import { useSession, signOut } from "next-auth/react"; import { useSession, signOut } from "next-auth/react";
import { getTicketOverviewCountsQuery } from "graphql/getTicketOverviewCountsQuery"; import { getTicketOverviewCountsQuery } from "@/app/_graphql/getTicketOverviewCountsQuery";
const openWidth = 270; const openWidth = 270;
const closedWidth = 100; const closedWidth = 100;
@ -157,8 +159,7 @@ interface SidebarProps {
} }
export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => { export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
const router = useRouter(); const pathname = usePathname();
const { pathname } = router;
const { data: session } = useSession(); const { data: session } = useSession();
const username = session?.user?.name || "User"; const username = session?.user?.name || "User";
const { data: overviewData, error: overviewError }: any = useSWR( const { data: overviewData, error: overviewError }: any = useSWR(
@ -358,14 +359,20 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
/> />
<MenuItem <MenuItem
name="Tickets" name="Tickets"
href="/tickets/assigned" href="/overview/assigned"
Icon={FeaturedPlayListIcon} Icon={FeaturedPlayListIcon}
selected={pathname.startsWith("/tickets")} selected={
pathname.startsWith("/overview") ||
pathname.startsWith("/tickets")
}
iconSize={20} iconSize={20}
open={open} open={open}
/> />
<Collapse <Collapse
in={pathname.startsWith("/tickets")} in={
pathname.startsWith("/overview") ||
pathname.startsWith("/tickets")
}
timeout="auto" timeout="auto"
unmountOnExit unmountOnExit
onClick={undefined} onClick={undefined}
@ -373,37 +380,37 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
<List component="div" disablePadding> <List component="div" disablePadding>
<MenuItem <MenuItem
name="Assigned" name="Assigned"
href="/tickets/assigned" href="/overview/assigned"
Icon={FeaturedPlayListIcon} Icon={FeaturedPlayListIcon}
iconSize={0} iconSize={0}
selected={pathname.endsWith("/tickets/assigned")} selected={pathname.endsWith("/overview/assigned")}
badge={assignedCount} badge={assignedCount}
open={open} open={open}
/> />
<MenuItem <MenuItem
name="Urgent" name="Urgent"
href="/tickets/urgent" href="/overview/urgent"
Icon={FeaturedPlayListIcon} Icon={FeaturedPlayListIcon}
iconSize={0} iconSize={0}
selected={pathname.endsWith("/tickets/urgent")} selected={pathname.endsWith("/overview/urgent")}
badge={urgentCount} badge={urgentCount}
open={open} open={open}
/> />
<MenuItem <MenuItem
name="Pending" name="Pending"
href="/tickets/pending" href="/overview/pending"
Icon={FeaturedPlayListIcon} Icon={FeaturedPlayListIcon}
iconSize={0} iconSize={0}
selected={pathname.endsWith("/tickets/pending")} selected={pathname.endsWith("/overview/pending")}
badge={pendingCount} badge={pendingCount}
open={open} open={open}
/> />
<MenuItem <MenuItem
name="Unassigned" name="Unassigned"
href="/tickets/unassigned" href="/overview/unassigned"
Icon={FeaturedPlayListIcon} Icon={FeaturedPlayListIcon}
iconSize={0} iconSize={0}
selected={pathname.endsWith("/tickets/unassigned")} selected={pathname.endsWith("/overview/unassigned")}
badge={unassignedCount} badge={unassignedCount}
open={open} open={open}
/> />

View file

@ -1,3 +1,5 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { import {

View file

@ -1,22 +1,25 @@
"use client";
import { FC, useState } from "react"; import { FC, useState } from "react";
import getConfig from "next/config"; import getConfig from "next/config";
import { useRouter } from "next/router"; import { useRouter } from "next/navigation";
import Iframe from "react-iframe"; import Iframe from "react-iframe";
type InternalZammadWrapperProps = { type ZammadWrapperProps = {
path: string; path: string;
hideSidebar?: boolean; hideSidebar?: boolean;
}; };
export const InternalZammadWrapper: FC<InternalZammadWrapperProps> = ({ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
path, path,
hideSidebar = true, hideSidebar = true,
}) => { }) => {
const router = useRouter(); const router = useRouter();
const [display, setDisplay] = useState("none"); const [display, setDisplay] = useState("inherit");
const { //const {
publicRuntimeConfig: { linkURL }, // publicRuntimeConfig: { linkURL },
} = getConfig(); // } = getConfig();
const linkURL = "http://localhost:3000";
const url = `${linkURL}/proxy/zammad${path}`; const url = `${linkURL}/proxy/zammad${path}`;
console.log({ url }); console.log({ url });

View file

@ -0,0 +1,22 @@
import { FC } from "react";
import { Grid } from "@mui/material";
import Iframe from "react-iframe";
export const LabelStudioWrapper: FC = () => (
<Grid
container
spacing={0}
sx={{ height: "100%", width: "100%" }}
direction="column"
>
<Grid item sx={{ height: "100vh", width: "100%" }}>
<Iframe
id="link"
url={"https://label-studio:3000"}
width="100%"
height="100%"
frameBorder={0}
/>
</Grid>
</Grid>
);

View file

@ -0,0 +1,10 @@
import { Metadata } from "next";
import { LabelStudioWrapper } from "./_components/LabelStudioWrapper";
export const metadata: Metadata = {
title: "Label Studio",
};
export default function Page() {
return <LabelStudioWrapper />;
}

View file

@ -1,14 +1,21 @@
"use client";
import { FC } from "react"; import { FC } from "react";
import Head from "next/head"; import getConfig from "next/config";
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { Layout } from "components/Layout";
import Iframe from "react-iframe"; import Iframe from "react-iframe";
const LabelStudio: FC = () => ( type MetamigoWrapperProps = {
<Layout> path: string;
<Head> };
<title>Link Shell</title>
</Head> export const MetamigoWrapper: FC<MetamigoWrapperProps> = ({ path }) => {
const {
publicRuntimeConfig: { linkURL },
} = getConfig();
const fullMetamigoURL = `${linkURL}/metamigo/${path}`;
return (
<Grid <Grid
container container
spacing={0} spacing={0}
@ -18,14 +25,12 @@ const LabelStudio: FC = () => (
<Grid item sx={{ height: "100vh", width: "100%" }}> <Grid item sx={{ height: "100vh", width: "100%" }}>
<Iframe <Iframe
id="link" id="link"
url={"https://label-studio:3000"} url={fullMetamigoURL}
width="100%" width="100%"
height="100%" height="100%"
frameBorder={0} frameBorder={0}
/> />
</Grid> </Grid>
</Grid> </Grid>
</Layout> );
); };
export default LabelStudio;

View file

@ -0,0 +1,16 @@
import { Metadata } from "next";
import { MetamigoWrapper } from "./_components/MetamigoWrapper";
export const metadata: Metadata = {
title: "Metamigo",
};
type PageProps = {
params: {
path: string;
};
};
export default function Page({ params: { path } }: PageProps) {
return <MetamigoWrapper path={path} />;
}

View file

@ -0,0 +1,10 @@
import { Metadata } from "next";
import { ZammadWrapper } from "@/app/_components/ZammadWrapper";
export const metadata: Metadata = {
title: "Zammad",
};
export default function Page() {
return <ZammadWrapper path="/#manage" hideSidebar={false} />;
}

View file

@ -2,7 +2,7 @@ import NextAuth from "next-auth";
import Google from "next-auth/providers/google"; import Google from "next-auth/providers/google";
// import Apple from "next-auth/providers/apple"; // import Apple from "next-auth/providers/apple";
export default NextAuth({ const handler = NextAuth({
providers: [ providers: [
Google({ Google({
clientId: process.env.GOOGLE_CLIENT_ID, clientId: process.env.GOOGLE_CLIENT_ID,
@ -17,3 +17,6 @@ export default NextAuth({
], ],
secret: process.env.NEXTAUTH_SECRET, secret: process.env.NEXTAUTH_SECRET,
}); });
export { handler as GET, handler as POST };

View file

@ -0,0 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
const handler = (req: NextRequest) => {
NextResponse.redirect('/proxy/zammad/api/v1' + req.url.substring('/api/v1'.length));
};
export { handler as GET, handler as POST };

11
apps/link/app/error.tsx Normal file
View file

@ -0,0 +1,11 @@
"use client";
import { DisplayError } from "./_components/DisplayError";
type PageProps = {
error: Error;
};
export default function Page({ error }: PageProps) {
return <DisplayError error={error} />;
}

View file

@ -0,0 +1,10 @@
import { Metadata } from "next";
import { ZammadWrapper } from "@/app/_components/ZammadWrapper";
export const metadata: Metadata = {
title: "Knowledge Base",
};
export default function Page() {
return <ZammadWrapper path="/#knowledge_base/1/locale/en-us" />;
}

35
apps/link/app/layout.tsx Normal file
View file

@ -0,0 +1,35 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import "./_styles/global.css";
import "@fontsource/poppins/400.css";
import "@fontsource/poppins/700.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/700.css";
import "@fontsource/playfair-display/900.css";
// import getConfig from "next/config";
// import { LicenseInfo } from "@mui/x-data-grid-pro";
import { MultiProvider } from "./_components/MultiProvider";
import { InternalLayout } from "./_components/InternalLayout";
export const metadata: Metadata = {
title: "Link",
};
type LayoutProps = {
children: ReactNode;
};
export default function Layout({ children }: LayoutProps) {
// const { publicRuntimeConfig } = getConfig();
// LicenseInfo.setLicenseKey(publicRuntimeConfig.muiLicenseKey);
return (
<html lang="en">
<body>
<MultiProvider>
<InternalLayout>{children}</InternalLayout>
</MultiProvider>
</body>
</html>
);
}

View file

@ -0,0 +1,36 @@
"use client";
import { FC } from "react";
import getConfig from "next/config";
import { Grid } from "@mui/material";
import Iframe from "react-iframe";
type LeafcutterWrapperProps = {
path: string;
};
export const LeafcutterWrapper: FC<LeafcutterWrapperProps> = ({ path }) => {
const {
publicRuntimeConfig: { linkURL },
} = getConfig();
const fullLeafcutterURL = `${linkURL}/proxy/leafcutter/${path}`;
return (
<Grid
container
spacing={0}
sx={{ height: "100%", width: "100%" }}
direction="column"
>
<Grid item sx={{ height: "100vh", width: "100%" }}>
<Iframe
id="leafcutter"
url={fullLeafcutterURL}
width="100%"
height="100%"
frameBorder={0}
/>
</Grid>
</Grid>
);
};

View file

@ -0,0 +1,11 @@
import { LeafcutterWrapper } from "./_components/LeafcutterWrapper";
type PageProps = {
params: {
view: string;
};
};
export default function Page({ params: { view } }: PageProps) {
<LeafcutterWrapper path={view} />;
}

Some files were not shown because too many files have changed in this diff Show more