+
{t("authentication.createAccount.noAccount")}
-
+ {process.env.showPwdless && (
+
+ {t("authentication.signIn.pwdless.createAccountCopy")}
+
+ )}
diff --git a/shared-helpers/src/views/sign-in/FormSignInDefault.tsx b/shared-helpers/src/views/sign-in/FormSignInDefault.tsx
new file mode 100644
index 0000000000..783d942c49
--- /dev/null
+++ b/shared-helpers/src/views/sign-in/FormSignInDefault.tsx
@@ -0,0 +1,76 @@
+import React, { useContext } from "react"
+import { useRouter } from "next/router"
+import type { UseFormMethods } from "react-hook-form"
+import { Field, Form, NavigationContext, t } from "@bloom-housing/ui-components"
+import { Button } from "@bloom-housing/ui-seeds"
+import { getListingRedirectUrl } from "../../utilities/getListingRedirectUrl"
+import styles from "./FormSignIn.module.scss"
+
+export type FormSignInDefaultProps = {
+ control: FormSignInDefaultControl
+ onSubmit: (data: FormSignInDefaultValues) => void
+}
+
+export type FormSignInDefaultValues = {
+ email: string
+ password: string
+}
+
+export type FormSignInDefaultControl = {
+ errors: UseFormMethods["errors"]
+ handleSubmit: UseFormMethods["handleSubmit"]
+ register: UseFormMethods["register"]
+}
+
+const FormSignInDefault = ({
+ onSubmit,
+ control: { errors, register, handleSubmit },
+}: FormSignInDefaultProps) => {
+ const onError = () => {
+ window.scrollTo(0, 0)
+ }
+ const { LinkComponent } = useContext(NavigationContext)
+ const router = useRouter()
+ const listingIdRedirect = router.query?.listingId as string
+ const forgetPasswordURL = getListingRedirectUrl(listingIdRedirect, "/forgot-password")
+
+ return (
+
+ )
+}
+
+export { FormSignInDefault as default, FormSignInDefault }
diff --git a/shared-helpers/src/views/sign-in/FormSignInPwdless.tsx b/shared-helpers/src/views/sign-in/FormSignInPwdless.tsx
new file mode 100644
index 0000000000..cae31bf2c8
--- /dev/null
+++ b/shared-helpers/src/views/sign-in/FormSignInPwdless.tsx
@@ -0,0 +1,91 @@
+import React, { useContext, useState } from "react"
+import { useRouter } from "next/router"
+import type { UseFormMethods } from "react-hook-form"
+import { Field, Form, NavigationContext, t } from "@bloom-housing/ui-components"
+import { Button } from "@bloom-housing/ui-seeds"
+import { getListingRedirectUrl } from "../../utilities/getListingRedirectUrl"
+import styles from "./FormSignIn.module.scss"
+
+export type FormSignInPwdlessProps = {
+ control: FormSignInPwdlessControl
+ onSubmit: (data: FormSignInPwdlessValues) => void
+}
+
+export type FormSignInPwdlessValues = {
+ email: string
+ password: string
+}
+
+export type FormSignInPwdlessControl = {
+ errors: UseFormMethods["errors"]
+ handleSubmit: UseFormMethods["handleSubmit"]
+ register: UseFormMethods["register"]
+}
+
+const FormSignInPwdless = ({
+ onSubmit,
+ control: { errors, register, handleSubmit },
+}: FormSignInPwdlessProps) => {
+ const onError = () => {
+ window.scrollTo(0, 0)
+ }
+ const { LinkComponent } = useContext(NavigationContext)
+ const router = useRouter()
+ const listingIdRedirect = router.query?.listingId as string
+ const forgetPasswordURL = getListingRedirectUrl(listingIdRedirect, "/forgot-password")
+
+ const [useCode, setUseCode] = useState(true)
+
+ return (
+
+ )
+}
+
+export { FormSignInPwdless as default, FormSignInPwdless }
diff --git a/sites/partners/src/pages/sign-in.tsx b/sites/partners/src/pages/sign-in.tsx
index 267d88ee64..16d4793f04 100644
--- a/sites/partners/src/pages/sign-in.tsx
+++ b/sites/partners/src/pages/sign-in.tsx
@@ -8,6 +8,7 @@ import {
AuthContext,
FormSignIn,
ResendConfirmationModal,
+ FormSignInDefault,
} from "@bloom-housing/shared-helpers"
import { useMutate, t } from "@bloom-housing/ui-components"
import FormsLayout from "../layouts/forms"
@@ -124,16 +125,6 @@ const SignIn = () => {
formToRender = (
{
setConfirmationStatusMessage(undefined)
},
}}
- />
+ control={{ errors }}
+ >
+
+
)
} else if (renderStep === EnumRenderStep.mfaType) {
formToRender = (
diff --git a/sites/public/.env.template b/sites/public/.env.template
index e91dda99a3..fc7defa67c 100644
--- a/sites/public/.env.template
+++ b/sites/public/.env.template
@@ -24,4 +24,5 @@ MAINTENANCE_WINDOW=
GTM_KEY=GTM-KF22FJP
# feature toggles
-SHOW_MANDATED_ACCOUNTS=FALSE
\ No newline at end of file
+SHOW_MANDATED_ACCOUNTS=FALSE
+SHOW_PWDLESS=FALSE
diff --git a/sites/public/next.config.js b/sites/public/next.config.js
index a81686633e..e96bf244dd 100644
--- a/sites/public/next.config.js
+++ b/sites/public/next.config.js
@@ -41,6 +41,7 @@ module.exports = withBundleAnalyzer({
cacheRevalidate: process.env.CACHE_REVALIDATE ? Number(process.env.CACHE_REVALIDATE) : 60,
cloudinaryCloudName: process.env.CLOUDINARY_CLOUD_NAME,
showMandatedAccounts: process.env.SHOW_MANDATED_ACCOUNTS === "TRUE",
+ showPwdless: process.env.SHOW_PWDLESS === "TRUE",
maintenanceWindow: process.env.MAINTENANCE_WINDOW,
},
i18n: {
diff --git a/sites/public/src/pages/sign-in.tsx b/sites/public/src/pages/sign-in.tsx
index 55f9676faf..d895d14c60 100644
--- a/sites/public/src/pages/sign-in.tsx
+++ b/sites/public/src/pages/sign-in.tsx
@@ -12,6 +12,8 @@ import {
AuthContext,
FormSignIn,
ResendConfirmationModal,
+ FormSignInDefault,
+ FormSignInPwdless,
} from "@bloom-housing/shared-helpers"
import { UserStatus } from "../lib/constants"
import { SuccessDTO } from "@bloom-housing/shared-helpers/src/types/backend-swagger"
@@ -138,8 +140,6 @@ const SignIn = () => {
)}
void onSubmit(data)}
- control={{ register, errors, handleSubmit, watch }}
networkStatus={{
content: networkStatusContent,
type: networkStatusType,
@@ -150,7 +150,20 @@ const SignIn = () => {
},
}}
showRegisterBtn={true}
- />
+ control={{ errors }}
+ >
+ {process.env.showPwdless ? (
+ void onSubmit(data)}
+ control={{ register, errors, handleSubmit }}
+ />
+ ) : (
+ void onSubmit(data)}
+ control={{ register, errors, handleSubmit }}
+ />
+ )}
+
{signUpCopy && (
diff --git a/sites/public/src/pages/verify.tsx b/sites/public/src/pages/verify.tsx
new file mode 100644
index 0000000000..3a5d8e023d
--- /dev/null
+++ b/sites/public/src/pages/verify.tsx
@@ -0,0 +1,119 @@
+import React, { useEffect, useState } from "react"
+import { useForm } from "react-hook-form"
+import { Button, Alert, Message, Dialog } from "@bloom-housing/ui-seeds"
+import { CardSection } from "@bloom-housing/ui-seeds/src/blocks/Card"
+import {
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+} from "@bloom-housing/ui-seeds/src/overlays/Dialog"
+import { Field, Form, t, SiteAlert } from "@bloom-housing/ui-components"
+import {
+ PageView,
+ pushGtmEvent,
+ useCatchNetworkError,
+ BloomCard,
+} from "@bloom-housing/shared-helpers"
+import { UserStatus } from "../lib/constants"
+import FormsLayout from "../layouts/forms"
+import styles from "../../styles/verify.module.scss"
+
+const Verify = () => {
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ const { register, handleSubmit, errors } = useForm()
+ const { determineNetworkError } = useCatchNetworkError()
+
+ const [isModalOpen, setIsModalOpen] = useState(false)
+ const [alertMessage, setAlertMessage] = useState(
+ t("account.pwdless.codeAlert", { email: "example@email.com" })
+ ) // This copy will change based on coming from the sign in flow or create account flow
+
+ useEffect(() => {
+ pushGtmEvent
({
+ event: "pageView",
+ pageTitle: "Verify",
+ status: UserStatus.NotLoggedIn,
+ })
+ }, [])
+
+ const onSubmit = (data: { code: string }) => {
+ // const { code } = data
+
+ try {
+ // Attempt to either create an account or sign in
+ } catch (error) {
+ const { status } = error.response || {}
+ determineNetworkError(status, error)
+ // "The code you've used is invalid or expired"
+ }
+ }
+
+ return (
+
+
+ <>
+
+
+
+ {!!Object.keys(errors).length && (
+
+ {t("errors.errorsToResolve")}
+
+ )}
+
+ {alertMessage}
+
+
+
+ >
+
+
+
+ )
+}
+
+export { Verify as default, Verify }
diff --git a/sites/public/styles/verify.module.scss b/sites/public/styles/verify.module.scss
new file mode 100644
index 0000000000..791692ac81
--- /dev/null
+++ b/sites/public/styles/verify.module.scss
@@ -0,0 +1,33 @@
+.verify-resend-link {
+ margin-bottom: var(--seeds-s6);
+}
+
+.verify-message {
+ max-width: 100%;
+ margin-bottom: var(--seeds-s4);
+}
+
+.verify-error {
+ margin-bottom: var(--seeds-s4);
+}
+
+.verify-code {
+ margin-bottom: 0;
+}
+
+.terms-card {
+ overflow-y: auto;
+ max-height: calc(100vh - 24rem);
+ min-height: 30rem;
+ position: relative;
+ margin-bottom: var(--seeds-s12);
+ @media (max-width: theme("screens.sm")) {
+ height: 100%;
+ max-height: 100%;
+ margin-bottom: 0;
+ }
+}
+
+.terms-form {
+ margin-bottom: var(--seeds-12);
+}