diff --git a/tuskar_boxes/static/tuskar_boxes/js/tuskar.boxes.js b/tuskar_boxes/static/tuskar_boxes/js/tuskar.boxes.js
new file mode 100644
index 0000000..a2bb326
--- /dev/null
+++ b/tuskar_boxes/static/tuskar_boxes/js/tuskar.boxes.js
@@ -0,0 +1,100 @@
+tuskar.boxes = (function () {
+ 'use strict';
+
+ var module = {};
+
+ module.init = function () {
+ if ($('div.boxes-available-roles').length === 0) {
+ // Only activate on a page that has the right classes.
+ return;
+ }
+
+ function get_role_counts($flavor) {
+ var roles = {};
+ $flavor.find('div.boxes-drop-roles div.boxes-role').each(function () {
+ var $this = $(this);
+ var name = $this.data('name');
+ var count = +$this.find('input.number-picker').val();
+ roles[name] = count;
+ });
+ return roles;
+ }
+
+ function update_boxes() {
+ $('div.boxes-flavor').each(function () {
+ var $flavor = $(this);
+ var roles = get_role_counts($flavor);
+ var role_names = Object.getOwnPropertyNames(roles);
+ var count = 0;
+ var role = 0;
+ $flavor.find('div.boxes-nodes div.boxes-node').each(function () {
+ var $this = $(this);
+ $this.removeClass('boxes-role-controller boxes-role-compute boxes-role-cinder-storage boxes-role-swift-storage');
+ while (count >= roles[role_names[role]]) {
+ role += 1;
+ count = 0;
+ }
+ if (!role_names[role]) {
+ $(this).html('free');
+ } else {
+ $this.addClass('boxes-role-' + role_names[role]).html(' ');
+ }
+ count += 1;
+ });
+ });
+ }
+
+ $('div.boxes-role').draggable({
+ revert: 'invalid',
+ helper: 'clone',
+ zIndex: 1000,
+ opacity: 0.5
+ });
+ $('div.boxes-drop').droppable({
+ accept: 'div.boxes-role',
+ activeClass: 'boxes-drop-active',
+ hoverClass: 'boxes-drop-hover',
+ tolerance: 'touch',
+ drop: function (ev, ui) {
+ ui.draggable.appendTo($(this).parent().prev('.boxes-drop-roles'));
+ var $count = ui.draggable.find('input.number-picker');
+ if (+$count.val() < 1) { $count.val(1); }
+ ui.draggable.find('input.boxes-flavor'
+ ).val($(this).closest('.boxes-flavor').data('flavor'));
+ $count.trigger('change');
+ window.setTimeout(update_boxes, 0);
+ }
+ });
+ $('div.boxes-available-roles').droppable({
+ accept: 'div.boxes-role',
+ activeClass: 'boxes-drop-active',
+ hoverClass: 'boxes-drop-hover',
+ tolerance: 'touch',
+ drop: function (ev, ui) {
+ ui.draggable.appendTo(this);
+ ui.draggable.find('input.boxes-flavor').val('');
+ ui.draggable.find('input.number-picker').trigger('change').val(0);
+ window.setTimeout(update_boxes, 0);
+ }
+ });
+
+ update_boxes();
+ $('input.number-picker').change(update_boxes);
+
+ $('.boxes-roles-menu li a').click(function () {
+ var name = $(this).data('role');
+ var $drop = $(this).closest('.boxes-drop-group').prev('.boxes-drop-roles');
+ var $role = $('.boxes-role[data-name="' + name + '"]');
+ var $count = $role.find('input.number-picker');
+ var $flavor = $role.find('input.boxes-flavor');
+ $role.appendTo($drop);
+ if (+$count.val() < 1) { $count.val(1); }
+ $flavor.val($drop.closest('.boxes-flavor').data('flavor'));
+ $count.trigger('change');
+ window.setTimeout(update_boxes, 0);
+ });
+ };
+
+ horizon.addInitFunction(module.init);
+ return module;
+} ());
diff --git a/tuskar_boxes/static/tuskar_boxes/scss/tuskar_boxes.scss b/tuskar_boxes/static/tuskar_boxes/scss/tuskar_boxes.scss
new file mode 100644
index 0000000..428cb19
--- /dev/null
+++ b/tuskar_boxes/static/tuskar_boxes/scss/tuskar_boxes.scss
@@ -0,0 +1,98 @@
+.boxes-roles-menu {
+ position: absolute;
+ z-index: 1000;
+ width: 100%;
+ left: 0;
+ top: 48px;
+}
+.boxes-node {
+ display: inline-block;
+ width: 60px;
+ height: 60px;
+ border-radius: 2px;
+ border: 1px solid #999;
+ background: #eee;
+ margin: 0 4px 4px 0;
+ text-align: center;
+ color: #666;
+ padding: 20px 4px 0 4px;
+}
+.boxes-available-roles {
+ border-radius: 4px;
+ border: 1px dashed #666;
+ min-height: 42px;
+ min-width: 120px;
+ display: inline-block;
+ padding: 4px 0 0 4px;
+}
+.boxes-role {
+ opacity: 0.75;
+ padding: 6px;
+ border: 1px solid;
+ cursor: move;
+ border-radius: 2px;
+ background-color: #fce94f;
+ border-color: #edd400;
+ color: #c4a000;
+ margin: 0 0 4px 0;
+}
+.boxes-available-roles .boxes-role {
+ display: inline-block;
+ text-align: center;
+ width: 120px;
+ margin: 0 4px 4px 0;
+ border: 1px solid #999;
+ background: #eee;
+ color: #666;
+}
+.boxes-available-roles .boxes-role .form-control {
+ display: none;
+}
+.boxes-role-controller {
+ background-color: #fcaf3e;
+ border-color: #f57900;
+ color: #ce5c00;
+}
+.boxes-role-compute {
+ background-color: #8ae234;
+ border-color: #73d216;
+ color: #4e9a06;
+}
+.boxes-role-swift-storage {
+ background-color: #729fcf;
+ border-color: #3465a4;
+ color: #204a87;
+}
+.boxes-role-cinder-storage {
+ background-color: #ad7fa8;
+ border-color: #75507b;
+ color: #5c3566;
+}
+.boxes-role .number_picker {
+ border-color: inherit;
+}
+.boxes-role .number_picker,
+.boxes-role .number_picker a {
+ color: inherit;
+}
+.boxes-drop-group {
+ width: 100%;
+}
+.boxes-drop {
+ position: relative;
+ padding: 6px;
+ border: 1px dashed;
+ text-align: center;
+ border-radius: 4px;
+ border-color: #666;
+ color: #444;
+ cursor: pointer;
+}
+.boxes-drop-active {
+ background-color: #eee;
+ border-style: solid;
+}
+.boxes-drop-hover {
+ background-color: #999;
+ border-style: solid;
+}
diff --git a/tuskar_boxes/templates/tuskar_boxes/overview/index.html b/tuskar_boxes/templates/tuskar_boxes/overview/index.html
index 034a3ed..0ad1c29 100644
--- a/tuskar_boxes/templates/tuskar_boxes/overview/index.html
+++ b/tuskar_boxes/templates/tuskar_boxes/overview/index.html
@@ -2,6 +2,21 @@
{% load i18n %}
{% load url from future %}
+{% block css %}
+ {{block.super}}
+
+ {% load compress %}
+ {% compress css %}
+
+ {% endcompress %}
+{% endblock %}
+
+{% block js %}
+ {{ block.super }}
+
+{% endblock %}
+
+
{% block title %}{% trans 'My OpenStack Deployment' %}{% endblock %}
{% block page_header %}
diff --git a/tuskar_boxes/templates/tuskar_boxes/overview/role_nodes_edit.html b/tuskar_boxes/templates/tuskar_boxes/overview/role_nodes_edit.html
index 812bb48..784db89 100644
--- a/tuskar_boxes/templates/tuskar_boxes/overview/role_nodes_edit.html
+++ b/tuskar_boxes/templates/tuskar_boxes/overview/role_nodes_edit.html
@@ -69,193 +69,3 @@
{% trans "Save changes" %}
-
-
-
-