Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Same repo: index.json and preset files are served from the same origin as the page
var INDEX_JSON_URL = './index.json';
var X2D_PROGRESS_JSON_URL = './x2d-progress.json';
// Use relative URL so fetch is same-origin (no CORS). Works on GitHub Pages and local.
var RAW_BASE = '';
var THEME_STORAGE_KEY = 'polymaker-preset-theme';
Expand Down Expand Up @@ -31,6 +32,182 @@ function applyTheme(theme) {
}
}

function formatDisplayDate(dateString) {
if (!dateString) return '';

var parts = String(dateString).split('-');
var year = parts[0];
var month = parseInt(parts[1], 10);
var day = parseInt(parts[2], 10);
var monthNames = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];

if (!year || isNaN(month) || isNaN(day) || month < 1 || month > 12) {
return dateString;
}

return monthNames[month - 1] + ' ' + day + ', ' + year;
}

function parseLocalDate(dateString) {
if (!dateString) return null;

var parts = String(dateString).split('-');
var year = parseInt(parts[0], 10);
var month = parseInt(parts[1], 10);
var day = parseInt(parts[2], 10);

if (isNaN(year) || isNaN(month) || isNaN(day)) {
return null;
}

return new Date(year, month - 1, day);
}

function getTimelineLabel(dateString) {
var targetDate = parseLocalDate(dateString);
var now = new Date();
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
var daysLeft;

if (!targetDate) {
return 'Date not set';
}

daysLeft = Math.round((targetDate.getTime() - today.getTime()) / 86400000);

if (daysLeft > 1) {
return daysLeft + ' days left';
}
if (daysLeft === 1) {
return '1 day left';
}
if (daysLeft === 0) {
return 'Due today';
}
if (daysLeft === -1) {
return '1 day overdue';
}
return Math.abs(daysLeft) + ' days overdue';
}

function buildX2DChecklist(items) {
var html = '';
var i;
var item;
var itemClass;
var statusText;

for (i = 0; i < items.length; i++) {
item = items[i] || {};
itemClass = item.completed ? ' is-complete' : ' is-pending';
statusText = item.completed ? 'Done' : 'Pending';

html += '<li class="adaptation-item' + itemClass + '">';
html += '<span class="adaptation-item-name">' + escapeHtml(item.productName || 'Unknown product') + '</span>';
html += '<span class="adaptation-item-status">' + statusText + '</span>';
html += '</li>';
}

return html;
}

function renderX2DProgress(progressData) {
var titleEl = document.getElementById('x2d-progress-title');
var metaEl = document.getElementById('x2d-progress-meta');
var percentEl = document.getElementById('x2d-progress-percentage');
var barEl = document.getElementById('x2d-progress-bar');
var fillEl = document.getElementById('x2d-progress-fill');
var scopeEl = document.getElementById('x2d-progress-scope');
var completeEl = document.getElementById('x2d-progress-complete');
var remainingEl = document.getElementById('x2d-progress-remaining');
var deadlineEl = document.getElementById('x2d-progress-deadline');
var timelineEl = document.getElementById('x2d-progress-timeline');
var listEl = document.getElementById('x2d-progress-list');
var items = progressData && progressData.items ? progressData.items : [];
var completedCount = 0;
var totalCount = items.length;
var remainingCount;
var percent;
var title;
var goal;
var formattedDeadline;
var timelineLabel;
var i;

if (!titleEl || !metaEl || !percentEl || !barEl || !fillEl || !scopeEl || !completeEl || !remainingEl || !deadlineEl || !timelineEl || !listEl) {
return;
}

for (i = 0; i < totalCount; i++) {
if (items[i] && items[i].completed === true) {
completedCount += 1;
}
}

remainingCount = Math.max(totalCount - completedCount, 0);
percent = totalCount ? Math.round((completedCount / totalCount) * 100) : 0;
title = progressData && progressData.title ? progressData.title : 'Bambu X2D Preset Adaptation Progress';
goal = progressData && progressData.goal ? progressData.goal : 'We are planning to finish X2D presets for all active products';
formattedDeadline = formatDisplayDate(progressData && progressData.targetDate);
timelineLabel = getTimelineLabel(progressData && progressData.targetDate);

titleEl.textContent = title;
metaEl.textContent = goal + (formattedDeadline ? ' before ' + formattedDeadline + '.' : '.');
percentEl.textContent = percent + '%';
fillEl.style.width = percent + '%';
barEl.setAttribute('aria-valuenow', String(percent));
scopeEl.textContent = 'View product checklist (' + totalCount + ')';
completeEl.textContent = completedCount + ' / ' + totalCount;
remainingEl.textContent = String(remainingCount);
deadlineEl.textContent = formattedDeadline ? formattedDeadline : 'Date not set';
timelineEl.textContent = timelineLabel;
listEl.innerHTML = buildX2DChecklist(items);
}

function renderX2DProgressError(message) {
var metaEl = document.getElementById('x2d-progress-meta');
var percentEl = document.getElementById('x2d-progress-percentage');
var barEl = document.getElementById('x2d-progress-bar');
var fillEl = document.getElementById('x2d-progress-fill');
var scopeEl = document.getElementById('x2d-progress-scope');
var completeEl = document.getElementById('x2d-progress-complete');
var remainingEl = document.getElementById('x2d-progress-remaining');
var deadlineEl = document.getElementById('x2d-progress-deadline');
var timelineEl = document.getElementById('x2d-progress-timeline');
var listEl = document.getElementById('x2d-progress-list');

if (metaEl) metaEl.textContent = message;
if (percentEl) percentEl.textContent = '0%';
if (fillEl) fillEl.style.width = '0%';
if (barEl) barEl.setAttribute('aria-valuenow', '0');
if (scopeEl) scopeEl.textContent = 'View product checklist (0)';
if (completeEl) completeEl.textContent = '0 / 0';
if (remainingEl) remainingEl.textContent = '0';
if (deadlineEl) deadlineEl.textContent = 'Date not set';
if (timelineEl) timelineEl.textContent = 'Unavailable';
if (listEl) listEl.innerHTML = '';
}

function initX2DProgress() {
fetch(X2D_PROGRESS_JSON_URL)
.then(function (r) {
if (!r.ok) {
throw new Error('Network response was not ok: ' + r.statusText);
}
return r.json();
})
.then(function (data) {
renderX2DProgress(data);
})
.catch(function (err) {
console.warn('Failed to load X2D progress:', err);
renderX2DProgressError('Unable to load adaptation progress right now.');
});
}

function initTheme() {
var initial = 'dark';
try {
Expand Down Expand Up @@ -137,6 +314,7 @@ function init() {
}

initTheme();
initX2DProgress();

// ============================================
// .bbsflmt Bundle Helper Functions
Expand Down
39 changes: 39 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,45 @@ <h1 class="hero-title">
<p class="hero-desc" data-i18n="hero.desc">Select your slicer to view and download Polymaker print profiles and filament presets</p>
</header>

<section class="card adaptation-card" id="x2d-progress-card" aria-live="polite">
<div class="adaptation-header">
<div class="adaptation-copy">
<p class="adaptation-kicker">Adaptation Progress</p>
<h2 class="adaptation-title" id="x2d-progress-title">Bambu X2D Preset Adaptation Progress</h2>
<p class="adaptation-summary" id="x2d-progress-meta">Loading adaptation progress...</p>
</div>
<div class="adaptation-score">
<span class="adaptation-score-value" id="x2d-progress-percentage">0%</span>
<span class="adaptation-score-label">Complete</span>
</div>
</div>
<div class="adaptation-progress-track" id="x2d-progress-bar" role="progressbar" aria-labelledby="x2d-progress-title" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="adaptation-progress-fill" id="x2d-progress-fill"></div>
</div>
<div class="adaptation-metrics">
<div class="adaptation-metric">
<span class="adaptation-metric-label">Completed</span>
<span class="adaptation-metric-value" id="x2d-progress-complete">0 / 0</span>
</div>
<div class="adaptation-metric">
<span class="adaptation-metric-label">Remaining</span>
<span class="adaptation-metric-value" id="x2d-progress-remaining">0</span>
</div>
<div class="adaptation-metric">
<span class="adaptation-metric-label">Target Date</span>
<span class="adaptation-metric-value" id="x2d-progress-deadline">May 10, 2026</span>
</div>
<div class="adaptation-metric">
<span class="adaptation-metric-label">Timeline</span>
<span class="adaptation-metric-value" id="x2d-progress-timeline">0 days left</span>
</div>
</div>
<details class="adaptation-details" id="x2d-progress-details">
<summary id="x2d-progress-scope">View product checklist (0)</summary>
<ul class="adaptation-items" id="x2d-progress-list"></ul>
</details>
</section>

<section class="card slicer-card" id="slicer-card">
<div class="slicer-filter">
<div class="filter-group slicer-filter-group">
Expand Down
Loading
Loading