mirror of
https://github.com/wasrusgen/zov-survey.git
synced 2026-06-03 13:24:49 +00:00
259 lines
11 KiB
HTML
259 lines
11 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||
<title>Анкета сборщика · ЗОВ</title>
|
||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||
<style>
|
||
:root {
|
||
--bg: var(--tg-theme-bg-color, #ffffff);
|
||
--txt: var(--tg-theme-text-color, #1e293b);
|
||
--hint:var(--tg-theme-hint-color, #64748b);
|
||
--sec: var(--tg-theme-secondary-bg-color, #f1f5f9);
|
||
--btn: var(--tg-theme-button-color, #4f46e5);
|
||
--btx: var(--tg-theme-button-text-color, #ffffff);
|
||
--sep: rgba(0,0,0,.07);
|
||
}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font-family:-apple-system,'Segoe UI',system-ui,sans-serif;
|
||
background:var(--bg);color:var(--txt);
|
||
padding:16px;padding-bottom:90px;font-size:15px}
|
||
|
||
.sec-title{font-size:11px;font-weight:700;text-transform:uppercase;
|
||
letter-spacing:.6px;color:var(--hint);margin:20px 0 8px}
|
||
|
||
/* Поля */
|
||
.fgroup{background:var(--sec);border-radius:12px;overflow:hidden}
|
||
.frow{display:flex;flex-direction:column;padding:11px 14px;
|
||
border-bottom:1px solid var(--sep)}
|
||
.frow:last-child{border-bottom:none}
|
||
.flabel{font-size:11px;color:var(--hint);margin-bottom:3px;font-weight:600}
|
||
.finput{background:transparent;border:none;outline:none;
|
||
font-size:15px;color:var(--txt);width:100%;padding:0}
|
||
.finput::placeholder{color:var(--hint)}
|
||
|
||
/* Инструмент */
|
||
.tlist{background:var(--sec);border-radius:12px;overflow:hidden}
|
||
.titem{display:flex;align-items:center;gap:12px;
|
||
padding:13px 14px;border-bottom:1px solid var(--sep);
|
||
cursor:pointer;-webkit-tap-highlight-color:transparent;
|
||
transition:background .1s}
|
||
.titem:last-child{border-bottom:none}
|
||
.titem:active{background:rgba(0,0,0,.04)}
|
||
.tcheck{width:22px;height:22px;border-radius:50%;border:2px solid #cbd5e1;
|
||
flex-shrink:0;display:flex;align-items:center;justify-content:center;
|
||
transition:all .15s}
|
||
.titem.on .tcheck{background:var(--btn);border-color:var(--btn)}
|
||
.tcheck svg{display:none}
|
||
.titem.on .tcheck svg{display:block}
|
||
.tico{font-size:20px;flex-shrink:0;width:26px;text-align:center}
|
||
.tname{flex:1;font-size:14px;line-height:1.35}
|
||
.tpts{font-size:11px;color:var(--hint);background:rgba(0,0,0,.06);
|
||
padding:2px 7px;border-radius:10px;font-weight:700;white-space:nowrap}
|
||
.tpts.pro{background:#ede9fe;color:#6d28d9}
|
||
|
||
/* Результат */
|
||
.scard{background:var(--sec);border-radius:12px;padding:16px;
|
||
display:flex;align-items:center;gap:14px}
|
||
.scirc{width:64px;height:64px;border-radius:50%;border:3px solid #e2e8f0;
|
||
display:flex;flex-direction:column;align-items:center;
|
||
justify-content:center;flex-shrink:0;transition:border-color .3s}
|
||
.snum{font-size:18px;font-weight:800;line-height:1;transition:color .3s}
|
||
.smax{font-size:10px;color:var(--hint)}
|
||
.sinfo{flex:1}
|
||
.scat{font-size:16px;font-weight:700;margin-bottom:5px}
|
||
.sbar-wrap{height:6px;background:#e2e8f0;border-radius:3px;overflow:hidden}
|
||
.sbar{height:100%;border-radius:3px;transition:width .3s,background .3s;width:0%}
|
||
.spct{font-size:11px;color:var(--hint);margin-top:4px}
|
||
|
||
/* Ошибка */
|
||
.err{display:none;color:#dc2626;font-size:12px;padding:8px 14px;
|
||
background:#fef2f2;border-radius:8px;margin-top:8px}
|
||
.err.show{display:block}
|
||
|
||
/* Success overlay */
|
||
.success{display:none;position:fixed;inset:0;background:var(--bg);
|
||
flex-direction:column;align-items:center;justify-content:center;
|
||
gap:12px;text-align:center;padding:32px;z-index:100}
|
||
.success.show{display:flex}
|
||
.success-icon{font-size:64px}
|
||
.success-title{font-size:20px;font-weight:700}
|
||
.success-sub{font-size:14px;color:var(--hint);max-width:260px}
|
||
|
||
/* Кнопка */
|
||
.bar{position:fixed;bottom:0;left:0;right:0;padding:12px 16px;
|
||
background:var(--bg);border-top:1px solid var(--sep)}
|
||
.sbtn{width:100%;padding:14px;background:var(--btn);color:var(--btx);
|
||
border:none;border-radius:12px;font-size:15px;font-weight:600;
|
||
cursor:pointer;transition:opacity .15s}
|
||
.sbtn:active{opacity:.8}
|
||
.sbtn:disabled{opacity:.45;cursor:not-allowed}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="sec-title">Личные данные</div>
|
||
<div class="fgroup">
|
||
<div class="frow">
|
||
<div class="flabel">ФИО</div>
|
||
<input class="finput" id="fio" type="text" placeholder="Иванов Иван Иванович"
|
||
autocomplete="name" autocorrect="off" spellcheck="false">
|
||
</div>
|
||
<div class="frow">
|
||
<div class="flabel">Дата рождения</div>
|
||
<input class="finput" id="dob" type="text" placeholder="15.03.1990"
|
||
inputmode="numeric" maxlength="10">
|
||
</div>
|
||
</div>
|
||
<div class="err" id="err">Заполните ФИО и дату рождения</div>
|
||
|
||
<div class="sec-title">Инструмент</div>
|
||
<div class="tlist" id="tlist"></div>
|
||
|
||
<div class="sec-title">Результат</div>
|
||
<div class="scard">
|
||
<div class="scirc" id="scirc">
|
||
<span class="snum" id="snum">0</span>
|
||
<span class="smax" id="smax">/ 17</span>
|
||
</div>
|
||
<div class="sinfo">
|
||
<div class="scat" id="scat">🥉 Базовый</div>
|
||
<div class="sbar-wrap"><div class="sbar" id="sbar"></div></div>
|
||
<div class="spct" id="spct">0% — отметьте инструмент</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Экран успеха -->
|
||
<div class="success" id="success">
|
||
<div class="success-icon">✅</div>
|
||
<div class="success-title">Анкета отправлена!</div>
|
||
<div class="success-sub">Ожидайте подтверждения категории от руководства</div>
|
||
</div>
|
||
|
||
<div class="bar">
|
||
<button class="sbtn" id="sbtn" onclick="doSubmit()">Отправить анкету</button>
|
||
</div>
|
||
|
||
<script>
|
||
const tg = window.Telegram.WebApp;
|
||
tg.ready();
|
||
tg.expand();
|
||
|
||
const TOOLS = [
|
||
{key:"drill", ico:"🔩", name:"Шуруповёрт / дрель", pts:1},
|
||
{key:"perforator", ico:"⚒️", name:"Перфоратор", pts:1},
|
||
{key:"perf_dust", ico:"💨", name:"Насадка пылеотсоса для перфоратора", pts:1},
|
||
{key:"jigsaw", ico:"🪚", name:"Электролобзик", pts:1},
|
||
{key:"plunge_saw", ico:"🔪", name:"Погружная (дисковая) пила", pts:2},
|
||
{key:"guide_rail", ico:"📏", name:"Шина направляющая", pts:1},
|
||
{key:"grinder", ico:"⚙️", name:"Угловая шлифмашина (болгарка)", pts:1},
|
||
{key:"router", ico:"🔧", name:"Фрезер", pts:1},
|
||
{key:"laser", ico:"📐", name:"Лазерный уровень", pts:1},
|
||
{key:"vacuum", ico:"🧹", name:"Строительный пылесос", pts:1},
|
||
{key:"clamps", ico:"🗜️", name:"Струбцины / зажимы (набор ≥4 шт.)", pts:1},
|
||
{key:"templates", ico:"📋", name:"Мебельные шаблоны / кондукторы", pts:1},
|
||
{key:"stepladder", ico:"🪜", name:"Стремянка", pts:1},
|
||
{key:"hand_tools", ico:"🔨", name:"Ручной инструмент (молоток, стамески, ключи)", pts:1},
|
||
{key:"car", ico:"🚗", name:"Личный автомобиль", pts:2},
|
||
];
|
||
const MAX = TOOLS.reduce((s,t) => s+t.pts, 0); // 17
|
||
document.getElementById('smax').textContent = '/ ' + MAX;
|
||
|
||
const state = {};
|
||
TOOLS.forEach(t => state[t.key] = false);
|
||
|
||
// Рендер списка
|
||
const tlist = document.getElementById('tlist');
|
||
TOOLS.forEach(t => {
|
||
const el = document.createElement('div');
|
||
el.className = 'titem';
|
||
el.id = 'ti_' + t.key;
|
||
const ptsCls = t.pts === 2 ? 'tpts pro' : 'tpts';
|
||
const ptsLbl = t.pts === 2 ? '+2 б' : '+1 б';
|
||
el.innerHTML = `
|
||
<div class="tcheck">
|
||
<svg width="12" height="10" viewBox="0 0 12 10">
|
||
<path d="M1 5L4.5 8.5L11 1" stroke="white" stroke-width="2.2"
|
||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||
</svg>
|
||
</div>
|
||
<span class="tico">${t.ico}</span>
|
||
<span class="tname">${t.name}</span>
|
||
<span class="${ptsCls}">${ptsLbl}</span>`;
|
||
el.addEventListener('click', () => {
|
||
state[t.key] = !state[t.key];
|
||
el.classList.toggle('on', state[t.key]);
|
||
updateScore();
|
||
});
|
||
tlist.appendChild(el);
|
||
});
|
||
|
||
function updateScore() {
|
||
const score = TOOLS.reduce((s,t) => s + (state[t.key] ? t.pts : 0), 0);
|
||
const pct = Math.round(score / MAX * 100);
|
||
document.getElementById('snum').textContent = score;
|
||
|
||
let cat, color;
|
||
if (pct >= 80) { cat = '🥇 Мастер'; color = '#16a34a'; }
|
||
else if (pct >= 50) { cat = '🥈 Специалист'; color = '#d97706'; }
|
||
else { cat = '🥉 Базовый'; color = '#64748b'; }
|
||
|
||
document.getElementById('scat').textContent = cat;
|
||
document.getElementById('sbar').style.cssText = `width:${pct}%;background:${color}`;
|
||
document.getElementById('spct').textContent = pct + '%';
|
||
document.getElementById('scirc').style.borderColor = color;
|
||
document.getElementById('snum').style.color = color;
|
||
}
|
||
|
||
// Автоформат ДР: 15.03.1990
|
||
document.getElementById('dob').addEventListener('input', function(e) {
|
||
let v = e.target.value.replace(/\D/g,'');
|
||
if (v.length > 2) v = v.slice(0,2) + '.' + v.slice(2);
|
||
if (v.length > 5) v = v.slice(0,5) + '.' + v.slice(5);
|
||
e.target.value = v.slice(0,10);
|
||
});
|
||
|
||
function doSubmit() {
|
||
const fio = document.getElementById('fio').value.trim();
|
||
const dob = document.getElementById('dob').value.trim();
|
||
const err = document.getElementById('err');
|
||
|
||
if (!fio || !dob) {
|
||
err.className = 'err show';
|
||
err.textContent = !fio ? 'Введите ФИО' : 'Введите дату рождения (например: 15.03.1990)';
|
||
document.getElementById(fio ? 'dob' : 'fio').focus();
|
||
return;
|
||
}
|
||
err.className = 'err';
|
||
|
||
const score = TOOLS.reduce((s,t) => s + (state[t.key] ? t.pts : 0), 0);
|
||
const pct = Math.round(score / MAX * 100);
|
||
let category;
|
||
if (pct >= 80) category = '🥇 Мастер';
|
||
else if (pct >= 50) category = '🥈 Специалист';
|
||
else category = '🥉 Базовый';
|
||
|
||
const payload = JSON.stringify({fio, dob, tools: state, score, max_score: MAX, pct, category});
|
||
|
||
// Показываем success-экран сразу (до отправки — UX)
|
||
document.getElementById('success').className = 'success show';
|
||
document.getElementById('sbtn').disabled = true;
|
||
|
||
// Отправляем данные в бот и закрываем
|
||
try {
|
||
tg.sendData(payload);
|
||
} catch(e) {
|
||
console.error('sendData error:', e);
|
||
}
|
||
// Fallback: если Telegram не закрыл сам — закрыть через 1.5 сек
|
||
setTimeout(() => {
|
||
try { tg.close(); } catch(e) {}
|
||
}, 1500);
|
||
}
|
||
|
||
updateScore();
|
||
</script>
|
||
</body>
|
||
</html>
|