zadacha-sotrudnika-prostoy-k/index.html

300 lines
13 KiB
HTML
Raw Permalink 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>
<!-- Дизайн: Стандартный дизайн. Сохраняй все пять CSS; подробности в design.md. -->
<html lang="ru" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Калькулятор отпускных</title>
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
<link rel="stylesheet" href="design-system/kt-ai-components.css">
<link rel="stylesheet" href="design-system/kt-ai-page.css">
<link rel="stylesheet" href="design-system/vibe-theme.css">
<style>
a.kt-ai-btn { text-decoration: none; }
.calc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--kt-ai-space-5); margin-top: var(--kt-ai-space-6); }
.calc-actions { display: flex; gap: var(--kt-ai-space-3); margin-top: var(--kt-ai-space-2); flex-wrap: wrap; }
.result-block { display: flex; flex-direction: column; gap: var(--kt-ai-space-2); }
.result-formula { font-size: var(--kt-ai-text-2xs); color: var(--kt-ai-fg-faint); font-family: var(--kt-ai-font-mono); }
.examples-head { display: flex; align-items: center; gap: var(--kt-ai-space-3); margin-top: var(--kt-ai-space-6); }
.examples-grid { display: grid; gap: var(--kt-ai-space-4); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); margin-top: var(--kt-ai-space-3); }
.example-card .cap { font-size: var(--kt-ai-text-2xs); color: var(--kt-ai-fg-muted); }
.example-card .num { font-size: var(--kt-ai-text-lg); font-weight: var(--kt-ai-weight-semibold); font-variant-numeric: tabular-nums; }
.example-card .sum { font-size: var(--kt-ai-text-xl); font-weight: var(--kt-ai-weight-semibold); color: var(--kt-ai-primary); font-variant-numeric: tabular-nums; }
.howto-grid { display: grid; gap: var(--kt-ai-space-4); grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin-top: var(--kt-ai-space-4); }
[hidden] { display: none !important; }
@media (max-width: 640px) {
.calc-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body class="kt-ai-app" data-kt-data-mode="demo">
<header class="kt-ai-pagehead">
<div class="kt-ai-wrap kt-ai-pagehead-in">
<img src="design-system/brand/kazakhtelecom-logo.png" alt="Казахтелеком" height="28" style="display:block">
<span style="font-weight:var(--kt-ai-weight-semibold)">Калькулятор отпускных</span>
<span class="kt-ai-spacer"></span>
<span class="kt-ai-chip" data-tone="orange">Демо-данные</span>
</div>
</header>
<section class="kt-ai-hero" data-align="center">
<div class="kt-ai-wrap">
<h1>Сколько выплатят за отпуск</h1>
<p class="kt-ai-hero-sub">Введите оклад и количество дней — калькулятор сразу покажет итоговую сумму. Расчёт: оклад ÷ 30 × дни.</p>
</div>
</section>
<section class="kt-ai-section" data-divider="true">
<div class="kt-ai-wrap">
<div class="kt-ai-card">
<div class="kt-ai-section-head">
<h2>Расчёт</h2>
<p>Заполните два поля и нажмите «Рассчитать». Последний расчёт сохраняется в этом браузере.</p>
</div>
<div class="calc-grid">
<div class="kt-ai-field">
<label for="salary">Оклад, тг</label>
<input id="salary" class="kt-ai-input" type="number" min="0" step="1" inputmode="numeric" placeholder="Например, 200000">
<span class="hint">Месячная зарплата без надбавок</span>
<span class="error" id="salary-err" hidden>Введите оклад больше нуля</span>
</div>
<div class="kt-ai-field">
<label for="days">Дни отпуска</label>
<input id="days" class="kt-ai-input" type="number" min="0" max="120" step="1" inputmode="numeric" placeholder="Например, 14">
<span class="hint">Целое количество дней, от 1 до 120</span>
<span class="error" id="days-err" hidden>Введите число дней от 1 до 120</span>
</div>
</div>
<div class="calc-actions">
<button id="calc-btn" class="kt-ai-btn" data-variant="primary" type="button">Рассчитать</button>
<button id="clear-btn" class="kt-ai-btn" data-variant="ghost" type="button">Очистить</button>
</div>
<div id="result" class="kt-ai-card" data-glow="strong" hidden style="margin-top:var(--kt-ai-space-5)">
<div class="result-block">
<span class="kt-ai-chip" data-tone="blue">Результат</span>
<div class="kt-ai-kpi">
<span class="value" id="result-total">0 тг</span>
<span class="label">итого к выплате за отпуск</span>
</div>
<div class="kt-ai-kpi-strip">
<div class="kt-ai-kpi">
<span class="value" id="result-perday">0 тг</span>
<span class="label">средний дневной оклад</span>
</div>
<div class="kt-ai-kpi">
<span class="value" id="result-days">0</span>
<span class="label">дней в отпуске</span>
</div>
<div class="kt-ai-kpi">
<span class="value" id="result-salary">0 тг</span>
<span class="label">оклад, за основу</span>
</div>
</div>
<div class="result-formula" id="result-formula"></div>
</div>
</div>
<div class="kt-ai-empty" id="empty" hidden>
<div class="icon-spot"><svg width="20" height="20"><use href="design-system/icons/kt-ai-lucide-sprite.svg#tool"></use></svg></div>
<div class="title">Пока ничего не рассчитано</div>
<div class="hint">Заполните поля выше или выберите демо-пример ниже — блок результата появится здесь.</div>
</div>
</div>
<div class="examples-head">
<h2 style="font-size:var(--kt-ai-text-md);font-weight:var(--kt-ai-weight-semibold)">Демо-примеры</h2>
<span class="kt-ai-chip" data-tone="orange">Демо-данные</span>
</div>
<p style="font-size:var(--kt-ai-text-sm);color:var(--kt-ai-fg-muted)">Нажмите на карточку, чтобы подставить значения в форму.</p>
<div class="examples-grid" id="examples"></div>
</div>
</section>
<section class="kt-ai-section" data-divider="true">
<div class="kt-ai-wrap">
<div class="kt-ai-section-head">
<h2>Как считается</h2>
<p>Формула калькулятора — демонстрационная и не заменяет расчёт по трудовому законодательству Казахстана.</p>
</div>
<div class="howto-grid">
<article class="kt-ai-tile">
<h3>1. Дневной оклад</h3>
<p>Оклад делим на 30 — условная длительность месяца в рабочих расчётах.</p>
</article>
<article class="kt-ai-tile">
<h3>2. Умножаем на дни</h3>
<p>Дневной оклад умножаем на количество дней отпуска.</p>
</article>
<article class="kt-ai-tile">
<h3>3. Итог в тг</h3>
<p>Сумма округляется до целых тенге и показывается в блоке результата.</p>
</article>
</div>
</div>
</section>
<footer class="kt-ai-wrap kt-ai-pagefoot">
<span>© 2026 Калькулятор отпускных</span>
<span class="kt-ai-spacer"></span>
<span>Демо-прототип, демонстрационные данные</span>
</footer>
<script>
(function () {
"use strict";
var LS_KEY = "vacation-calc:state:v1";
var EXAMPLES = [
{ id: "demo-14", name: "Стандартный отпуск", salary: 200000, days: 14 },
{ id: "demo-7", name: "Короткий отпуск", salary: 250000, days: 7 },
{ id: "demo-30", name: "Полный месяц", salary: 180000, days: 30 }
];
var salEl = document.getElementById("salary");
var daysEl = document.getElementById("days");
var salErr = document.getElementById("salary-err");
var daysErr = document.getElementById("days-err");
var calcBtn = document.getElementById("calc-btn");
var clearBtn = document.getElementById("clear-btn");
var resultBox = document.getElementById("result");
var emptyBox = document.getElementById("empty");
var totalEl = document.getElementById("result-total");
var perdayEl = document.getElementById("result-perday");
var daysResEl = document.getElementById("result-days");
var salaryResEl = document.getElementById("result-salary");
var formulaEl = document.getElementById("result-formula");
var examplesBox = document.getElementById("examples");
var nf = new Intl.NumberFormat("ru-RU");
function fmt(n) { return nf.format(Math.round(n)) + " тг"; }
function compute(salary, days) {
var perDay = salary / 30;
return {
salary: salary,
days: days,
perDay: perDay,
total: perDay * days
};
}
function save(state) {
try { localStorage.setItem(LS_KEY, JSON.stringify(state)); } catch (e) { /* приватный режим */ }
}
function load() {
try {
var raw = localStorage.getItem(LS_KEY);
return raw ? JSON.parse(raw) : null;
} catch (e) { return null; }
}
function showError(field, errEl, bad) {
errEl.hidden = !bad;
field.style.borderColor = bad ? "var(--kt-ai-status-risk-fg)" : "";
}
function validate(salary, days) {
var badSal = !(salary > 0);
var badDays = !(Number.isInteger(days) && days >= 1 && days <= 120);
showError(salEl, salErr, badSal);
showError(daysEl, daysErr, badDays);
return !badSal && !badDays;
}
function renderResult(r) {
resultBox.hidden = false;
emptyBox.hidden = true;
totalEl.textContent = fmt(r.total);
perdayEl.textContent = fmt(r.perDay);
daysResEl.textContent = String(r.days);
salaryResEl.textContent = fmt(r.salary);
formulaEl.textContent = nf.format(r.salary) + " ÷ 30 × " + r.days + " = " + nf.format(Math.round(r.total)) + " тг";
}
function renderEmpty() {
resultBox.hidden = true;
emptyBox.hidden = false;
}
function calc() {
var salary = Math.round(Number(salEl.value)) || 0;
var days = Math.round(Number(daysEl.value)) || 0;
if (!validate(salary, days)) return;
var r = compute(salary, days);
renderResult(r);
save({ last: r, inputs: { salary: salary, days: days } });
}
function clear() {
salEl.value = "";
daysEl.value = "";
showError(salEl, salErr, false);
showError(daysEl, daysErr, false);
renderEmpty();
save(null);
}
function applyExample(ex) {
salEl.value = String(ex.salary);
daysEl.value = String(ex.days);
showError(salEl, salErr, false);
showError(daysEl, daysErr, false);
var r = compute(ex.salary, ex.days);
renderResult(r);
save({ last: r, inputs: { salary: ex.salary, days: ex.days } });
resultBox.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
function buildExamples() {
examplesBox.innerHTML = "";
EXAMPLES.forEach(function (ex) {
var r = compute(ex.salary, ex.days);
var card = document.createElement("article");
card.className = "kt-ai-card example-card";
card.setAttribute("data-clickable", "true");
card.innerHTML =
'<div class="cap">' + ex.name + "</div>" +
'<div class="num">' + r.days + " дн. · " + nf.format(ex.salary) + " тг</div>" +
'<div class="sum">' + fmt(r.total) + "</div>" +
'<button class="kt-ai-btn" data-variant="ghost" data-size="sm" type="button" style="margin-top:var(--kt-ai-space-3)">Подставить</button>';
card.querySelector("button").addEventListener("click", function () { applyExample(ex); });
card.addEventListener("click", function (e) {
if (e.target.tagName !== "BUTTON") applyExample(ex);
});
examplesBox.appendChild(card);
});
}
calcBtn.addEventListener("click", calc);
clearBtn.addEventListener("click", clear);
[salEl, daysEl].forEach(function (el) {
el.addEventListener("keydown", function (e) { if (e.key === "Enter") calc(); });
el.addEventListener("input", function () {
if (!el.value) showError(el === salEl ? salEl : daysEl, el === salEl ? salErr : daysErr, false);
});
});
buildExamples();
var saved = load();
if (saved && saved.inputs) {
salEl.value = String(saved.inputs.salary);
daysEl.value = String(saved.inputs.days);
if (saved.last) renderResult(saved.last);
} else {
renderEmpty();
}
})();
</script>
</body>
</html>