From 049967649e82ba6d5699c1e1a3f00d246490dc60 Mon Sep 17 00:00:00 2001 From: iagirre Date: Tue, 19 Sep 2017 17:37:31 +0200 Subject: [PATCH 1/4] Selection of multiple tags separated by ',' works for hardcoded array of tags. --- app/assets/javascripts/application.js | 3 ++ .../javascripts/tag_autocomplete.js.coffee | 29 +++++++++++++ app/assets/stylesheets/application.scss | 2 + .../stylesheets/autocomplete_overrides.scss | 41 +++++++++++++++++++ app/views/debates/_form.html.erb | 3 +- config/routes.rb | 4 ++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/tag_autocomplete.js.coffee create mode 100644 app/assets/stylesheets/autocomplete_overrides.scss diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 2ff87100a..01c5dbaff 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -14,6 +14,7 @@ //= require jquery_ujs //= require jquery-ui/widgets/datepicker //= require jquery-ui/i18n/datepicker-es +//= require jquery-ui/widgets/autocomplete //= require jquery-fileupload/basic //= require foundation //= require turbolinks @@ -63,6 +64,7 @@ //= require documentable //= require tree_navigator //= require custom +//= require tag_autocomplete var initialize_modules = function() { App.Comments.initialize(); @@ -97,6 +99,7 @@ var initialize_modules = function() { App.WatchFormChanges.initialize(); App.TreeNavigator.initialize(); App.Documentable.initialize(); + App.TagAutocomplete.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/tag_autocomplete.js.coffee b/app/assets/javascripts/tag_autocomplete.js.coffee new file mode 100644 index 000000000..9e10c100e --- /dev/null +++ b/app/assets/javascripts/tag_autocomplete.js.coffee @@ -0,0 +1,29 @@ +App.TagAutocomplete = + + split: ( val ) -> + return (val.split( /,\s*/ )) + + extractLast: ( term ) -> + return (App.TagAutocomplete.split( term ).pop()) + + init_autocomplete: -> + $('.js-tag-list').autocomplete + source: (request, response) -> + response( $.ui.autocomplete.filter(["Arbol", "Becerro", "Caracol"], App.TagAutocomplete.extractLast( request.term ) ) ); + minLength: 0, + search: -> + console.log(this.value); + console.log(App.TagAutocomplete.extractLast( this.value )); + App.TagAutocomplete.extractLast( this.value ); + focus: -> + return false; + select: ( event, ui ) -> ( + terms = App.TagAutocomplete.split( this.value ); + terms.pop(); + terms.push( ui.item.value ); + terms.push( "" ); + this.value = terms.join( ", " ); + return false;); + + initialize: -> + App.TagAutocomplete.init_autocomplete(); \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 19c73de32..e4dd4ea1c 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -16,4 +16,6 @@ @import 'annotator_overrides'; @import 'jquery-ui/datepicker'; @import 'datepicker_overrides'; +@import 'jquery-ui/autocomplete'; +@import 'autocomplete_overrides'; @import 'documentable'; diff --git a/app/assets/stylesheets/autocomplete_overrides.scss b/app/assets/stylesheets/autocomplete_overrides.scss new file mode 100644 index 000000000..68537fd8a --- /dev/null +++ b/app/assets/stylesheets/autocomplete_overrides.scss @@ -0,0 +1,41 @@ +// Overrides styles of jquery-ui/autocomplete +// + +/* Autocomplete +----------------------------------*/ +.ui-autocomplete { position: absolute; cursor: default; } +.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* Menu +----------------------------------*/ +.ui-menu { + list-style:none; + padding: 0px 5px; + margin: 0; + display:block; + /*width:227px;*/ + background: white; + border: 1px solid $border; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + width: 200px; +} +.ui-menu .ui-menu-item a { + /* text-decoration:none; + display:block;*/ + padding:.2em .4em; + /* line-height:1.5; + zoom:1;*/ +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + margin: -1px; +} \ No newline at end of file diff --git a/app/views/debates/_form.html.erb b/app/views/debates/_form.html.erb index 96a5337ca..28ef69804 100644 --- a/app/views/debates/_form.html.erb +++ b/app/views/debates/_form.html.erb @@ -22,7 +22,8 @@ <%= f.text_field :tag_list, value: @debate.tag_list.to_s, label: false, placeholder: t("debates.form.tags_placeholder"), - aria: {describedby: "tag-list-help-text"} %> + aria: {describedby: "tag-list-help-text"}, + data: {js_url: tags_search_path}%>
<% if @debate.new_record? %> diff --git a/config/routes.rb b/config/routes.rb index a02384ced..666285496 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -167,6 +167,10 @@ Rails.application.routes.draw do resource :email, controller: "email", only: [:new, :show, :create] resource :letter, controller: "letter", only: [:new, :create, :show, :edit, :update] end + + namespace :tags do + get :search + end namespace :admin do From 4db2584f87d4b535aa9b6c39de15e14413fd926f Mon Sep 17 00:00:00 2001 From: iagirre Date: Thu, 21 Sep 2017 18:21:45 +0200 Subject: [PATCH 2/4] The tags are suggested based on the entries the user makes. Cambios para hacer commit: modificado: app/assets/javascripts/tag_autocomplete.js.coffee modificado: app/assets/stylesheets/autocomplete_overrides.scss nuevo archivo: app/controllers/tags_controller.rb modificado: app/models/abilities/common.rb modificado: app/views/debates/_form.html.erb modificado: app/views/proposals/_form.html.erb modificado: config/initializers/acts_as_taggable_on.rb modificado: config/routes.rb modificado: spec/lib/acts_as_taggable_on_spec.rb --- .../javascripts/tag_autocomplete.js.coffee | 13 +++++-- .../stylesheets/autocomplete_overrides.scss | 39 ++++++++----------- app/controllers/tags_controller.rb | 11 ++++++ app/models/abilities/common.rb | 1 + app/views/debates/_form.html.erb | 3 +- app/views/proposals/_form.html.erb | 5 ++- config/initializers/acts_as_taggable_on.rb | 13 +++++++ config/routes.rb | 33 ++++++++-------- spec/lib/acts_as_taggable_on_spec.rb | 15 +++++++ 9 files changed, 86 insertions(+), 47 deletions(-) create mode 100644 app/controllers/tags_controller.rb diff --git a/app/assets/javascripts/tag_autocomplete.js.coffee b/app/assets/javascripts/tag_autocomplete.js.coffee index 9e10c100e..be27cd81c 100644 --- a/app/assets/javascripts/tag_autocomplete.js.coffee +++ b/app/assets/javascripts/tag_autocomplete.js.coffee @@ -7,13 +7,18 @@ App.TagAutocomplete = return (App.TagAutocomplete.split( term ).pop()) init_autocomplete: -> - $('.js-tag-list').autocomplete + $('.tag-autocomplete').autocomplete source: (request, response) -> - response( $.ui.autocomplete.filter(["Arbol", "Becerro", "Caracol"], App.TagAutocomplete.extractLast( request.term ) ) ); + $.ajax + url: $('.tag-autocomplete').data('js-url'), + data: {search: App.TagAutocomplete.extractLast( request.term )}, + type: 'GET', + dataType: 'json' + success: ( data ) -> + response( data ); + minLength: 0, search: -> - console.log(this.value); - console.log(App.TagAutocomplete.extractLast( this.value )); App.TagAutocomplete.extractLast( this.value ); focus: -> return false; diff --git a/app/assets/stylesheets/autocomplete_overrides.scss b/app/assets/stylesheets/autocomplete_overrides.scss index 68537fd8a..9eac7162b 100644 --- a/app/assets/stylesheets/autocomplete_overrides.scss +++ b/app/assets/stylesheets/autocomplete_overrides.scss @@ -4,7 +4,6 @@ /* Autocomplete ----------------------------------*/ .ui-autocomplete { position: absolute; cursor: default; } -.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; } /* workarounds */ * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ @@ -13,29 +12,23 @@ ----------------------------------*/ .ui-menu { list-style:none; - padding: 0px 5px; - margin: 0; + padding: $line-height / 4 $line-height / 3; display:block; - /*width:227px;*/ background: white; border: 1px solid $border; -} -.ui-menu .ui-menu { - margin-top: -3px; -} -.ui-menu .ui-menu-item { - margin:0; - padding: 0; - width: 200px; -} -.ui-menu .ui-menu-item a { - /* text-decoration:none; - display:block;*/ - padding:.2em .4em; - /* line-height:1.5; - zoom:1;*/ -} -.ui-menu .ui-menu-item a.ui-state-hover, -.ui-menu .ui-menu-item a.ui-state-active { - margin: -1px; + font-size: $small-font-size; + + .ui-menu-item { + + .ui-menu-item-wrapper { + padding: $line-height / 4 $line-height / 3; + position: relative; + } + + .ui-state-hover, .ui-state-active { + background: #ececec; + border-radius: rem-calc(6); + } + } + } \ No newline at end of file diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb new file mode 100644 index 000000000..c58067048 --- /dev/null +++ b/app/controllers/tags_controller.rb @@ -0,0 +1,11 @@ +class TagsController < ApplicationController + + load_and_authorize_resource class: ActsAsTaggableOn::Tag + respond_to :json + + def suggest + @tags = ActsAsTaggableOn::Tag.search(params[:search]).map(&:name) + respond_with @tags + end + +end diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb index 620cfb212..0df92616d 100644 --- a/app/models/abilities/common.rb +++ b/app/models/abilities/common.rb @@ -24,6 +24,7 @@ module Abilities can :suggest, Debate can :suggest, Proposal + can :suggest, ActsAsTaggableOn::Tag can [:flag, :unflag], Comment cannot [:flag, :unflag], Comment, user_id: user.id diff --git a/app/views/debates/_form.html.erb b/app/views/debates/_form.html.erb index 28ef69804..a98611011 100644 --- a/app/views/debates/_form.html.erb +++ b/app/views/debates/_form.html.erb @@ -23,7 +23,8 @@ label: false, placeholder: t("debates.form.tags_placeholder"), aria: {describedby: "tag-list-help-text"}, - data: {js_url: tags_search_path}%> + data: {js_url: suggest_tags_path}, + class: 'tag-autocomplete'%>
<% if @debate.new_record? %> diff --git a/app/views/proposals/_form.html.erb b/app/views/proposals/_form.html.erb index 73b39dfdb..b46dbc69e 100644 --- a/app/views/proposals/_form.html.erb +++ b/app/views/proposals/_form.html.erb @@ -70,8 +70,9 @@ <%= f.text_field :tag_list, value: @proposal.tag_list.to_s, label: false, placeholder: t("proposals.form.tags_placeholder"), - class: 'js-tag-list', - aria: {describedby: "tag-list-help-text"} %> + class: 'js-tag-list tag-autocomplete', + aria: {describedby: "tag-list-help-text"}, + data: {js_url: suggest_tags_path} %>
<% if current_user.unverified? %> diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb index 57766c8c9..018319442 100644 --- a/config/initializers/acts_as_taggable_on.rb +++ b/config/initializers/acts_as_taggable_on.rb @@ -43,6 +43,18 @@ module ActsAsTaggableOn Tagging.public_for_api.pluck('DISTINCT taggings.tag_id')) end + include PgSearch + + pg_search_scope :pg_search, against: :name, + using: { + tsearch: {prefix: true} + }, + ignoring: :accents + + def self.search(term) + pg_search(term) + end + def increment_custom_counter_for(taggable_type) Tag.increment_counter(custom_counter_field_name_for(taggable_type), id) end @@ -78,6 +90,7 @@ module ActsAsTaggableOn end private + def custom_counter_field_name_for(taggable_type) "#{taggable_type.underscore.pluralize}_count" end diff --git a/config/routes.rb b/config/routes.rb index 666285496..7b1c1cac0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,17 +6,17 @@ Rails.application.routes.draw do end devise_for :users, controllers: { - registrations: 'users/registrations', - sessions: 'users/sessions', - confirmations: 'users/confirmations', - omniauth_callbacks: 'users/omniauth_callbacks' - } + registrations: 'users/registrations', + sessions: 'users/sessions', + confirmations: 'users/confirmations', + omniauth_callbacks: 'users/omniauth_callbacks' + } devise_for :organizations, class_name: 'User', - controllers: { - registrations: 'organizations/registrations', - sessions: 'devise/sessions', - }, - skip: [:omniauth_callbacks] + controllers: { + registrations: 'organizations/registrations', + sessions: 'devise/sessions' + }, + skip: [:omniauth_callbacks] devise_scope :organization do get 'organizations/sign_up/success', to: 'organizations/registrations#success' @@ -167,11 +167,12 @@ Rails.application.routes.draw do resource :email, controller: "email", only: [:new, :show, :create] resource :letter, controller: "letter", only: [:new, :create, :show, :edit, :update] end - - namespace :tags do - get :search - end + resources :tags do + collection do + get :suggest + end + end namespace :admin do root to: "dashboard#index" @@ -435,9 +436,7 @@ Rails.application.routes.draw do get '/graphql', to: 'graphql#query' post '/graphql', to: 'graphql#query' - if Rails.env.development? - mount LetterOpenerWeb::Engine, at: "/letter_opener" - end + mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql' diff --git a/spec/lib/acts_as_taggable_on_spec.rb b/spec/lib/acts_as_taggable_on_spec.rb index 761e90553..de53b35e7 100644 --- a/spec/lib/acts_as_taggable_on_spec.rb +++ b/spec/lib/acts_as_taggable_on_spec.rb @@ -133,6 +133,21 @@ describe 'ActsAsTaggableOn' do expect(ActsAsTaggableOn::Tag.public_for_api).to be_empty end end + + describe "search" do + it "containing the word in the name" do + create(:tag, name: 'Familia') + create(:tag, name: 'Cultura') + create(:tag, name: 'Salud') + create(:tag, name: 'Famosos') + + expect(ActsAsTaggableOn::Tag.pg_search('f').length).to eq(2) + expect(ActsAsTaggableOn::Tag.search('cultura').first.name).to eq('Cultura') + expect(ActsAsTaggableOn::Tag.search('sal').first.name).to eq('Salud') + expect(ActsAsTaggableOn::Tag.search('fami').first.name).to eq('Familia') + end + end + end end From c01208b203a319feac420fc0a78ee26239ffab2d Mon Sep 17 00:00:00 2001 From: iagirre Date: Thu, 21 Sep 2017 18:27:52 +0200 Subject: [PATCH 3/4] Autocomplete tags for investments projects --- app/views/budgets/investments/_form.html.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/budgets/investments/_form.html.erb b/app/views/budgets/investments/_form.html.erb index 7cf2bb1e2..8f98b9d5c 100644 --- a/app/views/budgets/investments/_form.html.erb +++ b/app/views/budgets/investments/_form.html.erb @@ -49,7 +49,8 @@ label: false, placeholder: t("budgets.investments.form.tags_placeholder"), aria: {describedby: "tags-list-help-text"}, - class: 'js-tag-list' %> + class: 'js-tag-list tag-autocomplete', + data: {js_url: suggest_tags_path} %> From 0948959a65318284014ad5cff4723285d04e01b1 Mon Sep 17 00:00:00 2001 From: iagirre Date: Fri, 22 Sep 2017 08:09:03 +0200 Subject: [PATCH 4/4] Run scss-lint to fix some errors in autocomplete_overrides.scss --- .../stylesheets/autocomplete_overrides.scss | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/autocomplete_overrides.scss b/app/assets/stylesheets/autocomplete_overrides.scss index 9eac7162b..dd2b939ad 100644 --- a/app/assets/stylesheets/autocomplete_overrides.scss +++ b/app/assets/stylesheets/autocomplete_overrides.scss @@ -3,18 +3,23 @@ /* Autocomplete ----------------------------------*/ -.ui-autocomplete { position: absolute; cursor: default; } +.ui-autocomplete { + position: absolute; + cursor: default; +} /* workarounds */ -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ +* html .ui-autocomplete { + width: 1px; +} /* without this, the menu expands to 100% in IE6 */ /* Menu ----------------------------------*/ .ui-menu { - list-style:none; + list-style: none; padding: $line-height / 4 $line-height / 3; - display:block; - background: white; + display: block; + background: #fff; border: 1px solid $border; font-size: $small-font-size; @@ -25,10 +30,11 @@ position: relative; } - .ui-state-hover, .ui-state-active { + .ui-state-hover, + .ui-state-active { background: #ececec; border-radius: rem-calc(6); } } -} \ No newline at end of file +}