diff --git a/index.html b/index.html
index 1961772..11c9109 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,7 @@
-
+
@@ -22,6 +22,8 @@
input[type=range]::-webkit-slider-runnable-track {
width: 100%; height: 4px; cursor: pointer; background: #e5e7eb; border-radius: 2px;
}
+ .dark input[type=range]::-webkit-slider-thumb { background: #e8e8e8; }
+ .dark input[type=range]::-webkit-slider-runnable-track { background: #333333; }
/* Ingredient Active States */
.ingredient-active .check-box,
@@ -30,14 +32,151 @@
.ingredient-active .rd-check-icon { display: block; }
.ingredient-active .ingredient-text,
.ingredient-active .rd-ing-text { text-decoration: line-through; color: #9ca3af; }
+ .dark .ingredient-active .check-box,
+ .dark .ingredient-active .rd-check-box { background-color: #f0f0f0; border-color: #f0f0f0; }
/* Utilities */
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border-width: 0; }
+
+ /* ===== Dark Mode Overrides ===== */
+ .dark { color-scheme: dark; }
+
+ /* --- Gray backgrounds --- */
+ .dark .bg-white { background-color: #1a1a1a !important; }
+ .dark .bg-gray-50 { background-color: #0d0d0d !important; }
+ .dark .bg-gray-100 { background-color: #242424 !important; }
+ .dark .bg-gray-200 { background-color: #2e2e2e !important; }
+ .dark .bg-gray-900 { background-color: #f0f0f0 !important; }
+ .dark .bg-gray-900.text-white,
+ .dark .bg-gray-900 .text-white { color: #0d0d0d !important; }
+ .dark .border-gray-900 { border-color: #f0f0f0 !important; }
+
+ /* Semi-transparent backgrounds */
+ .dark .bg-white\/90 { background-color: rgba(26, 26, 26, 0.92) !important; }
+ .dark .bg-white\/80 { background-color: rgba(26, 26, 26, 0.85) !important; }
+ .dark .bg-gray-50\/80 { background-color: rgba(20, 20, 20, 0.85) !important; }
+ .dark .bg-gray-50\/90 { background-color: rgba(20, 20, 20, 0.92) !important; }
+
+ /* --- Gray text --- */
+ .dark .text-gray-900 { color: #f5f5f5 !important; }
+ .dark .text-gray-800 { color: #e8e8e8 !important; }
+ .dark .text-gray-700 { color: #d4d4d4 !important; }
+ .dark .text-gray-600 { color: #a0a0a0 !important; }
+ .dark .text-gray-500 { color: #888888 !important; }
+ .dark .text-gray-400 { color: #666666 !important; }
+ .dark .text-gray-300 { color: #444444 !important; }
+ .dark .text-black { color: #ffffff !important; }
+
+ /* --- Gray borders --- */
+ .dark .border-gray-200 { border-color: #2a2a2a !important; }
+ .dark .border-gray-100 { border-color: #1f1f1f !important; }
+ .dark .border-gray-300 { border-color: #333333 !important; }
+
+ /* --- Dividers --- */
+ .dark .divide-gray-50 > :not([hidden]) ~ :not([hidden]) { border-color: #1a1a1a !important; }
+ .dark .divide-gray-100 > :not([hidden]) ~ :not([hidden]) { border-color: #1f1f1f !important; }
+ .dark .divide-gray-200 > :not([hidden]) ~ :not([hidden]) { border-color: #2a2a2a !important; }
+ .dark .divide-red-50 > :not([hidden]) ~ :not([hidden]) { border-color: #2a1a1a !important; }
+
+ /* --- Ring utilities --- */
+ .dark .ring-gray-200 { --tw-ring-color: #2a2a2a !important; }
+ .dark .ring-gray-900 { --tw-ring-color: #f0f0f0 !important; }
+
+ /* --- Hover states --- */
+ .dark .hover\:bg-gray-50:hover { background-color: #1f1f1f !important; }
+ .dark .hover\:bg-gray-100:hover { background-color: #2e2e2e !important; }
+ .dark .hover\:bg-white:hover { background-color: #242424 !important; }
+ .dark .hover\:text-gray-700:hover { color: #e8e8e8 !important; }
+ .dark .hover\:text-gray-900:hover { color: #f5f5f5 !important; }
+ .dark .hover\:text-black:hover { color: #ffffff !important; }
+ .dark .hover\:bg-black:hover { background-color: #e0e0e0 !important; color: #0d0d0d !important; }
+ .dark .hover\:bg-red-50:hover { background-color: #2a1a1a !important; }
+ .dark .hover\:border-gray-300:hover { border-color: #444444 !important; }
+ .dark .hover\:border-gray-400:hover { border-color: #555555 !important; }
+ .dark .hover\:border-gray-900:hover { border-color: #f0f0f0 !important; }
+ .dark .hover\:border-red-200:hover { border-color: #5c2020 !important; }
+
+ /* --- Focus --- */
+ .dark .focus\:border-gray-400:focus { border-color: #555555 !important; }
+ .dark .focus\:border-gray-300:focus { border-color: #444444 !important; }
+
+ /* --- Shadows --- */
+ .dark .shadow-lg { box-shadow: 0 10px 15px -3px rgba(0,0,0,0.5), 0 4px 6px -4px rgba(0,0,0,0.4) !important; }
+ .dark .shadow-sm { box-shadow: 0 1px 3px 0 rgba(0,0,0,0.4) !important; }
+ .dark .border-dashed { border-color: #333333 !important; }
+
+ /* --- Inputs --- */
+ .dark input, .dark select, .dark textarea {
+ background-color: #1a1a1a !important;
+ color: #e8e8e8 !important;
+ border-color: #2a2a2a !important;
+ }
+ .dark input::placeholder, .dark textarea::placeholder {
+ color: #666666 !important;
+ }
+
+ /* --- Toast --- */
+ .dark #app-toast .rounded-xl,
+ .dark #planner-toast .rounded-xl {
+ background-color: #f0f0f0 !important;
+ color: #0d0d0d !important;
+ }
+
+ /* --- Amber / Summary card --- */
+ .dark #planner-summary-card {
+ background: #1e1a10 !important;
+ border-color: #4a3800 !important;
+ }
+ .dark .bg-amber-50 { background-color: #1e1a10 !important; }
+ .dark .bg-amber-50\/30 { background-color: rgba(60, 45, 0, 0.25) !important; }
+ .dark .bg-amber-50\/50 { background-color: rgba(80, 60, 0, 0.2) !important; }
+ .dark .bg-amber-100\/50 { background-color: rgba(80, 60, 0, 0.2) !important; }
+ .dark .hover\:bg-amber-100\/50:hover { background-color: rgba(80, 60, 0, 0.35) !important; }
+ .dark .border-amber-200 { border-color: #4a3800 !important; }
+ .dark .border-amber-200\/80 { border-color: #4a3800 !important; }
+ .dark .border-amber-200\/60 { border-color: #3d2e00 !important; }
+ .dark .border-amber-100 { border-color: #332600 !important; }
+ .dark .text-amber-900\/70 { color: #fbbf24 !important; }
+ .dark .text-amber-900\/80 { color: #fbbf24 !important; }
+ .dark .text-amber-900 { color: #fcd34d !important; }
+ .dark .text-amber-600 { color: #f59e0b !important; }
+ .dark .text-amber-500 { color: #f59e0b !important; }
+ .dark .divide-amber-100\/80 > :not([hidden]) ~ :not([hidden]) { border-color: #332600 !important; }
+
+ /* --- Emerald accents --- */
+ .dark .bg-emerald-50 { background-color: #0a1f15 !important; }
+ .dark .bg-emerald-100 { background-color: #064e3b !important; }
+ .dark .bg-emerald-500 { background-color: #10b981 !important; }
+ .dark .bg-emerald-600 { background-color: #059669 !important; }
+ .dark .hover\:bg-emerald-700:hover { background-color: #047857 !important; }
+ .dark .hover\:bg-emerald-100\/80:hover { background-color: rgba(6, 78, 59, 0.5) !important; }
+ .dark .border-emerald-200\/80 { border-color: #065f46 !important; }
+ .dark .text-emerald-800 { color: #6ee7b7 !important; }
+ .dark .text-emerald-600 { color: #34d399 !important; }
+ .dark .text-emerald-600\/80 { color: rgba(52, 211, 153, 0.85) !important; }
+ .dark .text-emerald-500 { color: #34d399 !important; }
+
+ /* --- Red accents --- */
+ .dark .bg-red-50 { background-color: #1f0a0a !important; }
+ .dark .bg-red-100 { background-color: #450a0a !important; }
+ .dark .bg-red-500 { background-color: #ef4444 !important; }
+ .dark .border-red-200\/80 { border-color: #5c2020 !important; }
+ .dark .border-red-100\/80 { border-color: #3b1010 !important; }
+ .dark .text-red-800 { color: #fca5a5 !important; }
+ .dark .text-red-600 { color: #f87171 !important; }
+ .dark .text-red-600\/80 { color: rgba(248, 113, 113, 0.85) !important; }
+ .dark .text-red-500 { color: #f87171 !important; }
+ .dark .text-red-400 { color: #fb7185 !important; }
+ .dark .hover\:text-red-600:hover { color: #f87171 !important; }
+ .dark .bg-amber-500 { background-color: #f59e0b !important; }
+
diff --git a/js/app.js b/js/app.js
index 71e2cca..f7f23f6 100644
--- a/js/app.js
+++ b/js/app.js
@@ -3,7 +3,6 @@ import { getFilterHTML, setupFilter } from './views/Filter.js?v=2';
import { getRecipeDetailHTML, setupRecipeDetail } from './views/RecipeDetailV2.js?v=2';
import { getMealPlannerHTML, setupMealPlanner } from './views/MealPlanner.js?v=2';
import { getPantryHTML, refreshPantry, setupPantry } from './views/Pantry.js?v=2';
-import { getShoppingHTML, refreshShopping, setupShopping } from './views/Shopping.js?v=2';
function getAppToastHTML() {
return `
@@ -14,6 +13,7 @@ function getAppToastHTML() {
}
function getBottomNavHTML() {
+ const isDark = document.documentElement.classList.contains('dark');
return `
`;
}
+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 ? '#0d0d0d' : '#ffffff');
+ });
+}
+
function setupTabs() {
const main = document.getElementById('main-view');
const planner = document.getElementById('planner-view');
const pantry = document.getElementById('pantry-view');
- const shopping = document.getElementById('shopping-view');
const nav = document.getElementById('app-bottom-nav');
- if (!main || !planner || !pantry || !shopping || !nav) return;
+ 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]';
@@ -51,15 +69,13 @@ function setupTabs() {
main.classList.toggle('hidden', tab !== 'recipes');
planner.classList.toggle('hidden', tab !== 'planner');
pantry.classList.toggle('hidden', tab !== 'pantry');
- shopping.classList.toggle('hidden', tab !== 'shopping');
if (tab === 'pantry') refreshPantry();
- if (tab === 'shopping') refreshShopping();
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' || id === 'shopping') {
+ if (id === 'recipes' || id === 'planner' || id === 'pantry') {
btn.className = id === tab ? activeTab : idleTab;
}
});
@@ -69,14 +85,13 @@ function setupTabs() {
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' || tab === 'shopping') apply(tab);
+ if (tab === 'recipes' || tab === 'planner' || tab === 'pantry') apply(tab);
});
apply('recipes');
window.refreshStockViews = () => {
refreshPantry();
- refreshShopping();
};
}
@@ -87,7 +102,6 @@ document.addEventListener('DOMContentLoaded', () => {
${getRecipeListHTML()}
${getMealPlannerHTML()}
${getPantryHTML()}
- ${getShoppingHTML()}
${getBottomNavHTML()}
${getRecipeDetailHTML()}
${getFilterHTML()}
@@ -95,10 +109,10 @@ document.addEventListener('DOMContentLoaded', () => {
`;
setupTabs();
+ setupThemeToggle();
setupRecipeList();
setupMealPlanner();
setupPantry();
- setupShopping();
setupFilter();
setupRecipeDetail();
});
diff --git a/js/views/Shopping.js b/js/views/Shopping.js
deleted file mode 100644
index 17e1ed4..0000000
--- a/js/views/Shopping.js
+++ /dev/null
@@ -1,290 +0,0 @@
-import {
- addFreeformLine,
- addFreeformList,
- applyCheckedKitchenListToPantry,
- categoryLabel,
- clearCheckedInList,
- deleteList,
- getActiveList,
- getListSummaries,
- KITCHEN_LIST_ID,
- removeItemFromList,
- setActiveListId,
- toggleItemInList,
- updateKitchenItemAmount,
-} from '../services/pantryShopping.js';
-import { showAppToast } from '../ui/toast.js';
-
-function escapeHtml(s) {
- return String(s)
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"');
-}
-
-export function getShoppingHTML() {
- return `
-
- `;
-}
-
-function syncListSelect() {
- const sel = document.getElementById('shopping-list-select');
- if (!sel) return;
- const { lists, activeListId } = getListSummaries();
- sel.innerHTML = lists.map((l) => {
- const suffix = l.openCount ? ` (${l.openCount})` : '';
- const label = `${l.name}${suffix}`;
- return ``;
- }).join('');
- sel.value = activeListId;
-}
-
-function syncChromeForList() {
- const list = getActiveList();
- const isKitchen = list.type === 'kitchen';
- const delBtn = document.getElementById('shopping-delete-list');
- const ffAdd = document.getElementById('shopping-freeform-add');
- const kitchenActions = document.getElementById('shopping-kitchen-actions');
-
- if (ffAdd) ffAdd.classList.toggle('hidden', isKitchen);
- if (delBtn) delBtn.classList.toggle('hidden', isKitchen);
-
- if (kitchenActions) {
- const hasChecked = isKitchen && list.items.some((i) => i.checked);
- kitchenActions.classList.toggle('hidden', !hasChecked);
- }
-}
-
-function renderKitchenItems() {
- const root = document.getElementById('shopping-list-root');
- if (!root) return;
-
- const list = getActiveList();
- if (list.type !== 'kitchen') return;
-
- const items = list.items;
- if (items.length === 0) {
- root.innerHTML = 'Brak pozycji.
';
- return;
- }
-
- const groups = {};
- for (const it of items) {
- const c = it.category || 'inne';
- if (!groups[c]) groups[c] = [];
- groups[c].push(it);
- }
-
- root.innerHTML = Object.keys(groups)
- .sort((a, b) => categoryLabel(a).localeCompare(categoryLabel(b)))
- .map((cat) => `
-
-
${escapeHtml(categoryLabel(cat))}
-
- ${groups[cat].map((it) => `
- -
-
- ${it.checked ? '' : ''}
-
-
-
${escapeHtml(it.name)}
-
${escapeHtml(String(it.amount))} ${escapeHtml(it.unit)}
- ${it.sourceNote ? `
${escapeHtml(it.sourceNote)}
` : ''}
-
-
-
-
- `).join('')}
-
-
- `)
- .join('');
-
- bindItemButtons(list.id);
-}
-
-function renderFreeformItems() {
- const root = document.getElementById('shopping-list-root');
- if (!root) return;
-
- const list = getActiveList();
- if (list.type !== 'freeform') return;
-
- const items = list.items;
- if (items.length === 0) {
- root.innerHTML = 'Dodaj dowolny tekst powyżej — bez powiązania z katalogiem składników.
';
- return;
- }
-
- root.innerHTML = `
-
-
- ${items.map((it) => `
- -
-
- ${it.checked ? '' : ''}
-
-
-
${escapeHtml(it.text)}
- ${it.note ? `
${escapeHtml(it.note)}
` : ''}
-
-
-
-
- `).join('')}
-
-
`;
-
- bindItemButtons(list.id);
-}
-
-function bindItemButtons(listId) {
- const root = document.getElementById('shopping-list-root');
- if (!root) return;
-
- root.querySelectorAll('[data-shop-toggle]').forEach((btn) => {
- btn.addEventListener('click', () => {
- const id = btn.getAttribute('data-shop-toggle');
- if (id) toggleItemInList(listId, id);
- refreshShopping();
- });
- });
- root.querySelectorAll('[data-shop-remove]').forEach((btn) => {
- btn.addEventListener('click', () => {
- const id = btn.getAttribute('data-shop-remove');
- if (id) removeItemFromList(listId, id);
- refreshShopping();
- });
- });
- root.querySelectorAll('[data-shop-edit-amount]').forEach((btn) => {
- btn.addEventListener('click', (e) => {
- e.stopPropagation();
- const itemId = btn.getAttribute('data-shop-edit-amount');
- const current = btn.getAttribute('data-current-amount');
- const unit = btn.getAttribute('data-unit');
- const newVal = window.prompt(`Nowa ilość (${unit}):`, current);
- if (newVal === null) return;
- const num = parseFloat(newVal.replace(',', '.'));
- if (!Number.isFinite(num) || num < 0) {
- showAppToast('Nieprawidłowa wartość.');
- return;
- }
- updateKitchenItemAmount(listId, itemId, num);
- refreshShopping();
- });
- });
-}
-
-export function refreshShopping() {
- syncListSelect();
- syncChromeForList();
- const list = getActiveList();
- if (list.type === 'kitchen') renderKitchenItems();
- else renderFreeformItems();
-}
-
-export function setupShopping() {
- const sel = document.getElementById('shopping-list-select');
- sel?.addEventListener('change', () => {
- const v = sel.value;
- if (v) setActiveListId(v);
- refreshShopping();
- });
-
- document.getElementById('shopping-new-list')?.addEventListener('click', () => {
- const name = window.prompt('Nazwa nowej listy (dowolne zakupy):', 'Nowa lista');
- if (name === null) return;
- addFreeformList(name);
- showAppToast('Utworzono listę.');
- refreshShopping();
- });
-
- document.getElementById('shopping-delete-list')?.addEventListener('click', () => {
- const list = getActiveList();
- if (list.id === KITCHEN_LIST_ID) return;
- if (!window.confirm(`Usunąć listę „${list.name}”?`)) return;
- deleteList(list.id);
- showAppToast('Lista usunięta.');
- refreshShopping();
- });
-
- const submitFreeform = () => {
- const list = getActiveList();
- if (list.type !== 'freeform') return;
- const input = document.getElementById('shopping-freeform-input');
- const note = document.getElementById('shopping-freeform-note');
- const text = input?.value || '';
- addFreeformLine(list.id, text, note?.value || '');
- if (input) input.value = '';
- if (note) note.value = '';
- refreshShopping();
- };
-
- document.getElementById('shopping-freeform-submit')?.addEventListener('click', submitFreeform);
- document.getElementById('shopping-freeform-input')?.addEventListener('keydown', (e) => {
- if (e.key === 'Enter') {
- e.preventDefault();
- submitFreeform();
- }
- });
-
- document.getElementById('shopping-to-pantry')?.addEventListener('click', () => {
- const list = getActiveList();
- if (list.type !== 'kitchen') return;
- const checked = list.items.filter((i) => i.checked);
- if (checked.length === 0) return;
- const preview = checked.map((i) => ` • ${i.name}: ${i.amount} ${i.unit}`).join('\n');
- if (!window.confirm(`Przenieść do spiżarni?\n\n${preview}`)) return;
- applyCheckedKitchenListToPantry();
- showAppToast(`Przeniesiono ${checked.length} pozycji do spiżarni.`);
- window.refreshPantry?.();
- refreshShopping();
- });
-
- document.getElementById('shopping-clear-checked')?.addEventListener('click', () => {
- const list = getActiveList();
- const checkedCount = list.items.filter((i) => i.checked).length;
- if (checkedCount === 0) return;
- clearCheckedInList(list.id);
- showAppToast(`Usunięto ${checkedCount} kupionych pozycji.`);
- refreshShopping();
- });
-
- refreshShopping();
- window.refreshShopping = refreshShopping;
-}