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

integrate payment card section with API #43473

2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3217,7 +3217,7 @@ export default {
title: 'Payment',
subtitle: 'Add a payment card to pay for your Expensify subscription.',
addCardButton: 'Add payment card',
cardNextPayment: 'Your next payment date is',
cardNextPayment: ({nextPaymentDate}) => `Your next payment date is ${nextPaymentDate}.`,
cardEnding: ({cardNumber}) => `Card ending in ${cardNumber}`,
cardInfo: ({name, expiration, currency}) => `Name: ${name}, Expiration: ${expiration}, Currency: ${currency}`,
changeCard: 'Change payment card',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3721,7 +3721,7 @@ export default {
title: 'Pago',
subtitle: 'Añade una tarjeta de pago para abonar tu suscripción a Expensify',
addCardButton: 'Añade tarjeta de pago',
cardNextPayment: 'Your next payment date is',
cardNextPayment: ({nextPaymentDate}) => `Tu próxima fecha de pago es ${nextPaymentDate}.`,
cardEnding: ({cardNumber}) => `Tarjeta terminada en ${cardNumber}`,
cardInfo: ({name, expiration, currency}) => `Nombre: ${name}, Expiración: ${expiration}, Moneda: ${currency}`,
changeCard: 'Cambiar tarjeta de pago',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,27 @@ import ONYXKEYS from '@src/ONYXKEYS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import CardSectionActions from './CardSectionActions';
import CardSectionDataEmpty from './CardSectionDataEmpty';
import getNextBillingDate from './utils';

function CardSection() {
const {translate, preferredLocale} = useLocalize();
const styles = useThemeStyles();
const theme = useTheme();
const [fundList] = useOnyx(ONYXKEYS.FUND_LIST);
const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION);

const defaultCard = useMemo(() => Object.values(fundList ?? {}).find((card) => card.isDefault), [fundList]);

const cardMonth = useMemo(() => DateUtils.getMonthNames(preferredLocale)[(defaultCard?.accountData?.cardMonth ?? 1) - 1], [defaultCard?.accountData?.cardMonth, preferredLocale]);

const nextPaymentDate = !isEmptyObject(privateSubscription) ? getNextBillingDate() : undefined;

const sectionSubtitle = defaultCard && !!nextPaymentDate ? translate('subscription.cardSection.cardNextPayment', {nextPaymentDate}) : translate('subscription.cardSection.subtitle');

return (
<Section
title={translate('subscription.cardSection.title')}
subtitle={translate('subscription.cardSection.subtitle')}
subtitle={sectionSubtitle}
isCentralPane
titleStyles={styles.textStrong}
subtitleMuted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import ThreeDotsMenu from '@components/ThreeDotsMenu';
import type ThreeDotsMenuProps from '@components/ThreeDotsMenu/types';
import useLocalize from '@hooks/useLocalize';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Navigation from '@navigation/Navigation';
import type {AnchorPosition} from '@styles/index';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

const anchorAlignment = {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
Expand All @@ -24,12 +26,12 @@ function CardSectionActions() {
{
icon: Expensicons.CreditCard,
text: translate('subscription.cardSection.changeCard'),
onSelected: () => {}, // TODO: update with navigation to "add card" screen (https://github.com/Expensify/App/issues/38621)
onSelected: () => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD),
},
{
icon: Expensicons.MoneyCircle,
text: translate('subscription.cardSection.changeCurrency'),
onSelected: () => {}, // TODO: update with navigation to "change currency" screen (https://github.com/Expensify/App/issues/38621)
onSelected: () => {}, // TODO: update with navigation to "change currency" screen (https://github.com/Expensify/App/issues/38629)
},
],
[translate],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from 'react';
import Button from '@components/Button';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@navigation/Navigation';
import ROUTES from '@src/ROUTES';

function CardSectionDataEmpty() {
const {translate} = useLocalize();
Expand All @@ -10,7 +12,7 @@ function CardSectionDataEmpty() {
return (
<Button
text={translate('subscription.cardSection.addCardButton')}
onPress={() => {}} // TODO: update with navigation to "add card" screen (https://github.com/Expensify/App/issues/38621)
onPress={() => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD)}
style={styles.w100}
success
large
Expand Down
17 changes: 17 additions & 0 deletions src/pages/settings/Subscription/CardSection/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {addMonths, format, startOfMonth} from 'date-fns';
import CONST from '@src/CONST';

/**
* Get the next billing date.
*
* @returns - The next billing date in 'yyyy-MM-dd' format.
*/
function getNextBillingDate(): string {
const today = new Date();

const nextBillingDate = startOfMonth(addMonths(today, 1));

return format(nextBillingDate, CONST.DATE.MONTH_DAY_YEAR_FORMAT);
}

export default getNextBillingDate;
37 changes: 37 additions & 0 deletions tests/unit/CardsSectionUtilsTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import getNextBillingDate from '@src/pages/settings/Subscription/CardSection/utils';

describe('getNextBillingDate', () => {
beforeAll(() => {
jest.useFakeTimers();
// Month is zero indexed, so this is July 5th 2024
jest.setSystemTime(new Date(2024, 6, 5));
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB, maybe we can add a comment that month is zero-indexed? It tripped me up for a sec 😄

Copy link
Contributor

@JKobrynski JKobrynski Jun 18, 2024

Choose a reason for hiding this comment

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

Done!

CC: @blimpich

Copy link
Contributor

@blimpich blimpich Jun 19, 2024

Choose a reason for hiding this comment

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

Where is the comment? I think what Amy meant was something like this

Suggested change
jest.setSystemTime(new Date(2024, 6, 5));
// Month is zero indexed, so this is July 5th 2024
jest.setSystemTime(new Date(2024, 6, 5));

});

afterAll(() => {
jest.useRealTimers();
});

it('should return the next billing date when initial date is valid', () => {
const expectedNextBillingDate = 'August 1, 2024';

expect(getNextBillingDate()).toEqual(expectedNextBillingDate);
});

it('should handle end-of-month edge cases correctly', () => {
const nextBillingDate = getNextBillingDate();
const expectedNextBillingDate = 'August 1, 2024';
expect(nextBillingDate).toBe(expectedNextBillingDate);
});

it('should handle date when it at the current month', () => {
const nextBillingDate = getNextBillingDate();
const expectedNextBillingDate = 'August 1, 2024';
expect(nextBillingDate).toBe(expectedNextBillingDate);
});

it('should return the next billing date when initial date is invalid', () => {
const expectedNextBillingDate = 'August 1, 2024';

expect(getNextBillingDate()).toEqual(expectedNextBillingDate);
});
});
Loading