Skip to content

Commit c872efd

Browse files
committed
feat(payments,shared): Update cart with saved tax location
1 parent 4653f03 commit c872efd

9 files changed

Lines changed: 93 additions & 29 deletions

File tree

apps/payments/next/app/[locale]/[offeringId]/[interval]/checkout/[cartId]/layout.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export default async function RootLayout({
5656
cartDataPromise,
5757
sessionPromise,
5858
]);
59+
const purchaseDetails =
60+
cms.defaultPurchase.purchaseDetails.localizations.at(0) ||
61+
cms.defaultPurchase.purchaseDetails;
5962
return (
6063
<MetricsWrapper cart={cart}>
6164
{session?.user?.email && (
@@ -79,43 +82,40 @@ export default async function RootLayout({
7982
listAmount={cart.upcomingInvoicePreview.listAmount}
8083
/>
8184
}
82-
purchaseDetails={
83-
cms.defaultPurchase.purchaseDetails.localizations.at(0) ||
84-
cms.defaultPurchase.purchaseDetails
85-
}
85+
purchaseDetails={purchaseDetails}
8686
>
8787
<Details
8888
l10n={l10n}
8989
interval={cart.interval}
9090
invoice={cart.upcomingInvoicePreview}
91-
purchaseDetails={
92-
cms.defaultPurchase.purchaseDetails.localizations.at(0) ||
93-
cms.defaultPurchase.purchaseDetails
94-
}
91+
purchaseDetails={purchaseDetails}
9592
/>
9693
</PurchaseDetails>
97-
<CouponForm
94+
<SelectTaxLocation
9895
cartId={cart.id}
9996
cartVersion={cart.version}
100-
promoCode={cart.couponCode}
101-
readOnly={false}
102-
/>
103-
<SelectTaxLocation
97+
cmsCountries={cms.countries}
10498
locale={locale.substring(0, 2)}
99+
productName={purchaseDetails.productName}
105100
unsupportedLocations={config.subscriptionsUnsupportedLocations}
106101
countryCode={cart.taxAddress?.countryCode}
107102
postalCode={cart.taxAddress?.postalCode}
108103
/>
104+
<CouponForm
105+
cartId={cart.id}
106+
cartVersion={cart.version}
107+
promoCode={cart.couponCode}
108+
readOnly={false}
109+
/>
109110
</section>
110111

111112
<div className="bg-white rounded-b-lg shadow-sm shadow-grey-300 border-t-0 mb-6 pt-4 px-4 pb-14 rounded-t-lg text-grey-600 tablet:clip-shadow tablet:rounded-t-none desktop:px-12 desktop:pb-12">
112113
{children}
113114
<TermsAndPrivacy
114115
l10n={l10n}
115116
{...cart}
117+
{...purchaseDetails}
116118
{...(cms.commonContent.localizations.at(0) || cms.commonContent)}
117-
{...(cms.defaultPurchase.purchaseDetails.localizations.at(0) ||
118-
cms.defaultPurchase.purchaseDetails)}
119119
contentServerUrl={config.contentServerUrl}
120120
showFXALinks={true}
121121
/>

libs/payments/ui/src/lib/client/components/SelectTaxLocation/en.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ select-tax-location-country-code-label = Country
99
select-tax-location-country-code-placeholder = Select your country
1010
select-tax-location-error-missing-country-code = Please select your country
1111
12+
# $productName (String) - The name of the product to be downloaded, e.g. Mozilla VPN
13+
select-tax-location-product-not-available = { $productName } is not available in this location.
14+
1215
select-tax-location-postal-code-label = Postal Code
1316
select-tax-location-postal-code =
1417
.placeholder = Enter your postal code

libs/payments/ui/src/lib/client/components/SelectTaxLocation/index.tsx

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as Form from '@radix-ui/react-form';
99
import countries from 'i18n-iso-countries';
1010
import { useEffect, useState } from 'react';
1111
import { ButtonVariant } from '@fxa/payments/ui';
12-
import { validatePostalCode } from '@fxa/payments/ui/actions';
12+
import { updateCartAction, validatePostalCode } from '@fxa/payments/ui/actions';
1313
import { SubmitButton } from '../SubmitButton';
1414

1515
interface CollapsedProps {
@@ -37,7 +37,9 @@ const Collapsed = ({
3737
variant={ButtonVariant.Secondary}
3838
data-testid="tax-location-edit-button"
3939
>
40-
<Localized id="select-tax-location-edit-button">Edit</Localized>
40+
<Localized id="select-tax-location-edit-button">
41+
<p>Edit</p>
42+
</Localized>
4143
</SubmitButton>
4244
</Form.Submit>
4345
</span>
@@ -55,15 +57,19 @@ const Collapsed = ({
5557
};
5658

5759
interface ExpandedProps {
60+
cmsCountryCodes: string[];
5861
locale: string;
62+
productName: string;
5963
unsupportedLocations: string;
6064
countryCode: string | undefined;
6165
postalCode: string | undefined;
6266
saveAction: (countryCode: string, postalCode: string) => void;
6367
}
6468

6569
const Expanded = ({
70+
cmsCountryCodes,
6671
locale,
72+
productName,
6773
unsupportedLocations,
6874
countryCode,
6975
postalCode,
@@ -82,6 +88,7 @@ const Expanded = ({
8288
[key: string]: boolean;
8389
}>({
8490
missingCountryCode: false,
91+
productNotAvailable: false,
8592
unsupportedCountry: false,
8693
invalidPostalCode: false,
8794
locationNotUpdated: false,
@@ -113,6 +120,7 @@ const Expanded = ({
113120
setServerErrors((prev) => ({
114121
...prev,
115122
missingCountryCode: false,
123+
productNotAvailable: false,
116124
unsupportedCountries: false,
117125
}));
118126

@@ -122,10 +130,35 @@ const Expanded = ({
122130

123131
setSelectedCountryCode(countryCode);
124132

125-
if (unsupportedLocations.includes(countryCode)) {
126-
setServerErrors((prev) => ({ ...prev, unsupportedCountry: true }));
133+
// If the selected location is not supported per TOS, it is not necessary to
134+
// also inform the customer that the product is not available in their location.
135+
if (
136+
unsupportedLocations.includes(countryCode) &&
137+
!cmsCountryCodes.includes(countryCode)
138+
) {
139+
setServerErrors((prev) => ({
140+
...prev,
141+
productNotAvailable: false,
142+
unsupportedCountry: true,
143+
}));
144+
} else if (unsupportedLocations.includes(countryCode)) {
145+
setServerErrors((prev) => ({
146+
...prev,
147+
productNotAvailable: false,
148+
unsupportedCountry: true,
149+
}));
150+
} else if (!cmsCountryCodes.includes(countryCode)) {
151+
setServerErrors((prev) => ({
152+
...prev,
153+
productNotAvailable: true,
154+
unsupportedCountry: false,
155+
}));
127156
} else {
128-
setServerErrors((prev) => ({ ...prev, unsupportedCountry: false }));
157+
setServerErrors((prev) => ({
158+
...prev,
159+
productNotAvailable: false,
160+
unsupportedCountry: false,
161+
}));
129162
}
130163
};
131164

@@ -206,6 +239,15 @@ const Expanded = ({
206239
</p>
207240
</Localized>
208241
</Form.Message>
242+
{serverErrors.productNotAvailable && (
243+
<Form.Message>
244+
<Localized id="select-tax-location-product-not-available">
245+
<p className="mt-1 text-alert-red" role="alert">
246+
{productName} is not available in this location.
247+
</p>
248+
</Localized>
249+
</Form.Message>
250+
)}
209251
{serverErrors.unsupportedCountry && (
210252
<Form.Message>
211253
<Localized id="next-location-unsupported">
@@ -282,22 +324,32 @@ const Expanded = ({
282324
data-testid="tax-location-save-button"
283325
variant={ButtonVariant.Secondary}
284326
>
285-
<Localized id="select-tax-location-save-button">Save</Localized>
327+
<Localized id="select-tax-location-save-button">
328+
<p>Save</p>
329+
</Localized>
286330
</SubmitButton>
287331
</Form.Submit>
288332
</Form.Root>
289333
);
290334
};
291335

292336
interface SelectTaxLocationProps {
337+
cartId: string;
338+
cartVersion: number;
339+
cmsCountries: string[];
293340
locale: string;
341+
productName: string;
294342
unsupportedLocations: string;
295343
countryCode: string | undefined;
296344
postalCode: string | undefined;
297345
}
298346

299347
export function SelectTaxLocation({
348+
cartId,
349+
cartVersion,
350+
cmsCountries,
300351
locale,
352+
productName,
301353
unsupportedLocations,
302354
countryCode,
303355
postalCode,
@@ -306,6 +358,7 @@ export function SelectTaxLocation({
306358
!countryCode || !postalCode
307359
);
308360
const [alertStatus, setAlertStatus] = useState<boolean>(false);
361+
const cmsCountryCodes = cmsCountries.map((country) => country.slice(0, 2));
309362

310363
return (
311364
<div
@@ -318,15 +371,19 @@ export function SelectTaxLocation({
318371

319372
{expanded ? (
320373
<Expanded
374+
cmsCountryCodes={cmsCountryCodes}
321375
locale={locale}
376+
productName={productName}
322377
unsupportedLocations={unsupportedLocations}
323378
countryCode={countryCode}
324379
postalCode={postalCode}
325-
saveAction={(countryCode: string, postalCode: string) => {
380+
saveAction={async (countryCode: string, postalCode: string) => {
326381
setExpanded(false);
327382

328383
// Call function to save to Cart
329-
// await saveTaxLocationAction(countryCode, postalCode);
384+
await updateCartAction(cartId, cartVersion, {
385+
taxAddress: { countryCode, postalCode },
386+
});
330387
setAlertStatus(true);
331388
}}
332389
/>

libs/shared/cms/codegen.config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { CodegenConfig } from '@graphql-codegen/cli';
22

3-
const STRAPI_GRAPHQL_API_URL = process.env.STRAPI_GRAPHQL_API_URL;
4-
const STRAPI_API_KEY = process.env.STRAPI_API_KEY;
3+
const STRAPI_GRAPHQL_API_URL =
4+
process.env.STRAPI_CLIENT_CONFIG__GRAPHQL_API_URI;
5+
const STRAPI_API_KEY = process.env.STRAPI_CLIENT_CONFIG__API_KEY;
56

67
if (!STRAPI_GRAPHQL_API_URL || !STRAPI_API_KEY) {
78
throw new Error('Please provide a valid Strapi API URL and API key');

0 commit comments

Comments
 (0)