bonus-calculator/index.html
2026-06-19 05:50:58 +00:00

246 lines
9.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Калькулятор премий</title>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7}
*{box-sizing:border-box;margin:0;padding:0}
body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--white)}
.container{max-width:1140px;margin:0 auto;padding:80px 24px}
.hero{background:var(--ink);color:var(--white)}
.hero h1{font-size:56px;font-weight:800;line-height:1.05;margin-bottom:24px}
.hero p{font-size:20px;color:#9aa3b2;max-width:600px;margin-bottom:32px}
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none}
.btn:hover{background:#1be5ff}
.section h2{font-size:36px;font-weight:700;margin-bottom:24px}
.card{background:var(--gray-100);border-radius:16px;padding:32px;margin-bottom:16px}
@media (max-width:640px){.hero h1{font-size:36px}.section h2{font-size:28px}.container{padding:48px 20px}}
.form-group{margin-bottom:16px}
.form-group label{display:block;margin-bottom:8px;font-weight:600}
.form-group input,.form-group select{width:100%;padding:12px;border:2px solid var(--gray-100);border-radius:8px;font-size:16px}
.form-group input:focus,.form-group select:focus{outline:none;border-color:var(--cyan)}
.btn-add{background:var(--cyan);color:var(--ink);padding:12px 24px;border:none;border-radius:8px;font-weight:700;cursor:pointer;margin-bottom:24px}
.btn-add:hover{background:#1be5ff}
.table-container{overflow-x:auto}
table{width:100%;border-collapse:collapse;margin-top:16px}
th,td{padding:12px;text-align:left;border-bottom:2px solid var(--gray-100)}
th{background:var(--gray-100);font-weight:600}
.btn-remove{background:#ff4757;color:white;border:none;padding:6px 12px;border-radius:6px;cursor:pointer}
.btn-remove:hover{background:#ff3344}
.result-section{margin-top:32px;padding:24px;background:var(--cyan-50);border-radius:12px}
.result-section h3{margin-bottom:16px}
.result-item{display:flex;justify-content:space-between;margin-bottom:8px;padding:8px;border-bottom:1px solid var(--gray-100)}
.result-item.excluded{color:#ff4757}
.result-item.included{color:#2ed573}
.total{font-weight:700;font-size:20px;margin-top:16px;padding-top:16px;border-top:2px solid var(--ink)}
</style>
</head>
<body>
<section class="hero">
<div class="container">
<h1>Калькулятор премий</h1>
<p>Автоматический расчет премий с учетом испытательного срока. Исключает работников с незаконченным испытательным сроком и пересчитывает фактические рабочие дни.</p>
<a class="btn" href="#calculator">Начать расчет</a>
</div>
</section>
<section id="calculator" class="section">
<div class="container">
<h2>Добавьте работников</h2>
<div class="card">
<div class="form-group">
<label for="fullName">ФИО работника</label>
<input type="text" id="fullName" placeholder="Иванов Иван Иванович">
</div>
<div class="form-group">
<label for="hireDate">Дата приема на работу</label>
<input type="date" id="hireDate">
</div>
<div class="form-group">
<label for="salary">Фактический оклад (₽)</label>
<input type="number" id="salary" placeholder="50000">
</div>
<div class="form-group">
<label for="workedDays">Фактически отработанные рабочие дни</label>
<input type="number" id="workedDays" placeholder="22">
</div>
<div class="form-group">
<label for="reportMonth">Отчетный месяц</label>
<input type="month" id="reportMonth">
</div>
<button class="btn-add" onclick="addWorker()">Добавить работника</button>
</div>
<div class="table-container">
<table id="workersTable">
<thead>
<tr>
<th>ФИО</th>
<th>Дата приема</th>
<th>Оклад (₽)</th>
<th>Отработано дней</th>
<th>Отчетный месяц</th>
<th>Действие</th>
</tr>
</thead>
<tbody id="workersBody">
</tbody>
</table>
</div>
<button class="btn" onclick="calculateBonus()" style="margin-top:24px">Рассчитать премию</button>
<div id="results" class="result-section" style="display:none">
<h3>Результаты расчета</h3>
<div id="resultsList"></div>
<div id="totalResult" class="total"></div>
</div>
</div>
</section>
<script>
let workers = [];
function addWorker() {
const fullName = document.getElementById('fullName').value;
const hireDate = document.getElementById('hireDate').value;
const salary = parseFloat(document.getElementById('salary').value);
const workedDays = parseInt(document.getElementById('workedDays').value);
const reportMonth = document.getElementById('reportMonth').value;
if (!fullName || !hireDate || isNaN(salary) || isNaN(workedDays) || !reportMonth) {
alert('Заполните все поля');
return;
}
workers.push({
id: Date.now(),
fullName,
hireDate,
salary,
workedDays,
reportMonth
});
updateTable();
clearForm();
}
function removeWorker(id) {
workers = workers.filter(w => w.id !== id);
updateTable();
}
function updateTable() {
const tbody = document.getElementById('workersBody');
tbody.innerHTML = workers.map(w => `
<tr>
<td>${w.fullName}</td>
<td>${formatDate(w.hireDate)}</td>
<td>${w.salary.toLocaleString('ru-RU')}</td>
<td>${w.workedDays}</td>
<td>${formatMonth(w.reportMonth)}</td>
<td><button class="btn-remove" onclick="removeWorker(${w.id})">Удалить</button></td>
</tr>
`).join('');
}
function clearForm() {
document.getElementById('fullName').value = '';
document.getElementById('hireDate').value = '';
document.getElementById('salary').value = '';
document.getElementById('workedDays').value = '';
document.getElementById('reportMonth').value = '';
}
function formatDate(dateStr) {
const date = new Date(dateStr);
return date.toLocaleDateString('ru-RU');
}
function formatMonth(monthStr) {
const [year, month] = monthStr.split('-');
const months = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'];
return `${months[parseInt(month)-1]} ${year}`;
}
function calculateBonus() {
if (workers.length === 0) {
alert('Добавьте хотя бы одного работника');
return;
}
const results = [];
let totalBonus = 0;
workers.forEach(worker => {
const hireDate = new Date(worker.hireDate);
const probationEndDate = new Date(hireDate);
probationEndDate.setMonth(probationEndDate.getMonth() + 1);
const [reportYear, reportMonth] = worker.reportMonth.split('-').map(Number);
const reportEndDate = new Date(reportYear, reportMonth, 0);
const isProbationEnded = probationEndDate <= reportEndDate;
let includedDays = 0;
let bonusAmount = 0;
let excludedReason = '';
if (!isProbationEnded) {
excludedReason = `Испытательный срок до ${formatDate(probationEndDate.toISOString().split('T')[0])}`;
} else {
const daysInMonth = reportEndDate.getDate();
const daysBeforeProbation = Math.max(0, Math.ceil((probationEndDate - new Date(reportYear, reportMonth-1, 1)) / (1000*60*60*24)));
includedDays = daysInMonth - daysBeforeProbation;
const dailyRate = worker.salary / daysInMonth;
bonusAmount = dailyRate * includedDays;
}
results.push({
...worker,
isProbationEnded,
includedDays,
bonusAmount,
excludedReason,
probationEndDate: formatDate(probationEndDate.toISOString().split('T')[0])
});
totalBonus += bonusAmount;
});
displayResults(results, totalBonus);
}
function displayResults(results, totalBonus) {
const resultsDiv = document.getElementById('results');
const resultsList = document.getElementById('resultsList');
const totalResult = document.getElementById('totalResult');
resultsList.innerHTML = results.map(r => `
<div class="result-item ${r.isProbationEnded ? 'included' : 'excluded'}">
<div>
<strong>${r.fullName}</strong><br>
<small>${r.isProbationEnded ? `Включен: ${r.includedDays} дней` : `Исключен: ${r.excludedReason}`}</small>
</div>
<div>${r.isProbationEnded ? r.bonusAmount.toLocaleString('ru-RU') + ' ₽' : '—'}</div>
</div>
`).join('');
totalResult.innerHTML = `Итого к выплате: ${totalBonus.toLocaleString('ru-RU')}`;
resultsDiv.style.display = 'block';
}
</script>
</body>
</html>