Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fed1e22
add admin ui
manuthecoder Jul 16, 2026
9c0333d
Merge remote-tracking branch 'origin/main' into mxg-add-admin-ui-for-…
manuthecoder Jul 16, 2026
70fcbf2
fix bugs
manuthecoder Jul 16, 2026
c8dafed
fix
manuthecoder Jul 16, 2026
f81a3c4
undo
manuthecoder Jul 16, 2026
051a3e1
undo
manuthecoder Jul 16, 2026
949a9e4
remove comments
manuthecoder Jul 16, 2026
c5ae0a9
Update payroll_positions_controller.rb
manuthecoder Jul 16, 2026
e206f2e
remove unrelated changes
manuthecoder Jul 17, 2026
314dd10
fix search
manuthecoder Jul 17, 2026
fdabd03
Update index.html.erb
manuthecoder Jul 17, 2026
e0d8c20
Potential fix for pull request finding
manuthecoder Jul 17, 2026
82ab3ca
Potential fix for pull request finding
manuthecoder Jul 17, 2026
9d44b1e
Potential fix for pull request finding
manuthecoder Jul 17, 2026
3f36a45
Potential fix for pull request finding
manuthecoder Jul 17, 2026
c4533a1
Potential fix for pull request finding
manuthecoder Jul 17, 2026
c9ed0d3
Update nav.rb
manuthecoder Jul 17, 2026
6af5ab0
Merge remote-tracking branch 'origin/main' into mxg-add-admin-ui-for-…
manuthecoder Jul 17, 2026
74e9c14
improve code
manuthecoder Jul 17, 2026
0b5b604
Update show.html.erb
manuthecoder Jul 17, 2026
db813cd
Merge branch 'main' into mxg-add-admin-ui-for-contractors
manuthecoder Jul 21, 2026
ed75523
Update legal_entities_controller.rb
manuthecoder Jul 21, 2026
25ef899
Merge branch 'main' into mxg-add-admin-ui-for-contractors
polypixeldev Jul 22, 2026
14e4ff4
Rename contractor payments -> payments
polypixeldev Jul 22, 2026
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
29 changes: 29 additions & 0 deletions app/controllers/admin/legal_entities_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module Admin
class LegalEntitiesController < Admin::BaseController
def index
@page = params[:page] || 1
@per = params[:per] || 20

relation = LegalEntity.includes(:users, :managing_event, :latest_tax_form)

@q = params[:q].presence
if @q
relation = relation.left_joins(:users).where("legal_entities.name ILIKE :q OR users.full_name ILIKE :q OR users.email ILIKE :q", q: "%#{LegalEntity.sanitize_sql_like(@q)}%").distinct
end

@entity_type = params[:entity_type].presence
relation = relation.where(entity_type: @entity_type) if @entity_type

@managed = params[:managed] == "1"
relation = relation.managed if @managed

@archived = params[:archived] == "1"
relation = relation.where.not(archived_at: nil) if @archived

@legal_entities = relation.order(created_at: :desc).page(@page).per(@per)
end
Comment on lines +1 to +26

end
end
21 changes: 21 additions & 0 deletions app/controllers/admin/payments_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Admin
class PaymentsController < Admin::BaseController
def index
@page = params[:page] || 1
@per = params[:per] || 20

relation = Payment.includes(:payee, :event)

@q = params[:q].presence
relation = relation.search_recipient(@q) if @q

@state = params[:state].presence
relation = relation.where(aasm_state: @state) if @state

@payments = relation.order(created_at: :desc).page(@page).per(@per)
end
Comment on lines +1 to +18

end
end
29 changes: 29 additions & 0 deletions app/controllers/admin/payroll_positions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module Admin
class PayrollPositionsController < Admin::BaseController
def index
@page = params[:page] || 1
@per = params[:per] || 20

relation = Payroll::Position.includes(:payee, :event)

@q = params[:q].presence
relation = relation.search_recipient(@q) if @q

@state = params[:state].presence
relation = relation.where(aasm_state: @state) if @state

@positions = relation.order(Arel.sql("CASE WHEN aasm_state = 'under_review' THEN 0 ELSE 1 END, created_at DESC")).page(@page).per(@per)
end

def reject
position = Payroll::Position.find(params[:id])
position.mark_rejected!
redirect_back fallback_location: admin_payroll_positions_path, flash: { success: "Contractor rejected." }
rescue AASM::InvalidTransition
redirect_back fallback_location: admin_payroll_positions_path, flash: { error: "This contractor can no longer be rejected." }
end
Comment on lines +17 to +26

end
end
26 changes: 26 additions & 0 deletions app/controllers/admin/tax_forms_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module Admin
class TaxFormsController < Admin::BaseController
def index
@page = params[:page] || 1
@per = params[:per] || 20

relation = Tax::Form.includes(:legal_entity)

@q = params[:q].presence
if @q
relation = relation.left_joins(legal_entity: :users).where("legal_entities.name ILIKE :q OR users.full_name ILIKE :q OR users.email ILIKE :q", q: "%#{Tax::Form.sanitize_sql_like(@q)}%").distinct
end

@state = params[:state].presence
relation = relation.where(aasm_state: @state) if @state

@form_type = params[:form_type].presence
relation = relation.where(form_type: @form_type) if @form_type

@tax_forms = relation.order(created_at: :desc).page(@page).per(@per)
end
Comment on lines +1 to +23

end
end
24 changes: 15 additions & 9 deletions app/models/admin/nav.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ def spending
path: reimbursements_admin_index_path,
count: ->{ Reimbursement::Report.reimbursement_requested.count },
count_type: :tasks
),
make_item(
name: "Payments",
path: admin_payments_path,
count: ->{ Payment.under_review.count },
count_type: :tasks
)
Comment thread
polypixeldev marked this conversation as resolved.
]
)
Expand Down Expand Up @@ -302,21 +308,21 @@ def payroll
name: "Payroll",
items: [
make_item(
name: "Employees",
path: employees_admin_index_path,
count: ->{ Employee.onboarding.count },
name: "Contractors",
path: admin_payroll_positions_path,
count: ->{ Payroll::Position.under_review.count },
count_type: :tasks
),
Comment thread
polypixeldev marked this conversation as resolved.
make_item(
name: "Payments",
path: employee_payments_admin_index_path,
count: ->{ Employee::Payment.paid.count },
name: "Legal Entities",
path: admin_legal_entities_path,
count: ->{ LegalEntity.count },
count_type: :records
),
make_item(
name: "W9s",
path: admin_w9s_path,
count: ->{ W9.count },
name: "Tax Forms",
path: admin_tax_forms_path,
count: ->{ Tax::Form.count },
count_type: :records
)
]
Expand Down
69 changes: 69 additions & 0 deletions app/views/admin/legal_entities/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<% title "Legal Entities" %>

<%= form_with local: true, class: "card", url: admin_legal_entities_path, method: :get do |form| %>
<div class="flex flex-col sm:flex-row items-center gap-2">
<%= render "events/filters/search", form: %>
<%= form.select :entity_type, [%w[Person person], %w[Business business]], { include_blank: "All types", selected: @entity_type } %>
<label class="flex items-center gap-1 nowrap">
<%= form.check_box :managed, checked: @managed %> Managed
</label>
<label class="flex items-center gap-1 nowrap">
<%= form.check_box :archived, checked: @archived %> Archived
</label>
</div>
<div class="flex items-center mt-2">
<%= form.submit "Search", class: "ml-auto" %>
</div>
<% end %>

<div class="flex items-center mb2 flex-col sm:flex-row">
<div class="flex-grow">
<%= page_entries_info @legal_entities, entry_name: "legal entities" %>
</div>
<%= paginate @legal_entities %>
</div>

<table>
<thead>
<tr>
<th class="w-32">Date</th>
<th>Name</th>
<th>Type</th>
<th>Users / Managing org</th>
<th class="w-32">TIN</th>
<th>Payable?</th>
<th class="w-24">Actions</th>
</tr>
</thead>
<tbody>
<% @legal_entities.each do |legal_entity| %>
<tr>
<td><%= legal_entity.created_at.strftime("%Y-%m-%d") %></td>
<td>
<%= legal_entity.name || legal_entity.display_name %>
<% if legal_entity.archived? %><span class="badge bg-muted">Archived</span><% end %>
<% if legal_entity.tin_banned? %><span class="badge bg-error">Banned</span><% end %>
</td>
<td><%= legal_entity.entity_type&.humanize %></td>
<td>
<% if legal_entity.managed? %>
Managed by <%= link_to legal_entity.managing_event.name, legal_entity.managing_event %>
<% else %>
<%= legal_entity.users.map(&:email).to_sentence.presence || "—" %>
<% end %>
</td>
<td><%= legal_entity.tin_hash.present? ? "On file" : "—" %></td>
<td>
<% if legal_entity.payable? %>
<span class="badge ml-0 bg-success">Yes</span>
<% else %>
<span class="badge ml-0 bg-warning">No</span>
<% end %>
Comment on lines +56 to +61
</td>
<td><%= link_to "View", legal_entity_path(legal_entity) %></td>
</tr>
<% end %>
</tbody>
</table>

<%= paginate @legal_entities %>
50 changes: 50 additions & 0 deletions app/views/admin/payments/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<% title "Payments" %>

<%= form_with local: true, class: "card", url: admin_payments_path, method: :get do |form| %>
<div class="flex flex-col sm:flex-row items-center gap-2">
<%= render "events/filters/search", form: %>
<%= form.select :state, Payment.aasm.states_for_select, { include_blank: "All states", selected: @state } %>
</div>
<div class="flex items-center mt-2">
<%= form.submit "Search", class: "ml-auto" %>
</div>
<% end %>

<div class="flex items-center mb2 flex-col sm:flex-row">
<div class="flex-grow">
<%= page_entries_info @payments, entry_name: "payments" %>
</div>
<%= paginate @payments %>
</div>

<table>
<thead>
<tr>
<th class="w-32">Date</th>
<th>Organization</th>
<th>Recipient</th>
<th>Purpose</th>
<th class="w-32">Amount</th>
<th>Status</th>
<th class="w-24">Actions</th>
</tr>
</thead>
<tbody>
<% @payments.each do |payment| %>
<tr class="<%= "admin-bg-pending" if payment.under_review? %>">
<td><%= payment.created_at.strftime("%Y-%m-%d") %></td>
<td><%= link_to payment.event.name, payment.event %></td>
<td>
<%= payment.payee.display_name %>
<div class="muted h5"><%= payment.payee.email %></div>
</td>
<td><%= payment.purpose %></td>
<td><%= payment.amount.format %></td>
<td><span class="badge ml-0 bg-<%= payment.state_color %>"><%= payment.state_text %></span></td>
<td><%= link_to "View", payment_path(payment) %></td>
</tr>
<% end %>
</tbody>
</table>

<%= paginate @payments %>
54 changes: 54 additions & 0 deletions app/views/admin/payroll_positions/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<% title "Contractors" %>

<%= form_with local: true, class: "card", url: admin_payroll_positions_path, method: :get do |form| %>
<div class="flex flex-col sm:flex-row items-center gap-2">
<%= render "events/filters/search", form: %>
<%= form.select :state, Payroll::Position.aasm.states_for_select, { include_blank: "All states", selected: @state } %>
</div>
<div class="flex items-center mt-2">
<%= form.submit "Search", class: "ml-auto" %>
</div>
<% end %>

<div class="flex items-center mb2 flex-col sm:flex-row">
<div class="flex-grow">
<%= page_entries_info @positions, entry_name: "contractors" %>
</div>
<%= paginate @positions %>
</div>

<table>
<thead>
<tr>
<th class="w-32">Date</th>
<th>Organization</th>
<th>Contractor</th>
<th>Title</th>
<th class="w-32">Rate</th>
<th class="w-32">Period</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% @positions.each do |position| %>
<tr class="<%= "admin-bg-pending" if position.under_review? %>">
<td><%= position.created_at.strftime("%Y-%m-%d") %></td>
<td><%= link_to position.event.name, position.event %></td>
<td>
<%= position.payee.display_name %>
<div class="muted h5"><%= position.payee.email %></div>
</td>
<td><%= position.title %></td>
<td><%= position.rate.format %></td>
<td><%= position.period_label %></td>
<td><span class="badge ml-0 bg-<%= position.under_review? ? "warning" : position.status_color %>"><%= position.under_review? ? "Under review" : position.status_text %></span></td>
<td>
<%= link_to position.under_review? ? "Review" : "View", event_payroll_position_path(event_id: position.event.slug, id: position), data: { turbo: false } %>
</td>
</tr>
<% end %>
</tbody>
</table>

<%= paginate @positions %>
52 changes: 52 additions & 0 deletions app/views/admin/tax_forms/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<% title "Tax Forms" %>

<%= form_with local: true, class: "card", url: admin_tax_forms_path, method: :get do |form| %>
<div class="flex flex-col sm:flex-row items-center gap-2">
<%= render "events/filters/search", form: %>
<%= form.select :state, Tax::Form.aasm.states_for_select, { include_blank: "All states", selected: @state } %>
<%= form.select :form_type, Tax::Form.form_types.keys, { include_blank: "All form types", selected: @form_type } %>
</div>
<div class="flex items-center mt-2">
<%= form.submit "Search", class: "ml-auto" %>
</div>
<% end %>

<div class="flex items-center mb2 flex-col sm:flex-row">
<div class="flex-grow">
<%= page_entries_info @tax_forms, entry_name: "tax forms" %>
</div>
<%= paginate @tax_forms %>
</div>

<table>
<thead>
<tr>
<th class="w-32">Date</th>
<th>ID</th>
<th>Legal entity</th>
<th>Form</th>
<th>Service</th>
<th>Status</th>
<th>TaxBandits status</th>
<th>TIN match</th>
<th class="w-32">Completed</th>
</tr>
</thead>
<tbody>
<% @tax_forms.each do |form| %>
<tr>
<td><%= form.created_at.strftime("%Y-%m-%d") %></td>
<td><code><%= form.hashid %></code></td>
<td><%= link_to form.legal_entity.name || form.legal_entity.display_name, legal_entity_path(form.legal_entity) %></td>
<td><%= form.form_type || "—" %></td>
<td><%= form.external_service.humanize %></td>
<td><%= form.aasm_state.humanize %></td>
<td><%= form.taxbandits_status&.humanize || "—" %></td>
<td><%= form.taxbandits_tin_matching_status&.humanize || "—" %></td>
<td><%= form.completed_at&.strftime("%Y-%m-%d") || "—" %></td>
</tr>
<% end %>
</tbody>
</table>

<%= paginate @tax_forms %>
Loading