Dependency cleanup

This commit is contained in:
Darren Clarke 2024-08-07 12:02:33 +02:00
parent 2d892779bf
commit 2568547384
28 changed files with 170 additions and 482 deletions

View file

@ -1,12 +1,18 @@
import { Suspense } from "react";
import { Metadata } from "next";
import { getSession } from "next-auth/react";
import { Login } from "./_components/Login";
export const metadata: Metadata = {
title: "Login",
title: "CDR Link - Login",
};
export default async function Page() {
const session = await getSession();
return <Login session={session} />;
return (
<Suspense fallback={<div>Loading...</div>}>
<Login session={session} />
</Suspense>
);
}

View file

@ -7,11 +7,13 @@ import { SetupModeWarning } from "./SetupModeWarning";
interface InternalLayoutProps extends PropsWithChildren {
setupModeActive: boolean;
leafcutterEnabled: boolean;
}
export const InternalLayout: FC<InternalLayoutProps> = ({
children,
setupModeActive,
leafcutterEnabled,
}) => {
const [open, setOpen] = useState(true);
@ -19,7 +21,11 @@ export const InternalLayout: FC<InternalLayoutProps> = ({
<Box sx={{ position: "relative" }}>
<SetupModeWarning setupModeActive={setupModeActive} />
<Grid container direction="row">
<Sidebar open={open} setOpen={setOpen} />
<Sidebar
open={open}
setOpen={setOpen}
leafcutterEnabled={leafcutterEnabled}
/>
<Grid
item
sx={{ ml: open ? "270px" : "70px", width: "100%", height: "100vh" }}

View file

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

View file

@ -25,7 +25,6 @@ import {
Assessment as AssessmentIcon,
LibraryBooks as LibraryBooksIcon,
School as SchoolIcon,
Search as SearchIcon,
} from "@mui/icons-material";
import { usePathname } from "next/navigation";
import Link from "next/link";
@ -179,17 +178,21 @@ const MenuItem = ({
interface SidebarProps {
open: boolean;
setOpen: (open: boolean) => void;
leafcutterEnabled?: boolean;
}
export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
export const Sidebar: FC<SidebarProps> = ({
open,
setOpen,
leafcutterEnabled = false,
}) => {
const pathname = usePathname();
const { data: session } = useSession();
const [overviewCounts, setOverviewCounts] = useState<any>(null);
const { poppins } = fonts;
const username = session?.user?.name || "User";
const username = session?.user?.name || "";
// @ts-ignore
const roles = session?.user?.roles || [];
const leafcutterEnabled = false;
useEffect(() => {
const fetchCounts = async () => {
@ -205,12 +208,6 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
return () => clearInterval(interval);
}, []);
const recentCount = 0;
const assignedCount = overviewCounts?.["My Assigned Tickets"] ?? 0;
const openCount = overviewCounts?.["Open Tickets"] ?? 0;
const urgentCount = overviewCounts?.["Escalated Tickets"] ?? 0;
const unassignedCount = overviewCounts?.["Unassigned & Open Tickets"] ?? 0;
const logout = () => {
signOut({ callbackUrl: "/login" });
};
@ -439,7 +436,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
Icon={FeaturedPlayListIcon}
iconSize={0}
selected={pathname.endsWith("/overview/recent")}
badge={recentCount}
badge={overviewCounts?.recent}
open={open}
/>
<MenuItem
@ -448,7 +445,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
Icon={FeaturedPlayListIcon}
iconSize={0}
selected={pathname.endsWith("/overview/open")}
badge={openCount}
badge={overviewCounts?.open}
open={open}
/>
<MenuItem
@ -457,7 +454,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
Icon={FeaturedPlayListIcon}
iconSize={0}
selected={pathname.endsWith("/overview/urgent")}
badge={urgentCount}
badge={overviewCounts?.urgent}
open={open}
/>
<MenuItem
@ -466,7 +463,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
Icon={FeaturedPlayListIcon}
iconSize={0}
selected={pathname.endsWith("/overview/assigned")}
badge={assignedCount}
badge={overviewCounts?.assigned}
open={open}
/>
<MenuItem
@ -475,7 +472,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
Icon={FeaturedPlayListIcon}
iconSize={0}
selected={pathname.endsWith("/overview/unassigned")}
badge={unassignedCount}
badge={overviewCounts?.unassigned}
open={open}
/>
</List>

View file

@ -12,9 +12,13 @@ type LayoutProps = {
export default function Layout({ children }: LayoutProps) {
const setupModeActive = process.env.SETUP_MODE === "true";
const leafcutterEnabled = process.env.LEAFCUTTER_ENABLED === "true";
return (
<InternalLayout setupModeActive={setupModeActive}>
<InternalLayout
setupModeActive={setupModeActive}
leafcutterEnabled={leafcutterEnabled}
>
{children}
</InternalLayout>
);

View file

@ -6,22 +6,26 @@ import { getUserVisualizations } from "@link-stack/opensearch-common";
import { LeafcutterWrapper } from "@link-stack/leafcutter-ui";
export const metadata: Metadata = {
title: "Link",
title: "CDR Link - Home",
};
export default async function Page() {
const leafcutterEnabled = process.env.LEAFCUTTER_ENABLED === "true";
if (!leafcutterEnabled) {
redirect("/overview/recent");
}
const session = await getServerSession();
const {
user: { email },
}: any = session;
// const visualizations = await getUserVisualizations(email ?? "none", 20);
redirect("/overview/recent");
/*
const visualizations = await getUserVisualizations(email ?? "none", 20);
return (
<LeafcutterWrapper>
<Home visualizations={visualizations} showWelcome={false} />
</LeafcutterWrapper>
);
*/
}

View file

@ -4,15 +4,34 @@ import { executeGraphQL, executeREST } from "app/_lib/zammad";
import { getTicketOverviewCountsQuery } from "app/_graphql/getTicketOverviewCountsQuery";
import { getTicketsByOverviewQuery } from "app/_graphql/getTicketsByOverviewQuery";
const overviewLookup = {
Assigned: "My Assigned Tickets",
Open: "Open Tickets",
Urgent: "Escalated Tickets",
Unassigned: "Unassigned & Open Tickets",
Recent: "Recent Tickets",
Pending: "Pending Reached Tickets",
MyPending: "My Pending Reached Tickets",
MySubscribed: "My Subscribed Tickets",
};
export const getOverviewTicketCountsAction = async () => {
const recent = await executeREST({ path: "/api/v1/recent_view" });
const countResult = await executeGraphQL({
query: getTicketOverviewCountsQuery,
});
const overviews = countResult?.ticketOverviews?.edges;
const counts = overviews?.reduce((acc: any, overview: any) => {
acc[overview.node.name] = overview.node.ticketCount;
const overviews = countResult?.ticketOverviews?.edges ?? [];
const counts = overviews.reduce((acc: any, overview: any) => {
const name = overview.node.name;
const key = Object.keys(overviewLookup)
.find((k) => overviewLookup[k] === name)
?.toLowerCase();
if (key) {
acc[key] = overview.node.ticketCount ?? 0;
}
return acc;
});
}, {});
counts.recent = recent.length;
return counts;
};
@ -36,12 +55,6 @@ export const getOverviewTicketsAction = async (name: string) => {
});
}
} else {
const overviewLookup = {
Assigned: "My Assigned Tickets",
Open: "Open Tickets",
Urgent: "Escalated Tickets",
Unassigned: "Unassigned & Open Tickets",
};
const fullName = overviewLookup[name];
const countResult = await executeGraphQL({
query: getTicketOverviewCountsQuery,

View file

@ -162,14 +162,6 @@ export const getTicketStatesAction = async () => {
path: "/api/v1/ticket_states",
});
return states;
};
export const getTicketTagsAction = async () => {
const states = await executeREST({
path: "/api/v1/tags",
});
const formattedStates =
states?.map((state: any) => ({
value: state.id,
@ -179,6 +171,14 @@ export const getTicketTagsAction = async () => {
return formattedStates;
};
export const getTicketTagsAction = async () => {
const tags = await executeREST({
path: "/api/v1/tags",
});
return tags;
};
export const getTicketPrioritiesAction = async () => {
const priorities = await executeREST({
path: "/api/v1/ticket_priorities",

View file

@ -1,10 +1,9 @@
"use client";
import { FC, PropsWithChildren, useState } from "react";
import { FC, PropsWithChildren } 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 { I18n } from "react-polyglot";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFnsV3";
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
@ -17,27 +16,21 @@ LicenseInfo.setLicenseKey(
);
export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
const origin =
typeof window !== "undefined" && window.location.origin
? window.location.origin
: null;
const messages: any = { en: locales.en, fr: locales.fr };
const locale = "en";
return (
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
<SessionProvider>
<CssBaseline />
<SessionProvider>
<CSRFProvider>
<CookiesProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<I18n locale={locale} messages={messages[locale]}>
<LeafcutterProvider>{children}</LeafcutterProvider>
</I18n>
</LocalizationProvider>
</CookiesProvider>
</CSRFProvider>
</SessionProvider>
</NextAppDirEmotionCacheProvider>
<CSRFProvider>
<CookiesProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<I18n locale={locale} messages={messages[locale]}>
<LeafcutterProvider>{children}</LeafcutterProvider>
</I18n>
</LocalizationProvider>
</CookiesProvider>
</CSRFProvider>
</SessionProvider>
);
};

View file

@ -17,7 +17,6 @@ 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;
@ -26,13 +25,9 @@ const fetchRoles = async () => {
};
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;
@ -41,9 +36,7 @@ const fetchUser = async (email: string) => {
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;
@ -57,7 +50,6 @@ const getUserRoles = async (email: string) => {
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, {
@ -66,7 +58,6 @@ const login = async (email: string, password: string) => {
},
});
const user = await res.json();
console.log({ user });
if (user && !user.error && user.id) {
return user;
@ -96,7 +87,7 @@ export const authOptions: NextAuthOptions = {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
async authorize(credentials) {
const user = await login(credentials.email, credentials.password);
if (user) {
return user;
@ -108,7 +99,7 @@ export const authOptions: NextAuthOptions = {
],
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
signIn: async ({ user, account, profile }) => {
signIn: async ({ user }) => {
const roles = (await getUserRoles(user.email)) ?? [];
return (
roles.includes("admin") ||
@ -116,7 +107,7 @@ export const authOptions: NextAuthOptions = {
process.env.SETUP_MODE === "true"
);
},
session: async ({ session, user, token }) => {
session: async ({ session, token }) => {
// @ts-ignore
session.user.roles = token.roles ?? [];
// @ts-ignore
@ -126,7 +117,7 @@ export const authOptions: NextAuthOptions = {
return session;
},
jwt: async ({ token, user, account, profile, trigger, session }) => {
jwt: async ({ token, user, trigger, session }) => {
if (user) {
token.roles = (await getUserRoles(user.email)) ?? [];
}

View file

@ -1,10 +1,11 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import "./_styles/global.css";
import { AppRouterCacheProvider } from "@mui/material-nextjs/v14-appRouter";
import { MultiProvider } from "./_components/MultiProvider";
import "./_styles/global.css";
export const metadata: Metadata = {
title: "Link",
title: "CDR Link",
};
type LayoutProps = {
@ -15,7 +16,9 @@ export default function Layout({ children }: LayoutProps) {
return (
<html lang="en">
<body>
<MultiProvider>{children}</MultiProvider>
<AppRouterCacheProvider>
<MultiProvider>{children}</MultiProvider>
</AppRouterCacheProvider>
</body>
</html>
);

View file

@ -61,7 +61,8 @@ export default withAuth(checkRewrites, {
},
callbacks: {
authorized: ({ token, req }) => {
if (req.nextUrl.pathname.startsWith("/api/v1/")) {
const path = req.nextUrl.pathname;
if (path.startsWith("/api/v1/")) {
return true;
}
@ -70,6 +71,11 @@ export default withAuth(checkRewrites, {
}
const roles: any = token?.roles ?? [];
if (path.startsWith("/admin") && !roles.includes("admin")) {
return false;
}
if (roles.includes("admin") || roles.includes("agent")) {
return true;
}

View file

@ -1,8 +1,5 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
missingSuspenseWithCSRBailout: false,
},
transpilePackages: [
"@link-stack/leafcutter-ui",
"@link-stack/opensearch-common",

View file

@ -1,6 +1,6 @@
{
"name": "@link-stack/link",
"version": "0.0.1",
"version": "2.1.0",
"type": "module",
"scripts": {
"dev": "next dev",
@ -16,32 +16,29 @@
"@emotion/react": "^11.13.0",
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^5",
"@mui/lab": "^5.0.0-alpha.173",
"@mui/material": "^5",
"@mui/x-data-grid-pro": "^7.12.0",
"@mui/x-date-pickers-pro": "^7.12.0",
"@link-stack/bridge-common": "*",
"@link-stack/bridge-ui": "*",
"@link-stack/leafcutter-ui": "*",
"@link-stack/opensearch-common": "*",
"@link-stack/ui": "*",
"@mui/icons-material": "^5",
"@mui/material": "^5",
"@mui/material-nextjs": "^5.16.6",
"@mui/x-data-grid-pro": "^7.12.0",
"@mui/x-date-pickers-pro": "^7.12.0",
"date-fns": "^3.6.0",
"graphql": "^16.9.0",
"graphql-request": "^7.1.0",
"@link-stack/leafcutter-ui": "*",
"material-ui-popup-state": "^5.1.2",
"mui-chips-input": "^2.1.5",
"next": "14.2.5",
"next-auth": "^4.24.7",
"@link-stack/opensearch-common": "*",
"react": "18.3.1",
"react-cookie": "^7.2.0",
"react-dom": "18.3.1",
"react-iframe": "^1.8.5",
"react-polyglot": "^0.7.2",
"sharp": "^0.33.4",
"swr": "^2.2.5",
"tss-react": "^4.9.12",
"twilio-client": "^1.15.1",
"@link-stack/ui": "*"
"sharp": "^0.33.4"
},
"devDependencies": {
"@babel/core": "^7.25.2",