Mobile Optimizations

This commit is contained in:
Chris 2026-05-28 22:30:24 -04:00
parent 02f0ecb587
commit b9016991eb
4 changed files with 693 additions and 12 deletions

View file

@ -117,7 +117,22 @@ const elements = {
loadingOverlay: document.getElementById('loading-overlay'), loadingOverlay: document.getElementById('loading-overlay'),
loaderStepFetch: document.getElementById('loader-step-fetch'), loaderStepFetch: document.getElementById('loader-step-fetch'),
loaderStepParse: document.getElementById('loader-step-parse'), 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() { async function cloneActiveRecipe() {
if (!activeRecipe) return; if (!activeRecipe) return;
@ -646,7 +681,13 @@ function renderLibrary() {
let imgBlock = ''; let imgBlock = '';
if (recipe.image) { if (recipe.image) {
imgBlock = `<img src="${recipe.image}" class="recipe-card-img" alt="${recipe.title}" loading="lazy">`; imgBlock = `
<img src="${recipe.image}" class="recipe-card-img" alt="${recipe.title}" loading="lazy" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="recipe-card-img-placeholder" style="display: none;">
<i data-lucide="cooking-pot"></i>
<span>No dish photo</span>
</div>
`;
} else { } else {
imgBlock = ` imgBlock = `
<div class="recipe-card-img-placeholder"> <div class="recipe-card-img-placeholder">
@ -1027,8 +1068,10 @@ function initAuth() {
// Toggle Admin visibility // Toggle Admin visibility
if (currentUser.isAdmin) { if (currentUser.isAdmin) {
elements.tabUserAdmin.style.display = 'flex'; elements.tabUserAdmin.style.display = 'flex';
elements.mobileBtnAdmin.style.display = 'block';
} else { } else {
elements.tabUserAdmin.style.display = 'none'; elements.tabUserAdmin.style.display = 'none';
elements.mobileBtnAdmin.style.display = 'none';
} }
// Reset view to My Cookbook on init // Reset view to My Cookbook on init
@ -1073,8 +1116,10 @@ async function handleLogin(e) {
// Toggle Admin visibility // Toggle Admin visibility
if (currentUser.isAdmin) { if (currentUser.isAdmin) {
elements.tabUserAdmin.style.display = 'flex'; elements.tabUserAdmin.style.display = 'flex';
elements.mobileBtnAdmin.style.display = 'block';
} else { } else {
elements.tabUserAdmin.style.display = 'none'; elements.tabUserAdmin.style.display = 'none';
elements.mobileBtnAdmin.style.display = 'none';
} }
elements.loginForm.reset(); elements.loginForm.reset();
@ -1094,7 +1139,7 @@ function logout() {
elements.authModal.classList.add('active'); elements.authModal.classList.add('active');
elements.authModal.style.display = 'flex'; elements.authModal.style.display = 'flex';
elements.userBadgeContainer.style.display = 'none'; elements.userBadgeContainer.style.display = 'none';
elements.tabUserAdmin.style.display = 'none'; elements.mobileBtnAdmin.style.display = 'none';
allRecipes = []; allRecipes = [];
// Reset tabs visual state // Reset tabs visual state
@ -1119,6 +1164,11 @@ function switchTab(tabName) {
elements.tabUserAdmin.classList.remove('btn-primary'); elements.tabUserAdmin.classList.remove('btn-primary');
elements.tabUserAdmin.classList.add('btn-outline'); 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.recipesGrid.style.display = 'none';
elements.emptyState.style.display = 'none'; elements.emptyState.style.display = 'none';
elements.userAdminPanel.style.display = 'none'; elements.userAdminPanel.style.display = 'none';
@ -1126,19 +1176,33 @@ function switchTab(tabName) {
const filters = elements.librarySearch.closest('.filter-controls'); const filters = elements.librarySearch.closest('.filter-controls');
if (filters) filters.style.display = 'flex'; 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') { if (tabName === 'my-cookbook') {
elements.tabMyCookbook.classList.add('active'); elements.tabMyCookbook.classList.add('active');
elements.mobileTabCookbook.classList.add('active');
elements.recipesGrid.style.display = 'grid'; elements.recipesGrid.style.display = 'grid';
activeTab = 'my-cookbook'; activeTab = 'my-cookbook';
fetchRecipes(); fetchRecipes();
} else if (tabName === 'community-vault') { } else if (tabName === 'community-vault') {
elements.tabCommunityVault.classList.add('active'); elements.tabCommunityVault.classList.add('active');
elements.mobileTabCommunity.classList.add('active');
elements.recipesGrid.style.display = 'grid'; elements.recipesGrid.style.display = 'grid';
activeTab = 'community-vault'; activeTab = 'community-vault';
fetchRecipes(); fetchRecipes();
} else if (tabName === 'user-admin') { } else if (tabName === 'user-admin') {
elements.tabUserAdmin.classList.add('btn-primary'); elements.tabUserAdmin.classList.add('btn-primary');
elements.tabUserAdmin.classList.remove('btn-outline'); elements.tabUserAdmin.classList.remove('btn-outline');
elements.mobileTabProfile.classList.add('active');
elements.userAdminPanel.style.display = 'block'; elements.userAdminPanel.style.display = 'block';
if (filters) filters.style.display = 'none'; if (filters) filters.style.display = 'none';
activeTab = 'user-admin'; 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.`); 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();
}

View file

@ -16,12 +16,13 @@
<body class="dark-theme"> <body class="dark-theme">
<!-- Top Navigation --> <!-- Top Navigation -->
<header class="app-header"> <header class="app-header">
<div class="header-logo" style="cursor: pointer;" onclick="switchTab('my-cookbook')"> <div class="header-logo" style="cursor: pointer;" id="header-logo">
<div class="logo-icon"><i data-lucide="cooking-pot"></i></div> <div class="logo-icon"><i data-lucide="cooking-pot"></i></div>
<span class="logo-text">Fork<span>Folio</span></span> <span class="logo-text">Fork<span>Folio</span></span>
</div> </div>
<div class="header-actions"> <div class="header-actions">
<!-- Desktop Actions -->
<div class="user-badge-container" id="user-badge-container" style="display: none;"> <div class="user-badge-container" id="user-badge-container" style="display: none;">
<span class="user-display"><i data-lucide="user"></i> <span id="user-display-name">@chef</span></span> <span class="user-display"><i data-lucide="user"></i> <span id="user-display-name">@chef</span></span>
<button class="btn btn-icon-round" id="btn-settings" title="Application Settings"><i data-lucide="settings"></i></button> <button class="btn btn-icon-round" id="btn-settings" title="Application Settings"><i data-lucide="settings"></i></button>
@ -37,6 +38,22 @@
<i data-lucide="sun" class="sun-icon"></i> <i data-lucide="sun" class="sun-icon"></i>
<i data-lucide="moon" class="moon-icon"></i> <i data-lucide="moon" class="moon-icon"></i>
</button> </button>
<!-- Mobile Consolidated Header Group -->
<div class="mobile-header-group" id="mobile-header-group" style="display: none;">
<button class="mobile-header-tab active" id="mobile-header-tab-cookbook" title="My Cookbook">
<i data-lucide="book-open"></i>
</button>
<button class="mobile-header-tab" id="mobile-header-tab-community" title="Shared Cookbook">
<i data-lucide="globe"></i>
</button>
<button class="mobile-header-btn-add" id="mobile-header-btn-add" title="Add Recipe">
<i data-lucide="plus"></i>
</button>
<button class="mobile-header-tab" id="mobile-header-btn-profile" title="Chef Profile">
<i data-lucide="user"></i>
</button>
</div>
</div> </div>
</header> </header>
@ -74,7 +91,7 @@
<i data-lucide="book-open"></i> <span>My Cookbook</span> <i data-lucide="book-open"></i> <span>My Cookbook</span>
</button> </button>
<button class="dashboard-tab-btn" id="tab-community-vault"> <button class="dashboard-tab-btn" id="tab-community-vault">
<i data-lucide="globe"></i> <span>Community Vault</span> <i data-lucide="globe"></i> <span>Shared Cookbook</span>
</button> </button>
</div> </div>
@ -87,6 +104,9 @@
</div> </div>
</div> </div>
<!-- Dynamic Mobile Section Title -->
<h2 class="library-view-title" id="library-view-title" style="display: none;">My Cookbook</h2>
<!-- Recipes Grid --> <!-- Recipes Grid -->
<div class="recipes-grid" id="recipes-grid"> <div class="recipes-grid" id="recipes-grid">
<!-- Recipe cards will be loaded here dynamically --> <!-- Recipe cards will be loaded here dynamically -->
@ -95,7 +115,7 @@
<!-- Empty State --> <!-- Empty State -->
<div class="empty-state" id="empty-state"> <div class="empty-state" id="empty-state">
<div class="empty-icon"><i data-lucide="folder-open"></i></div> <div class="empty-icon"><i data-lucide="folder-open"></i></div>
<h3>Your vault is empty</h3> <h3>Your cookbook is empty</h3>
<p>Insert a cooking web link above or create one manually to build your digital recipe collections.</p> <p>Insert a cooking web link above or create one manually to build your digital recipe collections.</p>
</div> </div>
@ -193,7 +213,7 @@
<input type="checkbox" id="edit-is-public" checked> <input type="checkbox" id="edit-is-public" checked>
<span class="checkbox-label"><i data-lucide="globe" style="width: 14px; height: 14px; display: inline-block; vertical-align: middle; margin-right: 4px;"></i> Share with ForkFolio Community (Public)</span> <span class="checkbox-label"><i data-lucide="globe" style="width: 14px; height: 14px; display: inline-block; vertical-align: middle; margin-right: 4px;"></i> Share with ForkFolio Community (Public)</span>
</label> </label>
<p class="form-help text-sm opacity-80" style="margin-top: 4px; font-size: 0.8rem; color: var(--text-muted);">If enabled, this recipe will be shared to the global Community Vault feed for everyone to view and clone.</p> <p class="form-help text-sm opacity-80" style="margin-top: 4px; font-size: 0.8rem; color: var(--text-muted);">If enabled, this recipe will be shared to the global Shared Cookbook feed for everyone to view and clone.</p>
</div> </div>
<div class="form-grid-2"> <div class="form-grid-2">
@ -450,11 +470,11 @@
<label class="checkbox-container" style="align-items: flex-start;"> <label class="checkbox-container" style="align-items: flex-start;">
<input type="checkbox" id="delete-user-preserve-recipes" checked> <input type="checkbox" id="delete-user-preserve-recipes" checked>
<span class="checkbox-label" style="font-size: 0.9rem; line-height: 1.4; color: var(--text-main); font-weight: 500;"> <span class="checkbox-label" style="font-size: 0.9rem; line-height: 1.4; color: var(--text-main); font-weight: 500;">
<i data-lucide="globe" style="width: 14px; height: 14px; display: inline-block; vertical-align: middle; margin-right: 4px;"></i> Keep recipes in Community Vault? <i data-lucide="globe" style="width: 14px; height: 14px; display: inline-block; vertical-align: middle; margin-right: 4px;"></i> Keep recipes in Shared Cookbook?
</span> </span>
</label> </label>
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 6px; padding-left: 30px; margin-bottom: 0;"> <p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 6px; padding-left: 30px; margin-bottom: 0;">
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.
</p> </p>
</div> </div>
@ -542,6 +562,50 @@
</div> </div>
</div> </div>
</div> </div>
<!-- MOBILE SYSTEM COMPONENTS -->
<!-- Mobile Profile Drawer Sheet -->
<div class="modal-overlay" id="mobile-profile-modal">
<div class="modal-box max-width-xs mobile-profile-sheet">
<div class="bottom-sheet-handle"></div>
<div class="modal-header">
<h3>Chef Profile</h3>
<button class="close-btn" onclick="closeModal('mobile-profile-modal')"><i data-lucide="x"></i></button>
</div>
<div class="modal-body text-center" style="display: flex; flex-direction: column; align-items: center; gap: 16px;">
<div class="profile-avatar-large">
<i data-lucide="user"></i>
</div>
<div>
<h2 class="profile-username" id="mobile-profile-username" style="font-size: 1.6rem; color: var(--text-main); margin-bottom: 4px;">@chef</h2>
<span class="admin-role-badge chef" id="mobile-profile-role" style="font-size: 0.8rem; padding: 4px 10px; border-radius: var(--border-radius-full);">Chef Account</span>
</div>
<div class="mobile-profile-actions" style="width: 100%; display: flex; flex-direction: column; gap: 12px; margin-top: 8px;">
<button class="btn btn-outline btn-full-width" id="mobile-btn-theme-toggle" style="justify-content: flex-start; padding-left: 20px;">
<i data-lucide="sun" class="sun-icon"></i>
<i data-lucide="moon" class="moon-icon" style="display: none;"></i>
<span style="margin-left: 8px;">Toggle Theme</span>
</button>
<button class="btn btn-outline btn-full-width" id="mobile-btn-settings" style="justify-content: flex-start; padding-left: 20px;">
<i data-lucide="settings"></i>
<span style="margin-left: 8px;">Application Settings</span>
</button>
<button class="btn btn-outline btn-full-width" id="mobile-btn-admin" style="display: none; justify-content: flex-start; padding-left: 20px; border-color: var(--primary); color: var(--primary);">
<i data-lucide="shield"></i>
<span style="margin-left: 8px;">User Admin Panel</span>
</button>
<button class="btn btn-danger btn-full-width" id="mobile-btn-logout" style="justify-content: flex-start; padding-left: 20px; background-color: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.2); color: var(--danger);">
<i data-lucide="log-out"></i>
<span style="margin-left: 8px;">Log Out</span>
</button>
</div>
</div>
</div>
</div>
<script> <script>
// Initialize Lucide Icons initially // Initialize Lucide Icons initially

View file

@ -1884,3 +1884,511 @@ h1, h2, h3, h4, h5, h6 {
border: 1px dashed rgba(239, 68, 68, 0.35); border: 1px dashed rgba(239, 68, 68, 0.35);
color: #ef4444; color: #ef4444;
} }
/* =============================================================
MOBILE DESIGN AND NAVIGATION UPGRADES (HIGH FIDELITY UX)
============================================================= */
/* Layout adaptation variables */
:root {
--bottom-nav-height: 68px;
--fab-size: 56px;
}
/* Default Mobile Elements Hidden on Desktop */
.mobile-header-group,
.bottom-sheet-handle,
.profile-avatar-large {
display: none;
}
/* Mobile responsive media queries */
@media (max-width: 768px) {
/* Define layout safe offsets */
body {
padding-bottom: 0 !important;
}
/* Compact Header */
.app-header {
padding: 12px 16px;
height: 60px;
}
.logo-text {
font-size: 1.25rem;
}
.logo-icon {
width: 34px;
height: 34px;
}
/* Hide desktop action buttons in header */
.app-header #btn-manual-add,
.app-header .user-badge-container,
.app-header #tab-user-admin {
display: none !important;
}
/* Compact Hero */
.app-container {
padding: 16px 16px 40px 16px;
}
.scraping-hero {
margin-bottom: 24px;
padding: 0 8px;
}
.hero-content h1 {
font-size: 1.8rem;
line-height: 1.2;
}
.hero-content p {
font-size: 0.9rem;
margin-bottom: 16px;
}
.url-input-form {
flex-direction: column;
padding: 6px;
border-radius: var(--border-radius-md);
gap: 8px;
}
.url-input-form input {
padding: 10px 10px 10px 42px;
font-size: 0.9rem;
}
.hero-buttons-group {
width: 100%;
}
.hero-buttons-group button {
flex: 1;
height: 44px;
font-size: 0.9rem;
}
/* Hide desktop dashboard tab bar */
.library-header {
margin-bottom: 16px;
}
.dashboard-tabs {
display: none !important;
}
.search-wrapper {
width: 100%;
}
.search-wrapper input {
width: 100% !important;
height: 44px;
font-size: 0.95rem;
}
/* fluid grids */
.recipes-grid {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.recipe-card {
border-radius: var(--border-radius-md);
}
.recipe-card-content {
padding: 10px;
}
.recipe-card-origin {
font-size: 0.65rem;
margin-bottom: 2px;
}
.recipe-card-title {
font-size: 0.9rem;
margin-bottom: 4px;
line-height: 1.25;
}
.recipe-card-creator {
font-size: 0.65rem;
padding: 2px 6px;
margin-top: 2px;
margin-bottom: 4px;
}
.recipe-card-desc {
display: none !important;
}
.recipe-card-meta {
padding-top: 6px;
gap: 8px;
font-size: 0.7rem;
}
/* Bottom Navigation Styles */
.mobile-bottom-nav {
display: flex;
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
height: var(--bottom-nav-height);
background-color: var(--glass-bg);
border-top: 1px solid var(--border-color);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
z-index: 990;
justify-content: space-around;
align-items: center;
padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.08);
}
.mobile-tab-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: none;
border: none;
color: var(--text-muted);
font-family: var(--font-heading);
font-weight: 500;
font-size: 0.75rem;
gap: 4px;
cursor: pointer;
transition: all var(--transition-fast);
flex: 1;
height: 100%;
}
.mobile-tab-item i,
.mobile-tab-item svg {
width: 20px;
height: 20px;
transition: transform var(--transition-fast);
}
.mobile-tab-item:active i,
.mobile-tab-item:active svg {
transform: scale(0.88);
}
.mobile-tab-item.active {
color: var(--primary);
font-weight: 700;
}
/* Floating Action Button */
.fab-add-recipe {
display: flex;
position: fixed;
bottom: calc(var(--bottom-nav-height) + 16px + env(safe-area-inset-bottom));
right: 16px;
width: var(--fab-size);
height: var(--fab-size);
border-radius: var(--border-radius-full);
background: linear-gradient(135deg, var(--primary), var(--secondary));
box-shadow: 0 6px 20px var(--primary-glow);
border: none;
color: var(--text-white);
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 995;
transition: all var(--transition-fast);
}
.fab-add-recipe i,
.fab-add-recipe svg {
width: 24px;
height: 24px;
transition: transform var(--transition-fast);
}
.fab-add-recipe:active {
transform: scale(0.92);
box-shadow: 0 4px 12px var(--primary-glow);
}
/* Adaptive Bottom Sheet Modals */
.modal-overlay {
align-items: flex-end;
padding: 0;
}
.modal-box {
max-height: 88vh;
border-radius: var(--border-radius-lg) var(--border-radius-lg) 0 0;
border-bottom: none;
border-left: none;
border-right: none;
transform: translateY(100%);
transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.modal-overlay.active .modal-box {
transform: translateY(0);
}
/* Bottom Sheet Handle */
.bottom-sheet-handle {
display: block;
width: 40px;
height: 4px;
background-color: var(--border-color);
border-radius: var(--border-radius-full);
margin: 8px auto 0 auto;
flex-shrink: 0;
}
.modal-header {
padding: 12px 20px 16px 20px;
}
.modal-body {
padding: 16px 20px;
}
/* Restructure Detail Layout for Single-Column Bottom Sheet */
.modal-box.max-width-xl {
max-height: 92vh;
}
.detail-hero-banner {
height: 200px;
border-radius: 0;
}
.detail-main-content {
padding: 16px 20px;
}
.detail-header-info h2 {
font-size: 1.6rem;
}
.recipe-meta-tags {
flex-wrap: wrap;
gap: 8px;
}
.recipe-meta-tags span,
.recipe-meta-tags a#tag-source {
font-size: 0.8rem;
padding: 4px 10px;
}
.servings-scaler-panel {
flex-direction: column;
align-items: stretch;
gap: 12px;
padding: 12px;
background-color: var(--bg-app);
border-radius: var(--border-radius-md);
}
.cooking-mode-trigger {
width: 100%;
}
.detail-split-layout {
grid-template-columns: 1fr;
gap: 24px;
margin-top: 16px;
}
/* Interactive Cooking Mode adjustments */
.cooking-container {
padding: 16px;
}
.cooking-header {
padding-bottom: 12px;
}
.cooking-title-indicator {
font-size: 1.1rem;
max-width: 160px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cook-timer-panel {
gap: 4px;
}
.timer-display {
font-size: 1rem;
}
.timer-btn {
width: 28px;
height: 28px;
}
.cooking-stepper-box {
margin: 20px 0;
min-height: 240px;
padding: 24px 16px;
}
.cooking-step-text {
font-size: 1.35rem;
line-height: 1.45;
}
.cooking-controls {
flex-direction: column;
gap: 12px;
align-items: stretch;
}
.cooking-controls button {
width: 100%;
height: 48px;
}
.cooking-controls .step-dots {
justify-content: center;
order: -1;
margin-bottom: 8px;
}
/* Large Mobile Profile Style sheet */
.profile-avatar-large {
display: flex;
width: 72px;
height: 72px;
border-radius: var(--border-radius-full);
background: linear-gradient(135deg, var(--primary-glow), var(--border-color));
align-items: center;
justify-content: center;
color: var(--primary);
box-shadow: var(--shadow-sm);
}
.profile-avatar-large i,
.profile-avatar-large svg {
width: 32px;
height: 32px;
}
/* Tap animations */
.btn:active,
.recipe-card:active,
.image-option:active {
transform: scale(0.97);
}
.ingredient-checkbox-item {
min-height: 44px;
padding: 10px 12px;
}
/* Consolidated header navigation for mobile */
.mobile-header-group {
display: flex !important;
align-items: center;
gap: 6px;
}
.mobile-header-tab,
.mobile-header-btn-add {
width: 38px;
height: 38px;
border-radius: var(--border-radius-full);
border: none;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--glass-bg);
border: 1px solid var(--glass-border);
color: var(--text-muted);
cursor: pointer;
transition: all var(--transition-fast);
}
.mobile-header-tab i,
.mobile-header-tab svg,
.mobile-header-btn-add i,
.mobile-header-btn-add svg {
width: 16px;
height: 16px;
}
.mobile-header-tab:active,
.mobile-header-btn-add:active {
transform: scale(0.92);
}
.mobile-header-tab.active {
color: var(--primary);
background-color: var(--primary-glow);
border-color: hsla(var(--hue-primary), 95%, 52%, 0.25);
font-weight: 700;
}
.mobile-header-btn-add {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: var(--text-white);
border: none;
box-shadow: 0 4px 10px var(--primary-glow);
}
/* Library dynamic view header title */
.library-view-title {
display: block !important;
font-size: 1.45rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 18px;
font-family: var(--font-heading);
letter-spacing: -0.01em;
}
/* Prevent mobile recipe detail header button overlaps */
.detail-back-btn span,
#btn-detail-clone span {
display: none !important;
}
.detail-back-btn {
width: 40px !important;
height: 40px !important;
padding: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
border-radius: var(--border-radius-full) !important;
top: 16px !important;
left: 16px !important;
}
#btn-detail-clone {
width: 40px !important;
height: 40px !important;
padding: 0 !important;
border-radius: var(--border-radius-full) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
box-shadow: none !important;
}
.detail-actions {
top: 16px !important;
right: 16px !important;
gap: 8px !important;
}
}

View file

@ -355,7 +355,7 @@ app.delete('/api/admin/users/:id', requireAuth, (req, res) => {
let updatedRecipes; let updatedRecipes;
if (preserveRecipes) { if (preserveRecipes) {
// Keep in Community Vault (reassign to system_admin, set as public) // Keep in Shared Cookbook (reassign to system_admin, set as public)
updatedRecipes = recipes.map(r => { updatedRecipes = recipes.map(r => {
if (r.userId === deletedUser.id) { if (r.userId === deletedUser.id) {
r.userId = 'system_admin'; r.userId = 'system_admin';
@ -460,7 +460,7 @@ app.get('/api/recipes', requireAuth, (req, res) => {
res.json(userRecipes); res.json(userRecipes);
}); });
// API: List all public recipes for Community Vault // API: List all public recipes for Shared Cookbook
app.get('/api/recipes/public', requireAuth, (req, res) => { app.get('/api/recipes/public', requireAuth, (req, res) => {
const recipes = readRecipes(); const recipes = readRecipes();
const publicRecipes = recipes.filter(r => r.isPublic !== false); const publicRecipes = recipes.filter(r => r.isPublic !== false);