Create/edit flavors directly in the resource class form.

The formset in the resource class create/edit form now starts empty,
and allows the user to create or edit the flavors of that class
directly. Flavor templates are no longer used.

The formset starts with a single empty row at the end, for adding a new
flavor, and a "Delete" column with checkboxes for deleting existing
flavors.

If javascript is available, there is a link for adding more empty rows
and the empty line from the end is removed, and the checkboxes are
replaced with a cross icon, for deleting the rows instantly.

All traces of flavor templates have been removed from tuskar-ui.

The Max VMs field has been temporarily removed -- it will be added in
further iterations.

Change-Id: I1ff4c9f90657c62e7c5f45a4b7874524b9df204c
Implements: blueprint remove-flavor-templates
This commit is contained in:
Radomir Dopieralski
2013-09-03 15:12:19 +02:00
parent ae0d78c8f8
commit d491e06d9a
32 changed files with 351 additions and 1441 deletions

View File

@@ -131,7 +131,7 @@
<tr>
{% for flavor in resource_class.list_flavors %}
<td class="flavor_usage_label">
<a href="{% url 'horizon:infrastructure:resource_management:flavor_templates:detail' flavor.id %}">{{ flavor.name }}</a>
{{ flavor.name }}
</td>
{% endfor %}
</tr>

View File

@@ -0,0 +1,128 @@
{% load i18n %}
{{ formset.management_form }}
{% if formset.non_field_errors %}
<div class="alert alert-error">
{{ formset.non_field_errors }}
</div>
{% endif %}
<table class="table table-bordered formset-table" id="{{ formset.prefix }}-formset-table">
<thead>
<tr class="table_caption"></tr>
<tr>
{% for field in flavors_formset.0.visible_fields %}
<th class="normal_column head-{{ field.name }} {% if field.field.required %} required{% endif %}">
<span>{{ field.field.label }}</span>
</th>
{% endfor %}
</tr></thead>
<tbody>
{% for form in formset %}
<tr>
{% for field in form.visible_fields %}
<td class="control-group
{% if field.errors %} error{% endif %}
field-{{ field.name }}">
{{ field }}
{% for error in field.errors %}
<span class="help-inline">{{ error }}</span>
{% endfor %}
{% if forloop.first %}
{% for field in form.hidden_fields %}
{{ field }}
{% for error in field.errors %}
<span class="help-inline">{{ field.name }}: {{ error }}</span>
{% endfor %}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-error">
{{ form.non_field_errors }}
</div>
{% endif %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr><td colspan="{{ flavors_formset.0.visible_fields|length }}">
</td></tr>
</tfoot>
</table>
<script type="text/javascript">
(function () {
// prepares the js-enabled parts of the formset table
var init_formset = function () {
var prefix = '{{ formset.prefix|escapejs }}';
var input_name_re = new RegExp('^' + prefix + '-\\d+-');
var input_id_re = new RegExp('^id_' + prefix + '-\\d+-');
var table = $('#' + prefix + '-formset-table');
var empty_row = table.find('tbody tr:last').clone();
// go through the whole table and fix the numbering of rows
var reenumerate_rows = function () {
var count = 0;
table.find('tbody tr').each(function () {
$(this).find('input').each(function () {
var input = $(this);
input.attr('name', input.attr('name').replace(
input_name_re,
prefix + '-' + count + '-'));
input.attr('id', input.attr('id').replace(
input_id_re,
'id_' + prefix + '-' + count + '-'));
});
count += 1;
});
$('#id_' + prefix + '-TOTAL_FORMS').val(count);
$('#id_' + prefix + '-INITIAL_FORMS').val(count);
};
// replace the "Delete" checkboxes with × for deleting rows
var del_row = function () {
$(this).closest('tr').remove();
reenumerate_rows();
};
$('<a href="#" class="close">×</a>').replaceAll(
table.find('input[name$="-DELETE"]')
).click(del_row);
// add more empty rows in the flavors table
var add_row = function () {
var new_row = empty_row.clone();
// connect signals and clean up
$('<a href="#" class="close">×</a>').replaceAll(
new_row.find('input[name$="-DELETE"]')
).click(del_row);
new_row.find('input').val(null);
new_row.find('td').removeClass('error')
new_row.find('span.help-inline').remove();
table.find('tbody').append(new_row);
reenumerate_rows();
};
$('#{{ formset.prefix }}-formset-table tfoot td').append(
'<a href="#" class="btn">{% filter escapejs %}{% trans "Add a row" %}{% endfilter %}</a>'
).click(add_row);
// if the formset is not empty, and is not being redisplayed,
// delete the empty row from the end
if (table.find('tbody tr').length > 1 &&
$('#id_' + prefix + '-TOTAL_FORMS').val() >
$('#id_' + prefix + '-INITIAL_FORMS').val()) {
table.find('tbody tr:last').remove();
reenumerate_rows();
}
};
if (typeof($) !== 'undefined') {
$(init_formset);
} else {
addHorizonLoadEvent(init_formset);
}
} ());
</script>

View File

@@ -1,4 +1,4 @@
<noscript><h3>{{ step }}</h3>xx</noscript>
<noscript><h3>{{ step }}</h3></noscript>
<table class="table-fixed">
<tbody>
<tr>
@@ -13,82 +13,31 @@
</table>
<div id="id_resource_class_flavors_table">
{{ flavors_formset.management_form }}
{% if flavors_formset.non_field_errors %}
<div class="alert alert-error">
{{ flavors_formset.non_field_errors }}
</div>
{% endif %}
<table class="table table-bordered">
<thead>
<tr class="table_caption"></tr>
<tr>
{% for field in flavors_formset.0.visible_fields %}
<th class="normal_column">{{ field.field.label }}</th>
{% endfor %}
</tr></thead>
<tbody>
{% for form in flavors_formset %}
<tr>
{% for field in form.visible_fields %}
<td class="control-group{% if field.errors %} error{% endif %}">
{{ field }}
{% for error in field.errors %}
<span class="help-inline">{{ error }}</span>
{% endfor %}
{% if forloop.first %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-error">
{{ form.non_field_errors }}
</div>
{% endif %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr><td colspan="{{ flavors_formset.0.visible_fields|length }}"></tr>
</tfoot>
</table>
{% include 'infrastructure/resource_management/resource_classes/_formset.html' with formset=flavors_formset %}
</div>
<script type="text/javascript">
(function () {
// show the flavors table only when service_type is compute
var toggle_table = function(value){
if (value == 'compute'){
$('#id_resource_class_flavors_table').show();
}
else{
$('#id_resource_class_flavors_table').hide();
}
};
toggle_table($('#id_service_type').val())
var init_table = function () {
var toggle_table = function (value) {
if (value === 'compute') {
$('#id_resource_class_flavors_table').show();
} else {
$('#id_resource_class_flavors_table').hide();
}
};
$('#id_service_type').change(function(){
toggle_table($('#id_service_type').val())
});
var toggle_max_vms = function(check_box){
var checked = check_box.prop('checked');
var id = check_box.val();
if (checked){
$('input[name=flavors_object_ids__max_vms__' + id + "]").show();
}
else{
$('input[name=flavors_object_ids__max_vms__' + id + "]").hide();
}
toggle_table($('#id_service_type').val())
$('#id_service_type').change(function () {
toggle_table($('#id_service_type').val());
});
};
$(".modal #flavors input[type=checkbox]").each(function() {
toggle_max_vms($(this));
});
$(".modal #flavors input[type=checkbox]").bind('click', function() {
toggle_max_vms($(this));
});
if (typeof($) !== 'undefined') {
$(init_table);
} else {
addHorizonLoadEvent(init_table);
}
} ());
</script>