From b9016991eb532480d8911173e2d21244cbd5cfa7 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 28 May 2026 22:30:24 -0400 Subject: [PATCH] Mobile Optimizations --- public/app.js | 117 ++++++++++- public/index.html | 76 ++++++- public/style.css | 508 ++++++++++++++++++++++++++++++++++++++++++++++ server.js | 4 +- 4 files changed, 693 insertions(+), 12 deletions(-) diff --git a/public/app.js b/public/app.js index 6350209..ccdaba4 100644 --- a/public/app.js +++ b/public/app.js @@ -117,7 +117,22 @@ const elements = { loadingOverlay: document.getElementById('loading-overlay'), loaderStepFetch: document.getElementById('loader-step-fetch'), loaderStepParse: document.getElementById('loader-step-parse'), - loaderStepImages: document.getElementById('loader-step-images') + loaderStepImages: document.getElementById('loader-step-images'), + + // Mobile Components + fabAddRecipe: document.getElementById('mobile-header-btn-add'), + mobileTabCookbook: document.getElementById('mobile-header-tab-cookbook'), + mobileTabCommunity: document.getElementById('mobile-header-tab-community'), + mobileTabProfile: document.getElementById('mobile-header-btn-profile'), + mobileProfileModal: document.getElementById('mobile-profile-modal'), + mobileProfileUsername: document.getElementById('mobile-profile-username'), + mobileProfileRole: document.getElementById('mobile-profile-role'), + mobileBtnThemeToggle: document.getElementById('mobile-btn-theme-toggle'), + mobileBtnSettings: document.getElementById('mobile-btn-settings'), + mobileBtnAdmin: document.getElementById('mobile-btn-admin'), + mobileBtnLogout: document.getElementById('mobile-btn-logout'), + headerLogo: document.getElementById('header-logo'), + libraryViewTitle: document.getElementById('library-view-title') }; // ------------------------------------------------------------- @@ -211,6 +226,26 @@ function registerEvents() { } } }); + + // Mobile UI Events + elements.mobileTabCookbook.addEventListener('click', () => switchTab('my-cookbook')); + elements.mobileTabCommunity.addEventListener('click', () => switchTab('community-vault')); + elements.mobileTabProfile.addEventListener('click', openMobileProfile); + elements.headerLogo.addEventListener('click', () => switchTab('my-cookbook')); + elements.fabAddRecipe.addEventListener('click', handleFabClick); + elements.mobileBtnThemeToggle.addEventListener('click', toggleTheme); + elements.mobileBtnSettings.addEventListener('click', () => { + closeModal('mobile-profile-modal'); + openSettingsModal(); + }); + elements.mobileBtnAdmin.addEventListener('click', () => { + closeModal('mobile-profile-modal'); + switchTab('user-admin'); + }); + elements.mobileBtnLogout.addEventListener('click', () => { + closeModal('mobile-profile-modal'); + logout(); + }); } // ------------------------------------------------------------- @@ -472,7 +507,7 @@ async function deleteActiveRecipe() { } } -// Clone a Community Vault recipe to current user's collection +// Clone a Shared Cookbook recipe to current user's collection async function cloneActiveRecipe() { if (!activeRecipe) return; @@ -646,7 +681,13 @@ function renderLibrary() { let imgBlock = ''; if (recipe.image) { - imgBlock = `${recipe.title}`; + imgBlock = ` + ${recipe.title} + + `; } else { imgBlock = `
@@ -1027,8 +1068,10 @@ function initAuth() { // Toggle Admin visibility if (currentUser.isAdmin) { elements.tabUserAdmin.style.display = 'flex'; + elements.mobileBtnAdmin.style.display = 'block'; } else { elements.tabUserAdmin.style.display = 'none'; + elements.mobileBtnAdmin.style.display = 'none'; } // Reset view to My Cookbook on init @@ -1073,8 +1116,10 @@ async function handleLogin(e) { // Toggle Admin visibility if (currentUser.isAdmin) { elements.tabUserAdmin.style.display = 'flex'; + elements.mobileBtnAdmin.style.display = 'block'; } else { elements.tabUserAdmin.style.display = 'none'; + elements.mobileBtnAdmin.style.display = 'none'; } elements.loginForm.reset(); @@ -1094,7 +1139,7 @@ function logout() { elements.authModal.classList.add('active'); elements.authModal.style.display = 'flex'; elements.userBadgeContainer.style.display = 'none'; - elements.tabUserAdmin.style.display = 'none'; + elements.mobileBtnAdmin.style.display = 'none'; allRecipes = []; // Reset tabs visual state @@ -1119,6 +1164,11 @@ function switchTab(tabName) { elements.tabUserAdmin.classList.remove('btn-primary'); elements.tabUserAdmin.classList.add('btn-outline'); + // Mobile Tab Bar sync + elements.mobileTabCookbook.classList.remove('active'); + elements.mobileTabCommunity.classList.remove('active'); + elements.mobileTabProfile.classList.remove('active'); + elements.recipesGrid.style.display = 'none'; elements.emptyState.style.display = 'none'; elements.userAdminPanel.style.display = 'none'; @@ -1126,19 +1176,33 @@ function switchTab(tabName) { const filters = elements.librarySearch.closest('.filter-controls'); if (filters) filters.style.display = 'flex'; + // Update Dynamic Mobile Header Title + if (elements.libraryViewTitle) { + if (tabName === 'my-cookbook') { + elements.libraryViewTitle.textContent = 'My Cookbook'; + } else if (tabName === 'community-vault') { + elements.libraryViewTitle.textContent = 'Shared Cookbook'; + } else if (tabName === 'user-admin') { + elements.libraryViewTitle.textContent = 'User Administration'; + } + } + if (tabName === 'my-cookbook') { elements.tabMyCookbook.classList.add('active'); + elements.mobileTabCookbook.classList.add('active'); elements.recipesGrid.style.display = 'grid'; activeTab = 'my-cookbook'; fetchRecipes(); } else if (tabName === 'community-vault') { elements.tabCommunityVault.classList.add('active'); + elements.mobileTabCommunity.classList.add('active'); elements.recipesGrid.style.display = 'grid'; activeTab = 'community-vault'; fetchRecipes(); } else if (tabName === 'user-admin') { elements.tabUserAdmin.classList.add('btn-primary'); elements.tabUserAdmin.classList.remove('btn-outline'); + elements.mobileTabProfile.classList.add('active'); elements.userAdminPanel.style.display = 'block'; if (filters) filters.style.display = 'none'; activeTab = 'user-admin'; @@ -1654,3 +1718,48 @@ async function scanRecipeFile(file) { alert(`Scanning Failed: ${error.message}\n\nPlease verify your internet connection and check that your Gemini API Key is correct.`); } } + +// ------------------------------------------------------------- +// MOBILE PORTAL CONTROLS & GESTURES +// ------------------------------------------------------------- + +function openMobileProfile() { + if (!currentUser) return; + + elements.mobileProfileUsername.textContent = `@${currentUser.username}`; + + // Set role text + let roleText = 'Chef Account'; + if (currentUser.isAdmin) { + roleText = 'Administrator'; + } else if (currentUser.isRecipeManager) { + roleText = 'Recipe Manager'; + } + elements.mobileProfileRole.textContent = roleText; + + // Handle theme icon inside profile drawer + const isDark = document.body.classList.contains('dark-theme'); + const sunIcon = elements.mobileBtnThemeToggle.querySelector('.sun-icon'); + const moonIcon = elements.mobileBtnThemeToggle.querySelector('.moon-icon'); + if (sunIcon && moonIcon) { + if (isDark) { + sunIcon.style.display = 'block'; + moonIcon.style.display = 'none'; + } else { + sunIcon.style.display = 'none'; + moonIcon.style.display = 'block'; + } + } + + openModal('mobile-profile-modal'); +} + +function handleFabClick() { + // If not on cookbook tab, switch back to list first + if (activeTab !== 'my-cookbook') { + switchTab('my-cookbook'); + } + // Open the manual recipe creator + openManualAdd(); +} + diff --git a/public/index.html b/public/index.html index 860f728..5bf5724 100644 --- a/public/index.html +++ b/public/index.html @@ -16,12 +16,13 @@
-
@@ -74,7 +91,7 @@ My Cookbook
@@ -87,6 +104,9 @@ + + +
@@ -95,7 +115,7 @@
-

Your vault is empty

+

Your cookbook is empty

Insert a cooking web link above or create one manually to build your digital recipe collections.

@@ -193,7 +213,7 @@ Share with ForkFolio Community (Public) -

If enabled, this recipe will be shared to the global Community Vault feed for everyone to view and clone.

+

If enabled, this recipe will be shared to the global Shared Cookbook feed for everyone to view and clone.

@@ -450,11 +470,11 @@

- If enabled, this chef's private recipes will be transferred to a public archive in the Community Vault. If disabled, all recipes owned by this chef will be purged. + If enabled, this chef's private recipes will be transferred to a public archive in the Shared Cookbook. If disabled, all recipes owned by this chef will be purged.

@@ -542,6 +562,50 @@ + + + +