Statuses in task - db field and forms

This commit is contained in:
2025-10-25 17:41:06 +03:00
parent b08468568b
commit ba2aad93af
12 changed files with 86 additions and 13 deletions
+4
View File
@@ -28,6 +28,10 @@ Bundler/OrderedGems:
Style/Documentation:
Enabled: false
Style/FrozenStringLiteralComment:
Exclude:
- db/migrate/**/*.rb
Rails/ActionOrder:
Enabled: false
+5 -4
View File
@@ -17,11 +17,12 @@ class TasksController < ApplicationController
def show; end
def new
@form = Tasks::Create.new(project_id: fetch_project&.id)
@project = fetch_project || Project.order(:name).first
@form = Tasks::Create.new(project_id: @project.id)
end
def create
@form = Tasks::Create.new(params.expect(task: %i[project_id title description]))
@form = Tasks::Create.new(params.expect(task: %i[project_id title description status_id]))
if @form.perform
redirect_to tasks_path(project: @form.project)
else
@@ -30,11 +31,11 @@ class TasksController < ApplicationController
end
def edit
@form = Tasks::Update.new(id: @task.id, title: @task.title, description: @task.description)
@form = Tasks::Update.new(@task.attributes.slice(*Tasks::Update.attribute_names))
end
def update
@form = Tasks::Update.new(params.expect(task: %i[title description]))
@form = Tasks::Update.new(params.expect(task: %i[title description status_id]))
if @form.perform(@task)
redirect_to task_path(@task)
@@ -0,0 +1,22 @@
import { Controller } from '@hotwired/stimulus'
// TODO: unite with ProjectsSelectorController?
class FormProjectsSelectorController extends Controller {
static values = {
frame: String
}
connect() {
console.log("Connected", this.element)
}
changeProject(event) {
const loc = new URL(location)
const selected = event.target.selectedOptions[0]
const code = selected.dataset.code
loc.searchParams.set('project', code)
Turbo.visit(loc.toString(), {frame: this.frameValue})
}
}
export default FormProjectsSelectorController
+10
View File
@@ -2,9 +2,11 @@
class Task < ApplicationRecord
belongs_to :project
belongs_to :status, class_name: 'TaskStatus'
validates :number, :title, presence: true
validates :number, numericality: { greater_than: 0 }
validate :associations_should_have_same_project
has_rich_text :description
@@ -25,4 +27,12 @@ class Task < ApplicationRecord
project = Project.find_by!(code: project_code.downcase)
find_by!(project:, number:)
end
private
def associations_should_have_same_project
return if status&.project == project
errors.add(:status, "Doesn't belong in the same project")
end
end
+2
View File
@@ -7,4 +7,6 @@ class TaskStatus < ApplicationRecord
validates :name, presence: true, uniqueness: { scope: :project }
validates :category, presence: true
scope :default_order, -> { order(:category, :name) }
end
+8 -5
View File
@@ -5,18 +5,21 @@ module Tasks
attribute :project_id, :integer
attribute :title, :string
attribute :description, :string
attribute :status_id, :integer
validates :project_id, :title, presence: true
delegate :model_name, to: Task
attr_reader :project, :task
attr_reader :task
def project
@project ||= Project.find(project_id)
end
def perform
@project = Project.find(project_id)
@task = @project.tasks.build(title:, description:, number: @project.next_task_number)
@task.save.tap { @errors = @task.errors }
@task = project.tasks.build(title:, description:, status_id:, number: @project.next_task_number)
save @task
end
end
end
+2 -1
View File
@@ -5,6 +5,7 @@ module Tasks
attribute :id, :integer
attribute :title, :string
attribute :description, :string
attribute :status_id, :integer
validates :title, presence: true
@@ -15,7 +16,7 @@ module Tasks
def perform(task)
@task = task
@id = task.id
@task.assign_attributes(title:, description:)
@task.assign_attributes(title:, description:, status_id:)
save @task
end
end
+3
View File
@@ -3,6 +3,9 @@ h1
=< @task.full_number
= form_with model: @form do |form|
.field
= form.label :status_id
= form.select :status_id, current_project.task_statuses.default_order.map { |ts| [ts.name, ts.id] }
.field
= form.label :title
= form.text_field :title
+6 -2
View File
@@ -1,9 +1,13 @@
h1 New task
= form_with model: @form do |form|
= form_with model: @form, data: {controller: 'form-projects-selector', 'form-projects-selector-frame-value' => 'status_select'} do |form|
.field
= form.label :project_id
= form.select :project_id, Project.order(:name).map { |p| [p.name, p.id] }
= form.select :project_id, Project.order(:name).map { |p| [p.name, p.id, {'data-code': p.code}] }, {}, data: {action: 'form-projects-selector#changeProject'}
= turbo_frame_tag :status_select do
.field
= form.label :status_id
= form.select :status_id, TaskStatus.where(project: @form.project).default_order.map { |ts| [ts.name, ts.id] }
.field
= form.label :title
= form.text_field :title
@@ -0,0 +1,5 @@
class AddStatusToTasks < ActiveRecord::Migration[8.0]
def change
add_reference :tasks, :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.0].define(version: 2025_10_25_131322) do
ActiveRecord::Schema[8.0].define(version: 2025_10_25_132011) do
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.string "name", null: false
t.text "body", size: :long
@@ -86,7 +86,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_25_131322) do
t.string "title", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "status_id"
t.index ["project_id"], name: "index_tasks_on_project_id"
t.index ["status_id"], name: "index_tasks_on_status_id"
end
create_table "users", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
@@ -102,4 +104,5 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_25_131322) do
add_foreign_key "sessions", "users"
add_foreign_key "task_statuses", "projects"
add_foreign_key "tasks", "projects"
add_foreign_key "tasks", "task_statuses", column: "status_id"
end
+15
View File
@@ -0,0 +1,15 @@
# frozen_string_literal: true
namespace :data_migrations do
desc 'Set initial status for tasks'
task set_initial_status_for_tasks: :environment do
Task.includes(project: :task_statuses).in_batches do |tasks|
projects = Project.where(id: tasks.pluck(:project_id))
statuses = TaskStatus.backlog.where(project: projects).group_by(&:project)
tasks.each do |task|
task.update!(status: statuses[task.project].first)
end
end
end
end