v1.1.0 Kalendarz i listy zakupów
All checks were successful
Build and Push Docker Images / build-and-push (push) Successful in 3m10s
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:
67
frontend/src/components/ShoppingListItemRow.jsx
Normal file
67
frontend/src/components/ShoppingListItemRow.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user