Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dues estimator to reduce currencylayer usage #10767

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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],
);
Comment on lines +14 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's any need to memoize this.


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use useQuery here? useLoadedData has some issues and I think the plan is to replace it at some point anyway.


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