munificent-admin 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENCE +2 -0
- data/README.md +83 -0
- data/Rakefile +12 -0
- data/app/abilities/munificent/admin/ability.rb +51 -0
- data/app/abilities/munificent/admin/application_ability.rb +37 -0
- data/app/assets/config/munificent_admin_manifest.js +4 -0
- data/app/assets/javascripts/munificent_admin/application.js +6 -0
- data/app/assets/stylesheets/munificent/admin/application.scss +2 -0
- data/app/assets/stylesheets/munificent/admin/layout.scss +64 -0
- data/app/assets/stylesheets/munificent/admin/records.scss +7 -0
- data/app/controllers/munificent/admin/application_controller.rb +61 -0
- data/app/controllers/munificent/admin/bundle_tiers_controller.rb +12 -0
- data/app/controllers/munificent/admin/bundles_controller.rb +86 -0
- data/app/controllers/munificent/admin/charities_controller.rb +60 -0
- data/app/controllers/munificent/admin/dashboard_controller.rb +9 -0
- data/app/controllers/munificent/admin/donations_controller.rb +9 -0
- data/app/controllers/munificent/admin/donator_bundles_controller.rb +9 -0
- data/app/controllers/munificent/admin/donators_controller.rb +9 -0
- data/app/controllers/munificent/admin/fundraisers_controller.rb +68 -0
- data/app/controllers/munificent/admin/games_controller.rb +65 -0
- data/app/controllers/munificent/admin/otp_controller.rb +57 -0
- data/app/controllers/munificent/admin/user_sessions_controller.rb +44 -0
- data/app/controllers/munificent/admin/users_controller.rb +77 -0
- data/app/form_builders/munificent/admin/form_builder.rb +112 -0
- data/app/helpers/munificent/admin/application_helper.rb +6 -0
- data/app/helpers/munificent/admin/form_helper.rb +9 -0
- data/app/helpers/munificent/admin/layout_helper.rb +11 -0
- data/app/helpers/munificent/admin/state_machine_helper.rb +49 -0
- data/app/mailers/munificent/admin/application_mailer.rb +8 -0
- data/app/mailers/munificent/admin/panic_mailer.rb +19 -0
- data/app/models/concerns/authenticable.rb +9 -0
- data/app/models/munificent/admin/application_record.rb +7 -0
- data/app/models/munificent/admin/user.rb +54 -0
- data/app/models/munificent/admin/user_session.rb +19 -0
- data/app/presenters/munificent/admin/application_presenter.rb +29 -0
- data/app/presenters/munificent/admin/user_presenter.rb +24 -0
- data/app/presenters/munificent/bundle_presenter.rb +27 -0
- data/app/presenters/munificent/bundle_tier_presenter.rb +20 -0
- data/app/presenters/munificent/charity_presenter.rb +14 -0
- data/app/presenters/munificent/donation_presenter.rb +39 -0
- data/app/presenters/munificent/donator_bundle_presenter.rb +12 -0
- data/app/presenters/munificent/donator_presenter.rb +12 -0
- data/app/presenters/munificent/fundraiser_presenter.rb +41 -0
- data/app/presenters/munificent/game_presenter.rb +20 -0
- data/app/views/layouts/munificent/admin/application.html.erb +87 -0
- data/app/views/munificent/admin/bundles/_form.html.erb +15 -0
- data/app/views/munificent/admin/bundles/_tier_form.html.erb +13 -0
- data/app/views/munificent/admin/bundles/edit.html.erb +12 -0
- data/app/views/munificent/admin/bundles/index.html.erb +15 -0
- data/app/views/munificent/admin/bundles/new.html.erb +9 -0
- data/app/views/munificent/admin/bundles/show.html.erb +44 -0
- data/app/views/munificent/admin/charities/_form.html.erb +11 -0
- data/app/views/munificent/admin/charities/edit.html.erb +3 -0
- data/app/views/munificent/admin/charities/index.html.erb +11 -0
- data/app/views/munificent/admin/charities/new.html.erb +3 -0
- data/app/views/munificent/admin/charities/show.html.erb +23 -0
- data/app/views/munificent/admin/common/_record.html.erb +18 -0
- data/app/views/munificent/admin/common/_records.html.erb +38 -0
- data/app/views/munificent/admin/dashboard/index.html.erb +3 -0
- data/app/views/munificent/admin/donations/index.html.erb +15 -0
- data/app/views/munificent/admin/donator_bundles/index.html.erb +10 -0
- data/app/views/munificent/admin/donators/index.html.erb +11 -0
- data/app/views/munificent/admin/fundraisers/_form.html.erb +16 -0
- data/app/views/munificent/admin/fundraisers/edit.html.erb +3 -0
- data/app/views/munificent/admin/fundraisers/index.html.erb +17 -0
- data/app/views/munificent/admin/fundraisers/new.html.erb +3 -0
- data/app/views/munificent/admin/fundraisers/show.html.erb +41 -0
- data/app/views/munificent/admin/games/_form.html.erb +16 -0
- data/app/views/munificent/admin/games/csv_upload.html.erb +10 -0
- data/app/views/munificent/admin/games/edit.html.erb +3 -0
- data/app/views/munificent/admin/games/index.html.erb +15 -0
- data/app/views/munificent/admin/games/new.html.erb +3 -0
- data/app/views/munificent/admin/games/show.html.erb +25 -0
- data/app/views/munificent/admin/otp/_input_form.html.erb +6 -0
- data/app/views/munificent/admin/otp/input.html.erb +3 -0
- data/app/views/munificent/admin/otp/setup.html.erb +7 -0
- data/app/views/munificent/admin/panic_mailer/missing_key.html.erb +6 -0
- data/app/views/munificent/admin/panic_mailer/missing_key.text.erb +4 -0
- data/app/views/munificent/admin/user_sessions/new.html.erb +9 -0
- data/app/views/munificent/admin/users/_form.html.erb +29 -0
- data/app/views/munificent/admin/users/edit.html.erb +3 -0
- data/app/views/munificent/admin/users/index.html.erb +15 -0
- data/app/views/munificent/admin/users/new.html.erb +3 -0
- data/app/views/munificent/admin/users/show.html.erb +20 -0
- data/config/cucumber.yml +9 -0
- data/config/importmap.rb +4 -0
- data/config/initializers/inflections.rb +24 -0
- data/config/locales/en.yml +4 -0
- data/config/routes.rb +40 -0
- data/db/migrate/20220524130247_create_munificent_admin_user.rb +50 -0
- data/lib/munificent/admin/engine.rb +32 -0
- data/lib/munificent/admin/seeds.rb +19 -0
- data/lib/munificent/admin/version.rb +5 -0
- data/lib/munificent/admin.rb +7 -0
- data/lib/tasks/munificent/admin_tasks.rake +4 -0
- data/lib/tasks/munificent/cucumber.rake +75 -0
- data/lib/tasks/munificent/default.rake +32 -0
- metadata +354 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
require "rotp"
|
2
|
+
require "rqrcode"
|
3
|
+
|
4
|
+
module Munificent
|
5
|
+
module Admin
|
6
|
+
class OTPController < ApplicationController
|
7
|
+
skip_authorization_check
|
8
|
+
skip_before_action :enforce_2sv
|
9
|
+
|
10
|
+
before_action :require_setup, only: %i[input]
|
11
|
+
before_action :prevent_double_setup, only: %i[setup]
|
12
|
+
before_action :require_otp_issuer
|
13
|
+
|
14
|
+
def input; end
|
15
|
+
|
16
|
+
def setup
|
17
|
+
session[:otp_secret] = ROTP::Base32.random
|
18
|
+
totp = ROTP::TOTP.new(session[:otp_secret], issuer: ENV.fetch("OTP_ISSUER", nil))
|
19
|
+
@otp_url = totp.provisioning_uri(current_user.email_address)
|
20
|
+
@qr_code = RQRCode::QRCode.new(@otp_url).as_svg(standalone: false, module_size: 5)
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify
|
24
|
+
otp_secret = current_user.otp_secret || session[:otp_secret]
|
25
|
+
raise "Missing OTP secret" if otp_secret.blank?
|
26
|
+
|
27
|
+
totp = ROTP::TOTP.new(otp_secret, issuer: ENV.fetch("OTP_ISSUER", nil), after: current_user.last_otp_at)
|
28
|
+
|
29
|
+
if totp.verify(params[:otp_code], drift_behind: 3)
|
30
|
+
current_user.otp_secret ||= session[:otp_secret]
|
31
|
+
current_user.update(last_otp_at: (session[:last_otp_at] = Time.zone.now))
|
32
|
+
session[:otp_secret] = nil
|
33
|
+
|
34
|
+
redirect_to root_path
|
35
|
+
elsif current_user.has_2sv?
|
36
|
+
redirect_to otp_input_path
|
37
|
+
else
|
38
|
+
redirect_to otp_setup_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def require_setup
|
45
|
+
redirect_to otp_setup_path unless current_user&.has_2sv?
|
46
|
+
end
|
47
|
+
|
48
|
+
def prevent_double_setup
|
49
|
+
redirect_to otp_input_path if current_user&.has_2sv?
|
50
|
+
end
|
51
|
+
|
52
|
+
def require_otp_issuer
|
53
|
+
raise "Missing OTP issuer" if ENV["OTP_ISSUER"].blank?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class UserSessionsController < ApplicationController
|
4
|
+
skip_authorization_check
|
5
|
+
|
6
|
+
skip_before_action :require_login, only: %i[new create]
|
7
|
+
skip_before_action :enforce_2sv
|
8
|
+
|
9
|
+
def new
|
10
|
+
@user_session = UserSession.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
@user_session = UserSession.new(user_session_params.to_h)
|
15
|
+
|
16
|
+
if @user_session.save
|
17
|
+
redirect_to root_path
|
18
|
+
else
|
19
|
+
flash[:alert] = @user_session.errors.full_messages.join(", ")
|
20
|
+
redirect_to new_user_session_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy
|
25
|
+
current_user_session.destroy
|
26
|
+
|
27
|
+
redirect_to new_user_session_path
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def user_session_params
|
33
|
+
params.require(:user_session).permit(
|
34
|
+
*%i[
|
35
|
+
email_address
|
36
|
+
password
|
37
|
+
password_confirmation
|
38
|
+
remember_me
|
39
|
+
],
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class UsersController < ApplicationController
|
4
|
+
load_and_authorize_resource class: "Munificent::Admin::User"
|
5
|
+
|
6
|
+
def index
|
7
|
+
authorize!(:read, :user_accounts)
|
8
|
+
end
|
9
|
+
|
10
|
+
def show; end
|
11
|
+
def new; end
|
12
|
+
def edit; end
|
13
|
+
|
14
|
+
def create
|
15
|
+
if @user.save
|
16
|
+
flash[:notice] = "User created"
|
17
|
+
redirect_to edit_user_path(@user)
|
18
|
+
else
|
19
|
+
flash[:alert] = @user.errors.full_messages.join(", ")
|
20
|
+
redirect_to new_user_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def update
|
25
|
+
model = :user
|
26
|
+
if params[model][:password].blank?
|
27
|
+
%w[password password_confirmation].each { |p| params[model].delete(p) }
|
28
|
+
end
|
29
|
+
|
30
|
+
@user.assign_attributes(user_params)
|
31
|
+
|
32
|
+
if @user.save
|
33
|
+
flash[:notice] = "User saved"
|
34
|
+
else
|
35
|
+
flash[:alert] = @user.errors.full_messages.join(", ")
|
36
|
+
end
|
37
|
+
|
38
|
+
redirect_to edit_user_path(@user)
|
39
|
+
end
|
40
|
+
|
41
|
+
def destroy
|
42
|
+
@user.destroy
|
43
|
+
redirect_to users_path
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def user_params
|
49
|
+
params.require(:user).permit(
|
50
|
+
*%i[
|
51
|
+
active
|
52
|
+
approved
|
53
|
+
confirmed
|
54
|
+
data_entry
|
55
|
+
email_address
|
56
|
+
full_access
|
57
|
+
manages_users
|
58
|
+
name
|
59
|
+
password
|
60
|
+
password_confirmation
|
61
|
+
support
|
62
|
+
],
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def resource
|
67
|
+
@user
|
68
|
+
end
|
69
|
+
helper_method :resource
|
70
|
+
|
71
|
+
def presenter
|
72
|
+
@presenter ||= Munificent::Admin::ApplicationPresenter.present(@user)
|
73
|
+
end
|
74
|
+
helper_method :presenter
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
include ActionView::Context
|
6
|
+
|
7
|
+
TEXT_FIELDS = %i[
|
8
|
+
email_field
|
9
|
+
number_field
|
10
|
+
password_field
|
11
|
+
text_area
|
12
|
+
text_field
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
UNFLOATABLE_TYPES = %i[
|
16
|
+
select
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
def field(type, name, label: name.to_s.humanize, **opts)
|
20
|
+
field_tag = case type
|
21
|
+
when *TEXT_FIELDS
|
22
|
+
public_send(type, name, class: "form-control", placeholder: (label unless table_mode?))
|
23
|
+
when :select
|
24
|
+
select(name, opts[:source], select_field_opts(opts), select_html_opts(opts))
|
25
|
+
else
|
26
|
+
send(type, name, class: "form-control")
|
27
|
+
end
|
28
|
+
|
29
|
+
wrap(
|
30
|
+
label(name, label, class: "form-label"),
|
31
|
+
field_tag,
|
32
|
+
field_name: name,
|
33
|
+
float: UNFLOATABLE_TYPES.exclude?(type),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_box(name, label: name.to_s.humanize, **opts)
|
38
|
+
check_block = tag.div(class: "form-check") {
|
39
|
+
super(name, class: "form-check-input", **check_box_opts(opts)) +
|
40
|
+
label(name, label, class: "form-check-label")
|
41
|
+
}
|
42
|
+
|
43
|
+
wrap(check_block, field_name: name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def table(css_class: nil, &block)
|
47
|
+
@table_mode = true
|
48
|
+
contents = @template.capture(&block)
|
49
|
+
@table_mode = false
|
50
|
+
|
51
|
+
css_class = (%w[table table-bordered] + Array(css_class)).compact.join(" ")
|
52
|
+
tag.table(contents, class: css_class)
|
53
|
+
end
|
54
|
+
|
55
|
+
def money(name, default_currency: nil)
|
56
|
+
field(:select, "#{name}_currency",
|
57
|
+
label: "Currency",
|
58
|
+
source: Munificent::Currency.present_all,
|
59
|
+
selected: (object.public_send("#{name}_currency").presence || default_currency),
|
60
|
+
) + field(:text_field, "human_#{name}", label: name.to_s.humanize)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def wrap(*args, field_name: nil, float: true)
|
66
|
+
row_class = ["field-#{field_name}"]
|
67
|
+
|
68
|
+
args.reverse! if float
|
69
|
+
|
70
|
+
if table_mode?
|
71
|
+
tag.tr(class: row_class.compact.join(" ")) do
|
72
|
+
args.compact.map { |arg| tag.td(arg) }.reverse.reduce(:+)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
row_class << "mb-3"
|
76
|
+
row_class += %w[form-floating mb-3] if float
|
77
|
+
|
78
|
+
tag.div(class: row_class.compact.join(" ")) do
|
79
|
+
args.compact.reduce(:+)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def table_mode?
|
85
|
+
!!@table_mode
|
86
|
+
end
|
87
|
+
|
88
|
+
def select_field_opts(opts)
|
89
|
+
{ include_blank: opts[:optional] }.tap do |field_opts|
|
90
|
+
field_opts[:selected] = opts[:selected] if opts[:selected]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def select_html_opts(opts)
|
95
|
+
{ class: "form-select" }.tap do |html_opts|
|
96
|
+
if opts[:multiple]
|
97
|
+
html_opts.merge!(
|
98
|
+
multiple: true,
|
99
|
+
aria: { label: "multiple select" },
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def check_box_opts(opts)
|
106
|
+
{}.tap do |field_opts|
|
107
|
+
field_opts[:checked] = opts[:checked] unless opts[:checked].nil?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
module StateMachineHelper
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def aasm_buttons(resource, tag_name: "li")
|
7
|
+
resource.aasm.permitted_transitions.each do |transition|
|
8
|
+
event = transition.fetch(:event).to_s
|
9
|
+
|
10
|
+
content_for(:aasm_buttons) do
|
11
|
+
tag.public_send(tag_name) do
|
12
|
+
button_to(
|
13
|
+
event.humanize,
|
14
|
+
send("#{event}_#{resource.class.name.split('::').last.underscore}_path", resource),
|
15
|
+
method: :post,
|
16
|
+
data: { confirm: "Are you sure?" },
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
content_for(:aasm_buttons).presence
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_routes(router, resource_class)
|
26
|
+
router.send(:member) do
|
27
|
+
resource_class.aasm.events.each do |event|
|
28
|
+
router.send(:post, event.to_s)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def define_actions(controller, resource_class)
|
34
|
+
singular_name = resource_class.name.split("::").last.underscore
|
35
|
+
|
36
|
+
resource_class.aasm.events.each do |event|
|
37
|
+
controller.define_method(event.name) do
|
38
|
+
resource.public_send("#{event.name}!")
|
39
|
+
redirect_to(send("#{singular_name}_path", resource),
|
40
|
+
notice: "#{event.name.to_s.humanize} was successful",
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class PanicMailer < ApplicationMailer
|
4
|
+
# Subject can be set in your I18n file at config/locales/en.yml
|
5
|
+
# with the following lookup:
|
6
|
+
#
|
7
|
+
# en.panic_mailer.missing_key.subject
|
8
|
+
#
|
9
|
+
def missing_key(donator, game)
|
10
|
+
return if User.none?
|
11
|
+
|
12
|
+
@donator = donator
|
13
|
+
@game = game
|
14
|
+
|
15
|
+
mail to: User.pluck(:email_address)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class User < Munificent::ApplicationRecord
|
4
|
+
include Authenticable
|
5
|
+
|
6
|
+
attr_accessor :password_confirmation
|
7
|
+
attr_writer :require_password
|
8
|
+
|
9
|
+
def require_password?
|
10
|
+
!!@require_password
|
11
|
+
end
|
12
|
+
|
13
|
+
validates :password,
|
14
|
+
presence: true,
|
15
|
+
confirmation: true,
|
16
|
+
length: { minimum: 10 },
|
17
|
+
if: :require_password?
|
18
|
+
validates :email_address, presence: true
|
19
|
+
|
20
|
+
def has_2sv?
|
21
|
+
otp_secret.present?
|
22
|
+
end
|
23
|
+
|
24
|
+
def permissions
|
25
|
+
if full_access?
|
26
|
+
["full access"]
|
27
|
+
else
|
28
|
+
[].tap do |perms|
|
29
|
+
perms << "data entry" if data_entry?
|
30
|
+
perms << "manages users" if manages_users?
|
31
|
+
perms << "support" if support?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def states
|
37
|
+
[].tap do |states|
|
38
|
+
states << "active" if active?
|
39
|
+
states << "approved" if approved?
|
40
|
+
states << "confirmed" if confirmed?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# We may use these later, but for now default them to `true`.
|
45
|
+
def active? = true
|
46
|
+
def approved? = true
|
47
|
+
def confirmed? = true
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
name.presence || email_address
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class UserSession < Authlogic::Session::Base
|
4
|
+
login_field :email_address
|
5
|
+
record_selection_method :find_by_email_address
|
6
|
+
|
7
|
+
# This is a fairly restrictive configuration
|
8
|
+
# because this is an admin application.
|
9
|
+
consecutive_failed_logins_limit 5
|
10
|
+
encrypt_cookie true
|
11
|
+
generalize_credentials_error_messages true
|
12
|
+
httponly true
|
13
|
+
logout_on_timeout true
|
14
|
+
remember_me_for 20.hours
|
15
|
+
same_site "Strict"
|
16
|
+
single_access_allowed_request_types []
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class ApplicationPresenter < ActionView::Base
|
4
|
+
def self.delegate(array, to: :record)
|
5
|
+
super(*array, to:)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.present(record)
|
9
|
+
presenter_class_for(record).new(record)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.presenter_class_for(record)
|
13
|
+
"#{record.class.name}Presenter".constantize
|
14
|
+
rescue NameError
|
15
|
+
raise ArgumentError, "No presenter available for record type `#{record.class.name}`"
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :record
|
19
|
+
|
20
|
+
def initialize(record) # rubocop:disable Lint/MissingSuper
|
21
|
+
@record = record
|
22
|
+
end
|
23
|
+
|
24
|
+
def name
|
25
|
+
record
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Munificent
|
2
|
+
module Admin
|
3
|
+
class UserPresenter < ApplicationPresenter
|
4
|
+
delegate(
|
5
|
+
%i[
|
6
|
+
id
|
7
|
+
email_address
|
8
|
+
current_login_at
|
9
|
+
login_count
|
10
|
+
permissions
|
11
|
+
states
|
12
|
+
],
|
13
|
+
)
|
14
|
+
|
15
|
+
def permissions
|
16
|
+
record.permissions.to_sentence.capitalize
|
17
|
+
end
|
18
|
+
|
19
|
+
def states
|
20
|
+
record.states.to_sentence.capitalize
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Munificent
|
2
|
+
class BundlePresenter < Admin::ApplicationPresenter
|
3
|
+
delegate(
|
4
|
+
%i[
|
5
|
+
id
|
6
|
+
fundraiser
|
7
|
+
],
|
8
|
+
)
|
9
|
+
|
10
|
+
delegate(
|
11
|
+
%i[
|
12
|
+
ends_at
|
13
|
+
starts_at
|
14
|
+
], to: :"record.highest_tier",
|
15
|
+
)
|
16
|
+
|
17
|
+
def price
|
18
|
+
record.highest_tier&.human_price(symbol: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
def state
|
22
|
+
tag.span(class: "badge text-bg-#{record.live? ? 'success' : 'secondary'}") do
|
23
|
+
record.state.humanize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Munificent
|
2
|
+
class BundleTierPresenter < Admin::ApplicationPresenter
|
3
|
+
delegate(
|
4
|
+
%i[
|
5
|
+
id
|
6
|
+
ends_at
|
7
|
+
fundraiser
|
8
|
+
starts_at
|
9
|
+
],
|
10
|
+
)
|
11
|
+
|
12
|
+
def price
|
13
|
+
record.human_price(symbol: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def games
|
17
|
+
record.bundle_tier_games.map(&:game).map(&:name).join(", ")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Munificent
|
2
|
+
class DonationPresenter < Admin::ApplicationPresenter
|
3
|
+
delegate(
|
4
|
+
%i[
|
5
|
+
id
|
6
|
+
amount
|
7
|
+
created_at
|
8
|
+
curated_streamer
|
9
|
+
donated_by
|
10
|
+
donator_name
|
11
|
+
fundraiser
|
12
|
+
message
|
13
|
+
],
|
14
|
+
)
|
15
|
+
|
16
|
+
def state
|
17
|
+
tag.span(class: "badge text-bg-#{badge_type}") do
|
18
|
+
record.state.humanize
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def badge_type
|
25
|
+
case record.state
|
26
|
+
when "pending"
|
27
|
+
"secondary"
|
28
|
+
when "paid"
|
29
|
+
"info"
|
30
|
+
when "cancelled"
|
31
|
+
"danger"
|
32
|
+
when "fulfilled"
|
33
|
+
"success"
|
34
|
+
else
|
35
|
+
"light"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|