summaryrefslogtreecommitdiffstats
path: root/dashboard/src/templates/booking
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2018-10-10 16:06:47 -0400
committerParker Berberian <pberberian@iol.unh.edu>2018-10-15 13:16:11 -0400
commit25275685e9a735e51fae8b1a936ba5733f6fb770 (patch)
treec3af41d1986c0812ca7ed059c16c7c7c7bd354cf /dashboard/src/templates/booking
parentf2e83b24260b018bb7cc30421eda6c9a8cebc309 (diff)
Lab as a Service 2.0
See changes here: https://wiki.opnfv.org/display/INF/Pharos+Laas Change-Id: I59ada5f98e70a28d7f8c14eab3239597e236ca26 Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu> Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'dashboard/src/templates/booking')
-rw-r--r--dashboard/src/templates/booking/booking_calendar.html65
-rw-r--r--dashboard/src/templates/booking/booking_detail.html361
-rw-r--r--dashboard/src/templates/booking/booking_grid_item.html11
-rw-r--r--dashboard/src/templates/booking/booking_list.html6
-rw-r--r--dashboard/src/templates/booking/stats.html60
-rw-r--r--dashboard/src/templates/booking/steps/booking_confirm.html25
-rw-r--r--dashboard/src/templates/booking/steps/booking_meta.html66
-rw-r--r--dashboard/src/templates/booking/steps/resource_select.html73
-rw-r--r--dashboard/src/templates/booking/steps/swconfig_select.html73
9 files changed, 667 insertions, 73 deletions
diff --git a/dashboard/src/templates/booking/booking_calendar.html b/dashboard/src/templates/booking/booking_calendar.html
index b60db3c..349cb0a 100644
--- a/dashboard/src/templates/booking/booking_calendar.html
+++ b/dashboard/src/templates/booking/booking_calendar.html
@@ -4,6 +4,8 @@
{% load bootstrap3 %}
{% block extrahead %}
+ {{ block.super }}
+
<link href="{% static "bower_components/fullcalendar/dist/fullcalendar.css" %}"
rel='stylesheet'/>
<link href="{% static "bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" %}"
@@ -84,6 +86,7 @@
hidediv.parentNode.insertBefore(par,hidediv.nextSibling);
}
</script>
+ {% bootstrap_field form.opsys %}
{% bootstrap_field form.purpose %}
{% bootstrap_field form.installer %}
{% bootstrap_field form.scenario %}
@@ -95,6 +98,15 @@
{% endbuttons %}
</form>
</div>
+ <script type="text/javascript">
+ //Check if current view is an edit or a standard booking view
+ if(window.location.href.includes('edit')) {
+ var element = document.getElementById("bookingform");
+ element.parentNode.removeChild(element); }
+ else {
+ var element = document.getElementById("bookingeditform");
+ element.parentNode.removeChild(element); }
+ </script>
{% else %}
<p>Please
<a href="{% url 'account:login' %}">
@@ -132,8 +144,61 @@
var booking_detail_prefix = "{% url 'booking:detail_prefix' %}";
var booking_delete_prefix = "{% url 'booking:delete_prefix' %}";
var user_timezone = "{{ request.user.userprofile.timezone }}"
+ {% autoescape off %}
+ var sup_installer_dict = {{ installer_filter }}
+ var sup_scenario_dict = {{ scenario_filter }}
+ {% endautoescape %}
+
</script>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ $("#id_installer").children().hide();
+ $("#id_scenario").children().hide();
+ });
+
+ function installerHider() {
+ dropFilter("id_installer", sup_installer_dict, "id_opsys");
+ scenarioHider();
+ }
+ document.getElementById('id_opsys').addEventListener('change', installerHider);
+
+ function scenarioHider() {
+ dropFilter("id_scenario", sup_scenario_dict, "id_installer");
+ }
+ document.getElementById('id_installer').addEventListener('change', scenarioHider);
+ function dropFilter(target, target_filter, master) {
+ ob = document.getElementById(target);
+
+ for(var i=0; i<ob.options.length; i++) {
+ if ( ob.options[i].text == '---------' ) {
+ ob.selectedIndex = i;
+ }
+ }
+
+ targ_id = "#" + target;
+ $(targ_id).children().hide();
+ var drop = document.getElementById(master);
+ var opts = target_filter[drop.options[drop.selectedIndex].value];
+ if (!opts) {
+ opts = {};
+ }
+ var emptyMap = {}
+
+ var map = Object.create(null);
+ for (var i = 0; i < opts.length; i++) {
+ var j = opts[i];
+ map[j] = true;
+ }
+
+ for (var i = 0; i < document.getElementById(target).childNodes.length; i++) {
+ if (document.getElementById(target).childNodes[i].value in opts && !(document.getElementById(target).childNodes[i].value in emptyMap) ) {
+ document.getElementById(target).childNodes[i].style.display = "inherit";
+ }
+ }
+ }
+
+ </script>
<script src={% static "bower_components/moment/moment.js" %}></script>
<script src={% static "bower_components/fullcalendar/dist/fullcalendar.js" %}></script>
<script type="text/javascript"
diff --git a/dashboard/src/templates/booking/booking_detail.html b/dashboard/src/templates/booking/booking_detail.html
index 4628a62..126aaab 100644
--- a/dashboard/src/templates/booking/booking_detail.html
+++ b/dashboard/src/templates/booking/booking_detail.html
@@ -1,74 +1,293 @@
-{% load jira_filters %}
-
-<script type="text/javascript">
- function deleteClick(id) {
- var booking_delete_url = booking_delete_prefix + id
- $.ajax({
- url: booking_delete_url,
- type: 'get',
- success: function (data) {
- $('#booking_delete_content').html(data);
- },
- failure: function (data) {
- alert('Error finding that booking');
- }
- });
- $('#booking_delete_modal').modal('show');
- }
-</script>
-
-<p>
- <b>Resource: </b>
- <a href="{{ booking.resource.url }}">
- {{ booking.resource.name }}
- </a>
-</p>
-<p>
- <b>User: </b> {{ booking.user.username }}
-</p>
-<p>
- <b>Start: </b> {{ booking.start }}
-</p>
-<p>
- <b>End: </b> {{ booking.end }}
-</p>
-<p>
- <b>Purpose: </b> {{ booking.purpose }}
-</p>
-<p>
- <b>Operating System: </b> {{ booking.opsys }}
-</p>
-<p>
- <b>Installer: </b> {{ booking.installer }}
-</p>
-<p>
- <b>Scenario: </b> {{ booking.scenario }}
-</p>
-<p>
- <b>Extensions Remaining: </b> {{ booking.ext_count }}
-</p>
-
-{% if user.is_authenticated %}
-{% if user.get_username == booking.user.username %}
-<p>
- <a href="{% url 'booking:edit' booking_id=booking.id resource_id=booking.resource.id %}" class="btn btn btn-success">
- Edit Booking
- </a>
- <a href="javascript:deleteClick({{ booking.id }})" class="btn btn btn-danger">
- Delete Booking
- </a>
- <div id="booking_delete_modal" class="modal fade" role="dialog">
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal">&times;</button>
- <h4 class="modal-title">Delete Booking</h4>
- </div>
- <div class="modal-body" id="booking_delete_content">
- </div>
+{% 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 %}
+<div class="container-fluid">
+ <div class="row">
+ <div class="col-lg-6">
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Overview</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>Purpose</td>
+ <td>{{ booking.purpose }}</td>
+ </tr>
+ <tr>
+ <td>Project</td>
+ <td>{{ booking.project }}</td>
+ </tr>
+ <tr>
+ <td>Start Time</td>
+ <td>{{ booking.start }}</td>
+ </tr>
+ <tr>
+ <td>End Time</td>
+ <td>{{ booking.end }}</td>
+ </tr>
+ <tr>
+ <td>Pod Definition</td>
+ <td>{{ booking.resource.template }}</td>
+ </tr>
+ <tr>
+ <td>Pod Configuration</td>
+ <td>{{ booking.config_bundle }}</td>
+ </tr>
+ <tr>
+ <td>Lab Deployed At</td>
+ <td>{{ booking.lab }}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="row">
+
+ <div class="col-lg-6">
+
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Pod</h4>
+
+ <a data-toggle="collapse" data-target="#pod_panel" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+ <div class="panel-body pod_panel" id="pod_panel">
+ <table class="table">
+ {% for host in booking.resource.hosts.all %}
+ <tr>
+ <td><h4>{{host.template.resource.name}}</h4></td>
+ <td>
+ <table class="table">
+ <tr>
+ <td>Hostname:</td>
+ <td>{{host.template.resource.name}}</td>
+ </tr>
+ <tr>
+ <td>Profile:</td>
+ <td>{{host.name}}</td>
+ </tr>
+ <tr>
+ <td>Role:</td>
+ <td>{{host.config.opnfvRole}}</td>
+ </tr>
+ <tr>
+ <td>Image:</td>
+ <td>{{host.config.image}}</td>
+ </tr>
+ <tr>
+ <td>RAM:</td>
+ <td>{{host.profile.ramprofile.first.amount}}G,
+ {{host.profile.ramprofile.first.channels}} channels</td>
+ </tr>
+ <tr>
+ <td>CPU:</td>
+ <td>
+ <table class="table">
+ <tr>
+ <td>Arch:</td>
+ <td>{{host.profile.cpuprofile.first.architecture}}</td>
+ </tr>
+ <tr>
+ <td>Cores:</td>
+ <td>{{host.profile.cpuprofile.first.cores}}</td>
+ </tr>
+ <tr>
+ <td>Sockets:</td>
+ <td>{{host.profile.cpuprofile.first.cpus}}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td>DISK:</td>
+ <td>
+ <table class="table">
+ <tr>
+ <td>Size:</td>
+ <td>{{host.profile.diskprofile.first.size}}GiB</td>
+ </tr>
+ <tr>
+ <td>Type:</td>
+ <td>{{host.profile.diskprofile.first.media_type}}</td>
+ </tr>
+ <tr>
+ <td>Mount Point:</td>
+ <td>{{host.profile.diskprofile.first.name}}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td>Interfaces:</td>
+ <td>
+ <style>
+ .borderless td {
+ border: none !important;
+ }
+ </style>
+ <table class="table">
+ {% for intprof in host.profile.interfaceprofile.all %}
+ <tr>
+ <td>
+ <table class="table borderless">
+ <tr>
+ <td>Name:</td>
+ <td>{{intprof.name}}</td>
+ </tr>
+ <tr>
+ <td>Speed:</td>
+ <td>{{intprof.speed}}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ </td>
+ </tr>
+
+
+ </table>
+
+ </td>
+ {% endfor %}
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-lg-6">
+
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">PDF</h4>
+ <a data-toggle="collapse" data-target="#pdf_panel" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+
+ <div class="panel-body" id="pdf_panel" style="padding: 0px;">
+ <pre class="prettyprint lang-yaml" style="margin: 0px; padding: 0px; border: none;">
+
+ {{pdf}}
+
+
+ </pre>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+
+
+ <div class="col-lg-6">
+ <div class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <h4 style="display: inline;">Deployment Progress</h4>
+ <p style="display: inline; margin-left: 10px;"> These are the different tasks that have to be completed before your deployment is ready</p>
+ <a data-toggle="collapse" data-target="#panel_tasks" class="btn pull-right" style="line-height: 1;" >Expand</a>
+ </div>
+
+ <div class="panel-body" id="panel_tasks">
+ <table class="table">
+ <style>
+ .progress {
+ display: inline-block;
+ border: 3px solid #f3f3f3;
+ border-radius: 50%;
+ border-top: 3px solid #12aebb;
+ width: 20px;
+ height: 20px;
+ -webkit-animation: spin 2s linear infinite; /* Safari */
+ animation: spin 2s linear infinite;
+ }
+
+ @keyframes spin {
+ 0% {transform: rotate(0deg);}
+ 100% {transform: rotate(360deg);}
+ }
+
+ .new {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: #f3f3f3;
+ border-radius: 50%;
+ animation: fadeInOut 1s infinite alternate;
+
+
+ }
+ @keyframes fadeInOut {
+ from { opacity: 0;}
+ }
+ .done {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: #40B976;
+ border-radius: 50%;
+ }
+ </style>
+ <tr>
+ <th></th>
+ <th>Status</th>
+ <th>Lab Response</th>
+ <th>Type</th>
+ </tr>
+ {% for task in booking.job.get_tasklist %}
+ <tr>
+ <td>
+ {% if task.status < 100 %}
+ <div class="new"></div>
+ {% elif task.status < 200 %}
+ <div class="progress"></div>
+ {% else %}
+ <div class="done"></div>
+ {% endif %}
+ </td>
+
+
+ <td>
+ {% if task.status < 100 %}
+ PENDING
+ {% elif task.status < 200 %}
+ IN PROGRESS
+ {% else %}
+ DONE
+ {% endif %}
+ </td>
+ <td>
+
+ {% if task.message %}
+ {% if task.type_str == "Access Task" and user_id != task.config.user.id %}
+ Message from Lab: <pre>--secret--</pre>
+ {% else %}
+ Message from Lab: <pre>{{ task.message }}</pre>
+ {% endif %}
+ {% else %}
+ No response provided (yet)
+ {% endif %}
+ </td>
+ <td>
+ {{ task.type_str }}
+
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
</div>
</div>
+
+ </div>
</div>
-</p>
-{% endif %}
-{% endif %}
+</div>
+
+
+{% endblock content %}
diff --git a/dashboard/src/templates/booking/booking_grid_item.html b/dashboard/src/templates/booking/booking_grid_item.html
new file mode 100644
index 0000000..3c72fd2
--- /dev/null
+++ b/dashboard/src/templates/booking/booking_grid_item.html
@@ -0,0 +1,11 @@
+<p>{{ id|default:"id" }}</p>
+<p>{{ lab|default:"lab" }}</p>
+<p>{{ resource_name|default:"resource name" }}</p>
+<p>{{ start|default:"start" }}</p>
+<p>{{ end|default:"end" }}<p>
+<p>Collaborators:</p>
+<ul>
+ {% for collaborator in collaborators %}
+ <li>{{ collaborator }}</li>
+ {% endfor %}
+</ul>
diff --git a/dashboard/src/templates/booking/booking_list.html b/dashboard/src/templates/booking/booking_list.html
index ccdc46d..a245450 100644
--- a/dashboard/src/templates/booking/booking_list.html
+++ b/dashboard/src/templates/booking/booking_list.html
@@ -1,7 +1,9 @@
{% extends "base.html" %}
{% load staticfiles %}
+{% load bootstrap3 %}
{% block extrahead %}
+ {{ block.super }}
<!-- DataTables CSS -->
<link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
rel="stylesheet">
@@ -41,8 +43,8 @@
<script type="text/javascript">
$(document).ready(function () {
$('#table').DataTable({
- scrollX: true,
- });
+ scrollX: true,
+ });
});
</script>
{% endblock extrajs %}
diff --git a/dashboard/src/templates/booking/stats.html b/dashboard/src/templates/booking/stats.html
new file mode 100644
index 0000000..abb153b
--- /dev/null
+++ b/dashboard/src/templates/booking/stats.html
@@ -0,0 +1,60 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block extrahead %}
+{{ block.super }}
+<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
+<script>
+function drawGraph(data, graph_id, graph_title){
+ var container = document.getElementById(graph_id);
+ var plot_data = { x: data[0],
+ y: data[1],
+ fill: "tonexty",
+ type: "scatter",
+ mode: "none",
+ };
+ var layout = {
+ title: graph_title
+ };
+ Plotly.newPlot(container, [plot_data], layout);
+}
+function getData(){
+ var req = new XMLHttpRequest();
+ var url = "/booking/stats/json";
+ var day_input = document.getElementById("number_days");
+ var days = day_input.value;
+ //var days = document.getElementById("number_days").value;
+ if(days){
+ url = url + "?days=" + days;
+ }
+ req.onreadystatechange = function(){
+ if( req.readyState == XMLHttpRequest.DONE) {
+ var data = JSON.parse(req.responseText);
+ drawGraph(data["booking"], "booking_graph_container", "Active Bookings");
+ drawGraph(data["user"], "user_graph_container", "Active Users");
+ }
+ }
+ req.open("GET", url, true);
+ req.send();
+}
+</script>
+{% endblock %}
+
+{% block content %}
+ <p>Number of days to plot: </p>
+ <input id="number_days" type="number" min="1" step="1"/>
+ <button onclick="getData();">Submit</button>
+ <div id="all_graph_container">
+ <div id="booking_graph_wrapper">
+ <div id="booking_graph_container"/>
+ </div>
+ <div id="user_graph_wrapper" >
+ <div id="user_graph_container"/>
+ </div>
+ </div>
+ <script>
+var data = {{data|safe}};
+drawGraph(data["booking"], "booking_graph_container", "Active Bookings");
+drawGraph(data["user"], "user_graph_container", "Active Users");
+ </script>
+{% endblock content %}
diff --git a/dashboard/src/templates/booking/steps/booking_confirm.html b/dashboard/src/templates/booking/steps/booking_confirm.html
new file mode 100644
index 0000000..9c7e951
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/booking_confirm.html
@@ -0,0 +1,25 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap3 %}
+
+{% block content %}
+
+<style>
+ #form_div {
+ width: 100%;
+ padding: 5%;
+ text-align: center;
+ }
+</style>
+
+<div id="form_div">
+<p>confirm booking</p>
+<p>{{info|default:"booking info"}}</p>
+<form action="/wf/workflow/" method="post">
+ {% csrf_token %}
+ {{form|default:"<p> No Form Loaded</p>"}}
+ <input type="submit" value="Submit" style="display: none;"/>
+</form>
+</div>
+{% endblock content %} \ No newline at end of file
diff --git a/dashboard/src/templates/booking/steps/booking_meta.html b/dashboard/src/templates/booking/steps/booking_meta.html
new file mode 100644
index 0000000..fa8f7a9
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/booking_meta.html
@@ -0,0 +1,66 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap3 %}
+
+{% block content %}
+
+<style>
+ .bkmeta_panel {
+ padding: 5%;
+ }
+
+ .panel{
+ padding: 5%;
+ /*border: solid 1px black;*/
+ }
+
+ .panel_wrap {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 45% 10% 45%;
+ border: none;
+ }
+</style>
+
+{% bootstrap_form_errors form type='non_fields' %}
+<form id="booking_meta_form" action="/wf/workflow/" method="POST" class="form">
+{% csrf_token %}
+<div id="form_div">
+ <div class="panel_wrap">
+ <div class="panel bkmeta_panel">
+ {% bootstrap_field form.purpose %}
+ {% bootstrap_field form.project %}
+ {% bootstrap_field form.length %}
+ <p style="display:inline;">Days: </p><output id="daysout" style="display:inline;">0</output>
+ <script>
+ document.getElementById("id_length").setAttribute("oninput", "daysout.value=this.value");
+ document.getElementById("daysout").value = document.getElementById("id_length").value;
+ </script>
+ {% bootstrap_field form.info_file %}
+ <p>You must provide a url to your project's INFO.yaml file if you are a PTL and you are trying to book a POD with multiple servers in it.</p>
+ </div>
+ <div class="panel panel_center">
+ </div>
+ <div class="panel">
+ {% bootstrap_field form.users %}
+ </div>
+
+ {% buttons %}
+ <button type="submit" style="display: none;" class="btn btn-success">Confirm</button>
+ {% endbuttons %}
+ </div>
+</div>
+</form>
+{% endblock content %}
+
+{% block onleave %}
+var ajaxForm = $("#booking_meta_form");
+var formData = ajaxForm.serialize();
+console.log(formData);
+req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem submitting form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/booking/steps/resource_select.html b/dashboard/src/templates/booking/steps/resource_select.html
new file mode 100644
index 0000000..7ccceb3
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/resource_select.html
@@ -0,0 +1,73 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap3 %}
+
+{% block content %}
+
+<style>
+ #resource_form_div {
+ width: 100%;
+ padding: 5%;
+ }
+
+ .panel {
+ /*border: solid 1px black;*/
+ border: none;
+ }
+ .select_panels {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 45% 10% 45%;
+
+ }
+
+ .panel_center {
+ text-align: center;
+ border: none;
+
+
+ }
+ .panel_center p{
+ font-size: 20pt;
+ }
+</style>
+
+<div id="resource_form_div">
+ <div class="select_panels">
+ <div class="panel_chooser panel">
+ <form id="resource_select_form" method="post" action="" class="form" id="resourceselectorform">
+ {% csrf_token %}
+ {{ form|default:"<p>no form loaded</p>" }}
+ {% buttons %}
+
+ {% endbuttons %}
+ </form>
+ </div>
+ <div class="panel_center panel"><p>OR</p></div>
+ <div class="panel_add panel">
+ <button class="btn {% if disabled %} disabled {% endif %}"
+ style="width: 100%; height: 100%;"
+ {% if not disabled %}onclick="parent.add_wf(1)"
+ {% endif %}>Create Resource</button>
+ </div>
+
+</div>
+</div>
+<script>
+ {% if disabled %}
+ disable();
+ {% endif %}
+</script>
+
+{% endblock content %}
+{% block onleave %}
+var form = $("#resource_select_form");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}
+
diff --git a/dashboard/src/templates/booking/steps/swconfig_select.html b/dashboard/src/templates/booking/steps/swconfig_select.html
new file mode 100644
index 0000000..15c79d8
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/swconfig_select.html
@@ -0,0 +1,73 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap3 %}
+
+{% block content %}
+
+<style>
+ #sw_form_div {
+ width: 100%;
+ padding: 5%;
+ }
+
+ .panel {
+ /*border: solid 1px black;*/
+ border: none;
+ }
+ .select_panels {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 45% 10% 45%;
+
+ }
+
+ .panel_center {
+ text-align: center;
+ border: none;
+
+
+ }
+ .panel_center p{
+ font-size: 20pt;
+ }
+</style>
+
+<div id="sw_form_div">
+ <div class="select_panels">
+ <div class="panel_chooser panel">
+ <form id="software_select_form" method="post" action="" class="form" id="swselectorform">
+ {% csrf_token %}
+ {{ form|default:"<p>no form loaded</p>" }}
+ {% buttons %}
+
+ {% endbuttons %}
+ </form>
+ </div>
+ <div class="panel_center panel"><p>OR</p></div>
+ <div class="panel_add panel">
+ <button class="btn {% if disabled %} disabled {% endif %}"
+ style="width: 100%; height: 100%;"
+ {% if not disabled %}onclick="parent.add_wf(2)"
+ {% endif %}>Create Config</button>
+ </div>
+ <script>
+ {% if disabled %}
+ disable();
+ {% endif %}
+ </script>
+
+</div>
+</div>
+
+{% endblock content %}
+
+{% block onleave %}
+var form = $("#software_select_form");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}