Move most fundraising files into a Fundraising module

Mostly this is just me testing out what it would look like to
modularize the app more… I've noticed that some concerns, like
fundraising, are just not relevant to most of the app, and being able
to lock them away inside subfolders feels like it'll help tidy up
long folder lists.

Notably, I haven't touched the models case yet, because I worry that
might be a bit more complex, whereas everything else seems pretty
well-isolated? We'll try it out!
This commit is contained in:
Emi Matchu 2024-02-18 20:12:14 -08:00
parent 93bc300940
commit 82be7fe301
28 changed files with 417 additions and 401 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,2 +0,0 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View file

@ -8,11 +8,9 @@
@import partials/jquery.jgrowl
@import alt_styles/index
@import campaigns/show
@import closet_hangers/index
@import closet_hangers/petpage
@import closet_lists/form
@import donations/show
@import neopets_page_import_tasks/new
@import contributions/index
@import items

View file

@ -1,227 +0,0 @@
@import "partials/clean/constants"
@import "partials/campaign-progress"
@import "partials/outfit"
/* TODO: redundant with outfits/index; why is it not in the partial? */
$outfit-inner-height: 150px
$outfit-inner-width: 150px
$outfit-banner-h-padding: 4px
$outfit-banner-v-padding: 2px
$outfit-banner-inner-width: $outfit-inner-width - (2 * $outfit-banner-h-padding)
body.campaigns-show, body.campaigns-current
+campaign-progress
color: $campaign-text-color
a
color: $campaign-text-color + #222 !important
#home-link:hover
background-color: $campaign-background-color
#userbar, #footer
color: $text-color
a
color: $link-color
#home-link
color: $link-color
#title
display: none
#donation-form
+module
background: $campaign-background-color
border-color: $campaign-border-color
display: flex
flex-direction: row
margin-top: 1em
margin-bottom: 1.5em
padding-bottom: 32px
padding-left: 24px
padding-right: 24px
padding-top: 32px
position: relative
// We ignore the theme attribute on campaigns now, and just do purple.
&::after
background:
image: url(image_path("campaigns/purple.png"))
repeat: no-repeat
bottom: 0
content: " "
height: 123px
position: absolute
right: 4px
width: 150px
header, #donation-fields
flex: 1
#donation-form-title
font-size: 125%
font-weight: bold
margin-bottom: .25em
margin-top: 0
p
font-family: $main-font
font-size: 85%
margin-bottom: 0
margin-top: .5em
#donation-fields
margin-left: 20px
padding-top: 7px
#amount-header
font-size: 85%
font-weight: bold
line-height: 1
#amount-choices
$amount-choices-border-color: desaturate(lighten($campaign-border-color, 30%), 30%)
display: flex
flex-direction: row
margin-bottom: .75em
margin-top: .5em
li
border: 1px solid $amount-choices-border-color
border-radius: 5px
display: block
flex: 1
list-style: none
overflow: hidden
text-align: center
&:not(:last-of-type)
margin-right: .75em
input[type=radio]
height: 0
margin: 0
padding: 0
opacity: 0
position: absolute
width: 0
label
border: 1px solid transparent
box-sizing: border-box
display: block
padding: .5em .5em
width: 100%
input[type=radio]:checked ~ label
background: lighten($amount-choices-border-color, 15%)
font-weight: bold
input[type=radio]:focus ~ label
border-color: white
border-radius: 5px
#amount-custom-fields
display: none
input[type=text]
font-family: inherit
font-size: inherit
line-height: 1
padding: 0
text-align: center
#amount-custom:checked ~ #amount-custom-fields
display: block
#amount-custom:checked ~ label[for=amount-custom]
display: none
input[type=text]
border-color: #cce
color: $campaign-text-color
width: 3em
#donation-controls
button
+awesome-button-color(#004)
font-size: 120%
#campaign-text[data-campaign-complete]
#description
display: none
&[data-show]
display: block
#success-thanks
border: 1px dashed $module-border-color
margin-bottom: 1em
padding: 1em
position: relative
p:last-child
margin-bottom: 0
#success-thanks-toggle-description
position: absolute
bottom: 1em
font-style: italic
right: 1em
#outfits
+outfits-list
text-align: center
> li
+outfit
height: $outfit-inner-height
margin: 2px
width: $outfit-inner-width
header, footer
font-size: 85%
padding: $outfit-banner-v-padding $outfit-banner-h-padding
width: $outfit-banner-inner-width
img
height: $outfit-inner-height
width: $outfit-inner-width
&.banner
background-image: url(https://images.neopets.com/themes/004_bir_a2e60/footer_bg.png)
background-position: 0 -60px
border: 2px solid #006
color: white
height: 100px
line-height: 100px
margin: 4px 0
text-shadow: #335 2px 2px 1px
width: 800px - 4px
span
+inline-block
font-size: 32px
font-weight: bold
line-height: 1.5
vertical-align: middle
#last-years-donors
font-weight: bold
margin-top: 1em
text-align: center
#outfits-header > *
display: inline-block
#all-campaigns-list
li
display: inline-block
list-style: none
margin-left: 1em
#fine-print
font-size: 85%
margin-top: 2em

View file

@ -1,29 +0,0 @@
@import "../partials/clean/constants"
@import "../partials/clean/mixins"
body.donations-show
#thank-you
border: 3px solid $module-border-color
display: block
margin: 0 auto 1em
#edit-donation
ul
list-style: none
label
+inline-block
width: 16em
.name input[type=text]
width: 10em
.feature input[type=text]
width: 23em
.choose-outfit
font-size: 85%
margin-left: 1em
select
width: 10em

View file

@ -0,0 +1,227 @@
@import "../../partials/clean/constants"
@import "../../partials/campaign-progress"
@import "../../partials/outfit"
/* TODO: redundant with outfits/index; why is it not in the partial? */
$outfit-inner-height: 150px
$outfit-inner-width: 150px
$outfit-banner-h-padding: 4px
$outfit-banner-v-padding: 2px
$outfit-banner-inner-width: $outfit-inner-width - (2 * $outfit-banner-h-padding)
body.fundraising-campaigns
+campaign-progress
color: $campaign-text-color
a
color: $campaign-text-color + #222 !important
#home-link:hover
background-color: $campaign-background-color
#userbar, #footer
color: $text-color
a
color: $link-color
#home-link
color: $link-color
#title
display: none
#donation-form
+module
background: $campaign-background-color
border-color: $campaign-border-color
display: flex
flex-direction: row
margin-top: 1em
margin-bottom: 1.5em
padding-bottom: 32px
padding-left: 24px
padding-right: 24px
padding-top: 32px
position: relative
// We ignore the theme attribute on campaigns now, and just do purple.
&::after
background:
image: url(image_path("fundraising/purple.png"))
repeat: no-repeat
bottom: 0
content: " "
height: 123px
position: absolute
right: 4px
width: 150px
header, #donation-fields
flex: 1
#donation-form-title
font-size: 125%
font-weight: bold
margin-bottom: .25em
margin-top: 0
p
font-family: $main-font
font-size: 85%
margin-bottom: 0
margin-top: .5em
#donation-fields
margin-left: 20px
padding-top: 7px
#amount-header
font-size: 85%
font-weight: bold
line-height: 1
#amount-choices
$amount-choices-border-color: desaturate(lighten($campaign-border-color, 30%), 30%)
display: flex
flex-direction: row
margin-bottom: .75em
margin-top: .5em
li
border: 1px solid $amount-choices-border-color
border-radius: 5px
display: block
flex: 1
list-style: none
overflow: hidden
text-align: center
&:not(:last-of-type)
margin-right: .75em
input[type=radio]
height: 0
margin: 0
padding: 0
opacity: 0
position: absolute
width: 0
label
border: 1px solid transparent
box-sizing: border-box
display: block
padding: .5em .5em
width: 100%
input[type=radio]:checked ~ label
background: lighten($amount-choices-border-color, 15%)
font-weight: bold
input[type=radio]:focus ~ label
border-color: white
border-radius: 5px
#amount-custom-fields
display: none
input[type=text]
font-family: inherit
font-size: inherit
line-height: 1
padding: 0
text-align: center
#amount-custom:checked ~ #amount-custom-fields
display: block
#amount-custom:checked ~ label[for=amount-custom]
display: none
input[type=text]
border-color: #cce
color: $campaign-text-color
width: 3em
#donation-controls
button
+awesome-button-color(#004)
font-size: 120%
#campaign-text[data-campaign-complete]
#description
display: none
&[data-show]
display: block
#success-thanks
border: 1px dashed $module-border-color
margin-bottom: 1em
padding: 1em
position: relative
p:last-child
margin-bottom: 0
#success-thanks-toggle-description
position: absolute
bottom: 1em
font-style: italic
right: 1em
#outfits
+outfits-list
text-align: center
> li
+outfit
height: $outfit-inner-height
margin: 2px
width: $outfit-inner-width
header, footer
font-size: 85%
padding: $outfit-banner-v-padding $outfit-banner-h-padding
width: $outfit-banner-inner-width
img
height: $outfit-inner-height
width: $outfit-inner-width
&.banner
background-image: url(https://images.neopets.com/themes/004_bir_a2e60/footer_bg.png)
background-position: 0 -60px
border: 2px solid #006
color: white
height: 100px
line-height: 100px
margin: 4px 0
text-shadow: #335 2px 2px 1px
width: 800px - 4px
span
+inline-block
font-size: 32px
font-weight: bold
line-height: 1.5
vertical-align: middle
#last-years-donors
font-weight: bold
margin-top: 1em
text-align: center
#outfits-header > *
display: inline-block
#all-campaigns-list
li
display: inline-block
list-style: none
margin-left: 1em
#fine-print
font-size: 85%
margin-top: 2em

View file

@ -0,0 +1,28 @@
@import "../../partials/clean/constants"
@import "../../partials/clean/mixins"
#thank-you
border: 3px solid $module-border-color
display: block
margin: 0 auto 1em
#edit-donation
ul
list-style: none
label
+inline-block
width: 16em
.name input[type=text]
width: 10em
.feature input[type=text]
width: 23em
.choose-outfit
font-size: 85%
margin-left: 1em
select
width: 10em

View file

@ -1,27 +0,0 @@
class CampaignsController < ApplicationController
def show
@campaign = Campaign.find(params[:id])
redirect_to(action: :current) if @campaign.active?
@current_campaign = Campaign.current
@donations = find_donations
@all_campaigns = find_all_campaigns
end
def current
@campaign = Campaign.current
@current_campaign = @campaign
@donations = find_donations
@all_campaigns = find_all_campaigns
render action: :show
end
private
def find_all_campaigns
@all_campaigns = Campaign.order('created_at DESC').all
end
def find_donations
@campaign.donations.includes(features: :outfit).order('created_at DESC')
end
end

View file

@ -1,8 +0,0 @@
class DonationFeaturesController < ApplicationController
def index
# TODO: scope by campaign?
@features = DonationFeature.includes(:donation).includes(:outfit).
where('outfit_id IS NOT NULL')
render json: @features
end
end

View file

@ -1,56 +0,0 @@
class DonationsController < ApplicationController
def create
@campaign = Campaign.current
begin
@donation = Donation.create_from_charge(
@campaign, current_user, params[:donation])
rescue Stripe::CardError => e
flash[:alert] = "We couldn't process your donation: #{e.message}"
redirect_to :donate
rescue => e
flash[:alert] =
"We couldn't process your donation: #{e.message} " +
"Please try again later!"
redirect_to :donate
else
redirect_to @donation
end
end
def show
@donation = Donation.from_param(params[:id])
@features = @donation.features
@outfits = current_user.outfits.wardrobe_order if user_signed_in?
end
def update
@donation = Donation.from_param(params[:id])
@donation.attributes = donation_params
feature_params = params[:feature] || {}
@features = @donation.features.find(feature_params.keys)
@features.each do |feature|
feature.outfit_url = feature_params[feature.id.to_s][:outfit_url]
end
begin
Donation.transaction do
@donation.save!
@features.each(&:save!)
end
rescue ActiveRecord::RecordInvalid
flash[:alert] = "Couldn't save donation details. Do those outfits exist?"
redirect_to @donation
else
flash[:notice] = 'Donation details saved! ' +
'Also, have we thanked you yet today? Thank you!'
redirect_to @donation
end
end
private
def donation_params
params.require(:donation).permit(:donor_name)
end
end

View file

@ -0,0 +1,29 @@
module Fundraising
class CampaignsController < ApplicationController
def show
@campaign = Campaign.find(params[:id])
redirect_to(action: :current) if @campaign.active?
@current_campaign = Campaign.current
@donations = find_donations
@all_campaigns = find_all_campaigns
end
def current
@campaign = Campaign.current
@current_campaign = @campaign
@donations = find_donations
@all_campaigns = find_all_campaigns
render action: :show
end
private
def find_all_campaigns
@all_campaigns = Campaign.order('created_at DESC').all
end
def find_donations
@campaign.donations.includes(features: :outfit).order('created_at DESC')
end
end
end

View file

@ -0,0 +1,10 @@
module Fundraising
class DonationFeaturesController < ApplicationController
def index
# TODO: scope by campaign?
@features = DonationFeature.includes(:donation).includes(:outfit).
where('outfit_id IS NOT NULL')
render json: @features
end
end
end

View file

@ -0,0 +1,58 @@
module Fundraising
class DonationsController < ApplicationController
def create
@campaign = Campaign.current
begin
@donation = Donation.create_from_charge(
@campaign, current_user, params[:donation])
rescue Stripe::CardError => e
flash[:alert] = "We couldn't process your donation: #{e.message}"
redirect_to :donate
rescue => e
flash[:alert] =
"We couldn't process your donation: #{e.message} " +
"Please try again later!"
redirect_to :donate
else
redirect_to @donation
end
end
def show
@donation = Donation.from_param(params[:id])
@features = @donation.features
@outfits = current_user.outfits.wardrobe_order if user_signed_in?
end
def update
@donation = Donation.from_param(params[:id])
@donation.attributes = donation_params
feature_params = params[:feature] || {}
@features = @donation.features.find(feature_params.keys)
@features.each do |feature|
feature.outfit_url = feature_params[feature.id.to_s][:outfit_url]
end
begin
Donation.transaction do
@donation.save!
@features.each(&:save!)
end
rescue ActiveRecord::RecordInvalid
flash[:alert] = "Couldn't save donation details. Do those outfits exist?"
redirect_to @donation
else
flash[:notice] = 'Donation details saved! ' +
'Also, have we thanked you yet today? Thank you!'
redirect_to @donation
end
end
private
def donation_params
params.require(:donation).permit(:donor_name)
end
end
end

View file

@ -15,7 +15,8 @@ module ApplicationHelper
end
def body_class
"#{params[:controller]} #{params[:controller]}-#{params[:action]}".tap do |output|
controller = params[:controller].gsub("/", "-")
"#{controller} #{controller}-#{params[:action]}".tap do |output|
output << @body_class if @body_class
end
end

View file

@ -1,25 +0,0 @@
module DonationsHelper
THANK_YOU_GREETINGS = [
'https://images.neopets.com/new_greetings/1368.gif',
'https://images.neopets.com/new_greetings/466.gif',
'https://images.neopets.com/new_greetings/48.gif',
'https://images.neopets.com/new_greetings/49.gif',
'https://images.neopets.com/new_greetings/64.gif',
'https://images.neopets.com/new_greetings/65.gif',
'https://images.neopets.com/new_greetings/66.gif',
'https://images.neopets.com/new_greetings/67.gif',
'https://images.neopets.com/new_greetings/69.gif',
'https://images.neopets.com/new_greetings/71.gif',
'https://images.neopets.com/new_greetings/72.gif',
'https://images.neopets.com/new_greetings/103.gif',
'https://images.neopets.com/new_greetings/420.gif'
]
def thank_you_greeting_url
THANK_YOU_GREETINGS.sample
end
def feature_outfit_url(outfit_id)
outfit_url(outfit_id) if outfit_id
end
end

View file

@ -0,0 +1,11 @@
module Fundraising
module CampaignsHelper
def large_donation?(amount)
amount > 100_00
end
def outfit_image?(outfit)
outfit.present? && outfit.image?
end
end
end

View file

@ -0,0 +1,27 @@
module Fundraising
module DonationsHelper
THANK_YOU_GREETINGS = [
'https://images.neopets.com/new_greetings/1368.gif',
'https://images.neopets.com/new_greetings/466.gif',
'https://images.neopets.com/new_greetings/48.gif',
'https://images.neopets.com/new_greetings/49.gif',
'https://images.neopets.com/new_greetings/64.gif',
'https://images.neopets.com/new_greetings/65.gif',
'https://images.neopets.com/new_greetings/66.gif',
'https://images.neopets.com/new_greetings/67.gif',
'https://images.neopets.com/new_greetings/69.gif',
'https://images.neopets.com/new_greetings/71.gif',
'https://images.neopets.com/new_greetings/72.gif',
'https://images.neopets.com/new_greetings/103.gif',
'https://images.neopets.com/new_greetings/420.gif'
]
def thank_you_greeting_url
THANK_YOU_GREETINGS.sample
end
def feature_outfit_url(outfit_id)
outfit_url(outfit_id) if outfit_id
end
end
end

View file

@ -1,9 +0,0 @@
module StaticHelper
def large_donation?(amount)
amount > 100_00
end
def outfit_image?(outfit)
outfit.present? && outfit.image?
end
end

View file

@ -1,8 +0,0 @@
class DonationMailer < ActionMailer::Base
default from: "matchu@openneo.net"
def thank_you_email(donation, recipient)
@donation = donation
mail(to: recipient, subject: 'Thanks for donating to Dress to Impress!')
end
end

View file

@ -0,0 +1,10 @@
module Fundraising
class DonationMailer < ActionMailer::Base
default from: "matchu@openneo.net"
def thank_you_email(donation, recipient)
@donation = donation
mail(to: recipient, subject: 'Thanks for donating to Dress to Impress!')
end
end
end

View file

@ -125,4 +125,7 @@
- content_for :javascripts do
= javascript_include_tag 'https://checkout.stripe.com/checkout.js',
'static/donate.js'
'fundraising/campaigns/show'
- content_for :stylesheets do
= stylesheet_link_tag 'fundraising/campaigns/show'

View file

@ -43,4 +43,7 @@
- content_for :javascripts do
= include_javascript_libraries :jquery
= javascript_include_tag 'donations/show.js'
= javascript_include_tag 'fundraising/donations/show'
- content_for :stylesheets do
= stylesheet_link_tag 'fundraising/donations/show'

View file

@ -79,13 +79,15 @@ OpenneoImpressItems::Application.routes.draw do
end
# Donation campaign stuff!
resources :donations, only: [:create, :show, :update] do
collection do
resources :donation_features, path: 'features', only: [:index]
scope module: "fundraising" do
resources :donations, only: [:create, :show, :update] do
collection do
resources :donation_features, path: 'features', only: [:index]
end
end
resources :campaigns, only: [:show], path: '/donate/campaigns'
get '/donate' => 'campaigns#current', as: :donate
end
resources :campaigns, only: [:show], path: '/donate/campaigns'
get '/donate' => 'campaigns#current', as: :donate
# Static pages!
get '/terms', as: :terms,