diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index c7b7f72..f296340 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -76,14 +76,9 @@ form { text-transform: uppercase; } -details.dropdown { - td & { +table { + /* TODO: maybe extract into a separate file */ + details.dropdown { margin: 0; } - - &.small { - --pico-form-element-spacing-vertical: 0.25em; - --pico-form-element-spacing-horizontal: 0.5em; - } - } diff --git a/app/assets/stylesheets/tasks.css b/app/assets/stylesheets/tasks.css index 1c90197..3c5a50f 100644 --- a/app/assets/stylesheets/tasks.css +++ b/app/assets/stylesheets/tasks.css @@ -1,3 +1,10 @@ +.tasks-table { + .task-status-selector { + --pico-form-element-spacing-vertical: 0.25em; + --pico-form-element-spacing-horizontal: 0.5em; + } +} + .task-status { --color: var(--backlog-color); --background-color: var(--backlog-bg); diff --git a/app/helpers/tasks_helper.rb b/app/helpers/tasks_helper.rb index d825650..204ed67 100644 --- a/app/helpers/tasks_helper.rb +++ b/app/helpers/tasks_helper.rb @@ -9,18 +9,7 @@ module TasksHelper 'Tasks' end - # @param status [TaskStatus] - def task_status_badge(status) - # TODO: extract into a component probably - - content_tag(:span, status.name, class: ['badge', 'task-status', status.category.dasherize]) - end - - def task_status_selector(task, selector_class: '', id: nil, with_form: false) - # TODO: extract into a component probably - - raise 'You should pass id if you want the form' if with_form && id.blank? - - render partial: 'tasks/status_selector', locals: { task:, selector_class:, id:, with_form: } + def task_status_selector(task, with_form: false) + render Tasks::Statuses::SelectorViewModel.new(task, with_form:) end end diff --git a/app/view_models/tasks/statuses/selector_view_model.rb b/app/view_models/tasks/statuses/selector_view_model.rb new file mode 100644 index 0000000..dc5b401 --- /dev/null +++ b/app/view_models/tasks/statuses/selector_view_model.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Tasks + module Statuses + class SelectorViewModel + def initialize(task, with_form: false) + @task = task + @with_form = with_form + end + + def dom_id + id = @task.persisted? ? @task.id : '_' + "task_status_selector_#{id}" + end + + def render_in(view_context) + view_context.render( + partial: 'tasks/status_selector', + locals: { task: @task, id: dom_id, with_form: @with_form, + project_task_statuses:, + task_status_badge: ->(status) { task_status_badge(status, view_context) }} + ) + end + + private + + def project_task_statuses + # TODO: refactor because it causes N+1 (task statuses loaded separately) + @task.project.task_statuses.default_order + end + + def task_status_badge(status, view_context) + view_context.content_tag( + :span, status.name, + class: ['badge', 'task-status', status.category.dasherize] + ) + end + end + end +end diff --git a/app/views/tasks/_status_selector.html.slim b/app/views/tasks/_status_selector.html.slim index 9fdf54c..c91c34b 100644 --- a/app/views/tasks/_status_selector.html.slim +++ b/app/views/tasks/_status_selector.html.slim @@ -1,2 +1,9 @@ -details.dropdown class=selector_class id=id data-controller="tasks--status-selector" - = render partial: 'status_selector_inner', locals: {selector_id: id, task:, with_form:} +details.dropdown.task-status-selector id=id data-controller="tasks--status-selector" + summary= task_status_badge[task.status] + ul + - project_task_statuses.each do |status| + li + a href="#" data-status-id="#{status.id}" data-action="tasks--status-selector#changeStatus:prevent" = task_status_badge[status] + - if with_form + = form_with model: Tasks::ChangeStatus.new, url: change_status_task_path(task), method: :patch, data: {'tasks--status-selector-target': 'form', action: 'turbo:submit-end->tasks--status-selector#finalize'} do |f| + = f.hidden_field :status_id, data: {'tasks--status-selector-target': 'statusField'} diff --git a/app/views/tasks/_status_selector_inner.html.slim b/app/views/tasks/_status_selector_inner.html.slim deleted file mode 100644 index 8ddd2c5..0000000 --- a/app/views/tasks/_status_selector_inner.html.slim +++ /dev/null @@ -1,9 +0,0 @@ -summary= task_status_badge task.status -ul - - task.project.task_statuses.default_order.each do |status| - li - a href="#" data-status-id="#{status.id}" data-action="tasks--status-selector#changeStatus:prevent" = task_status_badge status -- if with_form - = form_with model: Tasks::ChangeStatus.new, url: change_status_task_path(task), method: :patch, data: {'tasks--status-selector-target': 'form', action: 'turbo:submit-end->tasks--status-selector#finalize'} do |f| - = hidden_field_tag :selector_id, selector_id - = f.hidden_field :status_id, data: {'tasks--status-selector-target': 'statusField'} diff --git a/app/views/tasks/change_status.turbo_stream.slim b/app/views/tasks/change_status.turbo_stream.slim index ac80b73..25f4b25 100644 --- a/app/views/tasks/change_status.turbo_stream.slim +++ b/app/views/tasks/change_status.turbo_stream.slim @@ -1,2 +1,2 @@ -- selector_id = params.fetch(:selector_id) -= turbo_stream.update selector_id, render(partial: 'status_selector_inner', locals: {selector_id:, task: @task, with_form: true}) +- view_model = Tasks::Statuses::SelectorViewModel.new(@task, with_form: true) += turbo_stream.replace view_model.dom_id, render(view_model) diff --git a/app/views/tasks/index.html.slim b/app/views/tasks/index.html.slim index 25f0a4d..4875a90 100644 --- a/app/views/tasks/index.html.slim +++ b/app/views/tasks/index.html.slim @@ -4,7 +4,7 @@ h1= tasks_index_title = link_to 'New', new_task_path(project: current_project&.code) - if @tasks.exists? - table + table.tasks-table thead tbody - @tasks.each do |task| @@ -12,7 +12,7 @@ h1= tasks_index_title tr td= link_to task.full_number, task_path(task) td - = task_status_selector task, selector_class: 'small', id: "task_status_selector_#{task.id}", with_form: true + = task_status_selector task, with_form: true td= task.title td = link_to 'Edit', edit_task_path(task)