my-recipes/frontend/src/components/PinnedGroceryLists.jsx

223 lines
6.0 KiB
JavaScript

import { useState, useEffect } from "react";
import { getGroceryLists, updateGroceryList } from "../groceryApi";
function PinnedGroceryLists({ onShowToast }) {
const [pinnedLists, setPinnedLists] = useState([]);
useEffect(() => {
loadPinnedLists();
}, []);
const loadPinnedLists = async () => {
try {
const allLists = await getGroceryLists();
const pinned = allLists.filter((list) => list.is_pinned);
setPinnedLists(pinned);
} catch (error) {
console.error("Failed to load pinned lists", error);
}
};
const handleToggleItem = async (listId, itemIndex) => {
const list = pinnedLists.find((l) => l.id === listId);
if (!list || !list.can_edit) return;
const updatedItems = [...list.items];
const item = updatedItems[itemIndex];
if (item.startsWith("✓ ")) {
updatedItems[itemIndex] = item.substring(2);
} else {
updatedItems[itemIndex] = "✓ " + item;
}
try {
await updateGroceryList(listId, { items: updatedItems });
setPinnedLists(
pinnedLists.map((l) =>
l.id === listId ? { ...l, items: updatedItems } : l
)
);
} catch (error) {
onShowToast?.(error.message, "error");
}
};
if (pinnedLists.length === 0) {
return null;
}
return (
<div className="pinned-grocery-lists">
{pinnedLists.map((list) => (
<div key={list.id} className="pinned-note">
<div className="pin-icon">📌</div>
<h3 className="note-title">{list.name}</h3>
<ul className="note-items">
{list.items.length === 0 ? (
<li className="empty-note">הרשימה ריקה</li>
) : (
list.items.map((item, index) => {
const isChecked = item.startsWith("✓ ");
const itemText = isChecked ? item.substring(2) : item;
return (
<li
key={index}
className={`note-item ${isChecked ? "checked" : ""}`}
onClick={() =>
list.can_edit && handleToggleItem(list.id, index)
}
style={{ cursor: list.can_edit ? "pointer" : "default" }}
>
<span className="checkbox">{isChecked ? "☑" : "☐"}</span>
<span className="item-text">{itemText}</span>
</li>
);
})
)}
</ul>
</div>
))}
<style jsx>{`
@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400;700&display=swap');
.pinned-grocery-lists {
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: 1rem;
max-width: 320px;
}
.pinned-note {
position: relative;
background: linear-gradient(135deg, #fff9e6 0%, #fffaed 100%);
padding: 2rem 1.5rem 1.5rem;
border-radius: 4px;
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.1),
0 4px 12px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.5);
border: 1px solid #f5e6c8;
transform: rotate(-1deg);
transition: all 0.3s ease;
font-family: 'Caveat', cursive;
}
.pinned-note:nth-child(even) {
transform: rotate(1deg);
background: linear-gradient(135deg, #fff5e1 0%, #fff9eb 100%);
}
.pinned-note:hover {
transform: rotate(0deg) scale(1.02);
box-shadow:
0 4px 8px rgba(0, 0, 0, 0.15),
0 8px 20px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
.pin-icon {
position: absolute;
top: -8px;
right: 20px;
font-size: 2rem;
filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.2));
transform: rotate(25deg);
}
.note-title {
margin: 0 0 1rem 0;
font-size: 1.8rem;
color: #5a4a2a;
font-weight: 700;
text-align: center;
border-bottom: 2px solid rgba(90, 74, 42, 0.2);
padding-bottom: 0.5rem;
letter-spacing: 0.5px;
}
.note-items {
list-style: none;
padding: 0;
margin: 0;
}
.empty-note {
text-align: center;
color: #9a8a6a;
font-size: 1.3rem;
padding: 1rem;
font-style: italic;
}
.note-item {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 0.5rem 0;
font-size: 1.4rem;
color: #4a3a1a;
line-height: 1.6;
transition: all 0.2s;
}
.note-item:hover {
color: #2a1a0a;
}
.note-item.checked .item-text {
text-decoration: line-through;
opacity: 0.5;
}
.checkbox {
font-size: 1.3rem;
flex-shrink: 0;
margin-top: 2px;
}
.item-text {
flex: 1;
word-break: break-word;
}
/* Paper texture overlay */
.pinned-note::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
repeating-linear-gradient(
0deg,
transparent,
transparent 31px,
rgba(90, 74, 42, 0.03) 31px,
rgba(90, 74, 42, 0.03) 32px
);
pointer-events: none;
border-radius: 4px;
}
/* Light mode specific adjustments */
@media (prefers-color-scheme: light) {
.pinned-note {
background: linear-gradient(135deg, #fffbf0 0%, #fffef8 100%);
border-color: #f0e0c0;
}
.pinned-note:nth-child(even) {
background: linear-gradient(135deg, #fff8e8 0%, #fffcf3 100%);
}
}
`}</style>
</div>
);
}
export default PinnedGroceryLists;