Skip to content

Commit

Permalink
Dues estimator to reduce currencylayer usage
Browse files Browse the repository at this point in the history
  • Loading branch information
danieljames-dj committed Feb 4, 2025
1 parent 221c44f commit 0d4b87b
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 60 deletions.
12 changes: 8 additions & 4 deletions app/controllers/competitions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,15 @@ def registration_collisions_json
end

def calculate_dues
country_iso2 = Country.find_by(id: params[:country_id])&.iso2
multiplier = ActiveRecord::Type::Boolean.new.cast(params[:competitor_limit_enabled]) ? params[:competitor_limit].to_i : 1
total_dues = DuesCalculator.dues_for_n_competitors(country_iso2, params[:base_entry_fee_lowest_denomination].to_i, params[:currency_code], multiplier)
country_iso2 = Country.find_by(id: params[:countryId])&.iso2
per_competitor_dues = DuesCalculator.dues_per_competitor(
country_iso2,
params[:baseEntryFee].to_i,
params[:currencyCode],
)

render json: {
dues_value: total_dues.present? ? total_dues.format : nil,
dues_value: per_competitor_dues.to_f,
}
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useMemo } from 'react';
import { Input, Modal } from 'semantic-ui-react';
import useInputState from '../../../lib/hooks/useInputState';
import { calculateDuesUrl } from '../../../lib/requests/routes.js.erb';
import { currenciesData } from '../../../lib/wca-data.js.erb';
import useLoadedData from '../../../lib/hooks/useLoadedData';
import Loading from '../../Requests/Loading';
import Errored from '../../Requests/Errored';

export default function DuesEstimate({
close, countryId, currencyCode, baseEntryFee, competitorLimit,
}) {
const [competitorCount, setCompetitorCount] = useInputState(competitorLimit || 0);
const currencyInfo = useMemo(
() => (currenciesData.byIso[currencyCode] || currenciesData.byIso.USD),
[currencyCode],
);

const {
data, loading, error,
} = useLoadedData(calculateDuesUrl(countryId, currencyCode, baseEntryFee));

const { dues_value: duesValue } = data || {};

if (loading) return <Loading />;
if (error) return <Errored error="Dues fetching failed..." />;

return (
<Modal
open
onClose={close}
closeIcon
>
<Modal.Header>Dues Estimate</Modal.Header>
<Modal.Content>
<Input
label="Competitor count"
type="number"
value={competitorCount}
onChange={setCompetitorCount}
/>
<p>
{`Dues for ${competitorCount} competitors (approximately): ${currencyInfo?.symbol || ''}${(duesValue * competitorCount).toFixed(2)}`}
</p>
</Modal.Content>
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useMemo } from 'react';
import React, { useState } from 'react';
import { Button } from 'semantic-ui-react';
import {
InputBoolean,
InputCurrencyAmount,
Expand All @@ -7,12 +8,10 @@ import {
InputSelect,
} from '../../wca/FormBuilder/input/FormInputs';
import { currenciesData } from '../../../lib/wca-data.js.erb';
import I18n from '../../../lib/i18n';
import { calculateDuesUrl } from '../../../lib/requests/routes.js.erb';
import useLoadedData from '../../../lib/hooks/useLoadedData';
import ConditionalSection from './ConditionalSection';
import SubSection from '../../wca/FormBuilder/SubSection';
import { useFormObject } from '../../wca/FormBuilder/provider/FormObjectProvider';
import DuesEstimate from './DuesEstimate';

const currenciesOptions = Object.keys(currenciesData.byIso).map((iso) => ({
key: iso,
Expand All @@ -29,59 +28,26 @@ export default function RegistrationFees() {
competitorLimit,
registration,
} = useFormObject();
const [showDuesEstimate, setShowDuesEstimate] = useState(false);

const currency = entryFees.currencyCode;

const canRegOnSite = registration && registration.allowOnTheSpot;

const savedParams = useMemo(() => {
const params = new URLSearchParams();

params.append('competitor_limit_enabled', competitorLimit.enabled);
params.append('competitor_limit', competitorLimit.count);
params.append('currency_code', entryFees.currencyCode);
params.append('base_entry_fee_lowest_denomination', entryFees.baseEntryFee);
params.append('country_id', country);

return params;
}, [competitorLimit, country, entryFees]);

const entryFeeDuesUrl = useMemo(
() => `${calculateDuesUrl}?${savedParams.toString()}`,
[savedParams],
);

const {
data: duesJson,
error,
} = useLoadedData(entryFeeDuesUrl);

const duesText = useMemo(() => {
if (error || !duesJson?.dues_value) {
return I18n.t('competitions.competition_form.dues_estimate.ajax_error');
}

if (competitorLimit.enabled) {
return `${I18n.t('competitions.competition_form.dues_estimate.calculated', {
limit: competitorLimit.count,
estimate: duesJson?.dues_value,
})} (${currency})`;
}

return `${I18n.t('competitions.competition_form.dues_estimate.per_competitor', {
estimate: duesJson?.dues_value,
})} (${currency})`;
}, [competitorLimit, currency, duesJson, error]);

return (
<SubSection section="entryFees">
<InputSelect id="currencyCode" options={currenciesOptions} required />
<InputCurrencyAmount id="baseEntryFee" currency={currency} required />
<p className="help-block">
<b>
{duesText}
</b>
</p>
<Button onClick={() => setShowDuesEstimate(true)}>Show Dues Estimate</Button>
{showDuesEstimate && (
<DuesEstimate
close={() => setShowDuesEstimate(false)}
countryId={country}
currencyCode={entryFees.currencyCode}
baseEntryFee={entryFees.baseEntryFee}
competitorLimit={competitorLimit.count}
/>
)}
<ConditionalSection showIf={canRegOnSite}>
<InputCurrencyAmount id="onTheSpotEntryFee" currency={currency} required={canRegOnSite} />
</ConditionalSection>
Expand Down
2 changes: 1 addition & 1 deletion app/webpacker/lib/requests/routes.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const seriesEligibleCompetitionsJsonUrl = `<%= CGI.unescape(Rails.applica

export const registrationCollisionsJsonUrl = `<%= CGI.unescape(Rails.application.routes.url_helpers.registration_collisions_json_path)%>`;

export const calculateDuesUrl = `<%= CGI.unescape(Rails.application.routes.url_helpers.calculate_dues_path)%>`;
export const calculateDuesUrl = (countryId, currencyCode, baseEntryFee) => `<%= CGI.unescape(Rails.application.routes.url_helpers.calculate_dues_path)%>?${jsonToQueryString({ countryId, currencyCode, baseEntryFee })}`;

export const adminPostingCompetitionsUrl = `<%= CGI.unescape(Rails.application.routes.url_helpers.results_posting_dashboard_path(format: "json")) %>`;

Expand Down
8 changes: 1 addition & 7 deletions lib/dues_calculator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
module DuesCalculator
def self.dues_for_n_competitors(country_iso2, base_entry_fee_lowest_denomination, currency_code, n)
dues_per_competitor_in_usd_money = dues_per_competitor_in_usd(country_iso2, base_entry_fee_lowest_denomination, currency_code)
if dues_per_competitor_in_usd_money.present?
(dues_per_competitor_in_usd_money * n).exchange_to(currency_code)
else
nil
end
rescue CurrencyUnavailable
nil
dues_per_competitor_in_usd_money.exchange_to(currency_code)
end

def self.dues_per_competitor_in_usd(country_iso2, base_entry_fee_lowest_denomination, currency_code)
Expand Down

0 comments on commit 0d4b87b

Please sign in to comment.