wa-dev-tools/public/dashboard.html

189 lines
14 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.0" />
<title>WA Dev Tools — Панель</title>
<link href="/vendor/tailwind.min.css" rel="stylesheet">
<script>window.WA_TOOL_ID = 'home';</script>
<script src="/shared.js?v=3"></script>
<link href="/vendor/fonts.css" rel="stylesheet">
<link href="/shared.css" rel="stylesheet">
<style>
.dash-header { position:fixed; top:0; left:56px; right:0; height:52px; background:var(--surface-800); border-bottom:1px solid var(--surface-600); display:flex; align-items:center; padding:0 24px; z-index:40; backdrop-filter:blur(12px); }
.dash-logo { font-size:16px; font-weight:800; color:var(--text-primary); text-decoration:none; display:flex; align-items:center; gap:8px; flex-shrink:0; }
.dash-logo span { color:var(--accent); }
.dash-right { margin-left:auto; display:flex; align-items:center; gap:12px; }
.dash-user { font-size:13px; color:var(--text-muted); font-family:'JetBrains Mono',monospace; }
.dash-logout { color:var(--text-muted); transition:color .15s; }
.dash-logout:hover { color:#ff5252; }
@media(max-width:768px) { .dash-header { left:0; } .dash-logo svg { display:none; } }
@keyframes fadeUp { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:translateY(0); } }
.fade-up { animation: fadeUp .4s ease-out forwards; }
.tool-tile { display:flex; flex-direction:column; gap:10px; padding:18px; border-radius:14px; border:1px solid var(--surface-600); background:var(--surface-800); color:inherit; text-decoration:none; transition:all .2s; }
.tool-tile:hover { transform:translateY(-2px); border-color:rgba(0,84,230,.3); box-shadow:0 8px 24px rgba(0,0,0,.1); background:var(--surface-700); }
.tile-icon { width:40px; height:40px; border-radius:10px; display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .2s; }
.tool-tile:hover .tile-icon { filter:brightness(1.2); }
.tile-title { font-size:14px; font-weight:600; color:var(--text-primary); }
.tile-desc { font-size:11px; color:var(--text-muted); font-family:'JetBrains Mono',monospace; line-height:1.4; }
.cat-header { display:flex; align-items:center; gap:10px; margin-bottom:14px; margin-top:32px; }
.cat-header:first-child { margin-top:0; }
.cat-icon { width:32px; height:32px; border-radius:8px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.cat-title { font-size:16px; font-weight:700; color:var(--text-primary); }
.cat-count { font-size:11px; color:var(--text-muted); font-family:'JetBrains Mono',monospace; background:var(--surface-600); padding:2px 8px; border-radius:6px; }
.cat-desc { font-size:12px; color:var(--text-muted); margin-left:auto; font-family:'JetBrains Mono',monospace; }
@media(max-width:640px) { .cat-desc { display:none; } }
</style>
</head>
<body class="font-sans antialiased">
<header class="dash-header">
<a href="/dashboard" class="dash-logo">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"/></svg>
WA Dev <span>Tools</span>
</a>
<div class="dash-right">
<span class="dash-user" id="headerUser"></span>
<a href="/auth/logout" class="dash-logout" title="Выйти">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9"/></svg>
</a>
</div>
</header>
<div class="main-content">
<div class="tool-container relative z-10 max-w-5xl mx-auto" style="padding-top:72px;">
<div class="fade-up" style="margin-bottom:28px;">
<h1 style="font-size:1.75rem;font-weight:800;color:var(--text-primary);letter-spacing:-0.025em;" id="greeting">Добро пожаловать</h1>
<p id="toolsCount" style="font-size:14px;color:var(--text-secondary);margin-top:8px;line-height:1.6;max-width:600px;">
Инструменты для веб-разработки: сжатие картинок и видео, форматирование кода, тестирование API и многое другое.
</p>
</div>
<div id="toolsGrid"></div>
</div>
</div>
<script>
// Category icons (SVG paths)
const CAT_ICONS = {
images: '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0021 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/>',
code: '<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"/>',
web: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"/>',
utils: '<path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17l-5.645 3.232a.75.75 0 01-1.09-.791l1.078-6.285L1.347 7.06a.75.75 0 01.416-1.28l6.31-.917L10.89.598a.75.75 0 011.341 0l2.816 5.266 6.31.917a.75.75 0 01.416 1.28l-4.416 4.266 1.078 6.285a.75.75 0 01-1.09.791L12 15.17z"/>',
};
// Tool icons — match sidebar icons from shared.js
const TOOL_ICONS = {
compress: '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0021 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/>',
placeholder: '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 01-1.59.659H9.06a2.25 2.25 0 01-1.591-.659L5 14.5m14 0V17a2.25 2.25 0 01-2.25 2.25H7.25A2.25 2.25 0 015 17v-2.5"/>',
svgeditor: '<path stroke-linecap="round" stroke-linejoin="round" d="M9.53 16.122a3 3 0 00-5.78 1.128 2.25 2.25 0 01-2.4 2.245 4.5 4.5 0 008.4-2.245c0-.399-.078-.78-.22-1.128zm0 0a15.998 15.998 0 003.388-1.62m-5.043-.025a15.994 15.994 0 011.622-3.395m3.42 3.42a15.995 15.995 0 004.764-4.648l3.876-5.814a1.151 1.151 0 00-1.597-1.597L14.146 6.32a15.996 15.996 0 00-4.649 4.763m3.42 3.42a6.776 6.776 0 00-3.42-3.42"/>',
editor: '<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"/>',
video: '<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z"/>',
favicon: '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6z"/>',
formatter: '<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"/>',
sanitizer: '<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z"/>',
converter: '<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"/>',
regex: '<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"/>',
parser: '<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m9.86-2.06a4.5 4.5 0 00-6.364-6.364L6.257 6.514a4.5 4.5 0 001.242 7.244"/>',
httpclient: '<path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"/>',
redirects: '<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"/>',
password: '<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"/>',
md: '<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/>',
pdf: '<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/>',
};
function svgIcon(pathHtml, size) {
return `<svg width="${size||20}" height="${size||20}" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">${pathHtml}</svg>`;
}
function esc(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
// Load tools from API
fetch('/api/tools')
.then(r => r.json())
.then(categories => {
const grid = document.getElementById('toolsGrid');
let totalTools = 0;
const delays = ['', 'fade-up', 'fade-up', 'fade-up', 'fade-up'];
let catIdx = 0;
categories.forEach(cat => {
if (!cat.tools || !cat.tools.length) return;
catIdx++;
totalTools += cat.tools.length;
const color = cat.color || '#0054e6';
const bgColor = color + '1a'; // 10% opacity hex
// Category header
const header = document.createElement('div');
header.className = 'cat-header fade-up';
header.style.animationDelay = (catIdx * 0.05) + 's';
header.innerHTML = `
<div class="cat-icon" style="background:${bgColor};color:${color}">
${svgIcon(CAT_ICONS[cat.slug] || CAT_ICONS.utils, 18)}
</div>
<span class="cat-title">${esc(cat.title)}</span>
<span class="cat-count">${cat.tools.length}</span>
<span class="cat-desc">${esc(cat.description || '')}</span>
`;
grid.appendChild(header);
// Tools grid
const tilesWrap = document.createElement('div');
tilesWrap.className = 'grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 fade-up';
tilesWrap.style.animationDelay = (catIdx * 0.05) + 's';
cat.tools.forEach(tool => {
const iconPath = TOOL_ICONS[tool.slug] || TOOL_ICONS.compress;
const a = document.createElement('a');
a.href = tool.path;
a.className = 'tool-tile';
a.innerHTML = `
<div class="tile-icon" style="background:${bgColor};color:${color}">${svgIcon(iconPath)}</div>
<div>
<div class="tile-title">${esc(tool.title)}</div>
<div class="tile-desc">${esc(tool.description || '')}</div>
</div>
`;
tilesWrap.appendChild(a);
});
grid.appendChild(tilesWrap);
});
// Update count
document.getElementById('toolsCount').textContent =
totalTools + ' инструментов для веб-разработки: сжатие картинок и видео, форматирование кода, тестирование API и многое другое.';
})
.catch(() => {
document.getElementById('toolsGrid').innerHTML = '<p style="color:var(--text-muted)">Не удалось загрузить инструменты</p>';
});
// User greeting
fetch('/auth/me').then(r => r.ok ? r.json() : null).then(user => {
if (!user) return;
const name = user.name || user.email.split('@')[0];
document.getElementById('greeting').textContent = 'Привет, ' + name;
document.getElementById('headerUser').textContent = name;
}).catch(() => {});
</script>
<!-- Yandex.Metrika counter -->
<script type="text/javascript">
(function(m,e,t,r,i,k,a){
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=108185982', 'ym');
ym(108185982, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/108185982" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
</body>
</html>