🛒 Meine Einkaufsliste
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>🛒 Einkaufswagen</title>
<style>
body{
margin:0;
font-family:Arial;
padding-bottom:120px;
}
.full-red{
width:100%;
background:#A52020;
color:white;
text-align:center;
padding:40px 20px;
}
.wrapper{
max-width:900px;
margin:40px auto;
padding:0 20px;
}
.category{
margin-bottom:30px;
}
.category h3{
border-bottom:2px solid #eee;
padding-bottom:6px;
cursor:pointer;
display:flex;
justify-content:space-between;
align-items:center;
}
.arrow{
font-size:14px;
}
.item{
display:flex;
justify-content:space-between;
align-items:center;
padding:6px 0;
cursor:pointer;
}
.item.checked{
text-decoration:line-through;
opacity:0.6;
}
.left{
display:flex;
gap:8px;
align-items:center;
width:100%;
}
/* 🔥 NEUE AUSRICHTUNG */
.label{
display:flex;
align-items:center;
gap:10px;
width:100%;
white-space:nowrap; /* ❗ alles in einer Zeile */
overflow:hidden; /* ❗ verhindert Umbruch */
}
.amount{
width:60px;
text-align:right;
font-variant-numeric: tabular-nums;
flex-shrink:0; /* ❗ bleibt fix */
}
.unit{
width:40px;
color:#666;
flex-shrink:0; /* ❗ bleibt fix */
}
.name{
flex:1;
overflow:hidden;
text-overflow:ellipsis; /* ❗ ... wenn zu lang */
white-space:nowrap;
}
/* Info Button */
.info{
background:#eee;
border-radius:50%;
width:20px;
height:20px;
display:flex;
align-items:center;
justify-content:center;
font-size:12px;
cursor:pointer;
}
/* Overlay unten */
.overlay{
position:fixed;
bottom:0;
left:0;
width:100%;
background:#A52020;
color:white;
padding:20px;
display:flex;
justify-content:space-between;
align-items:center;
}
.progress-bar{
height:8px;
background:rgba(255,255,255,0.3);
border-radius:5px;
overflow:hidden;
margin-top:6px;
}
.progress-inner{
height:8px;
background:white;
width:0%;
}
button{
padding:8px 14px;
border:none;
border-radius:6px;
cursor:pointer;
}
/* Modal */
.modal{
position:fixed;
top:0;
left:0;
width:100%;
height:100%;
background:rgba(0,0,0,0.6);
display:none;
justify-content:center;
align-items:center;
z-index:999;
}
.modal-content{
background:white;
padding:20px;
border-radius:10px;
max-width:400px;
width:90%;
}
.modal-content a{
display:block;
margin:6px 0;
color:#A52020;
text-decoration:none;
}
</style>
</head>
<body>
<div class="full-red">
<h1>🛒 Meine Einkaufsliste</h1>
</div>
<div class="wrapper">
<div id="shoppingContainer"></div>
</div>
<div class="overlay">
<div>
<div id="overlayText">0 / 0</div>
<div class="progress-bar">
<div class="progress-inner" id="progressBar"></div>
</div>
</div>
<button onclick="clearCart()">Liste leeren</button>
</div>
<!-- Modal -->
<div class="modal" id="modal">
<div class="modal-content">
<h2 id="modalTitle"></h2>
<div id="modalList"></div>
<button onclick="closeModal()">Schließen</button>
</div>
</div>
<script>
const STORAGE_KEY="shoppingCart";
const CHECKED_KEY="shoppingChecked";
const CATEGORIES=[
"🥦 Obst & Gemüse",
"🥩 Fleisch & Fisch",
"🧊 Kühlprodukte",
"🥖 Backzutaten",
"🍞 Brot & Backwaren",
"🥫 Trockenvorrat",
"🧂 Gewürze",
"❄ Tiefkühl",
"🍷 Alkoholische Getränke",
"🧃 Getränke (alkoholfrei)",
"📦 Sonstiges"
];
function render(){
let cart=JSON.parse(localStorage.getItem(STORAGE_KEY))||{};
let checked=JSON.parse(localStorage.getItem(CHECKED_KEY))||[];
let merged={};
/* Zusammenführen + Rezepte speichern */
for(let recipe in cart){
for(let category in cart[recipe]){
cart[recipe][category].forEach(item=>{
let key=item.name+"|"+item.unit;
if(!merged[key]){
merged[key]={
name:item.name,
unit:item.unit,
amount:0,
category:category,
recipes:[]
};
}
merged[key].amount+=parseFloat(item.amount)||0;
if(!merged[key].recipes.includes(recipe)){
merged[key].recipes.push(recipe);
}
});
}
}
/* Kategorien */
let allCategories=[...CATEGORIES];
for(let key in merged){
let cat=merged[key].category;
if(!allCategories.includes(cat)){
allCategories.push(cat);
}
}
let grouped={};
allCategories.forEach(c=>grouped[c]=[]);
for(let key in merged){
let item=merged[key];
if(!grouped[item.category]) grouped[item.category]=[];
grouped[item.category].push(item);
}
/* Anzeige */
let container=document.getElementById("shoppingContainer");
container.innerHTML="";
let total=0;
allCategories.forEach(cat=>{
let items=grouped[cat]||[];
/* SORTIERUNG */
items.sort((a,b)=>a.name.localeCompare(b.name,"de",{sensitivity:"base"}));
let div=document.createElement("div");
div.className="category";
let title=document.createElement("h3");
let text=document.createElement("span");
text.innerText=cat;
let arrow=document.createElement("span");
arrow.innerText="▼";
arrow.className="arrow";
title.appendChild(text);
title.appendChild(arrow);
let content=document.createElement("div");
title.onclick=function(){
content.style.display=
content.style.display==="none"?"block":"none";
arrow.innerText=content.style.display==="none"?"▶":"▼";
};
div.appendChild(title);
div.appendChild(content);
/* Items */
items.forEach(item=>{
total++;
let row=document.createElement("div");
row.className="item";
let left=document.createElement("div");
left.className="left";
let id=item.name+"_"+item.unit;
let checkbox=document.createElement("input");
checkbox.type="checkbox";
checkbox.checked=checked.includes(id);
if(checkbox.checked){
row.classList.add("checked");
}
/* 🔥 SPALTEN */
let label=document.createElement("div");
label.className="label";
let amountEl=document.createElement("span");
amountEl.className="amount";
let unitEl=document.createElement("span");
unitEl.className="unit";
let nameEl=document.createElement("span");
nameEl.className="name";
let amount=item.amount;
let unit=item.unit;
if(unit==="g" && amount>=1000){
amount=(amount/1000).toFixed(2);
unit="kg";
}
if(unit==="ml" && amount>=1000){
amount=(amount/1000).toFixed(2);
unit="l";
}
amountEl.innerText=amount;
unitEl.innerText=unit;
nameEl.innerText=item.name;
label.appendChild(amountEl);
label.appendChild(unitEl);
label.appendChild(nameEl);
left.appendChild(checkbox);
left.appendChild(label);
/* INFO BUTTON */
let info=document.createElement("div");
info.className="info";
info.innerText="i";
info.onclick=function(e){
e.stopPropagation();
openModal(item);
};
row.appendChild(left);
row.appendChild(info);
/* Klick */
row.onclick=function(e){
if(e.target.classList.contains("info")) return;
if(e.target.tagName!=="INPUT"){
checkbox.checked=!checkbox.checked;
}
if(checkbox.checked){
row.classList.add("checked");
if(!checked.includes(id)) checked.push(id);
}else{
row.classList.remove("checked");
checked=checked.filter(x=>x!==id);
}
localStorage.setItem(CHECKED_KEY,JSON.stringify(checked));
updateProgress(total,checked.length);
};
content.appendChild(row);
});
container.appendChild(div);
});
updateProgress(total,checked.length);
}
/* Modal */
function openModal(item){
document.getElementById("modal").style.display="flex";
document.getElementById("modalTitle").innerText=item.name;
let list=document.getElementById("modalList");
list.innerHTML="";
item.recipes.forEach(r=>{
let a=document.createElement("a");
a.innerText=r;
a.href="/rezept/"+encodeURIComponent(r);
a.target="_blank";
list.appendChild(a);
});
}
function closeModal(){
document.getElementById("modal").style.display="none";
}
/* Progress */
function updateProgress(total,done){
let percent=total?Math.round((done/total)*100):0;
document.getElementById("overlayText").innerText=
done+" von "+total+" erledigt";
document.getElementById("progressBar").style.width=
percent+"%";
}
/* Clear */
function clearCart(){
if(confirm("Liste löschen?")){
localStorage.removeItem(STORAGE_KEY);
localStorage.removeItem(CHECKED_KEY);
render();
}
}
document.addEventListener("DOMContentLoaded",render);
</script>
</body>
</html>