Redesign shopping list
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m19s
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m19s
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { INGREDIENTS, CATEGORY_LABELS, PRODUCTS, ingredientHasProducts } from '../data/catalog.js?v=9';
|
||||
import { PANTRY_STORAGE_KEY, PANTRY_STORAGE_KEY_V2, SHOPPING_STORAGE_KEY } from '../storageKeys.js';
|
||||
import { PANTRY_STORAGE_KEY, PANTRY_STORAGE_KEY_V2, SHOPPING_STORAGE_KEY, SHOPPING_SESSION_KEY } from '../storageKeys.js';
|
||||
|
||||
export const KITCHEN_LIST_ID = 'kitchen';
|
||||
export const MISC_LIST_ID = 'misc';
|
||||
@@ -535,6 +535,148 @@ export function setPantryProductQty(ingredientId, productId, qty) {
|
||||
return pantry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dodaj delta do spiżarni (używane przy zakupie ze listy).
|
||||
* @param {string} ingredientId
|
||||
* @param {string|undefined} productId
|
||||
* @param {number} amount
|
||||
*/
|
||||
export function addAmountToPantry(ingredientId, productId, amount) {
|
||||
const pantry = loadPantry();
|
||||
const rounded = Math.round(amount * 1000) / 1000;
|
||||
if (productId && PRODUCTS[productId]) {
|
||||
let val = pantry[ingredientId];
|
||||
if (!val || typeof val === 'number') {
|
||||
val = { items: [], _total: 0 };
|
||||
pantry[ingredientId] = val;
|
||||
}
|
||||
const idx = val.items.findIndex((i) => i.productId === productId);
|
||||
if (idx >= 0) val.items[idx].qty = Math.round((val.items[idx].qty + rounded) * 1000) / 1000;
|
||||
else val.items.push({ productId, qty: rounded });
|
||||
recalcTotal(val);
|
||||
} else {
|
||||
const cur = typeof pantry[ingredientId] === 'number' ? pantry[ingredientId] : 0;
|
||||
pantry[ingredientId] = Math.round((cur + rounded) * 1000) / 1000;
|
||||
}
|
||||
savePantry(pantry);
|
||||
return pantry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Odejmij delta ze spiżarni (undo zakupu).
|
||||
* @param {string} ingredientId
|
||||
* @param {string|undefined} productId
|
||||
* @param {number} amount
|
||||
*/
|
||||
export function subtractFromPantry(ingredientId, productId, amount) {
|
||||
const pantry = loadPantry();
|
||||
if (productId && PRODUCTS[productId]) {
|
||||
const val = pantry[ingredientId];
|
||||
if (!val || typeof val === 'number') return pantry;
|
||||
const idx = val.items.findIndex((i) => i.productId === productId);
|
||||
if (idx < 0) return pantry;
|
||||
val.items[idx].qty = Math.max(0, Math.round((val.items[idx].qty - amount) * 1000) / 1000);
|
||||
if (val.items[idx].qty <= 0) val.items.splice(idx, 1);
|
||||
recalcTotal(val);
|
||||
if (val._total <= 0) delete pantry[ingredientId];
|
||||
} else {
|
||||
const cur = typeof pantry[ingredientId] === 'number' ? pantry[ingredientId] : 0;
|
||||
const next = Math.max(0, Math.round((cur - amount) * 1000) / 1000);
|
||||
if (next <= 0) delete pantry[ingredientId];
|
||||
else pantry[ingredientId] = next;
|
||||
}
|
||||
savePantry(pantry);
|
||||
return pantry;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
* Session zakupowy — selectedDays + log zakupów bieżącej sesji
|
||||
* ══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/**
|
||||
* @typedef {{ id: string, ingredientId: string, productId?: string, name: string, addedAmount: number, unit: string, category: string, timestamp: number }} SessionLogEntry
|
||||
* @typedef {{ selectedDays: string[], sessionLog: SessionLogEntry[] }} ShoppingSessionState
|
||||
*/
|
||||
|
||||
function defaultShoppingSession() {
|
||||
return { selectedDays: [], sessionLog: [] };
|
||||
}
|
||||
|
||||
function normalizeShoppingSession(raw) {
|
||||
if (!raw || typeof raw !== 'object') return defaultShoppingSession();
|
||||
const selectedDays = Array.isArray(raw.selectedDays)
|
||||
? raw.selectedDays.filter((d) => typeof d === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(d))
|
||||
: [];
|
||||
const sessionLog = Array.isArray(raw.sessionLog)
|
||||
? raw.sessionLog.filter((e) => e && typeof e.ingredientId === 'string' && Number.isFinite(e.addedAmount))
|
||||
: [];
|
||||
return { selectedDays, sessionLog };
|
||||
}
|
||||
|
||||
/** @returns {ShoppingSessionState} */
|
||||
export function loadShoppingSession() {
|
||||
try {
|
||||
const raw = localStorage.getItem(SHOPPING_SESSION_KEY);
|
||||
if (!raw) return defaultShoppingSession();
|
||||
return normalizeShoppingSession(JSON.parse(raw));
|
||||
} catch {
|
||||
return defaultShoppingSession();
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {ShoppingSessionState} s */
|
||||
export function saveShoppingSession(s) {
|
||||
try {
|
||||
localStorage.setItem(SHOPPING_SESSION_KEY, JSON.stringify(s));
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/** @returns {string[]} */
|
||||
export function getSelectedDays() {
|
||||
return loadShoppingSession().selectedDays;
|
||||
}
|
||||
|
||||
/** @param {string[]} days */
|
||||
export function setSelectedDays(days) {
|
||||
const s = loadShoppingSession();
|
||||
s.selectedDays = days;
|
||||
saveShoppingSession(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ ingredientId: string, productId?: string, name: string, addedAmount: number, unit: string, category: string }} entry
|
||||
* @returns {string} new entry id
|
||||
*/
|
||||
export function addSessionLogEntry(entry) {
|
||||
const s = loadShoppingSession();
|
||||
const id = newId('sl');
|
||||
s.sessionLog.push({
|
||||
id,
|
||||
ingredientId: entry.ingredientId,
|
||||
productId: entry.productId || undefined,
|
||||
name: entry.name,
|
||||
addedAmount: Math.round(entry.addedAmount * 100) / 100,
|
||||
unit: entry.unit,
|
||||
category: entry.category,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
saveShoppingSession(s);
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @param {string} entryId */
|
||||
export function removeSessionLogEntry(entryId) {
|
||||
const s = loadShoppingSession();
|
||||
s.sessionLog = s.sessionLog.filter((e) => e.id !== entryId);
|
||||
saveShoppingSession(s);
|
||||
}
|
||||
|
||||
export function clearSessionLog() {
|
||||
const s = loadShoppingSession();
|
||||
s.sessionLog = [];
|
||||
saveShoppingSession(s);
|
||||
}
|
||||
|
||||
/** Kupione ze listy kuchennej → spiżarnia (zawsze ta lista, niezależnie od aktywnej zakładki). */
|
||||
export function applyCheckedKitchenListToPantry() {
|
||||
const s = loadShoppingState();
|
||||
|
||||
Reference in New Issue
Block a user