Files
iTop/lib/symfony/web-profiler-bundle/Resources/views/Collector/workflow.html.twig
bdalsass 27ce51ab07 N°6934 - Symfony 6.4 - upgrade Symfony bundles to 6.4 (#580)
* Update Symfony lib to version ~6.4.0
* Update code missing return type
* Add an iTop general configuration entry to store application secret (Symfony mandatory parameter)
* Use dependency injection in ExceptionListener & UserProvider classes
2023-12-05 13:56:56 +01:00

341 lines
12 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
dialog {
border: none;
border-radius: 6px;
box-shadow: var(--settings-modal-shadow);
max-width: 94%;
width: 1200px;
}
dialog::backdrop {
background: linear-gradient(
45deg,
rgb(18, 18, 20, 0.4),
rgb(17, 17, 20, 0.8)
);
}
dialog[open] {
animation: scale 0.3s ease normal;
}
dialog[open]::backdrop {
animation: backdrop 0.3s ease normal;
}
dialog.hide {
animation-direction: reverse;
}
dialog h2 {
margin-top: 0.2em
}
dialog i.cancel {
cursor: pointer;
padding: 0 5px;
float: right;
}
dialog table {
border: 1px solid #ccc;
border-collapse: collapse;
margin: 0 0 1em 0;
margin-bottom: 1em;
padding: 0;
table-layout: fixed;
}
dialog table tr {
background-color: #f8f8f8;
border: 1px solid #ddd;
padding: .35em;
}
dialog table th,
dialog table td {
padding: .625em;
text-align: center;
}
dialog table th {
font-size: .85em;
letter-spacing: .1em;
text-transform: uppercase;
}
dialog menu {
padding: 0;
margin: 0;
display: flex;
align-items: center;
flex-direction: row;
vertical-align: middle;
justify-content: center;
}
dialog menu small {
margin-right: auto;
}
dialog menu small i {
margin-right: 3px;
}
@keyframes scale {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes backdrop {
from { opacity: 0; }
to { opacity: 1; }
}
</style>
{% endblock %}
{% block toolbar %}
{% if collector.callsCount > 0 %}
{% set icon %}
{{ source('@WebProfiler/Icon/workflow.svg') }}
<span class="sf-toolbar-value">{{ collector.callsCount }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Workflow Calls</b>
<span>{{ collector.callsCount }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label {{ collector.workflows|length == 0 ? 'disabled' }}">
<span class="icon">
{{ source('@WebProfiler/Icon/workflow.svg') }}
</span>
<strong>Workflow</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Workflow</h2>
{% if collector.workflows|length == 0 %}
<div class="empty empty-panel">
<p>There are no workflows configured.</p>
</div>
{% else %}
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({
flowchart: { useMaxWidth: false },
securityLevel: 'loose',
});
{% for name, data in collector.workflows %}
window.showNodeDetails{{ collector.hash(name) }} = function (node) {
const map = {{ data.listeners|json_encode|raw }};
showNodeDetails(node, map);
};
{% endfor %}
const showNodeDetails = function (node, map) {
const dialog = document.getElementById('detailsDialog');
dialog.querySelector('tbody').innerHTML = '';
for (const [eventName, listeners] of Object.entries(map[node])) {
listeners.forEach(listener => {
const row = document.createElement('tr');
const eventNameCode = document.createElement('code');
eventNameCode.textContent = eventName;
const eventNameCell = document.createElement('td');
eventNameCell.appendChild(eventNameCode);
row.appendChild(eventNameCell);
const listenerDetailsCell = document.createElement('td');
row.appendChild(listenerDetailsCell);
let listenerDetails;
const listenerDetailsCode = document.createElement('code');
listenerDetailsCode.textContent = listener.title;
if (listener.file) {
const link = document.createElement('a');
link.href = listener.file;
link.appendChild(listenerDetailsCode);
listenerDetails = link;
} else {
listenerDetails = listenerDetailsCode;
}
listenerDetailsCell.appendChild(listenerDetails);
if (typeof listener.guardExpressions === 'object') {
listenerDetailsCell.appendChild(document.createElement('br'));
const guardExpressionsWrapper = document.createElement('span');
guardExpressionsWrapper.appendChild(document.createTextNode('guard expressions: '));
listener.guardExpressions.forEach((expression, index) => {
if (index > 0) {
guardExpressionsWrapper.appendChild(document.createTextNode(', '));
}
const expressionCode = document.createElement('code');
expressionCode.textContent = expression;
guardExpressionsWrapper.appendChild(expressionCode);
});
listenerDetailsCell.appendChild(guardExpressionsWrapper);
}
dialog.querySelector('tbody').appendChild(row);
});
};
if (dialog.dataset.processed) {
dialog.showModal();
return;
}
dialog.addEventListener('click', (e) => {
const rect = dialog.getBoundingClientRect();
const inDialog =
rect.top <= e.clientY &&
e.clientY <= rect.top + rect.height &&
rect.left <= e.clientX &&
e.clientX <= rect.left + rect.width;
!inDialog && dialog.close();
});
dialog.querySelectorAll('.cancel').forEach(elt => {
elt.addEventListener('click', () => dialog.close());
});
dialog.showModal();
dialog.dataset.processed = true;
};
// We do not load all mermaid diagrams at once, but only when the tab is opened
// This is because mermaid diagrams are in a tab, and cannot be renderer with a
// "good size" if they are not visible
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.tab').forEach((el) => {
const observer = new MutationObserver(() => {
if (!el.classList.contains('block')) {
return;
}
const mEl = el.querySelector('.sf-mermaid');
if (mEl.dataset.processed) {
return;
}
mermaid.run({
nodes: [mEl],
});
});
observer.observe(el, { attributeFilter: ['class'] });
});
});
</script>
<div class="sf-tabs js-tabs">
{% for name, data in collector.workflows %}
<div class="tab">
<h2 class="tab-title">{{ name }}{% if data.calls|length %} ({{ data.calls|length }}){% endif %}</h2>
<div class="tab-content">
<h3>Definition</h3>
<pre class="sf-mermaid">
{{ data.dump|raw }}
{% for nodeId, events in data.listeners %}
click {{ nodeId }} showNodeDetails{{ collector.hash(name) }}
{% endfor %}
</pre>
<h3>Calls</h3>
<table>
<thead>
<tr>
<th>#</th>
<th>Call</th>
<th>Args</th>
<th>Return</th>
<th>Exception</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{% for call in data.calls %}
<tr>
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td>
<td>
<code>{{ call.method }}()</code>
{% if call.previousMarking ?? null %}
<hr />
Previous marking:
{{ profiler_dump(call.previousMarking) }}
{% endif %}
</td>
<td>
{{ profiler_dump(call.args) }}
</td>
<td>
{% if call.return is defined %}
{% if call.return is same as true %}
<code>true</code>
{% elseif call.return is same as false %}
<code>false</code>
{% else %}
{{ profiler_dump(call.return) }}
{% endif %}
{% endif %}
</td>
<td>
{% if call.exception is defined %}
{{ profiler_dump(call.exception) }}
{% endif %}
</td>
<td>
{{ call.duration }}ms
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<dialog id="detailsDialog">
<h2>
Event listeners
<i class="cancel">×</i>
</h2>
<table>
<thead>
<tr>
<th>event</th>
<th>listener</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<menu>
<small><i>⌨</i> <kbd>esc</kbd></small>
<button class="btn btn-sm cancel">Close</button>
</menu>
</dialog>
{% endblock %}