Redesign meal planner
This commit is contained in:
36
index.html
36
index.html
@@ -182,24 +182,6 @@
|
|||||||
box-shadow: var(--panel-shadow);
|
box-shadow: var(--panel-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
#planner-summary-card {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
background: linear-gradient(145deg, rgba(var(--surface-rgb), 0.98) 0%, rgba(var(--surface-soft-rgb), 0.98) 100%) !important;
|
|
||||||
border-color: rgba(var(--warm-rgb), 0.26) !important;
|
|
||||||
box-shadow: var(--panel-shadow) !important;
|
|
||||||
}
|
|
||||||
#planner-summary-card::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: -20% auto auto -8%;
|
|
||||||
width: 9rem;
|
|
||||||
height: 9rem;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: rgba(var(--warm-rgb), 0.14);
|
|
||||||
filter: blur(44px);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.bg-amber-50 { background-color: rgba(var(--warm-rgb), 0.12) !important; }
|
.bg-amber-50 { background-color: rgba(var(--warm-rgb), 0.12) !important; }
|
||||||
.bg-amber-50\/30 { background-color: rgba(var(--warm-rgb), 0.09) !important; }
|
.bg-amber-50\/30 { background-color: rgba(var(--warm-rgb), 0.09) !important; }
|
||||||
.bg-amber-50\/50 { background-color: rgba(var(--warm-rgb), 0.14) !important; }
|
.bg-amber-50\/50 { background-color: rgba(var(--warm-rgb), 0.14) !important; }
|
||||||
@@ -246,6 +228,15 @@
|
|||||||
#pantry-view {
|
#pantry-view {
|
||||||
background: rgb(var(--app-bg-rgb)) !important;
|
background: rgb(var(--app-bg-rgb)) !important;
|
||||||
}
|
}
|
||||||
|
#planner-view,
|
||||||
|
#planner-view > div:first-child,
|
||||||
|
#planner-scroll,
|
||||||
|
#calendar-swipe-zone,
|
||||||
|
#calendar-week-wrap,
|
||||||
|
#calendar-month-wrap,
|
||||||
|
#planner-meal-slots {
|
||||||
|
background: #2d2e2b !important;
|
||||||
|
}
|
||||||
#main-view > div:first-child,
|
#main-view > div:first-child,
|
||||||
#planner-view > div:first-child,
|
#planner-view > div:first-child,
|
||||||
#pantry-view > div:first-child,
|
#pantry-view > div:first-child,
|
||||||
@@ -283,7 +274,6 @@
|
|||||||
}
|
}
|
||||||
#planner-picker-sheet,
|
#planner-picker-sheet,
|
||||||
#planner-ing-sheet,
|
#planner-ing-sheet,
|
||||||
#planner-copy-sheet,
|
|
||||||
#pv2-edit-sheet,
|
#pv2-edit-sheet,
|
||||||
#mpe-sheet,
|
#mpe-sheet,
|
||||||
#recipe-detail-view > div:last-child {
|
#recipe-detail-view > div:last-child {
|
||||||
@@ -303,7 +293,6 @@
|
|||||||
}
|
}
|
||||||
#planner-picker-backdrop,
|
#planner-picker-backdrop,
|
||||||
#planner-ing-backdrop,
|
#planner-ing-backdrop,
|
||||||
#planner-copy-backdrop,
|
|
||||||
#pv2-edit-bg,
|
#pv2-edit-bg,
|
||||||
#mpe-overlay {
|
#mpe-overlay {
|
||||||
background: rgba(7, 6, 5, 0.42) !important;
|
background: rgba(7, 6, 5, 0.42) !important;
|
||||||
@@ -313,13 +302,11 @@
|
|||||||
/* Planner and common interactive surfaces */
|
/* Planner and common interactive surfaces */
|
||||||
#planner-open-ingredients,
|
#planner-open-ingredients,
|
||||||
.planner-pick-recipe,
|
.planner-pick-recipe,
|
||||||
.planner-copy-target,
|
|
||||||
#mpe-nutrition-section > div,
|
#mpe-nutrition-section > div,
|
||||||
#mpe-add-area > div,
|
#mpe-add-area > div,
|
||||||
#pv2-edit-nutrition ul {
|
#pv2-edit-nutrition ul {
|
||||||
box-shadow: var(--panel-shadow) !important;
|
box-shadow: var(--panel-shadow) !important;
|
||||||
}
|
}
|
||||||
.planner-copy-target,
|
|
||||||
.planner-pick-recipe,
|
.planner-pick-recipe,
|
||||||
.pv2-chip,
|
.pv2-chip,
|
||||||
.pv2-cat-chip,
|
.pv2-cat-chip,
|
||||||
@@ -329,8 +316,7 @@
|
|||||||
#rd-tags span {
|
#rd-tags span {
|
||||||
transition: transform 160ms ease, background-color 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease;
|
transition: transform 160ms ease, background-color 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease;
|
||||||
}
|
}
|
||||||
.planner-pick-recipe:hover,
|
.planner-pick-recipe:hover { transform: translateY(-1px); }
|
||||||
.planner-copy-target:hover { transform: translateY(-1px); }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="m-0 min-h-dvh bg-white text-gray-800 antialiased">
|
<body class="m-0 min-h-dvh bg-white text-gray-800 antialiased">
|
||||||
@@ -349,6 +335,6 @@
|
|||||||
navigator.serviceWorker.register('./sw.js', { scope: './' }).catch(() => {});
|
navigator.serviceWorker.register('./sw.js', { scope: './' }).catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="module" src="js/app.js?v=2"></script>
|
<script type="module" src="js/app.js?v=18"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getRecipeListHTML, setupRecipeList } from './views/RecipeList.js?v=2';
|
import { getRecipeListHTML, setupRecipeList } from './views/RecipeList.js?v=2';
|
||||||
import { getFilterHTML, setupFilter } from './views/Filter.js?v=2';
|
import { getFilterHTML, setupFilter } from './views/Filter.js?v=2';
|
||||||
import { getRecipeDetailHTML, setupRecipeDetail } from './views/RecipeDetailV2.js?v=2';
|
import { getRecipeDetailHTML, setupRecipeDetail } from './views/RecipeDetailV2.js?v=2';
|
||||||
import { getMealPlannerHTML, setupMealPlanner } from './views/MealPlanner.js?v=4';
|
import { getMealPlannerHTML, setupMealPlanner } from './views/MealPlanner.js?v=20';
|
||||||
import { getPantryHTML, refreshPantry, setupPantry } from './views/Pantry.js?v=2';
|
import { getPantryHTML, refreshPantry, setupPantry } from './views/Pantry.js?v=2';
|
||||||
import { getMealPlanEditorHTML, setupMealPlanEditor } from './ui/mealPlanEditor.js?v=7';
|
import { getMealPlanEditorHTML, setupMealPlanEditor } from './ui/mealPlanEditor.js?v=7';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { INGREDIENTS, RECIPES } from '../data/catalog.js?v=2';
|
import { INGREDIENTS, RECIPES } from '../data/catalog.js?v=2';
|
||||||
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||||
import {
|
import {
|
||||||
addDays,
|
|
||||||
addMonths,
|
addMonths,
|
||||||
addWeeks,
|
addWeeks,
|
||||||
sameMonth,
|
sameMonth,
|
||||||
@@ -61,19 +60,20 @@ function syncTodayButton(mode, weekStart, monthAnchor, selected) {
|
|||||||
export function getMealPlannerHTML() {
|
export function getMealPlannerHTML() {
|
||||||
return `
|
return `
|
||||||
<div id="planner-view" class="hidden flex flex-col h-full absolute inset-0 overflow-hidden bg-[#2d2e2b] z-10 pb-24">
|
<div id="planner-view" class="hidden flex flex-col h-full absolute inset-0 overflow-hidden bg-[#2d2e2b] z-10 pb-24">
|
||||||
<div class="shrink-0 bg-white border-b border-gray-200 mt-3">
|
<div class="shrink-0 bg-[#2d2e2b] border-b border-[#444442] mt-3">
|
||||||
${createCalendarTopbarHTML({
|
${createCalendarTopbarHTML({
|
||||||
titleId: 'cal-period-label',
|
titleId: 'cal-period-label',
|
||||||
prevId: 'cal-prev',
|
prevId: 'cal-prev',
|
||||||
todayId: 'cal-go-today',
|
todayId: 'cal-go-today',
|
||||||
nextId: 'cal-next',
|
nextId: 'cal-next',
|
||||||
|
titleClass: 'text-[18px] font-semibold text-[#ddd6ca] leading-none tracking-[-0.03em]',
|
||||||
})}
|
})}
|
||||||
<div id="calendar-swipe-zone" class="overflow-x-hidden" style="touch-action: none">
|
<div id="calendar-swipe-zone" class="overflow-x-hidden bg-[#2d2e2b]" style="touch-action: none">
|
||||||
<div id="calendar-week-wrap" class="px-3 overflow-x-hidden" style="overflow: hidden; max-height: 10rem; opacity: 1; padding-bottom: 0.75rem">
|
<div id="calendar-week-wrap" class="px-3 overflow-x-hidden bg-[#2d2e2b]" style="overflow: hidden; max-height: 10rem; opacity: 1; padding-bottom: 0.75rem">
|
||||||
${createCalendarWeekdayHeaderHTML()}
|
${createCalendarWeekdayHeaderHTML()}
|
||||||
<div id="calendar-week-grid" class="grid grid-cols-7 gap-1.5 max-w-full overflow-x-hidden"></div>
|
<div id="calendar-week-grid" class="grid grid-cols-7 gap-1.5 max-w-full overflow-x-hidden"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="calendar-month-wrap" class="px-3" style="overflow: hidden; max-height: 0; opacity: 0; padding-bottom: 0">
|
<div id="calendar-month-wrap" class="px-3 bg-[#2d2e2b]" style="overflow: hidden; max-height: 0; opacity: 0; padding-bottom: 0">
|
||||||
${createCalendarWeekdayHeaderHTML()}
|
${createCalendarWeekdayHeaderHTML()}
|
||||||
<div id="calendar-month-grid" class="grid grid-cols-7 gap-1.5"></div>
|
<div id="calendar-month-grid" class="grid grid-cols-7 gap-1.5"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,97 +84,73 @@ export function getMealPlannerHTML() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="planner-scroll" class="flex-1 overflow-y-auto px-4 pt-3 pb-4 bg-[#2d2e2b]">
|
<div id="planner-scroll" class="flex-1 overflow-y-auto px-4 pt-3 pb-4 bg-[#2d2e2b]">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div id="planner-summary-card" class="mb-3">
|
||||||
<p id="planner-day-heading" class="text-[13px] font-semibold text-gray-900 tabular-nums"></p>
|
<div class="h-full flex flex-col" style="background:#2d2e2b !important; background-image:none !important; box-shadow:none !important;">
|
||||||
<button type="button" id="planner-copy-day" class="shrink-0 text-[11px] font-semibold text-gray-500 hover:text-gray-900 px-2.5 py-1 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-1.5">
|
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-2">Wartości odżywcze</p>
|
||||||
<i class="fas fa-copy text-[9px]"></i>Kopiuj dzień
|
<div class="flex-1 flex items-center">
|
||||||
</button>
|
<div class="w-full rounded-xl border px-3 py-2.5" style="border-color:#444442 !important;">
|
||||||
|
<div class="grid grid-cols-4 gap-3 text-left">
|
||||||
|
<div class="min-w-0">
|
||||||
|
<span id="planner-nutrition-kcal" class="block text-[15px] font-semibold text-[#ddd6ca] tabular-nums leading-none">—</span>
|
||||||
|
<span class="text-[9px] text-gray-500">kcal</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="planner-summary-card" class="rounded-xl border border-amber-200/80 bg-[#2d2e2b] p-2.5 shadow-sm mb-3">
|
<div class="min-w-0">
|
||||||
<div class="flex items-start justify-between gap-2 mb-2">
|
<span id="planner-nutrition-p" class="block text-[15px] font-semibold text-[#ddd6ca] tabular-nums leading-none">—</span>
|
||||||
<div>
|
<span class="text-[9px] text-gray-500">Białko</span>
|
||||||
<p class="text-[10px] font-semibold uppercase tracking-wide text-amber-900/70">Dziś — podsumowanie</p>
|
|
||||||
<p id="planner-summary-kcal" class="text-xl font-bold text-gray-900 tabular-nums leading-tight mt-0.5">0 <span class="text-[13px] font-semibold text-gray-500">kcal</span></p>
|
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="planner-toggle-nutrition" class="shrink-0 flex items-center gap-1 text-[11px] font-semibold text-amber-900/80 hover:text-gray-900 py-1 px-2 rounded-lg hover:bg-amber-100/50 transition-colors" aria-expanded="false">
|
<div class="min-w-0">
|
||||||
Szczegóły
|
<span id="planner-nutrition-c" class="block text-[15px] font-semibold text-[#ddd6ca] tabular-nums leading-none">—</span>
|
||||||
<i class="fas fa-chevron-down text-[9px] transition-transform" id="planner-nutrition-chevron" aria-hidden="true"></i>
|
<span class="text-[9px] text-gray-500">Węgle</span>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="planner-macro-row" class="flex gap-2 mb-0">
|
<div class="min-w-0">
|
||||||
<div class="flex-1 min-w-0 rounded-lg bg-white/80 border border-amber-100 px-2 py-1.5 text-center">
|
<span id="planner-nutrition-f" class="block text-[15px] font-semibold text-[#ddd6ca] tabular-nums leading-none">—</span>
|
||||||
<p class="text-[9px] font-semibold text-gray-500 uppercase">B</p>
|
<span class="text-[9px] text-gray-500">Tłuszcze</span>
|
||||||
<p id="planner-macro-p" class="text-xs font-bold text-gray-900 tabular-nums">0 g</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 min-w-0 rounded-lg bg-white/80 border border-amber-100 px-2 py-1.5 text-center">
|
|
||||||
<p class="text-[9px] font-semibold text-gray-500 uppercase">T</p>
|
|
||||||
<p id="planner-macro-f" class="text-xs font-bold text-gray-900 tabular-nums">0 g</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 min-w-0 rounded-lg bg-white/80 border border-amber-100 px-2 py-1.5 text-center">
|
|
||||||
<p class="text-[9px] font-semibold text-gray-500 uppercase">W</p>
|
|
||||||
<p id="planner-macro-c" class="text-xs font-bold text-gray-900 tabular-nums">0 g</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="planner-nutrition-details" class="hidden mt-3 pt-3 border-t border-amber-200/60">
|
|
||||||
<ul class="space-y-0 divide-y divide-amber-100/80 text-sm">
|
|
||||||
<li class="flex justify-between py-2 font-bold"><span class="text-gray-800">Kalorie</span><span id="planner-detail-kcal" class="text-gray-900 tabular-nums">0 kcal</span></li>
|
|
||||||
<li class="flex justify-between py-2"><span class="text-gray-700 font-medium">Białko</span><span id="planner-detail-p" class="font-medium text-gray-900 tabular-nums">0 g</span></li>
|
|
||||||
<li class="flex justify-between py-2"><span class="text-gray-700 font-medium">Tłuszcze</span><span id="planner-detail-f" class="font-medium text-gray-900 tabular-nums">0 g</span></li>
|
|
||||||
<li class="flex justify-between py-2"><span class="text-gray-700 font-medium">Węglowodany</span><span id="planner-detail-c" class="font-medium text-gray-900 tabular-nums">0 g</span></li>
|
|
||||||
</ul>
|
|
||||||
<p id="planner-summary-hint" class="text-[11px] text-gray-500 mt-2">Suma z zaplanowanych posiłków (porcje × wartości z przepisu).</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="planner-open-ingredients" class="w-full mb-3 flex items-center justify-center gap-2 py-2.5 rounded-xl border border-dashed border-gray-300 bg-white text-[13px] font-semibold text-gray-800 hover:border-gray-400 hover:bg-gray-50 transition-colors">
|
</div>
|
||||||
<i class="fas fa-shopping-basket text-gray-500 text-xs" aria-hidden="true"></i>
|
</div>
|
||||||
|
<button type="button" id="planner-open-ingredients" class="w-full mb-3 flex items-center justify-center gap-2 py-2.5 rounded-xl border border-dashed border-[#444442] bg-[#2d2e2b] text-[13px] font-semibold text-[#d7d2c8] hover:border-[#6d6c67] hover:bg-[#3a3a37] transition-colors">
|
||||||
|
<i class="fas fa-shopping-basket text-[#9b978f] text-xs" aria-hidden="true"></i>
|
||||||
Składniki na ten dzień
|
Składniki na ten dzień
|
||||||
</button>
|
</button>
|
||||||
<div id="planner-meal-slots" class="space-y-3 pb-2"></div>
|
<div id="planner-meal-slots" class="space-y-3 pb-2 bg-[#2d2e2b]"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="planner-picker-backdrop" class="absolute left-0 right-0 top-0 z-[45] bg-black/45 hidden opacity-0 transition-opacity duration-200" style="bottom: ${PLANNER_SHEET_BOTTOM_INSET}" aria-hidden="true"></div>
|
<div id="planner-picker-backdrop" class="absolute left-0 right-0 top-0 z-[45] bg-black/45 hidden opacity-0 transition-opacity duration-200" style="bottom: ${PLANNER_SHEET_BOTTOM_INSET}" aria-hidden="true"></div>
|
||||||
<div id="planner-picker-sheet" class="absolute left-0 right-0 z-[50] bg-white rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.12)] flex flex-col will-change-transform" style="visibility: hidden; bottom: ${PLANNER_SHEET_BOTTOM_INSET}; height: auto; max-height: ${PLANNER_SHEET_MAX_HEIGHT}; transform: ${PLANNER_SHEET_OFF_TRANSFORM}; transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1)" role="dialog" aria-labelledby="planner-picker-title" aria-modal="true">
|
<div id="planner-picker-sheet" class="absolute left-0 right-0 z-[50] rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.12)] flex flex-col will-change-transform" style="visibility: hidden; bottom: ${PLANNER_SHEET_BOTTOM_INSET}; height: auto; max-height: ${PLANNER_SHEET_MAX_HEIGHT}; transform: ${PLANNER_SHEET_OFF_TRANSFORM}; transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1); background:#2d2e2b !important; background-image:none !important;" role="dialog" aria-labelledby="planner-picker-title" aria-modal="true">
|
||||||
<div class="shrink-0 px-4 pt-3 pb-2 border-b border-gray-100 touch-none cursor-grab active:cursor-grabbing select-none" data-planner-sheet-drag-zone aria-label="Przeciągnij w dół, by zamknąć">
|
<div class="shrink-0 px-4 pt-3 pb-2 border-b border-[#444442] touch-none cursor-grab active:cursor-grabbing select-none" data-planner-sheet-drag-zone aria-label="Przeciągnij w dół, by zamknąć">
|
||||||
<div class="w-10 h-1 bg-gray-200 rounded-full mx-auto mb-2.5" aria-hidden="true"></div>
|
<div class="w-10 h-1 bg-[#6d6c67]/75 rounded-full mx-auto mb-2.5" aria-hidden="true"></div>
|
||||||
<h2 id="planner-picker-title" class="text-[15px] font-bold text-gray-900 leading-tight pr-2">Wybierz przepis</h2>
|
<h2 id="planner-picker-title" class="text-[15px] font-bold text-[#ddd6ca] leading-tight pr-2">Wybierz przepis</h2>
|
||||||
<p id="planner-picker-sub" class="text-[11px] text-gray-500 mt-1"></p>
|
<p id="planner-picker-sub" class="text-[11px] text-[#9b978f] mt-1"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="shrink-0 px-4 pt-2 pb-2">
|
<div class="shrink-0 px-4 pt-2 pb-2">
|
||||||
<input type="text" id="planner-picker-search" class="w-full rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 text-sm outline-none focus:border-gray-400 placeholder:text-gray-400" placeholder="Szukaj przepisu…" />
|
<input type="text" id="planner-picker-search" class="w-full rounded-xl border border-[#444442] bg-[#2f2f2d] px-3 py-2 text-sm text-[#ddd6ca] outline-none focus:border-[#6d6c67] placeholder:text-[#7d7a74]" placeholder="Szukaj przepisu…" />
|
||||||
</div>
|
</div>
|
||||||
<div id="planner-picker-list" class="min-h-0 flex-1 overflow-y-auto no-scrollbar px-4 py-2.5 pb-8 space-y-2"></div>
|
<div id="planner-picker-list" class="min-h-0 flex-1 overflow-y-auto no-scrollbar px-4 py-2.5 pb-8 space-y-2"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="planner-ing-backdrop" class="absolute left-0 right-0 top-0 z-[45] bg-black/45 hidden opacity-0 transition-opacity duration-200" style="bottom: ${PLANNER_SHEET_BOTTOM_INSET}" aria-hidden="true"></div>
|
<div id="planner-ing-backdrop" class="absolute left-0 right-0 top-0 z-[45] bg-black/45 hidden opacity-0 transition-opacity duration-200" style="bottom: ${PLANNER_SHEET_BOTTOM_INSET}" aria-hidden="true"></div>
|
||||||
<div id="planner-ing-sheet" class="absolute left-0 right-0 z-[50] bg-white rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.12)] flex flex-col will-change-transform" style="visibility: hidden; bottom: ${PLANNER_SHEET_BOTTOM_INSET}; height: auto; max-height: ${PLANNER_SHEET_MAX_HEIGHT}; transform: ${PLANNER_SHEET_OFF_TRANSFORM}; transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1)" role="dialog" aria-labelledby="planner-ing-title" aria-modal="true">
|
<div id="planner-ing-sheet" class="absolute left-0 right-0 z-[50] rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.12)] flex flex-col will-change-transform" style="visibility: hidden; bottom: ${PLANNER_SHEET_BOTTOM_INSET}; height: auto; max-height: ${PLANNER_SHEET_MAX_HEIGHT}; transform: ${PLANNER_SHEET_OFF_TRANSFORM}; transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1); background:#2d2e2b !important; background-image:none !important;" role="dialog" aria-labelledby="planner-ing-title" aria-modal="true">
|
||||||
<div class="shrink-0 px-4 pt-3 pb-2 border-b border-gray-100 touch-none cursor-grab active:cursor-grabbing select-none" data-planner-sheet-drag-zone aria-label="Przeciągnij w dół, by zamknąć">
|
<div class="shrink-0 px-4 pt-3 pb-2 border-b border-[#444442] touch-none cursor-grab active:cursor-grabbing select-none" data-planner-sheet-drag-zone aria-label="Przeciągnij w dół, by zamknąć">
|
||||||
<div class="w-10 h-1 bg-gray-200 rounded-full mx-auto mb-2.5" aria-hidden="true"></div>
|
<div class="w-10 h-1 bg-[#6d6c67]/75 rounded-full mx-auto mb-2.5" aria-hidden="true"></div>
|
||||||
<h2 id="planner-ing-title" class="text-[15px] font-bold text-gray-900 leading-tight pr-2">Składniki i spiżarnia</h2>
|
<h2 id="planner-ing-title" class="text-[15px] font-bold text-[#ddd6ca] leading-tight pr-2">Składniki i spiżarnia</h2>
|
||||||
<p id="planner-ing-sub" class="text-[11px] text-gray-500 mt-1">Porównanie potrzeb z zapasami.</p>
|
<p id="planner-ing-sub" class="text-[11px] text-[#9b978f] mt-1">Porównanie potrzeb z zapasami.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="planner-ing-body" class="min-h-0 flex-1 overflow-y-auto no-scrollbar px-4 py-2 pb-2"></div>
|
<div id="planner-ing-body" class="min-h-0 flex-1 overflow-y-auto no-scrollbar px-4 py-2 pb-2"></div>
|
||||||
<div id="planner-ing-footer" class="shrink-0 p-4 pt-2 pb-5 border-t border-gray-100 bg-white space-y-2">
|
<div id="planner-ing-footer" class="shrink-0 p-4 pt-2 pb-5 border-t border-[#444442] bg-[#2d2e2b] space-y-2">
|
||||||
<button type="button" id="planner-ing-add-all" class="w-full bg-gray-900 hover:bg-black text-white py-3 rounded-xl font-semibold shadow-sm transition-colors text-[13px] flex items-center justify-center gap-2">
|
<button type="button" id="planner-ing-add-all" class="w-full bg-gray-900 hover:bg-black text-white py-3 rounded-xl font-semibold shadow-sm transition-colors text-[13px] flex items-center justify-center gap-2">
|
||||||
<i class="fas fa-cart-plus text-xs" aria-hidden="true"></i>
|
<i class="fas fa-cart-plus text-xs" aria-hidden="true"></i>
|
||||||
Dodaj braki na dziś do listy
|
Dodaj braki na dziś do listy
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="planner-ing-add-btn" class="hidden w-full border border-gray-200 bg-white text-gray-800 hover:bg-gray-50 py-2.5 rounded-xl font-semibold text-[13px] flex items-center justify-center gap-2 transition-colors">
|
<button type="button" id="planner-ing-add-btn" class="hidden w-full border border-[#444442] bg-[#2d2e2b] text-[#d7d2c8] hover:bg-[#3a3a37] py-2.5 rounded-xl font-semibold text-[13px] flex items-center justify-center gap-2 transition-colors">
|
||||||
<i class="fas fa-calendar-week text-gray-500 text-[11px]" aria-hidden="true"></i>
|
<i class="fas fa-calendar-week text-[#9b978f] text-[11px]" aria-hidden="true"></i>
|
||||||
Dodaj braki na cały tydzień
|
Dodaj braki na cały tydzień
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="planner-copy-backdrop" class="absolute left-0 right-0 top-0 z-[45] bg-black/45 hidden opacity-0 transition-opacity duration-200" style="bottom: ${PLANNER_SHEET_BOTTOM_INSET}" aria-hidden="true"></div>
|
|
||||||
<div id="planner-copy-sheet" class="absolute left-0 right-0 z-[50] bg-white rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.12)] flex flex-col will-change-transform" style="visibility: hidden; bottom: ${PLANNER_SHEET_BOTTOM_INSET}; height: auto; max-height: ${PLANNER_SHEET_MAX_HEIGHT}; transform: ${PLANNER_SHEET_OFF_TRANSFORM}; transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1)" role="dialog" aria-modal="true">
|
|
||||||
<div class="shrink-0 px-4 pt-3 pb-2 border-b border-gray-100 touch-none cursor-grab active:cursor-grabbing select-none" data-planner-sheet-drag-zone>
|
|
||||||
<div class="w-10 h-1 bg-gray-200 rounded-full mx-auto mb-2.5"></div>
|
|
||||||
<h2 class="text-[15px] font-bold text-gray-900 leading-tight">Kopiuj plan dnia</h2>
|
|
||||||
<p id="planner-copy-sub" class="text-[11px] text-gray-500 mt-1">Wybierz dzień docelowy.</p>
|
|
||||||
</div>
|
|
||||||
<div id="planner-copy-list" class="min-h-0 flex-1 overflow-y-auto no-scrollbar px-4 py-2.5 pb-8 space-y-2"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="planner-toast" class="pointer-events-none absolute left-4 right-4 bottom-28 z-[55] opacity-0 translate-y-2 transition-all duration-300" role="status">
|
<div id="planner-toast" class="pointer-events-none absolute left-4 right-4 bottom-28 z-[55] 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="planner-toast-text"></div>
|
<div class="rounded-xl bg-gray-900 text-white text-sm font-medium px-4 py-3 shadow-lg text-center" id="planner-toast-text"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -346,30 +322,26 @@ function bindPlannerSheetDragClose(sheet, closeFn) {
|
|||||||
|
|
||||||
function renderDayContent(state) {
|
function renderDayContent(state) {
|
||||||
const sel = state.selected;
|
const sel = state.selected;
|
||||||
const heading = document.getElementById('planner-day-heading');
|
|
||||||
if (heading) {
|
|
||||||
const wd = WEEKDAYS_LONG[sel.getDay()];
|
|
||||||
heading.textContent = `${wd}, ${sel.getDate()} ${CALENDAR_MONTHS_SHORT[sel.getMonth()]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dayPlan = getDayPlan(state.plans, sel);
|
const dayPlan = getDayPlan(state.plans, sel);
|
||||||
const totals = sumDayNutrition(dayPlan);
|
const totals = sumDayNutrition(dayPlan);
|
||||||
|
|
||||||
const kcalEl = document.getElementById('planner-summary-kcal');
|
const setText = (id, value) => {
|
||||||
if (kcalEl) {
|
const el = document.getElementById(id);
|
||||||
kcalEl.innerHTML = totals.mealCount === 0
|
if (el) el.textContent = value;
|
||||||
? `— <span class="text-sm font-semibold text-gray-500">kcal</span>`
|
};
|
||||||
: `${totals.kcal} <span class="text-sm font-semibold text-gray-500">kcal</span>`;
|
const setGrams = (id, value) => {
|
||||||
}
|
const el = document.getElementById(id);
|
||||||
const fmt = (n) => `${n} g`;
|
if (!el) return;
|
||||||
document.getElementById('planner-macro-p').textContent = totals.mealCount ? fmt(totals.protein) : '—';
|
el.innerHTML = value === null
|
||||||
document.getElementById('planner-macro-f').textContent = totals.mealCount ? fmt(totals.fat) : '—';
|
? '—'
|
||||||
document.getElementById('planner-macro-c').textContent = totals.mealCount ? fmt(totals.carbs) : '—';
|
: `${value}<span class="ml-0.5 text-[12px] font-medium text-[#9b978f]">g</span>`;
|
||||||
|
};
|
||||||
|
|
||||||
document.getElementById('planner-detail-kcal').textContent = `${totals.kcal} kcal`;
|
const hasMeals = totals.mealCount > 0;
|
||||||
document.getElementById('planner-detail-p').textContent = fmt(totals.protein);
|
setText('planner-nutrition-kcal', hasMeals ? String(totals.kcal) : '—');
|
||||||
document.getElementById('planner-detail-f').textContent = fmt(totals.fat);
|
setGrams('planner-nutrition-p', hasMeals ? totals.protein : null);
|
||||||
document.getElementById('planner-detail-c').textContent = fmt(totals.carbs);
|
setGrams('planner-nutrition-f', hasMeals ? totals.fat : null);
|
||||||
|
setGrams('planner-nutrition-c', hasMeals ? totals.carbs : null);
|
||||||
|
|
||||||
const ingBtn = document.getElementById('planner-open-ingredients');
|
const ingBtn = document.getElementById('planner-open-ingredients');
|
||||||
if (ingBtn) {
|
if (ingBtn) {
|
||||||
@@ -407,7 +379,7 @@ function renderDayContent(state) {
|
|||||||
if (entry?.recipeId && RECIPES[entry.recipeId]) slotKcal += computeEntryNutrition(entry).kcal;
|
if (entry?.recipeId && RECIPES[entry.recipeId]) slotKcal += computeEntryNutrition(entry).kcal;
|
||||||
});
|
});
|
||||||
const kcalBadge = slotKcal > 0
|
const kcalBadge = slotKcal > 0
|
||||||
? `<span class="text-[10px] font-semibold text-amber-600 tabular-nums shrink-0 ml-auto">${slotKcal} kcal</span>`
|
? `<span class="text-[10px] font-semibold text-[#f2efe8] tabular-nums shrink-0 ml-auto">${slotKcal} kcal</span>`
|
||||||
: '';
|
: '';
|
||||||
const countLabel = entries.length > 1
|
const countLabel = entries.length > 1
|
||||||
? `<span class="text-[10px] font-semibold text-gray-400 tabular-nums shrink-0">${entries.length} dania</span>`
|
? `<span class="text-[10px] font-semibold text-gray-400 tabular-nums shrink-0">${entries.length} dania</span>`
|
||||||
@@ -424,30 +396,30 @@ function renderDayContent(state) {
|
|||||||
(entry.addedIngredients?.length > 0) ||
|
(entry.addedIngredients?.length > 0) ||
|
||||||
(entry.substitutions && Object.keys(entry.substitutions).length > 0);
|
(entry.substitutions && Object.keys(entry.substitutions).length > 0);
|
||||||
const customDot = hasCustom ? '<span class="w-1.5 h-1.5 rounded-full bg-amber-400 inline-block shrink-0 ml-1"></span>' : '';
|
const customDot = hasCustom ? '<span class="w-1.5 h-1.5 rounded-full bg-amber-400 inline-block shrink-0 ml-1"></span>' : '';
|
||||||
const servLabel = servings > 1 ? `<span class="mx-1.5 text-gray-300">·</span>×${servings}` : '';
|
const servLabel = servings > 1 ? `<span class="mx-1.5 text-[#6d6c67]">·</span>×${servings}` : '';
|
||||||
return `
|
return `
|
||||||
<div class="rounded-lg border border-gray-200 bg-white p-2 shadow-sm" data-slot-id="${slot.id}" data-entry-id="${eid}">
|
<div class="rounded-lg bg-[#2d2e2b] p-2" data-slot-id="${slot.id}" data-entry-id="${eid}">
|
||||||
<div class="flex items-start justify-between gap-2">
|
<div class="flex items-start justify-between gap-2">
|
||||||
<div class="flex items-center gap-2 min-w-0 cursor-pointer planner-open-recipe" data-recipe-id="${escapeHtml(recipe.id)}">
|
<div class="flex items-center gap-2 min-w-0 cursor-pointer planner-open-recipe" data-recipe-id="${escapeHtml(recipe.id)}">
|
||||||
<div class="w-8 h-8 rounded-lg bg-[#d4d4d4] overflow-hidden shrink-0">
|
<div class="w-8 h-8 rounded-lg bg-[#3a3a37] overflow-hidden shrink-0">
|
||||||
${recipe.image
|
${recipe.image
|
||||||
? `<img src="${escapeHtml(recipe.image)}" alt="" class="w-full h-full object-cover">`
|
? `<img src="${escapeHtml(recipe.image)}" alt="" class="w-full h-full object-cover">`
|
||||||
: `<span class="w-full h-full flex items-center justify-center text-white text-[8px] font-medium">${escapeHtml(recipe.thumbLabel)}</span>`}
|
: `<span class="w-full h-full flex items-center justify-center text-white text-[8px] font-medium">${escapeHtml(recipe.thumbLabel)}</span>`}
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0">
|
<div class="min-w-0">
|
||||||
<div class="flex items-center"><p class="text-[13px] font-bold text-gray-900 truncate underline decoration-1 underline-offset-2">${escapeHtml(recipe.title)}</p>${customDot}</div>
|
<div class="flex items-center"><p class="text-[13px] font-bold text-[#ddd6ca] truncate underline decoration-1 underline-offset-2">${escapeHtml(recipe.title)}</p>${customDot}</div>
|
||||||
<p class="text-[11px] text-gray-500 mt-0.5 tabular-nums">
|
<p class="text-[11px] text-[#9b978f] mt-0.5 tabular-nums">
|
||||||
<i class="fas fa-clock text-gray-400 mr-0.5" aria-hidden="true"></i>${recipe.minutes} min
|
<i class="fas fa-clock text-[#7d7a74] mr-0.5" aria-hidden="true"></i>${recipe.minutes} min
|
||||||
<span class="mx-1.5 text-gray-300">·</span>
|
<span class="mx-1.5 text-[#6d6c67]">·</span>
|
||||||
<i class="fas fa-fire text-gray-400 mr-0.5" aria-hidden="true"></i>${entryN.kcal} kcal${servLabel}
|
<i class="fas fa-fire text-[#7d7a74] mr-0.5" aria-hidden="true"></i>${entryN.kcal} kcal${servLabel}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 shrink-0">
|
<div class="flex items-center gap-1 shrink-0">
|
||||||
<button type="button" class="planner-edit-meal w-6 h-6 rounded-full border border-gray-200 text-gray-400 hover:text-gray-900 hover:border-gray-400 hover:bg-gray-50 flex items-center justify-center transition-colors" data-slot-id="${slot.id}" data-entry-id="${eid}" aria-label="Edytuj ten przepis">
|
<button type="button" class="planner-edit-meal w-6 h-6 rounded-full border border-[#444442] text-[#9b978f] hover:text-[#ddd6ca] hover:border-[#6d6c67] hover:bg-[#3a3a37] flex items-center justify-center transition-colors" data-slot-id="${slot.id}" data-entry-id="${eid}" aria-label="Edytuj ten przepis">
|
||||||
<i class="fas fa-pencil text-[9px]" aria-hidden="true"></i>
|
<i class="fas fa-pencil text-[9px]" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="planner-clear-meal w-6 h-6 rounded-full border border-gray-200 text-gray-400 hover:text-red-600 hover:border-red-200 hover:bg-red-50 transition-colors flex items-center justify-center" data-slot-id="${slot.id}" data-entry-id="${eid}" aria-label="Usuń ten przepis">
|
<button type="button" class="planner-clear-meal w-6 h-6 rounded-full border border-[#444442] text-[#9b978f] hover:text-red-400 hover:border-red-300/60 hover:bg-[#3a2326] transition-colors flex items-center justify-center" data-slot-id="${slot.id}" data-entry-id="${eid}" aria-label="Usuń ten przepis">
|
||||||
<i class="fas fa-times text-[9px]" aria-hidden="true"></i>
|
<i class="fas fa-times text-[9px]" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -457,27 +429,25 @@ function renderDayContent(state) {
|
|||||||
|
|
||||||
if (isSkipped) {
|
if (isSkipped) {
|
||||||
return `
|
return `
|
||||||
<div class="rounded-xl border border-gray-200 bg-white shadow-sm overflow-hidden opacity-60" data-slot-id="${slot.id}">
|
<div class="rounded-xl bg-[#393937] overflow-hidden opacity-60" style="background:#393937 !important;" data-slot-id="${slot.id}">
|
||||||
<div class="flex items-center gap-2 px-3 py-2 border-b border-gray-100 bg-gray-50/90">
|
<div class="flex items-center gap-2 px-3 py-2 border-b border-[#444442] bg-[#393937]" style="background:#393937 !important;">
|
||||||
<span class="w-7 h-7 rounded-lg bg-gray-100 flex items-center justify-center text-gray-400 shrink-0">
|
<i class="fas ${slot.icon} w-7 text-center text-[13px] text-[#7d7a74] shrink-0" aria-hidden="true"></i>
|
||||||
<i class="fas ${slot.icon} text-[13px]" aria-hidden="true"></i>
|
<span class="text-[13px] font-semibold text-[#9b978f] truncate min-w-0 flex-1">${slot.label}</span>
|
||||||
</span>
|
|
||||||
<span class="text-[13px] font-semibold text-gray-400 truncate min-w-0 flex-1">${slot.label}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2.5 flex items-center justify-between">
|
<div class="p-2.5 flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-400 italic"><i class="fas fa-forward text-[9px] mr-1.5"></i>Pominięto</span>
|
<span class="text-xs text-[#9b978f] italic"><i class="fas fa-forward text-[9px] mr-1.5"></i>Pominięto</span>
|
||||||
<button type="button" class="planner-unskip text-[11px] font-semibold text-gray-500 hover:text-gray-900 px-2 py-1 rounded-lg hover:bg-gray-100 transition-colors" data-slot-id="${slot.id}">Cofnij</button>
|
<button type="button" class="planner-unskip text-[11px] font-semibold text-[#9b978f] hover:text-[#ddd6ca] px-2 py-1 rounded-lg hover:bg-[#3a3a37] transition-colors" data-slot-id="${slot.id}">Cofnij</button>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addLabel = entries.length === 0 ? 'Dodaj przepis' : 'Dodaj kolejny';
|
const addLabel = entries.length === 0 ? 'Dodaj przepis' : 'Dodaj kolejny';
|
||||||
const addClasses = entries.length === 0
|
const addClasses = entries.length === 0
|
||||||
? 'planner-add-meal flex-1 py-2 rounded-lg border border-dashed border-gray-200 text-[13px] font-semibold text-gray-700 hover:bg-gray-50 hover:border-gray-300 transition-colors'
|
? 'planner-add-meal flex-1 py-2 rounded-lg border border-dashed border-[#444442] text-[13px] font-semibold text-[#d7d2c8] hover:bg-[#3a3a37] hover:border-[#6d6c67] transition-colors'
|
||||||
: 'planner-add-meal w-full py-1.5 rounded-lg border border-dashed border-gray-200 text-xs font-semibold text-gray-600 hover:bg-gray-50 hover:border-gray-300 transition-colors';
|
: 'planner-add-meal w-full py-1.5 rounded-lg border border-dashed border-[#444442] text-xs font-semibold text-[#d7d2c8] hover:bg-[#3a3a37] hover:border-[#6d6c67] transition-colors';
|
||||||
|
|
||||||
const skipBtn = entries.length === 0
|
const skipBtn = entries.length === 0
|
||||||
? `<button type="button" class="planner-skip-meal shrink-0 py-2 px-3 rounded-lg text-[11px] font-semibold text-gray-400 hover:text-gray-600 hover:bg-gray-100 transition-colors" data-slot-id="${slot.id}"><i class="fas fa-forward text-[9px] mr-1"></i>Pomijam</button>`
|
? `<button type="button" class="planner-skip-meal shrink-0 py-2 px-3 rounded-lg text-[11px] font-semibold text-[#9b978f] hover:text-[#ddd6ca] hover:bg-[#3a3a37] transition-colors" data-slot-id="${slot.id}"><i class="fas fa-forward text-[9px] mr-1"></i>Pomijam</button>`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const bottomRow = entries.length === 0
|
const bottomRow = entries.length === 0
|
||||||
@@ -485,12 +455,10 @@ function renderDayContent(state) {
|
|||||||
: `<button type="button" class="${addClasses}" data-slot-id="${slot.id}"><i class="fas fa-plus text-[10px] mr-1 opacity-70" aria-hidden="true"></i>${addLabel}</button>`;
|
: `<button type="button" class="${addClasses}" data-slot-id="${slot.id}"><i class="fas fa-plus text-[10px] mr-1 opacity-70" aria-hidden="true"></i>${addLabel}</button>`;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="rounded-xl border border-gray-200 bg-white shadow-sm overflow-hidden" data-slot-id="${slot.id}">
|
<div class="rounded-xl bg-[#393937] overflow-hidden" style="background:#393937 !important;" data-slot-id="${slot.id}">
|
||||||
<div class="flex items-center gap-2 px-3 py-2 border-b border-gray-100 bg-gray-50/90">
|
<div class="flex items-center gap-2 px-3 py-2 border-b border-[#444442] bg-[#393937]" style="background:#393937 !important;">
|
||||||
<span class="w-7 h-7 rounded-lg bg-gray-100 flex items-center justify-center text-gray-500 shrink-0">
|
<i class="fas ${slot.icon} w-7 text-center text-[13px] text-[#9b978f] shrink-0" aria-hidden="true"></i>
|
||||||
<i class="fas ${slot.icon} text-[13px]" aria-hidden="true"></i>
|
<span class="text-[13px] font-semibold text-[#ddd6ca] truncate min-w-0 flex-1">${slot.label}</span>
|
||||||
</span>
|
|
||||||
<span class="text-[13px] font-semibold text-gray-900 truncate min-w-0 flex-1">${slot.label}</span>
|
|
||||||
${countLabel}
|
${countLabel}
|
||||||
${kcalBadge}
|
${kcalBadge}
|
||||||
</div>
|
</div>
|
||||||
@@ -533,18 +501,18 @@ function getRecentRecipeIds(plans, limit = 5) {
|
|||||||
|
|
||||||
function recipeCardHtml(r) {
|
function recipeCardHtml(r) {
|
||||||
return `
|
return `
|
||||||
<button type="button" class="planner-pick-recipe w-full flex gap-2.5 p-2.5 rounded-xl border border-gray-200 bg-gray-50/80 hover:border-gray-900 hover:bg-white text-left transition-all" data-recipe-id="${r.id}">
|
<button type="button" class="planner-pick-recipe w-full flex gap-2.5 p-2.5 rounded-xl border border-[#444442] bg-[#2d2e2b] hover:border-[#6d6c67] hover:bg-[#3a3a37] text-left transition-all" data-recipe-id="${r.id}">
|
||||||
<div class="w-11 h-11 rounded-lg bg-[#d4d4d4] overflow-hidden shrink-0">
|
<div class="w-11 h-11 rounded-lg bg-[#3a3a37] overflow-hidden shrink-0">
|
||||||
${r.image
|
${r.image
|
||||||
? `<img src="${escapeHtml(r.image)}" alt="" class="w-full h-full object-cover">`
|
? `<img src="${escapeHtml(r.image)}" alt="" class="w-full h-full object-cover">`
|
||||||
: `<span class="w-full h-full flex items-center justify-center text-white text-[9px] font-medium">${escapeHtml(r.thumbLabel)}</span>`}
|
: `<span class="w-full h-full flex items-center justify-center text-white text-[9px] font-medium">${escapeHtml(r.thumbLabel)}</span>`}
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-1 py-0.5">
|
<div class="min-w-0 flex-1 py-0.5">
|
||||||
<p class="text-[13px] font-bold text-gray-900 line-clamp-2">${escapeHtml(r.title)}</p>
|
<p class="text-[13px] font-bold text-[#ddd6ca] line-clamp-2">${escapeHtml(r.title)}</p>
|
||||||
<p class="text-[11px] text-gray-500 mt-1 tabular-nums">
|
<p class="text-[11px] text-[#9b978f] mt-1 tabular-nums">
|
||||||
<i class="fas fa-fire text-gray-400 mr-0.5" aria-hidden="true"></i>${r.nutritionPerServing.kcal} kcal
|
<i class="fas fa-fire text-[#7d7a74] mr-0.5" aria-hidden="true"></i>${r.nutritionPerServing.kcal} kcal
|
||||||
<span class="mx-1 text-gray-300">·</span>
|
<span class="mx-1 text-[#6d6c67]">·</span>
|
||||||
<i class="fas fa-clock text-gray-400 mr-0.5" aria-hidden="true"></i>${r.minutes} min
|
<i class="fas fa-clock text-[#7d7a74] mr-0.5" aria-hidden="true"></i>${r.minutes} min
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</button>`;
|
</button>`;
|
||||||
@@ -573,11 +541,11 @@ function renderPickerList(slotId, plans, query = '') {
|
|||||||
: allRecipes;
|
: allRecipes;
|
||||||
|
|
||||||
if (filtered.length === 0 && q) {
|
if (filtered.length === 0 && q) {
|
||||||
list.innerHTML = '<p class="text-sm text-gray-500 text-center py-6">Brak wyników.</p>';
|
list.innerHTML = '<p class="text-sm text-[#9b978f] text-center py-6">Brak wyników.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (filtered.length === 0) {
|
if (filtered.length === 0) {
|
||||||
list.innerHTML = '<p class="text-sm text-gray-500 text-center py-6">Brak dopasowanych przepisów.</p>';
|
list.innerHTML = '<p class="text-sm text-[#9b978f] text-center py-6">Brak dopasowanych przepisów.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +557,7 @@ function renderPickerList(slotId, plans, query = '') {
|
|||||||
if (recentInSlot.length > 0) {
|
if (recentInSlot.length > 0) {
|
||||||
html += `<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider px-0.5 pt-1 pb-1"><i class="fas fa-history text-[9px] mr-1"></i>Ostatnio używane</p>`;
|
html += `<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider px-0.5 pt-1 pb-1"><i class="fas fa-history text-[9px] mr-1"></i>Ostatnio używane</p>`;
|
||||||
html += recentInSlot.map(recipeCardHtml).join('');
|
html += recentInSlot.map(recipeCardHtml).join('');
|
||||||
html += `<div class="border-t border-gray-100 my-2"></div>`;
|
html += `<div class="border-t border-[#444442] my-2"></div>`;
|
||||||
html += `<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider px-0.5 pt-1 pb-1">Wszystkie</p>`;
|
html += `<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider px-0.5 pt-1 pb-1">Wszystkie</p>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -659,7 +627,7 @@ function renderIngredientsSheet(state) {
|
|||||||
if (subEl) subEl.textContent = 'Porównanie potrzeb z zapasami w spiżarni.';
|
if (subEl) subEl.textContent = 'Porównanie potrzeb z zapasami w spiżarni.';
|
||||||
|
|
||||||
if (!today || today.items.length === 0) {
|
if (!today || today.items.length === 0) {
|
||||||
body.innerHTML = '<p class="text-sm text-gray-500 text-center py-8">Najpierw zaplanuj posiłki.</p>';
|
body.innerHTML = '<p class="text-sm text-[#9b978f] text-center py-8">Najpierw zaplanuj posiłki.</p>';
|
||||||
updateIngButtons(state);
|
updateIngButtons(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -669,22 +637,22 @@ function renderIngredientsSheet(state) {
|
|||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
if (shortItems.length === 0) {
|
if (shortItems.length === 0) {
|
||||||
html += `<div class="rounded-xl bg-emerald-50 border border-emerald-200/80 p-3 mb-4 flex items-center gap-2.5">
|
html += `<div class="rounded-xl bg-[#2d2e2b] border border-emerald-400/40 p-3 mb-4 flex items-center gap-2.5">
|
||||||
<div class="w-8 h-8 rounded-full bg-emerald-100 flex items-center justify-center shrink-0">
|
<div class="w-8 h-8 rounded-full bg-[#24352a] flex items-center justify-center shrink-0">
|
||||||
<i class="fas fa-check text-emerald-600 text-sm"></i>
|
<i class="fas fa-check text-emerald-600 text-sm"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-[13px] font-semibold text-emerald-800">Wszystko masz w spiżarni</p>
|
<p class="text-[13px] font-semibold text-emerald-300">Wszystko masz w spiżarni</p>
|
||||||
<p class="text-[11px] text-emerald-600/80">${today.items.length} ${plIngredientWord(today.items.length)} — zapasy wystarczą</p>
|
<p class="text-[11px] text-emerald-600/80">${today.items.length} ${plIngredientWord(today.items.length)} — zapasy wystarczą</p>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
} else {
|
} else {
|
||||||
html += `<div class="rounded-xl bg-red-50 border border-red-200/80 p-3 mb-4 flex items-center gap-2.5">
|
html += `<div class="rounded-xl bg-[#2d2e2b] border border-red-300/40 p-3 mb-4 flex items-center gap-2.5">
|
||||||
<div class="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center shrink-0">
|
<div class="w-8 h-8 rounded-full bg-[#3a2326] flex items-center justify-center shrink-0">
|
||||||
<i class="fas fa-exclamation text-red-500 text-sm"></i>
|
<i class="fas fa-exclamation text-red-500 text-sm"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-[13px] font-semibold text-red-800">${shortItems.length} ${plIngredientWord(shortItems.length)} do kupienia</p>
|
<p class="text-[13px] font-semibold text-red-300">${shortItems.length} ${plIngredientWord(shortItems.length)} do kupienia</p>
|
||||||
<p class="text-[11px] text-red-600/80">Brakuje składników na zaplanowane posiłki</p>
|
<p class="text-[11px] text-red-600/80">Brakuje składników na zaplanowane posiłki</p>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -695,15 +663,15 @@ function renderIngredientsSheet(state) {
|
|||||||
<p class="text-[10px] font-bold text-red-400 uppercase tracking-wider mb-2 px-0.5">
|
<p class="text-[10px] font-bold text-red-400 uppercase tracking-wider mb-2 px-0.5">
|
||||||
<i class="fas fa-cart-shopping text-[9px] mr-1"></i>Do kupienia
|
<i class="fas fa-cart-shopping text-[9px] mr-1"></i>Do kupienia
|
||||||
</p>
|
</p>
|
||||||
<ul class="border border-red-100/80 rounded-xl overflow-hidden bg-white divide-y divide-red-50">
|
<ul class="border border-red-300/30 rounded-xl overflow-hidden bg-[#2d2e2b] divide-y divide-[#3a2326]">
|
||||||
${shortItems.map((ing) => `
|
${shortItems.map((ing) => `
|
||||||
<li class="flex items-start gap-3 py-3 px-3">
|
<li class="flex items-start gap-3 py-3 px-3">
|
||||||
<div class="w-2 h-2 rounded-full bg-red-400 mt-1.5 shrink-0"></div>
|
<div class="w-2 h-2 rounded-full bg-red-400 mt-1.5 shrink-0"></div>
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<p class="text-[13px] font-semibold text-gray-900">${escapeHtml(ing.name)}</p>
|
<p class="text-[13px] font-semibold text-[#ddd6ca]">${escapeHtml(ing.name)}</p>
|
||||||
<p class="text-[11px] text-gray-500 mt-0.5">
|
<p class="text-[11px] text-[#9b978f] mt-0.5">
|
||||||
potrzeba <span class="font-medium text-gray-700">${formatAmount(ing.amount)} ${escapeHtml(ing.pantryUnit)}</span>
|
potrzeba <span class="font-medium text-[#d7d2c8]">${formatAmount(ing.amount)} ${escapeHtml(ing.pantryUnit)}</span>
|
||||||
<span class="mx-1 text-gray-300">·</span>
|
<span class="mx-1 text-[#6d6c67]">·</span>
|
||||||
w spiżarni <span class="font-medium ${ing.pantryQty > 0 ? 'text-amber-600' : 'text-gray-400'}">${ing.pantryQty > 0 ? formatAmount(ing.pantryQty) + ' ' + escapeHtml(ing.pantryUnit) : 'brak'}</span>
|
w spiżarni <span class="font-medium ${ing.pantryQty > 0 ? 'text-amber-600' : 'text-gray-400'}">${ing.pantryQty > 0 ? formatAmount(ing.pantryQty) + ' ' + escapeHtml(ing.pantryUnit) : 'brak'}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -721,15 +689,15 @@ function renderIngredientsSheet(state) {
|
|||||||
<p class="text-[10px] font-bold text-emerald-500 uppercase tracking-wider mb-2 px-0.5">
|
<p class="text-[10px] font-bold text-emerald-500 uppercase tracking-wider mb-2 px-0.5">
|
||||||
<i class="fas fa-check text-[9px] mr-1"></i>W spiżarni
|
<i class="fas fa-check text-[9px] mr-1"></i>W spiżarni
|
||||||
</p>
|
</p>
|
||||||
<ul class="border border-gray-100 rounded-xl overflow-hidden bg-white divide-y divide-gray-50">
|
<ul class="border border-[#444442] rounded-xl overflow-hidden bg-[#2d2e2b] divide-y divide-[#353632]">
|
||||||
${okItems.map((ing) => `
|
${okItems.map((ing) => `
|
||||||
<li class="flex items-start gap-3 py-2.5 px-3">
|
<li class="flex items-start gap-3 py-2.5 px-3">
|
||||||
<div class="w-2 h-2 rounded-full bg-emerald-400 mt-1.5 shrink-0"></div>
|
<div class="w-2 h-2 rounded-full bg-emerald-400 mt-1.5 shrink-0"></div>
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<p class="text-[13px] font-medium text-gray-700">${escapeHtml(ing.name)}</p>
|
<p class="text-[13px] font-medium text-[#d7d2c8]">${escapeHtml(ing.name)}</p>
|
||||||
<p class="text-[11px] text-gray-400 mt-0.5">
|
<p class="text-[11px] text-[#9b978f] mt-0.5">
|
||||||
potrzeba <span class="font-medium">${formatAmount(ing.amount)} ${escapeHtml(ing.pantryUnit)}</span>
|
potrzeba <span class="font-medium text-[#d7d2c8]">${formatAmount(ing.amount)} ${escapeHtml(ing.pantryUnit)}</span>
|
||||||
<span class="mx-1 text-gray-300">·</span>
|
<span class="mx-1 text-[#6d6c67]">·</span>
|
||||||
masz <span class="font-medium text-emerald-600">${formatAmount(ing.pantryQty)} ${escapeHtml(ing.pantryUnit)}</span>
|
masz <span class="font-medium text-emerald-600">${formatAmount(ing.pantryQty)} ${escapeHtml(ing.pantryUnit)}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -748,7 +716,7 @@ function renderIngredientsSheet(state) {
|
|||||||
const wd = WEEKDAYS_LONG[day.date.getDay()];
|
const wd = WEEKDAYS_LONG[day.date.getDay()];
|
||||||
const label = `${wd}, ${day.date.getDate()} ${CALENDAR_MONTHS_SHORT[day.date.getMonth()]}`;
|
const label = `${wd}, ${day.date.getDate()} ${CALENDAR_MONTHS_SHORT[day.date.getMonth()]}`;
|
||||||
const shorts = day.items.filter((it) => !it.enough);
|
const shorts = day.items.filter((it) => !it.enough);
|
||||||
return `<div class="rounded-xl border border-amber-200/80 bg-amber-50/50 p-3">
|
return `<div class="rounded-xl border border-amber-200/80 bg-[#2d2e2b] p-3">
|
||||||
<p class="text-[12px] font-semibold text-amber-900">
|
<p class="text-[12px] font-semibold text-amber-900">
|
||||||
<i class="fas fa-calendar-day text-[10px] mr-1.5 text-amber-500"></i>${escapeHtml(label)}
|
<i class="fas fa-calendar-day text-[10px] mr-1.5 text-amber-500"></i>${escapeHtml(label)}
|
||||||
</p>
|
</p>
|
||||||
@@ -797,7 +765,6 @@ export function setupMealPlanner() {
|
|||||||
monthAnchor: startOfDay(new Date()),
|
monthAnchor: startOfDay(new Date()),
|
||||||
selected: startOfDay(new Date()),
|
selected: startOfDay(new Date()),
|
||||||
plans,
|
plans,
|
||||||
nutritionExpanded: false,
|
|
||||||
pickerSlot: null,
|
pickerSlot: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -808,8 +775,6 @@ export function setupMealPlanner() {
|
|||||||
const pickerSheet = document.getElementById('planner-picker-sheet');
|
const pickerSheet = document.getElementById('planner-picker-sheet');
|
||||||
const ingBackdrop = document.getElementById('planner-ing-backdrop');
|
const ingBackdrop = document.getElementById('planner-ing-backdrop');
|
||||||
const ingSheet = document.getElementById('planner-ing-sheet');
|
const ingSheet = document.getElementById('planner-ing-sheet');
|
||||||
const copyBackdrop = document.getElementById('planner-copy-backdrop');
|
|
||||||
const copySheet = document.getElementById('planner-copy-sheet');
|
|
||||||
|
|
||||||
const rerender = () => {
|
const rerender = () => {
|
||||||
syncModeToggle(state.mode);
|
syncModeToggle(state.mode);
|
||||||
@@ -883,16 +848,6 @@ export function setupMealPlanner() {
|
|||||||
rerender();
|
rerender();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('planner-toggle-nutrition')?.addEventListener('click', () => {
|
|
||||||
state.nutritionExpanded = !state.nutritionExpanded;
|
|
||||||
const details = document.getElementById('planner-nutrition-details');
|
|
||||||
const chev = document.getElementById('planner-nutrition-chevron');
|
|
||||||
const btn = document.getElementById('planner-toggle-nutrition');
|
|
||||||
if (details) details.classList.toggle('hidden', !state.nutritionExpanded);
|
|
||||||
if (chev) chev.classList.toggle('rotate-180', state.nutritionExpanded);
|
|
||||||
if (btn) btn.setAttribute('aria-expanded', state.nutritionExpanded ? 'true' : 'false');
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('planner-meal-slots')?.addEventListener('click', (e) => {
|
document.getElementById('planner-meal-slots')?.addEventListener('click', (e) => {
|
||||||
const skipBtn = e.target.closest('.planner-skip-meal');
|
const skipBtn = e.target.closest('.planner-skip-meal');
|
||||||
if (skipBtn) {
|
if (skipBtn) {
|
||||||
@@ -1014,63 +969,6 @@ export function setupMealPlanner() {
|
|||||||
closeSheet(ingBackdrop, ingSheet);
|
closeSheet(ingBackdrop, ingSheet);
|
||||||
});
|
});
|
||||||
|
|
||||||
const closeCopy = () => closeSheet(copyBackdrop, copySheet);
|
|
||||||
bindPlannerSheetDragClose(copySheet, closeCopy);
|
|
||||||
copyBackdrop?.addEventListener('click', closeCopy);
|
|
||||||
|
|
||||||
document.getElementById('planner-copy-day')?.addEventListener('click', () => {
|
|
||||||
const srcKey = dateKey(state.selected);
|
|
||||||
const srcPlan = state.plans[srcKey];
|
|
||||||
if (!srcPlan || Object.keys(srcPlan).length === 0) {
|
|
||||||
showPlannerToast('Ten dzień jest pusty — nie ma co kopiować.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const copyList = document.getElementById('planner-copy-list');
|
|
||||||
if (!copyList) return;
|
|
||||||
const days = [];
|
|
||||||
for (let i = -3; i <= 10; i++) {
|
|
||||||
if (i === 0) continue;
|
|
||||||
const d = addDays(state.selected, i);
|
|
||||||
days.push(d);
|
|
||||||
}
|
|
||||||
copyList.innerHTML = days.map((d) => {
|
|
||||||
const wd = WEEKDAYS_LONG[d.getDay()];
|
|
||||||
const label = `${wd}, ${d.getDate()} ${CALENDAR_MONTHS_SHORT[d.getMonth()]}`;
|
|
||||||
const hasMeals = dayHasAnyMeal(state.plans, d);
|
|
||||||
const badge = hasMeals ? '<span class="text-[10px] text-amber-600 font-semibold">ma posiłki</span>' : '';
|
|
||||||
return `<button type="button" class="planner-copy-target w-full flex items-center justify-between gap-2 p-3 rounded-xl border border-gray-200 bg-gray-50/80 hover:border-gray-900 hover:bg-white transition-all text-left" data-target-ts="${d.getTime()}">
|
|
||||||
<span class="text-[13px] font-semibold text-gray-900">${escapeHtml(label)}</span>
|
|
||||||
${badge}
|
|
||||||
</button>`;
|
|
||||||
}).join('');
|
|
||||||
openSheet(copyBackdrop, copySheet);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('planner-copy-list')?.addEventListener('click', (e) => {
|
|
||||||
const btn = e.target.closest('.planner-copy-target');
|
|
||||||
if (!btn) return;
|
|
||||||
const targetDate = new Date(Number(btn.getAttribute('data-target-ts')));
|
|
||||||
const srcKey = dateKey(state.selected);
|
|
||||||
const tgtKey = dateKey(targetDate);
|
|
||||||
const srcPlan = state.plans[srcKey];
|
|
||||||
if (!srcPlan) return;
|
|
||||||
|
|
||||||
const copy = {};
|
|
||||||
for (const [slotId, entries] of Object.entries(srcPlan)) {
|
|
||||||
if (slotId === '_skipped') {
|
|
||||||
copy._skipped = { ...entries };
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (Array.isArray(entries)) {
|
|
||||||
copy[slotId] = entries.map((e) => ({ ...e, id: newPlanEntryId() }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.plans[tgtKey] = copy;
|
|
||||||
closeCopy();
|
|
||||||
persist();
|
|
||||||
showPlannerToast('Plan skopiowany!');
|
|
||||||
});
|
|
||||||
|
|
||||||
ingSheet?.addEventListener('click', (e) => {
|
ingSheet?.addEventListener('click', (e) => {
|
||||||
const row = e.target.closest('.planner-ing-row');
|
const row = e.target.closest('.planner-ing-row');
|
||||||
if (!row || !ingSheet.contains(row)) return;
|
if (!row || !ingSheet.contains(row)) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user