This commit is contained in:
Darren Clarke 2024-03-20 17:51:21 +01:00
parent b8c6e893ff
commit b09cc82544
167 changed files with 2196 additions and 1302 deletions

View file

@ -1,11 +1,9 @@
"use client";
import { FC } from "react";
import { OpenSearchWrapper } from "leafcutter-common";
import { OpenSearchWrapper } from "leafcutter-ui";
export const Home: FC = () => (
<OpenSearchWrapper
url="/app/dashboards#/view/c39012d0-eb7a-11ed-8e00-17d7d50cd7b2?embed=true&tenant=global"
marginTop="0"
<OpenSearchWrapper url="/app/visualize#/edit/237b8f00-e6a0-11ee-94b3-d7b7409294e7?embed=true" marginTop="0"
/>
);

View file

@ -35,6 +35,7 @@ import LinkLogo from "public/link-logo-small.png";
import { useSession, signOut } from "next-auth/react";
import { getTicketOverviewCountsQuery } from "app/_graphql/getTicketOverviewCountsQuery";
import { SearchBox } from "./SearchBox";
import { fonts } from "app/_styles/theme";
const openWidth = 270;
const closedWidth = 100;
@ -49,8 +50,10 @@ const MenuItem = ({
open = true,
badge,
target = "_self",
}: any) => (
<Link href={href} target={target}>
}: any) => {
const { roboto } = fonts;
return (<Link href={href} target={target}>
<ListItemButton
sx={{
p: 0,
@ -123,7 +126,7 @@ const MenuItem = ({
variant="body1"
sx={{
fontSize: 16,
fontFamily: "Roboto",
fontFamily: roboto.style.fontFamily,
fontWeight: "bold",
border: 0,
textAlign: "left",
@ -157,6 +160,7 @@ const MenuItem = ({
</ListItemButton>
</Link>
);
}
interface SidebarProps {
open: boolean;
@ -166,6 +170,7 @@ interface SidebarProps {
export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
const pathname = usePathname();
const { data: session } = useSession();
const { poppins } = fonts;
const username = session?.user?.name || "User";
// @ts-ignore
const roles = session?.user?.roles || [];
@ -272,7 +277,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
fontWeight: 700,
mt: 1,
ml: 0.5,
fontFamily: "Poppins",
fontFamily: poppins.style.fontFamily,
}}
>
CDR Link

View file

@ -1,30 +0,0 @@
"use client";
import { FC, /* useEffect,*/ useState } from "react";
import { Home as HomeInternal } from "leafcutter-common";
// import { fetchLeafcutter } from "@/app/_lib/utils";
import ClientOnly from "@/app/(main)/_components/ClientOnly";
export const Home: FC = () => {
const [visualizations, setVisualizations] = useState([]);
/*
useEffect(() => {
const getVisualizations = async () => {
const visualizations = await fetchLeafcutter(
"/api/visualizations/list",
{},
);
if (visualizations) {
setVisualizations(visualizations);
}
};
getVisualizations();
}, []);
*/
return (
<ClientOnly>
<HomeInternal visualizations={visualizations} />
</ClientOnly>
);
};

View file

@ -1,6 +0,0 @@
import { FC, PropsWithChildren } from "react";
import { Box } from "@mui/material";
export const LeafcutterWrapper: FC<PropsWithChildren> = ({ children }) => {
return <Box sx={{ p: 3 }}>{children}</Box>;
};

View file

@ -1,10 +1,5 @@
import { Box } from "@mui/material";
import { About } from "leafcutter-common";
import { About } from "leafcutter-ui";
export default function Page() {
return (
<Box sx={{ p: 3 }}>
<About />
</Box>
);
return <About />;
}

View file

@ -1,13 +1,8 @@
// import { getTemplates } from "app/_lib/opensearch";
import { Create } from "leafcutter-common";
import { Box } from "@mui/material";
import { getTemplates } from "opensearch-common";
import { Create } from "leafcutter-ui";
export default async function Page() {
const templates = []; // await getTemplates(100);
const templates = await getTemplates(100);
return (
<Box sx={{ p: 3 }}>
<Create templates={templates} />
</Box>
);
return <Create templates={templates} />;
}

View file

@ -1,10 +1,5 @@
import { Box } from "@mui/material";
import { FAQ } from "leafcutter-common";
import { FAQ } from "leafcutter-ui";
export default function Page() {
return (
<Box sx={{ p: 3 }}>
<FAQ />
</Box>
);
return <FAQ />;
}

View file

@ -0,0 +1,10 @@
import { ReactNode } from "react";
import { LeafcutterWrapper } from "leafcutter-ui";
type LayoutProps = {
children: ReactNode;
};
export default function Layout({ children }: LayoutProps) {
return <LeafcutterWrapper>{children}</LeafcutterWrapper>;
}

View file

@ -1,10 +0,0 @@
import { Box } from "@mui/material";
import { FAQ } from "leafcutter-common";
export default function Page() {
return (
<Box sx={{ p: 3 }}>
<FAQ />
</Box>
);
}

View file

@ -1,5 +1,4 @@
import { Home } from "./_components/Home";
import { LeafcutterWrapper } from "./_components/LeafcutterWrapper";
import { Home, LeafcutterWrapper } from "leafcutter-ui";
export default async function Page() {
return (

View file

@ -1,12 +1,7 @@
import { Box } from "@mui/material";
import { Trends } from "leafcutter-common";
export default function Page() {
return (
<Box sx={{ p: 3 }}>
<Trends visualizations={[]} />
</Box>
);
}
import { Trends } from "leafcutter-ui";
export const dynamic = "force-dynamic";
export default function Page() {
return <Trends visualizations={[]} />;
}

View file

@ -1,10 +1,23 @@
import { Metadata } from "next";
import { Home } from "./_components/Home";
import { getServerSession } from "app/_lib/authentication";
import { Home } from "leafcutter-ui";
import { getUserVisualizations } from "opensearch-common";
import { LeafcutterWrapper } from "leafcutter-ui";
export const metadata: Metadata = {
title: "Link",
};
export default function Page() {
return <Home />;
export default async function Page() {
const session = await getServerSession();
const {
user: { email },
}: any = session;
const visualizations = await getUserVisualizations(email ?? "none", 20);
return (
<LeafcutterWrapper>
<Home visualizations={visualizations} showWelcome={false} />
</LeafcutterWrapper>
);
}

View file

@ -1,7 +1,6 @@
"use client";
import { FC, PropsWithChildren, useState } from "react";
import { usePathname } from "next/navigation";
import { CssBaseline } from "@mui/material";
import { CookiesProvider } from "react-cookie";
import { SessionProvider } from "next-auth/react";
@ -12,7 +11,7 @@ import { I18n } from "react-polyglot";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFnsV3";
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
import { LicenseInfo } from "@mui/x-date-pickers-pro";
import { locales } from "leafcutter-common";
import { locales, LeafcutterProvider } from "leafcutter-ui";
LicenseInfo.setLicenseKey(
"7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=",
@ -113,7 +112,9 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
<CookiesProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<I18n locale={locale} messages={messages[locale]}>
{children}
<LeafcutterProvider>
{children}
</LeafcutterProvider>
</I18n>
</LocalizationProvider>
</CookiesProvider>

View file

@ -0,0 +1,140 @@
import type {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import {
NextAuthOptions,
getServerSession as internalGetServerSession,
} from "next-auth";
import Google from "next-auth/providers/google";
import Credentials from "next-auth/providers/credentials";
import Apple from "next-auth/providers/apple";
const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };
const fetchRoles = async () => {
const url = `${process.env.ZAMMAD_URL}/api/v1/roles`;
const res = await fetch(url, { headers });
const roles = await res.json();
console.log({ roles });
const formattedRoles = roles.reduce((acc: any, role: any) => {
acc[role.id] = role.name;
return acc;
}, {});
return formattedRoles;
};
const fetchUser = async (email: string) => {
console.log({ email });
const url = `${process.env.ZAMMAD_URL}/api/v1/users/search?query=login:${email}&limit=1`;
console.log({ url });
const res = await fetch(url, { headers });
console.log({ res });
const users = await res.json();
console.log({ users });
const user = users?.[0];
return user;
};
const getUserRoles = async (email: string) => {
try {
const user = await fetchUser(email);
console.log({ user });
const allRoles = await fetchRoles();
console.log({ allRoles });
const roles = user.role_ids.map((roleID: number) => {
const role = allRoles[roleID];
return role ? role.toLowerCase().replace(" ", "_") : null;
});
return roles.filter((role: string) => role !== null);
} catch (e) {
console.log({ e });
return [];
}
};
const login = async (email: string, password: string) => {
const url = `${process.env.ZAMMAD_URL}/api/v1/users/me`;
console.log({ url });
const authorization =
"Basic " + Buffer.from(email + ":" + password).toString("base64");
const res = await fetch(url, {
headers: {
authorization,
},
});
const user = await res.json();
console.log({ user });
if (user && !user.error && user.id) {
return user;
} else {
return null;
}
};
export const authOptions: NextAuthOptions = {
pages: {
signIn: "/login",
error: "/login",
signOut: "/logout",
},
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
Apple({
clientId: process.env.APPLE_CLIENT_ID,
clientSecret: process.env.APPLE_CLIENT_SECRET,
}),
Credentials({
name: "Zammad",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const user = await login(credentials.email, credentials.password);
if (user) {
return user;
} else {
return null;
}
},
}),
],
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
signIn: async ({ user, account, profile }) => {
const roles = (await getUserRoles(user.email)) ?? [];
return (
roles.includes("admin") ||
roles.includes("agent") ||
process.env.SETUP_MODE === "true"
);
},
session: async ({ session, user, token }) => {
// @ts-ignore
session.user.roles = token.roles ?? [];
// @ts-ignore
session.user.leafcutter = token.leafcutter; // remove
return session;
},
jwt: async ({ token, user, account, profile, trigger }) => {
if (user) {
token.roles = (await getUserRoles(user.email)) ?? [];
}
return token;
},
},
}
export const getServerSession = (
...args:
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
| [NextApiRequest, NextApiResponse]
| []
) => internalGetServerSession(...args, authOptions);

View file

@ -1,128 +1,6 @@
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import Credentials from "next-auth/providers/credentials";
import Apple from "next-auth/providers/apple";
import { authOptions } from "app/_lib/authentication";
const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };
const fetchRoles = async () => {
const url = `${process.env.ZAMMAD_URL}/api/v1/roles`;
const res = await fetch(url, { headers });
const roles = await res.json();
console.log({ roles });
const formattedRoles = roles.reduce((acc: any, role: any) => {
acc[role.id] = role.name;
return acc;
}, {});
return formattedRoles;
};
const fetchUser = async (email: string) => {
console.log({ email });
const url = `${process.env.ZAMMAD_URL}/api/v1/users/search?query=login:${email}&limit=1`;
console.log({ url });
const res = await fetch(url, { headers });
console.log({ res });
const users = await res.json();
console.log({ users });
const user = users?.[0];
return user;
};
const getUserRoles = async (email: string) => {
try {
const user = await fetchUser(email);
console.log({ user });
const allRoles = await fetchRoles();
console.log({ allRoles });
const roles = user.role_ids.map((roleID: number) => {
const role = allRoles[roleID];
return role ? role.toLowerCase().replace(" ", "_") : null;
});
return roles.filter((role: string) => role !== null);
} catch (e) {
console.log({ e });
return [];
}
};
const login = async (email: string, password: string) => {
const url = `${process.env.ZAMMAD_URL}/api/v1/users/me`;
console.log({ url });
const authorization =
"Basic " + Buffer.from(email + ":" + password).toString("base64");
const res = await fetch(url, {
headers: {
authorization,
},
});
const user = await res.json();
console.log({ user });
if (user && !user.error && user.id) {
return user;
} else {
return null;
}
};
const handler = NextAuth({
pages: {
signIn: "/login",
error: "/login",
signOut: "/logout",
},
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
Apple({
clientId: process.env.APPLE_CLIENT_ID,
clientSecret: process.env.APPLE_CLIENT_SECRET,
}),
Credentials({
name: "Zammad",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const user = await login(credentials.email, credentials.password);
if (user) {
return user;
} else {
return null;
}
},
}),
],
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
signIn: async ({ user, account, profile }) => {
const roles = (await getUserRoles(user.email)) ?? [];
return (
roles.includes("admin") ||
roles.includes("agent") ||
process.env.SETUP_MODE === "true"
);
},
session: async ({ session, user, token }) => {
// @ts-ignore
session.user.roles = token.roles ?? [];
// @ts-ignore
session.user.leafcutter = token.leafcutter; // remove
return session;
},
jwt: async ({ token, user, account, profile, trigger }) => {
if (user) {
token.roles = (await getUserRoles(user.email)) ?? [];
}
return token;
},
},
});
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };