UNPKG

doc-fui-ds

Version:

Doc

812 lines (668 loc) 35.2 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Random Spin Wheel</title> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- SweetAlert2 --> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> <!-- XLSX for Excel Handling --> <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script> <style> .wheel-container { position: relative; width: 300px; height: 300px; margin: 20px auto; } #wheel { width: 100%; height: 100%; border-radius: 50%; transition: transform 4s cubic-bezier(0.17, 0.67, 0.83, 0.67); } .arrow { position: absolute; top: -10px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 15px solid transparent; border-right: 15px solid transparent; border-bottom: 30px solid red; } .winner-item { background: #f8f9fa; padding: 5px 10px; margin: 5px 0; border-radius: 5px; border-left: 5px solid #28a745; } </style> </head> <body class="bg-light"> <div class="container mt-4"> <div class="row"> <!-- Left Section: Wheel --> <div class="col-md-7 text-center"> <h1 class="mb-3 text-primary">🎡 Random Spin Wheel 🎡</h1> <!-- Upload file --> <input type="file" id="fileInput" accept=".xls, .xlsx" class="form-control mb-3"> <!-- Text Area for Input Data --> <textarea id="nameInput" class="form-control mb-3" placeholder="Enter names (one per line)..." rows="4"></textarea> <div class="mb-3"> <label for="winnerLimit" class="form-label">Set Winner Limit:</label> <input type="number" id="winnerLimit" class="form-control" min="1" value="1"> </div> <!-- Update & Search Buttons --> <div class="input-group mb-3"> <input type="text" id="searchInput" placeholder="Search name..." class="form-control" aria-describedby="searchButton"> <button id="searchButton" class="btn btn-secondary">Search</button> <button id="resetSearchButton" class="btn btn-warning mt-2">Reset Search</button> </div> <!-- List Display with Search and Delete Option --> <div id="nameList" class="mt-3"></div> <div class="mb-3"> <button id="addButton" class="btn btn-success">Add Data to Spin Wheel</button> <button id="updateButton" class="btn btn-primary" style="display: none;">Update Spin Wheel Data</button> </div> <div class=""> <button id="drawButton" class="btn btn-primary me-2" style="display: none;">Spin the Wheel</button> <button id="resetButton" class="btn btn-danger" style="display: none;">Reset Game</button> </div> <div class=""> <button id="shuffleButton" class="btn btn-primary mt-3">Shuffle Data</button> <button id="sortButton" class="btn btn-secondary mt-3">Sort Data</button> </div> <div class=""> <button id="downloadInputTxtButton" class="btn btn-secondary mt-3">Download Input Data (.txt)</button> <button id="downloadInputExcelButton" class="btn btn-secondary mt-3">Download Input Data (.xlsx)</button> <button id="downloadInputCsvButton" class="btn btn-secondary mt-3">Download Input Data (.csv)</button> </div> <div class="wheel-container"> <div class="arrow"></div> <svg id="wheel" width="300" height="300" viewBox="0 0 300 300"></svg> </div> </div> <!-- Right Section: Winner List --> <div class="col-md-5"> <h2 class="text-success">🏆 Winners</h2> <div id="winnerList" class="border rounded p-3 bg-white"></div> <button id="downloadTxtButton" class="btn btn-success mt-3">Download Winners (.txt)</button> <button id="downloadExcelButton" class="btn btn-info mt-3">Download Winners (.xlsx)</button> <button id="downloadCsvButton" class="btn btn-warning mt-3">Download Winners (.csv)</button> </div> </div> </div> <!-- Modal Structure --> <div class="modal fade" id="adsModal" tabindex="-1" aria-labelledby="adsModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="adsModalLabel">Sponsored Ad</h5> </div> <div class="modal-body" id="adContent"> <p>Loading Ad...</p> </div> </div> </div> </div> <script> let namesArray = []; let rotation = 0; document.getElementById('fileInput').addEventListener('change', function (event) { const file = event.target.files[0]; if (!file) return; const fileType = file.name.split('.').pop().toLowerCase(); const reader = new FileReader(); if (fileType === 'txt') { // Handle .txt file reader.onload = function (e) { document.getElementById('nameInput').value = e.target.result; }; reader.readAsText(file); } else if (fileType === 'xls' || fileType === 'xlsx') { // Handle Excel file reader.onload = function (e) { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); // Extract data from the first sheet and first column const sheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(sheet, { header: 1 }); // Extract names from the first column const names = rows.map(row => row[0]).filter(name => name); // Remove empty values // Insert names into textarea document.getElementById('nameInput').value = names.join('\n'); }; reader.readAsArrayBuffer(file); } else { // alert('Unsupported file type. Please upload a .txt, .xls, or .xlsx file.'); // ✅ Show SweetAlert2 warning when no names are available Swal.fire({ title: "Unsupported file type.😕", text: "lease upload a .txt, .xls, or .xlsx file.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#007bff" }); return; } }); document.getElementById("drawButton").addEventListener("click", function () { if (namesArray.length === 0) { Swal.fire({ title: "Oops! No Names Found 😕", text: "Please enter names before spinning the wheel.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#007bff" }); return; } // ✅ Get the winner limit from user input let winnerLimit = parseInt(document.getElementById("winnerLimit").value) || 1; // ✅ Prevent multiple spins at the same time document.getElementById("drawButton").disabled = true; // ✅ Generate and spin the wheel generateWheel(); spinWheel(winnerLimit); }); function generateWheel() { let wheel = document.getElementById("wheel"); wheel.innerHTML = ""; // Clear previous segments let totalSegments = namesArray.length; let sliceAngle = 360 / totalSegments; for (let i = 0; i < totalSegments; i++) { let angle = i * sliceAngle; let color = `hsl(${(i * 360) / totalSegments}, 80%, 60%)`; let path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", describeArc(150, 150, 150, angle, angle + sliceAngle)); path.setAttribute("fill", color); path.setAttribute("stroke", "#fff"); path.setAttribute("stroke-width", "2"); wheel.appendChild(path); // Text positioning let text = document.createElementNS("http://www.w3.org/2000/svg", "text"); let textAngle = angle + sliceAngle / 2; // Center of the slice let textX = 150 + Math.cos((textAngle - 90) * Math.PI / 180) * 100; // Adjusted for diagonal alignment let textY = 150 + Math.sin((textAngle - 90) * Math.PI / 180) * 100; text.setAttribute("x", textX); text.setAttribute("y", textY); text.setAttribute("transform", `rotate(${textAngle + 90}, ${textX}, ${textY})`); // Rotates text diagonally text.setAttribute("text-anchor", "middle"); text.setAttribute("font-size", "14px"); text.setAttribute("fill", "#fff"); text.setAttribute("dominant-baseline", "middle"); // Center align text vertically text.textContent = namesArray[i]; wheel.appendChild(text); } } function addWinner(name) { let winnerList = document.getElementById("winnerList"); let winnerItem = document.createElement("div"); winnerItem.classList.add("winner-item", "d-flex", "justify-content-between", "align-items-center", "p-2", "border", "rounded", "mb-2", "bg-light"); let winnerText = document.createElement("span"); winnerText.textContent = name; let deleteBtn = document.createElement("button"); deleteBtn.textContent = "❌"; deleteBtn.classList.add("btn", "btn-sm", "btn-danger", "ms-2"); deleteBtn.onclick = function () { confirmDeleteWinner(winnerItem, name); }; winnerItem.appendChild(winnerText); winnerItem.appendChild(deleteBtn); winnerList.appendChild(winnerItem); } function confirmDeleteWinner(winnerItem, name) { Swal.fire({ title: "Are you sure?", text: `Do you want to remove ${name} from the winner list?`, icon: "warning", showCancelButton: true, confirmButtonColor: "#d33", cancelButtonColor: "#3085d6", confirmButtonText: "Yes, delete it!" }).then((result) => { if (result.isConfirmed) { deleteWinner(winnerItem, name); } }); } function deleteWinner(winnerItem, name) { winnerItem.remove(); // Remove the winner from the list // ✅ Add the deleted name back to the namesArray so they can be drawn again if (!namesArray.includes(name)) { namesArray.push(name); } // ✅ Check if the draw button should be enabled let winnerLimit = parseInt(document.getElementById("winnerLimit").value) || 1; let currentWinners = document.getElementById("winnerList").children.length; if (currentWinners < winnerLimit) { document.getElementById("drawButton").disabled = false; } } function spinWheel(winnerLimit) { let totalSegments = namesArray.length; let sliceAngle = 360 / totalSegments; let randomIndex = Math.floor(Math.random() * totalSegments); let finalRotation = 360 * 5 + (randomIndex * sliceAngle) + (sliceAngle / 2); let wheel = document.getElementById("wheel"); rotation += finalRotation; wheel.style.transform = `rotate(${rotation}deg)`; setTimeout(() => { let actualRotation = rotation % 360; let winningIndex = Math.floor((360 - actualRotation) / sliceAngle) % totalSegments; let winner = namesArray[winningIndex]; // ✅ Check if winner limit has been reached let currentWinners = document.getElementById("winnerList").children.length; if (currentWinners >= winnerLimit) { Swal.fire({ title: "Winner Limit Reached!", text: `You have already selected ${winnerLimit} winners.`, icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#007bff" }); // ✅ Re-enable the spin button document.getElementById("drawButton").disabled = false; return; } Swal.fire({ title: "🎉 Winner Selected! 🎉", text: `${winner} is the lucky winner! 🏆`, icon: "success", confirmButtonText: "OK", confirmButtonColor: "#007bff" }); // ✅ Add winner to the winner list let winnerList = document.getElementById("winnerList"); let winnerItem = document.createElement("div"); winnerItem.classList.add("winner-item", "d-flex", "justify-content-between", "align-items-center", "p-2", "border", "rounded", "mb-2", "bg-light"); let winnerText = document.createElement("span"); winnerText.textContent = `🏆 ${winner}`; let deleteBtn = document.createElement("button"); deleteBtn.textContent = "❌"; deleteBtn.classList.add("btn", "btn-sm", "btn-danger", "ms-2"); deleteBtn.onclick = function () { confirmDeleteWinner(winnerItem, winner); }; winnerItem.appendChild(winnerText); winnerItem.appendChild(deleteBtn); winnerList.appendChild(winnerItem); // ✅ Remove winner from namesArray namesArray.splice(winningIndex, 1); // ✅ Disable the spin button if only one name remains if (namesArray.length <= 1) { document.getElementById("drawButton").disabled = true; } else { // ✅ Re-enable the spin button after selecting a winner document.getElementById("drawButton").disabled = false; } // ✅ Check if all winners have been selected if (namesArray.length === 0 || document.getElementById("winnerList").children.length >= winnerLimit) { setTimeout(() => { Swal.fire({ title: "Game Over! 🎊", text: "All winners have been selected!", icon: "info", confirmButtonText: "OK" }); }, 2000); } }, 5000); } function describeArc(x, y, radius, startAngle, endAngle) { let start = polarToCartesian(x, y, radius, endAngle); let end = polarToCartesian(x, y, radius, startAngle); let largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1"; return [ "M", x, y, "L", start.x, start.y, "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y, "Z" ].join(" "); } function polarToCartesian(centerX, centerY, radius, angleInDegrees) { let angleInRadians = (angleInDegrees - 90) * Math.PI / 180; return { x: centerX + (radius * Math.cos(angleInRadians)), y: centerY + (radius * Math.sin(angleInRadians)) }; } // Function to shuffle input data document.getElementById("shuffleButton").addEventListener("click", function () { let inputData = document.getElementById("nameInput").value.trim().split("\n"); if (inputData.length === 0 || (inputData.length === 1 && inputData[0] === "")) { Swal.fire({ title: "No Input Data!", text: "Please enter or upload names before shuffling.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#dc3545" }); return; } inputData = inputData.sort(() => Math.random() - 0.5); document.getElementById("nameInput").value = inputData.join("\n"); }); // Function to sort input data document.getElementById("sortButton").addEventListener("click", function () { let inputData = document.getElementById("nameInput").value.trim().split("\n"); if (inputData.length === 0 || (inputData.length === 1 && inputData[0] === "")) { Swal.fire({ title: "No Input Data!", text: "Please enter or upload names before sorting.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#dc3545" }); return; } inputData = inputData.sort(); document.getElementById("nameInput").value = inputData.join("\n"); }); // Function to toggle draw and reset buttons function toggleButtons() { let drawButton = document.getElementById("drawButton"); let resetButton = document.getElementById("resetButton"); if (namesArray.length > 0) { drawButton.style.display = "block"; resetButton.style.display = "block"; } else { drawButton.style.display = "none"; resetButton.style.display = "none"; } } // Function to add input data document.getElementById("addButton").addEventListener("click", function () { let newData = document.getElementById("nameInput").value.trim().split("\n"); let newNames = newData.map(name => name.trim()).filter(name => name !== ""); namesArray = [...new Set([...namesArray, ...newNames])]; // Merge unique names updateTextArea(); displayNames(); toggleButtons(); // ✅ Hide Add Button & Show Update Button when data is added if (namesArray.length > 0) { document.getElementById("addButton").style.display = "none"; document.getElementById("updateButton").style.display = "block"; } // ✅ Generate wheel after adding data generateWheel(); Swal.fire({ title: "Data Added!", text: "The new names have been added successfully.", icon: "success", confirmButtonText: "OK", confirmButtonColor: "#28a745" }); }); // Function to update input data document.getElementById("updateButton").addEventListener("click", function () { let updatedData = document.getElementById("nameInput").value.trim().split("\n"); let updatedNames = updatedData.map(name => name.trim()).filter(name => name !== ""); namesArray = updatedNames; // Replace the current list with the new input updateTextArea(); displayNames(); toggleButtons(); // ✅ Ensure Update Button stays visible if data exists if (namesArray.length > 0) { document.getElementById("updateButton").style.display = "block"; document.getElementById("addButton").style.display = "none"; } else { document.getElementById("updateButton").style.display = "none"; document.getElementById("addButton").style.display = "block"; // Show Add Button if empty } // ✅ Generate wheel after updating data generateWheel(); Swal.fire({ title: "List Updated!", text: "The input names have been updated successfully.", icon: "success", confirmButtonText: "OK", confirmButtonColor: "#007bff" }); }); // ✅ Reset Button Click Event document.getElementById("resetButton").addEventListener("click", function () { Swal.fire({ title: "Are you sure?", text: "This will clear all data, reset the name list, and refresh the wheel.", icon: "warning", showCancelButton: true, confirmButtonColor: "#dc3545", cancelButtonColor: "#6c757d", confirmButtonText: "Yes, Reset" }).then((result) => { if (result.isConfirmed) { // ✅ Clear the text area document.getElementById("nameInput").value = ""; // ✅ Clear the winner list document.getElementById("winnerList").innerHTML = ""; // ✅ Clear the name list document.getElementById("nameList").innerHTML = ""; // ✅ Reset the names array namesArray = []; // ✅ Completely remove and recreate the wheel SVG let wheelContainer = document.querySelector(".wheel-container"); let oldWheel = document.getElementById("wheel"); if (oldWheel) { oldWheel.parentNode.removeChild(oldWheel); // Remove old SVG element } let newWheel = document.createElementNS("http://www.w3.org/2000/svg", "svg"); newWheel.setAttribute("id", "wheel"); newWheel.setAttribute("width", "300"); newWheel.setAttribute("height", "300"); newWheel.setAttribute("viewBox", "0 0 300 300"); wheelContainer.appendChild(newWheel); // Append new empty wheel // ✅ Reset the rotation rotation = 0; // ✅ Reset file input so users can re-upload Excel after reset document.getElementById("fileInput").value = ""; // ✅ Hide Draw, Update, and Reset buttons document.getElementById("drawButton").style.display = "none"; document.getElementById("updateButton").style.display = "none"; document.getElementById("addButton").style.display = "block"; Swal.fire({ title: "Game Reset!", text: "The spin wheel, name list, and winners have been cleared.", icon: "success", confirmButtonText: "OK", confirmButtonColor: "#007bff" }); } }); }); // Function to update textarea with current namesArray function updateTextArea() { document.getElementById("nameInput").value = namesArray.join("\n"); } // Function to display and filter the list with delete option function displayNames(searchText = "", filter = false) { let nameListDiv = document.getElementById("nameList"); nameListDiv.innerHTML = ""; let filteredNames = filter ? namesArray.filter(name => name.toLowerCase().includes(searchText.toLowerCase())) : namesArray; filteredNames.forEach((name) => { let listItem = document.createElement("div"); listItem.classList.add("d-flex", "justify-content-between", "align-items-center", "p-2", "border", "rounded", "mb-2", "bg-light"); let nameText = document.createElement("span"); nameText.textContent = name; let deleteButton = document.createElement("button"); deleteButton.textContent = "❌"; deleteButton.classList.add("btn", "btn-danger", "btn-sm", "ms-2"); // ✅ Correctly delete the clicked item deleteButton.onclick = function () { let nameIndex = namesArray.indexOf(name); if (nameIndex !== -1) { namesArray.splice(nameIndex, 1); } updateTextArea(); displayNames(); toggleButtons(); // ✅ Regenerate wheel after deletion generateWheel(); }; listItem.appendChild(nameText); listItem.appendChild(deleteButton); nameListDiv.appendChild(listItem); }); } // Function to search input data document.getElementById("searchButton").addEventListener("click", function () { let searchText = document.getElementById("searchInput").value.trim(); displayNames(searchText, true); }); // Function to reset search document.getElementById("resetSearchButton").addEventListener("click", function () { document.getElementById("searchInput").value = ""; displayNames(); }); // Show the name list and toggle buttons when input data is added or updated displayNames(); toggleButtons(); // ✅ Ads image list const adsArray = [ "<img src='https://via.placeholder.com/300x250?text=Ad+1' alt='Ad 1'>", "<img src='https://via.placeholder.com/300x250?text=Ad+2' alt='Ad 2'>", "<img src='https://via.placeholder.com/300x250?text=Ad+3' alt='Ad 3'>" ]; // ✅ Ads modal after clicking on download button function showAdModal(callback) { let randomAd = adsArray[Math.floor(Math.random() * adsArray.length)]; document.getElementById("adContent").innerHTML = `<span id='countdown'>5</span><br>` + randomAd; let modal = new bootstrap.Modal(document.getElementById("adsModal")); modal.show(); let countdown = 5; const countdownElem = document.getElementById("countdown"); const interval = setInterval(() => { countdown--; countdownElem.textContent = countdown; if (countdown <= 0) clearInterval(interval); }, 1000); setTimeout(() => { modal.hide(); callback(); }, 5000); } // ✅ Function to download winner list as a .txt file document.getElementById("downloadTxtButton").addEventListener("click", function () { let winnerList = document.getElementById("winnerList").children; if (winnerList.length === 0) { Swal.fire({ title: "No Winners Yet!", text: "There are no winners to download.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#dc3545" }); return; } showAdModal(() => { let winners = Array.from(winnerList).map(item => item.textContent).join("\n"); let blob = new Blob([winners], { type: "text/plain" }); let link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "WinnerList.txt"; link.click(); }); }); // ✅ Function to download winner list as an .xlsx (Excel) file document.getElementById("downloadExcelButton").addEventListener("click", function () { let winnerList = document.getElementById("winnerList").children; if (winnerList.length === 0) { Swal.fire({ title: "No Winners Yet!", text: "There are no winners to download.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#dc3545" }); return; } showAdModal(() => { let winners = Array.from(winnerList).map(item => [item.textContent]); let worksheet = XLSX.utils.aoa_to_sheet([["Winner List"], ...winners]); let workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "Winners"); let excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "array" }); let blob = new Blob([excelBuffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }); let link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "WinnerList.xlsx"; link.click(); }); }); // ✅ Function to download winner list as an .csv (CSV) file document.getElementById("downloadCsvButton").addEventListener("click", function () { let winnerList = document.getElementById("winnerList").children; if (winnerList.length === 0) { Swal.fire({ title: "No Winners Yet!", text: "There are no winners to download.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#dc3545" }); return; } showAdModal(() => { let winners = Array.from(winnerList).map(item => item.textContent).join("\n"); let csvContent = "data:text/csv;charset=utf-8," + "Winner List\n" + winners; let encodedUri = encodeURI(csvContent); let link = document.createElement("a"); link.href = encodedUri; link.download = "WinnerList.csv"; link.click(); }); }); // Function to download input data in all formats function downloadInputData(format) { let inputData = document.getElementById("nameInput").value.trim(); if (!inputData) { Swal.fire({ title: "No Input Data!", text: "Please enter or upload names before downloading.", icon: "warning", confirmButtonText: "OK", confirmButtonColor: "#dc3545" }); return; } showAdModal(() => { if (format === "txt") { let blob = new Blob([inputData], { type: "text/plain" }); let link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "InputData.txt"; link.click(); } else if (format === "xlsx") { let names = inputData.split("\n").map(name => [name]); let worksheet = XLSX.utils.aoa_to_sheet([["Input Data"], ...names]); let workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "Input Data"); let excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "array" }); let blob = new Blob([excelBuffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }); let link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "InputData.xlsx"; link.click(); } else if (format === "csv") { let csvContent = "data:text/csv;charset=utf-8," + "Input Data\n" + inputData.replace(/\n/g, "\n"); let encodedUri = encodeURI(csvContent); let link = document.createElement("a"); link.href = encodedUri; link.download = "InputData.csv"; link.click(); } }); } document.getElementById("downloadInputTxtButton").addEventListener("click", () => downloadInputData("txt")); document.getElementById("downloadInputExcelButton").addEventListener("click", () => downloadInputData("xlsx")); document.getElementById("downloadInputCsvButton").addEventListener("click", () => downloadInputData("csv")); </script> <!-- Bootstrap JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> </body> </html>