114 lines
4.6 KiB
JavaScript
114 lines
4.6 KiB
JavaScript
/* ═══════════════════════════════════════════════════════
|
|
CHANGELOG — Mission Update Log Controller
|
|
Groups entries by date into unified cards
|
|
═══════════════════════════════════════════════════════ */
|
|
(function () {
|
|
'use strict';
|
|
|
|
const root = document.getElementById('changelogRoot');
|
|
if (!root) return;
|
|
|
|
function esc(s) {
|
|
const d = document.createElement('div');
|
|
d.textContent = s || '';
|
|
return d.innerHTML;
|
|
}
|
|
|
|
function formatDate(dateStr) {
|
|
const d = new Date(dateStr + 'T00:00:00');
|
|
const day = d.getDate().toString().padStart(2, '0');
|
|
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
|
const year = d.getFullYear();
|
|
return `${day}/${month}/${year}`;
|
|
}
|
|
|
|
function render(data) {
|
|
const entries = data.entries || [];
|
|
const totalChanges = entries.reduce((sum, e) => sum + (e.changes ? e.changes.length : 0), 0);
|
|
const latestVersion = entries.length > 0 ? entries[0].version : '0.0.0';
|
|
|
|
// Group entries by date
|
|
const grouped = [];
|
|
const dateMap = {};
|
|
for (const entry of entries) {
|
|
if (!dateMap[entry.date]) {
|
|
dateMap[entry.date] = { date: entry.date, entries: [] };
|
|
grouped.push(dateMap[entry.date]);
|
|
}
|
|
dateMap[entry.date].entries.push(entry);
|
|
}
|
|
|
|
let html = '';
|
|
|
|
// Header
|
|
html += `<div class="changelog-header">`;
|
|
html += `<div class="changelog-title">MAINTENANCE LOG</div>`;
|
|
html += `<div class="changelog-subtitle">// ALL MODIFICATIONS — LOGGED & VERIFIED</div>`;
|
|
html += `<div class="changelog-stats">`;
|
|
html += `<span class="changelog-stat">CURRENT BUILD: <span class="changelog-stat-value">v${esc(latestVersion)}</span></span>`;
|
|
html += `<span class="changelog-stat">UPDATES: <span class="changelog-stat-value">${entries.length}</span></span>`;
|
|
html += `<span class="changelog-stat">TOTAL CHANGES: <span class="changelog-stat-value">${totalChanges}</span></span>`;
|
|
html += `</div></div>`;
|
|
|
|
// Timeline — grouped by date
|
|
html += `<div class="changelog-timeline">`;
|
|
|
|
for (const group of grouped) {
|
|
// Version range for this date
|
|
const versions = group.entries.map(e => e.version);
|
|
const versionRange = versions.length > 1
|
|
? `v${esc(versions[versions.length - 1])} — v${esc(versions[0])}`
|
|
: `v${esc(versions[0])}`;
|
|
|
|
html += `<div class="changelog-entry">`;
|
|
html += `<div class="changelog-entry-card">`;
|
|
|
|
// Date header
|
|
html += `<div class="changelog-date-header">`;
|
|
html += `<span class="changelog-date">${formatDate(group.date)}</span>`;
|
|
html += `<span class="changelog-version-range">${versionRange}</span>`;
|
|
html += `</div>`;
|
|
|
|
// Each sub-entry within this date
|
|
for (const entry of group.entries) {
|
|
const badge = entry.category || 'update';
|
|
|
|
html += `<div class="changelog-sub-entry">`;
|
|
html += `<div class="changelog-entry-header">`;
|
|
html += `<span class="changelog-version">v${esc(entry.version)}</span>`;
|
|
html += `<span class="changelog-badge ${esc(badge)}">${esc(badge)}</span>`;
|
|
html += `<span class="changelog-entry-title">${esc(entry.title)}</span>`;
|
|
html += `</div>`;
|
|
|
|
if (entry.changes && entry.changes.length > 0) {
|
|
html += `<ul class="changelog-changes">`;
|
|
for (const change of entry.changes) {
|
|
html += `<li>${esc(change)}</li>`;
|
|
}
|
|
html += `</ul>`;
|
|
}
|
|
|
|
html += `</div>`;
|
|
}
|
|
|
|
html += `</div></div>`;
|
|
}
|
|
|
|
html += `</div>`;
|
|
root.innerHTML = html;
|
|
}
|
|
|
|
// Load data
|
|
root.innerHTML = '<div class="changelog-loading">LOADING UPDATE LOG...</div>';
|
|
|
|
fetch('/api/changelog')
|
|
.then(res => {
|
|
if (!res.ok) throw new Error('API ' + res.status);
|
|
return res.json();
|
|
})
|
|
.then(data => render(data))
|
|
.catch(err => {
|
|
root.innerHTML = '<div class="changelog-loading">⚠ FAILED TO LOAD UPDATE LOG</div>';
|
|
console.error('Changelog load error:', err);
|
|
});
|
|
})();
|