Festival Tracking App Shape HTML (index.html)

Festival Getränke Tracker

Teilnehmerverwaltung

Teams

Getränke

Getränke entfernen:

Rangliste Teilnehmer

    Rangliste Teams

      Shape CSS (style.css) /* =========================    Allgemeines Styling ========================= */ body {   font-family: Arial, sans-serif;   text-align: center;   background-color: #f5f5f5;   color: #444;   padding: 20px; } h1 {   margin-bottom: 20px; } .panel {   background-color: #fff;   padding: 15px 20px;   margin: 15px auto 25px auto;   border-radius: 10px;   max-width: 600px;   box-shadow: 0 2px 6px rgba(0,0,0,0.1);   text-align: center; } .input-row {   display: flex;   flex-wrap: wrap;   justify-content: center;   gap: 10px;   margin-bottom: 10px; } input {   padding: 5px;   margin: 5px;   font-size: 14px;   border-radius: 6px;   border: 1px solid #ccc; } select {   padding: 5px 10px;   margin: 5px;   font-size: 14px;   border-radius: 6px;   border: 1px solid #ccc;   min-width: 150px;  /* Team-Auswahlbox größer */ } button {   padding: 5px 10px;   margin: 5px;   cursor: pointer;   border-radius: 6px;   border: 1px solid #ccc;   background-color: #eee;   color: #333;   min-width: 80px;    /* Einheitliche Button-Breite */   height: 35px;       /* Einheitliche Höhe für Text und Emoji */ } button:hover {   background-color: #ddd; } #player-buttons button, #drink-buttons button, #remove-drinks-buttons button {   margin: 3px;   font-size: 14px;   min-width: 80px;   height: 35px; } .selected {   background-color: #d1ffd1; /* grün hinterlegt für aktuell ausgewählt */ } #current-player {   font-weight: bold;   color: green; } ul {   list-style-type: none;   padding: 0;   margin: 10px 0; } ul li {   padding: 6px 10px;   text-align: left;   position: relative;   background-color: #f0f0f0;  /* neutrale Hintergrundfarbe für Balken */   border-radius: 6px;   margin-bottom: 5px;   overflow: hidden; } ul li .bar {   position: absolute;   top: 0;   left: 0;   height: 100%;   background-color: #90caf9; /* dezenter Blau-Ton für Balken */   z-index: 0;   border-radius: 6px 0 0 6px; } ul li span {   position: relative;   z-index: 1; } /* Hervorhebung für ausgewählten Spieler */ ul li.selected {   background-color: #cce5ff;   /* hellblauer Hintergrund */   font-weight: bold; } /* Hervorhebung für ausgewähltes Team */ #team-leaderboard li.selected {   background-color: #c8e6c9;   /* hellgrün */   font-weight: bold; } /* Optional: Balken für ausgewählten Spieler/Team noch heller */ ul li.selected .bar {   opacity: 0.8; } /* =========================    Responsive Anpassungen ========================= */ @media (max-width: 600px) {   .input-row {     flex-direction: column;     align-items: center;   }   #player-buttons button,   #drink-buttons button,   #remove-drinks-buttons button {     width: 90%;   }   select {     width: 90%;   } } Shape JavaScript (app.js) // ========================= // KONSTANTEN const DELETE_PASSWORD = "festival123"; // Admin Passwort // ========================= // DATEN let players = JSON.parse(localStorage.getItem('players') || '{}'); let drinks = JSON.parse(localStorage.getItem('drinks') || 'null') || {   "Bier": { points:1, emoji:"🍺" },   "Cocktail": { points:2, emoji:"🍹" },   "Softdrink": { points:0.5, emoji:"🥤" },   "Wasser": { points:0, emoji:"💧" } }; let teams = JSON.parse(localStorage.getItem('teams') || 'null') || {   "Team Yeah":"Team Yeah",   "Team Whoo":"Team Whoo" }; let selectedPlayer = ""; // ========================= // PLAYER FUNCTIONS function addPlayer(){   const name = document.getElementById("player-name").value.trim();   const team = document.getElementById("player-team").value;   if(!name){alert("Bitte Namen eingeben"); return;}   if(!team){alert("Bitte Team wählen"); return;}   if(players[name]){alert("Teilnehmer existiert bereits!"); return;}   const password = prompt(`Passwort für ${name} festlegen:`);   if(!password){alert("Kein Passwort."); return;}   players[name] = { points:0, team:team, drinks:[], password };   document.getElementById("player-name").value = "";   savePlayers(); updatePlayerButtons(); updateLeaderboards(); updateRemoveDrinkButtons(); } function deletePlayer(){   if(!selectedPlayer){alert("Bitte Teilnehmer auswählen"); return;}   const password = prompt("Admin-Passwort:");   if(password!==DELETE_PASSWORD){alert("Falsches Passwort."); return;}   delete players[selectedPlayer]; selectedPlayer="";   savePlayers(); updatePlayerButtons(); updateLeaderboards(); updateRemoveDrinkButtons(); } function updatePlayerButtons(){   const container = document.getElementById("player-buttons");   container.innerHTML="

      Teilnehmer auswählen:

      ";   Object.keys(players).forEach(name=>{     const btn = document.createElement("button");     btn.textContent = `${name} (${players[name].team})`;     btn.onclick = ()=>selectPlayer(name);     if(name===selectedPlayer) btn.classList.add("selected");     container.appendChild(btn);   }); } function selectPlayer(name){   selectedPlayer=name;   updatePlayerButtons();   updateLeaderboards();   updateRemoveDrinkButtons(); } // ========================= // DRINK FUNCTIONS function addDrink(drinkName){   if(!selectedPlayer || !drinks[drinkName]){alert("Fehler"); return;}   players[selectedPlayer].points += drinks[drinkName].points;   players[selectedPlayer].drinks.push(drinkName);   savePlayers(); updateLeaderboards(true); updateRemoveDrinkButtons(); } function addOrEditDrink(){   const password = prompt("Admin-Passwort:");   if(password!==DELETE_PASSWORD){alert("Falsches Passwort"); return;}   const name = prompt("Getränk Name:");   if(!name){alert("Ungültig"); return;}   if(drinks[name]){     const points=parseFloat(prompt(`"${name}" existiert. Neue Punktzahl:`, drinks[name].points));     if(isNaN(points)){alert("Ungültige Zahl"); return;}     const emoji=prompt(`Neues Emoji für "${name}":`, drinks[name].emoji);     if(!emoji){alert("Kein Emoji"); return;}     drinks[name]={points,emoji}; alert(`"${name}" aktualisiert`);   } else {     const points=parseFloat(prompt("Punkte:"));     if(isNaN(points)){alert("Ungültige Zahl"); return;}     const emoji=prompt("Emoji:");     if(!emoji){alert("Kein Emoji"); return;}     drinks[name]={points,emoji}; alert(`"${name}" hinzugefügt`);   }   saveDrinks(); updateDrinkButtons(); } function updateDrinkButtons(){   const container=document.getElementById("drink-buttons");   container.innerHTML="

      Getränk auswählen:

      ";   Object.entries(drinks).forEach(([name,data])=>{     const btn=document.createElement("button");     btn.textContent=`${data.emoji} ${name} (+${data.points})`;     btn.onclick=()=>addDrink(name);     container.appendChild(btn);   }); } // ========================= // REMOVE DRINK function updateRemoveDrinkButtons(){   const container=document.getElementById("remove-drinks-buttons");   container.innerHTML="";   if(!selectedPlayer || !players[selectedPlayer] || !players[selectedPlayer].drinks.length){     container.textContent="Keine Getränke zum Entfernen.";     return;   }   players[selectedPlayer].drinks.forEach((drinkName,index)=>{     const btn=document.createElement("button");     btn.textContent=drinks[drinkName]?.emoji||"";     btn.title=`${drinkName} (-${drinks[drinkName]?.points||0} Punkte)`;     btn.onclick=()=>removeDrink(index);     container.appendChild(btn);   }); } function removeDrink(index){   if(!selectedPlayer) return;   const inputPassword = prompt("Passwort für Getränke entfernen:");   if(inputPassword!==players[selectedPlayer].password){alert("Falsches Passwort."); return;}   const drinkName = players[selectedPlayer].drinks[index];   const points = drinks[drinkName]?.points||0;   players[selectedPlayer].points -= points;   if(players[selectedPlayer].points<0) players[selectedPlayer].points=0;   players[selectedPlayer].drinks.splice(index,1);   savePlayers(); updateLeaderboards(true); updateRemoveDrinkButtons(); } // ========================= // TEAM FUNCTIONS function toggleTeamPanel(){   const panel = document.getElementById("team-panel");   if(panel.style.display==="none"){     const password = prompt("Admin-Passwort:");     if(password!==DELETE_PASSWORD){alert("Falsches Passwort."); return;}     panel.style.display="block";     document.getElementById("team-yeah-name").value = teams["Team Yeah"];     document.getElementById("team-whoo-name").value = teams["Team Whoo"];     updateTeamSelectOptions();   } else panel.style.display="none"; } function updateTeamNames(){   const yeah=document.getElementById("team-yeah-name").value.trim();   const whoo=document.getElementById("team-whoo-name").value.trim();   if(yeah) teams["Team Yeah"]=yeah;   if(whoo) teams["Team Whoo"]=whoo;   localStorage.setItem('teams',JSON.stringify(teams));   updateTeamSelectOptions(); updatePlayerTeamDropdown(); updatePlayerButtons(); updateLeaderboards();   alert("Teamnamen aktualisiert!"); } function updateTeamSelectOptions(){   const select=document.getElementById("change-player-team-select");   select.innerHTML="";   Object.values(teams).forEach(teamName=>{     const opt=document.createElement("option"); opt.value=teamName; opt.textContent=teamName;     select.appendChild(opt);   }); } function updatePlayerTeamDropdown(){   const select=document.getElementById("player-team");   select.innerHTML="";   Object.values(teams).forEach(teamName=>{     const opt=document.createElement("option"); opt.value=teamName; opt.textContent=teamName;     select.appendChild(opt);   }); } function changePlayerTeam(){   if(!selectedPlayer){alert("Teilnehmer auswählen"); return;}   const newTeam=document.getElementById("change-player-team-select").value;   if(!newTeam){alert("Team auswählen"); return;}   players[selectedPlayer].team=newTeam;   savePlayers(); updatePlayerButtons(); updateLeaderboards();   alert(`${selectedPlayer} ist jetzt im Team ${newTeam}`); } // ========================= // LEADERBOARDS function updateLeaderboards(highlight=false){   const leaderboard=document.getElementById("leaderboard");   leaderboard.innerHTML="";   let maxPoints = 1;   Object.values(players).forEach(p=>{if(p.points>maxPoints) maxPoints=p.points;});   // Teilnehmer-Rangliste   Object.entries(players)     .sort((a,b)=>b[1].points-a[1].points)     .forEach(([name,data])=>{       let emojis = data.drinks.map(d=>drinks[d]?.emoji||"").join(" ");       const li = document.createElement("li");       // Balken proportional       const bar = document.createElement("div");       bar.className = "bar";       const widthPercent = (data.points/maxPoints)*100;       bar.style.width = widthPercent + "%";       const span = document.createElement("span");       span.textContent = `${name} (${data.team}): ${data.points} Punkt(e) ${emojis}`;       li.appendChild(bar);       li.appendChild(span);       if(highlight && name===selectedPlayer){li.classList.add("updated"); setTimeout(()=>li.classList.remove("updated"),1000);}       if(name===selectedPlayer) li.classList.add("selected"); // Hervorhebung ausgewählter Spieler       leaderboard.appendChild(li);     });   // Team-Rangliste   const teamLeaderboard=document.getElementById("team-leaderboard");   teamLeaderboard.innerHTML="";   const teamScores={}; Object.values(teams).forEach(t=>teamScores[t]=0);   Object.values(players).forEach(p=>{if(p.team && teamScores[p.team]!==undefined) teamScores[p.team]+=p.points;});   let maxTeamPoints=1;   Object.values(teamScores).forEach(p=>{if(p>maxTeamPoints) maxTeamPoints=p;});   Object.entries(teamScores)     .sort((a,b)=>b[1]-a[1])     .forEach(([team,points])=>{       const li = document.createElement("li");       const bar = document.createElement("div");       bar.className = "bar";       bar.style.backgroundColor = "#a5d6a7";       bar.style.width = (points/maxTeamPoints)*100 + "%";       const span = document.createElement("span");       span.textContent = `${team}: ${points} Punkt(e)`;       li.appendChild(bar);       li.appendChild(span);       // Hervorhebung Team des ausgewählten Spielers       if(selectedPlayer && players[selectedPlayer].team === team){         li.classList.add("selected");       }       teamLeaderboard.appendChild(li);     }); } // ========================= // LOCALSTORAGE function savePlayers(){localStorage.setItem('players',JSON.stringify(players));} function saveDrinks(){localStorage.setItem('drinks',JSON.stringify(drinks));} // ========================= // INITIALIZE updatePlayerButtons(); updateDrinkButtons(); updateLeaderboards(); updateRemoveDrinkButtons(); updateTeamSelectOptions(); updatePlayerTeamDropdown(); Shape   Festival Getränke Tracker

      Festival Getränke Tracker

         

      Teilnehmerverwaltung

         
                          
         
         
         

      Teams

             
         

      Getränke

         
             
           

      Getränke entfernen:

           
         
         

      Rangliste Teilnehmer

         
           

        Rangliste Teams