App directory #3

This commit is contained in:
Darren Clarke 2023-06-28 10:52:23 +00:00 committed by GitHub
parent 8bbeaa25cf
commit 69706053c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 69 additions and 40 deletions

View file

@ -0,0 +1,164 @@
"use client";
import { FC } from "react";
import { useTranslate } from "react-polyglot";
import Image from "next/legacy/image";
import Link from "next/link";
import { Grid, Container, Box, Button } from "@mui/material";
import { useAppContext } from "@/app/_components/AppProvider";
import { PageHeader } from "@/app/_components/PageHeader";
import { AboutFeature } from "./AboutFeature";
import { AboutBox } from "./AboutBox";
import AbstractDiagram from "images/abstract-diagram.png";
import AboutHeader from "images/about-header.png";
import Globe from "images/globe.png";
import Controls from "images/controls.png";
import CommunityBackground from "images/community-background.png";
import Bicycle from "images/bicycle.png";
export const About: FC = () => {
const t = useTranslate();
const {
colors: { white, leafcutterElectricBlue, cdrLinkOrange },
typography: { h1, h4, p },
} = useAppContext();
return (
<>
<PageHeader
backgroundColor={leafcutterElectricBlue}
sx={{
backgroundImage: `url(${AboutHeader.src})`,
backgroundSize: "200px",
backgroundPosition: "bottom right",
backgroundRepeat: "no-repeat",
}}
>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item xs={9}>
<Box component="h1" sx={h1}>
{t("aboutLeafcutterTitle")}
</Box>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("aboutLeafcutterDescription")}
</Box>
</Grid>
</Grid>
</PageHeader>
<Container maxWidth="lg">
<AboutFeature
title={t("whatIsLeafcutterTitle")}
description={t("whatIsLeafcutterDescription")}
direction="row"
image={AbstractDiagram}
showBackground={false}
textColumns={8}
/>
<AboutFeature
title={t("whatIsItForTitle")}
description={t("whatIsItForDescription")}
direction="row-reverse"
image={Controls}
showBackground
textColumns={8}
/>
<AboutFeature
title={t("whoCanUseItTitle")}
description={t("whoCanUseItDescription")}
direction="row"
image={Globe}
showBackground
textColumns={6}
/>
</Container>
<AboutBox backgroundColor={cdrLinkOrange}>
<Box component="h4" sx={{ ...h4, mt: 0 }}>
{t("whereDataComesFromTitle")}
</Box>
{t("whereDataComesFromDescription")
.split("\n")
.map((line: string, i: number) => (
<Box component="p" key={i} sx={p}>
{line}
</Box>
))}
</AboutBox>
<AboutBox backgroundColor={leafcutterElectricBlue}>
<Box component="h4" sx={{ ...h4, mt: 0 }}>
{t("projectSupportTitle")}
</Box>
{t("projectSupportDescription")
.split("\n")
.map((line: string, i: number) => (
<Box component="p" key={i} sx={p}>
{line}
</Box>
))}
</AboutBox>
<Box
sx={{
backgroundImage: `url(${CommunityBackground.src})`,
backgroundSize: "90%",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
position: "relative",
height: "700px",
}}
>
<Box sx={{ position: "absolute", left: 0, bottom: -20, width: 300 }}>
<Image src={Bicycle} alt="" />
</Box>
<Container
maxWidth="md"
sx={{ textAlign: "center", paddingTop: "280px" }}
>
<Box
component="h4"
sx={{ ...h4, maxWidth: 500, margin: "0 auto", mt: 3 }}
>
{t("interestedInLeafcutterTitle")}
</Box>
{t("interestedInLeafcutterDescription")
.split("\n")
.map((line: string, i: number) => (
<Box
component="p"
key={i}
sx={{ ...p, maxWidth: 500, margin: "0 auto" }}
>
{line}
</Box>
))}
<Link href="mailto:info@digiresilience.org" passHref>
<Button
sx={{
fontSize: 14,
borderRadius: 500,
color: white,
backgroundColor: cdrLinkOrange,
fontWeight: "bold",
textTransform: "uppercase",
pl: 6,
pr: 5,
mt: 4,
":hover": {
backgroundColor: leafcutterElectricBlue,
color: white,
opacity: 0.8,
},
}}
>
{t("contactUs")}
</Button>
</Link>
</Container>
</Box>
</>
);
};

View file

@ -0,0 +1,35 @@
"use client";
import { FC, PropsWithChildren } from "react";
import { Box } from "@mui/material";
import { useAppContext } from "../../../_components/AppProvider";
type AboutBoxProps = PropsWithChildren<{
backgroundColor: string;
}>;
export const AboutBox: FC<AboutBoxProps> = ({
backgroundColor,
children,
}: any) => {
const {
colors: { white },
} = useAppContext();
return (
<Box
sx={{
width: "100%",
backgroundColor,
color: white,
p: 4,
borderRadius: "10px",
mt: "66px",
mb: "22px",
textAlign: "center",
}}
>
{children}
</Box>
);
};

View file

@ -0,0 +1,68 @@
"use client";
import { FC } from "react";
import Image from "next/legacy/image";
import { Grid, Box, GridSize } from "@mui/material";
import AboutDots from "images/about-dots.png";
import { useAppContext } from "app/_components/AppProvider";
interface AboutFeatureProps {
title: string;
description: string;
direction: "row" | "row-reverse";
image: any;
showBackground: boolean;
textColumns: number;
}
export const AboutFeature: FC<AboutFeatureProps> = ({
title,
description,
direction,
image,
showBackground,
textColumns,
}) => {
const {
typography: { h2, p },
} = useAppContext();
return (
<Box
sx={{
p: "20px",
mt: "40px",
backgroundImage: showBackground ? `url(${AboutDots.src})` : "",
backgroundSize: "200px 200px",
backgroundPosition: direction === "row" ? "20% 50%" : "80% 50%",
backgroundRepeat: "no-repeat",
}}
>
<Grid
direction={direction}
container
spacing={5}
alignContent="flex-start"
>
<Grid item xs={textColumns as GridSize}>
<Box component="h2" sx={h2}>
{title}
</Box>
<Box component="p" sx={p}>
{description}
</Box>
</Grid>
<Grid
item
xs={(12 - textColumns) as GridSize}
container
direction={direction}
>
<Box sx={{ width: "150px", mt: "-20px" }}>
<Image src={image} alt="" objectFit="contain" />
</Box>
</Grid>
</Grid>
</Box>
);
};

View file

@ -0,0 +1,5 @@
import { About } from './_components/About';
export default function Page() {
return <About />;
}

View file

@ -0,0 +1,65 @@
"use client";
import { FC, useEffect } from "react";
import { useTranslate } from "react-polyglot";
import { useRouter, usePathname } from "next/navigation";
import { Box, Grid } from "@mui/material";
import { useCookies } from "react-cookie";
import { useAppContext } from "@/app/_components/AppProvider";
import { PageHeader } from "@/app/_components/PageHeader";
import { VisualizationBuilder } from "@/app/_components/VisualizationBuilder";
type CreateProps = {
templates: any;
};
export const Create: FC<CreateProps> = ({ templates }) => {
const t = useTranslate();
const {
colors: { cdrLinkOrange },
typography: { h1, h4 },
} = useAppContext();
const router = useRouter();
const pathname = usePathname();
const cookieName = "searchIntroComplete";
const [cookies, setCookie] = useCookies([cookieName]);
const searchIntroComplete = parseInt(cookies[cookieName], 10) || 0;
useEffect(() => {
if (searchIntroComplete === 0) {
setCookie(cookieName, `${1}`, { path: "/" });
router.push(`${pathname}?group=search&tooltip=1&checklist=1`);
}
}, [searchIntroComplete, router, setCookie]);
return (
<>
<PageHeader backgroundColor={cdrLinkOrange}>
<Grid container direction="row" spacing={2} alignItems="center">
{/* <Grid item xs={2} sx={{ textAlign: "center" }}>
<Image src={SearchCreateHeader} width={100} height={100} alt="" />
</Grid> */}
<Grid container direction="column" item xs={10}>
<Grid item>
<Box component="h1" sx={{ ...h1 }}>
{t("searchAndCreateTitle")}
</Box>
</Grid>
<Grid item>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("searchAndCreateSubtitle")}
</Box>
</Grid>
{/* <Grid>
<Box component="p" sx={{ ...p }}>
{t("searchAndCreateDescription")}
</Box>
</Grid> */}
</Grid>
</Grid>
</PageHeader>
<VisualizationBuilder templates={templates} />
</>
);
};

View file

@ -0,0 +1,8 @@
import { getTemplates } from "@/app/_lib/opensearch";
import { Create } from "./_components/Create";
export default async function Page() {
const templates = await getTemplates(100);
return <Create templates={templates} />;
};

View file

@ -0,0 +1,102 @@
"use client";
import { FC } from "react";
import { useTranslate } from "react-polyglot";
import { Box, Grid } from "@mui/material";
import { PageHeader } from "@/app/_components/PageHeader";
import { Question } from "@/app/_components/Question";
import { useAppContext } from "@/app/_components/AppProvider";
import FaqHeader from "images/faq-header.svg";
export const FAQ: FC = () => {
const t = useTranslate();
const {
colors: { lavender },
typography: { h1, h4, p },
} = useAppContext();
const questions = [
{
question: t("whatIsLeafcutterQuestion"),
answer: t("whatIsLeafcutterAnswer"),
},
{
question: t("whoBuiltLeafcutterQuestion"),
answer: t("whoBuiltLeafcutterAnswer"),
},
{
question: t("whoCanUseLeafcutterQuestion"),
answer: t("whoCanUseLeafcutterAnswer"),
},
{
question: t("whatCanYouDoWithLeafcutterQuestion"),
answer: t("whatCanYouDoWithLeafcutterAnswer"),
},
{
question: t("whereIsTheDataComingFromQuestion"),
answer: t("whereIsTheDataComingFromAnswer"),
},
{
question: t("whereIsTheDataStoredQuestion"),
answer: t("whereIsTheDataStoredAnswer"),
},
{
question: t("howDoWeKeepTheDataSafeQuestion"),
answer: t("howDoWeKeepTheDataSafeAnswer"),
},
{
question: t("howLongDoYouKeepTheDataQuestion"),
answer: t("howLongDoYouKeepTheDataAnswer"),
},
{
question: t("whatOrganizationsAreParticipatingQuestion"),
answer: t("whatOrganizationsAreParticipatingAnswer"),
},
{
question: t("howDidYouGetMyProfileInformationQuestion"),
answer: t("howDidYouGetMyProfileInformationAnswer"),
},
{
question: t("howCanILearnMoreAboutLeafcutterQuestion"),
answer: t("howCanILearnMoreAboutLeafcutterAnswer"),
},
];
return (
<>
<PageHeader
backgroundColor={lavender}
sx={{
backgroundImage: `url(${FaqHeader.src})`,
backgroundSize: "150px",
backgroundPosition: "bottom right",
backgroundRepeat: "no-repeat",
}}
>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Box component="h1" sx={{ ...h1 }}>
{t("frequentlyAskedQuestionsTitle")}
</Box>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("frequentlyAskedQuestionsSubtitle")}
</Box>
<Box component="p" sx={{ ...p }}>
{t("frequentlyAskedQuestionsDescription")}
</Box>
</Grid>
</Grid>
</PageHeader>
{questions.map((q: any, index: number) => (
<Question key={index} question={q.question} answer={q.answer} />
))}
</>
);
};

View file

@ -0,0 +1,5 @@
import { FAQ } from "./_components/FAQ";
export default function Page() {
return <FAQ />;
}

View file

@ -0,0 +1,22 @@
import { ReactNode } from "react";
import "app/_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 { InternalLayout } from "app/_components/InternalLayout";
import { headers } from 'next/headers'
type LayoutProps = {
children: ReactNode;
};
export default function Layout({ children }: LayoutProps) {
const allHeaders = headers();
const embedded = Boolean(allHeaders.get('x-leafcutter-embedded'));
console.log({embedded})
return <InternalLayout embedded={embedded}>{children}</InternalLayout>;
}

View file

@ -0,0 +1,21 @@
import { FC } from "react";
/* eslint-disable no-underscore-dangle */
import { RawDataViewer } from "@/app/_components/RawDataViewer";
import { VisualizationDetail } from "@/app/_components/VisualizationDetail";
interface PreviewProps {
visualization: any;
visualizationType: string;
data: any[];
}
export const Preview: FC<PreviewProps> = ({
visualization,
visualizationType,
data,
}) =>
visualizationType === "rawData" ? (
<RawDataViewer rows={data} height={750} />
) : (
<VisualizationDetail {...visualization} />
);

View file

@ -0,0 +1,89 @@
/* eslint-disable no-underscore-dangle */
// import { Client } from "@opensearch-project/opensearch";
import { Preview } from "./_components/Preview";
// import { createVisualization } from "lib/opensearch";
export default function Page() {
return <Preview visualization={undefined} visualizationType={""} data={[]}/>;
}
/*
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const {
visualizationID,
searchQuery,
visualizationType = "table",
} = context.query;
const node = `https://${process.env.OPENSEARCH_USERNAME}:${process.env.OPENSEARCH_PASSWORD}@${process.env.OPENSEARCH_URL}`;
const client = new Client({
node,
ssl: {
rejectUnauthorized: false,
},
});
res.props.visualizationType = visualizationType as string;
if (visualizationType !== "rawData") {
await createVisualization({
id: visualizationID as string,
query: await JSON.parse(decodeURI(searchQuery as string)),
kind: visualizationType as string,
});
const rawResponse = await client.search({
index: ".kibana_1",
size: 200,
});
const response = rawResponse.body;
const hits = response.hits.hits.filter(
(hit) => hit._id.split(":")[1] === visualizationID[0]
);
const hit = hits[0];
res.props.visualization = {
id: hit._id.split(":")[1],
title: hit._source.visualization.title,
description: hit._source.visualization.description,
url: `/app/visualize?security_tenant=global#/edit/${
hit._id.split(":")[1]
}?embed=true`,
};
}
const rawQuery = await JSON.parse(decodeURI(searchQuery as string));
const query = {
bool: {
should: [],
must_not: [],
},
};
if (rawQuery.impactedTechnology.values.length > 0) {
rawQuery.impactedTechnology.values.forEach((value) => {
query.bool.should.push({
match: { technology: value },
});
});
}
console.log({ query });
const dataResponse = await client.search({
index: "demo_data",
size: 200,
body: { query },
});
console.log({ dataResponse });
res.props.data = dataResponse.body.hits.hits.map((hit) => ({
id: hit._id,
...hit._source,
}));
console.log({ data: res.props.data });
console.log(res.props.data[0]);
return res;
};
*/

View file

@ -0,0 +1,49 @@
"use client";
import { FC } from "react";
import { useLayoutEffect } from "react";
import { useRouter } from "next/navigation";
import { Grid, CircularProgress } from "@mui/material";
import Iframe from "react-iframe";
import { useAppContext } from "@/app/_components/AppProvider";
export const Setup:FC = () => {
const {
colors: { leafcutterElectricBlue },
} = useAppContext();
const router = useRouter();
useLayoutEffect(() => {
setTimeout(() => router.push("/"), 4000);
}, [router]);
return (
<Grid
sx={{ width: "100%", height: 700 }}
direction="row"
justifyContent="space-around"
alignItems="center"
alignContent="center"
>
<Grid
item
xs={12}
sx={{
width: "200px",
height: 700,
textAlign: "center",
margin: "0 auto",
pt: 30,
}}
>
<Iframe url="/app/home" height="1" width="1" frameBorder={0} />
<CircularProgress
size={80}
thickness={5}
sx={{ color: leafcutterElectricBlue }}
/>
</Grid>
</Grid>
);
};
export default Setup;

View file

@ -0,0 +1,6 @@
import { Setup } from './_components/Setup';
export default function Page() {
return <Setup />;
}

View file

@ -0,0 +1,72 @@
"use client";
import { FC, } from "react";
import { Grid, Box } from "@mui/material";
import { useTranslate } from "react-polyglot";
import { PageHeader } from "@/app/_components/PageHeader";
import { VisualizationCard } from "@/app/_components/VisualizationCard";
import { useAppContext } from "@/app/_components/AppProvider";
type TrendsProps = {
visualizations: any;
};
export const Trends: FC<TrendsProps> = ({ visualizations }) => {
const t = useTranslate();
const {
colors: { cdrLinkOrange },
typography: { h1, h4, p },
} = useAppContext();
return (
<>
<PageHeader backgroundColor={cdrLinkOrange}>
<Grid
container
direction="row"
spacing={2}
justifyContent="space-between"
alignItems="center"
>
{/* <Grid item xs={3} sx={{ textAlign: "center" }}>
<Image src={SearchCreateHeader} width={200} height={200} alt="" />
</Grid> */}
<Grid item container direction="column" xs={12}>
<Grid item>
<Box component="h1" sx={{ ...h1 }}>
{t("trendsTitle")}
</Box>
</Grid>
<Grid item>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("trendsSubtitle")}
</Box>
</Grid>
<Grid>
<Box component="p" sx={{ ...p }}>
{t("trendsDescription")}
</Box>
</Grid>
</Grid>
</Grid>
</PageHeader>
<Grid
container
direction="row"
wrap="wrap"
spacing={3}
justifyContent="space-between"
>
{visualizations.map((visualization: any, index: number) => (
<VisualizationCard
key={index}
id={visualization.id}
title={visualization.title}
description={visualization.description}
url={visualization.url}
/>
))}
</Grid>
</>
);
};

View file

@ -0,0 +1,8 @@
import { getTrends } from "@/app/_lib/opensearch";
import { Trends } from "./_components/Trends";
export default async function Page() {
const visualizations = await getTrends(25);
return <Trends visualizations={visualizations} />;
}

View file

@ -0,0 +1,48 @@
/* eslint-disable no-underscore-dangle */
import { Client } from "@opensearch-project/opensearch";
import { VisualizationDetail } from "@/app/_components/VisualizationDetail";
const getVisualization = async (visualizationID: string) => {
const node = `https://${process.env.OPENSEARCH_USERNAME}:${process.env.OPENSEARCH_PASSWORD}@${process.env.OPENSEARCH_URL}`;
const client = new Client({
node,
ssl: {
rejectUnauthorized: false,
},
});
const rawResponse = await client.search({
index: ".kibana_1",
size: 200,
});
const response = rawResponse.body;
const hits = response.hits.hits.filter(
(hit: any) => hit._id.split(":")[1] === visualizationID[0]
);
const hit = hits[0];
const visualization = {
id: hit._id.split(":")[1],
title: hit._source.visualization.title,
description: hit._source.visualization.description,
url: `/app/visualize?security_tenant=global#/edit/${
hit._id.split(":")[1]
}?embed=true`,
};
return visualization;
}
type PageProps = {
params: {
visualizationID: string;
};
}
export default async function Page({ params: { visualizationID } }: PageProps) {
const visualization = await getVisualization(visualizationID);
return <VisualizationDetail {...visualization} editing={false}/>;
}