Skip to content
Snippets Groups Projects
Commit aeb31904 authored by Eric Eastwood's avatar Eric Eastwood
Browse files

WIP: Schedule pipelines with variables with nested_form gem

parent 1dc1a7e9
No related branches found
No related tags found
1 merge request!12484WIP: Schedule pipelines with variables with nested_form gem
Pipeline #
Loading
Loading
@@ -251,6 +251,7 @@ gem 'jquery-rails', '~> 4.1.0'
gem 'request_store', '~> 1.3'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
gem 'nested_form', '~> 0.3.2'
gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0'
 
Loading
Loading
Loading
Loading
@@ -474,6 +474,7 @@ GEM
mustermann-grape (0.4.0)
mustermann (= 0.4.0)
mysql2 (0.3.20)
nested_form (0.3.2)
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
Loading
Loading
@@ -1017,6 +1018,7 @@ DEPENDENCIES
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
nested_form (~> 0.3.2)
net-ssh (~> 3.0.1)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.4)
Loading
Loading
Loading
Loading
@@ -8,4 +8,5 @@ import 'vendor/jquery.atwho';
import 'vendor/jquery.scrollTo';
import 'vendor/jquery.nicescroll';
import 'vendor/jquery.waitforimages';
import 'vendor/jquery_nested_form';
import 'select2/select2';
Loading
Loading
@@ -74,3 +74,77 @@
margin-right: 3px;
}
}
.pipeline-variable-list {
margin-left: 0;
margin-bottom: 0;
padding-left: 0;
}
.pipeline-variable-row {
display: flex;
margin-bottom: $gl-btn-padding;
&:not(:last-child) {
margin-bottom: $gl-btn-padding;
}
@media (max-width: $screen-xs-max) {
flex-wrap: wrap;
}
& > .pipeline-variable-row-add-button {
display: none;
}
&:last-of-type {
& > .pipeline-variable-row-remove-button {
display: none;
}
& > .pipeline-variable-row-add-button {
display: inline-flex;
}
}
}
.pipeline-variable-key-input {
margin-right: $gl-btn-padding;
@media (max-width: $screen-xs-max) {
margin-right: calc(1em + #{2 * $gl-padding});
margin-bottom: $gl-btn-padding;
}
}
.pipeline-variable-value-input {
@media (max-width: $screen-xs-max) {
flex: 1;
}
}
.pipeline-variable-row-remove-button {
display: inline-flex;
align-items: center;
padding: 0 $gl-padding;
background: transparent;
border: 0;
color: $gl-text-color-secondary;
transition: color $general-hover-transition-duration $general-hover-transition-curve;
&:hover,
&:focus {
outline: none;
text-decoration: none;
color: $gl-text-color;
}
& > .fa {
width: 1em;
}
}
.pipeline-variable-row-add-button {
@extend .pipeline-variable-row-remove-button;
}
Loading
Loading
@@ -2,7 +2,7 @@
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'schedule_form'
 
= form_for [@project.namespace.becomes(Namespace), @project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "form-horizontal js-pipeline-schedule-form" } do |f|
= nested_form_for [@project.namespace.becomes(Namespace), @project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "form-horizontal js-pipeline-schedule-form" } do |f|
= form_errors(@schedule)
.form-group
.col-md-9
Loading
Loading
@@ -22,38 +22,23 @@
= f.label :ref, _('Target Branch'), class: 'label-light'
= dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
= f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
-# TODO: Test code
= @schedule.variables.inspect
- if @schedule.variables.present?
- @schedule.variables.each_with_index do |variable, i|
.form-group
.col-md-9
%label.label-light Key
%input.form-control{:name => "schedule[variables_attributes][#{i}][key]", :type => "text", :value => variable.key}/
%p.gl-field-error.hide This field is required.
%label.label-light Value
%input.form-control{:name => "schedule[variables_attributes][#{i}][value]", :type => "text", :value => variable.value}/
%p.gl-field-error.hide This field is required.
- if @schedule.variables.count == 1
- (1..1).each do |i|
.form-group
.col-md-9
%label.label-light Key
%input.form-control{:name => "schedule[variables_attributes][#{i}][key]", :type => "text"}/
%p.gl-field-error.hide This field is required.
%label.label-light Value
%input.form-control{:name => "schedule[variables_attributes][#{i}][value]", :type => "text"}/
%p.gl-field-error.hide This field is required.
- else
- (0..0).each do |i|
.form-group
.col-md-9
%label.label-light Key
%input.form-control{:name => "schedule[variables_attributes][#{i}][key]", :type => "text"}/
%p.gl-field-error.hide This field is required.
%label.label-light Value
%input.form-control{:name => "schedule[variables_attributes][#{i}][value]", :type => "text"}/
%p.gl-field-error.hide This field is required.
.form-group
.col-md-9
%label.label-light
#{ _('Variables') }
%ul.js-pipeline-variable-list.pipeline-variable-list
= f.fields_for :variables, :wrapper => false do |variable_form|
%li.pipeline-variable-row.fields
= variable_form.text_field :key, class: 'pipeline-variable-key-input form-control', placeholder: _('Input variable key')
= variable_form.text_area :value, class: 'pipeline-variable-value-input form-control', rows: 1, placeholder: _('Input variable value')
= variable_form.link_to_remove class: 'pipeline-variable-row-remove-button' do
%i.fa.fa-minus-circle{ aria: { hidden: "true" } }
= f.link_to_add :variables, 'data-target': '.js-pipeline-variable-list', class: 'pipeline-variable-row-add-button' do
%i.fa.fa-plus-circle{ aria: { hidden: "true" } }
= f.link_to_add :variables, class: '', 'data-target': '.js-pipeline-variable-list' do
Add variable
%i.fa.fa-plus-circle{ aria: { hidden: "true" } }
.form-group
.col-md-9
= f.label :active, s_('PipelineSchedules|Activated'), class: 'label-light'
Loading
Loading
(function($) {
window.NestedFormEvents = function() {
this.addFields = $.proxy(this.addFields, this);
this.removeFields = $.proxy(this.removeFields, this);
};
NestedFormEvents.prototype = {
addFields: function(e) {
// Setup
var link = e.currentTarget;
var assoc = $(link).data('association'); // Name of child
var blueprint = $('#' + $(link).data('blueprint-id'));
var content = blueprint.data('blueprint'); // Fields template
// Make the context correct by replacing <parents> with the generated ID
// of each of the parent objects
var context = ($(link).closest('.fields').closestChild('input, textarea, select').eq(0).attr('name') || '').replace(/\[[a-z_]+\]$/, '');
// If the parent has no inputs we need to strip off the last pair
var current = content.match(new RegExp('\\[([a-z_]+)\\]\\[new_' + assoc + '\\]'));
if (current) {
context = context.replace(new RegExp('\\[' + current[1] + '\\]\\[(new_)?\\d+\\]$'), '');
}
// context will be something like this for a brand new form:
// project[tasks_attributes][1255929127459][assignments_attributes][1255929128105]
// or for an edit form:
// project[tasks_attributes][0][assignments_attributes][1]
if (context) {
var parentNames = context.match(/[a-z_]+_attributes(?=\]\[(new_)?\d+\])/g) || [];
var parentIds = context.match(/[0-9]+/g) || [];
for(var i = 0; i < parentNames.length; i++) {
if(parentIds[i]) {
content = content.replace(
new RegExp('(_' + parentNames[i] + ')_.+?_', 'g'),
'$1_' + parentIds[i] + '_');
content = content.replace(
new RegExp('(\\[' + parentNames[i] + '\\])\\[.+?\\]', 'g'),
'$1[' + parentIds[i] + ']');
}
}
}
// Make a unique ID for the new child
var regexp = new RegExp('new_' + assoc, 'g');
var new_id = this.newId();
content = $.trim(content.replace(regexp, new_id));
var field = this.insertFields(content, assoc, link);
// bubble up event upto document (through form)
field
.trigger({ type: 'nested:fieldAdded', field: field })
.trigger({ type: 'nested:fieldAdded:' + assoc, field: field });
return false;
},
newId: function() {
return new Date().getTime();
},
insertFields: function(content, assoc, link) {
var target = $(link).data('target');
if (target) {
return $(content).appendTo($(target));
} else {
return $(content).insertBefore(link);
}
},
removeFields: function(e) {
var $link = $(e.currentTarget),
assoc = $link.data('association'); // Name of child to be removed
var hiddenField = $link.prev('input[type=hidden]');
hiddenField.val('1');
var field = $link.closest('.fields');
field.hide();
field
.trigger({ type: 'nested:fieldRemoved', field: field })
.trigger({ type: 'nested:fieldRemoved:' + assoc, field: field });
return false;
}
};
window.nestedFormEvents = new NestedFormEvents();
$(document)
.delegate('form a.add_nested_fields', 'click', nestedFormEvents.addFields)
.delegate('form a.remove_nested_fields', 'click', nestedFormEvents.removeFields);
})(jQuery);
// http://plugins.jquery.com/project/closestChild
/*
* Copyright 2011, Tobias Lindig
*
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
*
*/
(function($) {
$.fn.closestChild = function(selector) {
// breadth first search for the first matched node
if (selector && selector != '') {
var queue = [];
queue.push(this);
while(queue.length > 0) {
var node = queue.shift();
var children = node.children();
for(var i = 0; i < children.length; ++i) {
var child = $(children[i]);
if (child.is(selector)) {
return child; //well, we found one
}
queue.push(child);
}
}
}
return $();//nothing found
};
})(jQuery);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment