Skip to content

Commit

Permalink
Add tabbed_by to group of columns in forms
Browse files Browse the repository at this point in the history
  • Loading branch information
scambra committed Oct 18, 2024
1 parent 8b285f4 commit 8a738a8
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 42 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Add tabbed_by to group of columns in forms. The columns in the group must be collection associations, with a common column or association, used to partition them in different tabs.

= 3.7.8
- Rollback previous behaviour when submitting empty values, broken when default_value was added. Default value set in column is not used when trying to save empty value, DB default is used in that case, and save NULL when string is empty, as before.
- Support operators =, null and not null for :select search_ui, so it's possible to search for NULL with :select search UI.
Expand Down
25 changes: 11 additions & 14 deletions app/views/active_scaffold_overrides/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
<%
scope ||= nil
subsection_id ||= nil
tab_value ||= nil
tabbed_by ||= nil
tab_id ||= nil
show_unauthorized_columns = active_scaffold_config.send(form_action).show_unauthorized_columns if active_scaffold_config.actions.include? form_action
%>
<ol class="form" <%= "id=#{subsection_id}" unless subsection_id.nil? %> <%= "style=\"display: none;\"".html_safe if columns.collapsed %>>
<% columns.each_column(for: @record, crud_type: (:read if show_unauthorized_columns)) do |column| %>
<% column_css_class = column.css_class unless column.css_class.nil? || column.css_class.is_a?(Proc) %>
<% renders_as = column_renders_as(column) %>
<% authorized = show_unauthorized_columns || renders_as == :subsection ? @record.authorized_for?(:crud_type => form_action, :column => column.name) : true %>
<% if renders_as == :subsection -%>
<% if authorized %>
<% subsection_id = sub_section_id(:sub_section => column.label) %>
<li class="sub-section <%= column_css_class %>">
<h5>
<%= column.label %>
<%= link_to_visibility_toggle(subsection_id, {:default_visible => !column.collapsed}) -%>
</h5>
<%= render :partial => 'form', :locals => { :columns => column, :subsection_id => subsection_id, :form_action => form_action, :scope => scope } %>
<% authorized = show_unauthorized_columns || renders_as == :subsection ? @record.authorized_for?(crud_type: form_action, column: column.name) : true %>
<% if renders_as == :subsection || renders_as == :tabbed -%>
<% next unless authorized %>
<li class="<%= column.tabbed_by ? 'form-element' : 'sub-section' %> <%= column_css_class %>">
<%= render_subsection(column, @record, scope, form_action) %>
</li>
<% end %>
<% elsif renders_as == :subform and authorized -%>
<%= content_tag :li, active_scaffold_subform_attributes(column, column_css_class) do %>
<%= render_column(column, @record, renders_as, scope) %>
<%= content_tag :li, active_scaffold_subform_attributes(column, column_css_class, tab_id: tab_id) do %>
<%= render_column(column, @record, renders_as, scope, tabbed_by: (column.options[:tabbed_by] || tabbed_by if tabbed_by), tab_value: tab_value, tab_id: tab_id) %>
<% end %>
<% else -%>
<li class="form-element <%= 'required' if column.required?(action_for_validation?(@record)) %> <%= column.form_ui %> <%= column_css_class %>">
<%= render_column(column, @record, renders_as, scope, !authorized) %>
<%= render_column(column, @record, renders_as, scope, only_value: !authorized) %>
</li>
<% end -%>
<% end -%>
Expand Down
31 changes: 20 additions & 11 deletions app/views/active_scaffold_overrides/_form_association.html.erb
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
<%
associated = column.association.singular? ? [parent_record.send(column.name)].compact : parent_record.send(column.name).to_a
if column.show_blank_record?(associated)
show_blank_record = build_associated(column.association, parent_record)
end
disable_required_for_new = @disable_required_for_new
@disable_required_for_new = !!show_blank_record unless (column.association.singular? && column.required?(action_for_validation?(parent_record)))
subform_div_id = "#{sub_form_id(:association => column.name, :id => parent_record.id || generated_id(parent_record) || 99999999999)}-div"
tabbed_by ||= nil
tab_value ||= nil
tab_id ||= nil
associated = column.association.singular? ? [parent_record.send(column.name)].compact : parent_record.send(column.name).to_a
associated = associated.select { |record| record.send(tabbed_by) == tab_value } if tabbed_by && tab_value
if column.show_blank_record?(associated)
show_blank_record = build_associated(column.association, parent_record) do |blank_record|
blank_record.send("#{tabbed_by}=", tab_value) if tabbed_by && tab_value
end
end
if column == :labor_line_items
@financial_months = (associated.first || show_blank_record)&.monthly_task_costs.map(&:financial_month)
end
disable_required_for_new = @disable_required_for_new
@disable_required_for_new = !!show_blank_record unless (column.association.singular? && column.required?(action_for_validation?(parent_record)))
subform_div_id = "#{sub_form_id(association: column.name, tab_id: tab_id, id: parent_record.id || generated_id(parent_record) || 99999999999)}-div"

# render footer before rendering associated records, fixes create new on self-associations
# so generated_id for blank associated record is not used in create new button
footer = render(:partial => 'form_association_footer', :locals => {:parent_record => parent_record, :column => column, :associated => associated, :scope => scope})
# render footer before rendering associated records, fixes create new on self-associations
# so generated_id for blank associated record is not used in create new button
footer = render('form_association_footer', parent_record: parent_record, column: column, associated: associated, scope: scope, subform_div_id: subform_div_id)
-%>
<h5>
<%= column.label -%>
Expand All @@ -19,7 +28,7 @@ footer = render(:partial => 'form_association_footer', :locals => {:parent_recor
<div id ="<%= subform_div_id %>" <%= 'style="display: none;"'.html_safe if column.collapsed -%>>
<%# HACK: to be able to delete all associated records %>
<%= hidden_field_tag "#{(opts = active_scaffold_input_options(column, scope, :object => parent_record))[:name]}[0]", '', :id => "#{opts[:id]}_0" if column.association.collection? %>
<%= render :partial => subform_partial_for_column(column), :locals => {:column => column, :parent_record => parent_record, :associated => associated, :show_blank_record => show_blank_record, :scope => scope} %>
<%= render subform_partial_for_column(column), column: column, parent_record: parent_record, associated: associated, show_blank_record: show_blank_record, scope: scope, tab_id: tab_id %>
<%= footer -%>
</div>
<%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ add_new_url = params_for(url_options) if show_add_new
add_label = as_(:replace_with_new)
add_class = 'as_replace_with_new'
end
create_another_id = "#{sub_form_id(:association => column.name, :id => parent_record.id || temporary_id || 99999999999)}-create-another" %>
create_another_id = "#{subform_div_id}-create-another" %>
<%= link_to add_label, add_new_url, :id => create_another_id, :remote => true, :class => "as-js-button #{add_class}", :style=> "display: none;" %>
<% end -%>

Expand All @@ -39,7 +39,7 @@ add_new_url = params_for(url_options) if show_add_new
<%= link_to_record_select as_(:add_existing), remote_controller.controller_path, record_select_params_for_add_existing(column.association, edit_associated_url, parent_record) -%>
<% else -%>
<% select_options = options_from_collection_for_select(sorted_association_options_find(column.association, nil, parent_record), :to_param, :to_label)
add_existing_id = "#{sub_form_id(:association => column.name, :id => parent_record.id || temporary_id || 99999999999)}-add-existing"
add_existing_id = "#{subform_div_id}-add-existing"
add_existing_label = column.association.collection? ? :add_existing : :replace_existing %>
<%= select_tag 'associated_id', content_tag(:option, as_(:_select_), value: '') + select_options %>
<%= link_to as_(add_existing_label), edit_associated_url, :id => add_existing_id, :remote => true, :class=> "as-js-button as_#{add_existing_label}", :style => "display: none;" %>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<table id="<%= sub_form_list_id(association: column.name, id: parent_record&.id || generated_id(parent_record) || 99999999999) %>">
<table id="<%= sub_form_list_id(association: column.name, id: parent_record&.id || generated_id(parent_record) || 99999999999, tab_id: local_assigns[:tab_id]) %>">
<%
header_record_class = (show_blank_record && show_blank_record.class) || column.association.klass
-%>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div id="<%= sub_form_list_id(:association => column.name, :id => parent_record.id || generated_id(parent_record) || 99999999999) %>">
<div id="<%= sub_form_list_id(:association => column.name, :id => parent_record.id || generated_id(parent_record) || 99999999999, tab_id: local_assigns[:tab_id]) %>">
<%= render partial: 'form_association_record', collection: associated, locals: {scope: scope, parent_record: parent_record, column: column, columns: local_assigns[:columns], layout: :vertical} %>
<%= render partial: 'form_association_record', object: show_blank_record, locals: {scope: scope, parent_record: parent_record, column: column, columns: local_assigns[:columns], layout: :vertical, locked: true, index: associated.size} if show_blank_record %>
</div>
15 changes: 15 additions & 0 deletions app/views/active_scaffold_overrides/add_tab.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%
subsection_id = sub_section_id(:sub_section => @column.label)
tab_options = active_scaffold_tab_options(@column, @record)
tab_label, tab_value, tab_record = tab_options.find { |_, value, _| params[:value] == value.to_s }
if tab_label
tab_id_suffix = clean_id(tab_record&.id&.to_s || tab_value.to_s)
tab_id = "#{subsection_id}-#{tab_id_suffix}-tab"
tab_content = render('form', columns: @column, subsection_id: "#{subsection_id}-#{tab_id_suffix}", form_action: @form_action, scope: @scope, tab_value: tab_record || tab_value, tab_id: tab_id_suffix, tabbed_by: @column.tabbed_by)
tab = active_scaffold_tab_content(tab_id, true, tab_content)
%>
$('#<%= subsection_id %> .tab-content > .tab-pane.active').removeClass('in active');
ActiveScaffold.create_associated_record_form('<%= subsection_id %> .tab-content', '<%= j tab %>', {singular: false});
$('#<%= subsection_id %> .nav-tabs').find('.nav-item.active').removeClass('active').end().append('<%= j active_scaffold_tab(tab_label, tab_id, true) %>');
$('#<%= @source_id %> option[value=<%= tab_value %>]').hide().parent().val('');
<% end %>
21 changes: 18 additions & 3 deletions lib/active_scaffold/actions/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def render_field
respond_to do |format|
format.js { render :action => 'render_field_inplace', :layout => false }
end
elsif params[:tabbed_by]
add_tab
respond_to do |format|
format.js { render :action => 'add_tab', :layout => false }
end
else
render_field_for_update_columns
respond_to { |format| format.js }
Expand Down Expand Up @@ -58,10 +63,14 @@ def render_field_for_inplace_editing
@record = find_if_allowed(params[:id], :crud_type => :update, :column => params[:update_column])
end

def render_field_for_update_columns
return if (@column = active_scaffold_config.columns[params.delete(:column)]).nil?
def add_tab
process_render_field_params
@column = @main_columns.find_by_name(params[:column])
@record = updated_record_with_form(@main_columns, {}, @scope)
end

def process_render_field_params
@source_id = params.delete(:source_id)
@columns = @column.update_columns || []
@scope = params.delete(:scope)
if @scope
@form_action = :subform
Expand All @@ -70,7 +79,13 @@ def render_field_for_update_columns
end
@form_action ||= params[:id] ? :update : :create
@main_columns = active_scaffold_config.send(@form_action).columns
end

def render_field_for_update_columns
return if (@column = active_scaffold_config.columns[params.delete(:column)]).nil?
@columns = @column.update_columns || []
@columns << @column.name if @column.options[:refresh_link] && @columns.exclude?(@column.name)
process_render_field_params

@record =
if @column.send_form_on_update_column
Expand Down
5 changes: 5 additions & 0 deletions lib/active_scaffold/data_structures/action_columns.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ class ActionColumns < ActiveScaffold::DataStructures::Set

# labels are useful for the Create/Update forms, when we display columns in a grouped fashion and want to name them separately
attr_writer :label

# a common column in the association columns included in the group, used to group the records from the
# association columns and split them in tabs
attr_accessor :tabbed_by

def label
as_(@label) if @label
end
Expand Down
2 changes: 1 addition & 1 deletion lib/active_scaffold/helpers/controller_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def build_associated(association, parent_record)
assign_default_attributes record
save_record_to_association(record, association.reverse_association, parent_record) # set inverse
end
end
end.tap { |record| yield record if block_given? }
end

def save_record_to_association(record, association, value, reverse = nil)
Expand Down
Loading

0 comments on commit 8a738a8

Please sign in to comment.