182 lines
6.9 KiB
JavaScript
182 lines
6.9 KiB
JavaScript
const APP_ASSET_VERSION = window.__APP_ASSET_VERSION__
|
|
|| new URL(import.meta.url).searchParams.get('v')
|
|
|| 'dev';
|
|
|
|
let getRecipeListHTML;
|
|
let setupRecipeList;
|
|
let getFilterHTML;
|
|
let setupFilter;
|
|
let getRecipeDetailHTML;
|
|
let setupRecipeDetail;
|
|
let getMealPlannerHTML;
|
|
let setupMealPlanner;
|
|
let getPantryHTML;
|
|
let refreshPantry;
|
|
let setupPantry;
|
|
let getMealPlanEditorHTML;
|
|
let setupMealPlanEditor;
|
|
|
|
const moduleLoadPromise = Promise.all([
|
|
import(`./views/RecipeList.js?v=${APP_ASSET_VERSION}`),
|
|
import(`./views/Filter.js?v=${APP_ASSET_VERSION}`),
|
|
import(`./views/RecipeDetailV2.js?v=${APP_ASSET_VERSION}`),
|
|
import(`./views/MealPlanner.js?v=${APP_ASSET_VERSION}`),
|
|
import(`./views/Pantry.js?v=${APP_ASSET_VERSION}`),
|
|
import(`./ui/mealPlanEditor.js?v=${APP_ASSET_VERSION}`),
|
|
]).then(([
|
|
recipeListModule,
|
|
filterModule,
|
|
recipeDetailModule,
|
|
mealPlannerModule,
|
|
pantryModule,
|
|
mealPlanEditorModule,
|
|
]) => {
|
|
({ getRecipeListHTML, setupRecipeList } = recipeListModule);
|
|
({ getFilterHTML, setupFilter } = filterModule);
|
|
({ getRecipeDetailHTML, setupRecipeDetail } = recipeDetailModule);
|
|
({ getMealPlannerHTML, setupMealPlanner } = mealPlannerModule);
|
|
({ getPantryHTML, refreshPantry, setupPantry } = pantryModule);
|
|
({ getMealPlanEditorHTML, setupMealPlanEditor } = mealPlanEditorModule);
|
|
});
|
|
|
|
function getAppToastHTML() {
|
|
return `
|
|
<div id="app-toast" class="pointer-events-none absolute left-4 right-4 bottom-28 z-[60] opacity-0 translate-y-2 transition-all duration-300" role="status">
|
|
<div class="rounded-xl bg-gray-900 text-white text-sm font-medium px-4 py-3 shadow-lg text-center" id="app-toast-text"></div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function getBottomNavHTML() {
|
|
const isDark = document.documentElement.classList.contains('dark');
|
|
return `
|
|
<nav id="app-bottom-nav" class="absolute bottom-0 left-0 right-0 w-full bg-white border-t border-gray-200 flex justify-between px-1 py-2.5 pb-6 z-20 gap-0" aria-label="Główna nawigacja">
|
|
<button type="button" data-tab="recipes" id="nav-recipes" class="nav-tab flex flex-col items-center gap-0.5 text-black flex-1 min-w-0 max-w-[5.5rem]">
|
|
<i class="fas fa-book text-base" aria-hidden="true"></i>
|
|
<span class="text-[9px] font-medium leading-tight text-center">Przepisy</span>
|
|
</button>
|
|
<button type="button" data-tab="planner" id="nav-planner" class="nav-tab flex flex-col items-center gap-0.5 text-gray-500 hover:text-gray-700 flex-1 min-w-0 max-w-[5.5rem]">
|
|
<i class="far fa-calendar-alt text-base" aria-hidden="true"></i>
|
|
<span class="text-[9px] font-medium leading-tight text-center">Planer</span>
|
|
</button>
|
|
<button type="button" data-tab="pantry" id="nav-pantry" class="nav-tab flex flex-col items-center gap-0.5 text-gray-500 hover:text-gray-700 flex-1 min-w-0 max-w-[5.5rem]">
|
|
<i class="fas fa-warehouse text-base" aria-hidden="true"></i>
|
|
<span class="text-[9px] font-medium leading-tight text-center">Spiżarnia</span>
|
|
</button>
|
|
<button type="button" id="nav-theme-toggle" class="flex flex-col items-center gap-0.5 text-gray-500 hover:text-gray-700 flex-1 min-w-0 max-w-[5.5rem]" aria-label="Przełącz tryb ciemny/jasny">
|
|
<i class="${isDark ? 'fas fa-sun' : 'fas fa-moon'} text-base" aria-hidden="true"></i>
|
|
<span class="text-[9px] font-medium leading-tight text-center">${isDark ? 'Jasny' : 'Ciemny'}</span>
|
|
</button>
|
|
</nav>
|
|
`;
|
|
}
|
|
|
|
function setupThemeToggle() {
|
|
const btn = document.getElementById('nav-theme-toggle');
|
|
if (!btn) return;
|
|
|
|
btn.addEventListener('click', () => {
|
|
const html = document.documentElement;
|
|
const isDark = html.classList.toggle('dark');
|
|
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
|
|
const icon = btn.querySelector('i');
|
|
const label = btn.querySelector('span');
|
|
if (icon) icon.className = isDark ? 'fas fa-sun text-base' : 'fas fa-moon text-base';
|
|
if (label) label.textContent = isDark ? 'Jasny' : 'Ciemny';
|
|
|
|
const meta = document.querySelector('meta[name="theme-color"]');
|
|
if (meta) meta.setAttribute('content', isDark ? '#161513' : '#f3efe9');
|
|
});
|
|
}
|
|
|
|
function setupTabs() {
|
|
const main = document.getElementById('main-view');
|
|
const planner = document.getElementById('planner-view');
|
|
const pantry = document.getElementById('pantry-view');
|
|
const nav = document.getElementById('app-bottom-nav');
|
|
if (!main || !planner || !pantry || !nav) return;
|
|
|
|
const activeTab = 'nav-tab flex flex-col items-center gap-0.5 text-black flex-1 min-w-0 max-w-[5.5rem]';
|
|
const idleTab = 'nav-tab flex flex-col items-center gap-0.5 text-gray-500 hover:text-gray-700 flex-1 min-w-0 max-w-[5.5rem]';
|
|
|
|
const apply = (tab) => {
|
|
main.classList.toggle('hidden', tab !== 'recipes');
|
|
planner.classList.toggle('hidden', tab !== 'planner');
|
|
pantry.classList.toggle('hidden', tab !== 'pantry');
|
|
|
|
if (tab === 'pantry') refreshPantry();
|
|
|
|
nav.querySelectorAll('.nav-tab[data-tab]').forEach((btn) => {
|
|
const id = btn.getAttribute('data-tab');
|
|
if (btn.hasAttribute('disabled')) return;
|
|
if (id === 'recipes' || id === 'planner' || id === 'pantry') {
|
|
btn.className = id === tab ? activeTab : idleTab;
|
|
}
|
|
});
|
|
};
|
|
|
|
nav.addEventListener('click', (e) => {
|
|
const btn = e.target.closest('.nav-tab[data-tab]');
|
|
if (!btn || btn.hasAttribute('disabled')) return;
|
|
const tab = btn.getAttribute('data-tab');
|
|
if (tab === 'recipes' || tab === 'planner' || tab === 'pantry') apply(tab);
|
|
});
|
|
|
|
apply('recipes');
|
|
|
|
window.refreshStockViews = () => {
|
|
refreshPantry();
|
|
};
|
|
}
|
|
|
|
let initAppPromise = null;
|
|
|
|
async function initApp() {
|
|
if (initAppPromise) return initAppPromise;
|
|
|
|
initAppPromise = (async () => {
|
|
await moduleLoadPromise;
|
|
|
|
const appContainer = document.getElementById('app-container');
|
|
if (!appContainer) return;
|
|
|
|
appContainer.innerHTML = `
|
|
${getRecipeListHTML()}
|
|
${getMealPlannerHTML()}
|
|
${getPantryHTML()}
|
|
${getBottomNavHTML()}
|
|
${getRecipeDetailHTML()}
|
|
${getFilterHTML()}
|
|
${getMealPlanEditorHTML()}
|
|
${getAppToastHTML()}
|
|
`;
|
|
|
|
setupTabs();
|
|
setupThemeToggle();
|
|
setupRecipeList();
|
|
setupMealPlanner();
|
|
setupPantry();
|
|
setupFilter();
|
|
setupMealPlanEditor();
|
|
setupRecipeDetail();
|
|
})().catch((error) => {
|
|
initAppPromise = null;
|
|
throw error;
|
|
});
|
|
|
|
return initAppPromise;
|
|
}
|
|
|
|
function bootApp() {
|
|
initApp().catch((error) => {
|
|
console.error('Failed to initialize app', error);
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', bootApp, { once: true });
|
|
} else {
|
|
bootApp();
|
|
}
|