Dynamic content: landing and dashboard load texts from DB via API
- Landing hero texts (headline, description, badge) from settings table - Landing categories from /api/tools (dynamic from DB) - Landing advantages from /api/content/advantages - Dashboard about block from /api/content/dashboard - Static HTML remains as fallback if API fails - All editable via AdminJS admin panel
This commit is contained in:
parent
1837f42a91
commit
e8aef7aa28
7
.adminjs/bundle.js
Normal file
7
.adminjs/bundle.js
Normal file
@ -0,0 +1,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
AdminJS.UserComponents = {};
|
||||
|
||||
})();
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlcyI6WyJlbnRyeS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJBZG1pbkpTLlVzZXJDb21wb25lbnRzID0ge31cbiJdLCJuYW1lcyI6WyJBZG1pbkpTIiwiVXNlckNvbXBvbmVudHMiXSwibWFwcGluZ3MiOiI7OztDQUFBQSxPQUFPLENBQUNDLGNBQWMsR0FBRyxFQUFFOzs7Ozs7In0=
|
||||
1
.adminjs/entry.js
Normal file
1
.adminjs/entry.js
Normal file
@ -0,0 +1 @@
|
||||
AdminJS.UserComponents = {}
|
||||
@ -237,6 +237,17 @@ fetch('/auth/me').then(r => r.ok ? r.json() : null).then(user => {
|
||||
const hu = document.getElementById('headerUser');
|
||||
if (hu) hu.textContent = name;
|
||||
}).catch(() => {});
|
||||
|
||||
// Load about section from DB
|
||||
fetch('/api/content/dashboard').then(r => r.ok ? r.json() : []).then(blocks => {
|
||||
const about = blocks.find(b => b.block_key === 'about');
|
||||
if (about) {
|
||||
const h = document.querySelector('.about-card h2');
|
||||
const p = document.querySelector('.about-card p');
|
||||
if (h) h.textContent = about.title;
|
||||
if (p) p.innerHTML = '<strong style="color:var(--text-primary);">WA Dev Tools</strong> — ' + about.body;
|
||||
}
|
||||
}).catch(() => {});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -612,19 +612,19 @@
|
||||
<div style="position:relative;z-index:1;width:100%;">
|
||||
<div class="hero-badge animate-fade-in">
|
||||
<span class="dot"></span>
|
||||
<span>13 инструментов — бесплатно навсегда</span>
|
||||
<span id="hero-badge-text">13 инструментов — бесплатно навсегда</span>
|
||||
</div>
|
||||
|
||||
<div class="hero-logo animate-slide-up delay-100">
|
||||
WA Dev <span>Tools</span>
|
||||
</div>
|
||||
|
||||
<h1 class="hero-headline animate-slide-up delay-200">
|
||||
<h1 class="hero-headline animate-slide-up delay-200" id="hero-headline">
|
||||
Инструменты для<br>
|
||||
<span class="accent">веб-разработчика</span>
|
||||
</h1>
|
||||
|
||||
<p class="hero-sub animate-slide-up delay-300">
|
||||
<p class="hero-sub animate-slide-up delay-300" id="hero-sub">
|
||||
Сжатие картинок, форматирование кода, HTTP-клиент и ещё 10 инструментов —
|
||||
всё в браузере, бесплатно и без регистрации карты.
|
||||
</p>
|
||||
@ -901,3 +901,61 @@
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
/* ── Dynamic content from API ── */
|
||||
(async function() {
|
||||
try {
|
||||
const [settingsRes, toolsRes, advRes] = await Promise.all([
|
||||
fetch('/api/settings'),
|
||||
fetch('/api/tools'),
|
||||
fetch('/api/content/advantages'),
|
||||
]);
|
||||
|
||||
if (settingsRes.ok) {
|
||||
const s = await settingsRes.json();
|
||||
const headline = document.getElementById('hero-headline');
|
||||
if (headline && s.hero_headline) {
|
||||
const parts = s.hero_headline.split(' ');
|
||||
const last = parts.pop();
|
||||
headline.innerHTML = parts.join(' ') + '<br><span class="accent">' + last + '</span>';
|
||||
}
|
||||
const sub = document.getElementById('hero-sub');
|
||||
if (sub && s.hero_description) sub.textContent = s.hero_description;
|
||||
|
||||
const badge = document.getElementById('hero-badge-text');
|
||||
if (badge && s.site_tagline) badge.textContent = s.site_tagline;
|
||||
}
|
||||
|
||||
if (toolsRes.ok) {
|
||||
const cats = await toolsRes.json();
|
||||
const grid = document.querySelector('.cat-grid');
|
||||
if (grid && cats.length) {
|
||||
grid.innerHTML = cats.map((cat, i) => {
|
||||
const toolsList = cat.tools.map(t => '<li class="cat-tool-item">' + t.title + '</li>').join('');
|
||||
return '<div class="cat-card reveal visible reveal-delay-' + (i+1) + '">' +
|
||||
'<div class="cat-icon-wrap" style="color:' + (cat.color || '#0054e6') + '">' +
|
||||
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="3" y="3" width="18" height="18" rx="3"/></svg>' +
|
||||
'</div><div><div class="cat-header"><span class="cat-title">' + cat.title + '</span>' +
|
||||
'<span class="cat-badge">' + cat.tools.length + ' инстр.</span></div></div>' +
|
||||
'<p class="cat-desc">' + (cat.description || '') + '</p>' +
|
||||
'<ul class="cat-tools">' + toolsList + '</ul></div>';
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
|
||||
if (advRes.ok) {
|
||||
const blocks = await advRes.json();
|
||||
const advGrid = document.querySelector('.adv-grid');
|
||||
if (advGrid && blocks.length) {
|
||||
advGrid.innerHTML = blocks.map((b, i) =>
|
||||
'<div class="adv-card reveal visible reveal-delay-' + i + '">' +
|
||||
'<div class="adv-icon-wrap"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z"/></svg></div>' +
|
||||
'<div class="adv-title">' + b.title + '</div>' +
|
||||
'<p class="adv-desc">' + b.body + '</p></div>'
|
||||
).join('');
|
||||
}
|
||||
}
|
||||
} catch(e) { /* silent — static content stays as fallback */ }
|
||||
})();
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user