Skip to content

Commit

Permalink
Standalone page (#893)
Browse files Browse the repository at this point in the history
This adds a standardized "standalone" page design for login/signup, forgot-password, reset-password, verify email (plus a modified version for unsubscribe/manage subscriptions).
[sc-14218]

This new page also gets a new minimal footer.
[sc-13964]

Mobile styles are also fixed for all these pages with the new layout.
[sc-14066]

All automatic/countdown redirects are removed from login/signup via nav, reset-password and verify.
[sc-14144]

The login flow from nav now has an extra final step. If not verified you get the same prompt to do so as at the end of sign up, and it sends a new email. And if you are already verified then you get a a new "success" step with a link to the homepage.

Focus now automatically shifts to the password input after submitting email in the login/signup flows.
[sc-14271]

Also, I realized there was a bad copy/paste error on the reset-password page - it was sending a verify email instead of a reset one! So I fixed that and also realized there was a problem with the request because cookies weren't getting sent and those are needed for accessing session on the auth server.

I also changed the JF logo svg (also used in nav) to a component so that we can make use of the "currentColor" to style with css instead of adding a second black version.
  • Loading branch information
austensen authored Apr 5, 2024
1 parent 55cd1d4 commit 99717a2
Show file tree
Hide file tree
Showing 21 changed files with 492 additions and 295 deletions.
11 changes: 0 additions & 11 deletions client/src/assets/img/logo.svg

This file was deleted.

2 changes: 1 addition & 1 deletion client/src/components/AuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const resetPasswordRequest = async (username?: string) => {
} else {
const params = new URLSearchParams(window.location.search);
await postAuthRequest(
`${BASE_URL}auth/reset_password_request_with_token?u=${params.get("token")}`
`${BASE_URL}auth/reset_password_request_with_token?token=${params.get("token")}`
);
}
return true;
Expand Down
1 change: 0 additions & 1 deletion client/src/components/EmailAlertSignup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ const BuildingSubscribeWithoutI18n = (props: BuildingSubscribeProps) => {
{!isEmailResent && <Trans render="div">Didn’t get the link?</Trans>}
<SendNewLink
setParentState={setIsEmailResent}
variant="secondary"
className="is-full-width"
onClick={() => AuthClient.resendVerifyEmail()}
/>
Expand Down
41 changes: 41 additions & 0 deletions client/src/components/JFLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { SVGProps } from "react";

export const JFLogo = (props: SVGProps<SVGSVGElement>) => (
<svg
width="96"
height="22"
viewBox="0 0 96 22"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M4.8626 15.8124C4.8626 17.6707 4.05216 18.8277 2.1366 18.8277C1.39984 18.8277 1.03146 18.7926 0.331541 18.6524L0 21.5624C0.84727 21.7027 1.58403 21.7728 2.61549 21.7728C6.5203 21.7728 8.25168 19.3536 8.25168 15.8826V0.455792H4.8626V15.8124Z"
fill="currentColor"
/>
<path
d="M23.1845 21.4923H26.3894V5.11889H23.1845V14.8307C23.1845 17.5655 21.4163 19.1082 18.4693 19.1082C15.3749 19.1082 14.3434 17.0746 14.3434 14.8307V5.11889H11.1386V15.6021C11.1386 19.1082 12.6857 21.913 17.1431 21.913C20.1638 21.913 22.4109 20.4054 23.1477 17.0746H23.1845V21.4923Z"
fill="currentColor"
/>
<path
d="M36.1592 21.913C40.8008 21.913 43.0111 19.9496 43.0111 16.9695C43.0111 14.4451 41.2797 12.8323 36.5644 12.0609C33.028 11.5 32.1439 10.6585 32.1439 9.29113C32.1439 7.92376 33.2859 7.08229 35.4593 7.08229C37.7433 7.08229 38.8852 8.064 39.5115 10.5183L42.569 9.81704C41.7954 6.6265 39.8798 4.69816 35.4593 4.69816C31.1493 4.69816 28.939 6.6265 28.939 9.46643C28.939 12.0259 30.5967 13.5686 35.1278 14.375C38.8115 14.971 39.843 15.6722 39.843 17.0396C39.8062 18.5472 38.6274 19.4938 36.1961 19.4938C32.9543 19.4938 31.7755 17.846 31.1861 15.6021L28.1654 16.1631C28.9022 19.564 30.8177 21.913 36.1592 21.913Z"
fill="currentColor"
/>
<path
d="M46.1377 16.6189C46.1377 19.6691 47.7217 21.913 51.3318 21.913C52.9158 21.913 53.8368 21.7027 55.0156 21.3521L54.6104 18.5472C53.6894 18.7926 52.9158 18.9679 51.9949 18.9679C50.0793 18.9679 49.3426 18.0213 49.3426 16.1981V7.74845H54.8314V5.11889H49.3426V0.596035H46.1377V5.11889H43.5959V7.74845H46.1377V16.6189Z"
fill="currentColor"
/>
<path
d="M70.2788 12.8323V9.78198H60.1484V3.50609H71.4945V0.455792H56.7593V21.4923H60.1484V12.8323H70.2788Z"
fill="currentColor"
/>
<path
d="M77.8197 2.5945V0H74.1727V2.5945H77.8197ZM77.5618 21.4923V5.11889H74.3937V21.4923H77.5618Z"
fill="currentColor"
/>
<path
d="M92.2794 21.4923H96V21.4222L90.1059 13.4283L95.8526 5.18901V5.11889H92.3531L87.7852 12.096L83.1068 5.11889H79.3861V5.18901L85.1697 13.0777L79.2756 21.4222V21.4923H82.8857L87.4536 14.41L92.2794 21.4923Z"
fill="currentColor"
/>
</svg>
);
53 changes: 35 additions & 18 deletions client/src/components/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState, useContext } from "react";
import { Trans, t } from "@lingui/macro";
import { I18n } from "@lingui/core";
import { withI18n } from "@lingui/react";
import classNames from "classnames";
import { Button, Link as JFCLLink } from "@justfixnyc/component-library";

import "styles/Login.css";
Expand All @@ -28,14 +27,14 @@ enum Step {
RegisterAccount,
RegisterUserType,
VerifyEmail,
LoginSuccess,
}

type LoginProps = {
i18n: I18n;
addr?: AddressRecord;
onBuildingPage?: boolean;
onSuccess?: (user: JustfixUser) => void;
handleRedirect?: () => void;
registerInModal?: boolean;
setLoginRegisterInProgress?: React.Dispatch<React.SetStateAction<boolean>>;
showForgotPassword?: boolean;
Expand All @@ -47,13 +46,12 @@ const LoginWithoutI18n = (props: LoginProps) => {
addr,
onBuildingPage,
onSuccess,
handleRedirect,
registerInModal,
setLoginRegisterInProgress,
} = props;

const userContext = useContext(UserContext);
const { account } = createWhoOwnsWhatRoutePaths();
const { home, account } = createWhoOwnsWhatRoutePaths();

const [showRegisterModal, setShowRegisterModal] = useState(false);

Expand All @@ -63,6 +61,7 @@ const LoginWithoutI18n = (props: LoginProps) => {
const isRegisterAccountStep = step === Step.RegisterAccount;
const isRegisterUserTypeStep = step === Step.RegisterUserType;
const isVerifyEmailStep = step === Step.VerifyEmail;
const isLoginSuccessStep = step === Step.LoginSuccess;

const {
value: email,
Expand Down Expand Up @@ -157,7 +156,7 @@ const LoginWithoutI18n = (props: LoginProps) => {

const renderFooter = () => {
return (
<div className="building-page-footer">
<div className="login-footer">
{isRegisterAccountStep && (
<span className="privacy-links">
<Trans>
Expand Down Expand Up @@ -219,18 +218,27 @@ const LoginWithoutI18n = (props: LoginProps) => {
};

const renderResendVerifyEmail = () => (
<>
<div className="resend-email-container">
{!isEmailResent && (
<Trans render="span" className="resend-verify-label">
<Trans render="p" className="didnt-get-link">
Didn’t get the link?
</Trans>
)}
<SendNewLink
setParentState={setIsEmailResent}
variant="secondary"
size={onBuildingPage ? "small" : "large"}
className="is-full-width"
onClick={() => AuthClient.resendVerifyEmail()}
/>
</div>
);

const renderLoginSuccess = () => (
<>
<Trans render="h1">You are logged in</Trans>
<Trans render="h2">
<JFCLLocaleLink to={home}>Search for an address</JFCLLocaleLink> to add to Building Updates
</Trans>
</>
);

Expand Down Expand Up @@ -291,12 +299,17 @@ const LoginWithoutI18n = (props: LoginProps) => {
return;
}

!!setLoginRegisterInProgress && setLoginRegisterInProgress(false);

if (!onBuildingPage) {
handleRedirect && handleRedirect();
if (resp?.user?.verified) {
setStep(Step.LoginSuccess);
} else {
await AuthClient.resendVerifyEmail();
setStep(Step.VerifyEmail);
}
return;
}

!!setLoginRegisterInProgress && setLoginRegisterInProgress(false);
};

const onAccountSubmit = async () => {
Expand Down Expand Up @@ -433,12 +446,15 @@ const LoginWithoutI18n = (props: LoginProps) => {
const renderLoginFlow = () => {
return (
<div className="Login">
{!!headerText && (
<h4 className={classNames(!onBuildingPage && "page-title")}>{headerText}</h4>
)}
{!!subHeaderText && <div className="card-description">{subHeaderText}</div>}
{!!headerText && (onBuildingPage ? <h4>{headerText}</h4> : <h1>{headerText}</h1>)}
{!!subHeaderText &&
(onBuildingPage ? (
<div className="card-description">{subHeaderText}</div>
) : (
<h2>{subHeaderText}</h2>
))}
{renderAlert()}
{!isVerifyEmailStep && (
{!isVerifyEmailStep && !isLoginSuccessStep && (
<form
className="input-group"
onSubmit={(e) => {
Expand Down Expand Up @@ -468,7 +484,7 @@ const LoginWithoutI18n = (props: LoginProps) => {
setError={setPasswordError}
onChange={onChangePassword}
showPasswordRules={isRegisterAccountStep}
autoFocus={showRegisterModal && !!email && !password}
autoFocus={!!email && !password}
/>
)}
{isRegisterUserTypeStep && (
Expand All @@ -483,14 +499,15 @@ const LoginWithoutI18n = (props: LoginProps) => {
<Button
type="submit"
variant="primary"
size="small"
size={onBuildingPage ? "small" : "large"}
className="is-full-width"
labelText={submitButtonText}
/>
</div>
</form>
)}
{isVerifyEmailStep && renderResendVerifyEmail()}
{isLoginSuccessStep && renderLoginSuccess()}
{(isLoginStep || isRegisterAccountStep) && renderFooter()}
</div>
);
Expand Down
7 changes: 4 additions & 3 deletions client/src/components/SendNewLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import { CheckIcon } from "./Icons";
type SendNewLinkProps = withI18nProps & {
setParentState: React.Dispatch<React.SetStateAction<boolean>>;
onClick: () => void;
variant: "primary" | "secondary" | "tertiary";
variant?: "primary" | "secondary" | "tertiary";
size?: "small" | "large";
className?: string;
};

const SendNewLinkWithoutI18n = (props: SendNewLinkProps) => {
const { setParentState, onClick, variant, className, i18n } = props;
const { setParentState, onClick, variant = "secondary", size = "small", className, i18n } = props;

const [linkSent, setLinkSent] = useState(false);

Expand All @@ -30,7 +31,7 @@ const SendNewLinkWithoutI18n = (props: SendNewLinkProps) => {
<div className="send-new-link">
<Button
variant={variant}
size="small"
size={size}
className={className}
labelText={i18n._(t`Send new link`)}
onClick={handleClick}
Expand Down
63 changes: 63 additions & 0 deletions client/src/components/StandalonePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from "react";
import classNames from "classnames";
import { withI18n, withI18nProps } from "@lingui/react";
import { Link as JFCLLink } from "@justfixnyc/component-library";

import "styles/StandalonePage.css";
import Page from "./Page";
import { createWhoOwnsWhatRoutePaths } from "routes";
import { JFCLLocaleLink, LocaleLink } from "i18n";
import { Trans } from "@lingui/macro";
import { JFLogo } from "./JFLogo";

export const StandalonePageFooter = () => {
const { home } = createWhoOwnsWhatRoutePaths();
return (
<Trans render="div" className="wow-footer">
<JFCLLocaleLink to={home}>Who Owns What</JFCLLocaleLink> by JustFix.
<br />
Read our{" "}
<JFCLLink
href="https://www.justfix.org/en/privacy-policy/"
target="_blank"
rel="noopener noreferrer"
>
Privacy Policy
</JFCLLink>{" "}
and{" "}
<JFCLLink
href="https://www.justfix.org/en/terms-of-use/"
target="_blank"
rel="noopener noreferrer"
>
Terms of Service
</JFCLLink>
</Trans>
);
};

type StandalonePageProps = withI18nProps & {
title: string;
className: string;
children: React.ReactNode;
};

const StandalonePage = withI18n()((props: StandalonePageProps) => {
const { title, className, children } = props;
const { home } = createWhoOwnsWhatRoutePaths();
return (
<Page title={title}>
<div className={classNames("StandalonePage Page", className)}>
<div className="page-container">
<LocaleLink className="jf-logo" to={home}>
<JFLogo />
</LocaleLink>
<div className="standalone-container">{children}</div>
<StandalonePageFooter />
</div>
</div>
</Page>
);
});

export default StandalonePage;
1 change: 0 additions & 1 deletion client/src/components/UserSettingField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ const EmailSettingFieldWithoutI18n = (props: EmailSettingFieldProps) => {
{!isEmailResent && <Trans render="p">Didn’t get the link?</Trans>}
<SendNewLink
setParentState={setIsEmailResent}
variant="secondary"
onClick={() => AuthClient.resendVerifyEmail()}
/>
</div>
Expand Down
18 changes: 14 additions & 4 deletions client/src/containers/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, { useEffect, useState, useContext } from "react";
import { BrowserRouter as Router, Switch, Route, useLocation } from "react-router-dom";
import { Trans, t } from "@lingui/macro";
import logo from "../assets/img/logo.svg";
import logoDivider from "../assets/img/logo-divider.svg";

import "styles/App.css";

Expand Down Expand Up @@ -49,6 +47,8 @@ import ResetPasswordPage from "./ResetPasswordPage";
import ForgotPasswordPage from "./ForgotPasswordPage";
import UnsubscribePage from "./UnsubscribePage";
import LoginPage from "./LoginPage";
import { JFLogo } from "components/JFLogo";
import logoDivider from "../assets/img/logo-divider.svg";

const HomeLink = withI18n()((props: withI18nProps) => {
const { i18n } = props;
Expand All @@ -65,7 +65,7 @@ const HomeLink = withI18n()((props: withI18nProps) => {
}}
to={isLegacyPath(pathname) ? legacy.home : home}
>
<img className="jf-logo" src={logo} width="96" height="22" alt="JustFix" />
<JFLogo className="jf-logo" />
<img
className="jf-logo-divider"
src={logoDivider}
Expand Down Expand Up @@ -264,7 +264,17 @@ const Navbar = () => {

const userContext = useContext(UserContext);

return (
const standalonePages = [
"forgot-password",
"login",
"verify-email",
"forgot-password",
"reset-password",
"unsubscribe",
];
const hideNavbar = standalonePages.some((v) => pathname.includes(`account/${v}`));

return hideNavbar ? null : (
<div
className={classnames(
"App__header",
Expand Down
Loading

0 comments on commit 99717a2

Please sign in to comment.