voluntary_feedback 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +3 -2
  4. data/app/assets/javascripts/voluntary_feedback/app.js.coffee +9 -0
  5. data/app/assets/javascripts/voluntary_feedback/application.js +3 -0
  6. data/app/assets/javascripts/voluntary_feedback/components/reply_cell_component.js.coffee +44 -0
  7. data/app/assets/javascripts/voluntary_feedback/components/reply_comments_component.js.coffee +17 -0
  8. data/app/assets/javascripts/voluntary_feedback/controllers/community/categories_controller.js.coffee +5 -0
  9. data/app/assets/javascripts/voluntary_feedback/controllers/community/category_controller.js.coffee +1 -0
  10. data/app/assets/javascripts/voluntary_feedback/controllers/community/communities_controller.js.coffee +5 -0
  11. data/app/assets/javascripts/voluntary_feedback/controllers/community/community_controller.js.coffee +2 -0
  12. data/app/assets/javascripts/voluntary_feedback/controllers/community/edit_category_controller.js.coffee +2 -0
  13. data/app/assets/javascripts/voluntary_feedback/controllers/community/edit_community_controller.js.coffee +2 -0
  14. data/app/assets/javascripts/voluntary_feedback/controllers/community/edit_feedback_controller.js.coffee +2 -0
  15. data/app/assets/javascripts/voluntary_feedback/controllers/community/feedback_controller.js.coffee +25 -0
  16. data/app/assets/javascripts/voluntary_feedback/controllers/community/feedbacks_controller.js.coffee +11 -0
  17. data/app/assets/javascripts/voluntary_feedback/controllers/community/new_category_controller.js.coffee +2 -0
  18. data/app/assets/javascripts/voluntary_feedback/controllers/community/new_community_controller.js.coffee +2 -0
  19. data/app/assets/javascripts/voluntary_feedback/controllers/community/new_feedback_controller.js.coffee +2 -0
  20. data/app/assets/javascripts/voluntary_feedback/helpers/mood_icon_helper.js.coffee +11 -0
  21. data/app/assets/javascripts/voluntary_feedback/mixins/community_category_persistence.js.coffee +29 -0
  22. data/app/assets/javascripts/voluntary_feedback/mixins/community_persistence.js.coffee +33 -0
  23. data/app/assets/javascripts/voluntary_feedback/mixins/feedback_persistence.js.coffee +39 -0
  24. data/app/assets/javascripts/voluntary_feedback/mixins/pagination_controller.js.coffee +56 -0
  25. data/app/assets/javascripts/voluntary_feedback/models/community.js.coffee +7 -0
  26. data/app/assets/javascripts/voluntary_feedback/models/community_category.js.coffee +4 -0
  27. data/app/assets/javascripts/voluntary_feedback/models/feedback.js.coffee +12 -0
  28. data/app/assets/javascripts/voluntary_feedback/models/reply.js.coffee +6 -0
  29. data/app/assets/javascripts/voluntary_feedback/models/user.js.coffee +18 -0
  30. data/app/assets/javascripts/voluntary_feedback/router.js.coffee +34 -0
  31. data/app/assets/javascripts/voluntary_feedback/routes/community/categories_route.js.coffee +10 -0
  32. data/app/assets/javascripts/voluntary_feedback/routes/community/category_route.js.coffee +7 -0
  33. data/app/assets/javascripts/voluntary_feedback/routes/community/communities_route.js.coffee +9 -0
  34. data/app/assets/javascripts/voluntary_feedback/routes/community/community_route.js.coffee +4 -0
  35. data/app/assets/javascripts/voluntary_feedback/routes/community/edit_category_route.js.coffee +9 -0
  36. data/app/assets/javascripts/voluntary_feedback/routes/community/edit_community_route.js.coffee +16 -0
  37. data/app/assets/javascripts/voluntary_feedback/routes/community/edit_feedback_route.js.coffee +29 -0
  38. data/app/assets/javascripts/voluntary_feedback/routes/community/feedback_route.js.coffee +28 -0
  39. data/app/assets/javascripts/voluntary_feedback/routes/community/feedbacks_route.js.coffee +42 -0
  40. data/app/assets/javascripts/voluntary_feedback/routes/community/new_category_route.js.coffee +3 -0
  41. data/app/assets/javascripts/voluntary_feedback/routes/community/new_community_route.js.coffee +7 -0
  42. data/app/assets/javascripts/voluntary_feedback/routes/community/new_feedback_route.js.coffee +8 -0
  43. data/app/assets/javascripts/voluntary_feedback/templates/application.handlebars.erb +20 -0
  44. data/app/assets/javascripts/voluntary_feedback/templates/category_form.js.handlebars +13 -0
  45. data/app/assets/javascripts/voluntary_feedback/templates/communities.js.handlebars +32 -0
  46. data/app/assets/javascripts/voluntary_feedback/templates/community.js.handlebars +24 -0
  47. data/app/assets/javascripts/voluntary_feedback/templates/community/categories.js.handlebars +32 -0
  48. data/app/assets/javascripts/voluntary_feedback/templates/community/category.js.handlebars +11 -0
  49. data/app/assets/javascripts/voluntary_feedback/templates/community/edit_category.js.handlebars +3 -0
  50. data/app/assets/javascripts/voluntary_feedback/templates/community/edit_feedback.js.handlebars +3 -0
  51. data/app/assets/javascripts/voluntary_feedback/templates/community/feedback.js.handlebars +68 -0
  52. data/app/assets/javascripts/voluntary_feedback/templates/community/feedbacks.js.handlebars +51 -0
  53. data/app/assets/javascripts/voluntary_feedback/templates/community/new_category.js.handlebars +3 -0
  54. data/app/assets/javascripts/voluntary_feedback/templates/community/new_feedback.js.handlebars +3 -0
  55. data/app/assets/javascripts/voluntary_feedback/templates/community_form.js.handlebars +29 -0
  56. data/app/assets/javascripts/voluntary_feedback/templates/components/reply-cell.js.handlebars +34 -0
  57. data/app/assets/javascripts/voluntary_feedback/templates/components/reply-comments.js.handlebars +27 -0
  58. data/app/assets/javascripts/voluntary_feedback/templates/edit_community.js.handlebars +3 -0
  59. data/app/assets/javascripts/voluntary_feedback/templates/feedback_form.js.handlebars +91 -0
  60. data/app/assets/javascripts/voluntary_feedback/templates/navigation.js.handlebars +28 -0
  61. data/app/assets/javascripts/voluntary_feedback/templates/new_community.js.handlebars +3 -0
  62. data/app/assets/javascripts/voluntary_feedback/templates/shared/_pagination.js.handlebars +23 -0
  63. data/app/assets/stylesheets/voluntary_feedback/application.css +15 -0
  64. data/app/controllers/product/feedback_controller.rb +16 -0
  65. data/app/controllers/voluntary/api/v1/communities_controller.rb +67 -0
  66. data/app/controllers/voluntary/api/v1/community_categories_controller.rb +87 -0
  67. data/app/controllers/voluntary/api/v1/feedbacks_controller.rb +83 -0
  68. data/app/controllers/voluntary/api/v1/replies_controller.rb +91 -0
  69. data/app/models/community.rb +21 -0
  70. data/app/models/community_category.rb +21 -0
  71. data/app/models/community_category_feedback.rb +7 -0
  72. data/app/models/feedback.rb +54 -0
  73. data/app/models/product/feedback.rb +2 -0
  74. data/app/models/reply.rb +16 -0
  75. data/app/serializers/community_category_serializer.rb +3 -0
  76. data/app/serializers/community_serializer.rb +11 -0
  77. data/app/serializers/feedback_serializer.rb +11 -0
  78. data/app/serializers/reply_serializer.rb +19 -0
  79. data/app/views/product/feedback/index.html.erb +0 -0
  80. data/config/locales/resources/community/en.yml +36 -0
  81. data/config/locales/resources/community_category/en.yml +26 -0
  82. data/config/locales/resources/feedback/en.yml +42 -0
  83. data/config/locales/resources/reply/en.yml +29 -0
  84. data/config/routes.rb +11 -1
  85. data/db/migrate/20150825160004_add_feedback_product.rb +79 -0
  86. data/lib/voluntary_feedback.rb +3 -1
  87. data/lib/voluntary_feedback/concerns/model/organization/has_communities.rb +15 -0
  88. data/lib/voluntary_feedback/concerns/model/user/has_communities.rb +15 -0
  89. data/lib/voluntary_feedback/engine.rb +13 -0
  90. data/lib/voluntary_feedback/version.rb +2 -2
  91. metadata +353 -20
  92. data/MIT-LICENSE +0 -20
@@ -0,0 +1,83 @@
1
+ class Voluntary::Api::V1::FeedbacksController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def index
7
+ options = {}
8
+
9
+ community = Community.friendly.find(params[:community_slug])
10
+ collection = community.feedbacks.includes(:user)
11
+ collection = collection.for_category(community.id, params[:category_slug]) if params[:category_slug].present?
12
+ collection = collection.where(feedback_type: params[:feedback_type]) if params[:feedback_type].present?
13
+ options[:json] = collection.paginate page: params[:page], per_page: 10
14
+
15
+ options[:meta] = {
16
+ pagination: {
17
+ total_pages: options[:json].total_pages, current_page: options[:json].current_page,
18
+ previous_page: options[:json].previous_page, next_page: options[:json].next_page
19
+ }
20
+ }
21
+
22
+ respond_with do |format|
23
+ format.json { render options }
24
+ end
25
+ end
26
+
27
+ def show
28
+ resource = Community.friendly.find(params[:community_slug]).feedbacks.friendly.find(params[:id])
29
+
30
+ if current_user
31
+ resource.positive = Feedback.likes_or_dislikes_for(current_user, [resource.id])[resource.id].try(:positive)
32
+ end
33
+
34
+ respond_to do |format|
35
+ format.json do
36
+ render json: resource
37
+ end
38
+ end
39
+ end
40
+
41
+ def create
42
+ resource = Community.friendly.find(params[:feedback][:community_slug]).feedbacks.new params[:feedback]
43
+ resource.user_id = current_user.id
44
+ resource.save
45
+ resource.category_ids = params[:feedback][:category_ids] || [] if resource.persisted?
46
+
47
+ respond_to do |format|
48
+ format.json do
49
+ render json: resource.persisted? ? resource : { errors: resource.errors.to_hash }
50
+ end
51
+ end
52
+ end
53
+
54
+ def update
55
+ resource = Community.friendly.find(params[:feedback][:community_slug]).feedbacks.friendly.find(params[:id])
56
+
57
+ raise CanCan::AccessDenied unless resource.user_id == current_user.id
58
+
59
+ resource.update_attributes params[:feedback]
60
+ resource.category_ids = params[:feedback][:category_ids] || [] if resource.valid?
61
+
62
+ respond_to do |format|
63
+ format.json do
64
+ render json: resource.valid? ? resource : { errors: resource.errors.to_hash }
65
+ end
66
+ end
67
+ end
68
+
69
+ def destroy
70
+ resource = Community.friendly.find(params[:community_slug]).feedbacks.friendly.find(params[:id])
71
+ resource.destroy
72
+
73
+ respond_to do |format|
74
+ format.json do
75
+ render json: if resource.persisted?
76
+ { error: I18n.t('activerecord.errors.models.feedback.attributes.base.deletion_failed') }
77
+ else
78
+ {}
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,91 @@
1
+ class Voluntary::Api::V1::RepliesController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def index
7
+ options = {}
8
+ collection = Feedback.find(params[:feedback_id]).replies.includes(:user).paginate page: params[:page], per_page: 10
9
+
10
+ options[:meta] = {
11
+ pagination: {
12
+ total_pages: collection.total_pages, current_page: collection.current_page,
13
+ previous_page: collection.previous_page, next_page: collection.next_page
14
+ }
15
+ }
16
+
17
+ collection = collection.to_a
18
+
19
+ if current_user
20
+ likes = Reply.likes_or_dislikes_for(current_user, collection.map(&:id))
21
+
22
+ collection.map! do |reply|
23
+ reply.positive = likes[reply.id].try(:positive)
24
+ reply
25
+ end
26
+ end
27
+
28
+ options[:json] = collection
29
+
30
+ respond_with do |format|
31
+ format.json { render options }
32
+ end
33
+ end
34
+
35
+ def show
36
+ respond_to do |format|
37
+ format.json do
38
+ render json: Reply.find(params[:id])
39
+ end
40
+ end
41
+ end
42
+
43
+ def create
44
+ resource = if params[:reply][:feedback_id]
45
+ Feedback.find(params[:reply][:feedback_id]).replies.new params[:reply]
46
+ elsif params[:reply][:reply_id]
47
+ Reply.find(params[:reply][:reply_id]).replies.new params[:reply]
48
+ end
49
+
50
+ resource.user_id = current_user.id
51
+ resource.save
52
+
53
+ respond_to do |format|
54
+ format.json do
55
+ render json: resource.persisted? ? resource : { errors: resource.errors.to_hash }
56
+ end
57
+ end
58
+ end
59
+
60
+ def update
61
+ resource = Reply.find(params[:id])
62
+
63
+ raise CanCan::AccessDenied unless resource.user_id == current_user.id
64
+
65
+ resource.update_attributes params[:reply]
66
+
67
+ respond_to do |format|
68
+ format.json do
69
+ render json: resource.valid? ? resource : { errors: resource.errors.to_hash }
70
+ end
71
+ end
72
+ end
73
+
74
+ def destroy
75
+ resource = Reply.find(params[:id])
76
+
77
+ raise CanCan::AccessDenied unless resource.user_id == current_user.id
78
+
79
+ resource.destroy
80
+
81
+ respond_to do |format|
82
+ format.json do
83
+ render json: if resource.persisted?
84
+ { error: I18n.t('activerecord.errors.models.reply.attributes.base.deletion_failed') }
85
+ else
86
+ {}
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,21 @@
1
+ class Community < ActiveRecord::Base
2
+ extend FriendlyId
3
+
4
+ belongs_to :organization
5
+
6
+ has_many :categories, class_name: 'CommunityCategory'
7
+ has_many :feedbacks
8
+
9
+ validates :organization_id, presence: true
10
+ validates :name, presence: true, uniqueness: true
11
+
12
+ friendly_id :name, use: :slugged
13
+
14
+ attr_accessible :organization_id, :name, :text
15
+
16
+ private
17
+
18
+ def should_generate_new_friendly_id?
19
+ slug.blank? || name_changed?
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ class CommunityCategory < ActiveRecord::Base
2
+ extend FriendlyId
3
+
4
+ belongs_to :community
5
+
6
+ has_many :community_category_feedbacks, source: 'category'
7
+ has_many :feedbacks, through: :community_category_feedbacks
8
+
9
+ validates :community_id, presence: true
10
+ validates :name, presence: true, uniqueness: { scope: :community_id }
11
+
12
+ friendly_id :name, use: :scoped, scope: :community
13
+
14
+ attr_accessible :name
15
+
16
+ private
17
+
18
+ def should_generate_new_friendly_id?
19
+ slug.blank? || name_changed?
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ class CommunityCategoryFeedback < ActiveRecord::Base
2
+ belongs_to :category, class_name: 'CommunityCategory', counter_cache: 'feedbacks_count'
3
+ belongs_to :feedback
4
+
5
+ validates :category_id, presence: true
6
+ validates :feedback_id, presence: true
7
+ end
@@ -0,0 +1,54 @@
1
+ class Feedback < ActiveRecord::Base
2
+ include Likeable
3
+ extend FriendlyId
4
+
5
+ belongs_to :community
6
+ belongs_to :user
7
+
8
+ has_many :community_category_feedbacks
9
+ has_many :categories, class_name: 'CommunityCategory', through: :community_category_feedbacks
10
+ has_many :replies
11
+
12
+ scope :for_category, ->(community_id, category_slug) do
13
+ category_id = Community.find(community_id).categories.friendly.find(category_slug).id
14
+ joins(:community_category_feedbacks).where('community_category_feedbacks.category_id = ?', category_id)
15
+ end
16
+
17
+ validates :community_id, presence: true
18
+ validates :name, presence: true, uniqueness: { scope: :community_id }
19
+ validates :text, presence: true
20
+ validates :user_id, presence: true
21
+ validate :announcement_only_for_organization_owner, if: 'feedback_type == "Announcement"'
22
+
23
+ friendly_id :name, use: :scoped, scope: :community
24
+
25
+ attr_accessible :community_id, :feedback_type, :name, :text, :mood_type, :mood_text
26
+
27
+ attr_accessor :positive
28
+
29
+ def category_ids=(ids)
30
+ categories.select{|c| !ids.map(&:to_i).include?(c.id)}.each do |category|
31
+ categories.delete category
32
+ end
33
+
34
+ category_ids = categories.where('community_categories.id IN(?)', ids).map(&:id)
35
+
36
+ ids.each do |category_id|
37
+ next if category_ids.include? category_id.to_i
38
+
39
+ categories << CommunityCategory.find(category_id)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def announcement_only_for_organization_owner
46
+ unless community.organization.user_id == user_id
47
+ errors[:base] << I18n.t('activerecord.errors.models.feedback.attributes.base.announcement_only_for_organization_owner')
48
+ end
49
+ end
50
+
51
+ def should_generate_new_friendly_id?
52
+ slug.blank? || name_changed?
53
+ end
54
+ end
@@ -0,0 +1,2 @@
1
+ class Product::Feedback < ::Product
2
+ end
@@ -0,0 +1,16 @@
1
+ class Reply < ActiveRecord::Base
2
+ include Likeable
3
+
4
+ belongs_to :feedback
5
+ belongs_to :user
6
+ belongs_to :reply
7
+
8
+ has_many :replies
9
+
10
+ validates :user_id, presence: true
11
+ validates :text, presence: true
12
+
13
+ attr_accessible :text
14
+
15
+ attr_accessor :positive
16
+ end
@@ -0,0 +1,3 @@
1
+ class CommunityCategorySerializer < ActiveModel::Serializer
2
+ attributes :id, :community_id, :name, :slug, :feedbacks_count
3
+ end
@@ -0,0 +1,11 @@
1
+ class CommunitySerializer < ActiveModel::Serializer
2
+ attributes :id, :organization_id, :organization_slug, :organization_name, :name, :slug, :text
3
+
4
+ def organization_slug
5
+ object.organization.try(:slug)
6
+ end
7
+
8
+ def organization_name
9
+ object.organization.try(:name)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class FeedbackSerializer < ActiveModel::Serializer
2
+ attributes :id, :feedback_type, :user_id, :user_slug, :user_name, :name, :slug, :text, :mood_type, :mood_text, :positive, :likes_count, :dislikes_count, :created_at
3
+
4
+ def user_slug
5
+ object.user.try(:slug)
6
+ end
7
+
8
+ def user_name
9
+ object.user.try(:name)
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ class ReplySerializer < ActiveModel::Serializer
2
+ attributes :id, :reply_id, :user_id, :user_slug, :user_name, :text, :positive, :likes_count, :dislikes_count, :created_at, :replies
3
+
4
+ def user_slug
5
+ object.user.try(:slug)
6
+ end
7
+
8
+ def user_name
9
+ object.user.try(:name)
10
+ end
11
+
12
+ def replies
13
+ if object.reply_id
14
+ []
15
+ else
16
+ object.replies.map{|r| ReplySerializer.new(r)}
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ en:
2
+ communities:
3
+ index:
4
+ title: Communities
5
+ empty_collection: No communities found.
6
+ new:
7
+ title: New Community
8
+ edit:
9
+ title: Edit Community
10
+ save:
11
+ failed: Something went wrong at saving community
12
+ successful: Successfully saved community.
13
+
14
+ create:
15
+ title: Create Community
16
+ show:
17
+ all: All
18
+ feedbacks: Feedbacks
19
+ problems: Problems
20
+ questions: Questions
21
+ ideas: Ideas
22
+ praises: Praises
23
+ announcements: Announcements
24
+
25
+ update:
26
+ title: Update Community
27
+ destroy:
28
+ confirmation: Do you really want to remove this community?
29
+
30
+ activerecord:
31
+ errors:
32
+ models:
33
+ community:
34
+ attributes:
35
+ base:
36
+ deletion_failed: Community could not be deleted!
@@ -0,0 +1,26 @@
1
+ en:
2
+ community_categories:
3
+ index:
4
+ title: Categories
5
+ empty_collection: No categories found.
6
+ new:
7
+ title: New Category
8
+ edit:
9
+ title: Edit Category
10
+ save:
11
+ failed: Something went wrong at saving category
12
+ successful: Successfully saved category.
13
+ create:
14
+ title: Create Category
15
+ update:
16
+ title: Update Category
17
+ destroy:
18
+ confirmation: Do you really want to remove this category?
19
+
20
+ activerecord:
21
+ errors:
22
+ models:
23
+ community_category:
24
+ attributes:
25
+ base:
26
+ deletion_failed: Category could not be deleted!
@@ -0,0 +1,42 @@
1
+ en:
2
+ feedbacks:
3
+ index:
4
+ title: Feedbacks
5
+ empty_collection: No feedbacks found.
6
+ new:
7
+ title: New Feedback
8
+ edit:
9
+ title: Edit Feedback
10
+ save:
11
+ failed: Something went wrong at saving feedback
12
+ successful: Successfully saved feedback.
13
+
14
+ create:
15
+ title: Create Feedback
16
+ show:
17
+ feedback: Feedback
18
+ problem: Problem
19
+ question: Question
20
+ idea: Idea
21
+ praise: Praise
22
+ announcement: Announcement
23
+
24
+ update:
25
+ title: Update Feedback
26
+ destroy:
27
+ confirmation: Do you really want to remove this feedback?
28
+
29
+ activerecord:
30
+ attributes:
31
+ feedback:
32
+ name: Title
33
+ feedback_type: Feedback type
34
+ mood_type: Mood type
35
+ mood_text: Mood description
36
+ errors:
37
+ models:
38
+ feedback:
39
+ attributes:
40
+ base:
41
+ announcement_only_for_organization_owner: Announcements are only for community owner.
42
+ deletion_failed: Feedback could not be deleted!