-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #71 from appKom/setup-admin-dashboard
Setup admin dashboard
- Loading branch information
Showing
17 changed files
with
971 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const ApplicationsPage = () => { | ||
return ( | ||
<div className="flex flex-col min-h-screen items-center pt-8"> | ||
<h1 className="text-4xl">Søknader</h1> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ApplicationsPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
'use client'; | ||
|
||
import { signIn, signOut, useSession } from 'next-auth/react'; | ||
import Button from '@/components/all/Button'; | ||
|
||
export default function AdminLayout({ | ||
children, | ||
}: Readonly<{ | ||
children: React.ReactNode; | ||
}>) { | ||
const { data: session, status } = useSession(); | ||
|
||
const handleLogin = () => | ||
signIn('google', { | ||
callbackUrl: '/admin', | ||
}); | ||
|
||
const handleLogout = () => signOut({ callbackUrl: '/' }); | ||
|
||
if (status === 'loading') { | ||
return ( | ||
<div className="min-h-screen text-white flex items-center justify-center"> | ||
<div className="text-center"> | ||
<div className="inline-block animate-spin rounded-full h-16 w-16 border-y-2 border-onlineyellow mb-4"></div> | ||
<h2 className="text-2xl font-semibold"> | ||
Laster inn administrasjonspanel... | ||
</h2> | ||
<p className="text-slate-400 mt-2"> | ||
Vennligst vent mens vi henter informasjonen din | ||
</p> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
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> | ||
); | ||
} | ||
|
||
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 && session?.user?.role === 'admin') { | ||
return ( | ||
<div> | ||
<div className="flex flex-col min-h-screen items-center"> | ||
{children} | ||
</div> | ||
</div> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const MembersPage = () => { | ||
return ( | ||
<div className="flex flex-col min-h-screen items-center pt-8"> | ||
<h1 className="text-4xl">Medlemmer</h1> | ||
</div> | ||
); | ||
}; | ||
|
||
export default MembersPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
'use client'; | ||
|
||
import { useSession } from 'next-auth/react'; | ||
import Link from 'next/link'; | ||
import { CircleDollarSignIcon, PaperclipIcon, UserIcon } from 'lucide-react'; | ||
|
||
const AdminPage = () => { | ||
const { data: session } = useSession(); | ||
|
||
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, | ||
}, | ||
]; | ||
|
||
return ( | ||
<div className="flex flex-col items-center py-8 "> | ||
<div className="flex flex-col items-center px-6 gap-5"> | ||
<h1 className="text-3xl">{`Velkommen ${session?.user!.name}`}</h1> | ||
|
||
<div className="grid gap-4 grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3"> | ||
{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> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AdminPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const PortfolioPage = () => { | ||
return ( | ||
<div className="flex flex-col min-h-screen items-center pt-8"> | ||
<h1 className="text-4xl">Portfølje</h1> | ||
</div> | ||
); | ||
}; | ||
|
||
export default PortfolioPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
'use client'; | ||
|
||
import Link from 'next/link'; | ||
|
||
interface Props { | ||
title: string; | ||
onClick?: () => void; | ||
href?: string; | ||
color: 'orange' | 'white'; | ||
className?: string; | ||
} | ||
|
||
const Button = ({ title, onClick, color, href, className }: Props) => { | ||
let colorStyle = ''; | ||
|
||
switch (color) { | ||
case 'orange': | ||
colorStyle = | ||
'border border-onlineyellow text-onlineyellow hover:border-orange-600 hover:text-orange-600'; | ||
break; | ||
|
||
case 'white': | ||
colorStyle = | ||
'border border-white text-white hover:border-gray-600 hover:text-gray-600'; | ||
break; | ||
} | ||
|
||
const buttonStyle = `px-4 py-3 rounded-md ${colorStyle} ${className}`; | ||
|
||
if (onClick) { | ||
return ( | ||
<button className={`${buttonStyle}`} onClick={onClick}> | ||
{title} | ||
</button> | ||
); | ||
} | ||
if (href) { | ||
return ( | ||
<Link className={`${buttonStyle}`} href={href}> | ||
{title} | ||
</Link> | ||
); | ||
} | ||
}; | ||
|
||
export default Button; |
Oops, something went wrong.