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

Multi-step log in/sign up modal, with user type #849

Merged
merged 35 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3b7d0c4
multi-step modal login/signup flow
austensen Feb 20, 2024
cc1d308
+
austensen Feb 20, 2024
dd08c25
ignore unused footer for now
austensen Feb 20, 2024
0beb6b3
add preview urls pattern to cors whitelist
austensen Feb 20, 2024
8343e22
add back preview urls to whitelist
austensen Feb 20, 2024
7c9eb33
clean up styles for login - text styles, padding, etc.
austensen Feb 21, 2024
dd804a8
refactor error handling in input components, clean type
austensen Feb 21, 2024
64768ca
login flow ending
austensen Feb 21, 2024
33a66af
ix double padding on subscribed box
austensen Feb 21, 2024
c266c18
prettier
austensen Feb 21, 2024
3b99332
clean up styles and fix subscribed icon
austensen Feb 21, 2024
c35e754
update headers and existinguser alerts on building apge
austensen Feb 22, 2024
b7e7220
update verify copy/styles, fix input-level errors for sign up
austensen Feb 22, 2024
3df1a46
update password props on reset and change pages
austensen Feb 23, 2024
7228b2b
fix account settings pw input styles
austensen Feb 23, 2024
670de39
remove unused icon
austensen Feb 23, 2024
edd268a
fix duplicate id on change password page
austensen Feb 23, 2024
ece5b38
don't show input-lvel error on bad pw format during login
austensen Feb 23, 2024
34d4c4a
make setError required for password input
austensen Feb 23, 2024
4bd4fe0
use same showError props for usertype error handling
austensen Feb 26, 2024
be1f499
add input props extends for email/pw, set auto focus on modal
austensen Feb 26, 2024
fff9d57
remove old id prop for pw input
austensen Feb 26, 2024
da8d76f
prevent extra i18n prop getting passed to input
austensen Feb 26, 2024
85e3760
usertype input remove preventDefault bug radio not updating
austensen Feb 26, 2024
3e5a4bb
add "didn't get link" line to on page verify reminder
austensen Feb 26, 2024
45c4318
show footer on login as well as sign up in modal
austensen Feb 26, 2024
5b5ff59
Merge branch 'email-alerts-account-signup' into user-type-modal
austensen Feb 26, 2024
baa71c5
fix typo in usertype input error
austensen Feb 28, 2024
0d603be
Merge branch 'email-alerts-account-signup' into user-type-modal
austensen Feb 29, 2024
fa1cf67
Merge branch 'email-alerts-account-signup' into user-type-modal
austensen Mar 4, 2024
bb7cb97
more updates for base 16px rem units
austensen Mar 4, 2024
f2ea547
undo account settings rem changes (doubled up)
austensen Mar 4, 2024
cae9622
remove duplicates cors regex from merge update
austensen Mar 5, 2024
7eed028
update register operations to include user type
austensen Mar 5, 2024
c28cc16
delete commented code, fix js var case format
austensen Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions client/src/components/AuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ const clearUser = () => (_user = undefined);
* Authenticates a user with the given email and password.
* Creates an account for this user if one does not already exist.
*/
const register = async (username: string, password: string) => {
const json = await postAuthRequest(`${BASE_URL}auth/register`, { username, password });
const register = async (username: string, password: string, userType: string) => {
const json = await postAuthRequest(`${BASE_URL}auth/register`, {
username,
password,
user_type: userType,
});
fetchUser();
return json;
};
Expand Down
93 changes: 28 additions & 65 deletions client/src/components/EmailAlertSignup.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { Fragment, useContext, useState } from "react";

import { withI18n, withI18nProps, I18n } from "@lingui/react";
import { t } from "@lingui/macro";
import { Trans } from "@lingui/macro";
import Login from "./Login";
import { UserContext } from "./UserContext";
Expand All @@ -11,7 +10,8 @@ import { LocaleLink as Link } from "../i18n";
import "styles/EmailAlertSignup.css";
import { JustfixUser } from "state-machine";
import AuthClient from "./AuthClient";
import { AlertIconOutline, SubscribedIcon } from "./Icons";
import { SubscribedIcon } from "./Icons";
import { Alert } from "./Alert";
import Modal from "./Modal";

const SUBSCRIPTION_LIMIT = 15;
Expand Down Expand Up @@ -49,29 +49,27 @@ const BuildingSubscribeWithoutI18n = (props: BuildingSubscribeProps) => {
const showEmailVerification = (i18n: any) => {
return (
<div className="building-subscribe-status">
<div>
<div className="status-title">
<AlertIconOutline />
<Trans>Email verification required</Trans>
</div>
<div className="status-description">
{i18n._(t`Click the link we sent to ${email}. It may take a few minutes to arrive.`)}
</div>
<button
className="button is-secondary is-full-width"
onClick={() => AuthClient.resendVerifyEmail()}
>
<Trans>Resend email</Trans>
</button>
</div>
<Alert type="info">
<Trans>Verify your email to start receiving updates.</Trans>
</Alert>
<Trans render="div" className="status-description">
Click the link we sent to {email}. It may take a few minutes to arrive.
Copy link
Member

Choose a reason for hiding this comment

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

{email} is showing up as plaintext here!
Screenshot 2024-03-05 at 2 27 49 PM

Copy link
Member Author

Choose a reason for hiding this comment

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

It's working on local so I'm pretty sure this is just that issue with not having done lingui extract yet, but I've been holding off until everything is merged into email-alerts..

</Trans>
<Trans render="div">Didn’t get the link?</Trans>
<button
className="button is-secondary is-full-width"
onClick={() => AuthClient.resendVerifyEmail()}
>
<Trans>Resend email</Trans>
</button>
</div>
);
};
return (
<I18n>
{({ i18n }) => (
<>
<div className="table-content building-subscribe">
<div className="building-subscribe">
{!(subscriptions && !!subscriptions?.find((s) => s.bbl === bbl)) ? (
<button
className="button is-primary"
Expand All @@ -89,7 +87,6 @@ const BuildingSubscribeWithoutI18n = (props: BuildingSubscribeProps) => {
showEmailVerification(i18n)
)}
</div>

<Modal
key={1}
showModal={showSubscriptionLimitModal}
Expand Down Expand Up @@ -125,18 +122,15 @@ const EmailAlertSignupWithoutI18n = (props: EmailAlertProps) => {
const { bbl, housenumber, streetname, zip, boro } = props;
const userContext = useContext(UserContext);
const { user } = userContext;
const [showVerifyModal, setShowVerifyModal] = useState(false);
const [loginRegisterInProgress, setLoginRegisterInProgress] = useState(false);

return (
<>
<div className="EmailAlertSignup card-body-table">
<div className="table-row">
<I18n>
{({ i18n }) => (
<div
title={i18n._(t`Get data updates for this building`)}
className="table-small-font"
>
<div className="table-small-font">
<label className="data-updates-label-container">
<span className="pill-new">
<Trans>NEW</Trans>
Expand All @@ -148,50 +142,19 @@ const EmailAlertSignupWithoutI18n = (props: EmailAlertProps) => {
)}
</label>
<div className="table-content">
{!user ? (
<Fragment>
<div className="email-description">
<Trans>
Each weekly email includes HPD Complaints, HPD Violations, and Eviction
Filings.
</Trans>
</div>
<Login
onBuildingPage={true}
onSuccess={(user: JustfixUser) => {
userContext.subscribe(bbl, housenumber, streetname, zip, boro, user);
!user.verified && setShowVerifyModal(true);
}}
/>
</Fragment>
) : (
{user && !loginRegisterInProgress ? (
<BuildingSubscribe {...props} />
) : (
<Login
registerInModal
onBuildingPage
setLoginRegisterInProgress={setLoginRegisterInProgress}
onSuccess={(user: JustfixUser) => {
userContext.subscribe(bbl, housenumber, streetname, zip, boro, user);
}}
/>
)}
</div>
<Modal
key={1}
showModal={showVerifyModal}
width={40}
onClose={() => setShowVerifyModal(false)}
>
<Trans render="h4">Verify your email to start receiving updates</Trans>
{i18n._(
t`Click the link we sent to ${user?.email}. It may take a few minutes to arrive.`
)}
<br />
<br />
<Trans>
Once your email has been verified, you’ll be signed up for Data Updates.
</Trans>
<br />
<br />
<button
className="button is-secondary is-full-width"
onClick={() => AuthClient.resendVerifyEmail()}
>
<Trans>Resend email</Trans>
</button>
</Modal>
</div>
)}
</I18n>
Expand Down
74 changes: 74 additions & 0 deletions client/src/components/EmailInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ChangeEvent, forwardRef } from "react";
import { t } from "@lingui/macro";
import { I18n } from "@lingui/core";
import { withI18n } from "@lingui/react";
import { AlertIcon } from "./Icons";

import "styles/EmailInput.css";
import "styles/_input.scss";
import classNames from "classnames";

interface EmailInputProps extends React.ComponentPropsWithoutRef<"input"> {
i18n: I18n;
email: string;
error: boolean;
setError: React.Dispatch<React.SetStateAction<boolean>>;
showError: boolean;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
i18nHash?: string;
}

const EmailInputWithoutI18n = forwardRef<HTMLInputElement, EmailInputProps>(
({ i18n, i18nHash, email, error, setError, showError, onChange, ...props }, ref) => {
const isBadEmailFormat = (value: string) => {
/* valid email regex rules
alpha numeric characters are ok, upper/lower case agnostic
username: leading \_ ok, chars \_\.\-\+ ok in all other positions
domain name: chars \.\- ok as long as not leading. must end in a \. and at least two alphabet chars */
const pattern =
"^([a-zA-Z0-9_]+[a-zA-Z0-9+_.-]+@[a-zA-Z0-9]+[a-zA-Z0-9.-]+[a-zA-Z0-9]+.[a-zA-Z]{2,})$";

// HTML input element has loose email validation requirements, so we check the input against a custom regex
const passStrictRegex = value.match(pattern);
const passAutoValidation = document.querySelectorAll("input:invalid").length === 0;

return !passAutoValidation || !passStrictRegex;
};

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
onChange(e);
const emailIsInvalid = isBadEmailFormat(e.target.value);
setError(emailIsInvalid);
};

return (
<div className="email-input-field">
<div className="email-input-label">
<label htmlFor="email-input">{i18n._(t`Email address`)}</label>
</div>
{showError && error && (
<div className="email-input-errors">
<span id="input-field-error">
<AlertIcon />
{i18n._(t`Please enter a valid email address.`)}
</span>
</div>
)}
<div className="email-input">
<input
type="email"
id="email-input"
className={classNames("input", { invalid: showError && error })}
onChange={handleChange}
value={email}
{...props}
/>
</div>
</div>
);
}
);

const EmailInput = withI18n({ withHash: false })(EmailInputWithoutI18n);

export default EmailInput;
8 changes: 4 additions & 4 deletions client/src/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ export const CheckIcon = (props: SVGProps<SVGSVGElement>) => (

export const SubscribedIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
width="36"
width="31"
height="30"
viewBox="0 0 36 30"
viewBox="0 0 31 30"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect x="0.5" width="34.6667" height="30" rx="15" fill="#242323" />
<path d="M10.5 15.3333L15.1667 20L25.1667 10" stroke="#F2F2F2" stroke-width="2" />
<rect x="0.5" width="30" height="30" rx="15" fill="#242323" />
<path d="M8 15.3333L12.6667 20L22.6667 10" stroke="#F2F2F2" strokeWidth="2" />
</svg>
);

Expand Down
Loading