diff --git a/public/classroom_poc.html b/public/classroom_poc.html
index 704967d..edf0300 100644
--- a/public/classroom_poc.html
+++ b/public/classroom_poc.html
@@ -235,6 +235,19 @@
const desksContainer = document.getElementById('desksContainer');
const toastContainer = document.getElementById('toastContainer');
+ // Authentication utils
+ function getAuth() {
+ return {
+ token: localStorage.getItem('edu_token'),
+ user: JSON.parse(localStorage.getItem('edu_user') || 'null')
+ };
+ }
+
+ function isAuthenticated() {
+ const { token, user } = getAuth();
+ return token && user;
+ }
+
// COORDINATE HELPERS
// Positions are stored and transmitted as NORMALIZED values (0.0 – 1.0)
// representing a fraction of the canvas width/height.
@@ -414,6 +427,15 @@
// Join / Leave
function joinClassroom() {
+ if (!isAuthenticated()) {
+ showToast('Please sign in to join the virtual classroom.', 'warning');
+ // Preserve the user's intended destination so login.html can send
+ // them back here after authentication.
+ const returnTo = window.location.pathname + window.location.search;
+ sessionStorage.setItem('post_login_redirect', returnTo);
+ setTimeout(() => { window.location.href = '/login.html'; }, 800);
+ return;
+ }
intentionalDisconnect = false;
reconnectAttempts = 0;
isSeated = false;
diff --git a/public/index.html b/public/index.html
index 9be30f3..15fc516 100644
--- a/public/index.html
+++ b/public/index.html
@@ -153,9 +153,14 @@ Global Virtual Classroom
21 learners inside
-
- Join Lobby Classroom
-
+
diff --git a/public/js/layout.js b/public/js/layout.js
index e016c9c..182ffbe 100644
--- a/public/js/layout.js
+++ b/public/js/layout.js
@@ -178,4 +178,16 @@ document.addEventListener('DOMContentLoaded', async () => {
await inject('site-navbar', '/partials/navbar.html', updateAuthSection);
await inject('site-footer', '/partials/footer.html');
updateDarkModeIcon();
+});
+
+// Conditional rendering for the "Join Lobby Classroom" button.
+// These elements only exist on the homepage, so we guard against nulls.
+document.addEventListener('DOMContentLoaded', () => {
+ const loggedInEl = document.getElementById('join-logged-in');
+ const notLoggedInEl = document.getElementById('join-not-logged-in');
+ if (!loggedInEl || !notLoggedInEl) return;
+
+ const authed = isAuthenticated();
+ loggedInEl.style.display = authed ? 'flex' : 'none';
+ notLoggedInEl.style.display = authed ? 'none' : 'flex';
});
\ No newline at end of file
diff --git a/public/login.html b/public/login.html
index 336beeb..cd7ad04 100644
--- a/public/login.html
+++ b/public/login.html
@@ -256,11 +256,33 @@ Create your
: 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300');
}
-// Store authentication data
+// Store authentication data and handle post-login flow
function storeAuth(data) {
localStorage.setItem('edu_token', data.token);
localStorage.setItem('edu_user', JSON.stringify(data.user));
- window.location.href = '/dashboard.html';
+
+ // Show success toast
+ showToast('Redirecting...');
+
+ // Wait a few seconds then redirect
+ setTimeout(() => {
+ handlePostLoginRedirect();
+ }, 500);
+}
+
+// Show success toast message
+function showToast(msg) {
+ const toast = document.createElement('div');
+ toast.className = 'fixed top-4 right-4 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-lg p-4 text-green-700 dark:text-green-300 text-sm flex items-start gap-2 animate-pulse shadow-lg z-50';
+ const icon = document.createElement('i');
+ icon.className = 'fas fa-check-circle mt-0.5 flex-shrink-0';
+ const span = document.createElement('span');
+ span.textContent = msg;
+ toast.append(icon, span);
+ document.body.appendChild(toast);
+ setTimeout(() => {
+ toast.remove();
+ }, 3000);
}
// Show error message
@@ -274,12 +296,28 @@ Create your
}, 5000);
}
+// Handle redirect after successful login
+function handlePostLoginRedirect() {
+ const redirectUrl = sessionStorage.getItem('post_login_redirect');
+ sessionStorage.removeItem('post_login_redirect');
+ // Only honor same-origin, path-only redirects to prevent open-redirect abuse.
+ const safe = redirectUrl && /^\/(?!\/)/.test(redirectUrl);
+ window.location.href = safe ? redirectUrl : '/dashboard.html';
+}
+
// Handle tab parameter in URL
const params = new URLSearchParams(location.search);
if (params.get('tab') === 'register') {
switchTab('register');
}
+// Handle next parameter for post-login redirect.
+// URLSearchParams.get() already performs percent-decoding — no need to decode again.
+const nextParam = params.get('next');
+if (nextParam) {
+ sessionStorage.setItem('post_login_redirect', nextParam);
+}
+
// Login form submission
document.getElementById('form-login').addEventListener('submit', async e => {
e.preventDefault();