Thesaurus/templates/PublisherDetailsModal.tpl
2026-02-23 16:11:35 +01:00

318 lines
12 KiB
Smarty

<!-- Modal - Verlag Details mit semantischem Netz -->
<div class="modal fade" id="DetailsModal" tabindex="-1" aria-labelledby="DetailsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 1200px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="DetailsModalHeadline">
<i class="fas fa-book"></i> Detailanzeige Verlag
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
</div>
<div class="modal-body">
<div class="row">
<!-- Linke Spalte: Stammdaten -->
<div class="col-md-4">
<div class="detail-info-panel">
<div class="form-group mb-3">
<label class="form-label fw-bold">Name</label>
<input type="text" id="display_term" class="form-control" disabled />
<input type="hidden" id="display_id" />
</div>
<div class="form-group mb-3">
<label class="form-label fw-bold">Deskriptor</label>
<input type="text" id="display_descriptor" class="form-control" disabled />
</div>
<div class="row mb-3">
<div class="col-6">
<label class="form-label fw-bold">Type</label>
<input type="text" id="display_type" class="form-control" disabled />
</div>
<div class="col-6">
<label class="form-label fw-bold">ID</label>
<input type="text" id="display_id_show" class="form-control" disabled />
</div>
</div>
<div class="form-group mb-3">
<label class="form-label fw-bold">Scopenote</label>
<textarea class="form-control" id="display_scopenote" rows="4" disabled></textarea>
</div>
</div>
</div>
<!-- Rechte Spalte: Semantisches Netz -->
<div class="col-md-8">
<div class="network-panel">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="mb-0"><i class="fas fa-project-diagram me-2"></i>Semantisches Netz</h6>
<div class="network-toolbar">
<button class="btn btn-sm btn-outline-secondary" onclick="networkFit()" title="Ansicht anpassen">
<i class="fas fa-expand"></i>
</button>
<button class="btn btn-sm btn-outline-secondary" onclick="networkZoomIn()" title="Vergrößern">
<i class="fas fa-search-plus"></i>
</button>
<button class="btn btn-sm btn-outline-secondary" onclick="networkZoomOut()" title="Verkleinern">
<i class="fas fa-search-minus"></i>
</button>
</div>
</div>
<div id="network-container"></div>
<div class="network-legend">
<span class="legend-item"><span class="legend-dot" style="background: #1a3a5c;"></span> Zentraler Verlag</span>
<span class="legend-item"><span class="legend-dot" style="background: #28a745;"></span> Früher verwendet</span>
<span class="legend-item"><span class="legend-dot" style="background: #dc3545;"></span> Später verwendet</span>
<span class="legend-item"><span class="legend-dot" style="background: #fd7e14;"></span> Alternativer Name</span>
<span class="legend-item"><span class="legend-dot" style="background: #6c757d;"></span> USE/USEDFOR</span>
</div>
</div>
</div>
</div>
<div class="modal_content" style="display: none;"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times"></i> schliessen
</button>
</div>
</div>
</div>
</div>
<style>
#network-container {
width: 100%;
height: 450px;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
}
.network-panel {
background: white;
border-radius: var(--radius-md);
padding: 15px;
border: 1px solid var(--border-color);
}
.detail-info-panel {
background: var(--bg-light);
border-radius: var(--radius-md);
padding: 15px;
border: 1px solid var(--border-color);
}
.network-toolbar {
display: flex;
gap: 5px;
}
.network-toolbar .btn {
padding: 4px 8px;
}
.network-legend {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--border-light);
font-size: 12px;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.legend-dot {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
}
#DetailsModal .modal-body {
max-height: 80vh;
overflow-y: auto;
}
</style>
<script src="/libs/vis-network/current/vis-network.min.js"></script>
<script>if (typeof vis === "undefined") { document.write('<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"><\/script>'); }</script>
<script>
var network = null;
var networkData = null;
function createSemanticNetwork(centralTerm, centralId, relations) {
console.log('🕸️ Erstelle semantisches Netz für Verlag:', centralTerm);
var nodes = [];
var edges = [];
nodes.push({
id: centralId,
label: wrapLabel(centralTerm, 20),
shape: 'ellipse',
color: {
background: '#1a3a5c',
border: '#0d1f30',
highlight: { background: '#2a5a8c', border: '#1a3a5c' },
hover: { background: '#2a5a8c', border: '#1a3a5c' }
},
font: { color: '#ffffff', size: 14, face: 'Arial', bold: true },
size: 35,
borderWidth: 3,
shadow: true
});
// Farben für Verlags-Relationstypen
var relationColors = {
'USEDEARLIER': { node: '#28a745', edge: '#28a745', label: 'Früher verwendet' },
'USEDLATER': { node: '#dc3545', edge: '#dc3545', label: 'Später verwendet' },
'ALTERNATIVENAME': { node: '#fd7e14', edge: '#fd7e14', label: 'Alternativer Name' },
'USE': { node: '#6c757d', edge: '#6c757d', label: 'Benutze' },
'USEDFOR': { node: '#6c757d', edge: '#6c757d', label: 'Benutzt für' }
};
if (relations && relations.length > 0) {
relations.forEach(function(rel, index) {
var relType = rel.Relationtype || 'USE';
var relColor = relationColors[relType] || { node: '#17a2b8', edge: '#17a2b8' };
var relId = rel.IDRelation || ('rel_' + index);
var relText = rel.TextRelation || 'Unbekannt';
nodes.push({
id: relId,
label: wrapLabel(relText, 18),
shape: 'ellipse',
color: {
background: relColor.node,
border: shadeColor(relColor.node, -20),
highlight: { background: shadeColor(relColor.node, 20), border: relColor.node },
hover: { background: shadeColor(relColor.node, 20), border: relColor.node }
},
font: { color: '#ffffff', size: 12, face: 'Arial' },
size: 25,
borderWidth: 2,
shadow: true,
title: relType + ': ' + relText + ' (ID: ' + relId + ')'
});
var arrowDirection = {};
if (relType === 'USEDEARLIER') {
arrowDirection = { from: { enabled: true, scaleFactor: 1 } };
} else if (relType === 'USEDLATER') {
arrowDirection = { to: { enabled: true, scaleFactor: 1 } };
}
edges.push({
from: centralId,
to: relId,
label: relType,
color: { color: relColor.edge, highlight: relColor.edge, hover: relColor.edge },
width: 2,
arrows: arrowDirection,
font: { size: 10, color: '#666', strokeWidth: 3, strokeColor: '#ffffff' },
smooth: { type: 'curvedCW', roundness: 0.2 }
});
});
}
var container = document.getElementById('network-container');
networkData = {
nodes: new vis.DataSet(nodes),
edges: new vis.DataSet(edges)
};
var options = {
nodes: { shape: 'ellipse', scaling: { min: 20, max: 40 } },
edges: { smooth: { type: 'curvedCW', roundness: 0.2 } },
physics: {
enabled: true,
barnesHut: {
gravitationalConstant: -3000,
centralGravity: 0.3,
springLength: 150,
springConstant: 0.04,
damping: 0.09
},
stabilization: { enabled: true, iterations: 200, updateInterval: 25 }
},
interaction: { hover: true, tooltipDelay: 200, zoomView: true, dragView: true },
layout: { improvedLayout: true }
};
network = new vis.Network(container, networkData, options);
network.on('click', function(params) {
if (params.nodes.length > 0) {
var clickedNodeId = params.nodes[0];
if (clickedNodeId != centralId) {
ShowModalDetails(clickedNodeId);
}
}
});
network.once('stabilizationIterationsDone', function() {
network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
});
}
function wrapLabel(text, maxLength) {
if (!text) return '';
if (text.length <= maxLength) return text;
var words = text.split(' ');
var lines = [];
var currentLine = '';
words.forEach(function(word) {
if ((currentLine + ' ' + word).trim().length <= maxLength) {
currentLine = (currentLine + ' ' + word).trim();
} else {
if (currentLine) lines.push(currentLine);
currentLine = word;
}
});
if (currentLine) lines.push(currentLine);
return lines.join('\n');
}
function shadeColor(color, percent) {
var R = parseInt(color.substring(1,3), 16);
var G = parseInt(color.substring(3,5), 16);
var B = parseInt(color.substring(5,7), 16);
R = parseInt(R * (100 + percent) / 100);
G = parseInt(G * (100 + percent) / 100);
B = parseInt(B * (100 + percent) / 100);
R = (R < 255) ? R : 255;
G = (G < 255) ? G : 255;
B = (B < 255) ? B : 255;
var RR = ((R.toString(16).length == 1) ? "0" + R.toString(16) : R.toString(16));
var GG = ((G.toString(16).length == 1) ? "0" + G.toString(16) : G.toString(16));
var BB = ((B.toString(16).length == 1) ? "0" + B.toString(16) : B.toString(16));
return "#" + RR + GG + BB;
}
function networkFit() {
if (network) network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
}
function networkZoomIn() {
if (network) network.moveTo({ scale: network.getScale() * 1.3, animation: { duration: 300 } });
}
function networkZoomOut() {
if (network) network.moveTo({ scale: network.getScale() / 1.3, animation: { duration: 300 } });
}
</script>