From 3615ad1827e38ced41cfa0cf24c22b86d8056b7b Mon Sep 17 00:00:00 2001 From: Vitalii Bakun Date: Thu, 1 May 2025 18:02:42 +0300 Subject: [PATCH 1/5] PC-49: Update Category's pages in ActiveAdmin --- app/admin/categories.rb | 39 +++++ app/admin/items.rb | 54 +++--- ...ource_helper.rb => action_check_helper.rb} | 2 +- app/models/category.rb | 4 +- app/models/item.rb | 4 +- ...501143252_add_items_count_to_categories.rb | 5 + db/schema.rb | 157 +++++++++++++++++- 7 files changed, 240 insertions(+), 25 deletions(-) rename app/helpers/active_admin/{resource_helper.rb => action_check_helper.rb} (76%) create mode 100644 db/migrate/20250501143252_add_items_count_to_categories.rb diff --git a/app/admin/categories.rb b/app/admin/categories.rb index 978a3c53..b45b77ac 100644 --- a/app/admin/categories.rb +++ b/app/admin/categories.rb @@ -10,6 +10,7 @@ id_column column :name column('Disabled') { |item| status_tag item.is_disabled, label: item.is_disabled? ? 'True' : 'False' } + column :items_count column :created_at column :updated_at actions defaults: false do |category| @@ -34,6 +35,25 @@ f.input :description end f.actions + + panel edit_action? ? "Items" : "Items without category" do + div do + link_to 'Add Item', edit_action? ? new_admin_item_path(category_id: resource.id) : new_admin_item_path, class: 'button' + end + + items = edit_action? ? resource.items : Item.without_category + + if items.any? + table_for items do + column 'Item name' do |item| + link_to item.name, edit_admin_item_path(item) + end + column 'Item description', :description + end + else + "Items not found" + end + end end show do @@ -44,6 +64,19 @@ row :created_at row :updated_at end + + panel "Items" do + if resource.items.any? + table_for resource.items do + column 'Item name' do |item| + link_to item.name, admin_item_path(item) + end + column 'Item description', :description + end + else + "Items not found" + end + end end action_item :toggle, only: :show do @@ -64,6 +97,10 @@ resource.toggle(:is_disabled) if resource.save + # rubocop:disable Rails/SkipsModelValidations + resource.items.update_all(is_disabled: true) if resource.is_disabled? + # rubocop:enable Rails/SkipsModelValidations + redirect_to admin_categories_path, notice: "Category was successfully #{resource.is_disabled? ? 'disabled' : 'enabled'}." else redirect_to admin_categories_path, alert: resource.errors.messages_for(:name) @@ -71,6 +108,8 @@ end controller do + helper ActiveAdmin::ActionCheckHelper + def create @category = Category.new(permitted_params[:category]) diff --git a/app/admin/items.rb b/app/admin/items.rb index 9ff1e44a..ea98c401 100644 --- a/app/admin/items.rb +++ b/app/admin/items.rb @@ -44,9 +44,9 @@ f.input :name, required: true, input_html: { value: f.object.name.presence || meta_data["name"] } f.input :description, as: :string, input_html: { value: f.object.description.presence || meta_data["description"] } f.input :category_id, as: :select, - collection: Category.pluck(:name, :id), - include_blank: "No Category", - selected: f.object.category_id.presence || meta_data["category_id"] + collection: Category.pluck(:name, :id), + include_blank: "No Category", + selected: f.object.category_id.presence || meta_data["category_id"] || params[:category_id] end div class: "add-param-link-wrapper" do @@ -55,14 +55,14 @@ para do concat(link_to("Add Parameter", "#", class: "button store-and-navigate", data: { - redirect: new_parameter_admin_item_path(id: item_id), - item_id: item_id - })) + redirect: new_parameter_admin_item_path(id: item_id), + item_id: item_id + })) concat(link_to(formula_label, "#", class: "button store-and-navigate", data: { - redirect: new_formula_admin_item_path(id: item_id), - item_id: item_id - })) + redirect: new_formula_admin_item_path(id: item_id), + item_id: item_id + })) end end @@ -81,8 +81,8 @@ end end - tmp_fixed = session_service.get(:fixed) || {} - tmp_open = session_service.get(:open) || [] + tmp_fixed = session_service.get(:fixed) || {} + tmp_open = session_service.get(:open) || [] tmp_select = session_service.get(:select) || {} panel "Pricing Parameters" do @@ -149,7 +149,12 @@ def create if @item.save session_service.delete - redirect_to admin_item_path(@item), notice: "Item was successfully created." + + if @item.category.present? + redirect_to edit_admin_category_path(@item.category), notice: "Item was successfully created." + else + redirect_to new_admin_category_path, notice: "Item was successfully created." + end else flash.now[:error] = "Failed to create item: #{@item.errors.full_messages.to_sentence}" render :new, status: :unprocessable_entity @@ -181,7 +186,12 @@ def update if @item.update(permitted_params[:item].except(:formula_parameters)) session_service.delete - redirect_to admin_item_path(@item), notice: "Item was successfully updated." + + if @item.category.present? + redirect_to edit_admin_category_path(@item.category), notice: "Item was successfully updated." + else + redirect_to admin_item_path(@item), notice: "Item was successfully updated." + end else flash[:error] = "Failed to update item: #{@item.errors.full_messages.to_sentence}" render :edit @@ -192,9 +202,9 @@ def update def session_service @session_service ||= begin - item_key = params[:id].presence || "new" - TmpParamsSessionService.new(session, item_key.to_s) - end + item_key = params[:id].presence || "new" + TmpParamsSessionService.new(session, item_key.to_s) + end end end @@ -212,7 +222,11 @@ def session_service end action_item :back, only: :show do - link_to "Back to Items", admin_items_path + if resource.category.present? + link_to "Back to Category", admin_category_path(resource.category) + else + link_to "Back to Items", admin_items_path + end end member_action :toggle, method: :put do @@ -229,8 +243,8 @@ def session_service param_type = params[:parameter_type] param_name = case param_type - when "Fixed" then params[:fixed_parameter_name].to_s.strip - when "Open" then params[:open_parameter_name].to_s.strip + when "Fixed" then params[:fixed_parameter_name].to_s.strip + when "Open" then params[:open_parameter_name].to_s.strip when "Select" then params[:select_parameter_name].to_s.strip end @@ -269,7 +283,7 @@ def session_service valid_options = false (params[:select_options] || []).each do |pair| desc = pair["description"].to_s.strip - val = pair["value"].to_s.strip + val = pair["value"].to_s.strip next if desc.blank? || val.blank? sub_hash[desc] = val diff --git a/app/helpers/active_admin/resource_helper.rb b/app/helpers/active_admin/action_check_helper.rb similarity index 76% rename from app/helpers/active_admin/resource_helper.rb rename to app/helpers/active_admin/action_check_helper.rb index 35982ff7..06af8c57 100644 --- a/app/helpers/active_admin/resource_helper.rb +++ b/app/helpers/active_admin/action_check_helper.rb @@ -1,5 +1,5 @@ module ActiveAdmin - module ResourceHelper + module ActionCheckHelper def edit_action? params[:action] == 'edit' end diff --git a/app/models/category.rb b/app/models/category.rb index 98373d7b..39898f4a 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,8 +1,8 @@ class Category < ApplicationRecord - ASCII_CHARACTERS = /\A[[:ascii:]]*\z/ - has_many :items, dependent: :nullify + ASCII_CHARACTERS = /\A[[:ascii:]]*\z/ + normalizes :name, with: ->(name) { name.gsub(/\s+/, ' ').strip } after_update :disable_related_items_if_disabled diff --git a/app/models/item.rb b/app/models/item.rb index 3888681f..2f776418 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -1,5 +1,5 @@ class Item < ApplicationRecord - belongs_to :category, optional: true + belongs_to :category, optional: true, counter_cache: true validates :name, presence: true validates :name, uniqueness: { scope: :category_id, message: "Item name must be unique within category" } @@ -9,6 +9,8 @@ class Item < ApplicationRecord validates :calculation_formula, presence: true, if: :requires_calculation_formula? validate :calculation_formula_must_be_valid, if: :requires_calculation_formula? + scope :without_category, -> { where(category_id: nil) } + def self.ransackable_attributes(_auth_object = nil) %w[category_id created_at id is_disabled name updated_at] end diff --git a/db/migrate/20250501143252_add_items_count_to_categories.rb b/db/migrate/20250501143252_add_items_count_to_categories.rb new file mode 100644 index 00000000..e56c9bc0 --- /dev/null +++ b/db/migrate/20250501143252_add_items_count_to_categories.rb @@ -0,0 +1,5 @@ +class AddItemsCountToCategories < ActiveRecord::Migration[8.0] + def change + add_column :categories, :items_count, :integer, default: 0, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index e01d3293..83a14e73 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[8.0].define(version: 2025_04_01_102711) do +ActiveRecord::Schema[8.0].define(version: 2025_05_01_143252) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -66,6 +66,7 @@ t.boolean "is_disabled", default: false, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "items_count", default: 0, null: false t.index ["is_disabled"], name: "index_categories_on_is_disabled" t.index ["name"], name: "index_categories_on_name", unique: true, where: "(is_disabled = false)" end @@ -113,6 +114,154 @@ t.index ["user_id"], name: "index_quotes_on_user_id" end + create_table "settings", force: :cascade do |t| + t.text "style" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "solid_cable_messages", force: :cascade do |t| + t.binary "channel", null: false + t.binary "payload", null: false + t.datetime "created_at", null: false + t.bigint "channel_hash", null: false + t.index ["channel"], name: "index_solid_cable_messages_on_channel" + t.index ["channel_hash"], name: "index_solid_cable_messages_on_channel_hash" + t.index ["created_at"], name: "index_solid_cable_messages_on_created_at" + end + + create_table "solid_cache_entries", force: :cascade do |t| + t.binary "key", null: false + t.binary "value", null: false + t.datetime "created_at", null: false + t.bigint "key_hash", null: false + t.integer "byte_size", null: false + t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size" + t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size" + t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true + end + + create_table "solid_queue_blocked_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.string "concurrency_key", null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release" + t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + end + + create_table "solid_queue_claimed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.bigint "process_id" + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + end + + create_table "solid_queue_failed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.text "error" + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true + end + + create_table "solid_queue_jobs", force: :cascade do |t| + t.string "queue_name", null: false + t.string "class_name", null: false + t.text "arguments" + t.integer "priority", default: 0, null: false + t.string "active_job_id" + t.datetime "scheduled_at" + t.datetime "finished_at" + t.string "concurrency_key" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id" + t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name" + t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at" + t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering" + t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting" + end + + create_table "solid_queue_pauses", force: :cascade do |t| + t.string "queue_name", null: false + t.datetime "created_at", null: false + t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true + end + + create_table "solid_queue_processes", force: :cascade do |t| + t.string "kind", null: false + t.datetime "last_heartbeat_at", null: false + t.bigint "supervisor_id" + t.integer "pid", null: false + t.string "hostname" + t.text "metadata" + t.datetime "created_at", null: false + t.string "name", null: false + t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" + end + + create_table "solid_queue_ready_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index ["priority", "job_id"], name: "index_solid_queue_poll_all" + t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue" + end + + create_table "solid_queue_recurring_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "task_key", null: false + t.datetime "run_at", null: false + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + end + + create_table "solid_queue_recurring_tasks", force: :cascade do |t| + t.string "key", null: false + t.string "schedule", null: false + t.string "command", limit: 2048 + t.string "class_name" + t.text "arguments" + t.string "queue_name" + t.integer "priority", default: 0 + t.boolean "static", default: true, null: false + t.text "description" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static" + end + + create_table "solid_queue_scheduled_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "scheduled_at", null: false + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all" + end + + create_table "solid_queue_semaphores", force: :cascade do |t| + t.string "key", null: false + t.integer "value", default: 1, null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at" + t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value" + t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true + end + create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -131,4 +280,10 @@ add_foreign_key "items", "categories" add_foreign_key "quotes", "customers" add_foreign_key "quotes", "users" + add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade end From f927f2b6317634028addc4c971f3bdc92f1099ec Mon Sep 17 00:00:00 2001 From: Vitalii Bakun Date: Fri, 2 May 2025 16:15:03 +0300 Subject: [PATCH 2/5] PC-49: Add controller tests for categories and items --- app/admin/items.rb | 24 +++--- spec/requests/admin/items_controller_spec.rb | 83 +++++++++++++++++--- 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/app/admin/items.rb b/app/admin/items.rb index ea98c401..70996d5b 100644 --- a/app/admin/items.rb +++ b/app/admin/items.rb @@ -44,9 +44,9 @@ f.input :name, required: true, input_html: { value: f.object.name.presence || meta_data["name"] } f.input :description, as: :string, input_html: { value: f.object.description.presence || meta_data["description"] } f.input :category_id, as: :select, - collection: Category.pluck(:name, :id), - include_blank: "No Category", - selected: f.object.category_id.presence || meta_data["category_id"] || params[:category_id] + collection: Category.pluck(:name, :id), + include_blank: "No Category", + selected: f.object.category_id.presence || meta_data["category_id"] || params[:category_id] end div class: "add-param-link-wrapper" do @@ -55,14 +55,14 @@ para do concat(link_to("Add Parameter", "#", class: "button store-and-navigate", data: { - redirect: new_parameter_admin_item_path(id: item_id), - item_id: item_id - })) + redirect: new_parameter_admin_item_path(id: item_id), + item_id: item_id + })) concat(link_to(formula_label, "#", class: "button store-and-navigate", data: { - redirect: new_formula_admin_item_path(id: item_id), - item_id: item_id - })) + redirect: new_formula_admin_item_path(id: item_id), + item_id: item_id + })) end end @@ -202,9 +202,9 @@ def update def session_service @session_service ||= begin - item_key = params[:id].presence || "new" - TmpParamsSessionService.new(session, item_key.to_s) - end + item_key = params[:id].presence || "new" + TmpParamsSessionService.new(session, item_key.to_s) + end end end diff --git a/spec/requests/admin/items_controller_spec.rb b/spec/requests/admin/items_controller_spec.rb index e160bdee..a3cc2e27 100644 --- a/spec/requests/admin/items_controller_spec.rb +++ b/spec/requests/admin/items_controller_spec.rb @@ -60,16 +60,45 @@ } end + let(:valid_params_without_category) do + { + item: { + name: 'Test Item', + description: 'A test item' + } + } + end + let(:invalid_params) { { item: { name: '' } } } - it 'creates a new item and redirects' do - expect do - post '/admin/items', params: valid_params - end.to change(Item, :count).by(1) + context 'when a category is' do + it 'not selected and redirects to the new category page' do + expect do + post admin_items_path, params: valid_params_without_category + end.to change(Item, :count).by(1) - expect(response).to redirect_to(%r{/admin/items/\d+}) - follow_redirect! - expect(response.body).to include('Test Item') + expect(response).to redirect_to(new_admin_category_path) + + follow_redirect! + + expect(response).to have_http_status(:ok) + expect(response.body).to include('Test Item') + expect(response.body).to include('Item was successfully created.') + end + + it 'selected and redirects to the edit category page' do + expect do + post admin_items_path(category_id: category.id), params: valid_params + end.to change(Item, :count).by(1) + + expect(response).to redirect_to(edit_admin_category_path(category)) + + follow_redirect! + + expect(response).to have_http_status(:ok) + expect(response.body).to include('Test Item') + expect(response.body).to include('Item was successfully created.') + end end context 'with session parameters' do @@ -114,7 +143,7 @@ expect(item.is_fixed).to be true expect(item.is_open).to be true expect(item.is_selectable_options).to be true - expect(response).to redirect_to(%r{/admin/items/\d+}) + expect(response).to redirect_to(edit_admin_category_path(item.category)) end it 'does not create an item with invalid params' do @@ -137,16 +166,50 @@ } } end - + let(:valid_params_without_category) do + { + item: { + name: 'Updated Item', + description: 'Updated description', + category_id: nil + } + } + end let(:invalid_params) { { item: { name: '' } } } + context 'when category is' do + it 'selected and redirects to the edit category page' do + put admin_item_path(item), params: valid_params + + expect(response).to redirect_to(edit_admin_category_path(category)) + + follow_redirect! + + expect(response).to have_http_status(:ok) + expect(response.body).to include('Updated Item') + expect(response.body).to include("Item was successfully updated.") + end + + it 'not selected and redirects to the item page' do + put admin_item_path(item), params: valid_params_without_category + + expect(response).to redirect_to(admin_item_path(item)) + + follow_redirect! + + expect(response).to have_http_status(:ok) + expect(response.body).to include('Updated Item') + expect(response.body).to include("Item was successfully updated.") + end + end + it 'updates the item and redirects' do put "/admin/items/#{item.id}", params: valid_params item.reload expect(item.name).to eq('Updated Item') expect(item.description).to eq('Updated description') - expect(response).to redirect_to("/admin/items/#{item.id}") + expect(response).to redirect_to(edit_admin_category_path(item.category)) follow_redirect! expect(response.body).to include('Updated Item') end From 1f08c6e5854c7931d52b19203be6679c3da65612 Mon Sep 17 00:00:00 2001 From: Vitalii Bakun Date: Fri, 2 May 2025 16:21:03 +0300 Subject: [PATCH 3/5] PC-49: Fix rubocop lint --- app/admin/items.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/admin/items.rb b/app/admin/items.rb index 13c79a53..9e8ace9c 100644 --- a/app/admin/items.rb +++ b/app/admin/items.rb @@ -44,9 +44,9 @@ f.input :name, required: true, input_html: { value: f.object.name.presence || meta_data["name"] } f.input :description, as: :string, input_html: { value: f.object.description.presence || meta_data["description"] } f.input :category_id, as: :select, - collection: Category.pluck(:name, :id), - include_blank: "No Category", - selected: f.object.category_id.presence || meta_data["category_id"] || params[:category_id] + collection: Category.pluck(:name, :id), + include_blank: "No Category", + selected: f.object.category_id.presence || meta_data["category_id"] || params[:category_id] end div class: "add-param-link-wrapper" do @@ -205,7 +205,6 @@ def session_service item_key = params[:id].presence || "new" TmpParamsSessionService.new(session, item_key.to_s) end - end end From 562b2bfc024a9097bdb3b7a92047dbf47659e586 Mon Sep 17 00:00:00 2001 From: Vitalii Bakun Date: Mon, 12 May 2025 11:23:23 +0300 Subject: [PATCH 4/5] PC-146: Fix redirect on category edit page and add ability to select items for category --- app/admin/categories.rb | 46 ++++++++++++++++++++--------------------- app/admin/items.rb | 28 +++++++++++++++++-------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/app/admin/categories.rb b/app/admin/categories.rb index b45b77ac..fd192751 100644 --- a/app/admin/categories.rb +++ b/app/admin/categories.rb @@ -36,22 +36,24 @@ end f.actions - panel edit_action? ? "Items" : "Items without category" do - div do - link_to 'Add Item', edit_action? ? new_admin_item_path(category_id: resource.id) : new_admin_item_path, class: 'button' - end + if edit_action? + panel "Items" do + div do + link_to 'Add Item', new_admin_item_path(category_id: resource.id), class: 'button' + end - items = edit_action? ? resource.items : Item.without_category + items = resource.items - if items.any? - table_for items do - column 'Item name' do |item| - link_to item.name, edit_admin_item_path(item) + if items.any? + table_for items do + column 'Item name' do |item| + link_to item.name, edit_admin_item_path(item) + end + column 'Item description', :description end - column 'Item description', :description + else + "Items not found" end - else - "Items not found" end end end @@ -66,8 +68,14 @@ end panel "Items" do - if resource.items.any? - table_for resource.items do + div do + link_to 'Add Item', new_admin_item_path(category_id: resource.id), class: 'button' + end + + items = resource.items + + if items.any? + table_for items do column 'Item name' do |item| link_to item.name, admin_item_path(item) end @@ -109,15 +117,5 @@ controller do helper ActiveAdmin::ActionCheckHelper - - def create - @category = Category.new(permitted_params[:category]) - - if @category.save - redirect_to admin_categories_path, notice: 'Category was successfully created.' - else - render :new - end - end end end diff --git a/app/admin/items.rb b/app/admin/items.rb index 9e8ace9c..cc6c6866 100644 --- a/app/admin/items.rb +++ b/app/admin/items.rb @@ -11,6 +11,7 @@ filter :is_disabled, label: "Disabled" index do + selectable_column id_column column :name column :category, sortable: :category_id @@ -81,8 +82,8 @@ end end - tmp_fixed = session_service.get(:fixed) || {} - tmp_open = session_service.get(:open) || [] + tmp_fixed = session_service.get(:fixed) || {} + tmp_open = session_service.get(:open) || [] tmp_select = session_service.get(:select) || {} panel "Pricing Parameters" do @@ -140,6 +141,19 @@ end end + batch_action :assign_to_category, form: -> { { category: Category.pluck(:name, :id) } } do |ids, inputs| + category = Category.find_by(id: inputs[:category]) + + if category + # rubocop:disable Rails/SkipsModelValidations + Item.where(id: ids).update_all(category_id: category.id) + # rubocop:enable Rails/SkipsModelValidations + redirect_to admin_items_path, notice: "Category was successfully assigned." + else + redirect_back fallback_location: admin_items_path, alert: "Category not found." + end + end + controller do helper_method :session_service @@ -150,11 +164,7 @@ def create if @item.save session_service.delete - if @item.category.present? - redirect_to edit_admin_category_path(@item.category), notice: "Item was successfully created." - else - redirect_to new_admin_category_path, notice: "Item was successfully created." - end + redirect_to admin_item_path(@item), notice: "Item was successfully created." else flash.now[:error] = "Failed to create item: #{@item.errors.full_messages.to_sentence}" render :new, status: :unprocessable_entity @@ -243,8 +253,8 @@ def session_service param_type = params[:parameter_type] param_name = case param_type - when "Fixed" then params[:fixed_parameter_name].to_s.strip - when "Open" then params[:open_parameter_name].to_s.strip + when "Fixed" then params[:fixed_parameter_name].to_s.strip + when "Open" then params[:open_parameter_name].to_s.strip when "Select" then params[:select_parameter_name].to_s.strip end From b9a371c5b0c18c6eabafb22004fed126918f541a Mon Sep 17 00:00:00 2001 From: Vitalii Bakun Date: Mon, 12 May 2025 11:35:13 +0300 Subject: [PATCH 5/5] PC-146: Fix test --- spec/requests/admin/categories_controller_spec.rb | 4 ++-- spec/requests/admin/items_controller_spec.rb | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/requests/admin/categories_controller_spec.rb b/spec/requests/admin/categories_controller_spec.rb index 92ab75d5..508f56d5 100644 --- a/spec/requests/admin/categories_controller_spec.rb +++ b/spec/requests/admin/categories_controller_spec.rb @@ -46,10 +46,10 @@ end describe 'POST /admin/categories' do - it 'checks redirect to the categories page and successfully creates a category' do + it 'checks redirect to the category page and successfully creates a category' do post admin_categories_path, params: { category: { name: 'Category', description: 'Description' } } - expect(response).to redirect_to(admin_categories_path) + expect(response).to redirect_to(admin_category_path(Category.last)) follow_redirect! diff --git a/spec/requests/admin/items_controller_spec.rb b/spec/requests/admin/items_controller_spec.rb index a3cc2e27..527105f7 100644 --- a/spec/requests/admin/items_controller_spec.rb +++ b/spec/requests/admin/items_controller_spec.rb @@ -72,12 +72,12 @@ let(:invalid_params) { { item: { name: '' } } } context 'when a category is' do - it 'not selected and redirects to the new category page' do + it 'not selected and redirects to the item page' do expect do post admin_items_path, params: valid_params_without_category end.to change(Item, :count).by(1) - expect(response).to redirect_to(new_admin_category_path) + expect(response).to redirect_to(admin_item_path(Item.last)) follow_redirect! @@ -86,12 +86,12 @@ expect(response.body).to include('Item was successfully created.') end - it 'selected and redirects to the edit category page' do + it 'selected and redirects to the item page' do expect do post admin_items_path(category_id: category.id), params: valid_params end.to change(Item, :count).by(1) - expect(response).to redirect_to(edit_admin_category_path(category)) + expect(response).to redirect_to(admin_item_path(Item.last)) follow_redirect! @@ -143,7 +143,7 @@ expect(item.is_fixed).to be true expect(item.is_open).to be true expect(item.is_selectable_options).to be true - expect(response).to redirect_to(edit_admin_category_path(item.category)) + expect(response).to redirect_to(admin_item_path(item)) end it 'does not create an item with invalid params' do