diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index b857caa0..39e9d69d 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -3,34 +3,15 @@ class DocumentsController < ApplicationController before_action :set_document, only: %i[ show edit update destroy ] protect_from_forgery :except => [:api_markdown] - def search_check(param) - if param.present? - key_words = param.split(/[\p{blank}\s]+/) - grouping_hash = key_words.reduce({}) {|hash, word| hash.merge(word => {content_or_creator_screen_name_or_description_or_project_name_or_location_cont: word})} - else - nil - end - end - - def sort_check(param) - if param.present? - sort_column = [] - sort_column << param - else - "start_at DESC" - end - end - # GET /documents or /documents.json def index - if params[:q].nil? - @q = Document.ransack(params[:q]) - @q.sorts = "start_at DESC" - else - @q = Document.ransack({combinator: 'and', groupings: search_check(params[:q][:content_or_creator_screen_name_or_description_or_project_name_or_location_cont])}) - @q.sorts = sort_check(params[:q][:s]) - end - @documents = @q.result.page(params[:page]).per(50).includes(:user) + search_query = { combinator: "and", groupings: split_into_search_queries(params.dig(:q, :text_cont)) } + search_query.merge!({ tags_id_eq: params[:tag_id] }) if params[:tag_id].present? + + @q = Document.ransack(search_query) + @q.sorts = build_sort_query_with_default(params.dig(:q, :s)) + @documents = @q.result.page(params[:page]).per(50).includes(:user, :creator, :project, :tags) + @tags = Tag.all end # GET /documents/1 or /documents/1.json @@ -130,4 +111,26 @@ def parse_tag_names(tag_names) end end + # Build ransack AND search query from params + # Example: { text_cont: "foo bar" } => [{ text_cont: "foo" }, { text_cont: "bar" }] + def split_into_search_queries(param) + return nil if param.nil? || param.blank? + + # Split keyword and build AND search condition + queries = [] + words = param.split(/[\p{blank}\s]+/) + words.each do |word| + queries << { text_cont: word } + end + + queries + end + + def build_sort_query_with_default(param) + if param.present? + param + else + "start_at DESC" + end + end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 72d9359b..97168e5c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,7 +4,7 @@ class ProjectsController < ApplicationController # GET /projects or /projects.json def index - @projects = Project.all.includes(:user) + @projects = Project.all.includes(:user, tasks: [:state, :tags, :assigner, :project]) end # GET /projects/1 or /projects/1.json diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index 84b7176d..a946031c 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -3,43 +3,22 @@ class TasksController < ApplicationController before_action :set_task, only: %i[ show edit update destroy ] before_action :get_form_data, only: %i[ new edit ] - def search_check(param) - if param.present? - key_words = param.split(/[\p{blank}\s]+/) - grouping_hash = key_words.reduce({}) { |hash, word| hash.merge(word => { content_or_assigner_screen_name_or_description_or_project_name_cont: word }) } - else - nil - end - end - - def sort_check(param) - if param.present? - sort_column = [] - sort_column << "state.priority DESC" << param - else - "state.priority DESC" - end - end - # GET /tasks or /tasks.json def index - if params[:q].nil? - @q = Task.joins(:state).ransack(params[:q]) - @q.sorts = ["state.priority DESC", "due_at ASC"] - else - @q = Task.joins(:state).ransack({combinator: 'and', groupings: search_check(params[:q][:content_or_assigner_screen_name_or_description_or_project_name_cont])}) - @q.sorts = sort_check(params[:q][:s]) - end + search_query = { combinator: "and", groupings: split_into_search_queries(params.dig(:q, :text_cont)) } - @show_all = params[:all] == 'true' - tasks_query = @q.result + session[:show_all] = params[:all] == 'true' if params[:all].present? + search_query.merge!({ assigner_id_eq: current_user.id }) unless session[:show_all] - if params[:only_todo] == '1' - tasks_query = tasks_query.merge(Task.active) - end - - tasks_query = tasks_query.joins(:user).where(users: {screen_name: current_user&.screen_name}) unless @show_all - @tasks = tasks_query.page(params[:page]).per(50).includes(:user, :state) + session[:only_todo] = params[:only_todo] if params[:only_todo].present? + search_query.merge!({ task_state_id_eq: TaskState.todo.id }) if session[:only_todo] == "1" + + search_query.merge!({ tags_id_eq: params[:tag_id] }) if params[:tag_id].present? + + @q = Task.ransack(search_query) + @q.sorts = build_sort_query_with_default(params.dig(:q, :s)) + @tasks = @q.result.page(params[:page]).per(50).includes(:user, :project, :tags, :assigner, :state) + @tags = Tag.all end # GET /tasks/1 or /tasks/1.json @@ -136,4 +115,29 @@ def parse_tag_names(tag_names) tag ? tag : Tag.create(name: tag_name) end end + + # Build ransack AND search query from params + # Example: { text_cont: "foo bar" } => [{ text_cont: "foo" }, { text_cont: "bar" }] + def split_into_search_queries(param) + return nil if param.nil? || param.blank? + + # Split keyword and build AND search condition + queries = [] + words = param.split(/[\p{blank}\s]+/) + words.each do |word| + queries << { text_cont: word } + end + + queries + end + + def build_sort_query_with_default(param) + query = ["state_priority DESC"] + if param.present? + query << param + else + query << "due_at ASC" + end + query + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 163290b9..8b99c224 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -2,4 +2,12 @@ module ApplicationHelper def tag_label(tag) raw %( #{tag.name}) end + + def url_with_query(url, key, value) + uri = URI(url) + query = Rack::Utils.parse_query(uri.query) + query[key] = value + uri.query = query.to_query + uri.to_s + end end diff --git a/app/helpers/documents_helper.rb b/app/helpers/documents_helper.rb index 242b4fce..8900fb76 100644 --- a/app/helpers/documents_helper.rb +++ b/app/helpers/documents_helper.rb @@ -1,2 +1,15 @@ module DocumentsHelper + def preserve_other_params(except: []) + forms = [] + params.except(:controller, :action, :commit, *except).each do |key, value| + if key == 'q' + value.each do |q_key, q_value| + forms << hidden_field_tag("q[#{q_key}]", q_value) + end + else + forms << hidden_field_tag(key, value) + end + end + forms.join.html_safe + end end diff --git a/app/helpers/tasks_helper.rb b/app/helpers/tasks_helper.rb index 1e5645f6..b153400b 100644 --- a/app/helpers/tasks_helper.rb +++ b/app/helpers/tasks_helper.rb @@ -7,4 +7,19 @@ def days_to_deadline_as_string(task) "" end end + + def preserve_other_params(except: []) + forms = [] + params.except(:controller, :action, :commit, *except).each do |key, value| + if key == 'q' + value.each do |q_key, q_value| + forms << hidden_field_tag("q[#{q_key}]", q_value) + end + else + forms << hidden_field_tag(key, value) + end + end + p forms + forms.join.html_safe + end end diff --git a/app/models/document.rb b/app/models/document.rb index 6ecc5f1c..9d461bfd 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -14,8 +14,10 @@ class Document < ApplicationRecord #validates :project, presence: true before_validation :add_unique_action_item_marker + ransack_alias :text, :content_or_creator_screen_name_or_description_or_project_name_or_location_cont + def self.ransackable_attributes(auth_object = nil) - ["content", "created_at", "creator_id", "description", "end_at", "id", "location", "project_id", "start_at", "updated_at"] + ["content", "created_at", "creator_id", "description", "end_at", "id", "location", "project_id", "start_at", "updated_at", "text"] end def self.ransackable_associations(auth_object = nil) diff --git a/app/models/tag.rb b/app/models/tag.rb index 54280602..2a690828 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -5,6 +5,10 @@ class Tag < ApplicationRecord has_many :documents, through: :document_tags, dependent: :restrict_with_exception validates :name, presence: true, uniqueness: true + def self.ransackable_attributes(auth_object = nil) + [ "id", "name" ] + end + def self.order_by_popularity self.find_by_sql <<~'SQL' SELECT diff --git a/app/models/task.rb b/app/models/task.rb index 0002e1d4..24478ea9 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -15,9 +15,11 @@ class Task < ApplicationRecord validates :content, presence: true + ransack_alias :text, :content_or_assigner_screen_name_or_description_or_project_name + def self.ransackable_attributes(auth_object = nil) - [ "assigner_id", "content", "created_at", "creator_id", "description", "due_at", "id", "project_id", "tag_id", "task_state_id", "updated_at" ] - end + [ "assigner_id", "content", "created_at", "creator_id", "description", "due_at", "id", "project_id", "tag_id", "task_state_id", "updated_at", "text" ] + end def self.ransackable_associations(auth_object = nil) [ "assigner", "project", "state", "tags", "task_tags", "user" ] diff --git a/app/models/task_state.rb b/app/models/task_state.rb index 7236730d..a807bb08 100644 --- a/app/models/task_state.rb +++ b/app/models/task_state.rb @@ -7,4 +7,16 @@ class TaskState < ApplicationRecord def self.ransackable_attributes(auth_object = nil) [ "priority" ] end + + def self.todo + @todo ||= find_by(name: 'todo') + end + + def self.done + @done ||= find_by(name: 'done') + end + + def self.draft + @draft ||= find_by(name: 'draft') + end end diff --git a/app/views/documents/index.html.erb b/app/views/documents/index.html.erb index c15f2f02..86ebde80 100644 --- a/app/views/documents/index.html.erb +++ b/app/views/documents/index.html.erb @@ -1,3 +1,7 @@ +<% content_for :tag_list do %> + <%= render partial: "layouts/tag_list", locals: { base_link: request.original_url } %> +<% end %> +
<%= render 'layouts/h1', title: "文書一覧" %>
@@ -18,10 +22,11 @@
<%= search_form_for @q, class: "d-flex align-items-center w-100" do |f| %> - <%= f.text_field :content_or_creator_screen_name_or_description_or_project_name_or_location_cont, - class: "form-control me-2", - placeholder: '検索ワードを入力して下さい', + <%= f.text_field :text_cont, + class: "form-control me-2", + placeholder: '検索ワードを入力して下さい', style: "flex: 1;" %> + <%= preserve_other_params(except: [:q]) %> <%= f.submit "検索", class: "btn btn-primary" %> <% end %>
diff --git a/app/views/layouts/_tag_list.html.erb b/app/views/layouts/_tag_list.html.erb index 27460f70..97a7c254 100644 --- a/app/views/layouts/_tag_list.html.erb +++ b/app/views/layouts/_tag_list.html.erb @@ -1,8 +1,11 @@ -
-

タグ一覧

-
- <% Tag.order_by_popularity.each do |tag| %> - <%= link_to tag_label(tag), tag, class: "btn btn-primary btn-outline-primary" %> - <% end %> +
+ diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index aa1d1e61..ff012b05 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -37,9 +37,7 @@
- + <%= yield :tag_list %>
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index 3b73ac5c..bdd2b21b 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -9,14 +9,14 @@

<%= project.name %>

-

達成率 : <%= project.tasks.count { |task| task.completed? } %> / <%= project.tasks.count %>

+

達成率 : <%= project.tasks.size { |task| task.completed? } %> / <%= project.tasks.size %>

<% if logged_in? %>
<%= link_to '詳細', project, class: "btn btn-sm" %> <%= link_to '編集', edit_project_path(project), class: "btn btn-sm" %> - <% if project.tasks.count != 0 %> + <% if project.tasks.size != 0 %> 削除 <% else %> <%= button_to '削除', project, method: :delete, data: { turbo_confirm: 'このプロジェクトを削除しますか?' }, class: "btn btn-sm text-black" %> @@ -35,7 +35,7 @@
- <% if index < project.tasks.count - 1 %> + <% if index < project.tasks.size - 1 %>
<% end %>
diff --git a/app/views/tags/_tag.html.erb b/app/views/tags/_tag.html.erb index d21ff4ca..68f9a0d7 100644 --- a/app/views/tags/_tag.html.erb +++ b/app/views/tags/_tag.html.erb @@ -1,15 +1,15 @@

<%= link_to tag.name, tag, class: "btn btn-primary" %>

-

タスク数 : <%= tag.tasks.count %>

-

文書数 : <%= tag.documents.count %>

+

タスク数 : <%= tag.tasks.size %>

+

文書数 : <%= tag.documents.size %>

<% if logged_in? %>
<%= link_to '詳細', tag, class: "btn btn-sm text-black" %> <%= link_to '編集', edit_tag_path(tag), class: "btn btn-sm mx-2 text-black" %> - <% if tag.tasks.count == 0 && tag.documents.count == 0 %> + <% if tag.tasks.size == 0 && tag.documents.size == 0 %> <%= button_to '削除', tag, method: :delete, data: { turbo_confirm: 'このタグを削除しますか?' }, class: "btn btn-sm text-black" %> <% else %>

削除

diff --git a/app/views/tasks/_task.html.erb b/app/views/tasks/_task.html.erb index b4ac98e7..fcc989de 100644 --- a/app/views/tasks/_task.html.erb +++ b/app/views/tasks/_task.html.erb @@ -4,11 +4,11 @@ <%= turbo_frame_tag "task_#{task.id}" do %> <% if task.completed? %> - <%= button_to task, method: :patch, params: { task: { task_state_id: TaskState.find_by(name: 'todo').id } }, class: "btn btn-link p-0 border-0", style: "background: none;" do %> + <%= button_to task, method: :patch, params: { task: { task_state_id: TaskState.todo.id } }, class: "btn btn-link p-0 border-0", style: "background: none;" do %> <% end %> <% else %> - <%= button_to task, method: :patch, params: { task: { task_state_id: TaskState.find_by(name: 'done').id } }, class: "btn btn-link p-0 border-0", style: "background: none;" do %> + <%= button_to task, method: :patch, params: { task: { task_state_id: TaskState.todo.id } }, class: "btn btn-link p-0 border-0", style: "background: none;" do %> <% end %> <% end %> diff --git a/app/views/tasks/index.html.erb b/app/views/tasks/index.html.erb index 7ecd5db3..900a9934 100644 --- a/app/views/tasks/index.html.erb +++ b/app/views/tasks/index.html.erb @@ -1,3 +1,7 @@ +<% content_for :tag_list do %> + <%= render partial: "layouts/tag_list", locals: { base_link: request.original_url } %> +<% end %> +
<%= render 'layouts/h1', title: "タスク一覧" %>
@@ -18,11 +22,11 @@
<%= search_form_for @q, class: "d-flex align-items-center w-100" do |f| %> - <%= f.text_field :content_or_assigner_screen_name_or_description_or_project_name_cont, - class: "form-control me-2", - placeholder: '検索ワードを入力して下さい', + <%= f.text_field :text_cont, + class: "form-control me-2", + placeholder: '検索ワードを入力して下さい', style: "flex: 1;" %> - <%= hidden_field_tag :only_todo, params[:only_todo] if params[:only_todo].present? %> + <%= preserve_other_params(except: [:q]) %> <%= f.submit "検索", class: "btn btn-primary" %> <% end %>
@@ -31,25 +35,25 @@
<%= form_with url: request.path, method: :get, local: true, html: { class: "d-flex" } do %> - <%= check_box_tag :only_todo, "1", params[:only_todo] == "1", onchange: "this.form.submit()" %> + <%= preserve_other_params(except: [:only_todo]) %> + <%= hidden_field_tag :only_todo, session[:only_todo] == '1' ? '0' : '1' %> + <%= check_box_tag "", "", checked: session[:only_todo] == "1", onchange: "this.form.submit()" %>
todoのみ
- - <% if params[:q].present? %> - <% params[:q].each do |key, value| %> - <%= hidden_field_tag "q[#{key}]", value %> - <% end %> - <% end %> <% end %> -<% current_q_params = request.query_parameters.except(:controller, :action, :page) %> @@ -58,7 +62,7 @@ <%= render @tasks %>