Skip to content

Commit

Permalink
feat(user): enhance pending offer confirmation and on the payments page
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristiaanScheermeijer committed Jun 1, 2023
1 parent 69bd60d commit 2a598b7
Show file tree
Hide file tree
Showing 30 changed files with 174 additions and 34 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ module.exports = {
],
// Not needed in React 17
'react/react-in-jsx-scope': 'off',
'import/no-named-as-default-member': 'off',
},
overrides: [
{
Expand Down
6 changes: 6 additions & 0 deletions public/locales/en/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
"redeem_coupon": "Redeem coupon",
"start_watching": "Start watching ({{countdown}})",
"total_price": "Total",
"upgrade_error": "Failed to update subscription!",
"upgrade_error_message": "Due to an unknown error the update has failed. Please contact an administrator.",
"upgrade_pending": "Subscription updated!",
"upgrade_pending_message": "Your subscription will be updated after the next billing date.",
"upgrade_success": "Subscription updated!",
"upgrade_success_message": "Your subscription is updated and can be used immediately.",
"waiting_for_payment": "Waiting for payment",
"welcome_description": "Thank you for subscribing to {{siteName}}. Please enjoy all our content.",
"welcome_title": "Welcome to {{siteName}}",
Expand Down
1 change: 1 addition & 0 deletions public/locales/en/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"no_transactions": "No transactions",
"other": "other",
"payment_method": "Payment method",
"pending_offer_switch": "Will update to a \"{{title}}\" after the next billing date",
"price_payed_with": "{{price}} payed with {{method}}",
"price_payed_with_card": "Price payed with card",
"renew_subscription": "Renew subscription",
Expand Down
6 changes: 6 additions & 0 deletions public/locales/es/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
"redeem_coupon": "Canjear cupón",
"start_watching": "Comenzar a ver ({{countdown}})",
"total_price": "Total",
"upgrade_error": "No se pudo actualizar la suscripción!",
"upgrade_error_message": "Debido a un error desconocido, la actualización ha fallado. Póngase en contacto con un administrador.",
"upgrade_pending": "¡Suscripción actualizada!",
"upgrade_pending_message": "Su suscripción se actualizará después de la próxima fecha de facturación.",
"upgrade_success": "¡Suscripción actualizada!",
"upgrade_success_message": "Su suscripción se actualiza y se puede utilizar de inmediato.",
"waiting_for_payment": "Esperando el pago",
"welcome_description": "Gracias por suscribirte a {{siteName}}. Disfruta de todo nuestro contenido.",
"welcome_title": "Bienvenido/a a {{siteName}}",
Expand Down
1 change: 1 addition & 0 deletions public/locales/es/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"no_transactions": "No hay transacciones",
"other": "otro",
"payment_method": "Método de pago",
"pending_offer_switch": "Se actualizará a \"{{title}}\" después de la próxima fecha de facturación",
"price_payed_with": "{{price}} pagado con {{method}}",
"price_payed_with_card": "Precio pagado con tarjeta",
"renew_subscription": "Renovar suscripción",
Expand Down
2 changes: 1 addition & 1 deletion scripts/i18next/generate.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* This script generates all i18next resource entries.
*/

const fs = require('fs');
const path = require('path');

const prettier = require('prettier');

const localesPath = './public/locales';
Expand Down
4 changes: 2 additions & 2 deletions src/components/Favorites/Favorites.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

.header {
display: flex;
margin-bottom: variables.$base-spacing * 1.5;
margin-bottom: calc(#{variables.$base-spacing} * 1.5);

> h3 {
margin-right: variables.$base-spacing * 1.5;
margin-right: calc(#{variables.$base-spacing} * 1.5);
font-weight: var(--body-font-weight-bold);
font-size: 34px;
font-style: normal;
Expand Down
8 changes: 4 additions & 4 deletions src/components/Header/Header.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

.header {
height: variables.$header-height;
padding: 10px variables.$base-spacing * 2;
padding: 10px calc(#{variables.$base-spacing} * 2);
color: var(--header-contrast-color, variables.$white);
background: var(--header-background, transparent);

Expand Down Expand Up @@ -80,7 +80,7 @@
height: 36px;
min-height: 36px;
margin: 0 6px;
padding: 0 variables.$base-spacing / 2;
padding: 0 calc(#{variables.$base-spacing} / 2);
font-weight: var(--body-font-weight-bold);
font-size: 18px;
}
Expand All @@ -100,7 +100,7 @@
}

.actionButton {
margin-left: variables.$base-spacing / 2;
margin-left: calc(#{variables.$base-spacing} / 2);
}

//
Expand Down Expand Up @@ -149,7 +149,7 @@
@include responsive.mobile-and-tablet() {
.header {
height: variables.$header-height-mobile;
padding: 10px variables.$base-spacing * 2;
padding: 10px calc(#{variables.$base-spacing} * 2);
}

.menu {
Expand Down
4 changes: 2 additions & 2 deletions src/components/MenuButton/MenuButton.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
transition: background 0.1s ease;

&.small {
padding: 0 variables.$base-spacing * 1.5;
padding: 0 calc(#{variables.$base-spacing} * 1.5);
font-size: 16px;
> .startIcon {
margin-right: variables.$base-spacing * 1.5;
margin-right: calc(#{variables.$base-spacing} * 1.5);
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/components/Payment/Payment.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
}
}

.pendingSwitch {
display: inline-block;
margin-top: variables.$base-spacing;
}

.transactionDetails {
display: flex;
justify-content: center;
Expand All @@ -41,7 +46,7 @@

.cardDetails {
display: flex;
margin-top: variables.$base-spacing * 2;
margin-top: calc(#{variables.$base-spacing} * 2);
}

.paypal {
Expand Down
1 change: 1 addition & 0 deletions src/components/Payment/Payment.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('<Payment>', () => {
transactions={transactions as Transaction[]}
activeSubscription={subscription as Subscription}
activePaymentDetail={paymentDetail as PaymentDetail}
pendingOffer={null}
canUpdatePaymentMethod={false}
showAllTransactions={false}
isLoading={false}
Expand Down
6 changes: 6 additions & 0 deletions src/components/Payment/Payment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { addQueryParam } from '#src/utils/location';
import type { PaymentDetail, Subscription, Transaction } from '#types/subscription';
import type { AccessModel } from '#types/Config';
import PayPal from '#src/icons/PayPal';
import type { Offer } from '#types/checkout';

const VISIBLE_TRANSACTIONS = 4;

Expand All @@ -26,6 +27,7 @@ type Props = {
activePaymentDetail: PaymentDetail | null;
transactions: Transaction[] | null;
customer: Customer;
pendingOffer: Offer | null;
isLoading: boolean;
offerSwitchesAvailable: boolean;
onShowReceiptClick: (transactionId: string) => void;
Expand All @@ -43,6 +45,7 @@ const Payment = ({
accessModel,
activePaymentDetail,
activeSubscription,
pendingOffer,
transactions,
customer,
isLoading,
Expand Down Expand Up @@ -110,6 +113,9 @@ const Payment = ({
{activeSubscription.status === 'active' && !isGrantedSubscription
? t('user:payment.next_billing_date_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) })
: t('user:payment.subscription_expires_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) })}
{pendingOffer && (
<span className={styles.pendingSwitch}>{t('user:payment.pending_offer_switch', { title: getTitle(pendingOffer.period) })}</span>
)}
</p>
{!isGrantedSubscription && (
<p className={styles.price}>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Spinner/Spinner.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
.buffer div {
position: absolute;
display: block;
width: variables.$base-spacing * 4;
height: variables.$base-spacing * 4;
width: calc(#{variables.$base-spacing} * 4);
height: calc(#{variables.$base-spacing} * 4);
margin: 8px;
border: 4px solid variables.$white;
border-color: variables.$white transparent transparent transparent;
Expand Down
13 changes: 13 additions & 0 deletions src/components/UpgradeSubscription/UpgradeSubscription.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@use 'src/styles/variables';
@use 'src/styles/theme';

.title {
display: flex;
align-items: center;
font-weight: var(--body-font-weight-bold);
font-size: 24px;
}

.message {
font-size: 16px;
}
37 changes: 37 additions & 0 deletions src/components/UpgradeSubscription/UpgradeSubscription.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import styles from './UpgradeSubscription.module.scss';

import Button from '#components/Button/Button';

type Props = {
type: 'error' | 'success' | 'pending';
onCloseButtonClick: () => void;
};

const UpgradeSubscription: React.FC<Props> = ({ type, onCloseButtonClick }: Props) => {
const { t } = useTranslation('account');

// these comments exist for extracting dynamic i18n keys
// t('account:checkout.upgrade_success');
// t('account:checkout.upgrade_success_message');
// t('account:checkout.upgrade_pending');
// t('account:checkout.upgrade_pending_message');
// t('account:checkout.upgrade_error');
// t('account:checkout.upgrade_error_message');
const title = t(`checkout.upgrade_${type}`);
const message = t(`checkout.upgrade_${type}_message`);

return (
<div>
<h2 className={styles.title}>{title}</h2>
<p className={styles.message}>{message}</p>
<div>
<Button label={t('checkout.close')} onClick={onCloseButtonClick} color="primary" variant="contained" size="large" fullWidth />
</div>
</div>
);
};

export default UpgradeSubscription;
7 changes: 7 additions & 0 deletions src/containers/AccountModal/AccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import FinalizePayment from '#components/FinalizePayment/FinalizePayment';
import WaitingForPayment from '#components/WaitingForPayment/WaitingForPayment';
import UpdatePaymentMethod from '#src/containers/UpdatePaymentMethod/UpdatePaymentMethod';
import useEventCallback from '#src/hooks/useEventCallback';
import UpgradeSubscription from '#components/UpgradeSubscription/UpgradeSubscription';

const PUBLIC_VIEWS = ['login', 'create-account', 'forgot-password', 'reset-password', 'send-confirmation', 'edit-password'];

Expand Down Expand Up @@ -80,6 +81,12 @@ const AccountModal = () => {
return <ChooseOffer />;
case 'upgrade-subscription':
return <ChooseOffer />;
case 'upgrade-subscription-error':
return <UpgradeSubscription type="error" onCloseButtonClick={closeHandler} />;
case 'upgrade-subscription-success':
return <UpgradeSubscription type="success" onCloseButtonClick={closeHandler} />;
case 'upgrade-subscription-pending':
return <UpgradeSubscription type="pending" onCloseButtonClick={closeHandler} />;
case 'checkout':
return <Checkout />;
case 'payment-error':
Expand Down
20 changes: 14 additions & 6 deletions src/containers/AccountModal/forms/ChooseOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useAccountStore } from '#src/stores/AccountStore';
import type { ChooseOfferFormData } from '#types/account';
import type { Subscription } from '#types/subscription';
import useEventCallback from '#src/hooks/useEventCallback';
import { logDev } from '#src/utils/common';

const determineSwitchDirection = (subscription: Subscription | null) => {
const currentPeriod = subscription?.period;
Expand Down Expand Up @@ -53,8 +54,8 @@ const ChooseOffer = () => {
navigate(removeQueryParam(location, 'u'), { replace });
});

const toCheckout = useEventCallback(() => {
navigate(addQueryParam(location, 'u', 'checkout'));
const updateAccountModal = useEventCallback((modal: string) => {
navigate(addQueryParam(location, 'u', modal));
});

const chooseOfferSubmitHandler: UseFormOnSubmitHandler<ChooseOfferFormData> = useCallback(
Expand All @@ -67,18 +68,25 @@ const ChooseOffer = () => {
const targetOffer = offerSwitches.find((offer) => offer.offerId === offerId);
const targetOfferId = targetOffer?.offerId || '';

await switchSubscription(targetOfferId, determineSwitchDirection(subscription));
closeModal();
try {
await switchSubscription(targetOfferId, determineSwitchDirection(subscription));
const isPendingSwitch = !!useAccountStore.getState().pendingOffer;

updateAccountModal(isPendingSwitch ? 'upgrade-subscription-pending' : 'upgrade-subscription-success');
} catch (error: unknown) {
logDev('Error occurred while upgrading subscription', error);
updateAccountModal('upgrade-subscription-error');
}
} else {
const selectedOffer = availableOffers.find((offer) => offer.offerId === offerId) || null;

setOffer(selectedOffer);
updateOffer(selectedOffer);
setSubmitting(false);
toCheckout();
updateAccountModal('checkout');
}
},
[availableOffers, closeModal, isOfferSwitch, offerSwitches, offersDict, setOffer, subscription, t, toCheckout, updateOffer],
[availableOffers, isOfferSwitch, offerSwitches, offersDict, setOffer, subscription, t, updateAccountModal, updateOffer],
);

const { handleSubmit, handleChange, setValue, values, errors, submitting } = useForm(initialValues, chooseOfferSubmitHandler, validationSchema);
Expand Down
2 changes: 1 addition & 1 deletion src/containers/ShelfList/ShelfList.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
}

@include responsive.tablet-only() {
padding: 0 variables.$base-spacing * 2;
padding: 0 calc(#{variables.$base-spacing} * 2);

&.featured {
padding: 24px 10%;
Expand Down
1 change: 0 additions & 1 deletion src/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const initI18n = async () => {
throw new Error(`The default language is not enabled: ${defaultLanguage}`);
}

// eslint-disable-next-line import/no-named-as-default-member
await i18next
.use(I18NextHttpBackend)
.use(LanguageDetector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@use 'src/styles/mixins/responsive';

.playlist {
margin: 0 variables.$base-spacing * 4;
margin: 0 calc(#{variables.$base-spacing} * 4);
color: var(--primary-color);
font-family: var(--body-alt-font-family);
text-align: center;
Expand All @@ -17,11 +17,11 @@
}

@include responsive.tablet-only() {
margin: 0 variables.$base-spacing * 2;
margin: 0 calc(#{variables.$base-spacing} * 2);
}

@include responsive.desktop-small-only() {
margin: 0 variables.$base-spacing * 3;
margin: 0 calc(#{variables.$base-spacing} * 3);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/pages/Search/Search.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@use 'src/styles/mixins/responsive';

.search {
margin: 0 variables.$base-spacing * 4;
margin: 0 calc(#{variables.$base-spacing} * 4);
color: var(--primary-color);
font-family: var(--body-alt-font-family);
text-align: center;
Expand All @@ -17,11 +17,11 @@
}

@include responsive.tablet-only() {
margin: 0 variables.$base-spacing * 2;
margin: 0 calc(#{variables.$base-spacing} * 2);
}

@include responsive.desktop-small-only() {
margin: 0 variables.$base-spacing * 3;
margin: 0 calc(#{variables.$base-spacing} * 3);
}
}

Expand Down
Loading

0 comments on commit 2a598b7

Please sign in to comment.