update last version of the website
This commit is contained in:
commit
abc54697fd
131 changed files with 11253 additions and 0 deletions
176
assets/js/pages/services.js
Normal file
176
assets/js/pages/services.js
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/**
|
||||
* OmégaKube — Services Tabs Renderer
|
||||
* Charge assets/data/services.json et construit dynamiquement
|
||||
* les onglets de navigation et leurs contenus.
|
||||
*
|
||||
* Dépend de main.js et services-process.js (déjà chargés avant ce script).
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* ── Helpers ─────────────────────────────────────────────── */
|
||||
|
||||
function stampHtml(stamp, extraClass) {
|
||||
if (!stamp) return '';
|
||||
const animated = stamp.animated ? ' service-stamp-animated' : '';
|
||||
const extra = extraClass ? ' ' + extraClass : '';
|
||||
return `<span class="service-stamp service-stamp-${window.escapeHTML(stamp.type)}${animated}${extra}">${window.escapeHTML(stamp.text)}</span>`;
|
||||
}
|
||||
|
||||
function particlesHtml() {
|
||||
return `<div class="service-visual-particles">${'<div class="particle"></div>'.repeat(6)}</div>`;
|
||||
}
|
||||
|
||||
/* ── Rendu ───────────────────────────────────────────────── */
|
||||
|
||||
function renderNavBtn(service, isFirst) {
|
||||
return `
|
||||
<button class="service-tab-btn${isFirst ? ' active' : ''}" data-tab="${window.escapeHTML(service.id)}">
|
||||
<i data-feather="${window.escapeHTML(service.nav.icon)}"></i>
|
||||
<span>${window.escapeHTML(service.nav.label)}</span>
|
||||
${stampHtml(service.nav.stamp)}
|
||||
</button>`;
|
||||
}
|
||||
|
||||
function renderVisual(service) {
|
||||
const { accentFrom, accentTo, stamp } = service.visual;
|
||||
const safeFrom = window.escapeHTML(accentFrom);
|
||||
const safeTo = window.escapeHTML(accentTo);
|
||||
return `
|
||||
<div class="service-visual" style="--accent-primary:${safeFrom};--accent-secondary:${safeTo};">
|
||||
${stampHtml(stamp, 'service-visual-stamp')}
|
||||
${particlesHtml()}
|
||||
<div class="service-visual-icon">
|
||||
<div class="icon-main" style="background:linear-gradient(135deg,${safeFrom},${safeTo});">
|
||||
<i data-feather="${window.escapeHTML(service.nav.icon)}"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderHighlights(highlights) {
|
||||
return highlights.map(h => `
|
||||
<div class="service-highlight">
|
||||
<div class="service-highlight-value">${window.escapeHTML(h.value)}</div>
|
||||
<div class="service-highlight-label">${window.escapeHTML(h.label)}</div>
|
||||
</div>`).join('');
|
||||
}
|
||||
|
||||
function renderFeatures(features, accentFrom, accentTo) {
|
||||
const safeFrom = window.escapeHTML(accentFrom);
|
||||
const safeTo = window.escapeHTML(accentTo);
|
||||
return features.map(f => `
|
||||
<div class="service-feature-item">
|
||||
<div class="service-feature-icon" style="background:linear-gradient(135deg,${safeFrom},${safeTo});">
|
||||
<i data-feather="${window.escapeHTML(f.icon)}"></i>
|
||||
</div>
|
||||
<div class="service-feature-text">
|
||||
<h4>${window.escapeHTML(f.title)}</h4>
|
||||
<p>${window.escapeHTML(f.desc)}</p>
|
||||
</div>
|
||||
</div>`).join('');
|
||||
}
|
||||
|
||||
function renderNote(note) {
|
||||
if (!note || !note.length) return '';
|
||||
return `<p class="service-note">${note.map(n => `<span class="service-note-mark">${window.escapeHTML(n.value)}</span> ${window.escapeHTML(n.label)}<br>`).join(' ')}</p>`;
|
||||
}
|
||||
|
||||
function renderCtas(ctas) {
|
||||
return ctas.map(c => `
|
||||
<a href="${window.escapeHTML(c.href)}" class="btn btn-${window.escapeHTML(c.style)}">
|
||||
<i data-feather="${window.escapeHTML(c.icon)}"></i>
|
||||
${window.escapeHTML(c.label)}
|
||||
</a>`).join('');
|
||||
}
|
||||
|
||||
function renderTabContent(service, isFirst) {
|
||||
const { accentFrom, accentTo } = service.visual;
|
||||
const titleStamp = service.titleStamp
|
||||
? ' ' + stampHtml({ ...service.titleStamp }, 'service-stamp-inline')
|
||||
: '';
|
||||
|
||||
return `
|
||||
<div class="service-tab-content${isFirst ? ' active' : ''}" id="tab-${window.escapeHTML(service.id)}">
|
||||
<div class="service-tab-grid">
|
||||
${renderVisual(service)}
|
||||
<div class="service-tab-details">
|
||||
<div class="service-tab-header">
|
||||
<h2>${window.escapeHTML(service.title)}${titleStamp}</h2>
|
||||
<p>${window.escapeHTML(service.description)}</p>
|
||||
</div>
|
||||
<div class="service-highlights">
|
||||
${renderHighlights(service.highlights)}
|
||||
</div>
|
||||
<div class="service-tab-features">
|
||||
${renderFeatures(service.features, accentFrom, accentTo)}
|
||||
</div>
|
||||
<div class="service-tab-note">
|
||||
${renderNote(service.note)}
|
||||
</div>
|
||||
<div class="service-tab-cta">
|
||||
${renderCtas(service.cta)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/* ── Logique des onglets ─────────────────────────────────── */
|
||||
|
||||
function initTabs() {
|
||||
const tabBtns = document.querySelectorAll('.service-tab-btn');
|
||||
const tabContents = document.querySelectorAll('.service-tab-content');
|
||||
if (!tabBtns.length || !tabContents.length) return;
|
||||
|
||||
tabBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const tabId = btn.dataset.tab;
|
||||
|
||||
tabBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
|
||||
tabContents.forEach(content => {
|
||||
content.classList.remove('active');
|
||||
if (content.id === 'tab-' + tabId) content.classList.add('active');
|
||||
});
|
||||
|
||||
if (window.feather) window.feather.replace();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Init ────────────────────────────────────────────────── */
|
||||
|
||||
async function init() {
|
||||
const navContainer = document.getElementById('service-tabs-nav');
|
||||
const contentContainer = document.getElementById('service-tabs-contents');
|
||||
if (!navContainer || !contentContainer) return;
|
||||
|
||||
let services;
|
||||
try {
|
||||
const res = await fetch('../assets/data/services.json');
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
services = await res.json();
|
||||
} catch (err) {
|
||||
console.error('[services-tabs.js] Impossible de charger services.json :', err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(services) || services.length === 0) {
|
||||
console.warn('[services-tabs.js] services.json est vide ou mal formé.');
|
||||
return;
|
||||
}
|
||||
|
||||
navContainer.innerHTML = services.map((s, i) => renderNavBtn(s, i === 0)).join('');
|
||||
contentContainer.innerHTML = services.map((s, i) => renderTabContent(s, i === 0)).join('');
|
||||
|
||||
if (window.feather) window.feather.replace();
|
||||
|
||||
initTabs();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue