| <!DOCTYPE html> |
| <html lang="fa" dir="rtl"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>برنامه کاری گروه پذیرایی مدرسه</title> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script> |
| <link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet"> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <style> |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(20px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| .fade-in { |
| animation: fadeIn 0.5s ease-out forwards; |
| } |
| |
| .task-cell { |
| transition: all 0.3s ease; |
| } |
| |
| .task-cell:hover { |
| transform: scale(1.05); |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); |
| } |
| |
| .highlight-leader { |
| position: relative; |
| } |
| |
| .highlight-leader::after { |
| content: "⭐"; |
| position: absolute; |
| left: -20px; |
| top: 50%; |
| transform: translateY(-50%); |
| } |
| </style> |
| </head> |
| <body class="bg-gradient-to-b from-blue-50 to-gray-100 min-h-screen font-[Vazirmatn]"> |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> |
| |
| <header class="bg-gradient-to-r from-blue-600 to-blue-800 text-white rounded-xl shadow-lg p-6 mb-8 text-center"> |
| <h1 class="text-2xl md:text-3xl font-bold">📅 برنامهریزی گروه پذیرایی مدرسه</h1> |
| <p class="mt-2 opacity-90">تقسیم منصفانه مسئولیتهای پذیرایی بین دانشآموزان</p> |
| </header> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <div class="max-w-md mx-auto space-y-6"> |
| <div> |
| <label for="names" class="block text-sm font-medium text-blue-700 mb-1">👥 اسامی اعضا (با نقطه ویرگول جدا کنید):</label> |
| <input type="text" id="names" |
| class="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" |
| placeholder="مثال: علی محمدی.رضا احمدی.امیر حسینی...."> |
| </div> |
|
|
| <div> |
| <label for="leader" class="block text-sm font-medium text-blue-700 mb-1">⭐ سرگروه</label> |
| <input type="text" id="leader" |
| class="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" |
| placeholder="نام سرگروه را وارد کنید"> |
| </div> |
|
|
| <div class="flex gap-4 pt-2"> |
| <button onclick="generateSchedule()" |
| class="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-xl shadow-md transition-all hover:shadow-lg flex items-center justify-center gap-2"> |
| <span>🚀 ایجاد برنامه</span> |
| </button> |
| <button onclick="exportToExcel()" |
| class="flex-1 bg-green-600 hover:bg-green-700 text-white font-medium py-3 px-4 rounded-xl shadow-md transition-all hover:shadow-lg flex items-center justify-center gap-2"> |
| <span>📥 خروجی اکسل</span> |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="leaderDisplay" class="bg-blue-100 text-blue-800 rounded-xl p-4 mb-6 text-center font-medium hidden"></div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md overflow-hidden"> |
| <div class="overflow-x-auto"> |
| <table id="scheduleTable" class="w-full"> |
| <thead class="bg-blue-600 text-white"> |
| <tr> |
| <th class="py-3 px-4">هفته</th> |
| <th class="py-3 px-4">🍽 شستن ظروف (2 نفر)</th> |
| <th class="py-3 px-4">🥘 شستن قابلمه (1 نفر)</th> |
| <th class="py-3 px-4">🧺 جمعآوری سفره (2 نفر)</th> |
| <th class="py-3 px-4">🤝 کمک در پخش (1 نفر)</th> |
| </tr> |
| </thead> |
| <tbody class="divide-y divide-gray-200"> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| let scheduleData = []; |
| let previousWeekAssignments = []; |
| let members = []; |
| let leader = ''; |
| |
| function generateSchedule() { |
| const namesInput = document.getElementById('names').value; |
| leader = document.getElementById('leader').value.trim(); |
| members = namesInput.split('.').map(name => name.trim()).filter(name => name !== ''); |
| const totalWeeks = 20; |
| |
| if (!leader) { |
| showAlert('لطفاً نام سرگروه را وارد کنید.', 'error'); |
| return; |
| } |
| |
| if (members.includes(leader)) { |
| showAlert('نام سرگروه نباید در لیست افراد باشد.', 'error'); |
| return; |
| } |
| if (members.length < 6) { |
| showAlert('برای توزیع عادلانه حداقل باید 6 نفر وارد کنید.', 'error'); |
| return; |
| } |
| |
| const leaderDisplay = document.getElementById('leaderDisplay'); |
| leaderDisplay.textContent = `سرگروه: ${leader}`; |
| leaderDisplay.classList.remove('hidden'); |
| leaderDisplay.classList.add('fade-in'); |
| |
| |
| const scheduleTableBody = document.querySelector('#scheduleTable tbody'); |
| scheduleTableBody.innerHTML = ''; |
| |
| const tasks = { |
| '🍽 شستن ظروف': 2, |
| '🥘 شستن قابلمه': 1, |
| '🧺 جمعآوری سفره': 2, |
| '🤝 کمک در پخش': 1 |
| }; |
| |
| scheduleData = []; |
| previousWeekAssignments = []; |
| const taskRotation = calculateFairness(members, tasks, totalWeeks); |
| for (let week = 1; week <= totalWeeks; week++) { |
| const weekData = { هفته: `هفته ${week}` }; |
| const currentWeekAssignments = {}; |
| const availableMembers = [...members]; |
| const assignments = {}; |
| |
| |
| Object.keys(tasks).forEach(task => { |
| assignments[task] = []; |
| }); |
| |
| const totalTasksPerPerson = Math.floor((Object.values(tasks).reduce((a,b) => a+b, 0) * totalWeeks) / members.length); |
| |
| |
| Object.entries(tasks).forEach(([task, count]) => { |
| |
| const sortedMembers = [...availableMembers].sort((a, b) => { |
| const totalA = Object.values(taskRotation[a]).reduce((sum, val) => sum + val, 0); |
| const totalB = Object.values(taskRotation[b]).reduce((sum, val) => sum + val, 0); |
| return totalA - totalB || taskRotation[a][task] - taskRotation[b][task]; |
| }); |
| |
| |
| for (let i = 0; i < count && i < sortedMembers.length; i++) { |
| const member = sortedMembers[i]; |
| assignments[task].push(member); |
| currentWeekAssignments[member] = task; |
| taskRotation[member][task]++; |
| |
| |
| const index = availableMembers.indexOf(member); |
| if (index !== -1) { |
| availableMembers.splice(index, 1); |
| } |
| } |
| }); |
| previousWeekAssignments.push(currentWeekAssignments); |
| |
| |
| const row = document.createElement('tr'); |
| if (week % 2 === 0) { |
| row.classList.add('bg-gray-50'); |
| } |
| row.classList.add('fade-in'); |
| row.style.animationDelay = `${week * 0.1}s`; |
| |
| |
| const weekCell = document.createElement('td'); |
| weekCell.className = 'py-3 px-4 font-medium'; |
| weekCell.textContent = `هفته ${week}`; |
| row.appendChild(weekCell); |
| |
| |
| Object.entries(tasks).forEach(([task, count]) => { |
| const taskCell = document.createElement('td'); |
| taskCell.className = 'py-3 px-4 task-cell'; |
| |
| const assignedPeople = assignments[task]; |
| taskCell.innerHTML = assignedPeople.map(person => { |
| return person === leader ? `<span class="highlight-leader">${person}</span>` : person; |
| }).join('، '); |
| |
| row.appendChild(taskCell); |
| weekData[task] = assignedPeople.join('، '); |
| }); |
| |
| scheduleTableBody.appendChild(row); |
| scheduleData.push(weekData); |
| } |
| |
| showAlert('برنامه با موفقیت ایجاد شد!', 'success'); |
| } |
| |
| function showAlert(message, type) { |
| const alert = document.createElement('div'); |
| alert.className = `fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg text-white font-medium ${ |
| type === 'error' ? 'bg-red-500' : 'bg-green-500' |
| }`; |
| alert.textContent = message; |
| alert.style.zIndex = 1000; |
| document.body.appendChild(alert); |
| |
| setTimeout(() => { |
| alert.classList.add('opacity-0', 'transition-opacity', 'duration-500'); |
| setTimeout(() => { |
| document.body.removeChild(alert); |
| }, 500); |
| }, 3000); |
| } |
| |
| function exportToExcel() { |
| if (scheduleData.length === 0) { |
| showAlert('ابتدا برنامه کاری را تولید کنید.', 'error'); |
| return; |
| } |
| |
| const worksheetData = [ |
| ['سرگروه:', leader], |
| [], |
| ['هفته', 'شستن ظروف (2 نفر)', 'شستن قابلمه (1 نفر)', 'جمعآوری سفره (2 نفر)', 'کمک در پخش (1 نفر)'] |
| ]; |
| |
| scheduleData.forEach(row => { |
| worksheetData.push([row['هفته'], row['🍽 شستن ظروف'], row['🥘 شستن قابلمه'], row['🧺 جمعآوری سفره'], row['🤝 کمک در پخش']]); |
| }); |
| |
| const worksheet = XLSX.utils.aoa_to_sheet(worksheetData); |
| const workbook = XLSX.utils.book_new(); |
| XLSX.utils.book_append_sheet(workbook, worksheet, 'برنامه کاری'); |
| XLSX.writeFile(workbook, `برنامه_کاری_گروه_پذیرایی_${new Date().toLocaleDateString('fa-IR')}.xlsx`); |
| } |
| |
| |
| |
| function calculateFairness(members, tasks, totalWeeks) { |
| const stats = {}; |
| members.forEach(member => { |
| stats[member] = {}; |
| Object.keys(tasks).forEach(task => { |
| stats[member][task] = 0; |
| }); |
| }); |
| return stats; |
| } |
| </script> |
| </body> |
| </html> |
|
|