ontoref-code/crates/ontoref-daemon/templates/pages/panel.html
2026-07-10 01:44:59 +01:00

143 lines
5.6 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ panel.title | default(value="Panel") }} — Ontoref{% endblock title %}
{% block nav_group_knowledge %}active{% endblock nav_group_knowledge %}
{% block main_class %}container mx-auto px-4 py-6{% endblock main_class %}
{% block head %}
{% if panel.kind == "Timeline" or panel.kind == "Gantt" %}
<link rel="stylesheet" href="{{ base_url }}/assets/vendor/vis-timeline.min.css" />
<style>
#panel-root {
width: 100%; height: calc(100vh - 220px); min-height: 380px;
border-radius: 0.5rem; overflow: hidden;
border: 1px solid oklch(var(--bc) / 0.12);
}
.vis-timeline { background: oklch(var(--b1)); color: oklch(var(--bc)); }
.vis-time-axis .vis-text { color: oklch(var(--bc) / 0.6); }
.vis-panel.vis-center, .vis-panel.vis-left { border-color: oklch(var(--bc) / 0.12); }
</style>
{% elif panel.kind == "Graph" %}
<style>
#panel-root { width: 100%; height: calc(100vh - 220px); min-height: 380px;
border-radius: 0.5rem; overflow: hidden; border: 1px solid oklch(var(--bc) / 0.12); }
#cy { width: 100%; height: 100%; }
</style>
{% endif %}
{% endblock head %}
{% block content %}
<div class="flex items-center justify-between mb-4 gap-4">
<div>
<h1 class="text-xl font-semibold">{{ panel.title | default(value="Panel") }}</h1>
<p class="text-xs opacity-60 mt-0.5">
kind={{ panel.kind | default(value="?") }}
&nbsp;·&nbsp; op={{ panel.data_source_op | default(value="—") }}
&nbsp;·&nbsp; id={{ panel.id | default(value="—") }}
</p>
</div>
{% if panel.kind == "Timeline" or panel.kind == "Gantt" %}
<button class="btn btn-ghost btn-sm" onclick="pTimeline && pTimeline.fit()">Fit</button>
{% endif %}
</div>
{% if panel.kind == "Timeline" or panel.kind == "Gantt" %}
<div id="panel-root"></div>
{% elif panel.kind == "Graph" %}
<div id="panel-root"><div id="cy"></div></div>
{% elif panel.kind == "Table" %}
<div class="overflow-x-auto">
<table class="table table-zebra text-sm w-full">
<thead>
<tr id="tbl-head"></tr>
</thead>
<tbody id="tbl-body"></tbody>
</table>
</div>
{% elif panel.kind == "Board" %}
<div id="board-root" class="grid grid-cols-1 md:grid-cols-3 gap-4"></div>
{% else %}
<div class="alert alert-info">
Panel kind <strong>{{ panel.kind | default(value="unknown") }}</strong> has no renderer.
Raw data: <code>{{ panel.data_source_op | default(value="—") }}</code>
</div>
{% endif %}
{% endblock content %}
{% block scripts %}
{% if panel.kind == "Timeline" or panel.kind == "Gantt" %}
<script src="{{ base_url }}/assets/vendor/vis-timeline.min.js"></script>
<script>
const PANEL_DATA = {{ panel_data_json | safe }};
const items = (PANEL_DATA.items || PANEL_DATA.entries || []).map((e, i) => ({
id: e.id || String(i),
start: e.date || e.start || e.target_date || "1970-01-01",
end: e.end || undefined,
content: `<span style="max-width:220px;overflow:hidden;text-overflow:ellipsis;display:inline-block;white-space:nowrap">${e.title || e.content || JSON.stringify(e)}</span>`,
group: e.kind || e.group || e.status || "item",
title: e.title || "",
}));
const groups = [...new Set(items.map(i => i.group))].map(g => ({ id: g, content: g }));
const pTimeline = new vis.Timeline(
document.getElementById('panel-root'),
new vis.DataSet(items),
new vis.DataSet(groups),
{ stack: true, orientation: { axis: "top" }, selectable: false }
);
pTimeline.fit();
</script>
{% elif panel.kind == "Graph" %}
<script src="https://cdn.jsdelivr.net/npm/cytoscape@3.30.2/dist/cytoscape.min.js"></script>
<script>
const PANEL_DATA = {{ panel_data_json | safe }};
const cy = cytoscape({
container: document.getElementById('cy'),
elements: [
...(PANEL_DATA.nodes || []).map(n => ({ data: { id: n.id, label: n.name || n.id } })),
...(PANEL_DATA.edges || []).map(e => ({ data: { id: e.id || `${e.from}-${e.to}`, source: e.from || e.source, target: e.to || e.target, label: e.kind || "" } })),
],
style: [
{ selector: 'node', style: { 'label': 'data(label)', 'background-color': '#3b82f6', 'color': '#fff', 'font-size': '11px', 'text-halign': 'center', 'text-valign': 'center' } },
{ selector: 'edge', style: { 'line-color': '#6b7280', 'target-arrow-color': '#6b7280', 'target-arrow-shape': 'triangle', 'curve-style': 'bezier', 'font-size': '10px' } },
],
layout: { name: 'cose', animate: false },
});
</script>
{% elif panel.kind == "Table" %}
<script>
const PANEL_DATA = {{ panel_data_json | safe }};
const rows = PANEL_DATA.items || PANEL_DATA.entries || [];
if (rows.length > 0) {
const cols = Object.keys(rows[0]);
document.getElementById('tbl-head').innerHTML = cols.map(c => `<th>${c}</th>`).join('');
document.getElementById('tbl-body').innerHTML = rows.map(r =>
`<tr>${cols.map(c => `<td class="max-w-xs overflow-hidden text-ellipsis">${String(r[c] ?? '')}</td>`).join('')}</tr>`
).join('');
}
</script>
{% elif panel.kind == "Board" %}
<script>
const PANEL_DATA = {{ panel_data_json | safe }};
const cols = PANEL_DATA.columns || [{ id: "items", label: "Items", items: PANEL_DATA.items || [] }];
const board = document.getElementById('board-root');
board.innerHTML = cols.map(col => `
<div class="bg-base-200 rounded-lg p-3">
<h3 class="font-semibold text-sm mb-3">${col.label || col.id}</h3>
<div class="space-y-2">
${(col.items || []).map(it => `
<div class="bg-base-100 rounded p-2 text-xs">
<p class="font-medium">${it.title || it.id || JSON.stringify(it)}</p>
${it.description ? `<p class="opacity-60 mt-1">${it.description}</p>` : ''}
</div>`).join('')}
</div>
</div>`).join('');
</script>
{% endif %}
{% endblock scripts %}