zov-survey/survey_webapp.html
2026-05-18 15:11:44 +03:00

346 lines
11 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, maximum-scale=1">
<title>Анкета сборщика · ЗОВ</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<style>
:root {
--tg-bg: var(--tg-theme-bg-color, #ffffff);
--tg-text: var(--tg-theme-text-color, #1e293b);
--tg-hint: var(--tg-theme-hint-color, #64748b);
--tg-link: var(--tg-theme-link-color, #4f46e5);
--tg-btn: var(--tg-theme-button-color, #4f46e5);
--tg-btn-text: var(--tg-theme-button-text-color, #ffffff);
--tg-secondary: var(--tg-theme-secondary-bg-color, #f1f5f9);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, 'Segoe UI', system-ui, sans-serif;
background: var(--tg-bg);
color: var(--tg-text);
padding: 16px;
padding-bottom: 80px;
font-size: 15px;
}
.section-title {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .6px;
color: var(--tg-hint);
margin: 20px 0 8px;
}
.field-group {
background: var(--tg-secondary);
border-radius: 12px;
overflow: hidden;
}
.field-row {
display: flex;
flex-direction: column;
padding: 12px 14px;
border-bottom: 1px solid rgba(0,0,0,.06);
}
.field-row:last-child { border-bottom: none; }
.field-label {
font-size: 11px;
color: var(--tg-hint);
margin-bottom: 4px;
font-weight: 600;
}
.field-input {
background: transparent;
border: none;
outline: none;
font-size: 15px;
color: var(--tg-text);
width: 100%;
padding: 0;
}
.field-input::placeholder { color: var(--tg-hint); }
/* Tool list */
.tool-list {
background: var(--tg-secondary);
border-radius: 12px;
overflow: hidden;
}
.tool-item {
display: flex;
align-items: center;
gap: 12px;
padding: 13px 14px;
border-bottom: 1px solid rgba(0,0,0,.06);
cursor: pointer;
transition: background .1s;
-webkit-tap-highlight-color: transparent;
}
.tool-item:last-child { border-bottom: none; }
.tool-item:active { background: rgba(0,0,0,.04); }
.tool-check {
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;
}
.tool-item.checked .tool-check {
background: var(--tg-btn);
border-color: var(--tg-btn);
}
.tool-check svg { display: none; }
.tool-item.checked .tool-check svg { display: block; }
.tool-icon { font-size: 20px; flex-shrink: 0; }
.tool-name { flex: 1; font-size: 14px; line-height: 1.3; }
.tool-pts {
font-size: 12px;
color: var(--tg-hint);
background: rgba(0,0,0,.06);
padding: 2px 7px;
border-radius: 10px;
font-weight: 600;
}
/* Score card */
.score-card {
background: var(--tg-secondary);
border-radius: 12px;
padding: 16px;
display: flex;
align-items: center;
gap: 16px;
}
.score-circle {
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;
}
.score-num { font-size: 18px; font-weight: 800; line-height: 1; }
.score-max { font-size: 10px; color: var(--tg-hint); }
.score-info { flex: 1; }
.score-cat { font-size: 16px; font-weight: 700; margin-bottom: 4px; }
.score-bar-wrap {
height: 6px;
background: #e2e8f0;
border-radius: 3px;
overflow: hidden;
}
.score-bar {
height: 100%;
border-radius: 3px;
transition: width .3s, background .3s;
width: 0%;
}
.score-pct { font-size: 11px; color: var(--tg-hint); margin-top: 3px; }
/* Error */
.error-msg {
display: none;
color: #dc2626;
font-size: 12px;
padding: 8px 14px;
background: #fef2f2;
border-radius: 8px;
margin-top: 8px;
}
.error-msg.show { display: block; }
/* Bottom button */
.submit-bar {
position: fixed;
bottom: 0; left: 0; right: 0;
padding: 12px 16px;
background: var(--tg-bg);
border-top: 1px solid rgba(0,0,0,.08);
}
.submit-btn {
width: 100%;
padding: 14px;
background: var(--tg-btn);
color: var(--tg-btn-text);
border: none;
border-radius: 12px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: opacity .15s;
}
.submit-btn:active { opacity: .8; }
.submit-btn:disabled { opacity: .45; cursor: not-allowed; }
</style>
</head>
<body>
<div class="section-title">Личные данные</div>
<div class="field-group">
<div class="field-row">
<div class="field-label">ФИО</div>
<input class="field-input" id="fio" type="text" placeholder="Иванов Иван Иванович"
autocomplete="name" autocorrect="off">
</div>
<div class="field-row">
<div class="field-label">Дата рождения</div>
<input class="field-input" id="dob" type="text" placeholder="15.03.1990"
inputmode="numeric" maxlength="10">
</div>
</div>
<div class="error-msg" id="err">Заполните ФИО и дату рождения</div>
<div class="section-title">Инструмент</div>
<div class="tool-list" id="toolList"></div>
<div class="section-title">Результат</div>
<div class="score-card">
<div class="score-circle" id="scoreCircle">
<span class="score-num" id="scoreNum">0</span>
<span class="score-max">/ 11</span>
</div>
<div class="score-info">
<div class="score-cat" id="scoreCat">🥉 Базовый</div>
<div class="score-bar-wrap">
<div class="score-bar" id="scoreBar"></div>
</div>
<div class="score-pct" id="scorePct">0% — заполните анкету</div>
</div>
</div>
<div class="submit-bar">
<button class="submit-btn" id="submitBtn" onclick="submit()">
Отправить анкету
</button>
</div>
<script>
const tg = window.Telegram.WebApp;
tg.ready();
tg.expand();
// ─── Инструменты ─────────────────────────────────────────
const TOOLS = [
{ key: "drill", icon: "🔩", name: "Шуруповёрт / дрель", pts: 1 },
{ key: "perforator", icon: "⚒", name: "Перфоратор", pts: 1 },
{ key: "jigsaw", icon: "🪚", name: "Электролобзик", pts: 1 },
{ key: "grinder", icon: "⚙️", name: "Угловая шлифмашина (болгарка)", pts: 1 },
{ key: "router", icon: "🔧", name: "Фрезер", pts: 1 },
{ key: "laser", icon: "📐", name: "Лазерный уровень", pts: 1 },
{ key: "vacuum", icon: "🧹", name: "Строительный пылесос", pts: 1 },
{ key: "clamps", icon: "🗜", name: "Струбцины / зажимы (набор)", pts: 1 },
{ key: "hand_tools", icon: "🔨", name: "Ручной инструмент (молоток, стамески, ключи)", pts: 1 },
{ key: "car", icon: "🚗", name: "Личный автомобиль", pts: 2 },
];
const MAX_SCORE = TOOLS.reduce((s, t) => s + t.pts, 0); // 11
const checked = {};
TOOLS.forEach(t => checked[t.key] = false);
// ─── Рендер чеклиста ────────────────────────────────────
const list = document.getElementById('toolList');
TOOLS.forEach(tool => {
const el = document.createElement('div');
el.className = 'tool-item';
el.id = 'item_' + tool.key;
el.innerHTML = `
<div class="tool-check">
<svg width="12" height="10" viewBox="0 0 12 10" fill="none">
<path d="M1 5L4.5 8.5L11 1" stroke="white" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<span class="tool-icon">${tool.icon}</span>
<span class="tool-name">${tool.name}</span>
<span class="tool-pts">${tool.pts === 2 ? '+2 б' : '+1 б'}</span>
`;
el.addEventListener('click', () => toggle(tool.key, el));
list.appendChild(el);
});
function toggle(key, el) {
checked[key] = !checked[key];
el.classList.toggle('checked', checked[key]);
updateScore();
}
// ─── Живой подсчёт ──────────────────────────────────────
function updateScore() {
const score = TOOLS.reduce((s, t) => s + (checked[t.key] ? t.pts : 0), 0);
const pct = Math.round(score / MAX_SCORE * 100);
document.getElementById('scoreNum').textContent = score;
let cat, color;
if (pct >= 80) { cat = '🥇 Мастер'; color = '#16a34a'; }
else if (pct >= 50) { cat = '🥈 Специалист'; color = '#d97706'; }
else { cat = '🥉 Базовый'; color = '#64748b'; }
document.getElementById('scoreCat').textContent = cat;
document.getElementById('scoreBar').style.width = pct + '%';
document.getElementById('scoreBar').style.background = color;
document.getElementById('scorePct').textContent = pct + '%';
document.getElementById('scoreCircle').style.borderColor = color;
document.getElementById('scoreNum').style.color = color;
}
// ─── Автоформат ДР ──────────────────────────────────────
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 submit() {
const fio = document.getElementById('fio').value.trim();
const dob = document.getElementById('dob').value.trim();
const errEl = document.getElementById('err');
if (!fio || !dob) {
errEl.classList.add('show');
errEl.textContent = !fio
? 'Введите ФИО'
: 'Введите дату рождения (например: 15.03.1990)';
document.getElementById(fio ? 'dob' : 'fio').focus();
return;
}
errEl.classList.remove('show');
const score = TOOLS.reduce((s, t) => s + (checked[t.key] ? t.pts : 0), 0);
const pct = Math.round(score / MAX_SCORE * 100);
let category;
if (pct >= 80) category = '🥇 Мастер';
else if (pct >= 50) category = '🥈 Специалист';
else category = '🥉 Базовый';
const payload = JSON.stringify({
fio, dob, tools: checked, score, max_score: MAX_SCORE, pct, category
});
const btn = document.getElementById('submitBtn');
btn.disabled = true;
btn.textContent = 'Отправляем...';
tg.sendData(payload);
}
// Начальный рендер
updateScore();
</script>
</body>
</html>