Files
test/public/prototype.html
2026-02-10 17:44:25 +07:00

527 lines
16 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>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Прототип</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700&display=swap');
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
font-family: 'Manrope', system-ui, sans-serif;
color: #1b1f23;
background: #eef1f4;
}
.app {
height: 100%;
display: flex;
flex-direction: column;
gap: 12px;
padding: 72px 16px 18px;
background: #eef1f4;
}
.screen-title {
font-size: 20px;
font-weight: 800;
letter-spacing: -0.02em;
color: #14171c;
}
.screen-subtitle {
margin-top: 6px;
font-size: 12px;
color: rgba(27, 31, 35, 0.65);
}
.analysis-hero {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: 10px;
padding: 8px 12px;
}
.analysis-sub {
font-size: 12px;
color: rgba(27, 31, 35, 0.6);
}
.card {
background: transparent;
border-radius: 12px;
border: none;
padding: 0;
box-shadow: none;
}
.card.main {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.section-title {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgba(27, 31, 35, 0.45);
}
.media-count {
font-size: 12px;
font-weight: 600;
color: rgba(27, 31, 35, 0.7);
}
.media-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 6px;
margin-top: 12px;
}
.thumb {
position: relative;
height: 64px;
border-radius: 10px;
background-size: cover;
background-position: center;
overflow: hidden;
border: 1px solid rgba(27, 31, 35, 0.08);
}
.thumb::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0.35));
}
.thumb.video::before {
content: '▶';
position: absolute;
top: 8px;
left: 8px;
width: 20px;
height: 20px;
border-radius: 999px;
background: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.thumb span {
position: absolute;
bottom: 6px;
left: 6px;
font-size: 10px;
font-weight: 600;
color: #fff;
z-index: 1;
}
.loader {
height: 8px;
background: rgba(15, 76, 129, 0.12);
border-radius: 999px;
overflow: hidden;
width: 140px;
}
.loader__bar {
height: 100%;
width: 0;
background: linear-gradient(90deg, #0f4c81, #4f8fc7);
border-radius: inherit;
transition: width 0.4s ease;
}
.hint {
font-size: 11px;
color: rgba(27, 31, 35, 0.55);
}
.table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
margin-top: 12px;
}
.table th,
.table td {
padding: 6px 6px;
text-align: left;
border-bottom: 1px solid rgba(27, 31, 35, 0.08);
}
.table th {
color: rgba(27, 31, 35, 0.55);
font-weight: 600;
}
.total {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 10px;
border-radius: 10px;
background: #f0f2f4;
font-size: 12px;
font-weight: 700;
}
.q {
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
margin-left: 4px;
border-radius: 999px;
background: rgba(15, 76, 129, 0.12);
color: #0f4c81;
font-size: 10px;
font-weight: 700;
}
.accept-card {
display: grid;
grid-template-columns: 54px 1fr;
gap: 10px;
align-items: center;
margin-top: 12px;
padding: 10px;
border-radius: 12px;
background: #f0f2f4;
}
.accept-photo {
width: 54px;
height: 54px;
border-radius: 10px;
background-size: cover;
background-position: center;
border: 1px solid rgba(27, 31, 35, 0.08);
}
.accept-title {
font-size: 12px;
font-weight: 600;
}
.accept-meta {
font-size: 11px;
color: rgba(27, 31, 35, 0.6);
}
.accept-note {
margin-top: 8px;
font-size: 11px;
color: rgba(27, 31, 35, 0.6);
}
.tag {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 600;
}
.tag.success {
background: rgba(31, 129, 84, 0.14);
color: #1f8154;
}
.tag.pending {
background: rgba(211, 135, 32, 0.15);
color: #b7741c;
}
.tag.info {
background: rgba(26, 77, 122, 0.14);
color: #1a4d7a;
}
.toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
padding: 8px 10px;
border-radius: 10px;
background: #f0f2f4;
font-size: 12px;
font-weight: 600;
}
.switch {
position: relative;
width: 42px;
height: 22px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
inset: 0;
background-color: #c7ced6;
transition: 0.2s;
border-radius: 999px;
}
.slider:before {
position: absolute;
content: '';
height: 18px;
width: 18px;
left: 2px;
top: 2px;
background-color: white;
transition: 0.2s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.switch input:checked + .slider {
background-color: #0f4c81;
}
.switch input:checked + .slider:before {
transform: translateX(20px);
}
.list {
display: grid;
gap: 8px;
margin-top: 12px;
}
.item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 10px;
border-radius: 10px;
background: #f0f2f4;
font-size: 12px;
}
.action {
border: none;
border-radius: 12px;
padding: 12px 14px;
font-size: 18px;
font-weight: 700;
cursor: pointer;
background: #0f4c81;
color: #fff;
box-shadow: 0 10px 20px rgba(15, 76, 129, 0.25);
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}
.action:active {
transform: scale(0.98);
}
.action[disabled] {
opacity: 0.5;
cursor: not-allowed;
box-shadow: none;
transform: none;
}
</style>
</head>
<body>
<div class="app">
<div class="card main">
<div id="screenContent"></div>
</div>
<button class="action" id="actionButton" aria-label="Дальше"></button>
</div>
<script>
(function () {
var state = {
screen: 0,
loadingTimer: null,
loadingProgress: 0,
accepted: false
};
var screens = [
{
content: function () {
return (
'<div class="screen-title">Добавление объекта</div>' +
'<div class="media-grid">' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1502005097973-6a7082348e28?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1484154218962-a197022b5858?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1493809842364-78817add7ffb?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1502005097973-6a7082348e28?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1484154218962-a197022b5858?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1493809842364-78817add7ffb?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div class="thumb video" style="background-image:url(https://images.unsplash.com/photo-1484154218962-a197022b5858?auto=format&fit=crop&w=600&q=60)"><span>Видео</span></div>' +
'<div class="thumb video" style="background-image:url(https://images.unsplash.com/photo-1502005097973-6a7082348e28?auto=format&fit=crop&w=600&q=60)"><span>Видео</span></div>' +
'<div class="thumb" style="background-image:url(https://images.unsplash.com/photo-1493809842364-78817add7ffb?auto=format&fit=crop&w=600&q=60)"></div>' +
'</div>'
);
},
onEnter: function () {},
onAction: function () {
state.screen = 1;
render();
}
},
{
content: function () {
return (
'<div class="analysis-hero">' +
'<div class="screen-title">Анализируем объект</div>' +
'<div class="analysis-sub">Формируем смету, этапы и критерии приемки</div>' +
'<div class="loader"><div class="loader__bar" id="loaderBar"></div></div>' +
'</div>'
);
},
onEnter: function () {
var bar = document.getElementById('loaderBar');
state.loadingProgress = 0;
if (state.loadingTimer) {
clearInterval(state.loadingTimer);
}
state.loadingTimer = setInterval(function () {
state.loadingProgress += 20;
if (bar) {
bar.style.width = state.loadingProgress + '%';
}
if (state.loadingProgress >= 100) {
clearInterval(state.loadingTimer);
state.loadingTimer = null;
state.screen = 2;
render();
}
}, 1000);
},
onAction: function () {}
},
{
content: function () {
return (
'<div class="screen-title">Готовая смета по вашему объекту</div>' +
'<div class="screen-subtitle">Ознакомьтесь с этапами работ и итоговой стоимостью.</div>' +
'<table class="table">' +
'<thead><tr><th>Работы</th><th>Старт</th><th>Финиш</th><th>Сумма</th></tr></thead>' +
'<tbody>' +
'<tr><td>Демонтаж<span class="q" title="Стены чистые, мусор вывезен">?</span></td><td>12.03</td><td>15.03</td><td>120 000 ₽</td></tr>' +
'<tr><td>Черновые работы<span class="q" title="Геометрия в допуске">?</span></td><td>16.03</td><td>24.03</td><td>340 000 ₽</td></tr>' +
'<tr><td>Электрика<span class="q" title="Маркировка и линии">?</span></td><td>25.03</td><td>28.03</td><td>180 000 ₽</td></tr>' +
'<tr><td>Чистовая отделка<span class="q" title="Без дефектов">?</span></td><td>29.03</td><td>08.04</td><td>410 000 ₽</td></tr>' +
'</tbody>' +
'</table>' +
'<div class="section-title" style="margin-top:8px;">Материалы</div>' +
'<table class="table">' +
'<thead><tr><th>Позиция</th><th>Объём</th><th>Сумма</th></tr></thead>' +
'<tbody>' +
'<tr><td>Штукатурка</td><td>120 м²</td><td>94 000 ₽</td></tr>' +
'<tr><td>Кабель</td><td>320 м</td><td>52 000 ₽</td></tr>' +
'<tr><td>Плитка</td><td>34 м²</td><td>126 000 ₽</td></tr>' +
'<tr><td>Смеси</td><td>48 меш.</td><td>37 000 ₽</td></tr>' +
'</tbody>' +
'</table>' +
'<div class="total" style="margin-top:8px;"><span>Итого</span><span>1 359 000 ₽</span></div>'
);
},
onEnter: function () {},
onAction: function () {
state.screen = 3;
render();
}
},
{
content: function () {
return (
'<div class="screen-title">Подтвердите выполнение этапа работ</div>' +
'<div class="accept-card">' +
'<div class="accept-photo" style="background-image:url(https://images.unsplash.com/photo-1484154218962-a197022b5858?auto=format&fit=crop&w=600&q=60)"></div>' +
'<div>' +
'<div class="accept-title">Черновые работы</div>' +
'<div class="accept-meta">Исполнитель: Смирнов А. · Фотоотчет загружен</div>' +
'</div>' +
'</div>' +
'<div class="toggle-row">' +
'<span>Подтвердить приемку</span>' +
'<label class="switch">' +
'<input type="checkbox" id="acceptToggle" ' + (state.accepted ? 'checked' : '') + ' />' +
'<span class="slider"></span>' +
'</label>' +
'</div>' +
'<div class="accept-note">Подтверждая приемку работ, вы переводите оплату исполнителю.</div>'
);
},
onEnter: function () {
var toggle = document.getElementById('acceptToggle');
if (toggle) {
toggle.addEventListener('change', function (event) {
state.accepted = event.target.checked;
render();
});
}
},
onAction: function () {
state.screen = 0;
state.accepted = false;
render();
}
}
];
var screenContent = document.getElementById('screenContent');
var actionButton = document.getElementById('actionButton');
function render() {
var screen = screens[state.screen];
screenContent.innerHTML = screen.content();
actionButton.disabled = state.screen === 1;
if (screen.onEnter) {
screen.onEnter();
}
}
actionButton.addEventListener('click', function () {
var screen = screens[state.screen];
if (screen.onAction) {
screen.onAction();
}
});
render();
})();
</script>
</body>
</html>