From c304f92b0225d93054f5cc3faa545b16aef0040b Mon Sep 17 00:00:00 2001 From: duncte123 Date: Wed, 26 Jul 2023 12:39:11 +0200 Subject: [PATCH 01/40] Add Euros to currency options --- tracker/models/donation.py | 6 +++++- tracker/models/event.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tracker/models/donation.py b/tracker/models/donation.py index 4d5bba818..b35c29aec 100644 --- a/tracker/models/donation.py +++ b/tracker/models/donation.py @@ -24,7 +24,11 @@ 'DonorCache', ] -_currencyChoices = (('USD', 'US Dollars'), ('CAD', 'Canadian Dollars')) +_currencyChoices = ( + ('USD', 'US Dollars'), + ('CAD', 'Canadian Dollars'), + ('EUR', 'Euros'), +) DonorVisibilityChoices = ( ('FULL', 'Fully Visible'), diff --git a/tracker/models/event.py b/tracker/models/event.py index 630033ac0..b01c5626b 100644 --- a/tracker/models/event.py +++ b/tracker/models/event.py @@ -29,7 +29,11 @@ ] _timezoneChoices = [(x, x) for x in pytz.common_timezones] -_currencyChoices = (('USD', 'US Dollars'), ('CAD', 'Canadian Dollars')) +_currencyChoices = ( + ('USD', 'US Dollars'), + ('CAD', 'Canadian Dollars'), + ('EUR', 'Euros'), +) logger = logging.getLogger(__name__) From 48bd336235b32ca2b278cd2391f65c8fd98b7faa Mon Sep 17 00:00:00 2001 From: duncte123 Date: Wed, 26 Jul 2023 13:53:37 +0200 Subject: [PATCH 02/40] Support euros in the front-end --- bundles/@types/global.d.ts | 1 + bundles/public/util/currency.ts | 13 ++++++++++- .../tracker/donation/components/Donate.tsx | 4 +++- .../donation/components/DonateInitializer.tsx | 3 +++ bundles/uikit/CurrencyInput.tsx | 6 ++++- .../0031_add_euros_to_donation_and_event.py | 23 +++++++++++++++++++ 6 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tracker/migrations/0031_add_euros_to_donation_and_event.py diff --git a/bundles/@types/global.d.ts b/bundles/@types/global.d.ts index 6178d6051..bccf406f8 100644 --- a/bundles/@types/global.d.ts +++ b/bundles/@types/global.d.ts @@ -4,5 +4,6 @@ declare global { interface Window { AdminApp: any; TrackerApp: any; + currency: string; } } diff --git a/bundles/public/util/currency.ts b/bundles/public/util/currency.ts index 73c6bd078..357fc777d 100644 --- a/bundles/public/util/currency.ts +++ b/bundles/public/util/currency.ts @@ -1,5 +1,16 @@ export function asCurrency(amount: string | number) { - return `$${Number(amount).toFixed(2)}`; + const currency = getCurrencySymbol(); + + return `${currency}${Number(amount).toFixed(2)}`; +} + +export function getCurrencySymbol(): string { + switch (window.currency.toUpperCase()) { + case 'EUR': + return '€'; + default: + return '$'; + } } export function parseCurrency(amount?: string) { diff --git a/bundles/tracker/donation/components/Donate.tsx b/bundles/tracker/donation/components/Donate.tsx index f2ac194eb..035ca0ba9 100644 --- a/bundles/tracker/donation/components/Donate.tsx +++ b/bundles/tracker/donation/components/Donate.tsx @@ -93,6 +93,7 @@ const Donate = (props: DonateProps) => { updateDonation, ]); const updateComment = React.useCallback((comment: string) => updateDonation({ comment }), [updateDonation]); + const currencySymbol = CurrencyUtils.getCurrencySymbol(); return ( @@ -169,7 +170,8 @@ const Donate = (props: DonateProps) => { key={amountPreset} look={Button.Looks.OUTLINED} onClick={updateAmountPreset(amountPreset)}> - ${amountPreset} + {currencySymbol} + {amountPreset} ))} diff --git a/bundles/tracker/donation/components/DonateInitializer.tsx b/bundles/tracker/donation/components/DonateInitializer.tsx index 39cbe1a77..cd8b3bd4f 100644 --- a/bundles/tracker/donation/components/DonateInitializer.tsx +++ b/bundles/tracker/donation/components/DonateInitializer.tsx @@ -56,6 +56,7 @@ type DonateInitializerProps = { }; initialIncentives: InitialIncentive[]; event: { + paypalcurrency: string; receivername: string; }; step: number; @@ -115,6 +116,8 @@ const DonateInitializer = (props: DonateInitializerProps) => { }; }); + window.currency = event.paypalcurrency; + dispatch( EventDetailsActions.loadEventDetails({ csrfToken, diff --git a/bundles/uikit/CurrencyInput.tsx b/bundles/uikit/CurrencyInput.tsx index f91bfea33..7b6c0496d 100644 --- a/bundles/uikit/CurrencyInput.tsx +++ b/bundles/uikit/CurrencyInput.tsx @@ -2,6 +2,8 @@ import * as React from 'react'; import classNames from 'classnames'; import ReactNumeric from 'react-numeric'; +import * as CurrencyUtils from '@public/util/currency'; + import InputWrapper, { InputWrapperPassthroughProps } from './InputWrapper'; import styles from './CurrencyInput.mod.css'; @@ -20,6 +22,8 @@ type CurrencyInputProps = InputWrapperPassthroughProps & { // - entering thousandths or beyond // - formatting according to user's locale const CurrencyInput = (props: CurrencyInputProps) => { + const currencySymbol = CurrencyUtils.getCurrencySymbol(); + const { size = InputWrapper.Sizes.NORMAL, value, @@ -28,7 +32,7 @@ const CurrencyInput = (props: CurrencyInputProps) => { name, label, hint, - leader = '$', + leader = currencySymbol, trailer, marginless = false, className, diff --git a/tracker/migrations/0031_add_euros_to_donation_and_event.py b/tracker/migrations/0031_add_euros_to_donation_and_event.py new file mode 100644 index 000000000..9ae4e5e58 --- /dev/null +++ b/tracker/migrations/0031_add_euros_to_donation_and_event.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.20 on 2023-07-26 10:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tracker', '0030_add_bid_chain'), + ] + + operations = [ + migrations.AlterField( + model_name='donation', + name='currency', + field=models.CharField(choices=[('USD', 'US Dollars'), ('CAD', 'Canadian Dollars'), ('EUR', 'Euros')], max_length=8, verbose_name='Currency'), + ), + migrations.AlterField( + model_name='event', + name='paypalcurrency', + field=models.CharField(choices=[('USD', 'US Dollars'), ('CAD', 'Canadian Dollars'), ('EUR', 'Euros')], default='USD', max_length=8, verbose_name='Currency'), + ), + ] From 1f367ba626ad7639217788f58db758e9f7ed48ed Mon Sep 17 00:00:00 2001 From: duncte123 Date: Thu, 27 Jul 2023 10:14:58 +0200 Subject: [PATCH 03/40] Store the currency in the store --- bundles/@types/global.d.ts | 1 - .../modules/donations/DonationRow.tsx | 9 +++- .../modules/donations/ModCommentModal.tsx | 10 +++-- .../modules/processing/ActionLog.tsx | 6 ++- .../reading/ReadingDonationRowPopout.tsx | 5 ++- bundles/public/util/currency.ts | 27 ++++++++---- bundles/public/util/currencySpec.ts | 41 +++++++++++++++++++ .../donation/__tests__/validateBid.spec.ts | 27 ++++++------ .../__tests__/validateDonation.spec.ts | 5 ++- .../tracker/donation/components/Donate.tsx | 11 +++-- .../donation/components/DonateInitializer.tsx | 3 +- .../donation/components/DonationBidForm.tsx | 11 +++-- .../donation/components/DonationBids.tsx | 4 +- .../donation/components/DonationPrizes.tsx | 3 +- bundles/tracker/donation/validateBid.ts | 11 +++-- bundles/tracker/donation/validateDonation.ts | 10 +++-- .../event_details/EventDetailsReducer.ts | 1 + .../event_details/EventDetailsStore.ts | 2 + .../event_details/EventDetailsTypes.ts | 1 + bundles/tracker/prizes/components/Prize.tsx | 13 +++--- .../tracker/prizes/components/PrizeCard.tsx | 4 +- bundles/uikit/CurrencyInput.tsx | 6 +-- 22 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 bundles/public/util/currencySpec.ts diff --git a/bundles/@types/global.d.ts b/bundles/@types/global.d.ts index bccf406f8..6178d6051 100644 --- a/bundles/@types/global.d.ts +++ b/bundles/@types/global.d.ts @@ -4,6 +4,5 @@ declare global { interface Window { AdminApp: any; TrackerApp: any; - currency: string; } } diff --git a/bundles/processing/modules/donations/DonationRow.tsx b/bundles/processing/modules/donations/DonationRow.tsx index d9e1e93a3..c50fa5e45 100644 --- a/bundles/processing/modules/donations/DonationRow.tsx +++ b/bundles/processing/modules/donations/DonationRow.tsx @@ -1,12 +1,15 @@ import * as React from 'react'; import classNames from 'classnames'; import { useDrag, useDrop } from 'react-dnd'; +import { useSelector } from 'react-redux'; import { Clickable, Stack, Text } from '@spyrothon/sparx'; import type { Donation, DonationBid } from '@public/apiv2/APITypes'; import * as CurrencyUtils from '@public/util/currency'; import DragHandle from '@uikit/icons/DragHandle'; +import * as EventDetailsStore from '@tracker/event_details/EventDetailsStore'; + import HighlightKeywords from './HighlightKeywords'; import styles from './DonationRow.mod.css'; @@ -19,9 +22,10 @@ interface BidsRowProps { function BidsRow(props: BidsRowProps) { const { bids } = props; + const currency = useSelector(EventDetailsStore.getEventCurrency); if (bids.length === 0) return null; - const bidNames = bids.map(bid => `${bid.bid_name} (${CurrencyUtils.asCurrency(bid.amount)})`); + const bidNames = bids.map(bid => `${bid.bid_name} (${CurrencyUtils.asCurrency(bid.amount, { currency })})`); return ( @@ -76,7 +80,8 @@ export default function DonationRow(props: DonationRowProps) { canDrop: checkDrop, } = props; - const amount = CurrencyUtils.asCurrency(donation.amount); + const currency = useSelector(EventDetailsStore.getEventCurrency); + const amount = CurrencyUtils.asCurrency(donation.amount, { currency }); const donationTitle = ( {amount} diff --git a/bundles/processing/modules/donations/ModCommentModal.tsx b/bundles/processing/modules/donations/ModCommentModal.tsx index 5994af89b..93287778c 100644 --- a/bundles/processing/modules/donations/ModCommentModal.tsx +++ b/bundles/processing/modules/donations/ModCommentModal.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { useMutation } from 'react-query'; +import { useSelector } from 'react-redux'; import { Button, Card, FormControl, Header, Stack, Text, TextArea } from '@spyrothon/sparx'; import APIClient from '@public/apiv2/APIClient'; @@ -7,14 +8,16 @@ import { Donation } from '@public/apiv2/APITypes'; import * as CurrencyUtils from '@public/util/currency'; import TimeUtils from '@public/util/TimeUtils'; +import * as EventDetailsStore from '@tracker/event_details/EventDetailsStore'; + import RelativeTime from '../time/RelativeTime'; import { useDonation } from './DonationsStore'; import styles from '../donation-groups/CreateEditDonationGroupModal.mod.css'; -function renderDonationHeader(donation: Donation) { +function renderDonationHeader(donation: Donation, currency: string) { const timestamp = TimeUtils.parseTimestamp(donation.timereceived); - const amount = CurrencyUtils.asCurrency(donation.amount); + const amount = CurrencyUtils.asCurrency(donation.amount, { currency }); return ( @@ -44,6 +47,7 @@ interface ModCommentModalProps { export default function ModCommentModal(props: ModCommentModalProps) { const { donationId, onClose } = props; + const currency = useSelector(EventDetailsStore.getEventCurrency); const donation = useDonation(donationId); const [comment, setComment] = React.useState(donation.modcomment ?? ''); @@ -59,7 +63,7 @@ export default function ModCommentModal(props: ModCommentModalProps) {
Edit Mod Comment
- {renderDonationHeader(donation)} + {renderDonationHeader(donation, currency)}