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

138 make homepage customizable #141

Merged
merged 10 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 47 additions & 0 deletions vre-panel/context/PaasConfig.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {useEffect, useState, createContext, ReactNode} from "react";
import getConfig from "next/config";


export const PaasConfigContext = createContext({
paasConfig: {
title: "",
description: "",
documentation_url: "",
site_icon: "",
},
paasConfigLoading: true,
});

export function PaasConfigProvider({children,}: { children: ReactNode }) {
const {publicRuntimeConfig} = getConfig()

const [paasConfig, setPaasConfig] = useState({
title: "Virtual Lab environments",
description: "A collection of virtual lab environments",
documentation_url: "https://github.com/QCDIS/NaaVRE/blob/main/README.md",
site_icon: `${publicRuntimeConfig.staticFolder}/LW_ERIC_Logo.png`,
})
const [paasConfigLoading, setPaasConfigLoading] = useState(true)

useEffect(() => {
const apiUrl = `${window.location.origin}/${publicRuntimeConfig.apiBasePath}`
fetch(`${apiUrl}/paas_configuration`)
.then((res) => res.json())
.then((data) => {
if (data.length > 0) {
setPaasConfig(data[0]);
}
setPaasConfigLoading(false)
})
.catch((error) => {
console.log(error)
setPaasConfigLoading(false)
});
}, []);

return (
<PaasConfigContext.Provider value={{paasConfig: paasConfig, paasConfigLoading: paasConfigLoading}}>
{children}
</PaasConfigContext.Provider>
);
}
2 changes: 1 addition & 1 deletion vre-panel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"devDependencies": {
"typescript": "^4.5",
"@types/node": "~20.5.7",
"@types/node": "~20.4.5",
"@types/react": "~18.2.0",
"autoprefixer": "~10.4.15",
"axios-mock-adapter": "~1.21.5",
Expand Down
6 changes: 5 additions & 1 deletion vre-panel/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useState } from 'react';
// import RefreshTokenHandler from './auth/refreshTokenHandler';
import getConfig from 'next/config'

import {PaasConfigProvider} from '../context/PaasConfig';

export default function App({ Component, pageProps: { session, ...pageProps }} : AppProps): JSX.Element {

const { publicRuntimeConfig } = getConfig()
Expand All @@ -13,7 +15,9 @@ export default function App({ Component, pageProps: { session, ...pageProps }} :

return (
<SessionProvider session={session} refetchInterval={interval} basePath={`${publicRuntimeConfig.basePath}/api/auth`}>
<Component {...pageProps} />
<PaasConfigProvider>
<Component {...pageProps} />
</PaasConfigProvider>
</SessionProvider>
)
}
13 changes: 12 additions & 1 deletion vre-panel/pages/auth/signin.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { getProviders, getSession, signIn } from "next-auth/react"
import getConfig from 'next/config'
import {useContext} from "react";
import {PaasConfigContext} from "../../context/PaasConfig";

const { publicRuntimeConfig } = getConfig()

export default function SignIn({ providers }: { providers: any }) {

const {paasConfig, paasConfigLoading} = useContext(PaasConfigContext)

return (
<div className="mx-auto flex flex-col items-center justify-center h-screen">
<img className="w-fit h-fit object-cover" src={`${publicRuntimeConfig.staticFolder}/LW_VLICVRE_logo.png`} />
<div className="flex flex-col justify-center rounded-md overflow-hidden shadow-lg bg-white p-10 mt-10 w-screen">
<img src={`${publicRuntimeConfig.staticFolder}/LW_ERIC_Logo.png`} className="w-36 self-center" alt="LifeWatch Logo" />
{paasConfigLoading || (
<img
src={paasConfig.site_icon}
alt="Site icon"
className="h-16 self-center"
/>
)}
<>
{Object.values(providers).map((provider: any) => (
<div className="self-center" key={provider.name}>
Expand Down
203 changes: 115 additions & 88 deletions vre-panel/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,106 +1,133 @@
import { getToken } from 'next-auth/jwt';
// import { signIn, useSession } from 'next-auth/react';
import {useEffect, useState, useContext} from 'react';
import getConfig from 'next/config';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { NewVREDialog } from '../components/NewVREDialog';
import { Nav } from '../templates/Nav';
// import useAuth from './auth/useAuth';

import {NewVREDialog} from '../components/NewVREDialog';
import {Nav} from '../templates/Nav';
import {PaasConfigContext} from '../context/PaasConfig';

const getSlug = (title: string) => {

return title
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '');
return title
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '');
}

const VLabs = () => {

const { publicRuntimeConfig } = getConfig()

// const isAuthenticated = useAuth(true);
const [isOpen, setIsOpen] = useState(false);
const [vlabs, setVlabs] = useState([]);
const VLabs = ({}) => {

useEffect(() => {
const {publicRuntimeConfig} = getConfig()

// if (isAuthenticated) {
const {paasConfig, paasConfigLoading} = useContext(PaasConfigContext)

// var requestOptions: RequestInit = {
// method: "GET",
// headers: {
// "Authorization": "Bearer: " + token.accessToken
// },
// };
const apiUrl = `${window.location.origin}/${publicRuntimeConfig.apiBasePath}`
fetch(`${apiUrl}/vlabs`)
.then((res) => res.json())
.then((data) => {
setVlabs(data);
})
.catch((error) => {
console.log(error)
});
// }
}, []);
const [isOpen, setIsOpen] = useState(false);
const [vlabs, setVlabs] = useState([]);
const [vlabsLoading, setVlabsLoading] = useState(true);

// const { status } = useSession({
// required: true,
// onUnauthenticated() {
// signIn();
// },
// })
useEffect(() => {

// if (status === "loading") {
// return "Loading or not authenticated..."
// }
const apiUrl = `${window.location.origin}/${publicRuntimeConfig.apiBasePath}`
fetch(`${apiUrl}/vlabs`)
.then((res) => res.json())
.then((data) => {
setVlabs(data);
setVlabsLoading(false)
})
.catch((error) => {
console.log(error)
setVlabsLoading(false)
});
}, []);

return (
<div>
<Nav />
<div className='min-h-screen mx-auto bg-gradient-to-b from-sky-100 to-orange-300'>
<div className='grid grid-cols-3'>
{vlabs.map((vlab: any) => {
return (
<div key={getSlug(vlab.title)} className="max-w-sm rounded overflow-hidden shadow-lg bg-white m-10">
<Link
href={{
pathname: '/vlabs/[slug]',
query: { slug: vlab.slug }
}}
>
<div>
<img className="w-35 h-30 object-cover" src={`${publicRuntimeConfig.staticFolder}/HP-VRES.png`} />
<div className="font-bold text-l mb-2 bg-blue-500 text-white p-5">{vlab.title}</div>
<div className="px-3 py-2">
<p className="text-gray-700 text-base truncate ...">
{vlab.description}
</p>
</div>
</div>
</Link>
</div>
);
})}
return (
<div>
<Nav/>
<div className='min-h-screen mx-auto bg-gradient-to-b from-sky-100 to-orange-300 space-y-10 p-10'>
<div className="max-w-full rounded bg-white p-8">
<h1 className="text-2xl text-gray-800 mb-8">
{paasConfigLoading ? (
<span className="animate-pulse">
<span className="inline-block min-h-[1em] w-3/12 flex-auto cursor-wait bg-current align-middle opacity-50"></span>
</span>
) : (
paasConfig.title
)}
</h1>
<p className="text-l text-gray-800">
{paasConfigLoading ? (
<span className="animate-pulse">
<span className="inline-block min-h-[1em] w-full flex-auto cursor-wait bg-current align-middle opacity-50"></span>
</span>
) : (
paasConfig.description
)}
</p>
{paasConfigLoading || (
paasConfig.documentation_url && (
<p className="mt-4">
<a
href={paasConfig.documentation_url}
className="text-blue-800 hover:underline"
>
Documentation
</a>
</p>
)
)}
</div>
<div className='flex flex-row space-x-10'>
{vlabsLoading ? (
<div className="w-1/3 rounded overflow-hidden shadow-lg bg-white animate">
<div>
<img className="w-35 h-30 object-cover" src={`${publicRuntimeConfig.staticFolder}/HP-VRES.png`}/>
<div className="font-bold text-l mb-2 bg-gray-300 text-white p-5">
<p className="animate-pulse">
<span className="inline-block min-h-[1em] w-6/12 flex-auto cursor-wait bg-current align-middle opacity-50"></span>
</p>
</div>
<div className="px-3 py-2">
<p className="text-gray-700 text-base truncate animate-pulse ...">
<span className="inline-block min-h-[0.6em] w-6/12 flex-auto cursor-wait bg-current align-middle opacity-50"></span>
</p>
</div>
<NewVREDialog isOpen={isOpen} setIsOpen={setIsOpen} />
</div>
</div>
</div >
)
) : (
vlabs.length > 0 ? (
vlabs.map((vlab: any) => {
return (
<div key={getSlug(vlab.title)} className="w-1/3 rounded overflow-hidden shadow-lg bg-white">
<Link
href={{
pathname: '/vlabs/[slug]',
query: {slug: vlab.slug}
}}
>
<div>
<img className="w-35 h-30 object-cover" src={`${publicRuntimeConfig.staticFolder}/HP-VRES.png`}/>
<div className="font-bold text-l mb-2 bg-blue-500 text-white p-5">{vlab.title}</div>
<div className="px-3 py-2">
<p className="text-gray-700 text-base truncate ...">
{vlab.description}
</p>
</div>
</div>
</Link>
</div>
);
})
) : (
<div className="w-1/3 rounded overflow-hidden shadow-lg bg-white">
No virtual labs found
</div>
)
)
}
</div>
<NewVREDialog isOpen={isOpen} setIsOpen={setIsOpen}/>
</div>
</div>
)
};

export async function getServerSideProps(context:any) {

const { req } = context;
const secret = process.env.SECRET;
const token = await getToken({ req, secret });

return {
props: {
token: token
}
};
}

export default VLabs;
14 changes: 12 additions & 2 deletions vre-panel/templates/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@ import Link from 'next/link';
import { Menu, Transition } from '@headlessui/react';
import { useSession, signIn, signOut } from "next-auth/react"
import getConfig from 'next/config'
import {useContext} from "react";
import {PaasConfigContext} from "../context/PaasConfig";


const Nav = () => {

const { publicRuntimeConfig } = getConfig()
const { data: session, status } = useSession()

const {paasConfig, paasConfigLoading} = useContext(PaasConfigContext)

return (
<header className="sticky top-0 z-30 w-full px-2 py-4 bg-white sm:px-4 shadow-xl">
<nav className="bg-white border-gray-200 px-2 sm:px-4 py-2.5 rounded dark:bg-gray-800">
<nav className="bg-white border-gray-200 px-2 sm:px-4 rounded dark:bg-gray-800">
<div className="container flex flex-wrap justify-between items-center mx-auto">
<Link href='/' className="flex items-center">
<img src={`${publicRuntimeConfig.staticFolder}/LW_ERIC_Logo.png`} className="mr-3 h-6 h-14" alt="LifeWatch Logo" />
{paasConfigLoading || (
<img
src={paasConfig.site_icon}
alt="Site icon"
className="mr-3 h-16"
/>
)}
</Link>
<div className="flex items-center md:order-2">
<div className="relative inline-block text-left">
Expand Down
Empty file.
4 changes: 4 additions & 0 deletions vreapis/paas_configuration/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.contrib import admin
from .models import PaasConfiguration

admin.site.register(PaasConfiguration)
6 changes: 6 additions & 0 deletions vreapis/paas_configuration/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class PaasConfigurationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'paas_configuration'
Empty file.
17 changes: 17 additions & 0 deletions vreapis/paas_configuration/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.db import models

# Create your models here.


class PaasConfiguration(models.Model):
title = models.CharField(max_length=100, null=True)
description = models.TextField(null=True)
documentation_url = models.URLField(null=True, blank=True)
site_icon = models.TextField(
null=True,
help_text=("Base 64-encoded image, eg. data:image/png;base64,"
"ZXhhbXBsZQo="),
)

def __str__(self):
return self.title
Loading