Statuses in task - db field and forms
This commit is contained in:
@@ -28,6 +28,10 @@ Bundler/OrderedGems:
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Exclude:
|
||||
- db/migrate/**/*.rb
|
||||
|
||||
Rails/ActionOrder:
|
||||
Enabled: false
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user