From df6b01c1fdfa8b923a74b6a520faf3c96a0f9acb Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Wed, 18 Dec 2024 18:03:17 -0500 Subject: [PATCH 1/7] refactor results styles, update print styles, expand accordions --- src/App.scss | 32 +- src/App.tsx | 2 +- src/Components/Footer/Footer.scss | 6 + src/Components/Header/Header.tsx | 6 + src/Components/KYRContent/KYRContent.tsx | 500 +++++++++++----------- src/Components/Pages/Results/Results.scss | 428 ++++++++---------- src/Components/Pages/Results/Results.tsx | 229 +++++----- src/Components/Pages/content-page.scss | 53 +-- src/mixins.scss | 2 +- 9 files changed, 611 insertions(+), 647 deletions(-) diff --git a/src/App.scss b/src/App.scss index 5a40b3e..fd28064 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,5 +1,19 @@ @import "colors.scss"; @import "variables.scss"; +@import "mixins.scss"; + +@media print { + html { + font-size: 12px; + } +} + +// removes default print page header/footer (date, url, etc.) +@page { + size: auto; + // TODO: should remove margin for PDF if possible + margin: 24px; +} #container { display: flex; @@ -21,10 +35,6 @@ text-wrap: nowrap; z-index: 100; - @media print { - display: none; - } - h1 { color: $OFF_WHITE_100; font-size: 1.25rem; // 20px @@ -36,12 +46,24 @@ color: $WHITE; text-decoration: none; } + + @media print { + position: unset; + background-color: unset; + h1, + .header__link { + color: black; + @include body_large_desktop; + } + } } #main { flex-grow: 1; margin-top: $headerH; - // padding-top: 2.25rem; // 36px + @media print { + margin-top: 0; + } #content { height: 100%; diff --git a/src/App.tsx b/src/App.tsx index 96cbedd..1fd750d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,7 +23,7 @@ const Layout = () => { diff --git a/src/Components/Footer/Footer.scss b/src/Components/Footer/Footer.scss index 8359b51..f4b755a 100644 --- a/src/Components/Footer/Footer.scss +++ b/src/Components/Footer/Footer.scss @@ -49,3 +49,9 @@ } } } + +@media print { + .footer { + display: none; + } +} diff --git a/src/Components/Header/Header.tsx b/src/Components/Header/Header.tsx index 115e55b..4c31cee 100644 --- a/src/Components/Header/Header.tsx +++ b/src/Components/Header/Header.tsx @@ -2,6 +2,7 @@ import React from "react"; import { BreadCrumbs } from "../BreadCrumbs/BreadCrumbs"; import { Address } from "../Pages/Home/Home"; import { BackLink } from "../JFCLLinkInternal"; +import { toTitleCase } from "../../helpers"; type HeaderProps = { title: string | React.ReactNode; @@ -26,6 +27,11 @@ export const Header: React.FC = ({
{showBreadcrumbs && } +
+ {toTitleCase( + `${address?.houseNumber} ${address?.streetName}, ${address?.zipcode}` + )} +
{isGuide && (
Back to Coverage Result diff --git a/src/Components/KYRContent/KYRContent.tsx b/src/Components/KYRContent/KYRContent.tsx index 5be000d..17c04bc 100644 --- a/src/Components/KYRContent/KYRContent.tsx +++ b/src/Components/KYRContent/KYRContent.tsx @@ -15,111 +15,114 @@ export const UniversalProtections: React.FC = ({ subtitle: headerSubtitle = "Protections that all New Yorkers have", children, }) => ( - - -

- The only way your landlord can evict you is through housing court. - Lockouts (also known as unlawful evictions or self-help evictions) are - illegal. All tenants, including those in private residential programs, - have the right to stay in their home unless they choose to leave or are - evicted through a court process. -

-
-

Learn more about the eviction process

- - NY Homes and Community Renewal - -
-
-

See if you are eligible for a free attorney

- - Eviction Free NYC - -
+ <> + + +

+ The only way your landlord can evict you is through housing court. + Lockouts (also known as unlawful evictions or self-help evictions) are + illegal. All tenants, including those in private residential programs, + have the right to stay in their home unless they choose to leave or + are evicted through a court process. +

+
+

Learn more about the eviction process

+ + NY Homes and Community Renewal + +
+
+

See if you are eligible for a free attorney

+ + Eviction Free NYC + +
- -

- Tenants have the right to live in a safe, sanitary, and well-maintained - apartment, including public areas of the building. This right is implied - in every residential lease, and any lease provision that waives it is - void. If your landlord is not providing these conditions in your - apartment or building, there are actions you can take to exercise your - rights. -

-
-

Learn about warranty of habitability

- - NY Courts - -
-
-

Learn how tenant associations can help

- - Met Council on Housing - -
-
-

Notify your landlord of repair issues

- - JustFix’s Letter of Complaint - -
- -

- Your landlord can’t evict you based on your race, religion, gender, - national origin, familial status, or disability. New York State law - promises protection from discrimination, banning bias based on age, - sexual orientation, and military status. -

-

- Source of income discrimination the illegal practice by landlords, - owners, and real estate brokers of refusing to rent to current or - prospective tenants seeking to pay for housing with housing assistance - vouchers, subsidies, or other forms of public assistance. -

-
-

Learn more about fair housing

- - Fair Housing NYC - -
-
-

- Learn more about lawful source of income discrimination -

- - Lawful source of income - -
-
-

Report source of income discrimination

- - Unlock NYC - -
- {children} -
+ +

+ Tenants have the right to live in a safe, sanitary, and + well-maintained apartment, including public areas of the building. + This right is implied in every residential lease, and any lease + provision that waives it is void. If your landlord is not providing + these conditions in your apartment or building, there are actions you + can take to exercise your rights. +

+
+

Learn about warranty of habitability

+ + NY Courts + +
+
+

Learn how tenant associations can help

+ + Met Council on Housing + +
+
+

Notify your landlord of repair issues

+ + JustFix’s Letter of Complaint + +
+ +

+ Your landlord can’t evict you based on your race, religion, gender, + national origin, familial status, or disability. New York State law + promises protection from discrimination, banning bias based on age, + sexual orientation, and military status. +

+

+ Source of income discrimination the illegal practice by landlords, + owners, and real estate brokers of refusing to rent to current or + prospective tenants seeking to pay for housing with housing assistance + vouchers, subsidies, or other forms of public assistance. +

+
+

Learn more about fair housing

+ + Fair Housing NYC + +
+
+

+ Learn more about lawful source of income discrimination +

+ + Lawful source of income + +
+
+

Report source of income discrimination

+ + Unlock NYC + +
+ {children} +
+
+ ); export const GoodCauseProtections: React.FC = ({ @@ -127,44 +130,47 @@ export const GoodCauseProtections: React.FC = ({ subtitle = "Protections you have under Good Cause Eviction", children, }) => ( - - -

- Your landlord will need to provide a good cause reason for ending a - tenancy. This includes evicting tenants, not renewing a lease, or, if - the tenant does not have a lease, giving notice that the tenancy will - end. -

-
+ <> + + +

+ Your landlord will need to provide a good cause reason for ending a + tenancy. This includes evicting tenants, not renewing a lease, or, if + the tenant does not have a lease, giving notice that the tenancy will + end. +

+
- -

- Your landlord is not allowed to increase your rent at a rate higher than - the local standard. The local rent standard is set every year at the - rate of inflation plus 5%, with a maximum of 10% total. -

-
-

- As of May 1, 2024, the rate of inflation for the New York City area is - 3.82%, meaning that the current local rent standard is 8.82%. A rent - increase of more than 8.82% could be found unreasonable by the court if - the rent was increased after April 20, 2024. -

-
+ +

+ Your landlord is not allowed to increase your rent at a rate higher + than the local standard. The local rent standard is set every year at + the rate of inflation plus 5%, with a maximum of 10% total. +

+
+

+ As of May 1, 2024, the rate of inflation for the New York City area is + 3.82%, meaning that the current local rent standard is 8.82%. A rent + increase of more than 8.82% could be found unreasonable by the court + if the rent was increased after April 20, 2024. +

+
- - - Housing Justice for All Good Cause Eviction fact sheet - - - Met Council on Housing Good Cause Eviction fact sheet - - - {children} -
+ + + Housing Justice for All Good Cause Eviction fact sheet + + + Met Council on Housing Good Cause Eviction fact sheet + + + {children} +
+
+ ); export const GoodCauseExercisingRights: React.FC = ({ @@ -172,40 +178,44 @@ export const GoodCauseExercisingRights: React.FC = ({ subtitle = "How to assert your rights", children, }) => ( - - -

- Assert your rights by printing your coverage results and sharing with - your landlord. You can use these results as an indicator that your - apartment is covered by Good Cause Eviction Law. -

- {/* TODO: add email/download/print coverage result buttons */} -
- -

- Since your apartment is covered by Good Cause Eviction, there is a good - chance other apartments in your building are covered as well. Organizing - with your neighbors can help you assert your rights as a group. -

- - Tenant Organizing Toolkit from Housing Justice for All - -
- -

- The Met Council on Housing helps organize tenants to stand up not only - for their individual rights, but also for changes to housing policies. - You can visit the website, or use their hotline to get answers to your - rights as a tenant, and understand your options for dealing with a - housing situation. -

- Met Council on Housing - - Call Met Council on Housing Hotline - -
- {children} -
+ <> + + +

+ Assert your rights by printing your coverage results and sharing with + your landlord. You can use these results as an indicator that your + apartment is covered by Good Cause Eviction Law. +

+ {/* TODO: add email/download/print coverage result buttons */} +
+ +

+ Since your apartment is covered by Good Cause Eviction, there is a + good chance other apartments in your building are covered as well. + Organizing with your neighbors can help you assert your rights as a + group. +

+ + Tenant Organizing Toolkit from Housing Justice for All + +
+ +

+ The Met Council on Housing helps organize tenants to stand up not only + for their individual rights, but also for changes to housing policies. + You can visit the website, or use their hotline to get answers to your + rights as a tenant, and understand your options for dealing with a + housing situation. +

+ Met Council on Housing + + Call Met Council on Housing Hotline + +
+ {children} +
+
+ ); export const RentStabilizedProtections: React.FC = ({ @@ -213,42 +223,45 @@ export const RentStabilizedProtections: React.FC = ({ subtitle = "Protections you have as a rent stabilized tenant", children, }) => ( - - -

- For rent-stabilized leases being renewed between October 1, 2024 and - September 30, 2025 the legal rent may be increased at the following - levels: for a one-year renewal there is a 2.75% increase, or for a - two-year renewal there is a 5.25% increase. -

- - Learn about rent increase rights - -
+ <> + + +

+ For rent-stabilized leases being renewed between October 1, 2024 and + September 30, 2025 the legal rent may be increased at the following + levels: for a one-year renewal there is a 2.75% increase, or for a + two-year renewal there is a 5.25% increase. +

+ + Learn about rent increase rights + +
- -

- If you are rent-stabilized your landlord cannot simply decide they don’t - want you as a tenant anymore, they are limited to certain reasons for - evicting you. -

- - Learn about lease renewal rights - -
+ +

+ If you are rent-stabilized your landlord cannot simply decide they + don’t want you as a tenant anymore, they are limited to certain + reasons for evicting you. +

+ + Learn about lease renewal rights + +
- -

- If you are the immediate family member of a rent-stabilized tenant and - have been living with them immediately prior to their moving or passing - away, you might be entitled to take over the lease. -

- - Learn about succession rights - -
- {children} -
+ +

+ If you are the immediate family member of a rent-stabilized tenant and + have been living with them immediately prior to their moving or + passing away, you might be entitled to take over the lease. +

+ + Learn about succession rights + +
+ {children} +
+
+ ); export const NYCHAProtections: React.FC = ({ @@ -256,40 +269,43 @@ export const NYCHAProtections: React.FC = ({ subtitle = "Protections you have if you live in NYCHA housing", children, }) => ( - - -

- For rent-stabilized leases being renewed between October 1, 2024 and - September 30, 2025 the legal rent may be increased at the following - levels: for a one-year renewal there is a 2.75% increase, or for a - two-year renewal there is a 5.25% increase. -

- - Learn about rent increase rights - -
+ <> + + +

+ For rent-stabilized leases being renewed between October 1, 2024 and + September 30, 2025 the legal rent may be increased at the following + levels: for a one-year renewal there is a 2.75% increase, or for a + two-year renewal there is a 5.25% increase. +

+ + Learn about rent increase rights + +
- -

- If you are rent-stabilized your landlord cannot simply decide they don’t - want you as a tenant anymore, they are limited to certain reasons for - evicting you. -

- - Learn about lease renewal rights - -
+ +

+ If you are rent-stabilized your landlord cannot simply decide they + don’t want you as a tenant anymore, they are limited to certain + reasons for evicting you. +

+ + Learn about lease renewal rights + +
- -

- If you are the immediate family member of a rent-stabilized tenant and - have been living with them immediately prior to their moving or passing - away, you might be entitled to take over the lease. -

- - Learn about succession rights - -
- {children} -
+ +

+ If you are the immediate family member of a rent-stabilized tenant and + have been living with them immediately prior to their moving or + passing away, you might be entitled to take over the lease. +

+ + Learn about succession rights + +
+ {children} +
+
+ ); diff --git a/src/Components/Pages/Results/Results.scss b/src/Components/Pages/Results/Results.scss index 10c55d0..9f7d1f4 100644 --- a/src/Components/Pages/Results/Results.scss +++ b/src/Components/Pages/Results/Results.scss @@ -5,328 +5,236 @@ $eligibilityTablePaddingH: 36px; -.eligibility__loading { - margin: 3rem 0; -} - -.eligibility__error { - margin: 3rem 0; -} - -.covered-pill { - display: inline-flex; - padding: 0px 18px 4px; - color: $WHITE; - border-radius: 44px; - background-color: $GREEN; - white-space: nowrap; - height: calc(1em + 4px); - - &.covered-pill--ineligible { - background-color: $ORANGE; - color: $OFF_BLACK; - } - - &.covered-pill--unknown { - background-color: $YELLOW; - color: $OFF_BLACK; - } -} - -.covered-underline { - text-decoration: underline; - color: $OFF_BLACK; -} - -.eligibility__toggle { - align-self: flex-start; - &.open { - margin-bottom: 2.25rem; // 36px - } - &.closed { - margin-bottom: 0.8125rem; // 13px - } -} - -@media screen { - .eligibility__result__print { - display: none; - } +#results-page { + .eligibility__loading, + .eligibility__error { + margin: 3rem 0; + } + + .covered-pill { + display: inline-flex; + padding: 0px 18px 4px; + color: $WHITE; + border-radius: 44px; + background-color: $GREEN; + white-space: nowrap; + height: calc(1em + 4px); + + &.covered-pill--ineligible { + background-color: $ORANGE; + color: $OFF_BLACK; + } - .eligibility__table__container__print { - display: none; + &.covered-pill--unknown { + background-color: $YELLOW; + color: $OFF_BLACK; + } } - .eligibility__table { + .eligibility__table, + .protections-on-next-page__print { border-radius: 4px; border: 2px solid $GREY_NEW; - padding: 1.5rem $eligibilityTablePaddingH; // 24px __ + padding: 1.5rem $eligibilityTablePaddingH; // 24px + margin-top: 3rem; // 48px max-width: $contentBoxWidth - (2 * $eligibilityTablePaddingH); background-color: $WHITE; } -} - -.eligibility__table { - margin-top: 3rem; // 48px -} -.eligibility__table__header { - display: flex; - flex-direction: column; - padding: 1.125rem 1.125rem 2.25rem 1.125rem; // 20px 20px 36px 20px; - gap: 0.75rem; // 12px - border-bottom: 1px solid $GREY_NEW; + .eligibility__table__header { + display: flex; + flex-direction: column; + padding: 1.125rem 1.125rem 2.25rem 1.125rem; // 20px 20px 36px 20px; + gap: 0.75rem; // 12px + border-bottom: 1px solid $GREY_NEW; - span { - @include body_large_desktop; + span { + @include body_large_desktop; + } + p { + @include body_standard_desktop; + margin: 0; + } } - p { - @include body_standard_desktop; + + .eligibility__table__list { margin: 0; + padding: 0; + list-style: none; } -} - -.eligibility__table__list { - margin: 0; - padding: 0; - list-style: none; -} -.eligibility__table__footer { - display: flex; - flex-direction: column; - align-items: center; - padding: $eligibilityTablePaddingH 0 0; - @include body_large_desktop; + .eligibility__table__footer { + display: flex; + flex-direction: column; + align-items: center; + padding: $eligibilityTablePaddingH 0 0; + @include body_large_desktop; - .eligibility__table__footer__link { - @include body_standard_desktop; - margin-top: 0.625rem; //10px - } + .eligibility__table__footer__link { + @include body_standard_desktop; + margin-top: 0.625rem; //10px + } - a { - color: black; - margin-left: 0.625rem; //10px + a { + color: black; + margin-left: 0.625rem; //10px + } } -} -.eligibility__row { - display: flex; - border-bottom: 1px solid $GREY_NEW; - align-items: center; - padding: 20px 0; - - @media (max-width: 772px) { - flex-direction: column; - gap: 16px; - text-align: center; - } + .eligibility__row { + display: flex; + border-bottom: 1px solid $GREY_NEW; + align-items: center; + padding: 20px 0; + + .eligibility__row__icon { + padding: 0 1.25rem; // 0 20px + font-size: 1.875rem; //30px + width: 1.875rem; //30px + } - .eligibility__row__icon { - padding: 0 1.25rem; // 0 20px - font-size: 1.875rem; //30px - width: 1.875rem; //30px + .eligibility__row__info { + display: flex; + flex-direction: column; + flex-shrink: 0; + width: 320px; + padding-right: 1rem; + box-sizing: border-box; + gap: 10px; + } - .eligible { - color: $GREEN; + .eligibility__row__criteria { + @include eyebrow_small_desktop; } - .ineligible { - color: $YELLOW; + + .eligibility__row__requirement { + @include body_standard_desktop; } - .unknown { - color: $YELLOW; + + .eligibility__row__userValue { + @include body_standard_desktop; + margin: 0 1.25rem; // 20px + box-sizing: border-box; + flex-grow: 1; + + p { + margin: 0 0 0.625rem 0; + } } } - .eligibility__row__info { + .eligible { + color: $GREEN; + } + .ineligible { + color: $YELLOW; + } + .unknown { + color: $YELLOW; + } + + .eligibility__footer { display: flex; flex-direction: column; - flex-shrink: 0; - width: 320px; - padding-right: 1rem; - box-sizing: border-box; - gap: 10px; + align-items: center; + margin: 2.625rem 0; //42px - @media (max-width: 772px) { - width: 100%; + .eligibility__footer__header { + @include h3_desktop; + margin: 0 0 $eligibilityTablePaddingH 0; + text-align: center; } } - .eligibility__row__criteria { - @include eyebrow_small_desktop; - } - - .eligibility__row__requirement { - @include body_standard_desktop; - } + .next-step { + --icon-size: 2.75rem; // 44px; + --icon-margin: 1.5rem; // 24px; - .eligibility__row__userValue { - @include body_standard_desktop; - margin: 0 1.25rem; // 20px - box-sizing: border-box; - flex-grow: 1; + .eligibility__icon { + font-size: 1.875rem; //30px + margin-right: var(--icon-margin); - p { - margin: 0 0 0.625rem 0; + svg { + width: var(--icon-size); + height: var(--icon-size); + } } - - @media (max-width: 772px) { - margin: 0; + .accordion__content { + margin-left: calc(var(--icon-size) + var(--icon-margin)); } } } -.eligibility__footer { - display: flex; - flex-direction: column; - align-items: center; - margin: 2.625rem 0; //42px - - .eligibility__footer__header { - @include h3_desktop; - margin: 0 0 $eligibilityTablePaddingH 0; - text-align: center; +@media screen { + @media (max-width: 772px) { + .eligibility__row { + flex-direction: column; + gap: 16px; + text-align: center; + } + .eligibility__row__info { + width: 100%; + } + .eligibility__row__userValue { + margin: 0; + } } -} -@media print { - .eligibility__result { + .protections-on-next-page__print { display: none; } - - .eligibility__table__container { + .divider__print { display: none; } +} - .eligibility__table__container__print { - .eligibility__table { - border-top: 2px solid $GREY_NEW; - background-color: $WHITE; +@media print { + #results-page { + .eligibility__result .covered-pill { + background-color: unset; + padding: unset; + text-decoration: underline; + text-underline-offset: 0.5rem; + color: $OFF_BLACK; } - .eligibility__table__header { - display: flex; - padding-bottom: $eligibilityTablePaddingH; - flex-direction: column; - gap: 10px; - - .eligibility__table__header-title { - @include eyebrow_small_desktop; - } - - .eligibility__table__header-subtitle { - @include h3_desktop; + .eligibility__row { + &:last-of-type { + border-bottom: unset; } } - .eligibility__table__list { - margin: 0; - padding: 0; - list-style: none; - } - .eligibility__table__footer { display: none; - //display: flex; - flex-direction: column; - align-items: center; - padding: $eligibilityTablePaddingH 0 0; - @include body_large_desktop; - - .eligibility__table__footer__link { - @include body_standard_desktop; - margin-top: 0.625rem; //10px - } - - a { - color: black; - margin-left: 0.625rem; //10px - } } - .eligibility__row { - display: flex; - border-bottom: 1px solid $GREY_NEW; - align-items: center; - flex-direction: row; - text-align: left; - - .eligibility__row__icon { - padding: 0 1.25rem; // 0 20px - font-size: 1.875rem; //30px - width: 1.875rem; //30px - - .eligible { - color: $OFF_BLACK; - } - .ineligible { - color: $OFF_BLACK; - } - .unknown { - color: $OFF_BLACK; - } - } - - .eligibility__row__info { - display: flex; - flex-direction: column; - flex-shrink: 0; - width: 320px; - padding-right: 1rem; - box-sizing: border-box; - gap: 10px; - } + .protections-on-next-page__print { + @include h3_desktop; + text-align: center; + } - .eligibility__row__criteria { - @include eyebrow_small_desktop; + .content-box { + border: unset; + .accordion__chevron { + display: none; } - - .eligibility__row__requirement { - @include body_standard_desktop; + .content-box__footer { + display: none; } - - .eligibility__row__userValue { - @include body_standard_desktop; - margin: 0 1.25rem; // 20px - box-sizing: border-box; - flex-grow: 1; - - p { - margin: 0 0 0.625rem 0; - } - - .jfcl-link { - display: none; - } + } + .eligibility__row__icon, + .eligibility__icon { + svg { + color: black; } } - } -} - -.next-step { - --icon-size: 2.75rem; // 44px; - --icon-margin: 1.5rem; // 24px; - .eligibility__icon { - font-size: 1.875rem; //30px - margin-right: var(--icon-margin); - - .eligible { - color: $GREEN; - } - .ineligible { - color: $RED; - } - .unknown { - color: $YELLOW; + .divider__print { + border-bottom: 1px solid black; } - svg { - width: var(--icon-size); - height: var(--icon-size); + .eligibility__footer { + display: none; } } - .accordion__content { - margin-left: calc(var(--icon-size) + var(--icon-margin)); - } } diff --git a/src/Components/Pages/Results/Results.tsx b/src/Components/Pages/Results/Results.tsx index da22d13..f50a39f 100644 --- a/src/Components/Pages/Results/Results.tsx +++ b/src/Components/Pages/Results/Results.tsx @@ -71,6 +71,34 @@ export const Results: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const handlePrintAccordions = (event: MediaQueryListEvent) => { + let accordions: NodeListOf; + if (event.matches) { + accordions = document.body.querySelectorAll("details:not([open])"); + accordions.forEach((e) => { + e.setAttribute("open", ""); + e.dataset.wasclosed = ""; + }); + } else { + accordions = document.body.querySelectorAll("details[data-wasclosed]"); + accordions.forEach((e) => { + e.removeAttribute("open"); + delete e.dataset.wasclosed; + }); + } + }; + + useEffect(() => { + window + .matchMedia("print") + .addEventListener("change", handlePrintAccordions); + return () => { + window + .matchMedia("print") + .removeEventListener("change", handlePrintAccordions); + }; + }, []); + const bbl = address.bbl; const { data: bldgData, isLoading, error } = useGetBuildingData(bbl); @@ -88,7 +116,6 @@ export const Results: React.FC = () => { title={ bldgData && eligibilityResults ? ( @@ -96,7 +123,6 @@ export const Results: React.FC = () => { "Loading your results..." ) } - subtitle="We'll use your answers to help determine your coverage" address={address} > {error && ( @@ -114,6 +140,9 @@ export const Results: React.FC = () => { )}
+
+ View tenant protection information on following pages +
@@ -215,9 +244,9 @@ const CoveredPill: React.FC<{ const longClassName = `${className} ${className}--${determination.toLowerCase()}`; if (determination === "ELIGIBLE") { - return covered; + return covered; } else if (determination === "INELIGIBLE") { - return not covered; + return not covered; } else { return might be covered; } @@ -261,7 +290,7 @@ const EligibilityCriteriaTable: React.FC<{ Is something not quite right?
- Back to Screener + Back to survey
@@ -279,88 +308,90 @@ const EligibilityNextSteps: React.FC<{ Boolean ).length; return ( - - {rentRegulationUnknown && ( - - - - } - className="next-step" - open - > -

- The Good Cause Eviction law only covers tenants whose apartments are - not rent regulated. You told us that you are unsure of your rent - regulation status. -

- - Learn how to find out - -
- )} + <> + + {rentRegulationUnknown && ( + + + + } + className="next-step" + open + > +

+ The Good Cause Eviction law only covers tenants whose apartments + are not rent regulated. You told us that you are unsure of your + rent regulation status. +

+ + Learn how to find out + +
+ )} - {portfolioSizeUnknown && ( - - - - } - className="next-step" - open - > - {bldgData.related_properties ? ( - <> -

- {`Good Cause Eviction law only covers tenants whose landlord owns + {portfolioSizeUnknown && ( + + + + } + className="next-step" + open + > + {bldgData.related_properties ? ( + <> +

+ {`Good Cause Eviction law only covers tenants whose landlord owns more than 10 units. Your building has only ${bldgData.unitsres} apartments, but your landlord may own other buildings.`} -

+

- - Learn how to find out - - - ) : ( - <> -

- {`Good Cause Eviction law only covers tenants whose landlord owns + + Learn how to find out + + + ) : ( + <> +

+ {`Good Cause Eviction law only covers tenants whose landlord owns more than 10 units. Your building has only ${bldgData.unitsres} apartments.`} -

-
-

- We are unable to find other apartments your landlord might own - in our records. -

- - )} -
- )} - Back to survey} - /> -
+

+
+

+ We are unable to find other apartments your landlord might own + in our records. +

+ + )} + + )} + Back to survey} + /> +
+
+ ); }; const EligibilityResultHeadline: React.FC<{ - address: string; determination: Determination; eligibilityResults: EligibilityResults; -}> = ({ address, determination, eligibilityResults }) => { +}> = ({ determination, eligibilityResults }) => { if (determination === "UNKNOWN") { return ( <> @@ -368,49 +399,28 @@ const EligibilityResultHeadline: React.FC<{ Your apartment{" "} - - {address}{" "} - - - by Good Cause Eviction Law + by Good Cause Eviction ); } else if (determination === "ELIGIBLE") { return ( <> - Your apartment is{" "} + Your apartment is likely{" "} - - {address} is{" "} - - - by Good Cause Eviction law + by Good Cause Eviction ); } else if (eligibilityResults.rentRegulation.determination === "INELIGIBLE") { return ( <> - Your apartment is not covered by Good Cause Eviction law but{" "} + Your apartment is protected by{" "} - you’re protected - {" "} - by rent stabilization laws - - - {address} is not covered by Good Cause Eviction law but{" "} - - you’re protected - {" "} - by rent stabilization laws + rent stabilization + + , which provides stronger protections than Good Cause Eviction ); @@ -418,17 +428,10 @@ const EligibilityResultHeadline: React.FC<{ return ( <> - Your apartment is{" "} + Your apartment is likely{" "} {" "} - - {address} is{" "} - - - by Good Cause Eviction law + by Good Cause Eviction ); } diff --git a/src/Components/Pages/content-page.scss b/src/Components/Pages/content-page.scss index 1dc37ba..f3efa23 100644 --- a/src/Components/Pages/content-page.scss +++ b/src/Components/Pages/content-page.scss @@ -12,10 +12,6 @@ $eligibilityTablePaddingH: 36px; padding-left: 3rem; // 48px padding-right: 3rem; // 48px - @media (max-width: 772px) { - padding-left: 1.5rem; // 24px - padding-right: 1.5rem; // 24px - } } .headline-section { @@ -37,38 +33,17 @@ $eligibilityTablePaddingH: 36px; .headline-section__page-type { @include eyebrow_large_desktop; padding-top: 30px; - @media print { - display: none; - } - } - .headline-section__page-type__print { - @include eyebrow_large_desktop; - @media screen { - display: none; - } } .headline-section__title { display: flex; flex-direction: column; gap: 4px; - @media print { - margin: 24px 0; - } } .headline-section__subtitle { @include body_large_desktop; margin-top: 1.125rem; // 18px - @media print { - margin-bottom: 0; - } - } - - .jfcl-button { - @media print { - display: none; - } } } } @@ -83,3 +58,31 @@ $eligibilityTablePaddingH: 36px; flex-direction: column; } } + +@media screen { + .headline-section, + .content-section { + @media (max-width: 772px) { + padding-left: 1.5rem; // 24px + padding-right: 1.5rem; // 24px + } + } + .headline-section__address__print { + @media screen { + display: none; + } + } +} +@media print { + .headline-section, + .content-section { + background-color: unset; + border-bottom: unset; + padding-top: 0; + padding-bottom: 0; + } + + .headline-section__address__print { + @include body_large_desktop; + } +} diff --git a/src/mixins.scss b/src/mixins.scss index 8b9e235..cafe185 100644 --- a/src/mixins.scss +++ b/src/mixins.scss @@ -8,7 +8,7 @@ } @mixin h3_desktop { - font-size: 36px; + font-size: 2.25rem; // 36px font-style: normal; font-weight: 600; line-height: 100%; /* 36px */ From cf09b0e91ace3b2cddb34cb52d9afdee27291a3a Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Thu, 19 Dec 2024 12:07:34 -0500 Subject: [PATCH 2/7] fix cut off content --- src/Components/Pages/Results/Results.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Components/Pages/Results/Results.scss b/src/Components/Pages/Results/Results.scss index 9f7d1f4..1b26db4 100644 --- a/src/Components/Pages/Results/Results.scss +++ b/src/Components/Pages/Results/Results.scss @@ -214,6 +214,7 @@ $eligibilityTablePaddingH: 36px; } .content-box { + margin-top: unset; border: unset; .accordion__chevron { display: none; From 403b7c8d12dad6b234edb24b14441d71bd2407ff Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Thu, 19 Dec 2024 12:29:55 -0500 Subject: [PATCH 3/7] change listeners from media change to before/after print --- src/Components/Pages/Results/Results.scss | 1 - src/Components/Pages/Results/Results.tsx | 41 +++++++++++------------ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/Components/Pages/Results/Results.scss b/src/Components/Pages/Results/Results.scss index 1b26db4..9f7d1f4 100644 --- a/src/Components/Pages/Results/Results.scss +++ b/src/Components/Pages/Results/Results.scss @@ -214,7 +214,6 @@ $eligibilityTablePaddingH: 36px; } .content-box { - margin-top: unset; border: unset; .accordion__chevron { display: none; diff --git a/src/Components/Pages/Results/Results.tsx b/src/Components/Pages/Results/Results.tsx index f50a39f..a9f69f5 100644 --- a/src/Components/Pages/Results/Results.tsx +++ b/src/Components/Pages/Results/Results.tsx @@ -71,31 +71,30 @@ export const Results: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const handlePrintAccordions = (event: MediaQueryListEvent) => { - let accordions: NodeListOf; - if (event.matches) { - accordions = document.body.querySelectorAll("details:not([open])"); - accordions.forEach((e) => { - e.setAttribute("open", ""); - e.dataset.wasclosed = ""; - }); - } else { - accordions = document.body.querySelectorAll("details[data-wasclosed]"); - accordions.forEach((e) => { - e.removeAttribute("open"); - delete e.dataset.wasclosed; - }); - } + const openAccordionsPrint = () => { + const accordions: NodeListOf = + document.body.querySelectorAll("details:not([open])"); + accordions.forEach((e) => { + e.setAttribute("open", ""); + e.dataset.wasclosed = ""; + }); + }; + + const closeAccordionsPrint = () => { + const accordions: NodeListOf = + document.body.querySelectorAll("details[data-wasclosed]"); + accordions.forEach((e) => { + e.removeAttribute("open"); + delete e.dataset.wasclosed; + }); }; useEffect(() => { - window - .matchMedia("print") - .addEventListener("change", handlePrintAccordions); + window.addEventListener("beforeprint", openAccordionsPrint); + window.addEventListener("afterprint", closeAccordionsPrint); return () => { - window - .matchMedia("print") - .removeEventListener("change", handlePrintAccordions); + window.removeEventListener("beforeprint", openAccordionsPrint); + window.removeEventListener("afterprint", closeAccordionsPrint); }; }, []); From 5b9f006221bfade53e1af689727d041ada5bb1c0 Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Fri, 20 Dec 2024 11:56:24 -0500 Subject: [PATCH 4/7] reorganize result page, rename types, add nycha result & content --- src/Components/ContentBox/ContentBox.scss | 4 + src/Components/KYRContent/KYRContent.tsx | 69 ++++-- src/Components/Pages/Results/Results.scss | 19 +- src/Components/Pages/Results/Results.tsx | 214 +++++++++--------- .../Pages/TenantRights/TenantRights.tsx | 2 +- src/api/helpers.ts | 35 +-- src/helpers.ts | 21 -- src/hooks/eligibility.tsx | 50 ++-- src/types/APIDataTypes.ts | 29 ++- 9 files changed, 215 insertions(+), 228 deletions(-) diff --git a/src/Components/ContentBox/ContentBox.scss b/src/Components/ContentBox/ContentBox.scss index c737c56..80f154d 100644 --- a/src/Components/ContentBox/ContentBox.scss +++ b/src/Components/ContentBox/ContentBox.scss @@ -87,6 +87,10 @@ $videoEmbedW: 623px; margin-top: 0; } } + + & > .jfcl-link:first-of-type { + margin-top: 0; + } } &.no-title { diff --git a/src/Components/KYRContent/KYRContent.tsx b/src/Components/KYRContent/KYRContent.tsx index 17c04bc..264c41e 100644 --- a/src/Components/KYRContent/KYRContent.tsx +++ b/src/Components/KYRContent/KYRContent.tsx @@ -266,44 +266,67 @@ export const RentStabilizedProtections: React.FC = ({ export const NYCHAProtections: React.FC = ({ title = "KNOW YOUR RIGHTS", - subtitle = "Protections you have if you live in NYCHA housing", + subtitle = "Protections you have as a NYCHA tenant", children, }) => ( <> - +

- For rent-stabilized leases being renewed between October 1, 2024 and - September 30, 2025 the legal rent may be increased at the following - levels: for a one-year renewal there is a 2.75% increase, or for a - two-year renewal there is a 5.25% increase. + Everyone has the right to live in a safe and habitable environment. + This includes timely repairs to their apartment to address any + maintenance issues that may arise. NYCHA Residents have the right to + request repairs and expect prompt action from management. +

+
+

+ If NYCHA is not responding to ticket requests, residents can file + housing court cases (HP actions) seeking a judicial order requiring + NYCHA to make prompt repairs.

- - Learn about rent increase rights -
- +

- If you are rent-stabilized your landlord cannot simply decide they - don’t want you as a tenant anymore, they are limited to certain - reasons for evicting you. + This ensures that housing remains affordable and equitable for all + residents, regardless of financial circumstances. Residents who have + recently experienced a change in income can request an interim + recertification to ensure that their apartment remains affordable.

- - Learn about lease renewal rights -
- +

- If you are the immediate family member of a rent-stabilized tenant and - have been living with them immediately prior to their moving or - passing away, you might be entitled to take over the lease. + In instances where residents disagree with management decisions or + believe their rights have been violated, they have the right to grieve + these issues through a formal process. +

+
+

+ Residents can file grievances online through the resident portal or on + paper by visiting the management office.

- - Learn about succession rights -
+ + +

+ Legal representation can make a significant difference in ensuring a + fair and just resolution to housing disputes, protecting residents + from wrongful eviction or other adverse outcomes. +

+
+

+ If you do not have a lawyer and are facing a termination of tenancy at + the Office of Impartial Hearings, you can ask for your case to be + adjourned in order for you to seek counsel. +

+
+ + + NY Legal Assistance Group + PACT Fact Sheet + + {children}
diff --git a/src/Components/Pages/Results/Results.scss b/src/Components/Pages/Results/Results.scss index 9f7d1f4..fca11be 100644 --- a/src/Components/Pages/Results/Results.scss +++ b/src/Components/Pages/Results/Results.scss @@ -11,24 +11,25 @@ $eligibilityTablePaddingH: 36px; margin: 3rem 0; } - .covered-pill { + .coverage-pill { display: inline-flex; padding: 0px 18px 4px; - color: $WHITE; border-radius: 44px; - background-color: $GREEN; white-space: nowrap; height: calc(1em + 4px); - &.covered-pill--ineligible { - background-color: $ORANGE; - color: $OFF_BLACK; + &.green { + background-color: $GREEN; + color: $WHITE; } - - &.covered-pill--unknown { + &.yellow { background-color: $YELLOW; color: $OFF_BLACK; } + &.orange { + background-color: $ORANGE; + color: $OFF_BLACK; + } } .eligibility__table, @@ -190,7 +191,7 @@ $eligibilityTablePaddingH: 36px; @media print { #results-page { - .eligibility__result .covered-pill { + .coverage-pill { background-color: unset; padding: unset; text-decoration: underline; diff --git a/src/Components/Pages/Results/Results.tsx b/src/Components/Pages/Results/Results.tsx index a9f69f5..d5dcc69 100644 --- a/src/Components/Pages/Results/Results.tsx +++ b/src/Components/Pages/Results/Results.tsx @@ -3,20 +3,21 @@ import { Link, useLoaderData, useSearchParams } from "react-router-dom"; import { Button, Icon } from "@justfixnyc/component-library"; import { useGetBuildingData, useSendGceData } from "../../../api/hooks"; -import { BuildingData, GCEUser } from "../../../types/APIDataTypes"; +import { + BuildingData, + CoverageResult, + CriteriaResults, + CriterionResult, + GCEUser, +} from "../../../types/APIDataTypes"; import { FormFields } from "../Form/Form"; import { - CriteriaEligibility, - Determination, - EligibilityResults, - useEligibility, + CriterionDetails, + CriteriaDetails, + useCriteriaResults as useCriteriaDetails, } from "../../../hooks/eligibility"; -import { - determinationToCoverage, - extractDeterminations, -} from "../../../api/helpers"; +import { getCriteriaResults } from "../../../api/helpers"; import { Address } from "../Home/Home"; -import { getDetermination } from "../../../helpers"; import { ContentBox, ContentBoxFooter, @@ -40,6 +41,11 @@ export const Results: React.FC = () => { }; const [, setSearchParams] = useSearchParams(); const { trigger } = useSendGceData(); + const bbl = address.bbl; + const { data: bldgData, isLoading, error } = useGetBuildingData(bbl); + const criteriaDetails = useCriteriaDetails(fields, bldgData); + const criteriaResults = getCriteriaResults(criteriaDetails); + const coverageResult = getCoverageResult(fields, criteriaResults); useEffect(() => { // save session state in params @@ -57,12 +63,12 @@ export const Results: React.FC = () => { }, []); useEffect(() => { - if (determination && eligibilityResults) { + if (coverageResult && criteriaResults) { try { trigger({ id: user?.id, - result_coverage: determinationToCoverage(determination), - result_criteria: extractDeterminations(eligibilityResults), + result_coverage: coverageResult, + result_criteria: criteriaResults, }); } catch (error) { console.log({ "tenants2-error": error }); @@ -98,26 +104,12 @@ export const Results: React.FC = () => { }; }, []); - const bbl = address.bbl; - - const { data: bldgData, isLoading, error } = useGetBuildingData(bbl); - - const eligibilityResults = useEligibility(fields, bldgData); - - const determination = getDetermination(eligibilityResults); - - const isRentStabilized = - eligibilityResults?.rentRegulation.determination === "INELIGIBLE"; - return (
+ bldgData && coverageResult ? ( + ) : ( "Loading your results..." ) @@ -135,9 +127,7 @@ export const Results: React.FC = () => { )}
- {bldgData && ( - - )} + {bldgData && }
View tenant protection information on following pages @@ -146,21 +136,23 @@ export const Results: React.FC = () => {
- {determination === "UNKNOWN" && bldgData && eligibilityResults && ( + {coverageResult === "UNKNOWN" && bldgData && criteriaDetails && ( )} - {isRentStabilized && } - {determination === "UNKNOWN" && ( + {coverageResult === "RENT_STABILIZED" && ( + + )} + {coverageResult === "UNKNOWN" && ( <> )} - {determination === "ELIGIBLE" && ( + {coverageResult === "COVERED" && ( <> @@ -193,7 +185,7 @@ const CRITERIA_LABELS = { certificateOfOccupancy: "Certificate of Occupancy", }; -const EligibilityIcon: React.FC<{ determination?: Determination }> = ({ +const EligibilityIcon: React.FC<{ determination?: CriterionResult }> = ({ determination, }) => { switch (determination) { @@ -211,7 +203,7 @@ const EligibilityIcon: React.FC<{ determination?: Determination }> = ({ } }; -const CriteriaResult: React.FC = (props) => { +const CriterionRow: React.FC = (props) => { return (
  • @@ -236,24 +228,9 @@ const CriteriaResult: React.FC = (props) => { ); }; -const CoveredPill: React.FC<{ - determination: Determination; - className: string; -}> = ({ determination, className }) => { - const longClassName = `${className} ${className}--${determination.toLowerCase()}`; - - if (determination === "ELIGIBLE") { - return covered; - } else if (determination === "INELIGIBLE") { - return not covered; - } else { - return might be covered; - } -}; - -const EligibilityCriteriaTable: React.FC<{ - eligibilityResults: EligibilityResults | undefined; -}> = ({ eligibilityResults }) => ( +const CriteriaTable: React.FC<{ + criteria?: CriteriaDetails; +}> = ({ criteria }) => (
    @@ -265,24 +242,16 @@ const EligibilityCriteriaTable: React.FC<{

      - {eligibilityResults?.rent && ( - - )} - {eligibilityResults?.rentRegulation && ( - - )} - {eligibilityResults?.buildingClass && ( - + {criteria?.rent && } + {criteria?.rentRegulation && ( + )} - {eligibilityResults?.certificateOfOccupancy && ( - - )} - {eligibilityResults?.subsidy && ( - - )} - {eligibilityResults?.portfolioSize && ( - + {criteria?.buildingClass && } + {criteria?.certificateOfOccupancy && ( + )} + {criteria?.subsidy && } + {criteria?.portfolioSize && }
    @@ -297,7 +266,7 @@ const EligibilityCriteriaTable: React.FC<{ const EligibilityNextSteps: React.FC<{ bldgData: BuildingData; - eligibilityResults: EligibilityResults; + eligibilityResults: CriteriaDetails; }> = ({ bldgData, eligibilityResults }) => { const portfolioSizeUnknown = eligibilityResults?.portfolioSize?.determination === "UNKNOWN"; @@ -387,51 +356,72 @@ const EligibilityNextSteps: React.FC<{ ); }; -const EligibilityResultHeadline: React.FC<{ - determination: Determination; - eligibilityResults: EligibilityResults; -}> = ({ determination, eligibilityResults }) => { - if (determination === "UNKNOWN") { - return ( - <> - +const getCoverageResult = ( + fields?: FormFields, + criteriaResults?: CriteriaResults +): CoverageResult | undefined => { + if (!fields || !criteriaResults) { + return undefined; + } + + const results = Object.values(criteriaResults); + + if (fields?.housingType === "NYCHA") { + return "NYCHA"; + } else if (fields?.rentStabilized === "YES") { + return "RENT_STABILIZED"; + } else if (results.includes("INELIGIBLE")) { + return "NOT_COVERED"; + } else if (results.includes("UNKNOWN")) { + return "UNKNOWN"; + } else { + return "COVERED"; + } +}; + +const CoverageResultHeadline: React.FC<{ + result: CoverageResult; +}> = ({ result }) => { + switch (result) { + case "UNKNOWN": + return ( + Your apartment{" "} - + might be covered by Good + Cause Eviction - by Good Cause Eviction - - ); - } else if (determination === "ELIGIBLE") { - return ( - <> - + ); + case "NOT_COVERED": + return ( + Your apartment is likely{" "} - + not covered by Good + Cause Eviction - by Good Cause Eviction - - ); - } else if (eligibilityResults.rentRegulation.determination === "INELIGIBLE") { - return ( - <> - + ); + case "RENT_STABILIZED": + return ( + Your apartment is protected by{" "} - - rent stabilization - - , which provides stronger protections than Good Cause Eviction + rent stabilization, which + provides stronger protections than Good Cause Eviction - - ); - } else if (determination === "INELIGIBLE") { - return ( - <> - + ); + case "COVERED": + return ( + Your apartment is likely{" "} - {" "} + covered by Good Cause + Eviction - by Good Cause Eviction - - ); + ); + case "NYCHA": + return ( + + Your apartment is part of{" "} + NYCHA, which provides + stronger protections than Good Cause Eviction + + ); } }; diff --git a/src/Components/Pages/TenantRights/TenantRights.tsx b/src/Components/Pages/TenantRights/TenantRights.tsx index 87d87d5..e434abd 100644 --- a/src/Components/Pages/TenantRights/TenantRights.tsx +++ b/src/Components/Pages/TenantRights/TenantRights.tsx @@ -32,7 +32,7 @@ export const TenantRights: React.FC = () => { View the guide diff --git a/src/api/helpers.ts b/src/api/helpers.ts index ea25209..131904c 100644 --- a/src/api/helpers.ts +++ b/src/api/helpers.ts @@ -1,8 +1,7 @@ import { FormFields } from "../Components/Pages/Form/Form"; -import { Determination, EligibilityResults } from "../hooks/eligibility"; +import { CriteriaDetails } from "../hooks/eligibility"; import { - Coverage, - ResultCriteria, + CriteriaResults, FormAnswers, GCEPostData, } from "../types/APIDataTypes"; @@ -85,28 +84,16 @@ export const cleanFormFields = ({ }; }; -// Recodes values for database model -export const determinationToCoverage = ( - determination: Determination -): Coverage => { - const COVERAGE: { [key in Determination]: Coverage } = { - ELIGIBLE: "COVERED", - INELIGIBLE: "NOT_COVERED", - UNKNOWN: "UNKNOWN", - }; - return COVERAGE[determination]; -}; - // Restructures criteria eligibility for database model -export const extractDeterminations = ( - eligibilityResults: EligibilityResults -): ResultCriteria => { +export const getCriteriaResults = ( + criteriaDetails?: CriteriaDetails +): CriteriaResults => { return { - rent: eligibilityResults.rent?.determination, - rent_stab: eligibilityResults.rentRegulation?.determination, - building_class: eligibilityResults.buildingClass?.determination, - c_of_o: eligibilityResults.certificateOfOccupancy?.determination, - subsidy: eligibilityResults.subsidy?.determination, - portfolio_size: eligibilityResults.portfolioSize?.determination, + rent: criteriaDetails?.rent?.determination, + rent_stab: criteriaDetails?.rentRegulation?.determination, + building_class: criteriaDetails?.buildingClass?.determination, + c_of_o: criteriaDetails?.certificateOfOccupancy?.determination, + subsidy: criteriaDetails?.subsidy?.determination, + portfolio_size: criteriaDetails?.portfolioSize?.determination, }; }; diff --git a/src/helpers.ts b/src/helpers.ts index 4ae257c..6377422 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,5 +1,4 @@ import { GeoSearchProperties } from "@justfixnyc/geosearch-requester"; -import { EligibilityResults, Determination } from "./hooks/eligibility"; import { RelatedProperty } from "./types/APIDataTypes"; import { Address } from "./Components/Pages/Home/Home"; @@ -30,26 +29,6 @@ export const formatGeosearchAddress = ( }` : ""; -export const getDetermination = ( - eligibilityResults?: EligibilityResults -): Determination => { - if (!eligibilityResults) { - return "UNKNOWN"; - } - - const determinations = Object.values(eligibilityResults).map((criteria) => { - return criteria.determination; - }); - - if (determinations.includes("INELIGIBLE")) { - return "INELIGIBLE"; - } else if (determinations.includes("UNKNOWN")) { - return "UNKNOWN"; - } else { - return "ELIGIBLE"; - } -}; - export const splitBBL = ( bbl: string ): { diff --git a/src/hooks/eligibility.tsx b/src/hooks/eligibility.tsx index 284834a..1bdf62c 100644 --- a/src/hooks/eligibility.tsx +++ b/src/hooks/eligibility.tsx @@ -1,6 +1,6 @@ import { FormFields } from "../Components/Pages/Form/Form"; import JFCLLinkInternal from "../Components/JFCLLinkInternal"; -import { BuildingData } from "../types/APIDataTypes"; +import { BuildingData, CriterionResult } from "../types/APIDataTypes"; export type Criteria = | "portfolioSize" @@ -12,29 +12,27 @@ export type Criteria = type CriteriaData = FormFields & BuildingData; -export type Determination = "ELIGIBLE" | "INELIGIBLE" | "UNKNOWN"; - -export type CriteriaEligibility = { +export type CriterionDetails = { criteria: Criteria; - determination?: Determination; + determination?: CriterionResult; requirement?: React.ReactNode; userValue?: React.ReactNode; moreInfo?: React.ReactNode; }; -export type EligibilityResults = { - rent: CriteriaEligibility; - rentRegulation: CriteriaEligibility; - portfolioSize?: CriteriaEligibility; - buildingClass?: CriteriaEligibility; - subsidy?: CriteriaEligibility; - certificateOfOccupancy?: CriteriaEligibility; +export type CriteriaDetails = { + rent: CriterionDetails; + rentRegulation: CriterionDetails; + portfolioSize?: CriterionDetails; + buildingClass?: CriterionDetails; + subsidy?: CriterionDetails; + certificateOfOccupancy?: CriterionDetails; }; -export function useEligibility( +export function useCriteriaResults( formFields: FormFields, bldgData?: BuildingData -): EligibilityResults | undefined { +): CriteriaDetails | undefined { if (!bldgData) return undefined; const criteriaData: CriteriaData = { ...formFields, ...bldgData }; return { @@ -49,7 +47,7 @@ export function useEligibility( function eligibilityPortfolioSize( criteriaData: CriteriaData -): CriteriaEligibility { +): CriterionDetails { const { unitsres, wow_portfolio_units, @@ -60,7 +58,7 @@ function eligibilityPortfolioSize( const criteria = "portfolioSize"; const requirement = <>Landlord must own more than 10 apartments; - let determination: Determination; + let determination: CriterionResult; let userValue: React.ReactNode; if (unitsres === undefined) { @@ -122,7 +120,7 @@ function eligibilityPortfolioSize( }; } -function eligibilityRent(criteriaData: CriteriaData): CriteriaEligibility { +function eligibilityRent(criteriaData: CriteriaData): CriterionDetails { const { bedrooms, rent: rentString } = criteriaData; const rent = parseFloat(rentString || ""); @@ -135,7 +133,7 @@ function eligibilityRent(criteriaData: CriteriaData): CriteriaEligibility { "4+": "$9,065", }; const criteria = "rent"; - let determination: Determination; + let determination: CriterionResult; // should remove null from type for all form fields, since required if (rent === null || bedrooms === null) return { criteria }; @@ -172,11 +170,11 @@ function eligibilityRent(criteriaData: CriteriaData): CriteriaEligibility { function eligibilityRentRegulated( criteriaData: CriteriaData -): CriteriaEligibility { +): CriterionDetails { const { rentStabilized } = criteriaData; const criteria = "rentRegulation"; const requirement = "Your apartment must not be rent stabilized."; - let determination: Determination; + let determination: CriterionResult; let userValue: React.ReactNode; // should remove null from type for all form fields, since required @@ -210,13 +208,13 @@ function eligibilityRentRegulated( function eligibilityBuildingClass( criteriaData: CriteriaData -): CriteriaEligibility { +): CriterionDetails { const { bldgclass, bbl } = criteriaData; const criteria = "buildingClass"; const requirement = ( <>Your building must not be a condo, co-op, or other exempt category. ); - let determination: Determination; + let determination: CriterionResult; let bldgTypeName = ""; if (bbl === "5013920002") { @@ -267,7 +265,7 @@ function eligibilityBuildingClass( function eligibilityCertificateOfOccupancy( criteriaData: CriteriaData -): CriteriaEligibility { +): CriterionDetails { const { co_issued } = criteriaData; const cutoffYear = 2009; const cutoffDate = new Date(cutoffYear, 1, 1); @@ -280,7 +278,7 @@ function eligibilityCertificateOfOccupancy( const criteria = "certificateOfOccupancy"; const requirement = "Your building must have received its certificate of occupancy before 2009."; - let determination: Determination; + let determination: CriterionResult; let userValue: React.ReactNode; if (co_issued === null || latestCoDate < cutoffDate) { @@ -300,11 +298,11 @@ function eligibilityCertificateOfOccupancy( }; } -function eligibilitySubsidy(criteriaData: CriteriaData): CriteriaEligibility { +function eligibilitySubsidy(criteriaData: CriteriaData): CriterionDetails { const { housingType, is_nycha, is_subsidized } = criteriaData; const criteria = "subsidy"; const requirement = <>You must not live in subsidized or public housing.; - let determination: Determination; + let determination: CriterionResult; let userValue: React.ReactNode; let moreInfo: React.ReactNode; diff --git a/src/types/APIDataTypes.ts b/src/types/APIDataTypes.ts index db54b94..972ef62 100644 --- a/src/types/APIDataTypes.ts +++ b/src/types/APIDataTypes.ts @@ -1,5 +1,3 @@ -import { Determination } from "../hooks/eligibility"; - // WOW API export type AcrisDocument = { @@ -46,15 +44,22 @@ export type GCEUser = { id: number; }; -export type Coverage = "COVERED" | "NOT_COVERED" | "UNKNOWN"; +export type CriterionResult = "ELIGIBLE" | "INELIGIBLE" | "UNKNOWN"; + +export type CoverageResult = + | "COVERED" + | "NOT_COVERED" + | "UNKNOWN" + | "RENT_STABILIZED" + | "NYCHA"; -export type ResultCriteria = { - rent?: Determination; - rent_stab?: Determination; - building_class?: Determination; - c_of_o?: Determination; - subsidy?: Determination; - portfolio_size?: Determination; +export type CriteriaResults = { + rent?: CriterionResult; + rent_stab?: CriterionResult; + building_class?: CriterionResult; + c_of_o?: CriterionResult; + subsidy?: CriterionResult; + portfolio_size?: CriterionResult; }; export type FormAnswers = { @@ -75,6 +80,6 @@ export type GCEPostData = { address_confirmed?: boolean; nycdb_results?: BuildingData; form_answers?: FormAnswers; - result_coverage?: Coverage; - result_criteria?: ResultCriteria; + result_coverage?: CoverageResult; + result_criteria?: CriteriaResults; }; From c245414a564398225e604cd1b25570bde19b0afc Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Fri, 20 Dec 2024 12:27:53 -0500 Subject: [PATCH 5/7] add share button copy url --- package.json | 2 +- .../Pages/PortfolioSize/PortfolioSize.tsx | 2 +- src/Components/Pages/Results/Results.scss | 13 ++++++---- src/Components/Pages/Results/Results.tsx | 24 +++++++++---------- yarn.lock | 8 +++---- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index e488376..c856e2a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "format": "prettier --write --ignore-unknown \"src/**/*\"" }, "dependencies": { - "@justfixnyc/component-library": "0.53.4", + "@justfixnyc/component-library": "0.53.5", "@justfixnyc/geosearch-requester": "^1.0.3-alpha.0", "@rollbar/react": "^0.12.0-beta", "react": "^18.3.1", diff --git a/src/Components/Pages/PortfolioSize/PortfolioSize.tsx b/src/Components/Pages/PortfolioSize/PortfolioSize.tsx index fdf1ce1..b52c700 100644 --- a/src/Components/Pages/PortfolioSize/PortfolioSize.tsx +++ b/src/Components/Pages/PortfolioSize/PortfolioSize.tsx @@ -57,7 +57,7 @@ export const PortfolioSize: React.FC = () => { isGuide > {error && ( -
    +
    There was an error loading your results, please try again in a few minutes.
    diff --git a/src/Components/Pages/Results/Results.scss b/src/Components/Pages/Results/Results.scss index fca11be..1384bc2 100644 --- a/src/Components/Pages/Results/Results.scss +++ b/src/Components/Pages/Results/Results.scss @@ -6,8 +6,8 @@ $eligibilityTablePaddingH: 36px; #results-page { - .eligibility__loading, - .eligibility__error { + .data__loading, + .data__error { margin: 3rem 0; } @@ -134,17 +134,20 @@ $eligibilityTablePaddingH: 36px; color: $YELLOW; } - .eligibility__footer { + .share-footer { display: flex; flex-direction: column; align-items: center; margin: 2.625rem 0; //42px - .eligibility__footer__header { + .share-footer__header { @include h3_desktop; margin: 0 0 $eligibilityTablePaddingH 0; text-align: center; } + .jfcl-button { + color: $OFF_WHITE_100; + } } .next-step { @@ -234,7 +237,7 @@ $eligibilityTablePaddingH: 36px; border-bottom: 1px solid black; } - .eligibility__footer { + .share-footer { display: none; } } diff --git a/src/Components/Pages/Results/Results.tsx b/src/Components/Pages/Results/Results.tsx index d5dcc69..bdbebd0 100644 --- a/src/Components/Pages/Results/Results.tsx +++ b/src/Components/Pages/Results/Results.tsx @@ -52,7 +52,7 @@ export const Results: React.FC = () => { if (address && fields) { setSearchParams( { - ...(!user && { user: JSON.stringify(user) }), + ...(!!user?.id && { user: JSON.stringify(user) }), address: JSON.stringify(address), fields: JSON.stringify(fields), }, @@ -117,18 +117,16 @@ export const Results: React.FC = () => { address={address} > {error && ( -
    +
    There was an error loading your results, please try again in a few minutes.
    )} {isLoading && ( -
    Loading your results...
    +
    Loading your results...
    )} -
    - {bldgData && } -
    + {bldgData && }
    View tenant protection information on following pages
    @@ -160,14 +158,16 @@ export const Results: React.FC = () => { )} -
    -

    - Help others understand their coverage +
    +

    + Help your neighbors learn if they’re covered{" "}

    diff --git a/yarn.lock b/yarn.lock index 4b99a03..67c7b63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -450,10 +450,10 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@justfixnyc/component-library@0.53.4": - version "0.53.4" - resolved "https://registry.yarnpkg.com/@justfixnyc/component-library/-/component-library-0.53.4.tgz#838f7c0c0d0cec41edd46dfd8e103cb4e655f9f8" - integrity sha512-ddXH/cJfmlL/Ujy48BT/spJ+PvENpKVzPbR2v0cR2NMflrlOqnza/WeDgocBRU4cwR5lCC6OLCptDd7y5izQrQ== +"@justfixnyc/component-library@0.53.5": + version "0.53.5" + resolved "https://registry.yarnpkg.com/@justfixnyc/component-library/-/component-library-0.53.5.tgz#5df3b76f00746c07165a84368f9fdc06ea4a5363" + integrity sha512-EciGdYItSA9PDlFQiKFpgB3eaD/ecR7EoGmKBcxWdoZveKt+y0zpT4Ljpz3pvjgQHFjdAfmXa+RE5Ib5+NTiIQ== dependencies: "@awesome.me/kit-dd32553919" "^1.0.13" "@babel/runtime" "^7.12.5" From 1ffe34e81a14e55994f628f59027b8e838e60481 Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Fri, 20 Dec 2024 14:21:36 -0500 Subject: [PATCH 6/7] update criteria table icons --- public/check-plus.svg | 5 ++ src/Components/CheckPlusIcon.tsx | 34 ++++++++ src/Components/Pages/Results/Results.scss | 33 ++++--- src/Components/Pages/Results/Results.tsx | 100 +++++++++++----------- src/api/helpers.ts | 2 +- src/hooks/eligibility.tsx | 16 ++-- 6 files changed, 115 insertions(+), 75 deletions(-) create mode 100644 public/check-plus.svg create mode 100644 src/Components/CheckPlusIcon.tsx diff --git a/public/check-plus.svg b/public/check-plus.svg new file mode 100644 index 0000000..7ab10f3 --- /dev/null +++ b/public/check-plus.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Components/CheckPlusIcon.tsx b/src/Components/CheckPlusIcon.tsx new file mode 100644 index 0000000..bcb781c --- /dev/null +++ b/src/Components/CheckPlusIcon.tsx @@ -0,0 +1,34 @@ +import classNames from "classnames"; + +export const CheckPlusIcon: React.FC<{ + className?: string; + title?: string; +}> = ({ className, title }) => ( + + {title && {title}} + + + + +); diff --git a/src/Components/Pages/Results/Results.scss b/src/Components/Pages/Results/Results.scss index 1384bc2..4758955 100644 --- a/src/Components/Pages/Results/Results.scss +++ b/src/Components/Pages/Results/Results.scss @@ -88,7 +88,7 @@ $eligibilityTablePaddingH: 36px; align-items: center; padding: 20px 0; - .eligibility__row__icon { + .criteria-icon { padding: 0 1.25rem; // 0 20px font-size: 1.875rem; //30px width: 1.875rem; //30px @@ -124,14 +124,16 @@ $eligibilityTablePaddingH: 36px; } } - .eligible { - color: $GREEN; - } - .ineligible { - color: $YELLOW; - } - .unknown { - color: $YELLOW; + svg.criteria-icon { + &.green { + color: $GREEN; + } + &.yellow { + color: $YELLOW; + } + &.orange { + color: $ORANGE; + } } .share-footer { @@ -154,14 +156,10 @@ $eligibilityTablePaddingH: 36px; --icon-size: 2.75rem; // 44px; --icon-margin: 1.5rem; // 24px; - .eligibility__icon { - font-size: 1.875rem; //30px + .criteria-icon { + width: var(--icon-size); + height: var(--icon-size); margin-right: var(--icon-margin); - - svg { - width: var(--icon-size); - height: var(--icon-size); - } } .accordion__content { margin-left: calc(var(--icon-size) + var(--icon-margin)); @@ -226,8 +224,7 @@ $eligibilityTablePaddingH: 36px; display: none; } } - .eligibility__row__icon, - .eligibility__icon { + .criteria-icon { svg { color: black; } diff --git a/src/Components/Pages/Results/Results.tsx b/src/Components/Pages/Results/Results.tsx index bdbebd0..8b7c41e 100644 --- a/src/Components/Pages/Results/Results.tsx +++ b/src/Components/Pages/Results/Results.tsx @@ -7,7 +7,6 @@ import { BuildingData, CoverageResult, CriteriaResults, - CriterionResult, GCEUser, } from "../../../types/APIDataTypes"; import { FormFields } from "../Form/Form"; @@ -32,6 +31,7 @@ import { } from "../../KYRContent/KYRContent"; import { Header } from "../../Header/Header"; import "./Results.scss"; +import { CheckPlusIcon } from "../../CheckPlusIcon"; export const Results: React.FC = () => { const { address, fields, user } = useLoaderData() as { @@ -137,7 +137,7 @@ export const Results: React.FC = () => { {coverageResult === "UNKNOWN" && bldgData && criteriaDetails && ( )} @@ -181,40 +181,45 @@ const CRITERIA_LABELS = { buildingClass: "Type of building", rent: "Rent", subsidy: "Subsidy", - rentRegulation: "Rent stabilization", + rentStabilized: "Rent stabilization", certificateOfOccupancy: "Certificate of Occupancy", }; -const EligibilityIcon: React.FC<{ determination?: CriterionResult }> = ({ - determination, -}) => { - switch (determination) { - case "ELIGIBLE": - return ; - default: - return ( - - ); +const EligibilityIcon: React.FC< + Pick +> = ({ criteria, determination }) => { + if ( + ["subsidy", "rentStabilized"].includes(criteria) && + determination === "INELIGIBLE" + ) { + return ; + } else if (determination === "ELIGIBLE") { + return ( + + ); + } else { + return ( + + ); } }; const CriterionRow: React.FC = (props) => { return (
  • - - {props.criteria === "rentRegulation" && - props.determination === "INELIGIBLE" ? ( - - ) : ( - - )} - - +
    {CRITERIA_LABELS[props?.criteria]} @@ -243,8 +248,8 @@ const CriteriaTable: React.FC<{
      {criteria?.rent && } - {criteria?.rentRegulation && ( - + {criteria?.rentStabilized && ( + )} {criteria?.buildingClass && } {criteria?.certificateOfOccupancy && ( @@ -266,15 +271,22 @@ const CriteriaTable: React.FC<{ const EligibilityNextSteps: React.FC<{ bldgData: BuildingData; - eligibilityResults: CriteriaDetails; -}> = ({ bldgData, eligibilityResults }) => { + criteriaDetails: CriteriaDetails; +}> = ({ bldgData, criteriaDetails }) => { const portfolioSizeUnknown = - eligibilityResults?.portfolioSize?.determination === "UNKNOWN"; - const rentRegulationUnknown = - eligibilityResults?.rentRegulation?.determination === "UNKNOWN"; - const steps = [portfolioSizeUnknown, rentRegulationUnknown].filter( + criteriaDetails?.portfolioSize?.determination === "UNKNOWN"; + const rentStabilizedUnknown = + criteriaDetails?.rentStabilized?.determination === "UNKNOWN"; + const steps = [portfolioSizeUnknown, rentStabilizedUnknown].filter( Boolean ).length; + const unsureIcon = ( + + ); return ( <> - {rentRegulationUnknown && ( + {rentStabilizedUnknown && ( - - - } + icon={unsureIcon} className="next-step" open >

      The Good Cause Eviction law only covers tenants whose apartments - are not rent regulated. You told us that you are unsure of your - rent regulation status. + are not rent stabilized. You told us that you are unsure of your + rent stabilized status.

      Learn how to find out @@ -310,11 +318,7 @@ const EligibilityNextSteps: React.FC<{ {portfolioSizeUnknown && ( - - - } + icon={unsureIcon} className="next-step" open > diff --git a/src/api/helpers.ts b/src/api/helpers.ts index 131904c..693b6c2 100644 --- a/src/api/helpers.ts +++ b/src/api/helpers.ts @@ -90,7 +90,7 @@ export const getCriteriaResults = ( ): CriteriaResults => { return { rent: criteriaDetails?.rent?.determination, - rent_stab: criteriaDetails?.rentRegulation?.determination, + rent_stab: criteriaDetails?.rentStabilized?.determination, building_class: criteriaDetails?.buildingClass?.determination, c_of_o: criteriaDetails?.certificateOfOccupancy?.determination, subsidy: criteriaDetails?.subsidy?.determination, diff --git a/src/hooks/eligibility.tsx b/src/hooks/eligibility.tsx index 1bdf62c..c2bca15 100644 --- a/src/hooks/eligibility.tsx +++ b/src/hooks/eligibility.tsx @@ -7,7 +7,7 @@ export type Criteria = | "buildingClass" | "rent" | "subsidy" - | "rentRegulation" + | "rentStabilized" | "certificateOfOccupancy"; type CriteriaData = FormFields & BuildingData; @@ -22,7 +22,7 @@ export type CriterionDetails = { export type CriteriaDetails = { rent: CriterionDetails; - rentRegulation: CriterionDetails; + rentStabilized: CriterionDetails; portfolioSize?: CriterionDetails; buildingClass?: CriterionDetails; subsidy?: CriterionDetails; @@ -39,7 +39,7 @@ export function useCriteriaResults( portfolioSize: eligibilityPortfolioSize(criteriaData), buildingClass: eligibilityBuildingClass(criteriaData), rent: eligibilityRent(criteriaData), - rentRegulation: eligibilityRentRegulated(criteriaData), + rentStabilized: eligibilityRentStabilized(criteriaData), certificateOfOccupancy: eligibilityCertificateOfOccupancy(criteriaData), subsidy: eligibilitySubsidy(criteriaData), }; @@ -168,11 +168,11 @@ function eligibilityRent(criteriaData: CriteriaData): CriterionDetails { }; } -function eligibilityRentRegulated( +function eligibilityRentStabilized( criteriaData: CriteriaData ): CriterionDetails { const { rentStabilized } = criteriaData; - const criteria = "rentRegulation"; + const criteria = "rentStabilized"; const requirement = "Your apartment must not be rent stabilized."; let determination: CriterionResult; let userValue: React.ReactNode; @@ -182,15 +182,15 @@ function eligibilityRentRegulated( if (rentStabilized === "YES") { determination = "INELIGIBLE"; - userValue = "Your apartment is rent regulated."; + userValue = "Your apartment is rent stabilized."; } else if (rentStabilized === "NO") { determination = "ELIGIBLE"; - userValue = "Your apartment is not rent regulated."; + userValue = "Your apartment is not rent stabilized."; } else { determination = "UNKNOWN"; userValue = ( <> -

      You don't know if your apartment is rent regulated.

      +

      You don't know if your apartment is rent stabilized.

      How to find out From bddadeea7256d24e4fd225fc9fc85e219cece196 Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Fri, 20 Dec 2024 14:25:28 -0500 Subject: [PATCH 7/7] remove check-plus svg (hardcoded instead) --- public/check-plus.svg | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 public/check-plus.svg diff --git a/public/check-plus.svg b/public/check-plus.svg deleted file mode 100644 index 7ab10f3..0000000 --- a/public/check-plus.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - -