diff --git a/sites/public/pages/applications/review/confirmation.tsx b/sites/public/pages/applications/review/confirmation.tsx index 486dc27c2b..cad4fb6cee 100644 --- a/sites/public/pages/applications/review/confirmation.tsx +++ b/sites/public/pages/applications/review/confirmation.tsx @@ -14,7 +14,7 @@ import { FormCard, t, } from "@bloom-housing/ui-components" -import { ListingReviewOrder } from "@bloom-housing/backend-core/types" +import { ListingEvent, ListingReviewOrder } from "@bloom-housing/backend-core/types" import { imageUrlFromListing, PageView, @@ -33,25 +33,22 @@ const ApplicationConfirmation = () => { const imageUrl = imageUrlFromListing(listing, parseInt(process.env.listingPhotoSize)) - const reviewOrder = useMemo(() => { - if (listing) { - if (listing.reviewOrderType == ListingReviewOrder.lottery) { - const lotteryEvent = getLotteryEvent(listing) - const lotteryText = [] - if (lotteryEvent?.startTime) { - lotteryText.push( - t("application.review.confirmation.eligibleApplicants.lotteryDate", { - lotteryDate: dayjs(lotteryEvent?.startTime).format("MMMM D, YYYY"), - }) - ) + const content = useMemo(() => { + switch (listing?.reviewOrderType) { + case ListingReviewOrder.firstComeFirstServe: + return { + text: t("application.review.confirmation.whatHappensNext.fcfs"), } - lotteryText.push(t("application.review.confirmation.eligibleApplicants.lottery")) - return lotteryText.join(" ") - } else { - return t("application.review.confirmation.eligibleApplicants.FCFS") - } - } else { - return "" + case ListingReviewOrder.lottery: + return { + text: t("application.review.confirmation.whatHappensNext.lottery"), + } + case ListingReviewOrder.waitlist: + return { + text: t("application.review.confirmation.whatHappensNext.waitlist"), + } + default: + return { text: "" } } }, [listing, router.locale]) @@ -93,9 +90,7 @@ const ApplicationConfirmation = () => {
- - {t("application.review.confirmation.whatHappensNext", { reviewOrder })} - + {content.text}
diff --git a/sites/public/pages/applications/review/terms.tsx b/sites/public/pages/applications/review/terms.tsx index 2a244a5c91..c1e9fcb389 100644 --- a/sites/public/pages/applications/review/terms.tsx +++ b/sites/public/pages/applications/review/terms.tsx @@ -2,7 +2,7 @@ 5.3 Terms View of application terms with checkbox */ -import React, { useContext, useEffect, useState } from "react" +import React, { useContext, useEffect, useMemo, useState } from "react" import { useRouter } from "next/router" import { AppearanceStyleType, @@ -14,7 +14,7 @@ import { AlertBox, ProgressNav, } from "@bloom-housing/ui-components" -import { ApplicationSection } from "@bloom-housing/backend-core" +import { ApplicationSection, ListingReviewOrder } from "@bloom-housing/backend-core/types" import { useForm } from "react-hook-form" import Markdown from "markdown-to-jsx" import { @@ -87,6 +87,25 @@ const ApplicationTerms = () => { }, ] + const content = useMemo(() => { + switch (listing?.reviewOrderType) { + case ListingReviewOrder.firstComeFirstServe: + return { + text: t("application.review.terms.fcfs.text"), + } + case ListingReviewOrder.lottery: + return { + text: t("application.review.terms.lottery.text"), + } + case ListingReviewOrder.waitlist: + return { + text: t("application.review.terms.waitlist.text"), + } + default: + return { text: "" } + } + }, [listing, router.locale]) + useEffect(() => { pushGtmEvent({ event: "pageView", @@ -122,20 +141,37 @@ const ApplicationTerms = () => { )}
-
+
{listing?.applicationDueDate && ( - - {t("application.review.terms.textSubmissionDate", { - applicationDueDate: applicationDueDate, - })} - + <> + + {t("application.review.terms.textSubmissionDate", { + applicationDueDate: applicationDueDate, + })} + +
+
+ )} - - {t("application.review.terms.text")} + ( +
  • + {children} +
  • + ), + }, + }, + }} + > + {content.text}
    -
    +
    { const { profile } = useContext(AuthContext) const { conductor, application, listing } = useFormConductor("whatToExpect") + const router = useRouter() const currentPageSection = 1 /* Form Handler */ @@ -28,6 +32,28 @@ const ApplicationWhatToExpect = () => { conductor.routeToNextOrReturnUrl() } + const content = useMemo(() => { + switch (listing?.reviewOrderType) { + case ListingReviewOrder.firstComeFirstServe: + return { + steps: t("application.start.whatToExpect.fcfs.steps"), + finePrint: t("application.start.whatToExpect.fcfs.finePrint"), + } + case ListingReviewOrder.lottery: + return { + steps: t("application.start.whatToExpect.lottery.steps"), + finePrint: t("application.start.whatToExpect.lottery.finePrint"), + } + case ListingReviewOrder.waitlist: + return { + steps: t("application.start.whatToExpect.waitlist.steps"), + finePrint: t("application.start.whatToExpect.waitlist.finePrint"), + } + default: + return { steps: "", finePrint: "" } + } + }, [listing, router.locale]) + useEffect(() => { pushGtmEvent({ event: "pageView", @@ -58,9 +84,41 @@ const ApplicationWhatToExpect = () => {
    -

    {t("application.start.whatToExpect.info1")}

    -

    {t("application.start.whatToExpect.info2")}

    -

    {t("application.start.whatToExpect.info3")}

    +
    + ( +
      + {children} +
    + ), + }, + }, + }} + > + {content.steps} +
    + + ( +
  • + {children} +
  • + ), + }, + }, + }} + > + {content.finePrint} +
    +
    diff --git a/ui-components/src/global/markdown.scss b/ui-components/src/global/markdown.scss index f24ce1b339..3567bc677a 100644 --- a/ui-components/src/global/markdown.scss +++ b/ui-components/src/global/markdown.scss @@ -1,53 +1,173 @@ .markdown { + /* Set sizing and gaps for built-in HTML elements */ + h2 { - @apply mt-8; - @apply mb-5; - @apply text-3xl; + margin-block: var(--bloom-s8) var(--bloom-s5); + font-size: var(--bloom-font-size-3xl); - @screen md { - @apply text-xl; + @media (min-width: $screen-md) { + font-size: var(--bloom-font-size-xl); } } h2:first-child { - @apply mt-0; + margin-block-start: 0; } hr { - @apply mt-8; - @apply mb-8; - @apply border-b; - @apply border-solid; - @apply border-gray-450; + margin-block: var(--bloom-s8); + border-block-end: 1px solid var(--bloom-color-gray-450); } p:not(:last-child), ul:not(:last-child) { - @apply mb-4; + margin-block-end: var(--bloom-s4); } ul { list-style: disc; - @apply ml-6; + margin-inline-start: var(--bloom-s6); } + /* Certain text blocks in the application form flow use this extra styling: */ + &.markdown-informational { - font-size: 0.9em; - @apply text-gray-750; + font-size: var(--bloom-font-size-tiny); + color: var(--bloom-color-gray-750); h3 { - @apply font-sans; - @apply font-bold; - @apply text-base; - @apply text-black; + font-family: var(--bloom-font-sans); + font-weight: bold; + font-size: var(--bloom-font-size-base); + color: var(--bloom-color-black); + margin-block-end: var(--bloom-s4); } li { - @apply mb-4; + margin-block-end: var(--bloom-s4); } a { - @apply underline; + text-align: underline; + } + } + + /* Used for a larger lead paragraph: */ + + .markdown__lead { + font-family: var(--bloom-font-sans); + font-size: var(--bloom-font-size-base); + color: var(--bloom-color-gray-700); + margin-block: var(--bloom-s3) var(--bloom-s4); + } + + /* Used for a (1), (2), (3) section on the application form flow: */ + + ol.large-numbers { + counter-reset: list-number; + list-style-type: none; + margin-block-end: var(--bloom-s8); + + li { + display: flex; + align-items: center; + gap: 0.8em; + counter-increment: list-number; + margin-block-end: var(--bloom-s4); + font-weight: 600; + } + + li::before { + content: counter(list-number); + flex-shrink: 0; + width: 32px; + height: 32px; + border: 1px solid var(--bloom-color-gray-600); + padding: 3px; + border-radius: 100px; + text-align: center; + font-weight: normal; + } + } + + /* Used for lengthy (1), (2), (3), etc. such as the How it Works page: */ + + ol.process-list { + font-size: 1.06rem; + line-height: 1.5; + margin-block: 0; + list-style-type: none; + counter-reset: list-number; + padding-block: 20px 0; + padding-inline: var(--bloom-s5) 0; + position: relative; + + & > li { + border-inline-start: 1px solid var(--bloom-color-gray-450); + font-size: 1.06rem; + margin-bottom: 0; + padding-block-end: var(--bloom-s8); + padding-inline-start: var(--bloom-s8); + + @media (min-width: $screen-md) { + padding-inline-start: var(--bloom-s10); + } + + &::before { + content: counter(list-number, decimal); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: var(--bloom-rounded-full); + background-color: var(--bloom-color-white); + border: var(--bloom-border-2) solid var(--bloom-color-gray-750); + box-shadow: 0 0 0 1px var(--bloom-color-white); + color: var(--bloom-color-gray-750); + counter-increment: list-number; + font-size: var(--bloom-font-size-base); + font-weight: normal; + line-height: 0.9; + width: var(--bloom-s10); + height: var(--bloom-s10); + margin-block-start: 0; + position: absolute; + left: 0; + } + + &:last-child { + border-inline-start-color: transparent; + } + } + + /* The homepage How it Works section uses a horizontal layout: */ + + &.has-horizontal-layout { + display: flex; + gap: var(--bloom-s8); + padding-inline-start: 0; + margin-block: var(--bloom-s12); + + @media (max-width: 1100px) { + flex-direction: column; + gap: var(--bloom-s12); + } + + & > li { + position: relative; + border-inline-start-width: 0; + text-align: center; + padding-top: var(--bloom-s8); + background-color: var(--bloom-color-white); + box-shadow: 0px 0px 3px var(--bloom-color-gray-450); + padding-inline: var(--bloom-s4); + + &::before { + inset-block-start: calc(0rem - var(--bloom-s7)); + inset-inline-start: 50%; + margin-inline-start: calc(0rem - var(--bloom-s0_5)); + } + } } } } diff --git a/ui-components/src/locales/general.json b/ui-components/src/locales/general.json index 3982ff32ee..08f5716680 100644 --- a/ui-components/src/locales/general.json +++ b/ui-components/src/locales/general.json @@ -379,7 +379,9 @@ "application.review.confirmation.whatExpectFirstParagraph.refer": "Please refer to the listing for the lottery results date.", "application.review.confirmation.whatExpectSecondparagraph": "Applicants will be contacted in order until vacancies are filled. Should your application be chosen, be prepared to fill out a more detailed application and provide required supporting documents.", "application.review.confirmation.whatExpectTitle": "What to expect next", - "application.review.confirmation.whatHappensNext": "### What happens next?\n\n* After all applications are submitted, the property manager will begin processing applications.\n\n* %{reviewOrder}\n\n* If you are contacted for an interview, you will need to fill out a more detailed application and provide supporting documents.", + "application.review.confirmation.whatHappensNext.fcfs": "### What happens next?\n\n* Eligible applicants will be contacted on a first come first serve basis until vacancies are filled.\n\n* Housing preferences, if applicable, will affect first come, first serve order.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.", + "application.review.confirmation.whatHappensNext.lottery": "### What happens next?\n\n* Once the application period closes, eligible applicants will be placed in order based on lottery rank order.\n\n* Housing preferences, if applicable, will affect lottery rank order.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.", + "application.review.confirmation.whatHappensNext.waitlist": "### What happens next?\n\n* Eligible applicants will be placed on the waitlist on a first come first serve basis until waitlist spots are filled.\n\n* Housing preferences, if applicable, will affect waitlist order.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* You may be contacted while on the waitlist to confirm that you wish to remain on the waitlist.", "application.review.demographics.ethnicityLabel": "Which best describes your ethnicity?", "application.review.demographics.ethnicityOptions.hispanicLatino": "Hispanic / Latino", "application.review.demographics.ethnicityOptions.notHispanicLatino": "Not Hispanic / Latino", @@ -439,13 +441,21 @@ "application.review.sameAddressAsApplicant": "Same Address as Applicant", "application.review.takeAMomentToReview": "Take a moment to review your information before submitting your application.", "application.review.terms.confirmCheckboxText": "I agree and understand that I cannot change anything after I submit.", - "application.review.terms.textSubmissionDate": "This application must be submitted by %{applicationDueDate}.

    ", - "application.review.terms.text": "Applicants will be contacted by the leasing agent in lottery and preference order or waitlist order until vacancies are filled. All of the information that you have provided will be verified and your eligibility confirmed. Your application may be removed from the waitlist if you have made any fraudulent statements and duplicate applications from the same household may be removed as only one application per household is permitted. Should your application be chosen for review, be prepared to fill out a more detailed application and provide required supporting documents. For more information, please contact the developer or leasing agent posted in the listing. Please contact the developer/property manager directly if there are any updates to your application.

    If we cannot verify a housing lottery preference that you have claimed, you will not receive the preference but will not be otherwise penalized.

    Completing this housing application does not entitle you to housing or indicate you are eligible for housing; all applicants will be screened as outlined in the property’s Resident Selection Criteria. We offer no guarantees about obtaining housing.

    You cannot change your online application after you submit.

    I declare that the foregoing is true and accurate, and acknowledge that any misstatement fraudulently or negligently made on this application may result in removal from the lottery.

    ", + "application.review.terms.textSubmissionDate": "This application must be submitted by %{applicationDueDate}.", + "application.review.terms.fcfs.text": "* Applicants are applying to currently vacant apartments on a first come, first serve basis.\n\n* Eligible applicants will be contacted on a first come first serve basis until vacancies are filled.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* All of the information that you have provided will be verified and your eligibility confirmed.\n\n* Your application may be removed if you have made any fraudulent statements.\n\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.\n\nFor more information, please contact the housing developer or property manager posted in the listing.\n\nCompleting this application does not entitle you to housing or indicate you are eligible for housing. All applicants will be screened as outlined in the property's Resident Selection Criteria.\n\nYou cannot change your online application after you submit.\n\nI declare that the foregoing is true and accurate, and acknowledge that any misstatement fraudulently or negligently made on this application will result in removal from the lottery.", + "application.review.terms.lottery.text": "* Applicants are applying to enter a lottery for currently vacant apartments.\n\n* Once the application period closes, eligible applicants will be placed in lottery rank order.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* All of the information that you have provided will be verified and your eligibility confirmed.\n\n* Your application may be removed if you have made any fraudulent statements.\n\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.\n\nFor more information, please contact the housing developer or property manager posted in the listing.\n\nCompleting this application does not entitle you to housing or indicate you are eligible for housing. All applicants will be screened as outlined in the property's Resident Selection Criteria.\n\nYou cannot change your online application after you submit.\n\nI declare that the foregoing is true and accurate, and acknowledge that any misstatement fraudulently or negligently made on this application will result in removal from the lottery.", + "application.review.terms.waitlist.text": "* Applicants are applying for an open waitlist and not a currently vacant apartment.\n\n* When vacancies become available, eligible applicants will be contacted by the property manager on a first come, first serve basis.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* All of the information that you have provided will be verified and your eligibility confirmed.\n\n* Your application may be removed if you have made any fraudulent statements.\n\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.\n\n* You may be contacted while on the waitlist to confirm that you wish to remain on the waitlist.\n\nFor more information, please contact the housing developer or property manager posted in the listing.\n\nCompleting this application does not entitle you to housing or indicate you are eligible for housing. All applicants will be screened as outlined in the property's Resident Selection Criteria.\n\nYou cannot change your online application after you submit.\n\nI declare that the foregoing is true and accurate, and acknowledge that any misstatement fraudulently or negligently made on this application will result in removal from the lottery.", "application.review.terms.title": "Terms", "application.review.voucherOrSubsidy": "Housing Voucher or Rental Subsidy", - "application.start.whatToExpect.info1": "First we'll ask about you and the people you plan to live with. Then, we'll ask about your income. Finally, we'll see if you qualify for any affordable housing lottery preference.", - "application.start.whatToExpect.info2": "Please be aware that each household member can only appear on one application for each listing.", - "application.start.whatToExpect.info3": "Any fraudulent statements will cause your application to be removed.", + "application.start.whatToExpect.fcfs.steps": "1. First we'll ask about you and the people you plan to live with.\n2. Then, we'll ask about your income.\n3. Finally, we'll see if you qualify for any affordable housing lottery preferences, if applicable.", + "application.start.whatToExpect.fcfs.finePrint": "* Applicants are applying to currently vacant apartments on a first come, first serve basis.\n* Please be aware that each household member can only appear on one application for each listing.\n* Applicants will be contacted by the property manager on a first come, first serve basis, until vacancies are filled.\n* All of the information that you have provided will be verified and your eligibility confirmed.\n* Your application may be removed if you have made any fraudulent statements.\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.", + + "application.start.whatToExpect.lottery.steps": "1. First we'll ask about you and the people you plan to live with.\n2. Then, we'll ask about your income.\n3. Finally, we'll see if you qualify for any affordable housing lottery preferences, if applicable.", + "application.start.whatToExpect.lottery.finePrint": "* Applicants are applying to enter a lottery for currently vacant apartments.\n* Please be aware that each household member can only appear on one application for each listing.\n* Applicants will be contacted by the property agent in lottery rank order until vacancies are filled.\n* All of the information that you have provided will be verified and your eligibility confirmed.\n* Your application may be removed if you have made any fraudulent statements.\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.", + + "application.start.whatToExpect.waitlist.steps": "1. First we'll ask about you and the people you plan to live with.\n2. Then, we'll ask about your income.\n3. Finally, we'll see if you qualify for any affordable housing preferences, if applicable.", + "application.start.whatToExpect.waitlist.finePrint": "* Applicants are applying for an open waitlist and not a currently available apartment.\n* Please be aware that each household member can only appear on one application for each listing.\n* When vacancies become available, eligible applicants will be contacted by the property manager on a first come, first serve basis.\n* All of the information that you have provided will be verified and your eligibility confirmed.\n* Your application may be removed if you have made any fraudulent statements.\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.", + "application.start.whatToExpect.title": "Here's what to expect from this application.", "application.status": "Status", "application.statuses.inProgress": "In Progress",