diff --git a/app/controllers/card_grants_controller.rb b/app/controllers/card_grants_controller.rb index 0bf2cdcabf..fa4ce9af7d 100644 --- a/app/controllers/card_grants_controller.rb +++ b/app/controllers/card_grants_controller.rb @@ -58,6 +58,31 @@ def update redirect_to card_grant_url(@card_grant) end + def set_index + card_grant = CardGrant.find(params[:id]) + authorize card_grant + + index = params[:index] + + # get all the card grants as an array + card_grants = StaticPageService::Index.new(current_user:).card_grants.not_hidden.to_a + + return head status: :bad_request if index < 0 || index >= card_grants.size + + # switch the position *in the in-memory array* + card_grants.delete card_grant + card_grants.insert index, card_grant + + # persist the sort order + ActiveRecord::Base.transaction do + card_grants.each_with_index do |cg, idx| + cg.update(sort_index: idx) + end + end + + render json: card_grants.pluck(:id) + end + def clear_purpose authorize @card_grant, :update? @card_grant.update(purpose: nil) diff --git a/app/javascript/controllers/sort_card_controller.js b/app/javascript/controllers/sort_card_controller.js new file mode 100644 index 0000000000..9a8d96ad17 --- /dev/null +++ b/app/javascript/controllers/sort_card_controller.js @@ -0,0 +1,30 @@ +import { Controller } from '@hotwired/stimulus' +import csrf from '../common/csrf' + +export default class extends Controller { + static values = { + order: Array, + } + + async sort({ detail: { oldIndex, newIndex } }) { + if (oldIndex == newIndex) return + + const copy = this.orderValue + + const id = copy[oldIndex] + + copy.splice(oldIndex, 1) + copy.splice(newIndex, 0, id) + + this.orderValue = copy + + await fetch(`/grants/${id}/set_index`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': csrf(), + }, + body: JSON.stringify({ index: newIndex }), + }) + } +} diff --git a/app/models/card_grant.rb b/app/models/card_grant.rb index fea5237231..03d31cf086 100644 --- a/app/models/card_grant.rb +++ b/app/models/card_grant.rb @@ -11,6 +11,7 @@ # keyword_lock :string # merchant_lock :string # purpose :string +# sort_index :float # status :integer default("active"), not null # created_at :datetime not null # updated_at :datetime not null diff --git a/app/views/static_pages/index.html.erb b/app/views/static_pages/index.html.erb index 0117a50bb4..bc47ff4a00 100644 --- a/app/views/static_pages/index.html.erb +++ b/app/views/static_pages/index.html.erb @@ -32,9 +32,17 @@ <% if current_user.card_grants.activated.size > 0 %>

Grants

-
+
<% current_user.card_grants.includes(:stripe_card).activated.each do |grant| %> - <%= render grant.stripe_card, headless: true, show_purpose: true, href: card_grant_path(grant) %> +
+ <%= render grant.stripe_card, headless: true, show_purpose: true, href: card_grant_path(grant) %> +
<% end %>
<% end %> diff --git a/config/routes.rb b/config/routes.rb index 2a2bced6c2..98e6f3a1c5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -655,6 +655,7 @@ post "activate" get "spending" post "clear_purpose" + post "set_index" end end diff --git a/db/migrate/20250621234202_add_sort_index_to_card_grants.rb b/db/migrate/20250621234202_add_sort_index_to_card_grants.rb new file mode 100644 index 0000000000..e138c79464 --- /dev/null +++ b/db/migrate/20250621234202_add_sort_index_to_card_grants.rb @@ -0,0 +1,5 @@ +class AddSortIndexToCardGrants < ActiveRecord::Migration[7.2] + def change + add_column :card_grants, :sort_index, :float + end +end diff --git a/db/schema.rb b/db/schema.rb index 460ab35d19..00efea4460 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_06_19_045020) do +ActiveRecord::Schema[7.2].define(version: 2025_06_21_234202) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" @@ -432,6 +432,7 @@ t.string "keyword_lock" t.string "purpose" t.boolean "one_time_use" + t.float "sort_index" t.index ["disbursement_id"], name: "index_card_grants_on_disbursement_id" t.index ["event_id"], name: "index_card_grants_on_event_id" t.index ["sent_by_id"], name: "index_card_grants_on_sent_by_id"