Skip to content

Commit

Permalink
Feature: Display additional fields when attaching a record to another (
Browse files Browse the repository at this point in the history
…#3048)

* add fields

* Apply options like update_using when attaching record

* Lint

* Use keyword arguments

* Ensure through attachments without additional params are correctly attached

* Refactor: use fill_record method itself

* Refactor: do not send redundant args to fieldsexecContext

* Add translation key

* Refactor: use @reflection and remove duplication

* Use extra_fields instead of extra

* Update app/controllers/avo/associations_controller.rb

Co-authored-by: Paul Bob <[email protected]>

* wip styling

* include blank on the select

* Do not pass in nil value for model because it is deprecated

* Remove duplicate key

* Format

* Give clear name

* Update app/controllers/avo/associations_controller.rb

Co-authored-by: Paul Bob <[email protected]>

* Rename to attach_fields

* Display correct label

* Enable additional fields when select is searchable

* Lint

* Add label_text

* Fix indentation

* Change texts to reflect the association

* Fix tests

* Fix feature test

* Refactor associations controller and display sigularized field names

* Remove useless assignment

* Update app/controllers/avo/associations_controller.rb

* Update app/controllers/avo/associations_controller.rb

* Update app/views/avo/associations/new.html.erb

* Update app/views/avo/associations/new.html.erb

* Update spec/system/avo/for_attribute_spec.rb

* Update app/controllers/avo/associations_controller.rb

* field_wrapper on searchable

* Update app/controllers/avo/associations_controller.rb

---------

Co-authored-by: Paul Bob <[email protected]>
Co-authored-by: Adrian Marin <[email protected]>
Co-authored-by: Paul Bob <[email protected]>
  • Loading branch information
4 people authored Aug 6, 2024
1 parent 9b4479b commit e5ae21d
Show file tree
Hide file tree
Showing 35 changed files with 231 additions and 44 deletions.
39 changes: 37 additions & 2 deletions app/controllers/avo/associations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class AssociationsController < BaseController
before_action :set_attachment_class, only: [:show, :index, :new, :create, :destroy]
before_action :set_attachment_resource, only: [:show, :index, :new, :create, :destroy]
before_action :set_attachment_record, only: [:create, :destroy]
before_action :set_attach_fields, only: [:new, :create]
before_action :authorize_index_action, only: :index
before_action :authorize_attach_action, only: :new
before_action :authorize_detach_action, only: :destroy
Expand Down Expand Up @@ -67,6 +68,7 @@ def create
notice: t("avo.attachment_class_attached", attachment_class: @related_resource.name)
}
else
flash[:error] = t("avo.attachment_failed", attachment_class: @related_resource.name)
format.turbo_stream {
render turbo_stream: turbo_stream.append("alerts", partial: "avo/partials/all_alerts")
}
Expand All @@ -78,7 +80,9 @@ def create_association
association_name = BaseResource.valid_association_name(@record, association_from_params)

perform_action_and_record_errors do
if has_many_reflection?
if through_reflection? && additional_params.present?
new_join_record.save
elsif has_many_reflection? || through_reflection?
@record.send(association_name) << @attachment_record
else
@record.send(:"#{association_name}=", @attachment_record)
Expand All @@ -90,7 +94,7 @@ def create_association
def destroy
association_name = BaseResource.valid_association_name(@record, @field.for_attribute || params[:related_name])

if @reflection.instance_of? ActiveRecord::Reflection::ThroughReflection
if through_reflection?
join_record.destroy!
elsif has_many_reflection?
@record.send(association_name).delete @attachment_record
Expand Down Expand Up @@ -190,5 +194,36 @@ def has_many_reflection?
ActiveRecord::Reflection::HasAndBelongsToManyReflection
]
end

def through_reflection?
@reflection.instance_of? ActiveRecord::Reflection::ThroughReflection
end

def additional_params
@additional_params ||= params[:fields].permit(@attach_fields&.map(&:id))
end

def set_attach_fields
@attach_fields = if @field.attach_fields.present?
Avo::FieldsExecutionContext.new(target: @field.attach_fields)
.detect_fields
.items_holder
.items
end
end

def new_join_record
@resource.fill_record(
@reflection.through_reflection.klass.new,
additional_params.merge(
{
source_foreign_key => @attachment_record.id,
through_foreign_key => @record.id
}
),
fields: @attach_fields,
extra_params: [source_foreign_key, through_foreign_key]
)
end
end
end
65 changes: 45 additions & 20 deletions app/views/avo/associations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,56 @@
} do |form| %>
<%= render Avo::ModalComponent.new do |c| %>
<% c.with_heading do %>
<%= t 'avo.choose_item', item: @related_resource.name.downcase %>
<%= t 'avo.choose_item', item: @field.name.singularize.downcase %>
<% end %>

<div class="flex-1 flex items-center justify-center px-0 lg:px-8 text-lg mt-8 mb-12">
<% if @field.is_searchable? %>
<%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: form,
classes: input_classes("w-full"),
field: @field,
model_key: @field.target_resource&.model_key,
foreign_key: 'related_id',
resource: @resource,
view: :new
%>
<% else %>
<div class="flex-1 flex flex-col items-center justify-center px-0 md:px-24 text-base">
<%= form.select :related_id, options_for_select(@options, nil),
{
<div class="flex-1 flex flex-col items-center justify-center px-0 md:px-24 text-base">
<div class="w-full">
<% if @field.is_searchable? %>
<%= field_wrapper stacked: true,
field: @field,
view: Avo::ViewInquirer.new("edit"),
form:,
index: 0,
resource: @resource,
label_for: @field.id,
label: @field.name.singularize.downcase do %>
<%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: form,
classes: input_classes("w-full"),
field: @field,
model_key: @field.target_resource&.model_key,
foreign_key: 'related_id',
resource: @resource,
view: :new
%>
<% end %>
<% else %>
<%= avo_edit_field :related_id,
as: :select,
form: form,
name: @field.name.singularize,
options: options_for_select(@options,
nil),
include_blank: t('avo.choose_an_option'),
},
{
class: input_classes('w-full'),
}
%>
stacked: true,
classes: 'w-full'
%>
<% end %>
<% @attach_fields&.each_with_index do |field, index| %>
<%= render(Avo::Items::SwitcherComponent.new(
resource: @related_resource,
item: field,
index: index + 1,
view: @view,
form: form,
field_component_extra_args: {
stacked: true,
classes: 'w-full'}
)) %>
<% end %>
</div>
<% end %>
</div>
</div>

<% c.with_controls do %>
Expand Down
2 changes: 1 addition & 1 deletion lib/avo/fields/base_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def to_permitted_param
end

def record_errors
record.nil? ? {} : record.errors
record.present? ? record.errors : {}
end

def type
Expand Down
2 changes: 1 addition & 1 deletion lib/avo/fields/concerns/is_required.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def is_required?
private

def required_from_validators
return false if record.nil?
return false unless record.present?

validators.any? do |validator|
validator.is_a? ActiveModel::Validations::PresenceValidator
Expand Down
2 changes: 2 additions & 0 deletions lib/avo/fields/has_base_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class HasBaseField < BaseField
attr_accessor :discreet_pagination
attr_accessor :hide_search_input
attr_reader :link_to_child_resource
attr_reader :attach_fields

def initialize(id, **args, &block)
super(id, **args, &block)
Expand All @@ -27,6 +28,7 @@ def initialize(id, **args, &block)
@link_to_child_resource = args[:link_to_child_resource] || false
@reloadable = args[:reloadable].present? ? args[:reloadable] : false
@linkable = args[:linkable].present? ? args[:linkable] : false
@attach_fields = args[:attach_fields]
end

def field_resource
Expand Down
13 changes: 13 additions & 0 deletions lib/avo/fields_execution_context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Avo
class FieldsExecutionContext < Avo::ExecutionContext
include Avo::Concerns::HasItems

def detect_fields
self.items_holder = Avo::Resources::Items::Holder.new(parent: self)

instance_exec(&target) if target.present? && target.respond_to?(:call)

self
end
end
end
38 changes: 22 additions & 16 deletions lib/avo/resources/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,16 @@ def detect_fields
self
end

VIEW_METHODS_MAPPING = {
index: [:index_fields, :display_fields],
show: [:show_fields, :display_fields],
edit: [:edit_fields, :form_fields],
update: [:edit_fields, :form_fields],
new: [:new_fields, :form_fields],
create: [:new_fields, :form_fields]
} unless defined? VIEW_METHODS_MAPPING
unless defined? VIEW_METHODS_MAPPING
VIEW_METHODS_MAPPING = {
index: [:index_fields, :display_fields],
show: [:show_fields, :display_fields],
edit: [:edit_fields, :form_fields],
update: [:edit_fields, :form_fields],
new: [:new_fields, :form_fields],
create: [:new_fields, :form_fields]
}
end

def fetch_fields
possible_methods_for_view = VIEW_METHODS_MAPPING[view.to_sym]
Expand Down Expand Up @@ -334,12 +336,12 @@ def divider(label = nil)
end

# def get_actions / def get_filters / def get_scopes
define_method "get_#{plural_entity}" do
define_method :"get_#{plural_entity}" do
return entity_loader(entity).bag if entity_loader(entity).present?

# ex: @actions_loader = Avo::Loaders::ActionsLoader.new
instance_variable_set(
"@#{plural_entity}_loader",
:"@#{plural_entity}_loader",
"Avo::Loaders::#{plural_entity.humanize}Loader".constantize.new
)

Expand All @@ -349,8 +351,8 @@ def divider(label = nil)
end

# def get_action_arguments / def get_filter_arguments / def get_scope_arguments
define_method "get_#{entity}_arguments" do |entity_class|
klass = send("get_#{plural_entity}").find { |entity| entity[:class].to_s == entity_class.to_s }
define_method :"get_#{entity}_arguments" do |entity_class|
klass = send(:"get_#{plural_entity}").find { |entity| entity[:class].to_s == entity_class.to_s }

raise "Couldn't find '#{entity_class}' in the 'def #{plural_entity}' method on your '#{self.class}' resource." if klass.nil?

Expand All @@ -359,7 +361,7 @@ def divider(label = nil)
end

def hydrate(...)
super(...)
super

if @record.present?
hydrate_model_with_default_values if @view&.new?
Expand Down Expand Up @@ -443,10 +445,14 @@ def fields_by_database_id
.to_h
end

def fill_record(record, params, extra_params: [])
def fill_record(record, params, extra_params: [], fields: nil)
# Write the field values
params.each do |key, value|
field = fields_by_database_id[key]
field = if fields.present?
fields.find { |f| f.id == key.to_sym }
else
fields_by_database_id[key]
end

next unless field.present?

Expand Down Expand Up @@ -601,7 +607,7 @@ def description_attributes
end

def entity_loader(entity)
instance_variable_get("@#{entity.to_s.pluralize}_loader")
instance_variable_get(:"@#{entity.to_s.pluralize}_loader")
end

def record_param
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.ar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ar:
attachment_class_attached: "%{attachment_class} تم ربط"
attachment_class_detached: "%{attachment_class} تم فصل"
attachment_destroyed: تم حذف المرفق
attachment_failed: فشل في إرفاق %{attachment_class}
cancel: إلغاء
choose_a_country: اختر دولة
choose_an_option: اختر خيارًا
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ de:
attach_item: "%{item} anhängen"
attachment_class_attached: "%{attachment_class} angehängt."
attachment_class_detached: "%{attachment_class} abgehängt."
attachment_failed: "Kon %{attachment_class} niet bijvoegen"
attachment_destroyed: Anhang gelöscht
cancel: Abbrechen
choose_a_country: Land auswählen
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ en:
attachment_class_attached: "%{attachment_class} attached."
attachment_class_detached: "%{attachment_class} detached."
attachment_destroyed: Attachment destroyed
attachment_failed: Failed to attach %{attachment_class}
cancel: Cancel
choose_a_country: Choose a country
choose_an_option: Choose an option
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ es:
attachment_class_attached: "%{attachment_class} adjuntado/a."
attachment_class_detached: "%{attachment_class} adjuntado/a."
attachment_destroyed: Adjunto eliminado
attachment_failed: No se pudo adjuntar %{attachment_class}
cancel: Cancelar
choose_a_country: Elige un país
choose_an_option: Elige una opción
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fr:
attachment_class_attached: "%{attachment_class} attaché."
attachment_class_detached: "%{attachment_class} détaché."
attachment_destroyed: Pièce jointe détruite
attachment_failed: Échec de l'ajout de %{attachment_class}
cancel: Annuler
choose_a_country: Sélectionnez un pays
choose_an_option: Sélectionnez une option
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.it.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ it:
attach_item: Allega %{item}
attachment_class_attached: "%{attachment_class} allegato."
attachment_class_detached: "%{attachment_class} staccato."
attachment_failed: "Impossibile allegare %{attachment_class}"
attachment_destroyed: Allegato distrutto
cancel: Annulla
choose_a_country: Scegli un paese
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ja:
attachment_class_attached: "%{attachment_class}をアタッチしました。"
attachment_class_detached: "%{attachment_class}をデタッチしました。"
attachment_destroyed: アタッチは削除されました
attachment_failed: "%{attachment_class}の添付に失敗しました"
cancel: キャンセル
choose_a_country: 国を選択
choose_an_option: オプションを選択
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.nb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nb:
attachment_class_attached: "%{attachment_class} lagt til."
attachment_class_detached: "%{attachment_class} fjernet."
attachment_destroyed: Vedlett slettet
attachment_failed: Kunne ikke legge ved %{attachment_class}
cancel: Avbryt
choose_a_country: Velg et land
choose_an_option: Velg et alternativ
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.nl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ nl:
attach_item: "%{item} bijvoegen"
attachment_class_attached: "%{attachment_class} bijgevoegd."
attachment_class_detached: "%{attachment_class} losgekoppeld."
attachment_failed: "Kon %{attachment_class} niet bijvoegen"
attachment_destroyed: Bijlage verwijderd
cancel: Annuleren
choose_a_country: Kies een land
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.nn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nn:
attachment_class_attached: "%{attachment_class} lagt til."
attachment_class_detached: "%{attachment_class} fjerna."
attachment_destroyed: Vedlegg sletta
attachment_failed: Klarte ikkje å legge ved %{attachment_class}
cancel: Avbryt
choose_a_country: Vel eit land
choose_an_option: Vel eit alternativ
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.pl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pl:
attach_item: Załącz %{item}
attachment_class_attached: "%{attachment_class} załączony."
attachment_class_detached: "%{attachment_class} odłączony."
attachment_failed: "Nie udało się dołączyć %{attachment_class}"
attachment_destroyed: Załącznik usunięty
cancel: Anuluj
choose_a_country: Wybierz kraj
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.pt-BR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pt-BR:
attachment_class_attached: "%{attachment_class} anexado."
attachment_class_detached: "%{attachment_class} separado."
attachment_destroyed: Anexo destruído
attachment_failed: Não foi possível anexar %{attachment_class}
cancel: Cancelar
choose_a_country: Escolha um país
choose_an_option: Escolha uma opção
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.pt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pt:
attachment_class_attached: "%{attachment_class} anexado."
attachment_class_detached: "%{attachment_class} separado."
attachment_destroyed: Anexo destruído
attachment_failed: Não foi possível anexar %{attachment_class}
cancel: Cancelar
choose_a_country: Escolha um país
choose_an_option: Escolha uma opção
Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/locales/avo.ro.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ro:
attachment_class_attached: "%{attachment_class} anexat."
attachment_class_detached: "%{attachment_class} separat."
attachment_destroyed: Atașamentul a fost distrus
attachment_failed: Nu s-a reușit atașarea %{attachment_class}
cancel: Anulează
choose_a_country: Alege o țară
choose_an_option: Alege o opțiune
Expand Down
Loading

0 comments on commit e5ae21d

Please sign in to comment.