Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/controllers/admin/shop_orders_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,31 @@ def assign_user
redirect_to admin_shop_orders_path(view: "fulfillment"), alert: "Failed to assign order"
end
end

def cancel_hcb_grant
authorize :admin, :manage_users?
@order = ShopOrder.find(params[:id])

unless @order.shop_card_grant.present?
redirect_to admin_shop_order_path(@order), alert: "This order has no HCB grant to cancel"
return
end

grant = @order.shop_card_grant
begin
HCBService.cancel_card_grant!(hashid: grant.hcb_grant_hashid)

PaperTrail::Version.create!(
item_type: "ShopOrder",
item_id: @order.id,
event: "hcb_grant_canceled",
whodunnit: current_user.id,
object_changes: { hcb_grant_hashid: grant.hcb_grant_hashid, canceled_by: current_user.display_name }.to_json
)

redirect_to admin_shop_order_path(@order), notice: "HCB grant canceled successfully"
rescue => e
redirect_to admin_shop_order_path(@order), alert: "Failed to cancel HCB grant: #{e.message}"
end
end
end
38 changes: 38 additions & 0 deletions app/controllers/admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,44 @@ def update
redirect_to admin_user_path(@user)
end

def cancel_all_hcb_grants
authorize :admin, :manage_users?
@user = User.find(params[:id])

grants = @user.shop_card_grants.where.not(hcb_grant_hashid: nil)

if grants.empty?
redirect_to admin_user_path(@user), alert: "This user has no HCB grants to cancel"
return
end

canceled_count = 0
errors = []

grants.find_each do |grant|
begin
HCBService.cancel_card_grant!(hashid: grant.hcb_grant_hashid)
canceled_count += 1
rescue => e
errors << "Grant #{grant.hcb_grant_hashid}: #{e.message}"
end
end

PaperTrail::Version.create!(
item_type: "User",
item_id: @user.id,
event: "all_hcb_grants_canceled",
whodunnit: current_user.id,
object_changes: { canceled_count: canceled_count, canceled_by: current_user.display_name }.to_json
)

if errors.any?
redirect_to admin_user_path(@user), alert: "Canceled #{canceled_count} grants, but #{errors.count} failed: #{errors.first}"
else
redirect_to admin_user_path(@user), notice: "Successfully canceled #{canceled_count} HCB grant(s)"
end
end

private

def user_params
Expand Down
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class User < ApplicationRecord
has_many :projects, through: :memberships
has_many :hackatime_projects, class_name: "User::HackatimeProject", dependent: :destroy
has_many :shop_orders, dependent: :destroy
has_many :shop_card_grants, dependent: :destroy
has_many :votes, dependent: :destroy
has_many :reports, class_name: "Project::Report", foreign_key: :reporter_id, dependent: :destroy
has_many :likes, dependent: :destroy
Expand Down
12 changes: 12 additions & 0 deletions app/views/admin/shop_orders/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,18 @@
<h2>Card Grant</h2>
<div class="info-rows">
<div><b>Card Grant ID:</b> <%= @order.shop_card_grant_id %></div>
<% if @order.shop_card_grant&.hcb_grant_hashid.present? %>
<div><b>HCB Grant ID:</b> <%= @order.shop_card_grant.hcb_grant_hashid %></div>
<div>
<%= link_to "View on HCB", @order.shop_card_grant.hcb_url, target: "_blank", class: "btn-primary", style: "margin-right: 0.5rem;" %>
<% if current_user.admin? && @order.fulfilled? %>
<form method="post" action="<%= cancel_hcb_grant_admin_shop_order_path(@order) %>" style="display: inline;">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<button type="submit" class="btn-danger" onclick="return confirm('Are you sure you want to cancel this HCB grant? This cannot be undone.')">Cancel HCB Grant</button>
</form>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
Expand Down
19 changes: 19 additions & 0 deletions app/views/admin/users/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@
<% end %>
<% end %>

<% if current_user.admin? %>
<% has_grants = @user.shop_card_grants.where.not(hcb_grant_hashid: nil).exists? %>
<button type="button" onclick="document.getElementById('cancel-grants-modal').showModal()" class="btn-danger slim" <%= 'disabled style="opacity: 0.5; cursor: not-allowed;"'.html_safe unless has_grants %>>
🚫 Cancel All HCB Grants
</button>
<% if has_grants %>
<%= render layout: "shared/modal", locals: { id: "cancel-grants-modal", title: "Cancel All HCB Grants", description: "This will cancel ALL HCB grants for #{@user.display_name}. This action cannot be undone!" } do %>
<form action="<%= cancel_all_hcb_grants_admin_user_path(@user) %>" method="post">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<p style="color: #ef4444; font-weight: bold;">⚠️ Warning: This will cancel <%= @user.shop_card_grants.where.not(hcb_grant_hashid: nil).count %> grant(s).</p>
<div class="modal__actions">
<button type="submit" class="btn-danger slim">Confirm Cancel All Grants</button>
<button type="button" onclick="document.getElementById('cancel-grants-modal').close()" class="btn-secondary slim">Cancel</button>
</div>
</form>
<% end %>
<% end %>
<% end %>

<% if policy(:admin).ban_users? %>
<% if @user.banned? %>
<%= button_to "🔓 Unban User", unban_admin_user_path(@user), method: :post, class: "btn-primary slim" %>
Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def self.matches?(request)
post :adjust_balance
post :ban
post :unban
post :cancel_all_hcb_grants
post :impersonate
end
collection do
Expand Down Expand Up @@ -216,6 +217,7 @@ def self.matches?(request)
post :mark_fulfilled
post :update_internal_notes
post :assign_user
post :cancel_hcb_grant
end
end
resources :audit_logs, only: [ :index, :show ]
Expand Down
Loading