Skip to content

Commit

Permalink
Prepare API for upcoming UI changes (#249)
Browse files Browse the repository at this point in the history
  • Loading branch information
sivayogasubramanian authored Oct 22, 2022
2 parents 7e43623 + e705af5 commit 9a16997
Show file tree
Hide file tree
Showing 43 changed files with 216 additions and 71 deletions.
28 changes: 18 additions & 10 deletions backend/app/controllers/campaigns_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ def admin_index
def show; end

def admin_show
@campaign = Campaign.includes(:primary_donor,
:secondary_donations,
image_attachment: :blob,
coupons: { secondary_donation: { campaign_charity: :charity } },
campaign_charities: [:secondary_donations,
:coupons,
{ charity: [logo_attachment: :blob, image_attachment: :blob] }])
@campaign = Campaign.includes(
:secondary_donations,
image_attachment: :blob,
primary_donor: { image_attachment: :blob },
coupons: { secondary_donation: { campaign_charity: :charity } },
campaign_charities: [:secondary_donations,
:coupons,
{ charity: [logo_attachment: :blob, image_attachment: :blob] }]
)
.find(params[:id])
end

Expand Down Expand Up @@ -72,7 +74,9 @@ def destroy
private

def set_campaign
@campaign = Campaign.includes(:primary_donor, :coupons, :secondary_donations,
@campaign = Campaign.includes(:coupons, :secondary_donations,
image_attachment: :blob,
primary_donor: [image_attachment: :blob],
campaign_charities: [:coupons,
{ secondary_donations: [:coupon] },
{ charity: [logo_attachment: :blob,
Expand All @@ -91,9 +95,13 @@ def campaign_params
def set_donor
primary_donor_params = params[:primary_donor]

@campaign.primary_donor = PrimaryDonor.find_or_initialize_by(email: primary_donor_params[:email]) do |new_donor|
new_donor.name = primary_donor_params[:name]
primary_donor = PrimaryDonor.find_or_initialize_by(email: primary_donor_params[:email])
primary_donor.name = primary_donor_params[:name]
if primary_donor_params[:image_base64].present?
primary_donor.image.attach(data: primary_donor_params[:image_base64])
end

@campaign.primary_donor = primary_donor
end

def set_interest
Expand Down
43 changes: 38 additions & 5 deletions backend/app/controllers/coupons_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class CouponsController < ApplicationController
before_action :authenticate_admin!, except: [:show]
before_action :authenticate_admin!, except: %i[show redeem]

def index
@coupons = Coupon.all.includes({ secondary_donation: { campaign_charity: :charity } })
Expand All @@ -14,9 +14,42 @@ def campaign_unredeemed
end

def show
@coupon = Coupon.includes({ campaign: [{ campaign_charities: [:secondary_donations, :coupons,
{ charity: { logo_attachment: :blob,
image_attachment: :blob } }] },
:primary_donor] }).find_by(url_token: params[:id])
@coupon = Coupon.includes([:secondary_donation,
{ campaign: [{ campaign_charities: [:secondary_donations, :coupons,
{ charity: { logo_attachment: :blob,
image_attachment: :blob } }] },
{ primary_donor: [image_attachment: :blob] },
{ image_attachment: :blob }] },
{ campaign_charity: [charity: { logo_attachment: :blob,
image_attachment: :blob }] }])
.find_by(url_token: params[:id])
end

def redeem
@coupon = Coupon.find_by(url_token: params[:url_token])

if @coupon.redeemed?
add_error_message('Coupon is already redeemed!')
render 'layouts/empty', status: :bad_request
return
end

@coupon.assign_attributes(redeem_params)

if params[:amount].positive?
@coupon.build_secondary_donation(campaign_charity_id: params[:campaign_charity_id],
amount: params[:amount])
end

@coupon.save!

add_success_message('Thank you for your donation!')
render 'show', status: :created
end

private

def redeem_params
params.require(:coupon).permit(:campaign_charity_id)
end
end
5 changes: 4 additions & 1 deletion backend/app/controllers/interests_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def show; end
def create
@interest = Interest.new(interest_params)
@interest.charity_ids = params[:charity_ids]
@interest.campaign_image.attach(data: params[:campaign_image_base64]) if params[:campaign_image_base64].present?
@interest.donor_image.attach(data: params[:donor_image_base64]) if params[:donor_image_base64].present?
@interest.save!

add_success_message "Interest form for \"#{@interest.donor_name}\" successfully submitted!"
Expand Down Expand Up @@ -47,7 +49,8 @@ def destroy
private

def set_interest
@interest = Interest.includes(charities: [logo_attachment: :blob, image_attachment: :blob]).find(params[:id])
@interest = Interest.includes(campaign_image_attachment: :blob, donor_image_attachment: :blob,
charities: [logo_attachment: :blob, image_attachment: :blob]).find(params[:id])
end

def interest_params
Expand Down
1 change: 0 additions & 1 deletion backend/app/controllers/secondary_donations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ def index

def create
@secondary_donation = SecondaryDonation.new(secondary_donation_params)
@secondary_donation.coupon = Coupon.find_by(url_token: params[:url_token])
@secondary_donation.campaign_charity = CampaignCharity.find(params[:campaign_charity_id])
@secondary_donation.save!

Expand Down
4 changes: 2 additions & 2 deletions backend/app/models/campaign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Campaign < ApplicationRecord

has_one_base64_attached :image

belongs_to :primary_donor
belongs_to :primary_donor, autosave: true
belongs_to :interest, optional: true
has_many :coupons, dependent: :destroy
has_many :campaign_charities, dependent: :destroy, autosave: true
Expand Down Expand Up @@ -73,7 +73,7 @@ def generate_coupons
end

def num_redeemed_coupons
coupons.count(&:is_redeemed?)
coupons.count(&:redeemed?)
end

private
Expand Down
2 changes: 1 addition & 1 deletion backend/app/models/campaign_charity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class CampaignCharity < ApplicationRecord
belongs_to :campaign
belongs_to :charity
has_many :secondary_donations, dependent: :destroy
has_many :coupons, through: :secondary_donations
has_many :coupons, dependent: :destroy

validates :giving_sg_url, presence: true, allow_blank: false, format: { with: URI::DEFAULT_PARSER.make_regexp }

Expand Down
6 changes: 6 additions & 0 deletions backend/app/models/coupon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ class Coupon < ApplicationRecord
NUM_ALPHANUMERIC_CHARS_IN_TOKEN = 6

belongs_to :campaign
belongs_to :campaign_charity, optional: true
has_one :secondary_donation, required: false, dependent: :nullify

validates :url_token, presence: true, uniqueness: true
validates :denomination, presence: true, numericality: { only_integer: true, greater_than: 0 }
validates_associated :secondary_donation

def redeemed?
campaign_charity_id.present?
end

def self.generate_unique_url_token
token = SecureRandom.alphanumeric(NUM_ALPHANUMERIC_CHARS_IN_TOKEN)
Expand Down
3 changes: 3 additions & 0 deletions backend/app/models/interest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ class Interest < ApplicationRecord

enum :status, { pending: 'pending', approved: 'approved', rejected: 'rejected' }

has_one_base64_attached :campaign_image
has_one_base64_attached :donor_image

has_one :campaign, required: false, dependent: nil
has_many :interest_charities, dependent: :destroy
has_many :charities, through: :interest_charities
Expand Down
2 changes: 2 additions & 0 deletions backend/app/models/primary_donor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

class PrimaryDonor < ApplicationRecord
has_one_base64_attached :image

has_many :campaigns, dependent: :destroy

validates :name, presence: true
Expand Down
14 changes: 12 additions & 2 deletions backend/app/models/secondary_donation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ class SecondaryDonation < ApplicationRecord
validates :amount, presence: true, numericality: { only_integer: true }
validates :coupon, final: true
validates :coupon_id, allow_nil: true, uniqueness: true
validate :coupon_must_be_for_campaign_charity
validate :coupon_from_same_campaign
validate :coupon_for_same_campaign_charity

private

def coupon_must_be_for_campaign_charity
def coupon_from_same_campaign
return if coupon.nil?

coupon_campaign = coupon.campaign
Expand All @@ -21,4 +22,13 @@ def coupon_must_be_for_campaign_charity

errors.add(:coupon, 'must be for the same campaign as the campaign charity')
end

def coupon_for_same_campaign_charity
return if coupon.nil?

coupon_campaign_charity = coupon.campaign_charity
return if coupon_campaign_charity == campaign_charity

errors.add(:coupon, 'must be redeemed for the same campaign charity as donation')
end
end
3 changes: 3 additions & 0 deletions backend/app/views/campaign_charities/_donation.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@

json.partial! 'campaign_charities/campaign_charity', campaign_charity: campaign_charity
json.partial! 'secondary_donations/breakdown', donation_breakdown: campaign_charity.donation_breakdown
json.primaryDonor do
json.partial! 'primary_donors/primary_donor', primary_donor: campaign_charity.campaign.primary_donor
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@

json.partial! 'campaign_charities/campaign_charity', campaign_charity: campaign_charity
json.partial! 'secondary_donations/breakdown', donation_breakdown: campaign_charity.donation_breakdown

json.primaryDonor do
json.partial! 'primary_donors/primary_donor', primary_donor: campaign_charity.campaign.primary_donor
end
json.attributes!.delete('givingSgUrl')
2 changes: 1 addition & 1 deletion backend/app/views/coupons/_coupon.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

json.id coupon.id
json.urlToken coupon.url_token
json.isRedeemed coupon.is_redeemed?
json.isRedeemed coupon.redeemed?

# TODO: Create another partial that only contains the info that is needed for the coupon_controller clients
json.campaign do
Expand Down
13 changes: 8 additions & 5 deletions backend/app/views/coupons/_list.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

json.partial! 'coupons/base', coupon: coupon

if coupon.campaign_charity.present?
json.charity do
json.partial! 'charities/minimal', charity: coupon.campaign_charity.charity
end
else
json.charity nil
end

if coupon.secondary_donation.present?
json.secondaryDonation do
json.partial! 'secondary_donations/secondary_donation', secondary_donation: coupon.secondary_donation
end

json.charity do
json.partial! 'charities/minimal', charity: coupon.secondary_donation.campaign_charity.charity
end
else
json.secondaryDonation nil
json.charity nil
end
16 changes: 16 additions & 0 deletions backend/app/views/coupons/show.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ json.campaign do
json.partial! 'campaigns/base', campaign: @coupon.campaign
end

if @coupon.campaign_charity.present?
json.campaignCharity do
json.partial! 'campaign_charities/campaign_charity', campaign_charity: @coupon.campaign_charity
end
else
json.campaignCharity nil
end

if @coupon.secondary_donation.present?
json.secondaryDonation do
json.partial! 'secondary_donations/secondary_donation', secondary_donation: @coupon.secondary_donation
end
else
json.secondaryDonation nil
end

json.charities do
json.array! @coupon.campaign.campaign_charities do |campaign_charity|
json.partial! 'campaign_charities/base', campaign_charity: campaign_charity
Expand Down
2 changes: 2 additions & 0 deletions backend/app/views/interests/show.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

json.partial! 'interests/interest', interest: @interest
json.campaignImageBase64 encoded_file_data_url(@interest.campaign_image)
json.donorImageBase64 encoded_file_data_url(@interest.donor_image)
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
json.id primary_donor.id
json.name primary_donor.name
json.email primary_donor.email
json.imageBase64 encoded_file_data_url(primary_donor.image)
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# frozen_string_literal: true

json.primaryDonor do
json.primaryDonation do
json.amount donation_breakdown[:primary_donor_amount]
json.fraction donation_breakdown[:primary_donor_fraction]
end

json.secondaryDonors do
json.secondaryDonation do
json.amount donation_breakdown[:secondary_donors_amount]
json.fraction donation_breakdown[:secondary_donors_fraction]
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@
json.id secondary_donation.id
json.amount secondary_donation.amount
json.campaignCharityId secondary_donation.campaign_charity_id

if secondary_donation.coupon
json.urlToken secondary_donation.coupon.url_token
else
json.urlToken nil
end
1 change: 1 addition & 0 deletions backend/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
resources :coupons, only: %i[index show] do
collection do
get 'campaign/:campaign_id/unredeemed', to: 'coupons#campaign_unredeemed'
post 'redeem'
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

class AddCampaignCharityToCoupon < ActiveRecord::Migration[7.0]
def self.up
remove_column :coupons, :is_redeemed, :boolean
add_reference :coupons, :campaign_charity, foreign_key: true

# Migrate existing data
execute "UPDATE coupons c SET campaign_charity_id =
(SELECT campaign_charity_id FROM secondary_donations sd WHERE sd.coupon_id = c.id)"
end

def self.down
remove_reference :coupons, :campaign_charity, foreign_key: true
add_column :coupons, :is_redeemed, :boolean
end
end
6 changes: 4 additions & 2 deletions backend/db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ interface Props {
const CampaignCharityCard = ({ campaignCharity, redirectTo }: Props) => {
const router = useRouter();

const primaryDonorDonationData = campaignCharity.primaryDonor;
const secondaryDonorDonationData = campaignCharity.secondaryDonors;
const primaryDonorDonationData = campaignCharity.primaryDonation;
const secondaryDonorDonationData = campaignCharity.secondaryDonation;

const imageOverlayContent = (
<Container sx={graphContainerSx} component="div">
Expand Down
Loading

0 comments on commit 9a16997

Please sign in to comment.