WIP: improve-UX-when-login-expired #36

Draft
brian wants to merge 2 commits from improve-UX-when-login-expired into main
13 changed files with 133 additions and 100 deletions
+6
View File
@@ -143,6 +143,12 @@ def serve_menu_js() -> FileResponse:
return FileResponse(static_path / "menu.js") return FileResponse(static_path / "menu.js")
@app.get("/auth-utils.js", include_in_schema=False)
def serve_auth_utils_js() -> FileResponse:
"""Serve the shared auth utilities JavaScript."""
return FileResponse(static_path / "auth-utils.js")
@app.get("/api/") @app.get("/api/")
def read_root(token: Annotated[str, Depends(verify_token)]) -> dict: def read_root(token: Annotated[str, Depends(verify_token)]) -> dict:
"""API root endpoint (requires authentication).""" """API root endpoint (requires authentication)."""
+2 -4
View File
@@ -211,12 +211,10 @@
</div> </div>
</div> </div>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated // Check if user is authenticated
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Get child ID from URL if editing // Get child ID from URL if editing
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
+16 -18
View File
@@ -424,35 +424,33 @@
</div> </div>
</div> </div>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated and is admin // Check if user is authenticated and is admin
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
let allUsers = []; let allUsers = [];
let userToDelete = null; let userToDelete = null;
// Verify user is admin and load data // Load data
fetch("/api/me", { fetch("/api/me", {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((user) => { .then((user) => {
if (!user.is_admin) { if (!user.is_admin) {
alert("Access denied. Admin privileges required."); alert("Access denied. Admin privileges required.");
window.location.href = "/"; window.location.href = "/";
} }
// Load users after verifying admin status // Load users after verifying admin status
loadUsers(); loadUsers();
}) })
.catch((error) => { .catch((error) => {
console.error("Error verifying admin status:", error); console.error("Error verifying admin status:", error);
window.location.href = "/login.html"; window.location.href = "/login.html";
}); });
async function loadUsers() { async function loadUsers() {
const container = document.getElementById("userListContainer"); const container = document.getElementById("userListContainer");
+21
View File
@@ -0,0 +1,21 @@
/**
* Redirect to login page with return URL
* @param {string} returnUrl - The URL to return to after login (defaults to current page)
*/
function redirectToLogin(returnUrl) {
const url = returnUrl || window.location.pathname + window.location.search;
window.location.href = `/login.html?return=${encodeURIComponent(url)}`;
}
/**
* Check if user has valid token, redirect to login if not
* @returns {string|null} - Returns token if valid, redirects to login if not
*/
function getToken() {
const token = localStorage.getItem("access_token");
if (!token) {
redirectToLogin();
return null;
}
return token;
}
+2 -4
View File
@@ -266,12 +266,10 @@
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<script src="/menu.js"></script> <script src="/menu.js"></script>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated // Check if user is authenticated
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Initialize burger menu // Initialize burger menu
initBurgerMenu({ includeHome: true }); initBurgerMenu({ includeHome: true });
+2 -4
View File
@@ -303,12 +303,10 @@
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<script src="/menu.js"></script> <script src="/menu.js"></script>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated // Check if user is authenticated
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Initialize burger menu // Initialize burger menu
initBurgerMenu({ includeHome: true }); initBurgerMenu({ includeHome: true });
+31 -43
View File
@@ -236,59 +236,47 @@
</div> </div>
<script src="/menu.js"></script> <script src="/menu.js"></script>
<script src="/auth-utils.js"></script>
<script> <script>
const token = localStorage.getItem("access_token"); const token = getToken();
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
// Check if user is logged in // Fetch current user info including admin status
if (!token) { fetch("/api/me", {
window.location.href = "/login.html"; headers: {
} else { Authorization: `Bearer ${token}`,
// Fetch current user info including admin status },
fetch("/api/me", { })
.then((response) => response.json())
.then((user) => {
if (!user) return; // Early exit if redirected
// Initialize burger menu
initBurgerMenu({
includeHome: true,
});
// Check if user has any children
return fetch("/api/children", {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}) })
.then((response) => { .then((response) => response.json())
if (response.ok) { .then((children) => {
return response.json(); // If no children, show welcome message with option to add
} else { if (children.length === 0) {
// Token invalid, redirect to login showNoChildrenMessage();
localStorage.removeItem("access_token"); return;
localStorage.removeItem("username");
window.location.href = "/login.html";
throw new Error("Authentication failed");
} }
})
.then((user) => {
// Initialize burger menu
initBurgerMenu({
includeHome: true,
});
// Check if user has any children // Load feeding status and show content
return fetch("/api/children", { loadFeedingStatus(children);
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((response) => response.json())
.then((children) => {
// If no children, show welcome message with option to add
if (children.length === 0) {
showNoChildrenMessage();
return;
}
// Load feeding status and show content
loadFeedingStatus(children);
});
})
.catch((error) => {
console.error("Error:", error);
}); });
} })
.catch((error) => {
console.error("Error:", error);
});
function showNoChildrenMessage() { function showNoChildrenMessage() {
const content = document.getElementById("content"); const content = document.getElementById("content");
+2 -4
View File
@@ -242,12 +242,10 @@
</form> </form>
</div> </div>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated // Check if user is authenticated
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Determine return URL based on referrer // Determine return URL based on referrer
function getReturnUrl() { function getReturnUrl() {
+2 -4
View File
@@ -199,12 +199,10 @@
</form> </form>
</div> </div>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated // Check if user is authenticated
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Determine return URL based on referrer // Determine return URL based on referrer
function getReturnUrl() { function getReturnUrl() {
+2 -4
View File
@@ -189,12 +189,10 @@
</form> </form>
</div> </div>
<script src="/auth-utils.js"></script>
<script> <script>
// Check if user is authenticated // Check if user is authenticated
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Determine return URL based on referrer // Determine return URL based on referrer
function getReturnUrl() { function getReturnUrl() {
+38 -2
View File
@@ -82,10 +82,36 @@
</div> </div>
<script> <script>
// Check if redirected due to expired session
const params = new URLSearchParams(window.location.search);
const returnUrl = params.get("return");
const form = document.getElementById("loginForm"); const form = document.getElementById("loginForm");
const messageDiv = document.getElementById("message"); const messageDiv = document.getElementById("message");
const loginButton = document.getElementById("loginButton"); const loginButton = document.getElementById("loginButton");
// Show session expired message if redirected from another page
if (returnUrl) {
showMessage("Your session has expired. Please log in again.", "error");
}
// Clear user-specific cache data on login
function clearUserCache() {
const keysToRemove = [
'lastDiaperChildFilter',
'lastFeedingChildFilter',
'lastSleepChildFilter',
'lastDiaperTimeRange',
'lastFeedingTimeRange'
];
keysToRemove.forEach(key => {
localStorage.removeItem(key);
});
console.log('Cleared user cache on login');
}
form.addEventListener("submit", async (e) => { form.addEventListener("submit", async (e) => {
e.preventDefault(); e.preventDefault();
@@ -109,13 +135,23 @@
if (response.ok) { if (response.ok) {
showMessage(data.message, "success"); showMessage(data.message, "success");
// Store token in localStorage // Store token in localStorage
localStorage.setItem("access_token", data.access_token); localStorage.setItem("access_token", data.access_token);
localStorage.setItem("username", data.username); localStorage.setItem("username", data.username);
// Clear stale user preferences from previous sessions
clearUserCache();
// Redirect based on is_admin from login response // Check for return URL parameter
const params = new URLSearchParams(window.location.search);
const returnUrl = params.get("return");
// Redirect based on return URL or is_admin from login response
setTimeout(() => { setTimeout(() => {
if (data.is_admin === true) { if (returnUrl) {
window.location.href = returnUrl;
} else if (data.is_admin === true) {
window.location.href = "/admin.html"; window.location.href = "/admin.html";
} else { } else {
window.location.href = "/"; window.location.href = "/";
+7 -7
View File
@@ -469,11 +469,9 @@
</div> </div>
<script src="/menu.js"></script> <script src="/menu.js"></script>
<script src="/auth-utils.js"></script>
<script> <script>
const token = localStorage.getItem("access_token"); const token = getToken();
if (!token) {
window.location.href = "/login.html";
}
// Initialize burger menu // Initialize burger menu
initBurgerMenu({ includeHome: true }); initBurgerMenu({ includeHome: true });
@@ -495,7 +493,7 @@
currentUser = await userResponse.json(); currentUser = await userResponse.json();
document.getElementById("username").value = currentUser.username; document.getElementById("username").value = currentUser.username;
} else { } else {
window.location.href = "/login.html"; redirectToLogin();
return; return;
} }
@@ -852,8 +850,10 @@
} }
}); });
// Load data on page load // Load data on page load (only if authenticated)
loadData(); if (token) {
loadData();
}
</script> </script>
</body> </body>
</html> </html>
+2 -6
View File
@@ -313,13 +313,9 @@
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<script src="/menu.js"></script> <script src="/menu.js"></script>
<script src="/auth-utils.js"></script>
<script> <script>
const token = localStorage.getItem("access_token"); const token = getToken();
// Check if user is logged in
if (!token) {
window.location.href = "/login.html";
}
// Initialize burger menu // Initialize burger menu
initBurgerMenu({ includeHome: true }); initBurgerMenu({ includeHome: true });