Files
portal/web/v8/v8_errors.html
T

325 lines
15 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>v8 · Экраны ошибок — Лидерра</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
:root {
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
--hairline:#D9D5CD;
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C;
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
--side-icon-act:#32C8A9;
--st-fail:#6C60C4;
--st-new:#B94837;
--r-sm:6px; --r-md:10px; --r-lg:14px;
--font-ui:'Inter',system-ui,sans-serif;
--font-mono:'JetBrains Mono',ui-monospace,monospace;
}
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
button { font-family:inherit; }
a { color:inherit; text-decoration:none; }
a:focus-visible, button:focus-visible {
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
}
/* Tab bar — for review only, not part of error pages themselves */
.review-bar { position:sticky; top:0; z-index:50; background:#012019; padding:10px 16px; display:flex; align-items:center; gap:14px; border-bottom:1px solid #1A3A30; }
.review-bar .label { font-size:11px; font-weight:600; letter-spacing:0.06em; color:#7A8C87; font-family:var(--font-mono); }
.review-bar .tabs { display:flex; gap:2px; }
.review-bar .tab { height:26px; padding:0 12px; border:none; background:transparent; color:#B1C2BD; font-family:inherit; font-size:12px; font-weight:500; cursor:pointer; border-radius:4px; }
.review-bar .tab:hover { background:#0A2A22; color:#fff; }
.review-bar .tab.active { background:#fff; color:#012019; }
/* Error page shell */
.error-page {
min-height: calc(100vh - 47px);
display: flex; flex-direction: column; align-items: center; justify-content: center;
padding: 60px 24px 80px;
text-align: center;
position: relative;
}
.error-page .top-brand {
position: absolute; top: 24px; left: 28px;
display: flex; align-items: center; gap: 10px;
font-weight: 600;
font-size: 14.5px;
letter-spacing: -0.01em;
font-variation-settings:'opsz' 18;
color: var(--ink);
}
.error-page .top-brand .mark {
width: 22px; height: 22px;
border-radius: 4px;
background: var(--ink);
display: inline-flex; align-items: center; justify-content: center;
flex-shrink: 0; overflow: hidden;
}
.error-page .top-brand .mark svg { width:100%; height:100%; display:block; }
.error-page .top-brand .dot { color: var(--accent); }
.err-code {
font-family: var(--font-mono);
font-feature-settings: 'tnum';
font-size: 130px;
font-weight: 600;
letter-spacing: -0.04em;
line-height: 1;
color: var(--ink);
margin: 0 0 8px;
display: inline-flex; align-items: baseline; gap: 0;
position: relative;
}
.err-code .accent {
color: var(--accent);
}
.err-code::after {
content: '';
position: absolute;
left: 50%; bottom: -10px; transform: translateX(-50%);
width: 36px; height: 2px;
background: var(--accent);
border-radius: 2px;
}
.err-title {
font-size: 26px;
font-weight: 600;
font-variation-settings:'opsz' 26;
letter-spacing: -0.02em;
line-height: 1.2;
margin: 28px 0 10px;
}
.err-desc {
font-size: 14px;
color: var(--ink-3);
line-height: 1.55;
max-width: 480px;
margin: 0 auto 28px;
letter-spacing: -0.005em;
}
.err-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
.btn { display:inline-flex; align-items:center; gap:7px; height:38px; padding:0 18px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:13px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
.btn:hover { border-color:#BFB9AB; background:var(--sunken); }
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
.btn svg { width:13px; height:13px; stroke-width:1.7; }
.err-id {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ink-3);
letter-spacing: -0.005em;
margin-top: 28px;
display: inline-flex; align-items: center; gap: 8px;
}
.err-id .copy-btn { width:18px; height:18px; border:none; background:none; color:var(--ink-3); border-radius:3px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
.err-id .copy-btn:hover { background:var(--sunken); color:var(--ink); }
.err-id .copy-btn svg { width:11px; height:11px; }
.err-help {
font-size: 12.5px;
color: var(--ink-3);
margin-top: 18px;
letter-spacing: -0.005em;
}
.err-help a { color: var(--accent); font-weight: 500; }
.err-help a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* Status decoration block */
.err-illust {
width: 100%; max-width: 320px;
margin: 0 auto 8px;
display: flex; align-items: center; justify-content: center;
}
/* status indicator (for 500) */
.status-list {
margin-top: 30px;
display: flex; gap: 14px; align-items: center;
font-size: 11.5px;
color: var(--ink-3);
}
.status-list .item { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-feature-settings:'tnum'; }
.status-list .dot { width: 7px; height: 7px; border-radius: 50%; position: relative; }
.status-list .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
.status-list .ok .dot { background: var(--accent); }
.status-list .deg .dot { background: var(--st-fail); }
.status-list .down .dot { background: var(--st-new); }
/* page switcher */
.error-page { display: none; }
.error-page.active { display: flex; }
/* error 403 — special */
#err-403 .err-code .accent { color: var(--st-fail); }
#err-403 .err-code::after { background: var(--st-fail); }
/* error 500 */
#err-500 .err-code .accent { color: var(--st-new); }
#err-500 .err-code::after { background: var(--st-new); }
@media (max-width: 768px) {
.err-code { font-size: 88px; }
.err-title { font-size: 22px; }
.error-page { padding: 80px 18px 40px; }
.error-page .top-brand { top: 14px; left: 16px; }
}
</style>
</head>
<body>
<!-- Review-only tabs to switch between error states -->
<nav class="review-bar" aria-label="Переключение между состояниями ошибок (только для review)">
<span class="label">PREVIEW</span>
<div class="tabs" role="tablist">
<button type="button" class="tab active" data-tab="404" role="tab" aria-selected="true">404 · не найдено</button>
<button type="button" class="tab" data-tab="403" role="tab" aria-selected="false">403 · нет доступа</button>
<button type="button" class="tab" data-tab="500" role="tab" aria-selected="false">500 · сервис недоступен</button>
</div>
</nav>
<!-- ===== 404 ===== -->
<main id="err-404" class="error-page active" role="main">
<a href="#" class="top-brand">
<span class="mark" aria-hidden="true">
<svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
</span>
<span>Лидерра<span class="dot">.</span></span>
</a>
<div class="err-illust" aria-hidden="true">
<svg width="240" height="120" viewBox="0 0 240 120" fill="none">
<path d="M20 90 Q60 60 120 60 T220 90" stroke="#D9D5CD" stroke-width="1.5" stroke-dasharray="3 4"/>
<circle cx="120" cy="60" r="3" fill="#0F6E56"/>
<path d="M115 30 L120 60 M125 30 L120 60" stroke="#0F6E56" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</div>
<h1 class="err-code">4<span class="accent">0</span>4</h1>
<h2 class="err-title">Страница не найдена</h2>
<p class="err-desc">
Похоже, такой страницы нет — её удалили, переименовали или вы ввели адрес с опечаткой.
Все рабочие экраны Лидерра доступны через дашборд.
</p>
<div class="err-actions">
<a href="/" class="btn btn-primary">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
На дашборд
</a>
<button type="button" class="btn" onclick="history.back()">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
Назад
</button>
</div>
<p class="err-help">Что-то не так? Напишите в <a href="mailto:support@liderra.app">support@liderra.app</a></p>
</main>
<!-- ===== 403 ===== -->
<main id="err-403" class="error-page" role="main">
<a href="#" class="top-brand">
<span class="mark" aria-hidden="true">
<svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
</span>
<span>Лидерра<span class="dot">.</span></span>
</a>
<div class="err-illust" aria-hidden="true">
<svg width="180" height="120" viewBox="0 0 180 120" fill="none">
<rect x="50" y="50" width="80" height="60" rx="6" stroke="#D9D5CD" stroke-width="1.5"/>
<path d="M62 50 L62 36 a28 28 0 0 1 56 0 L118 50" stroke="#6C60C4" stroke-width="2" stroke-linecap="round" fill="none"/>
<circle cx="90" cy="78" r="5" fill="#6C60C4"/>
<line x1="90" y1="83" x2="90" y2="92" stroke="#6C60C4" stroke-width="2.5" stroke-linecap="round"/>
</svg>
</div>
<h1 class="err-code">4<span class="accent">0</span>3</h1>
<h2 class="err-title">У вас нет доступа</h2>
<p class="err-desc">
Эта страница принадлежит другому тенанту, либо ваша роль не позволяет её увидеть.
Если вы считаете, что это ошибка — обратитесь к администратору вашей команды или в поддержку.
</p>
<div class="err-actions">
<a href="/" class="btn btn-primary">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
На дашборд
</a>
<a href="mailto:support@liderra.app" class="btn">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
Написать в поддержку
</a>
</div>
<div class="err-id">
<span>Запрос</span>
<span style="color:var(--ink-2);font-weight:500">REQ-3F8A2-0007</span>
<button type="button" class="copy-btn" aria-label="Скопировать ID запроса">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
</button>
</div>
</main>
<!-- ===== 500 / 503 ===== -->
<main id="err-500" class="error-page" role="main">
<a href="#" class="top-brand">
<span class="mark" aria-hidden="true">
<svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
</span>
<span>Лидерра<span class="dot">.</span></span>
</a>
<div class="err-illust" aria-hidden="true">
<svg width="220" height="120" viewBox="0 0 220 120" fill="none">
<path d="M20 90 L40 60 L60 80 L80 50 L100 70 L120 40" stroke="#D9D5CD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<path d="M120 40 L140 90" stroke="#B94837" stroke-width="2.2" stroke-linecap="round"/>
<circle cx="140" cy="90" r="4" fill="#B94837"/>
<text x="155" y="94" font-family="JetBrains Mono, monospace" font-size="11" fill="#B94837" font-weight="600">503</text>
</svg>
</div>
<h1 class="err-code">5<span class="accent">0</span>0</h1>
<h2 class="err-title">Что-то пошло не так</h2>
<p class="err-desc">
Внутренняя ошибка — мы уже занимаемся. Команда получила уведомление.
Большинство сбоев чинятся за 5–10 минут. Можно вернуться через минуту, или открыть страницу статуса.
</p>
<div class="err-actions">
<button type="button" class="btn btn-primary" onclick="location.reload()">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
Попробовать снова
</button>
<a href="https://status.liderra.app" class="btn">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="3 17 9 11 13 15 21 7"/><polyline points="14 7 21 7 21 14"/></svg>
Статус сервиса
</a>
</div>
<div class="status-list" aria-label="Состояние ключевых интеграций">
<span class="item ok"><span class="dot" aria-hidden="true"></span>API · OK</span>
<span class="item deg"><span class="dot" aria-hidden="true"></span>Telegram · деградация</span>
<span class="item ok"><span class="dot" aria-hidden="true"></span>YooKassa · OK</span>
</div>
<div class="err-id">
<span>Инцидент</span>
<span style="color:var(--ink-2);font-weight:500">INC-2026-0507-0034</span>
<button type="button" class="copy-btn" aria-label="Скопировать ID инцидента">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
</button>
</div>
</main>
<script>
// Tab switching for review preview only
const tabs = Array.from(document.querySelectorAll('.review-bar .tab'));
const pages = Array.from(document.querySelectorAll('.error-page'));
function setTab(id) {
tabs.forEach(t => {
const isA = t.dataset.tab === id;
t.classList.toggle('active', isA);
t.setAttribute('aria-selected', isA ? 'true' : 'false');
});
pages.forEach(p => p.classList.toggle('active', p.id === 'err-' + id));
}
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
</script>
</body>
</html>