Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup admin dashboard #71

Merged
merged 31 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
715fadc
add google OAUTH 2.0
fredrir Sep 19, 2024
0cc21c5
implement fetch user group logic
fredrir Sep 19, 2024
801fdb0
.
fredrir Sep 19, 2024
bfadf80
add more to admin dashboard
fredrir Sep 20, 2024
f5fe869
Add admin to navbar
fredrir Sep 20, 2024
96dc1aa
add all admin pages
fredrir Sep 20, 2024
e53b78c
expand admin check
fredrir Sep 20, 2024
a062914
Merge branch 'main' of https://github.com/appKom/onlinefondet into se…
fredrir Sep 20, 2024
d7c7d8f
fix merge
fredrir Sep 20, 2024
c272bb7
fix merge
fredrir Sep 20, 2024
4acbb6a
add admin root layout
fredrir Sep 20, 2024
ab4c584
remove Link component from Navbar
fredrir Sep 20, 2024
78f4dd5
Replace a- with link-tags
julian-ao Sep 23, 2024
d4bf638
fix double link nesting error
julian-ao Sep 23, 2024
844f0e8
Merge branch 'main' into setup-admin-dashboard
julian-ao Sep 23, 2024
867e198
Remove unused import
julian-ao Sep 23, 2024
21d55f7
fix styling bug
julian-ao Sep 23, 2024
1650cf6
fix linking bug
fredrir Sep 23, 2024
7821269
Merge branch 'setup-admin-dashboard' of https://github.com/appKom/onl…
fredrir Sep 23, 2024
104850e
merge fix
fredrir Sep 23, 2024
8d31b7b
add name to navbar
fredrir Sep 23, 2024
ac65e17
Merge branch 'main' into setup-admin-dashboard
julian-ao Sep 23, 2024
b241e2d
add log out to navbar
fredrir Sep 24, 2024
8a0e3e5
tweak
fredrir Sep 24, 2024
d229092
grid cols in adminPage
fredrir Sep 24, 2024
ea07c81
tweak
fredrir Sep 24, 2024
b5bf13d
SessionProvider -> SessionWrapper
julian-ao Sep 24, 2024
b676348
Remove bekk in navbar to make place for admin stuff
julian-ao Sep 24, 2024
a0e389f
Fix deployment error
julian-ao Sep 24, 2024
d021590
Add session loading state
julian-ao Sep 24, 2024
ef7f98a
Fix body-wrap
julian-ao Sep 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.local.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

NEXTAUTH_SECRET=your-secret-key
NEXTAUTH_URL=http://localhost:3000
17 changes: 17 additions & 0 deletions app/admin/applications/page.tsx
julian-ao marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';
import Custom404 from '@/app/not-found';
import { useSession } from 'next-auth/react';

const ApplicationsPage = () => {
const { data: session } = useSession();

if (!session || session.user?.role != 'admin') return <Custom404 />;
fredrir marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="flex flex-col min-h-screen items-center pt-8">
<h1 className="text-4xl">Søknader</h1>
</div>
);
};

export default ApplicationsPage;
17 changes: 17 additions & 0 deletions app/admin/members/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';
import Custom404 from '@/app/not-found';
import { useSession } from 'next-auth/react';

const MembersPage = () => {
const { data: session } = useSession();

if (!session || session.user?.role != 'admin') return <Custom404 />;

return (
<div className="flex flex-col min-h-screen items-center pt-8">
<h1 className="text-4xl">Medlemmer</h1>
</div>
);
};

export default MembersPage;
104 changes: 104 additions & 0 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use client';

import { useSession, signIn, signOut } from 'next-auth/react';
import Button from '@/components/all/Button';
import Link from 'next/link';
import { CircleDollarSignIcon, PaperclipIcon, UserIcon } from 'lucide-react';

const AdminPage = () => {
const { data: session } = useSession();

const handleLogin = () =>
signIn('google', {
callbackUrl: '/admin',
});

const handleLogout = () => signOut({ callbackUrl: '/' });

const routes = [
{
title: 'Portfølje',
href: '/admin/portfolio',
icon: CircleDollarSignIcon,
},
{
title: 'Medlemmer',
href: '/admin/members',
icon: UserIcon,
},
{
title: 'Søknader',
href: '/admin/applications',
icon: PaperclipIcon,
},
];

if (!session) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<div className="flex flex-col items-center justify-center px-6 gap-5">
<h1 className="text-3xl">Vennligst logg inn</h1>
<Button
color="orange"
title="Logg inn"
onClick={() => handleLogin()}
/>
</div>
</div>
);
}
fredrir marked this conversation as resolved.
Show resolved Hide resolved

if (session?.user?.role !== 'admin') {
console.log(session.user);
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<div className="flex flex-col items-center justify-center px-6 gap-5">
<h1 className="text-3xl">Du har ikke tilgang til denne siden</h1>
<h1>{session.user?.name}</h1>
<Button
color="orange"
title="Logg ut"
onClick={() => handleLogout()}
/>
</div>
</div>
);
}

if (session?.user?.role === 'admin') {
return (
<div className="flex flex-col items-center py-8 min-h-screen">
<div className="flex flex-col items-center px-6 gap-5">
<h1 className="text-3xl">{`Velkommen ${session.user.name}`}</h1>

<div className="flex flex-col sm:flex-row gap-5 pt-4 pb-12">
{routes.map((route, index) => (
<div key={index}>
<Link href={route.href} className="">
<div className="border-2 border-white rounded-md px-8 hover:scale-110">
<div className="flex flex-col items-center justify-between w-full shadow-md min-h-48 p-4">
<h1 className="mb-4 text-2xl font-semibold">
{route.title}
</h1>
<div className="flex-grow flex items-center justify-center">
<route.icon size={96} />
</div>
</div>
</div>
</Link>
</div>
))}
</div>

<Button
color="orange"
title="Logg ut"
onClick={() => handleLogout()}
/>
</div>
</div>
);
}
};

export default AdminPage;
17 changes: 17 additions & 0 deletions app/admin/portfolio/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';
import Custom404 from '@/app/not-found';
import { useSession } from 'next-auth/react';

const PortfolioPage = () => {
const { data: session } = useSession();

if (!session || session.user?.role != 'admin') return <Custom404 />;

return (
<div className="flex flex-col min-h-screen items-center pt-8">
<h1 className="text-4xl">Portfølje</h1>
</div>
);
};

export default PortfolioPage;
54 changes: 54 additions & 0 deletions app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { getUserGroups } from '@/lib/auth/getUserGroups';
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

const handler = NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID || '',
clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
authorization: {
params: {
scope:
'openid email profile https://www.googleapis.com/auth/admin.directory.group.readonly',
},
},
}),
],
pages: {
signIn: '/auth/signin',
signOut: '/auth/signout',
},
callbacks: {
async jwt({ token, account, user }) {
if (account) {
token.accessToken = account.access_token;
}

if (token.accessToken && user?.email) {
token.groups = await getUserGroups(
token.accessToken as string,
user.email,
);
}

return token;
},
async session({ session, token }) {
session.user = {
id: token.sub ? parseInt(token.sub, 10) : 0,
name: session.user?.name || '',
role:
session.user?.email == '[email protected]' ||
'[email protected] '
? 'admin'
: 'user', //TODO vente på dotkom
email: session.user?.email || '',
groups: (token.groups as string[]) || [],
};
return session;
},
},
});

export { handler as GET, handler as POST };
13 changes: 8 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next';
import './globals.css';
import Navbar from '@/components/all/Navbar';
import Footer from '@/components/all/Footer';
import SessionWrapper from '@/lib/auth/sessionProvider';

// TODO: add favicon, and maybe dynamic title and description based on page
export const metadata: Metadata = {
Expand All @@ -19,11 +20,13 @@ export default function RootLayout({
<head>
<link rel="icon" href="public/icon-256.png" sizes="any" />
</head>
<body className="antialiased">
<Navbar />
{children}
<Footer />
</body>
<SessionWrapper>
<body className="antialiased">
<Navbar />
{children}
<Footer />
</body>
</SessionWrapper>
</html>
);
}
25 changes: 25 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Button from '@/components/all/Button';
import Image from 'next/image';

export default function Custom404() {
return (
<div className="flex flex-col items-center justify-center min-h-screen text-center px-6 gap-12">
<h2 className="text-3xl sm:text-3xl lg:text-5xl font-bold text-online-darkBlue dark:text-white">
Fant ikke siden du lette etter
</h2>

<div>
<Image
src="/not-found.svg"
alt="Not Found Illustrasjon"
width={500}
height={500}
/>
</div>

<div className="py-10">
<Button title="Gå tilbake til hjem siden" color="orange" href="/" />
</div>
</div>
);
}
36 changes: 36 additions & 0 deletions components/all/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import Link from 'next/link';

interface Props {
title: string;
onClick?: () => void;
href?: string;
color: 'orange';
}

const Button = ({ title, onClick, color, href }: Props) => {
const colorStyle =
color === 'orange'
? 'border border-onlineyellow text-onlineyellow hover:border-orange-600 hover:text-orange-600'
: '';

const buttonStyle = `px-4 py-3 rounded-md ${colorStyle}`;

if (onClick) {
return (
<button className={`${buttonStyle}`} onClick={onClick}>
{title}
</button>
);
}
if (href) {
return (
<Link className={`${buttonStyle}`} href={href}>
{title}
</Link>
);
}
};

export default Button;
Loading