generated from coulomb/repo-seed
feat: add debug information panel for data loading transparency
Added comprehensive debug panel that displays: - Field mappings (CSV columns → item properties) - Template placeholders (required template fields) - CSV data preview (headers, sample data, validation warnings) The panel appears below the timeline viewer and helps troubleshoot: - Missing or mismatched CSV columns - Template structure issues - Data format problems Shows warnings when no valid items are found with actionable troubleshooting steps. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
139
engine.js
139
engine.js
@@ -190,6 +190,9 @@ window.timelineEngine = {
|
||||
// Update project status
|
||||
this.updateFileStatus('project', name, 'loaded');
|
||||
|
||||
// Show field mappings in debug panel
|
||||
this.showFieldMappings();
|
||||
|
||||
// Track loading errors for user feedback
|
||||
const loadingErrors = [];
|
||||
|
||||
@@ -235,6 +238,9 @@ window.timelineEngine = {
|
||||
console.log("SVG template loaded, length:", this.template.length);
|
||||
this.updateFileStatus('svg', cfg.svgTemplate, 'loaded');
|
||||
|
||||
// Show template fields in debug panel
|
||||
this.showTemplateFields();
|
||||
|
||||
// Show template preview if no CSV data is loaded yet
|
||||
if (!this.csvOverride && document.querySelector("#viewer").innerHTML.includes("Keine Timeline verfügbar")) {
|
||||
this.showTemplatePreview();
|
||||
@@ -321,6 +327,10 @@ window.timelineEngine = {
|
||||
complete: (res) => {
|
||||
console.log("Papa.parse complete, found", res.data.length, "rows");
|
||||
const rows = res.data;
|
||||
|
||||
// Show CSV preview in debug panel
|
||||
self.showCSVPreview(text, rows);
|
||||
|
||||
const items = rows.map((r) => {
|
||||
const dueField = (m.due || []).find(f => r[f]);
|
||||
const item = {
|
||||
@@ -396,6 +406,132 @@ window.timelineEngine = {
|
||||
// Fallback
|
||||
d = new Date(str);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
},
|
||||
|
||||
// --------- Debug Display Functions ---------
|
||||
|
||||
showFieldMappings() {
|
||||
if (!this.config || !this.config.fieldMapping) return;
|
||||
|
||||
const debugInfo = document.getElementById("debugInfo");
|
||||
const fieldMappingInfo = document.getElementById("fieldMappingInfo");
|
||||
const fieldMappingDisplay = document.getElementById("fieldMappingDisplay");
|
||||
|
||||
if (debugInfo && fieldMappingInfo && fieldMappingDisplay) {
|
||||
debugInfo.style.display = "block";
|
||||
fieldMappingInfo.style.display = "block";
|
||||
|
||||
const mapping = this.config.fieldMapping;
|
||||
let display = "CSV Column → Timeline Field\n";
|
||||
display += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
|
||||
display += `ID: ${JSON.stringify(mapping.id)}\n`;
|
||||
display += `Title: ${JSON.stringify(mapping.title)}\n`;
|
||||
display += `Lane: ${JSON.stringify(mapping.lane)}\n`;
|
||||
display += `Due: ${JSON.stringify(mapping.due)}\n`;
|
||||
if (mapping.epic) display += `Epic: ${JSON.stringify(mapping.epic)}\n`;
|
||||
if (mapping.type) display += `Type: ${JSON.stringify(mapping.type)}\n`;
|
||||
if (mapping.color) display += `Color: ${JSON.stringify(mapping.color)}\n`;
|
||||
|
||||
fieldMappingDisplay.textContent = display;
|
||||
}
|
||||
},
|
||||
|
||||
showTemplateFields() {
|
||||
if (!this.template) return;
|
||||
|
||||
const debugInfo = document.getElementById("debugInfo");
|
||||
const templateFieldsInfo = document.getElementById("templateFieldsInfo");
|
||||
const templateFieldsDisplay = document.getElementById("templateFieldsDisplay");
|
||||
|
||||
if (debugInfo && templateFieldsInfo && templateFieldsDisplay) {
|
||||
debugInfo.style.display = "block";
|
||||
templateFieldsInfo.style.display = "block";
|
||||
|
||||
// Extract placeholders from template
|
||||
const placeholders = new Set();
|
||||
const placeholderRegex = /\{\{([A-Z_]+)\}\}/g;
|
||||
let match;
|
||||
while ((match = placeholderRegex.exec(this.template)) !== null) {
|
||||
placeholders.add(match[1]);
|
||||
}
|
||||
|
||||
let display = "Required Template Placeholders:\n";
|
||||
display += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
|
||||
|
||||
// Group by category
|
||||
const monthFields = Array.from(placeholders).filter(p => p.includes('MONTH'));
|
||||
const laneFields = Array.from(placeholders).filter(p => p.includes('LANE'));
|
||||
const taskFields = Array.from(placeholders).filter(p => p.includes('TASK') || p.includes('TEXT'));
|
||||
const otherFields = Array.from(placeholders).filter(p =>
|
||||
!monthFields.includes(p) && !laneFields.includes(p) && !taskFields.includes(p)
|
||||
);
|
||||
|
||||
if (monthFields.length > 0) {
|
||||
display += "\nMonth Fields:\n";
|
||||
monthFields.forEach(f => display += ` • ${f}\n`);
|
||||
}
|
||||
if (laneFields.length > 0) {
|
||||
display += "\nLane Fields:\n";
|
||||
laneFields.forEach(f => display += ` • ${f}\n`);
|
||||
}
|
||||
if (taskFields.length > 0) {
|
||||
display += "\nTask Fields:\n";
|
||||
taskFields.forEach(f => display += ` • ${f}\n`);
|
||||
}
|
||||
if (otherFields.length > 0) {
|
||||
display += "\nOther Fields:\n";
|
||||
otherFields.forEach(f => display += ` • ${f}\n`);
|
||||
}
|
||||
|
||||
templateFieldsDisplay.textContent = display;
|
||||
}
|
||||
},
|
||||
|
||||
showCSVPreview(csvText, parsedData) {
|
||||
const debugInfo = document.getElementById("debugInfo");
|
||||
const csvDataInfo = document.getElementById("csvDataInfo");
|
||||
const csvDataDisplay = document.getElementById("csvDataDisplay");
|
||||
|
||||
if (debugInfo && csvDataInfo && csvDataDisplay) {
|
||||
debugInfo.style.display = "block";
|
||||
csvDataInfo.style.display = "block";
|
||||
|
||||
const lines = csvText.trim().split('\n');
|
||||
const headers = lines[0] ? lines[0].split(',') : [];
|
||||
const firstDataLine = lines[1] ? lines[1].split(',') : [];
|
||||
|
||||
let display = "CSV Structure Preview:\n";
|
||||
display += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
|
||||
display += `Headers: ${headers.join(', ')}\n\n`;
|
||||
|
||||
if (firstDataLine.length > 0) {
|
||||
display += "First Data Row:\n";
|
||||
headers.forEach((header, i) => {
|
||||
display += ` ${header}: "${firstDataLine[i] || ''}"\n`;
|
||||
});
|
||||
}
|
||||
|
||||
if (parsedData) {
|
||||
display += `\nParsed Rows: ${parsedData.length}\n`;
|
||||
const validCount = parsedData.filter(r => {
|
||||
const mapping = this.config?.fieldMapping || {};
|
||||
const titleField = mapping.title;
|
||||
const dueField = Array.isArray(mapping.due) ? mapping.due.find(f => r[f]) : mapping.due;
|
||||
return r[titleField] && r[dueField];
|
||||
}).length;
|
||||
display += `Valid Items: ${validCount}\n`;
|
||||
|
||||
if (validCount === 0 && parsedData.length > 0) {
|
||||
display += "\n⚠️ WARNING: No valid items found!\n";
|
||||
display += "Check that:\n";
|
||||
display += " • CSV headers match field mappings\n";
|
||||
display += " • Title and Due fields have values\n";
|
||||
display += " • Date format is parseable (e.g., YYYY-MM-DD)\n";
|
||||
}
|
||||
}
|
||||
|
||||
csvDataDisplay.textContent = display;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -610,6 +746,9 @@ window.setupEventHandlers = function() {
|
||||
window.timelineEngine.template = await file.text();
|
||||
window.timelineEngine.updateFileStatus('svg', file.name, 'loaded');
|
||||
|
||||
// Show template fields in debug panel
|
||||
window.timelineEngine.showTemplateFields();
|
||||
|
||||
// Show template preview immediately when manually loaded
|
||||
window.timelineEngine.showTemplatePreview();
|
||||
});
|
||||
|
||||
23
index.html
23
index.html
@@ -325,6 +325,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Debug Information Panel -->
|
||||
<div id="debugInfo" style="margin-top:16px; padding:16px; background:#f8f9fa; border:1px solid #e9ecef; border-radius:8px; display:none;">
|
||||
<h3 style="margin:0 0 12px 0; font-size:14px; font-weight:600; color:#495057;">🔍 Debug Information</h3>
|
||||
|
||||
<!-- Field Mappings -->
|
||||
<div id="fieldMappingInfo" style="display:none; margin-bottom:12px;">
|
||||
<h4 style="margin:0 0 8px 0; font-size:13px; color:#6c757d; font-weight:600;">📋 Configured Field Mappings</h4>
|
||||
<pre id="fieldMappingDisplay" style="background:#fff; padding:12px; border-radius:4px; border:1px solid #dee2e6; font-size:12px; margin:0; overflow-x:auto; font-family:'Courier New', monospace;"></pre>
|
||||
</div>
|
||||
|
||||
<!-- Template Fields -->
|
||||
<div id="templateFieldsInfo" style="display:none; margin-bottom:12px;">
|
||||
<h4 style="margin:0 0 8px 0; font-size:13px; color:#6c757d; font-weight:600;">🖼️ Template Placeholders</h4>
|
||||
<pre id="templateFieldsDisplay" style="background:#fff; padding:12px; border-radius:4px; border:1px solid #dee2e6; font-size:12px; margin:0; overflow-x:auto; font-family:'Courier New', monospace;"></pre>
|
||||
</div>
|
||||
|
||||
<!-- CSV Data Preview -->
|
||||
<div id="csvDataInfo" style="display:none;">
|
||||
<h4 style="margin:0 0 8px 0; font-size:13px; color:#6c757d; font-weight:600;">📊 CSV Data Preview</h4>
|
||||
<pre id="csvDataDisplay" style="background:#fff; padding:12px; border-radius:4px; border:1px solid #dee2e6; font-size:12px; margin:0; overflow-x:auto; font-family:'Courier New', monospace;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Setup event handlers first
|
||||
|
||||
Reference in New Issue
Block a user