From 10219a24fa2e433761e5c0107eb1c49943c8cd80 Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Tue, 17 Dec 2024 13:50:36 -0500 Subject: [PATCH 1/3] redesign home page --- package.json | 2 +- .../GeoSearchInput/GeoSearchInput.scss | 25 +++++- .../GeoSearchInput/GeoSearchInput.tsx | 14 +-- .../Pages/ConfirmAddress/ConfirmAddress.tsx | 2 +- src/Components/Pages/Home/Home.scss | 69 +++++++++++---- src/Components/Pages/Home/Home.tsx | 85 +++++++++++++++---- yarn.lock | 8 +- 7 files changed, 158 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index 1b5efc9..e488376 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "format": "prettier --write --ignore-unknown \"src/**/*\"" }, "dependencies": { - "@justfixnyc/component-library": "0.53.1", + "@justfixnyc/component-library": "0.53.4", "@justfixnyc/geosearch-requester": "^1.0.3-alpha.0", "@rollbar/react": "^0.12.0-beta", "react": "^18.3.1", diff --git a/src/Components/GeoSearchInput/GeoSearchInput.scss b/src/Components/GeoSearchInput/GeoSearchInput.scss index f994c4e..c69b70a 100644 --- a/src/Components/GeoSearchInput/GeoSearchInput.scss +++ b/src/Components/GeoSearchInput/GeoSearchInput.scss @@ -1,3 +1,26 @@ +@import "../../mixins.scss"; +@import "../../colors.scss"; + .geo-search { - margin-bottom: 1rem; //16px + // the focus outline shifts the layout a bit, so trying to counter this (still a bit of flicker, but better) + .jfcl-dropdown__control { + margin-bottom: 2px; + &.jfcl-dropdown__control--is-focused { + margin-bottom: 0; + } + } + + .jfcl-dropdown__value-container { + padding: 1.125rem; // 18px + } + .jfcl-dropdown__placeholder { + @include body_large_desktop; + svg { + color: $OFF_BLACK; + margin-right: 1.5rem; // 24px + } + } + .jfcl-dropdown__indicators { + display: none; + } } diff --git a/src/Components/GeoSearchInput/GeoSearchInput.tsx b/src/Components/GeoSearchInput/GeoSearchInput.tsx index 71f5460..146cf76 100644 --- a/src/Components/GeoSearchInput/GeoSearchInput.tsx +++ b/src/Components/GeoSearchInput/GeoSearchInput.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { Dropdown } from "@justfixnyc/component-library"; +import { Dropdown, Icon } from "@justfixnyc/component-library"; import { GeoSearchRequester, GeoSearchFeature, @@ -51,23 +51,23 @@ export const GeoSearchInput: React.FC = ({ + + Enter your address + + } filterOption={null} onInputChange={(value: string) => { requester.changeSearchRequest(value); return value; }} escapeClearsValue={false} - defaultValue={{ - value: initialAddress?.address, - label: initialAddress?.address, - }} defaultOptions={[ { value: initialAddress?.address, label: initialAddress?.address }, ]} // @ts-expect-error We need to update the JFCL onChange props to match react-select onChange={({ value }) => { - console.log("onchange", { value }); const selectedAddress = results.find( (result) => formatGeosearchAddress(result.properties) === value ); diff --git a/src/Components/Pages/ConfirmAddress/ConfirmAddress.tsx b/src/Components/Pages/ConfirmAddress/ConfirmAddress.tsx index 976d627..1d97148 100644 --- a/src/Components/Pages/ConfirmAddress/ConfirmAddress.tsx +++ b/src/Components/Pages/ConfirmAddress/ConfirmAddress.tsx @@ -7,9 +7,9 @@ import { BackLink } from "../../JFCLLinkInternal"; import { useGetBuildingData, useSendGceData } from "../../../api/hooks"; import { GCEUser } from "../../../types/APIDataTypes"; import { Header } from "../../Header/Header"; -import "./ConfirmAddress.scss"; import { toTitleCase } from "../../../helpers"; import { InfoBox } from "../../InfoBox/InfoBox"; +import "./ConfirmAddress.scss"; export const ConfirmAddress: React.FC = () => { const navigate = useNavigate(); diff --git a/src/Components/Pages/Home/Home.scss b/src/Components/Pages/Home/Home.scss index ec04634..f06cf50 100644 --- a/src/Components/Pages/Home/Home.scss +++ b/src/Components/Pages/Home/Home.scss @@ -1,22 +1,59 @@ @import "../../../mixins.scss"; +@import "../../../variables.scss"; +@import "../../../colors.scss"; -.wrapper { - display: flex; - flex-direction: column; - height: 100%; - padding-left: 3rem; // 48px - padding-right: 3rem; // 48px -} +#home-page { + .geo-search-form { + display: inline-flex; + gap: 1.5rem; // 24px + width: 100%; + max-width: $contentBoxWidth; + margin-top: 3rem; // 48px + .jfcl-dropdown { + width: 100%; + } + } -.main-content { - flex-grow: 1; - margin-top: 2.25rem; //36px + .content-section { + padding: 4.5rem 0; // 72px + &.home__about-law { + border-bottom: 1px solid $OFF_BLACK; + .callout-box { + margin-top: 3rem; // 48px + } + } + h3 { + @include h3_desktop; + margin: 0 0 1rem 0; // 16px + } + p { + @include body_large_desktop; + margin: 0; + } + .callout-box { + display: flex; + flex-direction: column; + gap: 1.5rem; // 24px + padding: 2.25rem; // 36px + background-color: $OFF_WHITE_200; + border-radius: 4px; - .content-p { - @include body_large_desktop; - } - .jfcl-input-header__label { - @include body_large_desktop; - font-weight: 600; + .callout-box__header { + @include body_large_desktop; + } + p { + @include body_standard_desktop; + margin: 0; + } + } + .about-project__orgs-container { + display: flex; + gap: 2.25rem; // 36px; + .callout-box { + width: 100%; + height: fit-content; + margin-top: 2.25rem; // 36px; + } + } } } diff --git a/src/Components/Pages/Home/Home.tsx b/src/Components/Pages/Home/Home.tsx index 7a02115..6671c19 100644 --- a/src/Components/Pages/Home/Home.tsx +++ b/src/Components/Pages/Home/Home.tsx @@ -1,12 +1,15 @@ import { useState } from "react"; import { useNavigate } from "react-router"; import { Button } from "@justfixnyc/component-library"; + import { GeoSearchInput } from "../../GeoSearchInput/GeoSearchInput"; import { useSessionStorage } from "../../../hooks/useSessionStorage"; -import "./Home.scss"; import { FormFields } from "../Form/Form"; import { useSendGceData } from "../../../api/hooks"; import { GCEPostData, GCEUser } from "../../../types/APIDataTypes"; +import { Header } from "../../Header/Header"; +import JFCLLinkInternal from "../../JFCLLinkInternal"; +import "./Home.scss"; export type Address = { bbl: string; @@ -48,24 +51,72 @@ export const Home: React.FC = () => { }; return ( -
-
-

Learn if you're covered by Good Cause Eviction in NYC

-

- If you’re covered by the law, you have a right to stay in your home - when your lease ends, and there are limits to how much your landlord - can increase your rent. This tool will show you which of the law's - requirements you meet and what you can do to assert your rights. -

- +
+
-
+
+ +
+
+

About the law

+

+ Good Cause Eviction went into effect on April 20th, 2024. If you are + covered by the law, you now have a right to remain in your home as + long as you pay rent and follow the terms of your lease. There are + also limits to how much your rent can be increased. +

+
+ + You’re protected, even if you aren’t covered + +

+ All NYC tenants are protected by certain rights, even if they are + not covered by the new Good Cause Eviction legislation. +

+ + Learn more about tenants’ rights in NYC + +
+
+
+
+
+

About the project

+

+ To be covered by Good Cause Eviction, your apartment must meet + certain requirements. If you live in New York City, you can use this + tool to see which of the law's requirements you meet and what you + can do to assert your rights. +

+
+

+ This project is a collaboration between JustFix and Housing Justice + for All +

+
+
+ JustFix +

+ A non-profit that builds free tools for tenants to exercise + their rights to a livable home. +

+ Learn more +
+
+ + Housing Justice for All + +

+ A statewide coalition of over 80 groups representing tenants and + homeless New Yorkers, united in the fight for housing as a human + right. +

+ Learn more +
+
diff --git a/yarn.lock b/yarn.lock index 4662090..4b99a03 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.1": - version "0.53.1" - resolved "https://registry.yarnpkg.com/@justfixnyc/component-library/-/component-library-0.53.1.tgz#ea501646a180ed16c087f56b51baef18a8e52477" - integrity sha512-V/3ujeX/3pzqTMZ/GwbdZlTpSkAvI1N89f+xyX5bJlqG/YYVoMNdMypNzs1FRAvZwjN9/hwjx/1aJUyo7QFO6Q== +"@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== dependencies: "@awesome.me/kit-dd32553919" "^1.0.13" "@babel/runtime" "^7.12.5" From d5fe88d5ff70c8593c7fc7317e9cc60e8686000f Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Tue, 17 Dec 2024 15:17:28 -0500 Subject: [PATCH 2/3] add standalone KYR page --- src/App.tsx | 2 + src/Components/ContentBox/ContentBox.scss | 1 + src/Components/Header/Header.tsx | 4 +- src/Components/KYRContent/KYRContent.tsx | 125 +++++++++++++----- src/Components/Pages/Home/Home.tsx | 2 +- src/Components/Pages/Results/Results.tsx | 8 +- .../Pages/TenantRights/TenantRights.scss | 8 ++ .../Pages/TenantRights/TenantRights.tsx | 49 +++++++ 8 files changed, 161 insertions(+), 38 deletions(-) create mode 100644 src/Components/Pages/TenantRights/TenantRights.scss create mode 100644 src/Components/Pages/TenantRights/TenantRights.tsx diff --git a/src/App.tsx b/src/App.tsx index 04e6f65..96cbedd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import { createBrowserRouter, Link, ScrollRestoration } from "react-router-dom"; import { RentStabilization } from "./Components/Pages/RentStabilization/RentStabilization"; import { PortfolioSize } from "./Components/Pages/PortfolioSize/PortfolioSize"; import { Footer } from "./Components/Footer/Footer"; +import { TenantRights } from "./Components/Pages/TenantRights/TenantRights"; const Layout = () => { return ( @@ -127,6 +128,7 @@ const router = createBrowserRouter( element={} loader={LoadURLSession} /> + } /> } /> } /> diff --git a/src/Components/ContentBox/ContentBox.scss b/src/Components/ContentBox/ContentBox.scss index ce55bff..c737c56 100644 --- a/src/Components/ContentBox/ContentBox.scss +++ b/src/Components/ContentBox/ContentBox.scss @@ -8,6 +8,7 @@ $videoEmbedW: 623px; .content-box { border-radius: 8px; border: 2px solid $GREY_NEW; + width: 100%; max-width: $contentBoxWidth; margin: 36px 0; background-color: $WHITE; diff --git a/src/Components/Header/Header.tsx b/src/Components/Header/Header.tsx index b192d46..115e55b 100644 --- a/src/Components/Header/Header.tsx +++ b/src/Components/Header/Header.tsx @@ -9,6 +9,7 @@ type HeaderProps = { pageType?: string; address?: Address; isGuide?: boolean; + showBreadcrumbs?: boolean; children?: React.ReactNode; }; @@ -18,12 +19,13 @@ export const Header: React.FC = ({ title, subtitle, isGuide = false, + showBreadcrumbs = true, children, }) => { return (
- + {showBreadcrumbs && } {isGuide && (
Back to Coverage Result diff --git a/src/Components/KYRContent/KYRContent.tsx b/src/Components/KYRContent/KYRContent.tsx index 006f29f..5be000d 100644 --- a/src/Components/KYRContent/KYRContent.tsx +++ b/src/Components/KYRContent/KYRContent.tsx @@ -5,13 +5,15 @@ import { ContentBoxProps, } from "../ContentBox/ContentBox"; import JFCLLinkExternal from "../JFCLLinkExternal"; -import { Button } from "@justfixnyc/component-library"; -type ContentBoxHeaderProps = Omit; +type KYRContentBoxProps = Omit & { + children?: React.ReactNode; +}; -export const UniversalProtections: React.FC = ({ +export const UniversalProtections: React.FC = ({ title = "UNIVERSAL TENANT RIGHTS", subtitle: headerSubtitle = "Protections that all New Yorkers have", + children, }) => ( @@ -116,12 +118,14 @@ export const UniversalProtections: React.FC = ({ Unlock NYC + {children} ); -export const GoodCauseProtections: React.FC = ({ +export const GoodCauseProtections: React.FC = ({ title = "KNOW YOUR RIGHTS", - subtitle = "Protections you have under Good Cause Eviction law", + subtitle = "Protections you have under Good Cause Eviction", + children, }) => ( @@ -159,40 +163,98 @@ export const GoodCauseProtections: React.FC = ({ Met Council on Housing Good Cause Eviction fact sheet + {children} ); -export const GoodCauseExercisingRights: React.FC = ({ - title = "EXERCISING YOUR RIGHTS", - subtitle: headerSubtitle = "Share your coverage with your landlord", +export const GoodCauseExercisingRights: React.FC = ({ + title = "KNOW YOUR RIGHTS", + 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. -

-
-
+ + +

+ 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 = ({ + title = "KNOW YOUR RIGHTS", + 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 + +
+ + +

+ 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}
); -export const RentStabilizedProtections: React.FC = ({ - title = "EXERCISING YOUR RIGHTS", - subtitle = "Your right to limited rent increases", +export const NYCHAProtections: React.FC = ({ + title = "KNOW YOUR RIGHTS", + subtitle = "Protections you have if you live in NYCHA housing", + children, }) => ( @@ -228,5 +290,6 @@ export const RentStabilizedProtections: React.FC = ({ Learn about succession rights + {children} ); diff --git a/src/Components/Pages/Home/Home.tsx b/src/Components/Pages/Home/Home.tsx index 6671c19..3878593 100644 --- a/src/Components/Pages/Home/Home.tsx +++ b/src/Components/Pages/Home/Home.tsx @@ -76,7 +76,7 @@ export const Home: React.FC = () => { All NYC tenants are protected by certain rights, even if they are not covered by the new Good Cause Eviction legislation.

- + Learn more about tenants’ rights in NYC
diff --git a/src/Components/Pages/Results/Results.tsx b/src/Components/Pages/Results/Results.tsx index 3273e94..da22d13 100644 --- a/src/Components/Pages/Results/Results.tsx +++ b/src/Components/Pages/Results/Results.tsx @@ -128,19 +128,17 @@ export const Results: React.FC = () => { {isRentStabilized && } {determination === "UNKNOWN" && ( <> - + )} {determination === "ELIGIBLE" && ( <> - + )} - {determination === "INELIGIBLE" && !isRentStabilized && ( - - )} +

diff --git a/src/Components/Pages/TenantRights/TenantRights.scss b/src/Components/Pages/TenantRights/TenantRights.scss new file mode 100644 index 0000000..2708326 --- /dev/null +++ b/src/Components/Pages/TenantRights/TenantRights.scss @@ -0,0 +1,8 @@ +#tenant-rights-page { + .headline-section__content { + margin: 0; + } + .headline-section__page-type { + padding: 0; + } +} diff --git a/src/Components/Pages/TenantRights/TenantRights.tsx b/src/Components/Pages/TenantRights/TenantRights.tsx new file mode 100644 index 0000000..87d87d5 --- /dev/null +++ b/src/Components/Pages/TenantRights/TenantRights.tsx @@ -0,0 +1,49 @@ +import { ContentBoxFooter } from "../../ContentBox/ContentBox"; +import { Header } from "../../Header/Header"; +import JFCLLinkInternal from "../../JFCLLinkInternal"; +import { + GoodCauseProtections, + NYCHAProtections, + RentStabilizedProtections, + UniversalProtections, +} from "../../KYRContent/KYRContent"; +import "./TenantRights.scss"; + +export const TenantRights: React.FC = () => { + return ( +
+
+ +
+
+ + + + Take the survey} + /> + + + + + View the guide + + } + /> + + + +
+
+
+ ); +}; From 6a36943fe2df3ff1ddd505a00d118de8d18ad730 Mon Sep 17 00:00:00 2001 From: Maxwell Austensen Date: Tue, 17 Dec 2024 16:47:29 -0500 Subject: [PATCH 3/3] add error states for address search --- src/Components/BreadCrumbs/BreadCrumbs.tsx | 1 - .../GeoSearchInput/GeoSearchInput.scss | 1 + .../GeoSearchInput/GeoSearchInput.tsx | 30 +++++++++---- src/Components/Pages/Home/Home.scss | 8 +++- src/Components/Pages/Home/Home.tsx | 44 ++++++++++++------- 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/Components/BreadCrumbs/BreadCrumbs.tsx b/src/Components/BreadCrumbs/BreadCrumbs.tsx index 2d08925..ba4790d 100644 --- a/src/Components/BreadCrumbs/BreadCrumbs.tsx +++ b/src/Components/BreadCrumbs/BreadCrumbs.tsx @@ -12,7 +12,6 @@ type BreadCrumbs = { export const BreadCrumbs: React.FC = ({ address }) => { const location = useLocation(); - console.log(location); const crumbs = makeBreadCrumbs(location.pathname, address); const crumbpath = crumbs.flatMap((crumb, index, array) => { diff --git a/src/Components/GeoSearchInput/GeoSearchInput.scss b/src/Components/GeoSearchInput/GeoSearchInput.scss index c69b70a..f740e4b 100644 --- a/src/Components/GeoSearchInput/GeoSearchInput.scss +++ b/src/Components/GeoSearchInput/GeoSearchInput.scss @@ -2,6 +2,7 @@ @import "../../colors.scss"; .geo-search { + width: 100%; // the focus outline shifts the layout a bit, so trying to counter this (still a bit of flicker, but better) .jfcl-dropdown__control { margin-bottom: 2px; diff --git a/src/Components/GeoSearchInput/GeoSearchInput.tsx b/src/Components/GeoSearchInput/GeoSearchInput.tsx index 146cf76..2928001 100644 --- a/src/Components/GeoSearchInput/GeoSearchInput.tsx +++ b/src/Components/GeoSearchInput/GeoSearchInput.tsx @@ -11,13 +11,25 @@ import { formatGeosearchAddress } from "../../helpers"; type GeoSearchInputProps = { initialAddress?: Address; onChange: (selectedAddress: Address) => void; + invalid: boolean; + setInvalid: React.Dispatch>; }; export const GeoSearchInput: React.FC = ({ initialAddress, onChange, + invalid, + setInvalid, }) => { const [results, setResults] = useState([]); + const [isFocused, setIsFocused] = useState(false); + + const placeholder = ( + <> + + Enter your address + + ); const requester = useMemo( () => @@ -47,16 +59,18 @@ export const GeoSearchInput: React.FC = ({ }); return ( - <> +
- - Enter your address - - } + placeholder={!isFocused && placeholder} + invalid={!isFocused && invalid} + invalidText="You must enter an address" + onFocus={() => { + setInvalid(false); + setIsFocused(true); + }} + onBlur={() => setIsFocused(false)} filterOption={null} onInputChange={(value: string) => { requester.changeSearchRequest(value); @@ -89,6 +103,6 @@ export const GeoSearchInput: React.FC = ({ } }} /> - +
); }; diff --git a/src/Components/Pages/Home/Home.scss b/src/Components/Pages/Home/Home.scss index f06cf50..13989db 100644 --- a/src/Components/Pages/Home/Home.scss +++ b/src/Components/Pages/Home/Home.scss @@ -5,10 +5,16 @@ #home-page { .geo-search-form { display: inline-flex; + align-items: end; gap: 1.5rem; // 24px width: 100%; + height: 6.25rem; // 100px max-width: $contentBoxWidth; - margin-top: 3rem; // 48px + margin-top: 0.875rem; // 14px + + .jfcl-input-header__label { + display: none; + } .jfcl-dropdown { width: 100%; } diff --git a/src/Components/Pages/Home/Home.tsx b/src/Components/Pages/Home/Home.tsx index 3878593..3b579ab 100644 --- a/src/Components/Pages/Home/Home.tsx +++ b/src/Components/Pages/Home/Home.tsx @@ -27,25 +27,32 @@ export const Home: React.FC = () => { const [address, setAddress] = useSessionStorage
("address"); const [, , removeFormFields] = useSessionStorage("fields"); const [geoAddress, setGeoAddress] = useState
(); + const [inputInvalid, setInputInvalid] = useState(false); const { trigger } = useSendGceData(); + console.log({ inputInvalid }); + const handleAddressSearch = async () => { - if (geoAddress) { - setAddress(geoAddress); - const postData: GCEPostData = { - bbl: geoAddress.bbl, - house_number: geoAddress.houseNumber, - street_name: geoAddress.streetName, - borough: geoAddress.borough, - zipcode: geoAddress.zipcode, - }; - try { - const userResp = (await trigger(postData)) as GCEUser; - setUser(userResp); - } catch (error) { - console.log({ "tenants2-error": error }); - } + if (!geoAddress) { + console.log("no-address"); + setInputInvalid(true); + return; + } + setAddress(geoAddress); + const postData: GCEPostData = { + bbl: geoAddress.bbl, + house_number: geoAddress.houseNumber, + street_name: geoAddress.streetName, + borough: geoAddress.borough, + zipcode: geoAddress.zipcode, + }; + try { + const userResp = (await trigger(postData)) as GCEUser; + setUser(userResp); + } catch (error) { + console.log({ "tenants2-error": error }); } + removeFormFields(); navigate("confirm_address"); }; @@ -54,7 +61,12 @@ export const Home: React.FC = () => {
- +