Skip to content

Commit

Permalink
Merge pull request #17846 from mozilla/FXA-7580
Browse files Browse the repository at this point in the history
feat(payments-next): Sign-in/sign-up form
  • Loading branch information
xlisachan authored Oct 18, 2024
2 parents 15e3d06 + 05906c1 commit 5bf120e
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## Page
next-new-user-step-1-2 = 1. Create a { -product-mozilla-account }
next-new-user-sign-in-link-2 = Already have a { -product-mozilla-account }? <a>Sign in</a>
checkout-signin-or-create = 1. Sign in or create a { -product-mozilla-account }
checkout-create-account = Create a { -product-mozilla-account }
next-continue-with-google-button = Continue with { -brand-google }
next-continue-with-apple-button = Continue with { -brand-apple }
next-payment-confirm-with-legal-links-static-3 = I authorize { -brand-mozilla } to charge my payment method for the amount shown, according to <termsOfServiceLink>Terms of Service</termsOfServiceLink> and <privacyNoticeLink>Privacy Notice</privacyNoticeLink>, until I cancel my subscription.
next-payment-method-header = Choose your payment method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import { BaseButton, ButtonVariant, PaymentSection } from '@fxa/payments/ui';
import { getApp, SupportedPages, CheckoutParams, } from '@fxa/payments/ui/server';
import Image from 'next/image';
import {
BaseButton,
ButtonVariant,
PaymentSection,
SignInForm,
} from '@fxa/payments/ui';
import {
getApp,
CheckoutParams,
SupportedPages,
} from '@fxa/payments/ui/server';
import { getCartOrRedirectAction } from '@fxa/payments/ui/actions';
import AppleLogo from '@fxa/shared/assets/images/apple-logo.svg';
import GoogleLogo from '@fxa/shared/assets/images/google-logo.svg';
import { DEFAULT_LOCALE } from '@fxa/shared/l10n';
import {
getFakeCartData,
getCMSContent,
} from 'apps/payments/next/app/_lib/apiClient';
import { auth, signIn } from 'apps/payments/next/auth';
import { auth } from 'apps/payments/next/auth';

export const dynamic = 'force-dynamic';

Expand Down Expand Up @@ -46,74 +57,37 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
<>
<h2 className="font-semibold text-grey-600 text-lg mt-10">
{l10n.getString(
'next-new-user-step-1-2',
'1. Create a Mozilla account'
'checkout-signin-or-create',
'1. Sign in or create a Mozilla account'
)}
</h2>

<form
action={async () => {
'use server';
await signIn('fxa');
}}
>
<p className="text-grey-400 text-sm mt-2 pb-4 ">
{l10n.getFragmentWithSource(
'next-new-user-sign-in-link-2',
{
elems: {
a: (
<button className="underline hover:text-grey-400">
Sign in
</button>
),
},
},
<>
Already have a Mozilla account?&nbsp;
<button className="underline hover:text-grey-400">
Sign in
</button>
</>
)}
</p>
</form>
<SignInForm
newsletterLabel={cms.commonContent.newsletterLabelTextCode}
/>

<h3 className="font-semibold text-grey-600 text-start">
{l10n.getString(
'checkout-create-account',
'Create a Mozilla account'
)}
</h3>

<div className="p-6 text-center">
{/**
Temporary Content. This will be replaced in M3b by the Passwordless
email signup form.
*/}
<p className="mb-6">{`Current cart email: ${cart.email}`}</p>
<form
action={async (formData: FormData) => {
'use server';
const email =
formData.get('email')?.toString() || '[email protected]';
await getApp().getActionsService().updateCart({
cartId: cart.id,
version: cart.version,
cartDetails: {
email,
},
});
revalidatePath('/');
}}
>
<input
type="email"
name="email"
className="w-full border rounded-md border-black/30 p-3 placeholder:text-grey-500 placeholder:font-normal focus:border focus:!border-black/30 focus:!shadow-[0_0_0_3px_rgba(10,132,255,0.3)] focus-visible:outline-none data-[invalid=true]:border-alert-red data-[invalid=true]:text-alert-red data-[invalid=true]:shadow-inputError"
/>
<BaseButton
className="mt-10 w-full"
type="submit"
variant={ButtonVariant.Primary}
>
{' '}
Set email
</BaseButton>
</form>
<div className="flex flex-col gap-4 mt-6 mb-10 desktop:flex-row desktop:items-center desktop:justify-center">
<BaseButton variant={ButtonVariant.ThirdParty}>
<Image src={GoogleLogo} alt="" />
{l10n.getString(
'next-continue-with-google-button',
'Continue with Google'
)}
</BaseButton>
<BaseButton variant={ButtonVariant.ThirdParty}>
<Image src={AppleLogo} alt="" />
{l10n.getString(
'next-continue-with-apple-button',
'Continue with Apple'
)}
</BaseButton>
</div>

<hr className="mx-auto w-full border-grey-200" />
Expand Down
1 change: 1 addition & 0 deletions libs/payments/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from './lib/client/components/CheckoutCheckbox';
export * from './lib/client/components/CouponForm';
export * from './lib/client/components/PaymentSection';
export * from './lib/client/components/PurchaseDetails';
export * from './lib/client/components/SignInForm';
export * from './lib/client/components/SubmitButton';
export * from './lib/client/components/LoadingSpinner';
export * from './lib/client/components/MetricsWrapper';
Expand Down
11 changes: 8 additions & 3 deletions libs/payments/ui/src/lib/client/components/BaseButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
export enum ButtonVariant {
Primary,
Secondary,
ThirdParty,
}

interface BaseButtonProps
Expand All @@ -17,17 +18,21 @@ export function BaseButton({ children, variant, ...props }: BaseButtonProps) {
let variantStyles = '';
switch (variant) {
case ButtonVariant.Primary:
variantStyles = 'bg-blue-500 hover:bg-blue-700 text-white';
variantStyles = 'bg-blue-500 font-semibold hover:bg-blue-700 text-white';
break;
case ButtonVariant.Secondary:
variantStyles = 'bg-grey-100 hover:bg-grey-200 text-black';
variantStyles = 'bg-grey-100 font-semibold hover:bg-grey-200 text-black';
break;
case ButtonVariant.ThirdParty:
variantStyles =
'w-full bg-transparent border border-grey-300 font-normal text-black hover:border-black';
break;
}

return (
<button
{...props}
className={`flex items-center justify-center font-semibold h-12 rounded-md p-4 z-10 aria-disabled:relative aria-disabled:after:absolute aria-disabled:after:content-[''] aria-disabled:after:top-0 aria-disabled:after:left-0 aria-disabled:after:w-full aria-disabled:after:h-full aria-disabled:after:bg-white aria-disabled:after:opacity-50 aria-disabled:after:z-30 aria-disabled:border-none ${props.className} ${variantStyles}`}
className={`flex items-center justify-center h-12 rounded-md p-4 z-10 cursor-pointer aria-disabled:relative aria-disabled:after:absolute aria-disabled:after:content-[''] aria-disabled:after:top-0 aria-disabled:after:left-0 aria-disabled:after:w-full aria-disabled:after:h-full aria-disabled:after:bg-white aria-disabled:after:opacity-50 aria-disabled:after:z-30 aria-disabled:border-none ${props.className} ${variantStyles}`}
>
{children}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function CheckoutForm({

return (
<Form.Root
aria-label="Checkout form"
onSubmit={submitHandler}
onChange={() => {
engageGlean();
Expand Down
7 changes: 7 additions & 0 deletions libs/payments/ui/src/lib/client/components/SignInForm/en.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
signin-form-continue-button = Continue
signin-form-email-input = Enter your email
next-new-user-subscribe-product-updates-mdnplus = I’d like to receive product news and updates from { -product-mdn-plus } and { -brand-mozilla }
next-new-user-subscribe-product-updates-mozilla = I’d like to receive product news and updates from { -brand-mozilla }
next-new-user-subscribe-product-updates-snp = I’d like to receive security and privacy news and updates from { -brand-mozilla }
next-new-user-subscribe-product-assurance = We only use your email to create your account. We will never sell it to a third party.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
107 changes: 107 additions & 0 deletions libs/payments/ui/src/lib/client/components/SignInForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use client';

import { Localized } from '@fluent/react';
import * as Form from '@radix-ui/react-form';
import Image from 'next/image';
import { BaseButton, ButtonVariant } from '@fxa/payments/ui';
import shieldIcon from './images/shield.svg';

const DEFAULT_NEWSLETTER_STRING_ID =
'next-new-user-subscribe-product-updates-mozilla';

/**
* The newsletter string is a configurable field. This function returns the correct
* localization string ID and fallback text for the different newsletter string ID options.
*/
function getNewsletterStringInfo(newsletterLabelTextCode: string) {
switch (newsletterLabelTextCode) {
case 'mdnplus':
return {
newsletterStringId: 'next-new-user-subscribe-product-updates-mdnplus',
newsletterStringFallbackText: `I’d like to receive product news and updates from MDN Plus and Mozilla`,
};
case 'snp':
return {
newsletterStringId: 'next-new-user-subscribe-product-updates-snp',
newsletterStringFallbackText: `I’d like to receive security and privacy news and updates from Mozilla`,
};
default:
return {
newsletterStringId: DEFAULT_NEWSLETTER_STRING_ID,
newsletterStringFallbackText: `I’d like to receive product news and updates from Mozilla`,
};
}
}

interface SignInFormProps {
newsletterLabel: string;
}

export const SignInForm = ({ newsletterLabel }: SignInFormProps) => {
const { newsletterStringId, newsletterStringFallbackText } =
getNewsletterStringInfo(newsletterLabel);
return (
<Form.Root aria-label="Sign-in/sign-up form">
<Form.Field name="email" className="my-6">
<Form.Label className="text-grey-400 block mb-1 text-start">
<Localized id="checkout-enter-your-email">Enter your email</Localized>
</Form.Label>
<Form.Control asChild>
<input
className="w-full border rounded-md border-black/30 p-3 placeholder:text-grey-500 placeholder:font-normal focus:border focus:!border-black/30 focus:!shadow-[0_0_0_3px_rgba(10,132,255,0.3)] focus-visible:outline-none data-[invalid=true]:border-alert-red data-[invalid=true]:text-alert-red data-[invalid=true]:shadow-inputError"
type="email"
data-testid="email"
aria-required
/>
</Form.Control>
</Form.Field>

<Form.Field
name="newsletter"
className="flex gap-4 items-center mb-4"
data-testid="new-user-subscribe-product-updates"
>
<Form.Control asChild>
<input
id="newsletter-checkbox"
type="checkbox"
name="confirm"
className="grow-0 shrink-0 basis-4 scale-150 cursor-pointer"
/>
</Form.Control>

<Form.Label htmlFor="newsletter-checkbox" className="text-sm">
<Localized id={newsletterStringId}>
{newsletterStringFallbackText}
</Localized>
</Form.Label>
</Form.Field>

<Form.Field
name="assurance"
className="flex items-center gap-4 text-sm"
data-testid="assurance-copy"
>
<Image src={shieldIcon} alt="" className="-mx-2" />
<Localized id="next-new-user-subscribe-product-assurance">
We only use your email to create your account. We will never sell it
to a third party.
</Localized>
</Form.Field>

<Form.Submit asChild>
<BaseButton
className="mt-6 my-8"
type="submit"
variant={ButtonVariant.Primary}
>
<Localized id="continue">Continue</Localized>
</BaseButton>
</Form.Submit>
</Form.Root>
);
};
3 changes: 3 additions & 0 deletions libs/shared/assets/src/images/apple-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions libs/shared/assets/src/images/google-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5bf120e

Please sign in to comment.