Dependency cleanup
This commit is contained in:
parent
2d892779bf
commit
2568547384
28 changed files with 170 additions and 482 deletions
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" }}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)) ?? [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
missingSuspenseWithCSRBailout: false,
|
||||
},
|
||||
transpilePackages: [
|
||||
"@link-stack/leafcutter-ui",
|
||||
"@link-stack/opensearch-common",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue