aboutsummaryrefslogtreecommitdiffstats
path: root/src/templates/dashboard
diff options
context:
space:
mode:
Diffstat (limited to 'src/templates/dashboard')
-rw-r--r--src/templates/dashboard/ci_pods.html61
-rw-r--r--src/templates/dashboard/dev_pods.html70
-rw-r--r--src/templates/dashboard/grid.html10
-rw-r--r--src/templates/dashboard/host_profile_detail.html71
-rw-r--r--src/templates/dashboard/jenkins_slaves.html46
-rw-r--r--src/templates/dashboard/lab_detail.html157
-rw-r--r--src/templates/dashboard/lab_list.html87
-rw-r--r--src/templates/dashboard/landing.html88
-rw-r--r--src/templates/dashboard/login.html8
-rw-r--r--src/templates/dashboard/multiple_select_filter_widget.html402
-rw-r--r--src/templates/dashboard/pdf.yaml95
-rw-r--r--src/templates/dashboard/resource.html3
-rw-r--r--src/templates/dashboard/resource_all.html5
-rw-r--r--src/templates/dashboard/resource_detail.html52
-rw-r--r--src/templates/dashboard/searchable_select_multiple.html408
-rw-r--r--src/templates/dashboard/table.html1
16 files changed, 1329 insertions, 235 deletions
diff --git a/src/templates/dashboard/ci_pods.html b/src/templates/dashboard/ci_pods.html
deleted file mode 100644
index a20be95..0000000
--- a/src/templates/dashboard/ci_pods.html
+++ /dev/null
@@ -1,61 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-{% load jenkins_filters %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Name</th>
- <th>Slave Name</th>
- <th>Status</th>
- <th>Installer</th>
- <th>Scenario</th>
- <th>Branch</th>
- <th>Job</th>
- </tr>
- </thead>
- <tbody>
- {% for pod in ci_pods %}
- <tr>
- <td>
- <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a>
- </td>
- <td>
- <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
- </td>
- <td style="background-color:{{ pod.slave.status | jenkins_status_color }}">
- {{ pod.slave.status }}
- </td>
- <td {{ pod.slave.last_job_result | jenkins_job_blink }}>
- {{ pod.slave.last_job_installer }}
- </td>
- <td {{ pod.slave.last_job_result | jenkins_job_blink }}>
- {{ pod.slave.last_job_scenario }}
- </td>
- <td {{ pod.slave.last_job_result | jenkins_job_blink }}>
- {{ pod.slave.last_job_branch }}
- </td>
- <td><a {{ pod.slave.last_job_result | jenkins_job_blink }}
- style="color:{{ pod.slave.last_job_result | jenkins_job_color }}"
- target='_blank'
- href={{ pod.slave.last_job_url }}>{{ pod.slave.last_job_name }}</a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- columnDefs: [
- {type: 'status', targets: 2}
- ],
- "order": [[2, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/src/templates/dashboard/dev_pods.html b/src/templates/dashboard/dev_pods.html
deleted file mode 100644
index a6f3b2e..0000000
--- a/src/templates/dashboard/dev_pods.html
+++ /dev/null
@@ -1,70 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-{% load jenkins_filters %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Name</th>
- <th>Slave Name</th>
- <th>Booked by</th>
- <th>Booked until</th>
- <th>Purpose</th>
- <th>Utilization</th>
- <th>Status</th>
- <th></th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- {% for pod, booking, utilization in dev_pods %}
- <tr>
- <td>
- <a href={% url 'dashboard:resource' resource_id=pod.id %}>{{ pod.name }}</a>
- </td>
- <td>
- <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
- </td>
- <td>
- {{ booking.user.username }}
- </td>
- <td>
- {{ booking.end }}
- </td>
- <td>
- {{ booking.purpose }}
- </td>
- <td>
- {{ utilization }}
- </td>
- <td style="background-color:{{ pod.slave.status | jenkins_status_color }}">
- {{ pod.slave.status }}
- </td>
- <td>
- <a href="{% url 'booking:create' resource_id=pod.id %}" class="btn btn-primary">
- Book
- </a>
- </td>
- <td>
- <a href="{% url 'dashboard:resource' resource_id=pod.id %}" class="btn btn-primary">
- Info
- </a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- columnDefs: [
- {type: 'status', targets: 6}
- ],
- "order": [[6, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/src/templates/dashboard/grid.html b/src/templates/dashboard/grid.html
new file mode 100644
index 0000000..ca47f44
--- /dev/null
+++ b/src/templates/dashboard/grid.html
@@ -0,0 +1,10 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+{% if script %} <script>{{ script }}</script> {% endif %}
+{% block content %}
+ {% for item in grid_items %}
+ <div class="grid-item" style="border:2px; border-style:solid; border-color:black; margin:3px">
+ {{ item }}
+ </div>
+ {% endfor %}
+{% endblock content %}
diff --git a/src/templates/dashboard/host_profile_detail.html b/src/templates/dashboard/host_profile_detail.html
new file mode 100644
index 0000000..abc7648
--- /dev/null
+++ b/src/templates/dashboard/host_profile_detail.html
@@ -0,0 +1,71 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block extrahead %}
+ {{block.super}}
+ <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
+<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
+
+{% endblock %}
+
+{% block content %}
+<script type="text/javascript">
+ $('.grid').masonry(
+ {
+ itemSelector: '.grid-item',
+ columnWidth: '.grid-sizer',
+ percentPosition: true
+ }
+ );
+</script>
+
+<style media="screen">
+
+ @media screen and (min-width: 0px) and (max-width: 767px)
+ {
+
+ .grid-item
+ {
+ width: 100%;
+ }
+}
+ @media screen and (min-width: 768px) and (max-width: 1200px) {
+ .grid-item {
+ width: 50%;
+ }
+
+ }
+
+ @media screen and (min-width: 1201px) and (max-width: 2000px) {
+ .grid-item {
+ width: 33%;
+ }
+ }
+ @media screen and (min-width: 2001px) {
+ .grid-item {
+ width: 25%;
+ }
+
+ }
+ .grid-item-content {
+ height: 100px;
+ background: #D26;
+ border: 2px solid hsla(0, 0%, 0%, 0.5);
+ border-radius: 5px;
+}
+
+.grid-item-content--height2 { height: 200px; }
+</style>
+
+<div class="container-fluid">
+ <!-- add extra container element for Masonry -->
+ <div class="grid">
+ {% for host in hosts %}
+ <div class="grid-item col-xs-4">
+ <p>stub for current PR</p>
+ </div>
+ {% endfor %}
+ </div>
+</div>
+
+{% endblock content %}
diff --git a/src/templates/dashboard/jenkins_slaves.html b/src/templates/dashboard/jenkins_slaves.html
deleted file mode 100644
index fa361b1..0000000
--- a/src/templates/dashboard/jenkins_slaves.html
+++ /dev/null
@@ -1,46 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-
-{% load jenkins_filters %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Slave name</th>
- <th>Status</th>
- <th>Job</th>
- </tr>
- </thead>
- <tbody>
- {% for slave in slaves %}
- <tr>
- <td><a target='_blank'
- href={{ slave.url }}>{{ slave.name }}</a>
- </td>
- <td style="background-color:{{ slave.status | jenkins_status_color }}">
- {{ slave.status }}
- </td>
- <td><a {{ slave.last_job_result | jenkins_job_blink }}
- style="color:{{ slave.last_job_result | jenkins_job_color }}"
- target="_blank" href={{ slave.last_job_url }}>
- {{ slave.last_job_name }}</a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- columnDefs: [
- {type: 'status', targets: 1}
- ],
- "order": [[1, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/src/templates/dashboard/lab_detail.html b/src/templates/dashboard/lab_detail.html
new file mode 100644
index 0000000..4c06245
--- /dev/null
+++ b/src/templates/dashboard/lab_detail.html
@@ -0,0 +1,157 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block extrahead %}
+ {{block.super}}
+ <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
+{% endblock %}
+
+{% block content %}
+<div class="row">
+ <div class="col-lg-4">
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Lab Profile</h4>
+ <a data-toggle="collapse" data-target="#panel_overview" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+ <div class="panel-body" id="panel_overview">
+ <table class="table">
+ <tr>
+ <td>Lab Name: </td><td>{{lab.name}}</td>
+ </tr>
+ <tr>
+ <td>Lab Location: </td><td>{{lab.location}}</td>
+ </tr>
+ <tr>
+ <td>Lab Email: </td>
+ <td>{{lab.contact_email}}</td>
+ </tr>
+ {% if lab.contact_phone %}
+ <tr>
+ <td>Lab Phone: </td>
+ <td>{{lab.contact_phone}}</td>
+ </tr>
+ {% endif %}
+ <tr>
+ <td>Lab Status: </td>
+ {% if lab.status < 100 %}
+ <td><div style="border-radius: 50%; background:#40B976; height: 20px; width: 20px; display: inline-block;"></div>
+ Up</td>
+ {% elif lab.status < 200 %}
+ <td>
+ <div style="border-radius: 50%; background:#eeee00; height: 20px; width: 20px; display: inline-block;"></div>
+ Temporarily Offline</td>
+ {% else %}
+ <td>
+ <div style="border-radius: 50%; background:#e50000; height: 20px; width: 20px; display: inline-block;"></div>
+ Offline Indefinitely</td>
+ {% endif %}
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Host Profiles</h4>
+
+ <a data-toggle="collapse" data-target="#profile_panel" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+ <div class="panel-body pod_panel" id="profile_panel">
+ <table class="table">
+ {% for profile in hostprofiles %}
+ <tr>
+ <td>{{profile.name}}</td>
+ <td>{{profile.description}}</td>
+ <td>{{profile.labs}}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ </div>
+
+
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Networking Capabilities</h4>
+ <a data-toggle="collapse" data-target="#network_panel" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+
+ <div class="panel-body" id="network_panel">
+
+ <table class="table">
+ <tr>
+ <td>Block Size: (number of VLANs allowed per deployment)</td><td>{{lab.vlan_manager.block_size}}</td>
+ </tr>
+ <tr>
+ <td>Overlapping Vlans Allowed (user can pick which VLANs they wish to use): </td>
+ <td>{{lab.vlan_manager.allow_overlapping}}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Images</h4>
+ <a data-toggle="collapse" data-target="#image_panel" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+ <div class="panel-body" id="image_panel">
+ <table class="table">
+ <tr>
+ <th>Name</th>
+ <th>Owner</th>
+ <th>For Host Type</th>
+ <th>Description</th>
+ </tr>
+ {% for image in images %}
+ <tr>
+ <td>{{image.name}}</td>
+ <td>{{image.owner}}</td>
+ <td>{{image.host_type}}</td>
+ <td>{{image.description}}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ </div>
+
+ </div>
+ <div class="col-lg-8">
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Lab Hosts</h4>
+ <p style="display: inline; margin-left: 10px;"></p>
+ <a data-toggle="collapse" data-target="#lab_hosts_panel" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+
+ <div class="panel-body" id="lab_hosts_panel">
+ <table class="table">
+ <tr>
+ <th>Name</th>
+ <th>Profile</th>
+ <th>Booked</th>
+ <th>Working</th>
+ <th>Vendor</th>
+ </tr>
+ {% for host in lab.host_set.all %}
+ <tr>
+ <td>{{host.labid}}</td>
+ <td>{{host.profile}}</td>
+ <td>{{host.booked}}</td>
+ {% if host.working %}
+ <td style="background-color: #40B976;">{{host.working}}</td>
+ {% else %}
+ <td>{{host.working}}</td>
+ {% endif %}
+ <td>{{host.vendor}}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ </div>
+ </div>
+
+</div>
+
+
+{% endblock content %}
+
diff --git a/src/templates/dashboard/lab_list.html b/src/templates/dashboard/lab_list.html
new file mode 100644
index 0000000..a86f7f4
--- /dev/null
+++ b/src/templates/dashboard/lab_list.html
@@ -0,0 +1,87 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block extrahead %}
+ {{block.super}}
+ <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
+{% endblock %}
+
+{% block content %}
+ <style>
+ .grid-item-container {
+ padding: 10px;
+ }
+
+ .grid-item {
+ cursor: pointer;
+ border:2px;
+ border-style:none;
+ border-color:black;
+ border-radius: 5px;
+ padding: 7px;
+ color: inherit;
+
+ box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow, background-color;
+ transition-duration: .2s;
+ }
+
+ .grid-item-text
+ {
+ color: inherit;
+ text-decoration: none;
+ }
+ .grid-item-text:hover
+ {
+ color: #121212;
+ text-decoration: none;
+ }
+
+ .grid-item:hover {
+ box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: .2s;
+
+ }
+
+ .selected_node {
+ box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75);
+ background-color: #CCECD7;
+ transition-property: background-color;
+ transition-duration: .2s;
+ }
+
+ .disabled_node {
+ cursor: not-allowed;
+ background-color: #EFEFEF;
+ box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: .2s;
+ }
+
+ .disabled_node:hover {
+ box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75);
+ }
+
+ </style>
+ <div class="container-fluid">
+ <div class="row">
+
+ <div class="listgrid">
+ {% for lab in labs %}
+ <div class="grid-item-container col-lg-2 col-mid-4 col-sm-6">
+
+ <a href="{{ lab.name }}" class="grid-item-text">
+
+ <div class="grid-item">
+ <h4 class="grid-item-header">{{ lab.name }}</h4>
+ <p class="grid-item-description">{{ lab.description }}</p>
+ </div>
+ </a>
+ </div>
+ {% endfor %}
+ </div>
+ </div>
+ </div>
+
+{% endblock content %}
diff --git a/src/templates/dashboard/landing.html b/src/templates/dashboard/landing.html
new file mode 100644
index 0000000..3e0aacd
--- /dev/null
+++ b/src/templates/dashboard/landing.html
@@ -0,0 +1,88 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block content %}
+ <div class="">
+ <p style="text-align:center;">Welcome to the Pharos Dashboard! To get started, select one of the options below:</p>
+ </div>
+{% csrf_token %}
+
+<style>
+ .wf_create{
+ display: inline-block;
+ text-align: center;
+ }
+ .wf_create_div{
+ text-align: center;
+ }
+ .hidden_form{
+ display: none;
+ }
+
+ .panel {
+ border: none;
+ }
+
+ .panels {
+ display: grid;
+ grid-template-columns: 33% 34% 33%;
+ }
+</style>
+<script type="text/javascript">
+ function cwf(wf_type){
+ document.getElementById('id_workflow').selectedIndex = wf_type;
+ document.getElementById('wf_selection_form').submit();
+ }
+</script>
+<div class='wf_create_div'>
+<button class="wf_create btn" onclick="cwf(0)">Create a Booking</button>
+<button class="wf_create btn" onclick="cwf(1)">Create a Pod</button>
+<button class="wf_create btn" onclick="cwf(2)">Configure a Pod</button>
+<button class="wf_create btn" onclick="cwf(3)">Create a Snapshot</button>
+{% if manager == True %}
+<button class="wf_continue btn" onclick="continue_wf()">Finish Unfinished Business</button>
+{% endif %}
+</div>
+
+<script type="text/javascript">
+ function cwf(type)
+ {
+ $.ajax({
+ type: "POST",
+ url: "/",
+ data: {"create":type},
+ beforeSend: function(request) {
+ request.setRequestHeader("X-CSRFToken",
+ $('input[name="csrfmiddlewaretoken"]').val()
+ );
+ }
+
+ }).done(function (data) {
+ window.location.replace("/wf/");
+ }).fail(function(jqxHR, textstatus) {
+ alert("Something went wrong...");});
+ }
+ function continue_wf()
+ {
+ window.location.replace("/wf/");
+ }
+
+ //success: window.location.replace("/wf/")
+
+</script>
+
+<div class="hidden_form" id="form_div">
+ <form method="post" action="" class="form" id="wf_selection_form">
+ {% csrf_token %}
+
+ <input type="hidden" id="landing_action">
+
+ <button type="submit" class="btn btn btn-success">
+ Confirm Edit
+ </button>
+ </form>
+</div>
+
+{% block vport_comm %}
+{% endblock %}
+{% endblock content %}
diff --git a/src/templates/dashboard/login.html b/src/templates/dashboard/login.html
new file mode 100644
index 0000000..d3aa4ad
--- /dev/null
+++ b/src/templates/dashboard/login.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+
+{% block content %}
+<h3>You Must Login To Do That</h3>
+
+<a href="/accounts/login">Login Here</a>
+
+{% endblock %}
diff --git a/src/templates/dashboard/multiple_select_filter_widget.html b/src/templates/dashboard/multiple_select_filter_widget.html
new file mode 100644
index 0000000..ed29ed6
--- /dev/null
+++ b/src/templates/dashboard/multiple_select_filter_widget.html
@@ -0,0 +1,402 @@
+<style>
+.object_class_wrapper {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ border: 0px;
+}
+.class_grid_wrapper {
+ border: 0px;
+ border-left: 1px;
+ border-right: 1px;
+ border-style: solid;
+ border-color: grey;
+ text-align: center;
+}
+.grid_wrapper {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+}
+.grid-item {
+ cursor: pointer;
+ border:2px;
+ border-style:none;
+ border-color:black;
+ border-radius: 5px;
+ margin:20px;
+ height: 200px;
+ padding: 7px;
+ box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow, background-color;
+ transition-duration: .2s;
+}
+
+.grid-item:hover {
+ box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: .2s;
+
+}
+
+.selected_node {
+ box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75);
+ background-color: #CCECD7;
+ transition-property: background-color;
+ transition-duration: .2s;
+}
+
+.disabled_node {
+ cursor: not-allowed;
+ background-color: #EFEFEF;
+ box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: .2s;
+}
+
+.disabled_node:hover {
+ box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75);
+}
+
+.cleared_node {
+ background-color: #FFFFFF;
+}
+
+.grid-item-header
+{
+ font-weight: bold;
+ font-size: 20px;
+ margin-top: 10px;
+}
+
+</style>
+<input name="filter_field" id="filter_field" type="hidden"/>
+<div id="grid_wrapper" class="grid_wrapper">
+{% for object_class, object_list in filter_objects %}
+<div class="class_grid_wrapper">
+ <div style="display:inline-block;margin:auto">
+ <h4>{{object_class}}</h4>
+ </div>
+ <div id="{{object_class}}" class="object_class_wrapper">
+ {% for obj in object_list %}
+ <div id="object_parent">
+ <div id="{{ obj.id|default:'not_provided' }}" class="grid-item">
+ <p class="grid-item-header">{{obj.name}}</p>
+ <p class="grid-item-description">{{obj.description}}</p>
+ <button type="button" class="btn btn-success grid-item-select-btn" onclick="processClick('{{obj.id}}', {% if obj.multiple %}true{% else %}false{% endif %});">{% if obj.multiple %}Add{% else %}Select{% endif %}</button>
+ </div>
+ <input type="hidden" name="{{obj.id}}_selected" value="false"/>
+ </div>
+ {% endfor %}
+ </div>
+ </div>
+{% endfor %}
+</div>
+
+<div id="dropdown_wrapper">
+</div>
+
+<script>
+var initialized = false;
+var mapping = {{ mapping|safe }};
+var items = {{ items|safe }};
+var result = {};
+var selection = {{selection_data|default_if_none:"null"|safe}};
+var dropdown_count = 0;
+
+{% if selection_data %}
+make_selection({{selection_data|safe}});
+{% endif %}
+
+function make_selection( selection_data ){
+ if(!initialized) {
+ init();
+ }
+ for(var k in selection_data) {
+ selected_items = selection_data[k];
+ for( var item in selected_items ){
+ var node = items[item];
+ if(!node['multiple']){
+ var input_value = selected_items[item];
+ if( input_value != 'false' ) {
+ select(node);
+ markAndSweep(node);
+ }
+ var div = document.getElementById(item)
+ var input = div.parentNode.getElementsByTagName("input")[0]
+ input.value = input_value;
+ updateResult(item);
+ } else {
+ make_multiple_selection(selected_items, item);
+ }
+ }
+ }
+}
+
+function make_multiple_selection(data, item_class){
+ var node = items[item_class];
+ select(node);
+ markAndSweep(node);
+ prepop_data = data[item_class];
+ for(var i=0; i<prepop_data.length; i++){
+ var div = add_item_prepopulate(node, prepop_data[i]);
+ updateObjectResult(div);
+ }
+}
+
+function markAndSweep(root){
+ for(var nodeId in items) {
+ node = items[nodeId];
+ node['marked'] = true; //mark all nodes
+ //clears grey background of everything
+ }
+
+ toCheck = [];
+ toCheck.push(root);
+
+ while(toCheck.length > 0){
+ node = toCheck.pop();
+ if(!node['marked']) {
+ //already visited, just continue
+ continue;
+ }
+ node['marked'] = false; //mark as visited
+ if(node['follow'] || node == root){ //add neighbors if we want to follow this node (labs)
+ var mappingId = node.id
+ var neighbors = mapping[mappingId];
+ for(var neighId in neighbors) {
+ neighId = neighbors[neighId];
+ var neighbor = items[neighId];
+ toCheck.push(neighbor);
+ }
+ }
+ }
+
+ //now remove all nodes still marked
+ for(var nodeId in items){
+ node = items[nodeId];
+ if(node['marked']){
+ disable(node);
+ }
+ }
+}
+
+function process(node) {
+ if(node['selected']) {
+ markAndSweep(node);
+ }
+ else {
+ var selected = []
+ //remember the currently selected, then reset everything and reselect one at a time
+ for(var nodeId in items) {
+ node = items[nodeId];
+ if(node['selected']) {
+ selected.push(node);
+ }
+ clear(node);
+
+ }
+ for(var i=0; i<selected.length; i++) {
+ node = selected[i];
+ select(node);
+ markAndSweep(selected[i]);
+ }
+ }
+}
+
+function select(node) {
+ elem = document.getElementById(node['id']);
+ node['selected'] = true;
+ elem.classList.remove('cleared_node')
+ elem.classList.remove('disabled_node')
+ elem.classList.add('selected_node')
+ var input = elem.parentNode.getElementsByTagName("input")[0];
+ input.disabled = false;
+ input.value = true;
+}
+
+function clear(node) {
+ elem = document.getElementById(node['id']);
+ node['selected'] = false;
+ node['selectable'] = true;
+ elem.classList.add('cleared_node')
+ elem.classList.remove('disabled_node')
+ elem.classList.remove('selected_node')
+ elem.parentNode.getElementsByTagName("input")[0].disabled = true;
+}
+
+function disable(node) {
+ elem = document.getElementById(node['id']);
+ node['selected'] = false;
+ node['selectable'] = false;
+ elem.classList.remove('cleared_node')
+ elem.classList.add('disabled_node')
+ elem.classList.remove('selected_node')
+ elem.parentNode.getElementsByTagName("input")[0].disabled = true;
+}
+
+function processClick(id, multiple){
+ if(!initialized){
+ init();
+ }
+ var element = document.getElementById(id);
+ var node = items[id];
+ if(!node['selectable']){
+ return;
+ }
+ if(multiple){
+ return processClickMultipleObject(node);
+ }
+ node['selected'] = !node['selected']; //toggle on click
+
+ if(node['selected']) {
+ select(node);
+ }
+ else {
+ clear(node);
+ }
+ process(node);
+ updateResult(id);
+}
+
+function processClickMultipleObject(node){
+ select(node);
+ add_item(node);
+ process(node);
+}
+
+function add_item(node){
+ return add_item_prepopulate(node, {});
+}
+
+inputs = []
+
+function restrictchars(input)
+{
+ if( input.validity.patternMismatch )
+ {
+ input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed");
+ input.reportValidity();
+ }
+
+ input.value = input.value.replace(/([^A-Za-z0-9-_.])+/g, "");
+
+ checkunique(input);
+}
+
+function checkunique(tocheck)
+{
+ val = tocheck.value;
+ for( var i = 0; i < inputs.length; i++ )
+ {
+ if( inputs[i].value == val && inputs[i] != tocheck)
+ {
+ tocheck.setCustomValidity("All hostnames must be unique");
+ tocheck.reportValidity();
+ return;
+ }
+ }
+ tocheck.setCustomValidity("");
+}
+
+function add_item_prepopulate(node, prepopulate){
+ inputs = [];
+ var div = document.createElement("DIV");
+ div.class = node['id'];
+ div.id = "dropdown_" + dropdown_count;
+ dropdown_count++;
+ var label = document.createElement("H5");
+ label.style['display'] = 'inline';
+ label.appendChild(document.createTextNode(node['name']));
+ div.appendChild(label);
+ for(var i=0; i<node['forms'].length; i++){
+ form = node['forms'][i];
+ var input = document.createElement("INPUT");
+ input.type = form['type'];
+ input.name = form['name'];
+ input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
+ input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
+ input.placeholder = form['placeholder'];
+ inputs.push(input);
+ input.onchange = function() { updateObjectResult(div); restrictchars(this); };
+ input.oninput = function() { restrictchars(this); };
+ if(form['name'] in prepopulate){
+ input.value = prepopulate[form['name']];
+ }
+ div.appendChild(input);
+ }
+ //add class id to dropdown object
+ var hiddenInput = document.createElement("INPUT");
+ hiddenInput.type = "hidden";
+ hiddenInput.name = "class";
+ hiddenInput.value = node['id'];
+ div.appendChild(hiddenInput);
+ button = document.createElement("BUTTON");
+ button.onclick = function(){
+ remove_dropdown(div.id);
+ }
+ button.type = "button";
+ button.appendChild(document.createTextNode("Remove"));
+ div.appendChild(button);
+ document.getElementById("dropdown_wrapper").appendChild(div);
+ return div;
+}
+
+function remove_dropdown(id){
+ var div = document.getElementById(id);
+ var parent = div.parentNode;
+ div.parentNode.removeChild(div);
+ //checks if we have removed last item in class
+ var deselect_class = true;
+ var div_inputs = div.getElementsByTagName("input");
+ var div_class = div_inputs[div_inputs.length-1].value;
+ var result_class = document.getElementById(div_class).parentNode.parentNode.id;
+ delete result[result_class][div.id];
+ for(var i=0; i<parent.children.length; i++){
+ var inputs = parent.children[i].getElementsByTagName("input");
+ var object_class = "";
+ for(var k=0; k<inputs.length; k++){
+ if(inputs[k].name == "class"){
+ object_class = inputs[k].value;
+ }
+ }
+ if(object_class == div_class){
+ deselect_class = false;
+ }
+ }
+ if(deselect_class){
+ clear(items[div_class]);
+ }
+}
+
+function updateResult(nodeId){
+ if(!initialized){
+ init();
+ }
+ if(!items[nodeId]['multiple']){
+ var node = document.getElementById(nodeId);
+ var value = {}
+ value[nodeId] = node.parentNode.getElementsByTagName("input")[0].value;
+ result[node.parentNode.parentNode.id][nodeId] = value;
+ }
+}
+
+function updateObjectResult(parentElem){
+ node_type = document.getElementById(parentElem.class).parentNode.parentNode.id;
+ input = {};
+ inputs = parentElem.getElementsByTagName("input");
+ for(var i in inputs){
+ var e = inputs[i];
+ input[e.name] = e.value;
+ }
+ result[node_type][parentElem.id] = input;
+}
+
+function init() {
+ for(nodeId in items) {
+ element = document.getElementById(nodeId);
+ node = items[nodeId];
+ result[element.parentNode.parentNode.id] = {}
+ }
+ initialized = true;
+}
+
+</script>
diff --git a/src/templates/dashboard/pdf.yaml b/src/templates/dashboard/pdf.yaml
new file mode 100644
index 0000000..297e04b
--- /dev/null
+++ b/src/templates/dashboard/pdf.yaml
@@ -0,0 +1,95 @@
+---
+version: {{version|default:"1.0"}}
+details:
+ pod_owner: {{details.owner}}
+ contact: {{details.contact}}
+ lab: {{details.lab}}
+ location: {{details.location}}
+ type: {{details.type}}
+ link: {{details.link}}
+
+jumphost:
+ name: {{jumphost.name}}
+ node:
+ type: {{jumphost.node.type}}
+ vendor: {{jumphost.node.vendor}}
+ model: {{jumphost.node.model}}
+ arch: {{jumphost.node.arch}}
+ cpus: {{jumphost.node.cpus}}
+ cpu_cflags: {{jumphost.node.cpu_cflags}}
+ cores: {{jumphost.node.cores}}
+ memory: {{jumphost.node.memory}}
+ disks:
+ {% for disk in jumphost.disks %}
+ - name: {{disk.name}}
+ disk_capacity: {{disk.capacity}}
+ disk_type: {{disk.type}}
+ disk_interface: {{disk.interface}}
+ disk_rotation: {{disk.rotation}}
+
+ {% endfor %}
+ os: {{jumphost.os}}
+ remote_params:
+ type: {{jumphost.remote.type}}
+ versions:
+ {% for version in jumphost.remote.versions %}
+ - {{version}}
+ {% endfor %}
+ user: {{jumphost.remote.user}}
+ pass: {{jumphost.remote.pass}}
+ remote_management:
+ type: {{jumphost.remote.type}}
+ versions:
+ {% for version in jumphost.remote.versions %}
+ - {{version}}
+ {% endfor %}
+ user: {{jumphost.remote.user}}
+ pass: {{jumphost.remote.pass}}
+ address: {{jumphost.remote.address}}
+ mac_address: {{jumphost.remote.mac_address}}
+ interfaces:
+ {% for interface in jumphost.interfaces %}
+ - name: {{interface.name}}
+ address: {{interface.address}}
+ mac_address: {{interface.mac_address}}
+ vlan: {{interface.vlan}}
+ {% endfor %}
+nodes:
+ {% for node in nodes %}
+ - name: {{node.name}}
+ node:
+ type: {{node.node.type}}
+ vendor: {{node.node.vendor}}
+ model: {{node.node.model}}
+ arch: {{node.node.arch}}
+ cpus: {{node.node.cpus}}
+ cpu_cflags: {{node.node.cpu_cflags}}
+ cores: {{node.node.cores}}
+ memory: {{node.node.memory}}
+ disks:
+ {% for disk in node.disks %}
+ - name: {{disk.name}}
+ disk_capacity: {{disk.capacity}}
+ disk_type: {{disk.type}}
+ disk_interface: {{disk.interface}}
+ disk_rotation: {{disk.rotation}}
+
+ {% endfor %}
+ remote_management:
+ type: {{node.remote.type}}
+ versions:
+ {% for version in node.remote.versions %}
+ - {{version}}
+ {% endfor %}
+ user: {{node.remote.user}}
+ pass: {{node.remote.pass}}
+ address: {{node.remote.address}}
+ mac_address: {{node.remote.mac_address}}
+ interfaces:
+ {% for interface in node.interfaces %}
+ - name: {{interface.name}}
+ address: {{interface.address}}
+ mac_address: {{interface.mac_address}}
+ vlan: {{interface.vlan}}
+ {% endfor %}
+ {% endfor %}
diff --git a/src/templates/dashboard/resource.html b/src/templates/dashboard/resource.html
index c9e5735..28e7998 100644
--- a/src/templates/dashboard/resource.html
+++ b/src/templates/dashboard/resource.html
@@ -2,6 +2,7 @@
{% load staticfiles %}
{% block extrahead %}
+ {{ block.super }}
<!-- Morris Charts CSS -->
<link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
@@ -50,8 +51,6 @@
var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}";
loadChartData(chart_id, utilization_url);
- var chart_id = "{{ resource.id }}_jenkins_utilization";
- var utilization_url = "{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}";
loadChartData(chart_id, utilization_url);
});
</script>
diff --git a/src/templates/dashboard/resource_all.html b/src/templates/dashboard/resource_all.html
index a770d4e..0b0d0d4 100644
--- a/src/templates/dashboard/resource_all.html
+++ b/src/templates/dashboard/resource_all.html
@@ -2,6 +2,7 @@
{% load staticfiles %}
{% block extrahead %}
+ {{ block.super }}
<!-- Morris Charts CSS -->
<link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
@@ -63,10 +64,6 @@
var chart_id = "{{ resource.id }}_booking_utilization";
var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}";
loadChartData(chart_id, utilization_url);
-
- var chart_id = "{{ resource.id }}_jenkins_utilization";
- var utilization_url = "{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}";
- loadChartData(chart_id, utilization_url);
{% endfor %}
});
</script>
diff --git a/src/templates/dashboard/resource_detail.html b/src/templates/dashboard/resource_detail.html
index 740dd25..79389f0 100644
--- a/src/templates/dashboard/resource_detail.html
+++ b/src/templates/dashboard/resource_detail.html
@@ -1,32 +1,4 @@
-{% load jenkins_filters %}
-
<div class="row">
- <div class="col-lg-3">
- <div class="panel panel-default">
- <div class="panel-heading">
- Jenkins Utilization
- <div class="pull-right">
- <div class="form-group">
- <select onchange="loadChartData('{{ resource.id }}_jenkins_utilization', this.value);">
- <option value="{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}">
- Last Week
- </option>
- <option value="{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=4 %}">
- Last Month
- </option>
- </select>
- </div>
- </div>
- </div>
- <div class="panel-body">
- <div class="flot-chart">
- <div class="flot-chart-content"
- id="{{ resource.id }}_jenkins_utilization"></div>
- </div>
- </div>
- </div>
- </div>
-
<div class="col-lg-9">
<div class="panel panel-default">
<div class="panel-heading">
@@ -141,30 +113,6 @@
</div>
</div>
</div>
- <div class="col-lg-3">
- <div class="panel panel-default">
- <div class="panel-heading">
- Jenkins Status
- </div>
- <div class="panel-body">
- <p>
- <b>Slave Name: </b>
- <a target='_blank'
- href={{ resource.slave.url }}>{{ resource.slave.name }}</a>
- </p>
- <p>
- <b>Status: </b>
- {{ resource.slave.status }}
- </p>
- <p>
- <b>Last Job: </b>
- <a href="{{ resource.slave.last_job_url }}">
- {{ resource.slave.last_job_name }}
- </a>
- </p>
- </div>
- </div>
- </div>
<div class="col-lg-6">
<div class="panel panel-default">
<div class="panel-heading">
diff --git a/src/templates/dashboard/searchable_select_multiple.html b/src/templates/dashboard/searchable_select_multiple.html
new file mode 100644
index 0000000..e7128b0
--- /dev/null
+++ b/src/templates/dashboard/searchable_select_multiple.html
@@ -0,0 +1,408 @@
+<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
+
+<div class="autocomplete" style="width:400px;">
+ <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="{{initial.name}}" oninput="search(this.value)"
+ {% if disabled %} disabled {% endif %}
+ >
+
+ <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;"
+ {% if disabled %} disabled {% endif %}
+ >
+ </input>
+
+ <ul id="drop_results"></ul>
+
+
+ <div id="default_entry_wrap" style="display: none;">
+ <div class="list_entry unremovable_list_entry">
+ <p id="default_text" class="full_name"></p>
+ <button class="btn-remove btn disabled">remove</button>
+ </div>
+ </div>
+
+ <div id="added_list">
+
+ </div>
+ <div id="added_counter" style="text-align: center; margin: 10px;"><p id="added_number" style="display: inline;">0</p><p style="display: inline;">/
+ {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} &infin; {% endif %}added</p></div>
+ <style>
+ #user_field {
+ font-size: 14pt;
+ width: 400px;
+ padding: 5px;
+
+ }
+
+ #drop_results{
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ max-height: 300px;
+ min-height: 0;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ border: solid 1px #ddd;
+ display: none;
+
+ }
+
+ #drop_results li a{
+ font-size: 14pt;
+ border: 1px solid #ddd;
+ background-color: #f6f6f6;
+ padding: 12px;
+ text-decoration: none;
+ display: block;
+ width: 400px;
+ }
+
+ .btn-remove {
+ float: right;
+ height: 30px;
+ margin: 4px;
+ }
+
+ .list_entry {
+ width: 400px;
+ border: 1px solid #ddd;
+ border-radius: 3px;
+ margin-top: 5px;
+ vertical-align: middle;
+ line-height: 40px;
+ height: 40px;
+ padding-left: 12px;
+ }
+
+ #drop_results li a:hover{
+ background-color: #ffffff;
+ }
+
+ .small_name {
+ display: inline-block;
+ }
+
+ .full_name {
+ display: inline-block;
+ }
+
+ </style>
+</div>
+
+<script type="text/javascript">
+ //flags
+ var show_from_noentry = {{show_from_noentry|default:"false"}};
+ var show_x_results = {{show_x_results|default:-1}};
+ var results_scrollable = {{results_scrollable|default:"false"}};
+ var selectable_limit = {{selectable_limit|default:-1}};
+ var field_name = "{{name|default:"users"}}";
+ var placeholder = "{{placeholder|default:"begin typing"}}";
+ var default_entry = "{{default_entry}}";
+
+ //needed info
+ var items = {{items|safe}}
+
+ //tries
+ var expanded_name_trie = {}
+ expanded_name_trie.isComplete = false;
+ var small_name_trie = {}
+ small_name_trie.isComplete = false;
+ var string_trie = {}
+ string_trie.isComplete = false;
+
+ var added_items = [];
+
+ var added_template = {{ added_list|default:"{}" }};
+
+ if( default_entry )
+ {
+ var default_entry_div = document.getElementById("default_entry_wrap");
+ default_entry_div.style.display = "inherit";
+
+ var entry_p = document.getElementById("default_text");
+ entry_p.innerText = default_entry;
+ }
+
+ init();
+
+ if( show_from_noentry )
+ {
+ search("");
+ }
+
+ function disable() {
+ var textfield = document.getElementById("user_field");
+ var drop = document.getElementById("drop_results");
+
+ textfield.disabled = "True";
+ drop.style.display = "none";
+
+ var btns = document.getElementsByClassName("btn-remove");
+ for( var i = 0; i < btns.length; i++ )
+ {
+ btns[i].classList.add("disabled");
+ }
+ }
+
+ function init() {
+ build_all_tries(items);
+
+ var initial = {{ initial|safe }};
+
+ for( var i = 0; i < initial.length; i++)
+ {
+ select_item(String(initial[i]));
+ }
+ if(initial.length == 1)
+ {
+ search(items[initial[0]]["small_name"]);
+ document.getElementById("user_field").value = items[initial[0]]["small_name"];
+ }
+ }
+
+ function build_all_tries(dict)
+ {
+ for( var i in dict )
+ {
+ add_item(dict[i]);
+ }
+ }
+
+ function add_item(item)
+ {
+ var id = item['id'];
+ add_to_tree(item['expanded_name'], id, expanded_name_trie);
+ add_to_tree(item['small_name'], id, small_name_trie);
+ add_to_tree(item['string'], id, string_trie);
+ }
+
+ function add_to_tree(str, id, trie)
+ {
+ inner_trie = trie;
+ while( str )
+ {
+ if( !inner_trie[str.charAt(0)] )
+ {
+ new_trie = {};
+ inner_trie[str.charAt(0)] = new_trie;
+ }
+ else
+ {
+ new_trie = inner_trie[str.charAt(0)];
+ }
+
+ if( str.length == 1 )
+ {
+ new_trie.isComplete = true;
+ new_trie.itemID = id;
+ }
+ inner_trie = new_trie;
+ str = str.substring(1);
+ }
+ }
+
+ function search(input)
+ {
+ if( input.length == 0 && !show_from_noentry){
+ dropdown([]);
+ return;
+ }
+ else if( input.length == 0 && show_from_noentry)
+ {
+ dropdown(items); //show all items
+ }
+ else
+ {
+ var trees = []
+ var tr1 = getSubtree(input, expanded_name_trie);
+ trees.push(tr1);
+ var tr2 = getSubtree(input, small_name_trie);
+ trees.push(tr2);
+ var tr3 = getSubtree(input, string_trie);
+ trees.push(tr3);
+ var results = collate(trees);
+ dropdown(results);
+ }
+ }
+
+ function getSubtree(input, given_trie)
+ {
+ /*
+ recursive function to return the trie accessed at input
+ */
+
+ if( input.length == 0 ){
+ return given_trie;
+ }
+
+ else{
+ var substr = input.substring(0, input.length - 1);
+ var last_char = input.charAt(input.length-1);
+ var subtrie = getSubtree(substr, given_trie);
+ if( !subtrie ) //substr not in the trie
+ {
+ return {};
+ }
+ var indexed_trie = subtrie[last_char];
+ return indexed_trie;
+ }
+ }
+
+ function serialize(trie)
+ {
+ /*
+ takes in a trie and returns a list of its item id's
+ */
+ var itemIDs = [];
+ if ( !trie )
+ {
+ return itemIDs; //empty, base case
+ }
+ for( var key in trie )
+ {
+ if(key.length > 1)
+ {
+ continue;
+ }
+ itemIDs = itemIDs.concat(serialize(trie[key]));
+ }
+ if ( trie.isComplete )
+ {
+ itemIDs.push( trie.itemID );
+ }
+
+ return itemIDs;
+ }
+
+ function collate(trees)
+ {
+ /*
+ takes a list of tries
+ returns a list of ids of objects that are available
+ */
+ results = [];
+ for( var i in trees )
+ {
+ var available_IDs = serialize(trees[i]);
+ for( var j=0; j<available_IDs.length; j++){
+ var itemID = available_IDs[j];
+ results[itemID] = items[itemID];
+ }
+ }
+ return results;
+ }
+
+ function dropdown(ids)
+ {
+ /*
+ takes in a mapping of ids to objects in items
+ and displays them in the dropdown
+ */
+ var drop = document.getElementById("drop_results");
+ while(drop.firstChild)
+ {
+ drop.removeChild(drop.firstChild);
+ }
+
+ for( var id in ids )
+ {
+ var result_entry = document.createElement("li");
+ var result_button = document.createElement("a");
+ var obj = items[id];
+ var result_text = document.createTextNode(obj['small_name'] + " : " + obj['expanded_name']);
+ result_button.appendChild(result_text);
+ result_button.setAttribute('onclick', 'select_item("' + obj['id'] + '")');
+ result_entry.appendChild(result_button);
+ drop.appendChild(result_entry);
+ }
+
+ if( !drop.firstChild )
+ {
+ drop.style.display = 'none';
+ }
+ else
+ {
+ drop.style.display = 'inherit';
+ }
+ }
+
+ function select_item(item_id)
+ {
+ //TODO make faster
+ var item = items[item_id];
+ if( (selectable_limit > -1 && added_items.length < selectable_limit) || selectable_limit < 0 )
+ {
+ if( added_items.indexOf(item) == -1 )
+ {
+ added_items.push(item);
+ }
+ }
+
+ update_selected_list();
+ document.getElementById("user_field").focus();
+ }
+
+ function remove_item(item_ref)
+ {
+
+ item = Object.values(items)[item_ref];
+ var index = added_items.indexOf(item);
+ added_items.splice(index, 1);
+
+ update_selected_list()
+ document.getElementById("user_field").focus();
+ }
+
+ function edit_item(item_id){
+ var wf_type = "{{wf_type}}";
+ parent.add_edit_wf(wf_type, item_id);
+ }
+
+ function update_selected_list()
+ {
+ document.getElementById("added_number").innerText = added_items.length;
+ selector = document.getElementById('selector');
+ selector.value = JSON.stringify(added_items);
+ added_list = document.getElementById('added_list');
+
+ while(selector.firstChild)
+ {
+ selector.removeChild(selector.firstChild);
+ }
+ while(added_list.firstChild)
+ {
+ added_list.removeChild(added_list.firstChild);
+ }
+
+ list_html = "";
+
+ for( var key in added_items )
+ {
+ item = added_items[key];
+
+ list_html += '<div class="list_entry"><p class="full_name">'
+ + item["expanded_name"]
+ + '</p><p class="small_name">, '
+ + item["small_name"]
+ + '</p><button onclick="remove_item('
+ + Object.values(items).indexOf(item)
+ + ')" class="btn-remove btn">remove</button>';
+ {% if edit %}
+ list_html += '<button onclick="edit_item('
+ + item['id']
+ + ')" class="btn-remove btn">edit</button>';
+ {% endif %}
+ list_html += '</div>';
+ }
+
+ added_list.innerHTML = list_html;
+ }
+
+</script>
+<style>
+ .full_name {
+ display: inline-block;
+ }
+ .small_name {
+ display: inline-block;
+ }
+</style>
diff --git a/src/templates/dashboard/table.html b/src/templates/dashboard/table.html
index d59f0e3..b3f4b5f 100644
--- a/src/templates/dashboard/table.html
+++ b/src/templates/dashboard/table.html
@@ -2,6 +2,7 @@
{% load staticfiles %}
{% block extrahead %}
+ {{ block.super }}
<!-- DataTables CSS -->
<link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
rel="stylesheet">