Workflow default status and computing positions

This commit is contained in:
2026-04-25 16:17:14 +03:00
parent 2a0a70c290
commit debff6dc22
10 changed files with 71 additions and 15 deletions
@@ -17,7 +17,7 @@ module ProjectAdmin
end
@form = ProjectAdmin::Workflows::Statuses::BatchUpdate.new(form_params)
if @form.call(@workflow)
if @form.perform(@workflow)
redirect_to(action: :edit_transitions)
else
render :edit
@@ -29,13 +29,14 @@ module ProjectAdmin
end
def batch_update_transitions
form_params = params.expect(workflow: { task_statuses_attributes: [[:id, { next_status_ids: [] }]] })
form_params = params.expect(workflow: [:default_status_id,
{ task_statuses_attributes: [[:id, { next_status_ids: [] }]] }])
if form_params[:task_statuses_attributes].respond_to?(:keys)
form_params[:task_statuses_attributes] = form_params[:task_statuses_attributes].values
end
@form = ProjectAdmin::Workflows::Statuses::UpdateTransitions.new(form_params)
if @form.call(@workflow)
if @form.perform(@workflow)
redirect_to project_admin_workflow_path(@project, @workflow)
else
render :edit_transitions
+3 -2
View File
@@ -13,7 +13,7 @@ class TasksController < ApplicationController
Task.all
end
@tasks = @tasks.includes(:status, :project, workflow: :task_statuses)
@tasks = @tasks.includes(:project, workflow: %i[task_statuses default_status], status: :next_statuses)
end
def show; end
@@ -21,7 +21,8 @@ class TasksController < ApplicationController
def new
@project = self.current_project = fetch_project || Project.order(:name).first
@workflow = fetch_workflow || @project.workflows.first
@form = Tasks::Create.new(project_id: @project.id, workflow_id: @workflow.id)
@form = Tasks::Create.new(project_id: @project.id, workflow_id: @workflow.id,
status_id: @workflow.default_status&.id)
end
def create
+11
View File
@@ -6,7 +6,18 @@ class Workflow < ApplicationRecord
has_many :tasks, dependent: :restrict_with_exception
has_many :task_statuses, dependent: :restrict_with_error
accepts_nested_attributes_for :task_statuses, allow_destroy: true
belongs_to :default_status, class_name: 'TaskStatus', optional: true
enum :icon, { task: 'task', warning: 'warning' }, default: 'task', scopes: false
enum :color, { blue: 'blue', gray: 'gray', lime: 'lime', red: 'red', teal: 'teal' }, default: 'gray', scopes: false
validate :should_own_default_status
private
def should_own_default_status
return if default_status.nil?
errors.add(:default_status, 'Should own default status') unless default_status.id.in?(task_status_ids)
end
end
@@ -40,7 +40,7 @@ module ProjectAdmin
@task_statuses = Array(attributes).map { |e| TaskStatus.new(e) }
end
def call(workflow)
def perform(workflow)
@workflow = workflow
@workflow.assign_attributes(task_statuses_attributes: task_statuses.map(&:to_model_attributes))
@@ -12,20 +12,50 @@ module ProjectAdmin
attribute :next_status_ids
end
attr_accessor :task_statuses
attr_accessor :workflow, :task_statuses
attribute :default_status_id, :integer
def self.from_model(workflow)
new(task_statuses_attributes: workflow.task_statuses
new(
workflow:,
task_statuses_attributes: workflow.task_statuses
.includes(:next_statuses)
.map { |ts| { id: ts.id, next_status_ids: ts.next_status_ids } })
.map { |ts| { id: ts.id, next_status_ids: ts.next_status_ids } },
default_status_id: workflow.default_status_id
)
end
def task_statuses_attributes=(attributes)
@task_statuses = Array(attributes).map { |e| TaskStatus.new(e) }
end
def call(workflow)
workflow.update(task_statuses_attributes: task_statuses.map(&:attributes))
def perform(workflow)
@workflow = workflow
workflow.assign_attributes(default_status_id:, task_statuses_attributes: task_statuses.map(&:attributes))
save workflow
end
after_success do
if @workflow.default_status.nil?
@workflow.task_statuses.update!(position: 0)
return
end
@workflow.transaction do
@workflow.default_status.update!(position: 0)
seen_status_ids = Set[@workflow.default_status.id]
statuses_to_process = [@workflow.default_status]
until statuses_to_process.empty?
status = statuses_to_process.pop
next_statuses = status.next_statuses.where.not(id: seen_status_ids)
next_statuses.update(position: status.position + 1)
statuses_to_process.concat(next_statuses.to_a)
seen_status_ids.merge(next_statuses.map(&:id))
end
end
end
end
end
@@ -40,7 +40,11 @@ module Tasks
private
def workflow_task_statuses
@task.workflow.task_statuses.sort_by { |e| [e.position, e.name] }
return @task.workflow.task_statuses.sort_by { |e| [e.position, e.name] } if @task.status.next_statuses.empty?
@task.status.next_statuses.sort_by { |e| [e.position, e.name] }.tap do |statuses|
statuses.prepend(@task.status)
end
end
end
end
@@ -9,5 +9,5 @@ h1
h2 Statuses
ul
- @workflow.task_statuses.each do |status|
- @workflow.task_statuses.default_order.each do |status|
li= task_status_badge(status)
@@ -3,6 +3,7 @@
= f.fields_for :task_statuses do |tsf|
section
div= task_status_badge tsf.object
div= f.radio_button :default_status_id, tsf.object.id
div= tsf.collection_checkboxes :next_status_ids, @workflow.task_statuses - [tsf.object], :id, :name
.submit
@@ -0,0 +1,5 @@
class AddDefaultStatusToWorkflows < ActiveRecord::Migration[8.1]
def change
add_reference :workflows, :default_status, foreign_key: { to_table: :task_statuses }
end
end
Generated
+4 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2026_04_25_112121) do
ActiveRecord::Schema[8.1].define(version: 2026_04_25_123859) do
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.text "body", size: :long
t.datetime "created_at", null: false
@@ -114,10 +114,12 @@ ActiveRecord::Schema[8.1].define(version: 2026_04_25_112121) do
create_table "workflows", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.string "color", null: false
t.datetime "created_at", null: false
t.bigint "default_status_id"
t.string "icon", null: false
t.string "name", null: false
t.bigint "project_id", null: false
t.datetime "updated_at", null: false
t.index ["default_status_id"], name: "index_workflows_on_default_status_id"
t.index ["project_id", "name"], name: "index_workflows_on_project_id_and_name", unique: true
t.index ["project_id"], name: "index_workflows_on_project_id"
end
@@ -130,4 +132,5 @@ ActiveRecord::Schema[8.1].define(version: 2026_04_25_112121) do
add_foreign_key "tasks", "projects"
add_foreign_key "tasks", "task_statuses", column: "status_id"
add_foreign_key "workflows", "projects"
add_foreign_key "workflows", "task_statuses", column: "default_status_id"
end