undefined / index.html
Ali36's picture
باز هم منصفانه نیست مثلا کسی که یک روز با دونفر یک مسئولیت داشته هفته بعد هم با کسی دیگر همان مسئولیت را دارد در حالی که باید منصفانه باشد و هرکس در طول این ۲۰ هفته برار با هم مسئولیت ها را داشته باشن
feb650a verified
<!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 -->
<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>
<!-- Input Section -->
<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>
<!-- Leader Display -->
<div id="leaderDisplay" class="bg-blue-100 text-blue-800 rounded-xl p-4 mb-6 text-center font-medium hidden"></div>
<!-- Schedule Table -->
<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; // Increased to 20 weeks for better distribution
// Validation
if (!leader) {
showAlert('لطفاً نام سرگروه را وارد کنید.', 'error');
return;
}
if (members.includes(leader)) {
showAlert('نام سرگروه نباید در لیست افراد باشد.', 'error');
return;
}
if (members.length < 6) {
showAlert('برای توزیع عادلانه حداقل باید 6 نفر وارد کنید.', 'error');
return;
}
// Show leader
const leaderDisplay = document.getElementById('leaderDisplay');
leaderDisplay.textContent = `سرگروه: ${leader}`;
leaderDisplay.classList.remove('hidden');
leaderDisplay.classList.add('fade-in');
// Generate schedule
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 = {};
// Initialize assignments
Object.keys(tasks).forEach(task => {
assignments[task] = [];
});
// Calculate fair distribution
const totalTasksPerPerson = Math.floor((Object.values(tasks).reduce((a,b) => a+b, 0) * totalWeeks) / members.length);
// Assign tasks with improved fairness algorithm
Object.entries(tasks).forEach(([task, count]) => {
// Get members with least assignments overall
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];
});
// Select required number of least busy members for this 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]++;
// Remove assigned member from available pool for this week
const index = availableMembers.indexOf(member);
if (index !== -1) {
availableMembers.splice(index, 1);
}
}
});
previousWeekAssignments.push(currentWeekAssignments);
// Create table row
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`;
// Week cell
const weekCell = document.createElement('td');
weekCell.className = 'py-3 px-4 font-medium';
weekCell.textContent = `هفته ${week}`;
row.appendChild(weekCell);
// Task cells
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`);
}
// Improved shuffle function
// Improved fair distribution tracking
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>