v1.1.0 Kalendarz i listy zakupów
All checks were successful
Build and Push Docker Images / build-and-push (push) Successful in 3m10s

Dodaje wspólny kalendarz wydarzeń grupy (zakres dat/godzin, cały dzień,
notatki, lokalizacja, uczestnicy z gospodarstwa + goście zewnętrzni)
oraz listy zakupów (wiele list, dodawanie/edycja/odznaczanie/usuwanie
produktów). Nowe tabele w schemacie SQLite są czysto addytywne
(CREATE TABLE IF NOT EXISTS), więc nie ruszają istniejących danych.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 23:26:00 +02:00
parent 350ccdbaf7
commit 9c39b33636
28 changed files with 1537 additions and 9 deletions

View File

@@ -0,0 +1,67 @@
import { useState } from 'react';
import { useUpdateShoppingItem, useDeleteShoppingItem } from '../api/queries.js';
import { useTranslation } from '../i18n/I18nContext.jsx';
import Icon from './Icon.jsx';
export default function ShoppingListItemRow({ item, listId }) {
const updateItem = useUpdateShoppingItem();
const deleteItem = useDeleteShoppingItem();
const { t } = useTranslation();
const [editing, setEditing] = useState(false);
const [name, setName] = useState(item.name);
const [quantity, setQuantity] = useState(item.quantity || '');
function toggleChecked() {
updateItem.mutate({ listId, itemId: item.id, checked: !item.checked });
}
function saveEdit() {
setEditing(false);
if (!name.trim()) {
setName(item.name);
return;
}
updateItem.mutate({ listId, itemId: item.id, name: name.trim(), quantity: quantity.trim() || null });
}
return (
<div className={`shopping-item-row ${item.checked ? 'checked' : ''}`}>
<input type="checkbox" checked={!!item.checked} onChange={toggleChecked} />
{editing ? (
<div className="shopping-item-edit">
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
onBlur={saveEdit}
onKeyDown={(e) => e.key === 'Enter' && saveEdit()}
autoFocus
/>
<input
type="text"
className="shopping-item-qty-input"
placeholder={t('shoppingListDetail.quantityPlaceholder')}
value={quantity}
onChange={(e) => setQuantity(e.target.value)}
onBlur={saveEdit}
onKeyDown={(e) => e.key === 'Enter' && saveEdit()}
/>
</div>
) : (
<button type="button" className="shopping-item-name" onClick={() => setEditing(true)}>
<span>{item.name}</span>
{item.quantity && <span className="shopping-item-qty">{item.quantity}</span>}
</button>
)}
<button
type="button"
className="icon-btn"
onClick={() => deleteItem.mutate({ listId, itemId: item.id })}
aria-label={t('common.delete')}
>
<Icon name="delete" />
</button>
</div>
);
}