Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

789 - Collapsible panels #790

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions changes/789.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add collapsible panels to Configuration Compliance tab.
36 changes: 36 additions & 0 deletions nautobot_golden_config/static/config_compliance_layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Enables panels on homepage to be collapsed and expanded
document.addEventListener("DOMContentLoaded", function() {
// Function to toggle and save state for a specific collapsible element
function toggleAndSaveState(elementId) {
// Remove "toggle-" in the ID to get the localStorage key the toggle btn references
elementId = elementId.replace("toggle-", "");
var collapsibleDiv = document.getElementById(elementId);

// Toggle the collapsed class
var isCollapsed = collapsibleDiv.classList.toggle("collapsed")

// Retrieve glyphicon
var icon = document.getElementById("collapse-icon-" + elementId);
// Rotate glyphicon
icon.classList.toggle("rotated");

// Update the state in localStorage
var isCollapsed = collapsibleDiv.classList.contains("in");
localStorage.setItem(elementId, isCollapsed ? "collapsed" : "expanded");
// Set Cookie value based on isCollapsed
if (isCollapsed) {
document.cookie = elementId + "=True; path=/";
} else {
document.cookie = elementId + "=False; path=/";
}
}

// Add event listener to each collapsible div
var collapseIcons = document.querySelectorAll(".collapsable-heading");
collapseIcons.forEach(function(icon) {
icon.addEventListener("click", function() {
var elementId = this.id;
toggleAndSaveState(elementId);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,23 @@
#navigation td:hover{
transform: scale(1.1);
}
.rotated {
transform: rotate(180deg);
}
.collapse-icon {
transition: transform 0.3s ease;
}
.collapsable-heading:hover {
cursor: pointer;
}
</style>
<noscript>
<style>
.collapse-icon {
display: none;
}
</style>
</noscript>
{% block navigation %}
<div class="panel panel-default" style="width:100%">
<div class="panel-heading"><strong>Feature Navigation</strong>
Expand All @@ -64,17 +80,17 @@
<a href="{% url 'plugins:nautobot_golden_config:configcompliance_devicetab' pk=device.pk %}?tab={{ active_tab }}" class="btn btn-info">Clear</a>
</div>
<div id="navigation">
<table>
<table border="1" cellspacing="0" cellpadding="0">
<tr>
{% for item in compliance_details %}
<td style="padding: 3px;">
{% if item.compliance %}
<td style="background-color: #5cb85c;">
<div style="background-color: #15a030; border-radius: 10px; padding: 10px; margin: 3px;">
{% else %}
<td style="background-color: #d9534f;">
<div style="background-color: #e02020; border-radius: 10px; padding: 10px; margin: 3px;">
{% endif %}
<a href="#{{ item.rule }}">
{{ item.rule }}
</a>
<a href="#{{ item.rule }}">{{ item.rule }}</a>
</div>
</td>
{% if forloop.counter|divisibleby:"5" %}
</tr>
Expand All @@ -86,121 +102,137 @@
</div>
</div>
{% endblock %}
<div class="homepage_column" id="draggable-configcompliance-panels">
{% for item in compliance_details %}
<div id="{{ item.rule }}" class="panel panel-default" style="width:100%">
<div class="panel-heading"><strong><a href="{% url 'plugins:nautobot_golden_config:configcompliance' pk=item.pk %}">{{ item.rule.feature.name|upper }}</a></strong></div>
<table id="compliance-content">
<tr>
<td style="width:250px">Status</td>
{% if item.rule.config_ordered %}
{% if item.compliance %}
<td><span class="label label-success">Compliant</span> <span><i class="mdi mdi-sort" title="Ordered Configuration Test"></i></span></td>
{% else %}
<td><span class="label label-danger">Non-Compliant</span> <span><i class="mdi mdi-sort" title="Ordered Configuration Test"></i></span></td>
{% endif %}
{% else %}
{% if item.compliance %}
<td><span class="label label-success">Compliant</span> <span><i class="mdi mdi-swap-vertical" title="Unordered Configuration Test"></i></span></td>
{% else %}
<td><span class="label label-danger">Non-Compliant</span> <span><i class="mdi mdi-swap-vertical" title="Unordered Configuration Test"></i></span></td>
{% endif %}
{% endif %}

</tr>
{% if item.ordered %}
<tr>
<td style="width:250px">Configuration</td>
<td class="config_hover">
{% if item.rule.config_type == "xml" %}
<span id="{{ item.rule|slugify }}_actual"><pre><code class="language-xml">{{ item.actual|placeholder }}</code></pre></span>
{% elif item.rule.config_type == "json" %}
<span id="{{ item.rule|slugify }}_actual"><pre><code class="language-json">{{ item.actual|placeholder|condition_render_json }}</code></pre></span>
{% else %}
<span id="{{ item.rule|slugify }}_actual"><pre>{{ item.actual|placeholder }}</pre></span>
{% endif %}
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item.rule|slugify }}_actual">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% else %}
{% with item_rule=item.rule|slugify %}
<div id="{{ item_rule }}" class="panel panel-default" style="width:100%">
{% with cookie_key='configcompliance-'|add:item_rule %}
<div id="toggle-configcompliance-{{ item_rule }}" class="panel-heading collapsable-heading" type="button" data-toggle="collapse" data-target="#configcompliance-{{ item_rule }}" aria-expanded="false" aria-controls="configcompliance-{{ item_rule }}">
<strong><a href="{% url 'plugins:nautobot_golden_config:configcompliance' pk=item.pk %}">{{ item.rule.feature.name|upper }}</a></strong>
<span id="collapse-icon-configcompliance-{{ item_rule }}" class="glyphicon glyphicon-chevron-down collapse-icon{% if request.COOKIES|default:''|get_item:cookie_key|default:'False' == 'False' %} rotated{% endif %}"></span>
</div>
<div class="list-group collapse{% if request.COOKIES|default:''|get_item:cookie_key|default:'False' == 'False' %} in{% endif %} collapsible-div" id="configcompliance-{{ item_rule }}">
{% endwith %}
<table id="compliance-content">
<tr>
<td style="width:250px">Intended Configuration</td>
<td class="config_hover">
{% if item.rule.config_type == "xml" %}
<span id="{{ item.rule|slugify }}_intended"><pre><code class="language-xml">{{ item.intended|placeholder }}</code></pre></span>
{% elif item.rule.config_type == "json" %}
<span id="{{ item.rule|slugify }}_intended"><pre><code class="language-json">{{ item.intended|placeholder|condition_render_json }}</code></pre></span>
<td style="width:250px">Status</td>
{% if item.rule.config_ordered %}
{% if item.compliance %}
<td><span class="label label-success">Compliant</span> <span><i class="mdi mdi-sort" title="Ordered Configuration Test"></i></span></td>
{% else %}
<span id="{{ item.rule|slugify }}_intended"><pre>{{ item.intended|placeholder }}</pre></span>
<td><span class="label label-danger">Non-Compliant</span> <span><i class="mdi mdi-sort" title="Ordered Configuration Test"></i></span></td>
{% endif %}
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item.rule|slugify }}_intended">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
<tr>
<td style="width:250px">Actual Configuration</td>
<td class="config_hover">
{% if item.rule.config_type == "xml" %}
<span id="{{ item.rule|slugify }}_actual"><pre><code class="language-xml">{{ item.actual|placeholder }}</code></pre></span>
{% elif item.rule.config_type == "json" %}
<span id="{{ item.rule|slugify }}_actual"><pre><code class="language-json">{{ item.actual|placeholder|condition_render_json }}</code></pre></span>
{% else %}
{% if item.compliance %}
<td><span class="label label-success">Compliant</span> <span><i class="mdi mdi-swap-vertical" title="Unordered Configuration Test"></i></span></td>
{% else %}
<span id="{{ item.rule|slugify }}_actual"><pre>{{ item.actual|placeholder }}</pre></span>
<td><span class="label label-danger">Non-Compliant</span> <span><i class="mdi mdi-swap-vertical" title="Unordered Configuration Test"></i></span></td>
{% endif %}
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item.rule|slugify }}_actual">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% endif %}
{% if item.missing != "" %}
<tr>
<td style="color:red;width:250px">Missing Configuration</td>
<td class="config_hover">
<span id="{{ item.rule|slugify }}_missing"><pre>{{ item.missing|condition_render_json }}</pre></span>
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item.rule|slugify }}_missing">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
{% endif %}

</tr>
{% endif %}
{% if item.extra != "" %}
{% if item.ordered %}
<tr>
<td style="width:250px">Configuration</td>
<td class="config_hover">
{% if item.rule.config_type == "xml" %}
<span id="{{ item_rule }}_actual"><pre><code class="language-xml">{{ item.actual|placeholder }}</code></pre></span>
{% elif item.rule.config_type == "json" %}
<span id="{{ item_rule }}_actual"><pre><code class="language-json">{{ item.actual|placeholder|condition_render_json }}</code></pre></span>
{% else %}
<span id="{{ item_rule }}_actual"><pre>{{ item.actual|placeholder }}</pre></span>
{% endif %}
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item_rule }}_actual">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% else %}
<tr>
<td style="width:250px">Intended Configuration</td>
<td class="config_hover">
{% if item.rule.config_type == "xml" %}
<span id="{{ item_rule }}_intended"><pre><code class="language-xml">{{ item.intended|placeholder }}</code></pre></span>
{% elif item.rule.config_type == "json" %}
<span id="{{ item_rule }}_intended"><pre><code class="language-json">{{ item.intended|placeholder|condition_render_json }}</code></pre></span>
{% else %}
<span id="{{ item_rule }}_intended"><pre>{{ item.intended|placeholder }}</pre></span>
{% endif %}
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item_rule }}_intended">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
<tr>
<td style="width:250px">Actual Configuration</td>
<td class="config_hover">
{% if item.rule.config_type == "xml" %}
<span id="{{ item_rule }}_actual"><pre><code class="language-xml">{{ item.actual|placeholder }}</code></pre></span>
{% elif item.rule.config_type == "json" %}
<span id="{{ item_rule }}_actual"><pre><code class="language-json">{{ item.actual|placeholder|condition_render_json }}</code></pre></span>
{% else %}
<span id="{{ item_rule }}_actual"><pre>{{ item.actual|placeholder }}</pre></span>
{% endif %}
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item_rule }}_actual">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% endif %}
{% if item.missing != "" %}
<tr>
<td style="color:red;width:250px">Missing Configuration</td>
<td class="config_hover">
<span id="{{ item_rule }}_missing"><pre>{{ item.missing|condition_render_json }}</pre></span>
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item_rule }}_missing">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% endif %}
{% if item.extra != "" %}
<tr>
<td style="color:red;width:250px">Extra Configuration</td>
<td class="config_hover">
<span id="{{ item_rule }}_extra"><pre>{{ item.extra|condition_render_json }}</pre></span>
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item_rule }}_extra">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% endif %}
{% if item.remediation != "" %}
<tr>
<td style="color:red;width:250px">Extra Configuration</td>
<td style="color:red;width:250px">Remediating Configuration</td>
<td class="config_hover">
<span id="{{ item.rule|slugify }}_extra"><pre>{{ item.extra|condition_render_json }}</pre></span>
<span id="{{ item_rule }}_remediation"><pre>{{ item.remediation|condition_render_json }}</pre></span>
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item.rule|slugify }}_extra">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item_rule }}_remediation">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% endif %}
{% if item.remediation != "" %}
<tr>
<td style="color:red;width:250px">Remediating Configuration</td>
<td class="config_hover">
<span id="{{ item.rule|slugify }}_remediation"><pre>{{ item.remediation|condition_render_json }}</pre></span>
<span class="config_hover_button">
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{{ item.rule|slugify }}_remediation">
<span class="mdi mdi-content-copy"></span>
</button>
</span>
</td>
</tr>
{% endif %}
</table>
</table>
</div>
</div>
{% endwith %}
{% endfor %}
</div>
{% endblock %}
{% block javascript %}
{{ block.super }}
<script src="{% versioned_static 'config_compliance_layout.js' %}"
onerror="window.location='{% url 'media_failure' %}?filename=config_compliance_layout.js'"></script>
{% endblock %}