Workflow default status and computing positions
This commit is contained in:
@@ -17,7 +17,7 @@ module ProjectAdmin
|
|||||||
end
|
end
|
||||||
|
|
||||||
@form = ProjectAdmin::Workflows::Statuses::BatchUpdate.new(form_params)
|
@form = ProjectAdmin::Workflows::Statuses::BatchUpdate.new(form_params)
|
||||||
if @form.call(@workflow)
|
if @form.perform(@workflow)
|
||||||
redirect_to(action: :edit_transitions)
|
redirect_to(action: :edit_transitions)
|
||||||
else
|
else
|
||||||
render :edit
|
render :edit
|
||||||
@@ -29,13 +29,14 @@ module ProjectAdmin
|
|||||||
end
|
end
|
||||||
|
|
||||||
def batch_update_transitions
|
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)
|
if form_params[:task_statuses_attributes].respond_to?(:keys)
|
||||||
form_params[:task_statuses_attributes] = form_params[:task_statuses_attributes].values
|
form_params[:task_statuses_attributes] = form_params[:task_statuses_attributes].values
|
||||||
end
|
end
|
||||||
|
|
||||||
@form = ProjectAdmin::Workflows::Statuses::UpdateTransitions.new(form_params)
|
@form = ProjectAdmin::Workflows::Statuses::UpdateTransitions.new(form_params)
|
||||||
if @form.call(@workflow)
|
if @form.perform(@workflow)
|
||||||
redirect_to project_admin_workflow_path(@project, @workflow)
|
redirect_to project_admin_workflow_path(@project, @workflow)
|
||||||
else
|
else
|
||||||
render :edit_transitions
|
render :edit_transitions
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class TasksController < ApplicationController
|
|||||||
Task.all
|
Task.all
|
||||||
end
|
end
|
||||||
|
|
||||||
@tasks = @tasks.includes(:status, :project, workflow: :task_statuses)
|
@tasks = @tasks.includes(:project, workflow: %i[task_statuses default_status], status: :next_statuses)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
@@ -21,7 +21,8 @@ class TasksController < ApplicationController
|
|||||||
def new
|
def new
|
||||||
@project = self.current_project = fetch_project || Project.order(:name).first
|
@project = self.current_project = fetch_project || Project.order(:name).first
|
||||||
@workflow = fetch_workflow || @project.workflows.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
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|||||||
@@ -6,7 +6,18 @@ class Workflow < ApplicationRecord
|
|||||||
has_many :tasks, dependent: :restrict_with_exception
|
has_many :tasks, dependent: :restrict_with_exception
|
||||||
has_many :task_statuses, dependent: :restrict_with_error
|
has_many :task_statuses, dependent: :restrict_with_error
|
||||||
accepts_nested_attributes_for :task_statuses, allow_destroy: true
|
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 :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
|
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
|
end
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ module ProjectAdmin
|
|||||||
@task_statuses = Array(attributes).map { |e| TaskStatus.new(e) }
|
@task_statuses = Array(attributes).map { |e| TaskStatus.new(e) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(workflow)
|
def perform(workflow)
|
||||||
@workflow = workflow
|
@workflow = workflow
|
||||||
|
|
||||||
@workflow.assign_attributes(task_statuses_attributes: task_statuses.map(&:to_model_attributes))
|
@workflow.assign_attributes(task_statuses_attributes: task_statuses.map(&:to_model_attributes))
|
||||||
|
|||||||
@@ -12,20 +12,50 @@ module ProjectAdmin
|
|||||||
attribute :next_status_ids
|
attribute :next_status_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :task_statuses
|
attr_accessor :workflow, :task_statuses
|
||||||
|
|
||||||
|
attribute :default_status_id, :integer
|
||||||
|
|
||||||
def self.from_model(workflow)
|
def self.from_model(workflow)
|
||||||
new(task_statuses_attributes: workflow.task_statuses
|
new(
|
||||||
.includes(:next_statuses)
|
workflow:,
|
||||||
.map { |ts| { id: ts.id, next_status_ids: ts.next_status_ids } })
|
task_statuses_attributes: workflow.task_statuses
|
||||||
|
.includes(:next_statuses)
|
||||||
|
.map { |ts| { id: ts.id, next_status_ids: ts.next_status_ids } },
|
||||||
|
default_status_id: workflow.default_status_id
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def task_statuses_attributes=(attributes)
|
def task_statuses_attributes=(attributes)
|
||||||
@task_statuses = Array(attributes).map { |e| TaskStatus.new(e) }
|
@task_statuses = Array(attributes).map { |e| TaskStatus.new(e) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(workflow)
|
def perform(workflow)
|
||||||
workflow.update(task_statuses_attributes: task_statuses.map(&:attributes))
|
@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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ module Tasks
|
|||||||
private
|
private
|
||||||
|
|
||||||
def workflow_task_statuses
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ h1
|
|||||||
h2 Statuses
|
h2 Statuses
|
||||||
|
|
||||||
ul
|
ul
|
||||||
- @workflow.task_statuses.each do |status|
|
- @workflow.task_statuses.default_order.each do |status|
|
||||||
li= task_status_badge(status)
|
li= task_status_badge(status)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
= f.fields_for :task_statuses do |tsf|
|
= f.fields_for :task_statuses do |tsf|
|
||||||
section
|
section
|
||||||
div= task_status_badge tsf.object
|
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
|
div= tsf.collection_checkboxes :next_status_ids, @workflow.task_statuses - [tsf.object], :id, :name
|
||||||
|
|
||||||
.submit
|
.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
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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|
|
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
|
||||||
t.text "body", size: :long
|
t.text "body", size: :long
|
||||||
t.datetime "created_at", null: false
|
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|
|
create_table "workflows", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
|
||||||
t.string "color", null: false
|
t.string "color", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
|
t.bigint "default_status_id"
|
||||||
t.string "icon", null: false
|
t.string "icon", null: false
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.bigint "project_id", null: false
|
t.bigint "project_id", null: false
|
||||||
t.datetime "updated_at", 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"], name: "index_workflows_on_project_id_and_name", unique: true
|
||||||
t.index ["project_id"], name: "index_workflows_on_project_id"
|
t.index ["project_id"], name: "index_workflows_on_project_id"
|
||||||
end
|
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", "projects"
|
||||||
add_foreign_key "tasks", "task_statuses", column: "status_id"
|
add_foreign_key "tasks", "task_statuses", column: "status_id"
|
||||||
add_foreign_key "workflows", "projects"
|
add_foreign_key "workflows", "projects"
|
||||||
|
add_foreign_key "workflows", "task_statuses", column: "default_status_id"
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user