Skip to content

Commit

Permalink
Merge pull request #13 from Reptudn/12-dynamic-menu
Browse files Browse the repository at this point in the history
12 dynamic menu
  • Loading branch information
FreddyMSchubert authored Feb 26, 2025
2 parents 891c686 + 2765c6c commit 7185c4c
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 102 deletions.
36 changes: 28 additions & 8 deletions back/raw/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,28 +126,48 @@ app.setNotFoundHandler((request, reply) => {
/* --------------STATIC------------- */
/* --------------------------------- */

app.get('/partial/:page', async (req: any, reply: any) => {
async function checkAuth(request: any): Promise<boolean> {
try {
await request.jwtVerify();
return true;
} catch (error) {
return false;
}
}

app.get('/partial/pages/:page', async (req: any, reply: any) => {
const page = req.params.page;
const loadpartial = req.headers['loadpartial'] === 'true';
const dataSample = { name: 'Jonas' };
const layoutOption = loadpartial ? false : 'basic.ejs';
const isAuthenticated = await checkAuth(req);

if (page === 'game') {
try {
await req.jwtVerify();
} catch (error) {
return reply.code(401).view('pages/no_access.ejs', dataSample, { layout: layoutOption });
return reply.code(401).view('partial/pages/no_access.ejs', { name: 'Freddy', isAuthenticated }, { layout: layoutOption });
}
}
return reply.view(`pages/${page}.ejs`, dataSample, { layout: layoutOption });
return reply.view(`partial/pages/${page}.ejs`, { name: 'Freddy', isAuthenticated }, { layout: layoutOption });
});
app.get('/menu', async (req: any, reply: any) => {
const isAuthenticated = await checkAuth(req);
const menuTemplate = isAuthenticated ? 'partial/menu/loggedin.ejs' : 'partial/menu/guest.ejs';
return reply.view(menuTemplate, { name: 'Freddy' });
});
app.get('/', async (req: any, reply: any) => {
logger.info('GET /');
return reply.view('pages/index.ejs', { name: 'Jonas' }, {
layout: 'basic.ejs'
});
let isAuthenticated: boolean = false;
try {
await req.jwtVerify();
isAuthenticated = true;
}
catch (error) {
isAuthenticated = false;
}
return reply.view('partial/pages/index.ejs', { name: 'Jonas', isAuthenticated }, { layout: 'basic.ejs' });
});


startServer();

export { app };
5 changes: 5 additions & 0 deletions front/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,9 @@ body.dark .text-gray-700 {
body.dark .bg-white {
background-color: #2d3748;
/* dark background for dark mode */
}

button.active {
background-color: #0056b3;
border: 2px solid #fff;
}
26 changes: 8 additions & 18 deletions front/layouts/basic.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,14 @@
<header class="bg-blue-600 text-white p-4 shadow-md">
<div class="container mx-auto flex justify-between items-center">
<h1 class="text-2xl font-bold" onclick="loadPartialView('index')">Welcome to Transcendence</h1>
<nav class="space-x-4">
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('index')">Home</button>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('profile')">Profile</button>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('game')">Game</button>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('login')">Login</button>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('register')">Register</button>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="localStorage.setItem('token', ''); loadPartialView('index');">Logout</button>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('about')">About</button>
<button id="darkModeToggle" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"
onclick="toggleDarkModeT()">Dark Mode</button>
</nav>
<div id="menu">
<% if (!isAuthenticated) { %>
<%- include('partial/menu/guest.ejs') %>
<% } else { %>
<%- include('partial/menu/loggedin.ejs') %>
<% } %>
</div>

</div>
</header>

Expand Down
8 changes: 8 additions & 0 deletions front/layouts/partial/menu/guest.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<nav class="space-x-4">
<button data-page="login" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('login')">Login</button>
<button data-page="register" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('register')">Register</button>
<button data-page="about" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('about')">About</button>
</nav>
12 changes: 12 additions & 0 deletions front/layouts/partial/menu/loggedin.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<nav class="space-x-4">
<button data-page="index" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('index')">Home</button>
<button data-page="profile" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('profile')">Profile</button>
<button data-page="game" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('game')">Game</button>
<button data-page="logout" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="localStorage.setItem('token', ''); updateMenu(); loadPartialView('index');">Logout</button>
<button data-page="about" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onclick="loadPartialView('about')">About</button>
</nav>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion front/raw/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ async function loginAction() {
body: JSON.stringify({ username, password })
});
const data = await response.json();
console.log("cool data received: " + JSON.stringify(data));
if (response.ok) {
localStorage.setItem("token", data.token);
updateMenu();
loadPartialView('game');
alert('You have logged in successfully');
} else {
Expand Down
142 changes: 67 additions & 75 deletions front/raw/script.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,85 @@
async function loadPartialView(page: string, pushState: boolean = true): Promise<void> {
const response: Response = await fetch(`/partial/${page}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'loadpartial': 'true'
function updateActiveMenu(selectedPage: string): void {
const menuButtons = document.querySelectorAll('nav button[data-page]');
menuButtons.forEach((button) => {
if (button.getAttribute('data-page') === selectedPage) {
button.classList.add('active');
} else {
button.classList.remove('active');
}
});
const html: string = await response.text();
console.log(`Switching to page: ${page}`);
}

const contentElement: HTMLElement | null = document.getElementById('content');
if (contentElement) {
contentElement.innerHTML = html;
const scripts = contentElement.querySelectorAll('script');
scripts.forEach(oldScript => {
const newScript = document.createElement('script');
newScript.type = oldScript.type || 'text/javascript';
if (oldScript.src) {
newScript.src = oldScript.src + '?cb=' + Date.now(); // refresh script, force cache break
} else {
newScript.textContent = oldScript.textContent;
}
document.body.appendChild(newScript);
});
} else {
console.warn("Content element not found");
async function loadPartialView(page: string, pushState: boolean = true): Promise<void> {
const token = localStorage.getItem('token');
const headers: Record<string, string> = { 'loadpartial': 'true' };
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
try {
const response: Response = await fetch(`/partial/pages/${page}`, {
method: 'GET',
headers
});

if (pushState) {
history.pushState({ page }, '', `/partial/${page}`);
const html: string = await response.text();
console.log(`Switching to page: ${page}`);

const contentElement: HTMLElement | null = document.getElementById('content');
if (contentElement) {
contentElement.innerHTML = html;
const scripts = contentElement.querySelectorAll('script');
scripts.forEach(oldScript => {
const newScript = document.createElement('script');
newScript.type = oldScript.type || 'text/javascript';
if (oldScript.src) {
newScript.src = oldScript.src + '?cb=' + Date.now(); // refresh script, force cache break
} else {
newScript.textContent = oldScript.textContent;
}
document.body.appendChild(newScript);
});
} else {
console.warn("Content element not found");
}

if (pushState) {
history.pushState({ page }, '', `/partial/pages/${page}`);
}

updateActiveMenu(page);
} catch (error) {
console.error('Error fetching partial view:', error);
}
}

// history change event
window.addEventListener('popstate', (event: PopStateEvent) => {
if (event.state && typeof event.state.page === 'string') {
loadPartialView(event.state.page, false);
}
});

let isDarkModeT: boolean = false;
window.addEventListener('DOMContentLoaded', () => {
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
toggleDarkMode(prefersDarkScheme.matches);
isDarkModeT = prefersDarkScheme.matches;

prefersDarkScheme.addEventListener('change', (event) => {
toggleDarkMode(event.matches);
isDarkModeT = event.matches;
});
});

function toggleDarkMode(isDarkMode: boolean = isDarkModeT): void {
if (isDarkMode) {
document.body.classList.add('dark');
isDarkModeT = true;
} else {
document.body.classList.remove('dark');
isDarkModeT = false;
}

const darkModeToggle: HTMLElement | null = document.getElementById('darkModeToggle');
if (darkModeToggle) {
darkModeToggle.textContent = isDarkModeT ? 'Light Mode' : 'Dark Mode';
} else {
console.warn("Dark mode toggle element not found");
}
}

function toggleDarkModeT(): void {
isDarkModeT = !isDarkModeT;
toggleDarkMode();
}

function setCookie(name: string, value: string, days: number): void {
const date: Date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires: string = "expires=" + date.toUTCString();
document.cookie = `${name}=${value};${expires};path=/`;
}
async function updateMenu(): Promise<void> {
try {
let response: Response;
const token = localStorage.getItem('token');
if (token) {
response = await fetch('/menu', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
}
});
} else {
response = await fetch(`/menu`);
}

function getCookie(name: string): string | null {
// A simple implementation might be:
const nameEQ: string = name + "=";
const ca: string[] = document.cookie.split(';');
for (let c of ca) {
c = c.trim();
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length);
const html = await response.text();
const menuElement = document.getElementById('menu');
if (menuElement) {
menuElement.innerHTML = html;
}
} catch (error) {
console.error('Menu fetch failed:', error);
}
return null;
}

// THE number
Expand Down

0 comments on commit 7185c4c

Please sign in to comment.