Skip to content

Commit

Permalink
Add admin dashboard, and ability to delete ratings
Browse files Browse the repository at this point in the history
  • Loading branch information
DCRepublic committed Nov 11, 2024
1 parent e0274c7 commit 7519810
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 12 deletions.
13 changes: 13 additions & 0 deletions app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function DocsLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<section className="flex flex-col items-center justify-center gap-4 max-h-[72vh] -mt-5 ">
<div className="inline-block text-center justify-center overflow-scroll rounded-lg scrollbar-hide">
{children}
</div>
</section>
);
}
128 changes: 128 additions & 0 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"use client";

import useSWR from "swr";
import { useState } from "react";
import React from "react";
import { signIn, signOut, useSession } from "next-auth/react";
import {
Table,
TableHeader,
TableColumn,
TableBody,
TableRow,
TableCell,
getKeyValue,
Dropdown,
DropdownTrigger,
Button,
DropdownMenu,
DropdownItem,
} from "@nextui-org/react";

import { MoreVert } from "@mui/icons-material";
import axios from "axios";

export default function AdminPage() {
const { data: session, status } = useSession();
const fetcher = (url: any) => fetch(url).then((r) => r.json());

const {
data: ratings,
isLoading,
error,
} = useSWR("/api/getRatings", fetcher, { refreshInterval: 5000 });
let columns = [];
let filtered_ratings: any = [];

if (!isLoading) {
for (let rating of ratings) {
const name = rating.User?.name;
const email = rating.User?.email;
rating.name = name;
rating.email = email;

filtered_ratings.push(rating);
}

if (filtered_ratings.length > 0) {
for (let key in filtered_ratings[0]) {
if (key != "User") {
columns.push({ key: key, label: key });
}
}
}
columns.push({ key: "actions", value: "actions" });
}

async function deleteRating(ratingID: any) {
console.log("Rating Gone, finito, finished");
await axios
.post("/api/deleteRating", {
ratingID: ratingID,
})
.then(function (response) {
// Handle response
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}

const renderCell = React.useCallback((user: any, columnKey: React.Key) => {
const cellValue = user[columnKey as keyof any];

switch (columnKey) {
case "actions":
return (
<div className="relative flex justify-end items-center gap-2">
<Dropdown>
<DropdownTrigger>
<Button isIconOnly size="sm" variant="light">
<MoreVert className="text-default-300" />
</Button>
</DropdownTrigger>
<DropdownMenu>
<DropdownItem onClick={() => deleteRating(user.id)}>
Delete
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
);
default:
return cellValue;
}
}, []);

if (status === "authenticated") {
// @ts-ignore
if (session.user?.role === "admin") {
return (
<div className="w-[90vw]">
<Table
isHeaderSticky
className="overflow-scroll"
fullWidth
aria-label="Rating table with dynamic content(ratings)"
>
<TableHeader columns={columns}>
{(column) => (
<TableColumn key={column.key}>{column.label}</TableColumn>
)}
</TableHeader>
<TableBody items={filtered_ratings}>
{(item: any) => (
<TableRow key={item.id}>
{(columnKey) => (
<TableCell>{renderCell(item, columnKey)}</TableCell>
)}
</TableRow>
)}
</TableBody>
</Table>
</div>
);
}
}
}
20 changes: 20 additions & 0 deletions app/api/deleteRating/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// api/test.ts
import { NextResponse, NextRequest } from "next/server";

import prisma from "../../../lib/prisma";
import { auth } from "../../../lib/auth";
import { getPlanCookie } from "../../../app/actions";

export async function POST(request: NextRequest) {
const data = await request.json();

const ratingID = data.ratingID;

const courses = await prisma.rating.delete({
where: {
id: parseInt(ratingID),
},
});

return NextResponse.json(courses, { status: 200 });
}
18 changes: 18 additions & 0 deletions app/api/getRatings/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextResponse, NextRequest } from "next/server";

import prisma from "../../../lib/prisma";

export async function GET(request: NextRequest) {
const profs = await prisma.rating.findMany({
include: {
User: {
select: {
name: true,
email: true,
},
},
},
});

return NextResponse.json(profs, { status: 200 });
}
17 changes: 8 additions & 9 deletions components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export const Navbar = (props: any) => {
const { data: session, status } = useSession();

let authenticated;
let loginLink;
let adminDashLink;
let loginLink: any;
let adminDashLink: any;
let nameButton;

if (props.hasOwnProperty("login")) {
Expand All @@ -68,16 +68,14 @@ export const Navbar = (props: any) => {
</div>
);
// @ts-ignore
/*

if (session.user?.role === "admin") {
adminDashLink = (
<NavbarContent key="admin">
<Link href="/admin">
<div>Admin</div>
</Link>
</NavbarContent>
<DropdownItem key="admin" href="/admin">
<div>Admin</div>
</DropdownItem>
);
}*/
}
nameButton = session.user?.name;
} else {
authenticated = false;
Expand Down Expand Up @@ -184,6 +182,7 @@ export const Navbar = (props: any) => {

{authenticated ? (
<DropdownMenu aria-label="Static Actions">
{adminDashLink}
<DropdownItem key="loginLink">{loginLink}</DropdownItem>
</DropdownMenu>
) : (
Expand Down
37 changes: 34 additions & 3 deletions lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@ import type {
NextApiResponse,
} from "next";
import type { NextAuthOptions } from "next-auth";
import { getToken } from "next-auth/jwt";

import KeycloakProvider from "next-auth/providers/keycloak";
import KeycloakProvider, {
KeycloakProfileToken,
} from "next-auth/providers/keycloak";
import { getServerSession } from "next-auth";

declare module "next-auth/providers/keycloak" {
export interface KeycloakProfileToken extends KeycloakProfile {
realm_access: { roles: [string] };
}
}
function parseJwt(token: string) {
return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
}
// You'll need to import and pass this
// to `NextAuth` in `app/api/auth/[...nextauth]/route.ts`
export const config = {
Expand All @@ -22,15 +33,36 @@ export const config = {
providers: [
KeycloakProvider({
profile(profile, tokens) {
const tokenData: KeycloakProfileToken = parseJwt(
//@ts-ignore
tokens.access_token
);

let theRole;

if ("scheduler" in tokenData.resource_access) {
theRole = tokenData.resource_access.scheduler.roles.find(
(role: string) => role === "admin" || "user"
);
} else {
theRole = "user";
}

return {
id: profile.sub,
name: profile.name,
email: profile.email,

///THE DUMB OLD WAY BELOW
//MAKE SURE TO ADD GROUPS UNDER client scopes ->[currentproject]-dedicated
//If you don't it wont redirect

//New way is to add a role under client scopes-> roles
// Then assign that role to your user specifically
role:
profile.groups.find((group: string) => group === "admin") || "user",
//tokenData.resource_access.scheduler.roles.find( (role: string) => role === "admin" || "user"),
//profile.groups.find((group: string) => group === "admin") || "user",
theRole,
};
},
clientId: process.env.KEYCLOAK_ID || "",
Expand All @@ -53,7 +85,6 @@ export const config = {
// @ts-ignore
session.user.id = token.sub;
}

return session;
},
},
Expand Down

0 comments on commit 7519810

Please sign in to comment.