Skip to content

Commit

Permalink
Merge pull request #17 from featurist/alternative-solution
Browse files Browse the repository at this point in the history
allow users to propose solutions
  • Loading branch information
dereke authored Nov 12, 2019
2 parents 2ac37d0 + 0770332 commit f86a466
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 77 deletions.
97 changes: 74 additions & 23 deletions app/controllers/initiatives_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

# rubocop:disable Metrics/ClassLength
class InitiativesController < ApplicationController
before_action :set_initiative, only: %i[show edit update destroy]
before_action :set_edit_data, only: %i[edit new create update]
Expand All @@ -17,6 +18,7 @@ def new
def edit; end

def create
create_proposed_solutions
@initiative = Initiative.new(initiative_params)

if check_user_belongs_to_group && @initiative.save
Expand All @@ -29,10 +31,10 @@ def create

def update
@initiative.solutions.clear
params = initiative_params
images = params.delete 'images'
create_proposed_solutions
images = initiative_params.delete 'images'

if check_user_belongs_to_group && @initiative.update(params)
if check_user_belongs_to_group && @initiative.update(initiative_params)
@initiative.images.attach images if images
redirect_to edit_initiative_path(@initiative),
notice: 'Initiative was successfully updated.'
Expand Down Expand Up @@ -73,27 +75,76 @@ def check_user_belongs_to_group
end

# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def create_proposed_solutions
return unless initiative_params['solutions_attributes']

initiative_params['solutions_attributes'] =
initiative_params['solutions_attributes'].values
proposed_solutions =
initiative_params['solutions_attributes'].filter do |solution|
solution['proposed_solution'].present?
end
initiative_params['solutions_attributes'].reject! do |solution|
solution['proposed_solution'].present?
end
proposed_solutions.each do |proposed_solution|
solution_solution_class = create_proposed_solution(proposed_solution)
append_solution(
solution_solution_class.solution,
solution_solution_class.solution_class
)
end
end
# rubocop:enable Metrics/AbcSize

def append_solution(solution, solution_class)
initiative_params['solutions_attributes'] <<
{ 'solution_id': solution.id, 'solution_class_id': solution_class.id }
end

def create_proposed_solution(proposed_solution)
solution_class = SolutionClass.find(proposed_solution['solution_class_id'])
solution =
Solution.new(
name: proposed_solution['proposed_solution'], created_by: current_user
)
solution_solution_class =
SolutionSolutionClass.new(
solution: solution, solution_class: solution_class
)
solution.solution_solution_classes << solution_solution_class
solution.save!

solution_solution_class
end

def initiative_params
params.require(:initiative).permit(
:name,
:summary,
:anticipated_carbon_saving,
:locality,
:location,
:latitude,
:longitude,
:alternative_solution_name,
:lead_group_id,
:contact_name,
:contact_email,
:contact_phone,
:partner_groups_role,
:status_id,
:consent_to_share,
solutions_attributes: %i[solution_id solution_class_id],
images: [],
websites_attributes: %i[website id _destroy]
)
@initiative_params ||=
params.require(:initiative).permit(
:name,
:summary,
:anticipated_carbon_saving,
:locality,
:location,
:latitude,
:longitude,
:lead_group_id,
:contact_name,
:contact_email,
:contact_phone,
:partner_groups_role,
:status_id,
:consent_to_share,
solutions_attributes: %i[
solution_id
solution_class_id
proposed_solution
],
images: [],
websites_attributes: %i[website id _destroy]
)
end
# rubocop:enable Metrics/MethodLength
end
# rubocop:enable Metrics/ClassLength
28 changes: 21 additions & 7 deletions app/models/solution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,37 @@

class Solution < ApplicationRecord
has_many :solution_solution_classes, dependent: :destroy
enum status: { proposed: 100, accepted: 200, deprecated: 300 }
belongs_to :created_by, class_name: 'User'
belongs_to :approved_by, class_name: 'User', optional: true

def self.deep_load_solutions
Solution.all.includes(
solution_solution_classes: { solution_class: { theme: :sector } }
)
end

# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def self.hierarchy
hierarchy = []
deep_load_solutions.find_each do |s|
s.solution_solution_classes.each do |sc|
sector = populate_sector(hierarchy, sc.solution_class)
theme = populate_theme(sector, sc.solution_class)
solution_class = populate_solution_class(theme, sc.solution_class.name)
solution_class =
populate_solution_class(
theme,
sc.solution_class.name,
sc.solution_class.id
)
populate_solution(solution_class, sc.solution, sc.solution_class)
end
end
hierarchy
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength

def self.populate_sector(hierarchy, solution_class)
name = solution_class.theme.sector.name
Expand All @@ -43,13 +55,15 @@ def self.populate_theme(sector, solution_class)
theme
end

def self.populate_solution_class(theme, name)
solution_class = theme[:classes].find { |t| t[:name] == name }
if solution_class.nil?
solution_class = { name: name, solutions: [] }
theme[:classes] << solution_class
def self.populate_solution_class(theme, name, id)
mapped_solution_class = theme[:classes].find { |t| t[:name] == name }
if mapped_solution_class.nil?
mapped_solution_class = {
name: name, solution_class_id: id, solutions: []
}
theme[:classes] << mapped_solution_class
end
solution_class
mapped_solution_class
end

def self.populate_solution(mapped_solution_class, solution, solution_class)
Expand Down
5 changes: 0 additions & 5 deletions app/views/initiatives/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,6 @@
<div id="solution_picker"></div>
<% end %>

<%= f.form_field :alternative_solution_name do %>
<%= f.label :alternative_solution_name %>
<%= f.text_field :alternative_solution_name %>
<% end %>

<%= f.form_field :consent_to_share do %>
<%= f.label :consent_to_share, 'Are you willing to share the information provided publicly, and have you gained permission to do so from anyone referenced? ' %>
<%= f.check_box :consent_to_share %>
Expand Down
5 changes: 0 additions & 5 deletions app/views/initiatives/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@
<%= @initiative.location %>
</p>

<p>
<strong>Alternative solution name:</strong>
<%= @initiative.alternative_solution_name %>
</p>

<p>
<strong>Lead group:</strong>
<%= @initiative.lead_group_id %>
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20191110215537_proposed_solution.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class ProposedSolution < ActiveRecord::Migration[6.0]
def change
add_column :solutions, :status, :bigint, default: 100, null: false
add_reference :solutions, :created_by, references: :users
add_reference :solutions, :approved_by, references: :users
remove_column :initiatives, :alternative_solution_name, :string
end
end
8 changes: 6 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2019_11_07_233921) do
ActiveRecord::Schema.define(version: 2019_11_10_215537) do
# These are extensions that must be enabled in order to support this database
enable_extension 'plpgsql'

Expand Down Expand Up @@ -108,7 +108,6 @@
t.integer 'anticipated_carbon_saving'
t.string 'locality'
t.string 'location'
t.string 'alternative_solution_name'
t.bigint 'lead_group_id', null: false
t.string 'contact_name'
t.string 'contact_email'
Expand Down Expand Up @@ -153,6 +152,11 @@
t.string 'name'
t.datetime 'created_at', precision: 6, null: false
t.datetime 'updated_at', precision: 6, null: false
t.bigint 'status', default: 100, null: false
t.bigint 'created_by_id'
t.bigint 'approved_by_id'
t.index %w[approved_by_id], name: 'index_solutions_on_approved_by_id'
t.index %w[created_by_id], name: 'index_solutions_on_created_by_id'
end

create_table 'tags', force: :cascade do |t|
Expand Down
102 changes: 75 additions & 27 deletions frontend/packs/initiative_solution_picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ class SolutionPicker {
const key = `${sol.solution_id}-${sol.solution_class_id}`;
return this.solution_map[key];
});
console.log("solution init");
}

resetProposedSolution(solution_class_id) {
this.proposedSolution = { proposed: true, solution_class_id };
}

render() {
Expand Down Expand Up @@ -207,7 +210,12 @@ class SolutionPicker {
>
{this.navigation.theme.classes.map(solutionClass => {
return (
<li onclick={() => this.navigate({ solutionClass })}>
<li
onclick={() => {
this.resetProposedSolution(solutionClass.solution_class_id);
this.navigate({ solutionClass });
}}
>
{solutionClass.name}
</li>
);
Expand All @@ -222,32 +230,55 @@ class SolutionPicker {
return (
<ul class="AddInitiativeSolution-group AddInitiativeSolution-solutionSelector">
{this.navigation.solutionClass.solutions.map(solution => {
return (
<li>
{solution.name}{" "}
<span
class="AddInitiativeSolution-addSolution"
onclick={() =>
this.addSolution({
sector: this.navigation.sector.name,
theme: this.navigation.theme.name,
class: this.navigation.solutionClass.name,
solution: solution.name,
solution_id: solution.solution_id,
solution_class_id: solution.solution_class_id
})
}
>
[ add ]
</span>
</li>
);
return this.renderSolution(solution);
})}

{this.renderSolution(this.proposedSolution)}
</ul>
);
}
}

renderSolutionInput(solution) {
if (solution.name && !solution.proposed) {
return solution.name;
}

return (
<div>
<label>
Can't find the solution you are after? Suggest a new one:
<input type="text" binding="solution.name" />
</label>
</div>
);
}

renderSolution(solution) {
return (
<li>
{this.renderSolutionInput(solution)}{" "}
<span
class="AddInitiativeSolution-addSolution"
onclick={() => {
console.log("to add", solution);
this.addSolution({
sector: this.navigation.sector.name,
theme: this.navigation.theme.name,
class: this.navigation.solutionClass.name,
solution: solution.name,
solution_id: solution.solution_id,
solution_class_id: solution.solution_class_id,
proposed: solution.proposed
});
}}
>
[ add ]
</span>
</li>
);
}

renderChosenSolutions() {
if (this.solutions.length === 0) {
return (
Expand All @@ -270,11 +301,7 @@ class SolutionPicker {
>
X
</span>
<input
type="hidden"
name={`initiative[solutions_attributes][${index}][solution_id]`}
value={solution.solution_id}
/>
{this.renderSolutionField(solution, index)}
<input
type="hidden"
name={`initiative[solutions_attributes][${index}][solution_class_id]`}
Expand All @@ -288,10 +315,31 @@ class SolutionPicker {
}
}

renderSolutionField(solution, index) {
if (solution.proposed) {
return (
<input
type="hidden"
name={`initiative[solutions_attributes][${index}][proposed_solution]`}
value={solution.solution}
/>
);
}
return (
<input
type="hidden"
name={`initiative[solutions_attributes][${index}][solution_id]`}
value={solution.solution_id}
/>
);
}

addSolution(solution) {
console.log(solution);
this.solutions.push(solution);
this.value = "";
this.results = [];
this.resetProposedSolution(solution.solution_class_id);
}

removeSolution(solution) {
Expand Down
Loading

0 comments on commit f86a466

Please sign in to comment.