From a65315400bddcecafe18b312a43028afcc79390a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Carruitero?= Date: Mon, 28 Sep 2020 09:57:01 -0500 Subject: [PATCH 1/3] add question and answer models --- app/models/spree/answer.rb | 8 ++++++++ .../supplier/question_ability.rb | 15 +++++++++++++++ app/models/spree/question.rb | 10 ++++++++++ app/models/spree/supplier.rb | 1 + db/migrate/20200925013627_create_questions.rb | 13 +++++++++++++ db/migrate/20200925013705_create_answers.rb | 10 ++++++++++ .../permitted_attributes.rb | 19 +++++++++++++++++++ spec/models/spree/answer_spec.rb | 10 ++++++++++ spec/models/spree/question_spec.rb | 12 ++++++++++++ spec/models/spree/supplier_spec.rb | 1 + 10 files changed, 99 insertions(+) create mode 100644 app/models/spree/answer.rb create mode 100644 app/models/spree/permission_sets/supplier/question_ability.rb create mode 100644 app/models/spree/question.rb create mode 100644 db/migrate/20200925013627_create_questions.rb create mode 100644 db/migrate/20200925013705_create_answers.rb create mode 100644 spec/models/spree/answer_spec.rb create mode 100644 spec/models/spree/question_spec.rb diff --git a/app/models/spree/answer.rb b/app/models/spree/answer.rb new file mode 100644 index 00000000..88f28769 --- /dev/null +++ b/app/models/spree/answer.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Spree + class Answer < Spree::Base + belongs_to :question, class_name: 'Spree::Question' + belongs_to :user, class_name: Spree::UserClassHandle.new + end +end diff --git a/app/models/spree/permission_sets/supplier/question_ability.rb b/app/models/spree/permission_sets/supplier/question_ability.rb new file mode 100644 index 00000000..9fb805ae --- /dev/null +++ b/app/models/spree/permission_sets/supplier/question_ability.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'cancan' + +module Spree + module PermissionSets + module Supplier + class QuestionAbility < PermissionSets::Base + def activate! + can :manage, Spree::Question, supplier_id: user.supplier_id + end + end + end + end +end diff --git a/app/models/spree/question.rb b/app/models/spree/question.rb new file mode 100644 index 00000000..44e020d5 --- /dev/null +++ b/app/models/spree/question.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Spree + class Question < Spree::Base + has_many :answers, dependent: :destroy + belongs_to :created_by, class_name: Spree::UserClassHandle.new + belongs_to :supplier, class_name: 'Spree::Supplier' + belongs_to :product, class_name: 'Spree::Product', optional: true + end +end diff --git a/app/models/spree/supplier.rb b/app/models/spree/supplier.rb index 6a283757..4c2473f8 100644 --- a/app/models/spree/supplier.rb +++ b/app/models/spree/supplier.rb @@ -21,6 +21,7 @@ class Supplier < Spree::Base has_many :stock_locations, dependent: :destroy has_many :shipments, through: :stock_locations has_many :admins, class_name: Spree.user_class.to_s + has_many :questions, class_name: 'Spree::Question' accepts_nested_attributes_for :admins validates :commission_flat_rate, presence: true diff --git a/db/migrate/20200925013627_create_questions.rb b/db/migrate/20200925013627_create_questions.rb new file mode 100644 index 00000000..38fb2938 --- /dev/null +++ b/db/migrate/20200925013627_create_questions.rb @@ -0,0 +1,13 @@ +class CreateQuestions < ActiveRecord::Migration[5.2] + def change + create_table :spree_questions do |t| + t.text :question_text + t.integer :created_by_id + t.integer :supplier_id + t.integer :product_id + t.boolean :published, default: false + t.boolean :resolved, default: false + t.timestamps + end + end +end diff --git a/db/migrate/20200925013705_create_answers.rb b/db/migrate/20200925013705_create_answers.rb new file mode 100644 index 00000000..d0a1f4cc --- /dev/null +++ b/db/migrate/20200925013705_create_answers.rb @@ -0,0 +1,10 @@ +class CreateAnswers < ActiveRecord::Migration[5.2] + def change + create_table :spree_answers do |t| + t.text :answer_text + t.integer :question_id + t.integer :user_id + t.timestamps + end + end +end diff --git a/lib/solidus_marketplace/permitted_attributes.rb b/lib/solidus_marketplace/permitted_attributes.rb index 279648c8..a2124db1 100644 --- a/lib/solidus_marketplace/permitted_attributes.rb +++ b/lib/solidus_marketplace/permitted_attributes.rb @@ -24,5 +24,24 @@ class << self mattr_reader(:supplier_attributes) end + + def question_attributes + %i[ + id + question_text + created_by_id + supplier_id + product_id + ] + end + + def answer_attributes + %i[ + id + answer_text + question_id + user_id + ] + end end end diff --git a/spec/models/spree/answer_spec.rb b/spec/models/spree/answer_spec.rb new file mode 100644 index 00000000..dbcc6698 --- /dev/null +++ b/spec/models/spree/answer_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Spree::Answer, type: :model do + describe 'associations' do + it { is_expected.to respond_to(:question) } + it { is_expected.to respond_to(:user) } + end +end diff --git a/spec/models/spree/question_spec.rb b/spec/models/spree/question_spec.rb new file mode 100644 index 00000000..c01fba82 --- /dev/null +++ b/spec/models/spree/question_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Spree::Question, type: :model do + describe 'associations' do + it { is_expected.to respond_to(:answers) } + it { is_expected.to respond_to(:created_by) } + it { is_expected.to respond_to(:supplier) } + it { is_expected.to respond_to(:product) } + end +end diff --git a/spec/models/spree/supplier_spec.rb b/spec/models/spree/supplier_spec.rb index 4b060de9..9c4cf000 100644 --- a/spec/models/spree/supplier_spec.rb +++ b/spec/models/spree/supplier_spec.rb @@ -5,6 +5,7 @@ it { is_expected.to respond_to(:products) } it { is_expected.to respond_to(:stock_locations) } it { is_expected.to respond_to(:variants) } + it { is_expected.to respond_to(:questions) } it '#deleted?' do subject.deleted_at = nil From d63f0c6b7ffd1321f99e75df87682e39c0c27b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Carruitero?= Date: Tue, 29 Sep 2020 22:50:20 -0500 Subject: [PATCH 2/3] add api questions controllers --- .../solidus_marketplace/api_controller.rb | 8 ++ .../spree/api/api_helpers_decorator.rb | 27 ------- .../solidus_marketplace/api_helpers.rb | 7 ++ .../supplier/question_ability.rb | 7 ++ config/routes.rb | 7 +- .../api/spree/api/answers_controller.rb | 19 +++++ .../api/spree/api/questions_controller.rb | 13 ++++ lib/solidus_marketplace/engine.rb | 2 +- lib/solidus_marketplace/factories.rb | 12 +++ .../spree/api/answers/_answer.json.jbuilder | 6 ++ .../api/spree/api/answers/index.json.jbuilder | 6 ++ .../api/spree/api/answers/show.json.jbuilder | 3 + .../api/questions/_question.json.jbuilder | 6 ++ .../spree/api/questions/index.json.jbuilder | 6 ++ .../spree/api/questions/show.json.jbuilder | 3 + .../supplier/question_ability_spec.rb | 23 ++++++ .../spree/api/answers_controller_spec.rb | 78 +++++++++++++++++++ .../spree/api/questions_controller_spec.rb | 65 ++++++++++++++++ spec/spec_helper.rb | 4 + 19 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 app/controllers/solidus_marketplace/api_controller.rb delete mode 100644 app/decorators/helpers/solidus_marketplace/spree/api/api_helpers_decorator.rb create mode 100644 app/helpers/solidus_marketplace/api_helpers.rb create mode 100644 lib/controllers/api/spree/api/answers_controller.rb create mode 100644 lib/controllers/api/spree/api/questions_controller.rb create mode 100644 lib/views/api/spree/api/answers/_answer.json.jbuilder create mode 100644 lib/views/api/spree/api/answers/index.json.jbuilder create mode 100644 lib/views/api/spree/api/answers/show.json.jbuilder create mode 100644 lib/views/api/spree/api/questions/_question.json.jbuilder create mode 100644 lib/views/api/spree/api/questions/index.json.jbuilder create mode 100644 lib/views/api/spree/api/questions/show.json.jbuilder create mode 100644 spec/models/spree/permission_sets/supplier/question_ability_spec.rb create mode 100644 spec/requests/spree/api/answers_controller_spec.rb create mode 100644 spec/requests/spree/api/questions_controller_spec.rb diff --git a/app/controllers/solidus_marketplace/api_controller.rb b/app/controllers/solidus_marketplace/api_controller.rb new file mode 100644 index 00000000..2c97b8ec --- /dev/null +++ b/app/controllers/solidus_marketplace/api_controller.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module SolidusMarketplace + class ApiController < ::Spree::Api::ResourceController + helper SolidusMarketplace::ApiHelpers + end +end + diff --git a/app/decorators/helpers/solidus_marketplace/spree/api/api_helpers_decorator.rb b/app/decorators/helpers/solidus_marketplace/spree/api/api_helpers_decorator.rb deleted file mode 100644 index 2c08be25..00000000 --- a/app/decorators/helpers/solidus_marketplace/spree/api/api_helpers_decorator.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module SolidusMarketplace - module Spree - module Api - module ApiHelpersDecorator - @@supplier_attributes = [ - :id, - :address_id, - :commission_flat_rate, - :commission_percentage, - :user_id, - :name, - :url, - :deleted_at, - :tax_id, - :token, - :slug - ] - - mattr_reader(:supplier_attributes) - - ::Spree::Api::ApiHelpers.prepend self - end - end - end -end diff --git a/app/helpers/solidus_marketplace/api_helpers.rb b/app/helpers/solidus_marketplace/api_helpers.rb new file mode 100644 index 00000000..b3445011 --- /dev/null +++ b/app/helpers/solidus_marketplace/api_helpers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module SolidusMarketplace + module ApiHelpers + include SolidusMarketplace::PermittedAttributes + end +end diff --git a/app/models/spree/permission_sets/supplier/question_ability.rb b/app/models/spree/permission_sets/supplier/question_ability.rb index 9fb805ae..4c03a429 100644 --- a/app/models/spree/permission_sets/supplier/question_ability.rb +++ b/app/models/spree/permission_sets/supplier/question_ability.rb @@ -8,6 +8,13 @@ module Supplier class QuestionAbility < PermissionSets::Base def activate! can :manage, Spree::Question, supplier_id: user.supplier_id + can :manage, Spree::Answer, question_id: question_ids + end + + private + + def question_ids + user.supplier.questions.pluck(:id) end end end diff --git a/config/routes.rb b/config/routes.rb index 6db4d2a2..56b3d2df 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,9 +17,14 @@ put :addcard end end + + resources :questions, only: %i[index] end - namespace :api do + namespace :api, defaults: { format: :json } do resources :suppliers, only: :index + resources :questions do + resources :answers + end end end diff --git a/lib/controllers/api/spree/api/answers_controller.rb b/lib/controllers/api/spree/api/answers_controller.rb new file mode 100644 index 00000000..4007aa9f --- /dev/null +++ b/lib/controllers/api/spree/api/answers_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Spree + module Api + class AnswersController < ::SolidusMarketplace::ApiController + before_action :normalize_params, only: %i[create] + + private + + def permitted_answer_attributes + permitted_attributes.answer_attributes + end + + def normalize_params + params[:answer][:user_id] = current_api_user.id + end + end + end +end diff --git a/lib/controllers/api/spree/api/questions_controller.rb b/lib/controllers/api/spree/api/questions_controller.rb new file mode 100644 index 00000000..3563ec1a --- /dev/null +++ b/lib/controllers/api/spree/api/questions_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Spree + module Api + class QuestionsController < ::SolidusMarketplace::ApiController + private + + def permitted_question_attributes + permitted_attributes.question_attributes + end + end + end +end diff --git a/lib/solidus_marketplace/engine.rb b/lib/solidus_marketplace/engine.rb index 497236c9..bb6c20b9 100644 --- a/lib/solidus_marketplace/engine.rb +++ b/lib/solidus_marketplace/engine.rb @@ -24,7 +24,7 @@ class Engine < Rails::Engine initializer 'solidus_marketplace.preferences', before: :load_config_initializers do |app| SolidusMarketplace::Config = SolidusMarketplace::Configuration.new Spree::PermittedAttributes.singleton_class.prepend(SolidusMarketplace::PermittedAttributes) - Spree::Config.roles.assign_permissions :supplier_admin, ['Spree::PermissionSets::Supplier::AdminAbility'] + Spree::Config.roles.assign_permissions :supplier_admin, ['Spree::PermissionSets::Supplier::AdminAbility', 'Spree::PermissionSets::Supplier::QuestionAbility'] Spree::Config.roles.assign_permissions :supplier_staff, ['Spree::PermissionSets::Supplier::StaffAbility', 'Spree::PermissionSets::OrderManagement'] end diff --git a/lib/solidus_marketplace/factories.rb b/lib/solidus_marketplace/factories.rb index 88f781b3..d9b13853 100644 --- a/lib/solidus_marketplace/factories.rb +++ b/lib/solidus_marketplace/factories.rb @@ -110,4 +110,16 @@ end end end + + factory :question, class: 'Spree::Question' do + supplier { create(:supplier) } + question_text { 'Do you deliver to my home?' } + created_by { create(:user) } + end + + factory :answer, class: 'Spree::Answer' do + answer_text { 'foo' } + question { create(:question) } + user { create(:user) } + end end diff --git a/lib/views/api/spree/api/answers/_answer.json.jbuilder b/lib/views/api/spree/api/answers/_answer.json.jbuilder new file mode 100644 index 00000000..ce4dbdbf --- /dev/null +++ b/lib/views/api/spree/api/answers/_answer.json.jbuilder @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +json.(answer, *answer_attributes) +json.user do + json.partial!('spree/api/users/user', user: answer.user) +end diff --git a/lib/views/api/spree/api/answers/index.json.jbuilder b/lib/views/api/spree/api/answers/index.json.jbuilder new file mode 100644 index 00000000..1048e5f8 --- /dev/null +++ b/lib/views/api/spree/api/answers/index.json.jbuilder @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +json.answers(@answers) do |answer| + json.partial!('spree/api/answers/answer', answer: answer) +end +json.partial! 'spree/api/shared/pagination', pagination: @answers diff --git a/lib/views/api/spree/api/answers/show.json.jbuilder b/lib/views/api/spree/api/answers/show.json.jbuilder new file mode 100644 index 00000000..85a77b28 --- /dev/null +++ b/lib/views/api/spree/api/answers/show.json.jbuilder @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +json.partial!('spree/api/answers/answer', answer: @answer) diff --git a/lib/views/api/spree/api/questions/_question.json.jbuilder b/lib/views/api/spree/api/questions/_question.json.jbuilder new file mode 100644 index 00000000..26dcc1be --- /dev/null +++ b/lib/views/api/spree/api/questions/_question.json.jbuilder @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +json.(question, *question_attributes) +json.answers(question.answers) do |answer| + json.partial!('spree/api/answers/answer', answer: answer) +end diff --git a/lib/views/api/spree/api/questions/index.json.jbuilder b/lib/views/api/spree/api/questions/index.json.jbuilder new file mode 100644 index 00000000..e2a79b20 --- /dev/null +++ b/lib/views/api/spree/api/questions/index.json.jbuilder @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +json.questions(@questions) do |question| + json.partial!('spree/api/questions/question', question: question) +end +json.partial! 'spree/api/shared/pagination', pagination: @questions diff --git a/lib/views/api/spree/api/questions/show.json.jbuilder b/lib/views/api/spree/api/questions/show.json.jbuilder new file mode 100644 index 00000000..9c10ce37 --- /dev/null +++ b/lib/views/api/spree/api/questions/show.json.jbuilder @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +json.partial!('spree/api/questions/question', question: @question) diff --git a/spec/models/spree/permission_sets/supplier/question_ability_spec.rb b/spec/models/spree/permission_sets/supplier/question_ability_spec.rb new file mode 100644 index 00000000..ea7e20ba --- /dev/null +++ b/spec/models/spree/permission_sets/supplier/question_ability_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'cancan' +require 'cancan/matchers' +require 'spree/testing_support/ability_helpers' + +describe Spree::PermissionSets::Supplier::QuestionAbility do + let(:ability) { Spree::Ability.new(user) } + let(:supplier) { create(:supplier) } + let(:supplier_role) { build(:role, name: 'supplier_admin') } + let(:user) { create(:user, supplier: supplier) } + subject { ability } + let(:resource) { Spree::Question } + + before(:each) do + user.spree_roles << supplier_role + described_class.new(ability).activate! + end + + it 'allow manage questions' do + expect(ability).to be_able_to :manage, resource + end +end diff --git a/spec/requests/spree/api/answers_controller_spec.rb b/spec/requests/spree/api/answers_controller_spec.rb new file mode 100644 index 00000000..62c20c81 --- /dev/null +++ b/spec/requests/spree/api/answers_controller_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Spree::Api::AnswersController, type: :request do + let(:user) { create(:supplier_admin) } + let(:supplier) { create(:supplier, admins: [user]) } + let!(:question) { create(:question, supplier: supplier) } + let!(:answer) { create(:answer, question: question, user: user) } + let(:params) { + { + answer: { + answer_text: 'bar', + question_id: question.id + } + } + } + + before do + user.generate_spree_api_key! + end + + describe '#index' do + it 'return answers for question' do + get "/api/questions/#{question.id}/answers", + headers: { Authorization: "Bearer #{user.spree_api_key}" } + expect(json_response['answers'].count).to eq(1) + end + end + + describe '#show' do + it 'return correctly answer' do + get "/api/questions/#{question.id}/answers/#{answer.id}", + headers: { Authorization: "Bearer #{user.spree_api_key}" } + + expect(json_response['answer_text']).to eq 'foo' + end + end + + describe '#create' do + it 'succesful create' do + expect do + post "/api/questions/#{question.id}/answers", + headers: { Authorization: "Bearer #{user.spree_api_key}" }, + params: params + end.to change { Spree::Answer.count }.by(1) + end + + context 'with user_id param' do + it 'use request user_id for answer' do + other_user = create(:user) + params[:answer][:user_id] = other_user.id + post "/api/questions/#{question.id}/answers", + headers: { Authorization: "Bearer #{user.spree_api_key}" }, + params: params + expect(json_response['user']['email']).to eq(user.email) + end + end + end + + describe '#update' do + it 'successful update' do + put "/api/questions/#{question.id}/answers/#{answer.id}", + headers: { Authorization: "Bearer #{user.spree_api_key}" }, + params: params.merge(answer: { answer_text: 'baz' }) + expect(json_response['answer_text']).to eq('baz') + end + end + + describe '#destroy' do + it 'successful destroy' do + expect do + delete "/api/questions/#{question.id}/answers/#{answer.id}", + headers: { Authorization: "Bearer #{user.spree_api_key}" } + end.to change { Spree::Answer.count }.by(-1) + end + end +end diff --git a/spec/requests/spree/api/questions_controller_spec.rb b/spec/requests/spree/api/questions_controller_spec.rb new file mode 100644 index 00000000..38f00962 --- /dev/null +++ b/spec/requests/spree/api/questions_controller_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Spree::Api::QuestionsController, type: :request do + let(:user) { create(:supplier_admin) } + let(:supplier) { create(:supplier, admins: [user]) } + let!(:question) { create(:question, supplier: supplier) } + let(:buyer) { create(:user) } + + before do + user.generate_spree_api_key! + buyer.generate_spree_api_key! + end + + describe '#index' do + it 'returns questions asked to supplier' do + get '/api/questions', + headers: { Authorization: "Bearer #{user.spree_api_key}" } + expect(json_response['questions'].count).to eq(1) + end + end + + describe '#create' do + context 'with valid params' do + let(:valid_params) { + { + question: { + question_text: 'foo?', + supplier_id: supplier.id, + created_by_id: buyer.id + } + } + } + + it 'succesfully create question' do + expect do + post '/api/questions', + params: valid_params, + headers: { Authorization: "Bearer #{user.spree_api_key}" } + end.to change { Spree::Question.count }.by(1) + end + end + end + + describe '#update' do + it 'update successfully' do + question_text = 'bar?' + put "/api/questions/#{question.id}", + params: { question: { question_text: question_text } }, + headers: { Authorization: "Bearer #{user.spree_api_key}" } + + expect(json_response['question_text']).to eq(question_text) + end + end + + describe '#destroy' do + it 'destroy successfully' do + expect do + delete "/api/questions/#{question.id}", + headers: { Authorization: "Bearer #{user.spree_api_key}" } + end.to change { Spree::Question.count }.by(-1) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bd7b84f7..484e62e7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -11,6 +11,8 @@ # Requires factories and other useful helpers defined in spree_core. require 'solidus_dev_support/rspec/feature_helper' +require 'spree/api/testing_support/helpers' + # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f } @@ -21,4 +23,6 @@ RSpec.configure do |config| config.infer_spec_type_from_file_location! config.use_transactional_fixtures = false + + config.include Spree::Api::TestingSupport::Helpers, type: :request end From 0537aed4abbf1383557492b76ae19d7c315b0cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Carruitero?= Date: Thu, 1 Oct 2020 18:03:12 -0500 Subject: [PATCH 3/3] add admin views for questions --- .../spree/backend/collections/answers.js | 9 ++++ .../spree/backend/collections/questions.js | 5 +++ .../spree/backend/models/answer.js | 6 +++ .../spree/backend/models/question.js | 19 +++++++++ .../javascripts/spree/backend/questions.js | 15 +++++++ .../spree/backend/solidus_marketplace.js | 4 +- .../backend/solidus_marketplace_namespaces.js | 4 ++ .../backend/templates/answers/answer-item.hbs | 4 ++ .../spree/backend/templates/answers/new.hbs | 2 + .../templates/questions/modal-content.hbs | 4 ++ .../backend/views/answers/answer-item.js | 15 +++++++ .../backend/views/answers/answer-list.js | 21 ++++++++++ .../spree/backend/views/answers/new.js | 41 ++++++++++++++++++ .../backend/views/questions/modal-content.js | 25 +++++++++++ config/locales/en.yml | 3 ++ config/locales/es.yml | 3 ++ .../spree/admin/questions_controller.rb | 16 +++++++ .../admin/questions/_question_modal.html.erb | 15 +++++++ .../spree/admin/questions/index.html.erb | 41 ++++++++++++++++++ spec/features/spree/admin/questions_spec.rb | 42 +++++++++++++++++++ 20 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/spree/backend/collections/answers.js create mode 100644 app/assets/javascripts/spree/backend/collections/questions.js create mode 100644 app/assets/javascripts/spree/backend/models/answer.js create mode 100644 app/assets/javascripts/spree/backend/models/question.js create mode 100644 app/assets/javascripts/spree/backend/questions.js create mode 100644 app/assets/javascripts/spree/backend/solidus_marketplace_namespaces.js create mode 100644 app/assets/javascripts/spree/backend/templates/answers/answer-item.hbs create mode 100644 app/assets/javascripts/spree/backend/templates/answers/new.hbs create mode 100644 app/assets/javascripts/spree/backend/templates/questions/modal-content.hbs create mode 100644 app/assets/javascripts/spree/backend/views/answers/answer-item.js create mode 100644 app/assets/javascripts/spree/backend/views/answers/answer-list.js create mode 100644 app/assets/javascripts/spree/backend/views/answers/new.js create mode 100644 app/assets/javascripts/spree/backend/views/questions/modal-content.js create mode 100644 lib/controllers/backend/spree/admin/questions_controller.rb create mode 100644 lib/views/backend/spree/admin/questions/_question_modal.html.erb create mode 100644 lib/views/backend/spree/admin/questions/index.html.erb create mode 100644 spec/features/spree/admin/questions_spec.rb diff --git a/app/assets/javascripts/spree/backend/collections/answers.js b/app/assets/javascripts/spree/backend/collections/answers.js new file mode 100644 index 00000000..e3f3fbe4 --- /dev/null +++ b/app/assets/javascripts/spree/backend/collections/answers.js @@ -0,0 +1,9 @@ +//= require spree/backend/models/answer + +Spree.Collections.Answers = Backbone.Collection.extend({ + model: Spree.Models.Answer, + + url: function() { + return this.parent.url() + '/answers'; + } +}); diff --git a/app/assets/javascripts/spree/backend/collections/questions.js b/app/assets/javascripts/spree/backend/collections/questions.js new file mode 100644 index 00000000..284d269f --- /dev/null +++ b/app/assets/javascripts/spree/backend/collections/questions.js @@ -0,0 +1,5 @@ +//= require spree/backend/models/question + +Spree.Collections.Questions = Backbone.Collection.extend({ + model: Spree.Models.Question +}) diff --git a/app/assets/javascripts/spree/backend/models/answer.js b/app/assets/javascripts/spree/backend/models/answer.js new file mode 100644 index 00000000..518a787d --- /dev/null +++ b/app/assets/javascripts/spree/backend/models/answer.js @@ -0,0 +1,6 @@ +Spree.Models.Answer = Backbone.Model.extend({ + paramRoot: 'answer', + urlRoot: function() { + return Spree.pathFor('api/questions/' + this.get('question_id') + '/answers'); + } +}); diff --git a/app/assets/javascripts/spree/backend/models/question.js b/app/assets/javascripts/spree/backend/models/question.js new file mode 100644 index 00000000..aee88cdb --- /dev/null +++ b/app/assets/javascripts/spree/backend/models/question.js @@ -0,0 +1,19 @@ +//=require spree/backend/collections/answers + +Spree.Models.Question = Backbone.Model.extend({ + urlRoot: Spree.pathFor('api/questions'), + + relations: { + 'answers': Spree.Collections.Answers + } +}); + +Spree.Models.Question.fetch = function(id, opts) { + const options = (opts || {}); + const model = new Spree.Models.Question({ + id: id, + answers: [] + }); + model.fetch(options); + return model; +} diff --git a/app/assets/javascripts/spree/backend/questions.js b/app/assets/javascripts/spree/backend/questions.js new file mode 100644 index 00000000..9af22d19 --- /dev/null +++ b/app/assets/javascripts/spree/backend/questions.js @@ -0,0 +1,15 @@ +//= require spree/backend/models/question +//= require spree/backend/views/questions/modal-content + +Spree.ready(function() { + if ($('.question-modal-content').length) { + $('.question-modal-content').each(function() { + var el = $(this); + var model = new Spree.Models.Question.fetch(el.data('question_id')); + new Spree.Views.Questions.ModalContent({ + el: el, + model: model + }); + }); + } +}); diff --git a/app/assets/javascripts/spree/backend/solidus_marketplace.js b/app/assets/javascripts/spree/backend/solidus_marketplace.js index dee90940..8380b155 100644 --- a/app/assets/javascripts/spree/backend/solidus_marketplace.js +++ b/app/assets/javascripts/spree/backend/solidus_marketplace.js @@ -2,4 +2,6 @@ // Shipments AJAX API //= require spree/backend/solidus_marketplace_routes -//= require spree/backend/suppliers_autocomplete \ No newline at end of file +//= require spree/backend/suppliers_autocomplete +//= require spree/backend/solidus_marketplace_namespaces +//= require spree/backend/questions diff --git a/app/assets/javascripts/spree/backend/solidus_marketplace_namespaces.js b/app/assets/javascripts/spree/backend/solidus_marketplace_namespaces.js new file mode 100644 index 00000000..3fafaad5 --- /dev/null +++ b/app/assets/javascripts/spree/backend/solidus_marketplace_namespaces.js @@ -0,0 +1,4 @@ +_.extend(window.Spree.Views, { + Questions: {}, + Answers: {} +}); diff --git a/app/assets/javascripts/spree/backend/templates/answers/answer-item.hbs b/app/assets/javascripts/spree/backend/templates/answers/answer-item.hbs new file mode 100644 index 00000000..1b7fe4ec --- /dev/null +++ b/app/assets/javascripts/spree/backend/templates/answers/answer-item.hbs @@ -0,0 +1,4 @@ +
+ {{answer.answer_text}} +

{{answer.user.email}} - {{answer.created_at}}

+
diff --git a/app/assets/javascripts/spree/backend/templates/answers/new.hbs b/app/assets/javascripts/spree/backend/templates/answers/new.hbs new file mode 100644 index 00000000..d57532d5 --- /dev/null +++ b/app/assets/javascripts/spree/backend/templates/answers/new.hbs @@ -0,0 +1,2 @@ + + diff --git a/app/assets/javascripts/spree/backend/templates/questions/modal-content.hbs b/app/assets/javascripts/spree/backend/templates/questions/modal-content.hbs new file mode 100644 index 00000000..ba22d3ea --- /dev/null +++ b/app/assets/javascripts/spree/backend/templates/questions/modal-content.hbs @@ -0,0 +1,4 @@ +
+
+
+
diff --git a/app/assets/javascripts/spree/backend/views/answers/answer-item.js b/app/assets/javascripts/spree/backend/views/answers/answer-item.js new file mode 100644 index 00000000..50301415 --- /dev/null +++ b/app/assets/javascripts/spree/backend/views/answers/answer-item.js @@ -0,0 +1,15 @@ +//= require spree/backend/templates/answers/answer-item + +Spree.Views.Answers.AnswerItem = Backbone.View.extend({ + initialize: function(options) { + this.listenTo(this.model, 'change', this.render); + }, + + template: HandlebarsTemplates['answers/answer-item'], + + render: function() { + this.$el.html(this.template({ + answer: this.model.toJSON() + })); + } +}); diff --git a/app/assets/javascripts/spree/backend/views/answers/answer-list.js b/app/assets/javascripts/spree/backend/views/answers/answer-list.js new file mode 100644 index 00000000..f9803b2b --- /dev/null +++ b/app/assets/javascripts/spree/backend/views/answers/answer-list.js @@ -0,0 +1,21 @@ +//= require spree/backend/views/answers/answer-item + +Spree.Views.Answers.AnswerList = Backbone.View.extend({ + initialize: function() { + this.listenTo(this.collection, 'add', this.add); + this.listenTo(this.collection, 'reset', this.reset); + }, + + add: function(answer) { + var view = new Spree.Views.Answers.AnswerItem({model: answer}); + view.render(); + this.$el.append(view.el); + }, + + reset: function() { + const add = this.add; + this.collection.each(function(answer) { + add(answer) + }) + } +}); diff --git a/app/assets/javascripts/spree/backend/views/answers/new.js b/app/assets/javascripts/spree/backend/views/answers/new.js new file mode 100644 index 00000000..b7617031 --- /dev/null +++ b/app/assets/javascripts/spree/backend/views/answers/new.js @@ -0,0 +1,41 @@ +//= require spree/backend/templates/answers/new +//= require spree/backend/models/answer + +Spree.Views.Answers.New = Backbone.View.extend({ + initialize: function() { + this.listenTo(this.collection, 'update', this.render) + this.render(); + }, + + template: HandlebarsTemplates['answers/new'], + + events: { + 'click .submit': 'onSubmit' + }, + + onSubmit: function(e) { + e.preventDefault(); + const input = $('.answer-input') + const options = { + success: this.onSuccess.bind(this), + error: this.onError.bind(this) + }; + this.collection.create({ + answer_text: input.val(), + question_id: this.collection.parent.id + }, options) + input.val(''); + }, + + onSuccess: function() { + show_flash("success", Spree.translations.created_successfully); + }, + + onError: function(model, response, options) { + show_flash("error", response.responseText); + }, + + render: function() { + this.$el.html(this.template()); + } +}); diff --git a/app/assets/javascripts/spree/backend/views/questions/modal-content.js b/app/assets/javascripts/spree/backend/views/questions/modal-content.js new file mode 100644 index 00000000..2a495ad3 --- /dev/null +++ b/app/assets/javascripts/spree/backend/views/questions/modal-content.js @@ -0,0 +1,25 @@ +//= require spree/backend/templates/questions/modal-content +//= require spree/backend/views/answers/new +//= require spree/backend/views/answers/answer-list + +Spree.Views.Questions.ModalContent = Backbone.View.extend({ + initialize: function(options){ + this.render(); + }, + template: HandlebarsTemplates['questions/modal-content'], + + render: function() { + this.$el.html(this.template()); + const collection = this.model.get('answers'); + + new Spree.Views.Answers.AnswerList({ + el: $('.answers'), + parent: this.model, + collection: collection + }); + new Spree.Views.Answers.New({ + el: $('.new-answer'), + collection: collection + }); + } +}); diff --git a/config/locales/en.yml b/config/locales/en.yml index 1f6f3d73..9f6f7677 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -128,3 +128,6 @@ en: user_admin: Admin User remove_payment_method: Remove payment method add_payment_method: Add payment method + questions: + title: Questions + reply: Reply diff --git a/config/locales/es.yml b/config/locales/es.yml index 921206e3..852b0dda 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -125,3 +125,6 @@ es: user_admin: Usuario administrador remove_payment_method: Remover método de pago add_payment_method: Agregar método de pago + questions: + title: Preguntas + reply: Contestar diff --git a/lib/controllers/backend/spree/admin/questions_controller.rb b/lib/controllers/backend/spree/admin/questions_controller.rb new file mode 100644 index 00000000..93985e93 --- /dev/null +++ b/lib/controllers/backend/spree/admin/questions_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Spree + module Admin + class QuestionsController < BaseController + def index + params[:q] ||= {} + @search = Spree::Question.accessible_by(current_ability, :index) + .ransack(params[:q]) + @questions = @search.result + .page(params[:page]) + .per(params[:per_page] || Spree::Config[:orders_per_page]) + end + end + end +end diff --git a/lib/views/backend/spree/admin/questions/_question_modal.html.erb b/lib/views/backend/spree/admin/questions/_question_modal.html.erb new file mode 100644 index 00000000..181c1818 --- /dev/null +++ b/lib/views/backend/spree/admin/questions/_question_modal.html.erb @@ -0,0 +1,15 @@ + diff --git a/lib/views/backend/spree/admin/questions/index.html.erb b/lib/views/backend/spree/admin/questions/index.html.erb new file mode 100644 index 00000000..6f253fb1 --- /dev/null +++ b/lib/views/backend/spree/admin/questions/index.html.erb @@ -0,0 +1,41 @@ +<% admin_layout "full-width" %> + +<% admin_breadcrumb(plural_resource_name(Spree::Question)) %> + +<%= paginate @questions, theme: "solidus_admin" %> +<% if @questions.any? %> + + + + + + + + + + + + <% @questions.each do |question| %> + + + + + + + + + <%= render 'question_modal', + question: question, + target: "modal-question#{question.id}" + %> + <% end %> + +<% else %> +
+ <% if can? :manage, Spree::Question %> + <%= render 'spree/admin/shared/no_objects_found', + resource: Spree::Question %> + <% end %> +
+<% end %> +<%= paginate @questions, theme: "solidus_admin" %> diff --git a/spec/features/spree/admin/questions_spec.rb b/spec/features/spree/admin/questions_spec.rb new file mode 100644 index 00000000..cae58eed --- /dev/null +++ b/spec/features/spree/admin/questions_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Questions admin page', type: :feature, js: true do + let(:user) { create(:supplier_admin) } + let(:supplier) { create(:supplier, admins: [user]) } + let!(:question) { create(:question, supplier: supplier, question_text: 'How much?') } + let!(:answer) { create(:answer, question: question, user: user, answer_text: 'Answer0') } + + def login_user(user, password = 'secret') + visit spree.admin_login_path + fill_in 'Email', with: user.email + fill_in 'Password', with: password + click_button 'Login' + end + + before do + login_user(user) + visit spree.admin_questions_path + end + + it 'render supplier questions' do + expect(page).to have_content('How much?') + end + + context 'with modal' do + before do + click_on('Reply') + end + + it 'show answers' do + expect(page).to have_content('Answer0') + end + + it 'allow reply question' do + find('.answer-input').fill_in with: 'Answer1' + click_on('Create') + expect(page).to have_content('Answer1') + end + end +end
<%= sort_link @search, :question_text %><%= sort_link @search, :created_at %><%= sort_link @search, :created_by %><%= sort_link @search, :published %><%= sort_link @search, :resolved %>
<%= question.question_text %><%= question.created_at %><%= question.created_by.email %><%= question.published %><%= question.resolved %><%= link_to t('spree.questions.reply'), '#', class: 'btn btn-primary', data: { toggle: 'modal', target: "#modal-question#{question.id}"} %>