From 9044f43622c4ba16ddb8699a098984d559eca2ee Mon Sep 17 00:00:00 2001 From: miyake13000 Date: Wed, 26 Nov 2025 00:43:38 +0900 Subject: [PATCH 1/5] Add a function for showing tasks associating with the slected tag --- app/controllers/documents_controller.rb | 55 ++++++++++---------- app/controllers/tasks_controller.rb | 67 +++++++++++++------------ app/models/document.rb | 4 +- app/models/tag.rb | 4 ++ app/models/task.rb | 6 ++- app/views/documents/index.html.erb | 10 ++-- app/views/layouts/_tag_list.html.erb | 17 ++++--- app/views/layouts/application.html.erb | 4 +- app/views/tasks/index.html.erb | 10 ++-- 9 files changed, 99 insertions(+), 78 deletions(-) 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/tasks_controller.rb b/app/controllers/tasks_controller.rb index 84b7176d..b31e536e 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -3,43 +3,19 @@ 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 - @show_all = params[:all] == 'true' - tasks_query = @q.result - 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) + search_query = { combinator: "and", groupings: split_into_search_queries(params.dig(:q, :text_cont)) } + search_query.merge!({ assigner_id_rq: current_user.id }) unless @show_all + search_query.merge!({ task_state_id_eq: TaskState.where(name: "todo").first.id }) if params[: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 +112,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/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/views/documents/index.html.erb b/app/views/documents/index.html.erb index c15f2f02..5a014b00 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: documents_path } %> +<% end %> +
<%= render 'layouts/h1', title: "文書一覧" %>
@@ -18,9 +22,9 @@
<%= 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;" %> <%= 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..2927849d 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/tasks/index.html.erb b/app/views/tasks/index.html.erb index 7ecd5db3..16b6d61f 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: tasks_path } %> +<% end %> +
<%= render 'layouts/h1', title: "タスク一覧" %>
@@ -18,9 +22,9 @@
<%= 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? %> <%= f.submit "検索", class: "btn btn-primary" %> From ff006c58778cf2e97881989da29d6e964650c74b Mon Sep 17 00:00:00 2001 From: miyake13000 Date: Wed, 26 Nov 2025 12:32:33 +0900 Subject: [PATCH 2/5] Update the search system to keep queries of other form when submitting one form --- app/controllers/tasks_controller.rb | 11 +++++++---- app/helpers/application_helper.rb | 8 ++++++++ app/helpers/documents_helper.rb | 13 +++++++++++++ app/helpers/tasks_helper.rb | 15 +++++++++++++++ app/views/documents/index.html.erb | 3 ++- app/views/layouts/_tag_list.html.erb | 2 +- app/views/tasks/index.html.erb | 26 +++++++++++++------------- 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index b31e536e..a3eb0af7 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -5,11 +5,14 @@ class TasksController < ApplicationController # GET /tasks or /tasks.json def index - @show_all = params[:all] == 'true' - search_query = { combinator: "and", groupings: split_into_search_queries(params.dig(:q, :text_cont)) } - search_query.merge!({ assigner_id_rq: current_user.id }) unless @show_all - search_query.merge!({ task_state_id_eq: TaskState.where(name: "todo").first.id }) if params[:only_todo] == "1" + + session[:show_all] = params[:all] == 'true' if params[:all].present? + search_query.merge!({ assigner_id_eq: current_user.id }) unless session[:show_all] + + session[:only_todo] = params[:only_todo] if params[:only_todo].present? + search_query.merge!({ task_state_id_eq: TaskState.where(name: "todo").first.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) 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/views/documents/index.html.erb b/app/views/documents/index.html.erb index 5a014b00..86ebde80 100644 --- a/app/views/documents/index.html.erb +++ b/app/views/documents/index.html.erb @@ -1,5 +1,5 @@ <% content_for :tag_list do %> - <%= render partial: "layouts/tag_list", locals: { base_link: documents_path } %> + <%= render partial: "layouts/tag_list", locals: { base_link: request.original_url } %> <% end %>
@@ -26,6 +26,7 @@ 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 2927849d..97a7c254 100644 --- a/app/views/layouts/_tag_list.html.erb +++ b/app/views/layouts/_tag_list.html.erb @@ -3,7 +3,7 @@

タグ一覧

<% Tag.order_by_popularity.each do |tag| %> - <% link = base_link.present? ? base_link + "?tag_id=#{tag.id}" : tag_path(tag) %> + <% link = base_link.present? ? url_with_query(base_link, "tag_id", tag.id) : tag_path(tag) %> <%= link_to tag_label(tag), link, class: "btn btn-primary btn-outline-primary" %> <% end %>
diff --git a/app/views/tasks/index.html.erb b/app/views/tasks/index.html.erb index 16b6d61f..900a9934 100644 --- a/app/views/tasks/index.html.erb +++ b/app/views/tasks/index.html.erb @@ -1,5 +1,5 @@ <% content_for :tag_list do %> - <%= render partial: "layouts/tag_list", locals: { base_link: tasks_path } %> + <%= render partial: "layouts/tag_list", locals: { base_link: request.original_url } %> <% end %>
@@ -26,7 +26,7 @@ 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 %>
@@ -35,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) %> @@ -62,7 +62,7 @@ <%= render @tasks %>
From 1f6317b5e2097a50ab398d4d207c9f7241d43c6d Mon Sep 17 00:00:00 2001 From: miyake13000 Date: Wed, 26 Nov 2025 13:15:19 +0900 Subject: [PATCH 3/5] Update TaskState to cache all record --- app/controllers/tasks_controller.rb | 2 +- app/models/task_state.rb | 12 ++++++++++++ app/views/tasks/_task.html.erb | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index a3eb0af7..a946031c 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -11,7 +11,7 @@ def index search_query.merge!({ assigner_id_eq: current_user.id }) unless session[:show_all] session[:only_todo] = params[:only_todo] if params[:only_todo].present? - search_query.merge!({ task_state_id_eq: TaskState.where(name: "todo").first.id }) if session[:only_todo] == "1" + 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? 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/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 %> From 212ad41216f6fb1f29e41d2c6ddfaf13a6cc0333 Mon Sep 17 00:00:00 2001 From: miyake13000 Date: Wed, 26 Nov 2025 13:18:54 +0900 Subject: [PATCH 4/5] Update view to use size mehod instead of count method --- app/views/projects/index.html.erb | 6 +++--- app/views/tags/_tag.html.erb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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 %>

削除

From 36ec3e8f2a2c9a88fdb387c3c3a599c10d6359b8 Mon Sep 17 00:00:00 2001 From: miyake13000 Date: Wed, 26 Nov 2025 13:19:48 +0900 Subject: [PATCH 5/5] Update project controller to include all necessarry models --- app/controllers/projects_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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