smlflg's picture
Initial public upload from Projekte/CloudCodeUsageAnalysis
a873849 verified
Raw
History Blame Contribute Delete
9.74 kB
const formatHours = (value) => `${Number(value).toFixed(1)} h`;
const create = (tag, className, text) => {
const node = document.createElement(tag);
if (className) node.className = className;
if (text !== undefined) node.textContent = text;
return node;
};
const load = async () => {
const response = await fetch("./data/analysis.json");
if (!response.ok) throw new Error("analysis.json konnte nicht geladen werden");
return response.json();
};
const renderHero = (data) => {
document.querySelector("#headline").textContent = data.executive_summary.headline;
const panel = document.querySelector("#heroPanel");
const stats = [
["Best Estimate", formatHours(data.work_estimate.best_estimate_hours)],
["Aktive Projekte", String(data.work_estimate.active_project_count)],
["Top Empfehlung", data.rankings.top_recommendation.name],
["Analysefenster", `${data.meta.window_start} bis ${data.meta.window_end}`],
];
stats.forEach(([label, value]) => {
const row = create("div", "summary-stat");
row.append(create("span", "", label));
row.append(create("strong", "", value));
panel.append(row);
});
};
const renderFindings = (data) => {
const host = document.querySelector("#findingCards");
data.executive_summary.key_findings.forEach((finding, index) => {
const card = create("article", "card");
card.append(create("div", "label", `Antwort ${index + 1}`));
card.append(create("p", "supporting", finding));
host.append(card);
});
};
const renderWork = (data) => {
const host = document.querySelector("#workCards");
const cards = [
["Best Estimate", formatHours(data.work_estimate.best_estimate_hours), "Zentralwert aus Session-Bloecken, Commit-Spans und Artefakt-Bonus."],
["Untergrenze", formatHours(data.work_estimate.lower_bound_hours), "Konservative Sicht: nur ein Teil der Session-Zeit zaehlt als aktive Build-Zeit."],
["Obergrenze", formatHours(data.work_estimate.upper_bound_hours), "Erweitert um stille Vor-/Nacharbeit, verworfene Versuche und uncommitted Arbeit."],
];
cards.forEach(([label, metric, support]) => {
const card = create("article", "card");
card.append(create("div", "label", label));
card.append(create("p", "metric", metric));
card.append(create("p", "supporting", support));
host.append(card);
});
const methodList = document.querySelector("#methodList");
data.work_estimate.method.forEach((line) => methodList.append(create("li", "", line)));
const uncertaintyList = document.querySelector("#uncertaintyList");
data.work_estimate.uncertainties.forEach((line) => uncertaintyList.append(create("li", "", line)));
};
const renderWorkflow = (data) => {
const workflow = data.workflow_patterns;
const narrative = document.querySelector("#workflowNarrative");
[workflow.startup_pattern, workflow.iteration_pattern, workflow.parallel_vs_serial.evidence].forEach((line) => {
narrative.append(create("p", "", line));
});
const total = workflow.prototyping_vs_refinement.prototype_signals + workflow.prototyping_vs_refinement.refinement_signals;
const protoPct = total ? (workflow.prototyping_vs_refinement.prototype_signals / total) * 100 : 0;
const refinePct = 100 - protoPct;
const chart = document.querySelector("#ratioChart");
[
["Prototyping", protoPct, "proto"],
["Refinement", refinePct, "refine"],
].forEach(([label, pct, klass]) => {
const row = create("div", "mini-row");
row.append(create("div", "", label));
const track = create("div", "track");
const fill = create("span");
fill.className = klass;
fill.style.width = `${pct}%`;
track.append(fill);
row.append(track);
row.append(create("div", "muted", `${pct.toFixed(0)}%`));
chart.append(row);
});
document.querySelector("#ratioNote").textContent = workflow.prototyping_vs_refinement.interpretation;
const cards = [
["Seriell vs. Parallel", workflow.parallel_vs_serial.label],
["Eval-Loops", workflow.evaluation_loops.interpretation],
["Bevorzugte Projektarten", workflow.preferred_project_types.join(" • ")],
["Meta-Systeme", workflow.meta_systems.join(" • ")],
];
const host = document.querySelector("#workflowCards");
cards.forEach(([label, support]) => {
const card = create("article", "card");
card.append(create("div", "label", label));
card.append(create("p", "supporting", support));
host.append(card);
});
};
const renderClusters = (data) => {
const grid = document.querySelector("#clusterGrid");
data.project_clusters.forEach((cluster) => {
const card = create("article", "cluster-card");
card.append(create("div", "label", `${cluster.count} Projekte`));
card.append(create("h3", "", cluster.name));
card.append(create("p", "supporting", cluster.summary));
const pills = create("div", "pill-row");
cluster.projects.forEach((project) => {
pills.append(create("span", "pill", `${project.name} · ${project.commit_count}`));
});
card.append(pills);
grid.append(card);
});
const tableHost = document.querySelector("#projectTable");
const table = create("table");
table.innerHTML = `
<thead>
<tr>
<th>Projekt</th>
<th>Cluster</th>
<th>Commits</th>
<th>Sessions</th>
<th>Status</th>
</tr>
</thead>
<tbody></tbody>
`;
const tbody = table.querySelector("tbody");
data.projects.slice(0, 12).forEach((project) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${project.name}</td>
<td>${project.cluster}</td>
<td>${project.commit_count}</td>
<td>${project.session_count}</td>
<td>${project.status}</td>
`;
tbody.append(row);
});
tableHost.append(table);
};
const renderTimeline = (data) => {
const host = document.querySelector("#timeline");
const grid = create("div", "timeline-grid");
data.timeline.forEach((day) => {
const cell = create("article", `day-cell level-${day.intensity}`);
const date = new Date(day.date);
const label = date.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit" });
const header = create("div", "day-head");
header.append(create("span", "", label));
header.append(create("span", "", `${day.projects.length} Projekte`));
cell.append(header);
const projects = create("div", "day-projects");
if (day.projects.length === 0) {
projects.append(create("span", "muted", "ruhiger Tag"));
} else {
day.projects.slice(0, 3).forEach((project) => {
projects.append(create("span", "", `${project.name} · ${project.session_count} Session`));
});
}
cell.append(projects);
grid.append(cell);
});
host.append(grid);
};
const renderRanking = (data) => {
const top = data.rankings.top_recommendation;
const main = document.querySelector("#topRecommendation");
main.append(create("div", "badge", "Top Empfehlung"));
main.append(create("h3", "", top.name));
main.append(create("p", "metric", `${top.score.toFixed(2)} / 5`));
main.append(create("p", "supporting", top.summary));
const stack = create("div", "mini-stack");
Object.entries(top.scores).forEach(([label, value]) => {
const row = create("div", "mini-row");
row.append(create("div", "", label.replace("_", " ")));
const track = create("div", "track");
const fill = create("span");
fill.style.width = `${(value / 5) * 100}%`;
track.append(fill);
row.append(track);
row.append(create("div", "muted", value.toFixed(1)));
stack.append(row);
});
main.append(stack);
const topThree = document.querySelector("#topThree");
data.rankings.top_three.forEach((project, index) => {
const item = create("div", "rank-item");
item.innerHTML = `<strong>#${index + 1} ${project.name}</strong><p class="supporting">${project.summary}</p>`;
topThree.append(item);
});
const rankingTable = document.querySelector("#rankingTable");
const table = create("table");
table.innerHTML = `
<thead>
<tr>
<th>Projekt</th>
<th>Score</th>
<th>Commits</th>
<th>Kernaussage</th>
</tr>
</thead>
<tbody></tbody>
`;
const tbody = table.querySelector("tbody");
data.rankings.shortlist.forEach((project) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${project.name}</td>
<td>${project.score.toFixed(2)}</td>
<td>${project.commit_count}</td>
<td>${project.summary}</td>
`;
tbody.append(row);
});
rankingTable.append(table);
};
const renderEvidence = (data) => {
const host = document.querySelector("#evidenceGrid");
data.evidence.forEach((entry) => {
const card = create("article", "evidence-card");
card.append(create("div", "label", entry.project));
card.append(create("p", "supporting", `${entry.commits} Commits im Fenster · letzter Commit ${entry.last_commit ?? "n/a"}`));
const pills = create("div", "pill-row");
Object.entries(entry.artifacts).forEach(([key, value]) => pills.append(create("span", "pill", `${key}: ${value}`)));
card.append(pills);
const list = create("ul", "bullet-list");
entry.sample_messages.forEach((message) => list.append(create("li", "", message)));
card.append(list);
host.append(card);
});
};
load()
.then((data) => {
renderHero(data);
renderFindings(data);
renderWork(data);
renderWorkflow(data);
renderClusters(data);
renderTimeline(data);
renderRanking(data);
renderEvidence(data);
})
.catch((error) => {
document.body.innerHTML = `<main class="page-shell"><article class="panel"><h1>Ladefehler</h1><p>${error.message}</p></article></main>`;
});