diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index 39e9d69d..6005754b 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -1,6 +1,7 @@ # coding: utf-8 class DocumentsController < ApplicationController before_action :set_document, only: %i[ show edit update destroy ] + before_action :get_form_data, only: %i[ new edit ] protect_from_forgery :except => [:api_markdown] # GET /documents or /documents.json @@ -26,9 +27,6 @@ def show # GET /documents/new def new @document = Document.new - @users = User.where(active: true) - @projects = Project.all - @tags = Tag.all # TODO: バージョン7.1以降では datetime_field に include_seconds オプションが導入されるため,以下の秒を0にする記述は不要になる @document.start_at = DateTime.current.change(sec: 0) @document.end_at = DateTime.current.change(sec: 0) @@ -41,9 +39,6 @@ def new # GET /documents/1/edit def edit - @users = User.where(active: true) - @projects = Project.all - @tags = Tag.all end # POST /documents or /documents.json @@ -51,13 +46,15 @@ def create @document = current_user.documents.build(document_params) parse_tag_names(params[:tag_names]) if params[:tag_names] - if @document.save #XXX: save! => save + if @document.save respond_to do |format| format.html { redirect_to @document, notice: "文書を追加しました" } format.json { render :show, status: :created, location: @document } end else respond_to do |format| + flash.now[:danger] = '文書の作成に失敗しました.' + get_form_data format.html { render :new, status: :unprocessable_entity } format.json { render json: @document.errors, status: :unprocessable_entity } end @@ -66,15 +63,28 @@ def create # PATCH/PUT /documents/1 or /documents/1.json def update - parse_tag_names(params[:tag_names]) if params[:tag_names] - if @document.update(document_params) - flash[:success] = "文書を更新しました" - redirect_to @document - else - respond_to do |format| - format.html { render :edit, status: :unprocessable_entity } - format.json { render json: @document.errors, status: :unprocessable_entity } - end + @document.transaction do + parse_tag_names(params[:tag_names]) if params[:tag_names] + @document.update!(document_params) + end + + respond_to do |format| + format.html { redirect_to @document, notice: '文書を更新しました' } + format.json { render :show, status: :ok } + end + rescue ActiveRecord::StaleObjectError + flash.now[:danger] = 'この文書は他のユーザーによって更新されました.' + get_form_data + respond_to do |format| + format.html { render :edit, status: :conflict } + format.json { render json: { error: flash[:error] }, status: :conflict } + end + rescue + flash.now[:danger] = '文書の更新に失敗しました.' + get_form_data + respond_to do |format| + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @document.errors, status: :unprocessable_entity } end end @@ -101,7 +111,7 @@ def set_document # Only allow a list of trusted parameters through. def document_params - params.require(:document).permit(:creator_id, :content, :description, :project_id, :start_at, :end_at, :location, :text,) + params.require(:document).permit(:creator_id, :content, :description, :project_id, :start_at, :end_at, :location, :text, :lock_version) end def parse_tag_names(tag_names) @@ -133,4 +143,10 @@ def build_sort_query_with_default(param) "start_at DESC" end end + + def get_form_data + @users = User.where(active: true) + @projects = Project.all + @tags = Tag.all + end end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index a946031c..f2945654 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -48,7 +48,7 @@ def create @task = current_user.tasks.build(task_params) parse_tag_names(params[:tag_names]) if params[:tag_names] - if @task.save! + if @task.save matched = task_params[:description].match(/\[AI([0-9]+)\]/) if matched != nil ActionItem.find(matched[1]).update(task_url: tasks_path + "/" + @task.id.to_s) @@ -59,6 +59,8 @@ def create end else respond_to do |format| + flash.now[:danger] = 'タスクの作成に失敗しました.' + get_form_data format.html { render :new, status: :unprocessable_entity } format.json { render json: @task.errors, status: :unprocessable_entity } end @@ -67,17 +69,28 @@ def create # PATCH/PUT /tasks/1 or /tasks/1.json def update - parse_tag_names(params[:tag_names]) if params[:tag_names] - if @task.update(task_params) - respond_to do |format| - format.html { redirect_to @task, notice: "タスクを更新しました." } - format.json { render :show, status: :ok, location: @task } - end - else - respond_to do |format| - format.html { render :edit, status: :unprocessable_entity } - format.json { render json: @task.errors, status: :unprocessable_entity } - end + @task.transaction do + parse_tag_names(params[:tag_names]) if params[:tag_names] + @task.update!(task_params) + end + + respond_to do |format| + format.html { redirect_to @task, notice: "タスクを更新しました." } + format.json { render :show, status: :ok, location: @task } + end + rescue ActiveRecord::StaleObjectError + flash.now[:danger] = 'このタスクは他のユーザーによって更新されました.' + get_form_data + respond_to do |format| + format.html { render :edit, status: :conflict } + format.json { render json: { error: flash[:error] }, status: :conflict } + end + rescue + flash.now[:danger] = 'タスクの更新に失敗しました.' + get_form_data + respond_to do |format| + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @document.errors, status: :unprocessable_entity } end end @@ -106,7 +119,7 @@ def get_form_data # Only allow a list of trusted parameters through. def task_params - params.require(:task).permit(:assigner_id, :due_at, :content, :description, :project_id, :task_state_id) + params.require(:task).permit(:assigner_id, :due_at, :content, :description, :project_id, :task_state_id, :lock_version) end def parse_tag_names(tag_names) diff --git a/app/views/documents/_form.html.erb b/app/views/documents/_form.html.erb index a0a361cd..783efaf2 100644 --- a/app/views/documents/_form.html.erb +++ b/app/views/documents/_form.html.erb @@ -10,6 +10,8 @@ <% end %> + <%= form.hidden_field :lock_version %> +
<%= form.label :content, "文書名", class: "form-label fw-bold mb-2" %> <%= form.text_field :content, class: "form-control", placeholder: "文書名を入力してください", required: true %> diff --git a/app/views/tasks/_form.html.erb b/app/views/tasks/_form.html.erb index 13e34352..cde1a94b 100644 --- a/app/views/tasks/_form.html.erb +++ b/app/views/tasks/_form.html.erb @@ -10,6 +10,8 @@
<% end %> + <%= form.hidden_field :lock_version %> +
<%= form.label :assigner_id, "担当者", class: "form-label fw-bold" %> <%= form.select :assigner_id, @users.map { |u| [u.screen_name, u.id] }, {}, class: "form-select" %> diff --git a/db/migrate/20260219050805_add_lock_version_to_documents.rb b/db/migrate/20260219050805_add_lock_version_to_documents.rb new file mode 100644 index 00000000..a4e77d87 --- /dev/null +++ b/db/migrate/20260219050805_add_lock_version_to_documents.rb @@ -0,0 +1,5 @@ +class AddLockVersionToDocuments < ActiveRecord::Migration[7.2] + def change + add_column :documents, :lock_version, :integer + end +end diff --git a/db/migrate/20260225143559_add_lock_version_to_tasks.rb b/db/migrate/20260225143559_add_lock_version_to_tasks.rb new file mode 100644 index 00000000..6166898b --- /dev/null +++ b/db/migrate/20260225143559_add_lock_version_to_tasks.rb @@ -0,0 +1,5 @@ +class AddLockVersionToTasks < ActiveRecord::Migration[7.2] + def change + add_column :tasks, :lock_version, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index de436750..51d75092 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2023_08_24_085144) do +ActiveRecord::Schema[7.2].define(version: 2026_02_25_143559) do create_table "action_items", force: :cascade do |t| t.integer "uid" t.string "task_url" @@ -44,6 +44,7 @@ t.text "location" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "lock_version" t.index ["project_id"], name: "index_documents_on_project_id" end @@ -91,6 +92,7 @@ t.datetime "updated_at", null: false t.integer "tag_id" t.integer "task_state_id" + t.integer "lock_version" t.index ["project_id"], name: "index_tasks_on_project_id" t.index ["tag_id"], name: "index_tasks_on_tag_id" t.index ["task_state_id"], name: "index_tasks_on_task_state_id"