@bloom-housing/ui-components 4.2.2-alpha.9 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +206 -0
- package/README.md +10 -4
- package/index.ts +12 -10
- package/package.json +6 -4
- package/src/actions/Button.docs.mdx +76 -0
- package/src/actions/Button.scss +58 -61
- package/src/blocks/DashBlock.tsx +5 -3
- package/src/blocks/DashBlocks.scss +4 -1
- package/src/global/app-css.scss +7 -0
- package/src/global/mixins.scss +66 -49
- package/src/global/tables.scss +236 -58
- package/src/global/text.scss +11 -3
- package/src/global/tokens/borders.scss +15 -0
- package/src/global/tokens/colors.scss +64 -0
- package/src/global/tokens/fonts.scss +45 -0
- package/src/global/tokens/screens.scss +6 -0
- package/src/global/tokens/sizes.scss +48 -0
- package/src/headers/Heading.tsx +2 -0
- package/src/headers/PageHeader.docs.mdx +45 -0
- package/src/headers/PageHeader.scss +30 -17
- package/src/headers/PageHeader.tsx +2 -10
- package/src/headers/SiteHeader.tsx +3 -0
- package/src/helpers/MultiLineAddress.tsx +37 -0
- package/src/helpers/OneLineAddress.tsx +21 -0
- package/src/helpers/tableSummaries.tsx +34 -23
- package/src/locales/general.json +4 -1
- package/src/navigation/FooterNav.scss +8 -3
- package/src/page_components/forgot-password/FormForgotPassword.tsx +5 -4
- package/src/page_components/listing/AdditionalFees.tsx +38 -31
- package/src/page_components/listing/ListingCard.scss +12 -0
- package/src/page_components/listing/ListingCard.tsx +5 -3
- package/src/page_components/listing/ListingMap.tsx +7 -2
- package/src/page_components/listing/UnitTables.tsx +19 -18
- package/src/page_components/listing/listing_sidebar/Contact.tsx +110 -0
- package/src/page_components/listing/listing_sidebar/ContactAddress.tsx +41 -0
- package/src/page_components/listing/listing_sidebar/GetApplication.tsx +35 -15
- package/src/page_components/listing/listing_sidebar/QuantityRowSection.tsx +46 -0
- package/src/page_components/listing/listing_sidebar/SubmitApplication.tsx +52 -57
- package/src/page_components/listing/listing_sidebar/events/EventSection.tsx +3 -2
- package/src/page_components/sign-in/FormSignIn.tsx +1 -1
- package/src/page_components/sign-in/ResendConfirmationModal.tsx +1 -3
- package/src/prototypes/Swatch.tsx +10 -0
- package/src/tables/CategoryTable.tsx +33 -0
- package/src/tables/GroupedTable.tsx +5 -5
- package/src/tables/MinimalTable.tsx +12 -2
- package/src/tables/StackedTable.tsx +38 -26
- package/src/tables/StandardTable.tsx +26 -10
- package/tailwind.config.js +76 -81
- package/tailwind.tosass.js +2 -1
- package/src/helpers/address.tsx +0 -46
- package/src/page_components/listing/listing_sidebar/LeasingAgent.tsx +0 -72
- package/src/page_components/listing/listing_sidebar/SidebarAddress.tsx +0 -56
- package/src/page_components/listing/listing_sidebar/Waitlist.tsx +0 -49
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from "react"
|
|
2
|
-
import { nanoid } from "nanoid"
|
|
3
2
|
import { MinMax, UnitSummary, Unit } from "@bloom-housing/backend-core/types"
|
|
4
3
|
|
|
5
|
-
import { StandardTable } from "../../tables/StandardTable"
|
|
4
|
+
import { StandardTable, StandardTableData } from "../../tables/StandardTable"
|
|
6
5
|
import { t } from "../../helpers/translator"
|
|
7
6
|
import { numberOrdinal } from "../../helpers/numberOrdinal"
|
|
8
|
-
import ContentAccordion from "./ContentAccordion"
|
|
7
|
+
import { ContentAccordion } from "./ContentAccordion"
|
|
9
8
|
|
|
10
9
|
const formatRange = (range: MinMax, ordinalize?: boolean) => {
|
|
11
10
|
let min: string | number = range.min
|
|
@@ -50,24 +49,26 @@ const UnitTables = (props: UnitTablesProps) => {
|
|
|
50
49
|
const units = props.units.filter(
|
|
51
50
|
(unit: Unit) => unit.unitType?.name == unitSummary.unitType.name
|
|
52
51
|
)
|
|
53
|
-
const unitsFormatted = [] as
|
|
52
|
+
const unitsFormatted = [] as StandardTableData
|
|
54
53
|
let floorSection: React.ReactNode
|
|
55
54
|
units.forEach((unit: Unit) => {
|
|
56
55
|
unitsFormatted.push({
|
|
57
|
-
number: unit.number,
|
|
58
|
-
sqFeet:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
number: { content: unit.number },
|
|
57
|
+
sqFeet: {
|
|
58
|
+
content: (
|
|
59
|
+
<>
|
|
60
|
+
{unit.sqFeet ? (
|
|
61
|
+
<>
|
|
62
|
+
<strong>{parseInt(unit.sqFeet)}</strong> {t("t.sqFeet")}
|
|
63
|
+
</>
|
|
64
|
+
) : (
|
|
65
|
+
<></>
|
|
66
|
+
)}
|
|
67
|
+
</>
|
|
68
|
+
),
|
|
69
|
+
},
|
|
70
|
+
numBathrooms: { content: <strong>{unit.numBathrooms}</strong> },
|
|
71
|
+
floor: { content: <strong>{unit.floor}</strong> },
|
|
71
72
|
})
|
|
72
73
|
})
|
|
73
74
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { ContactAddress } from "./ContactAddress"
|
|
3
|
+
import { Icon, IconFillColors } from "../../../icons/Icon"
|
|
4
|
+
import { Address } from "../../../helpers/MultiLineAddress"
|
|
5
|
+
import { Heading } from "../../../headers/Heading"
|
|
6
|
+
|
|
7
|
+
export interface ContactProps {
|
|
8
|
+
/** Any number of text sections rendered after the contact information */
|
|
9
|
+
additionalInformation?: { title: string; content: string | React.ReactNode }[]
|
|
10
|
+
/** The contact's address */
|
|
11
|
+
contactAddress?: Address
|
|
12
|
+
/** The contact's company and website */
|
|
13
|
+
contactCompany?: { name: string; website: string }
|
|
14
|
+
/** The contact's email */
|
|
15
|
+
contactEmail?: string
|
|
16
|
+
/** The contact's name */
|
|
17
|
+
contactName?: string
|
|
18
|
+
/** The contact's phone number */
|
|
19
|
+
contactPhoneNumber?: string
|
|
20
|
+
/** Additional information pertaining to the contact's phone number */
|
|
21
|
+
contactPhoneNumberNote?: string
|
|
22
|
+
/** The contact's title */
|
|
23
|
+
contactTitle?: string
|
|
24
|
+
/** The text for the section's header */
|
|
25
|
+
sectionTitle: string
|
|
26
|
+
strings: { email?: string; getDirections: string; website?: string }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Displays information about a contact, including their address, contact information, and notes */
|
|
30
|
+
const Contact = ({
|
|
31
|
+
additionalInformation,
|
|
32
|
+
contactAddress,
|
|
33
|
+
contactCompany,
|
|
34
|
+
contactEmail,
|
|
35
|
+
contactName,
|
|
36
|
+
contactPhoneNumber,
|
|
37
|
+
contactPhoneNumberNote,
|
|
38
|
+
contactTitle,
|
|
39
|
+
sectionTitle,
|
|
40
|
+
strings,
|
|
41
|
+
}: ContactProps) => {
|
|
42
|
+
const formattedPhoneLink = contactPhoneNumber
|
|
43
|
+
? `tel:${contactPhoneNumber.replace(/[-()]/g, "")}`
|
|
44
|
+
: undefined
|
|
45
|
+
const formattedCompanyWebsite =
|
|
46
|
+
contactCompany?.website && !contactCompany?.website.startsWith("http")
|
|
47
|
+
? `http://${contactCompany?.website}`
|
|
48
|
+
: contactCompany?.website
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<section className="aside-block">
|
|
52
|
+
<Heading priority={4} style={"sidebarHeader"}>
|
|
53
|
+
{sectionTitle}
|
|
54
|
+
</Heading>
|
|
55
|
+
|
|
56
|
+
{contactName && <p className="text-xl">{contactName}</p>}
|
|
57
|
+
{contactTitle && <p className="text-gray-700">{contactTitle}</p>}
|
|
58
|
+
{contactCompany?.name && <p className="text-gray-700">{contactCompany.name}</p>}
|
|
59
|
+
|
|
60
|
+
{contactPhoneNumber && (
|
|
61
|
+
<>
|
|
62
|
+
<p className="mt-3">
|
|
63
|
+
<a href={formattedPhoneLink}>
|
|
64
|
+
<Icon symbol="phone" size="medium" fill={IconFillColors.primary} className={"pr-2"} />
|
|
65
|
+
{contactPhoneNumber}
|
|
66
|
+
</a>
|
|
67
|
+
</p>
|
|
68
|
+
{contactPhoneNumberNote && (
|
|
69
|
+
<p className="text-sm text-gray-700">{contactPhoneNumberNote}</p>
|
|
70
|
+
)}
|
|
71
|
+
</>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{contactEmail && (
|
|
75
|
+
<p className="my-3">
|
|
76
|
+
<a href={`mailto:${contactEmail}`}>
|
|
77
|
+
<Icon symbol="mail" size="medium" fill={IconFillColors.primary} className={"pr-2"} />
|
|
78
|
+
{strings?.email && strings?.email}
|
|
79
|
+
</a>
|
|
80
|
+
</p>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{formattedCompanyWebsite && (
|
|
84
|
+
<p className="my-3">
|
|
85
|
+
<a href={formattedCompanyWebsite} target="_blank" rel="noreferrer noopener">
|
|
86
|
+
<Icon symbol="globe" size="medium" fill={IconFillColors.primary} className={"pr-2"} />
|
|
87
|
+
{strings?.website && strings?.website}
|
|
88
|
+
</a>
|
|
89
|
+
</p>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{contactAddress && (
|
|
93
|
+
<ContactAddress address={contactAddress} mapString={strings.getDirections} />
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
{additionalInformation?.map((info) => {
|
|
97
|
+
return (
|
|
98
|
+
<>
|
|
99
|
+
<Heading priority={3} style={"sidebarSubHeader"}>
|
|
100
|
+
{info.title}
|
|
101
|
+
</Heading>
|
|
102
|
+
<div className="text-gray-800 text-tiny markdown">{info.content}</div>
|
|
103
|
+
</>
|
|
104
|
+
)
|
|
105
|
+
})}
|
|
106
|
+
</section>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export { Contact as default, Contact }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import ReactDOMServer from "react-dom/server"
|
|
3
|
+
import { Icon, IconFillColors } from "../../../icons/Icon"
|
|
4
|
+
import { MultiLineAddress, Address } from "../../../helpers/MultiLineAddress"
|
|
5
|
+
import { OneLineAddress } from "../../../helpers/OneLineAddress"
|
|
6
|
+
|
|
7
|
+
export interface ContactAddressProps {
|
|
8
|
+
/** An address */
|
|
9
|
+
address: Address
|
|
10
|
+
/** A string for the map link */
|
|
11
|
+
mapString: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Renders an address followed by a Google Maps link to the address */
|
|
15
|
+
const ContactAddress = ({ address, mapString }: ContactAddressProps) => {
|
|
16
|
+
if (!address?.street) return null
|
|
17
|
+
|
|
18
|
+
const oneLineAddress = <OneLineAddress address={address} />
|
|
19
|
+
const mainAddress = <MultiLineAddress address={address} />
|
|
20
|
+
const googleMapsHref =
|
|
21
|
+
"https://www.google.com/maps/place/" + ReactDOMServer.renderToStaticMarkup(oneLineAddress)
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<p className="text-gray-700 mb-1">{mainAddress}</p>
|
|
26
|
+
<p className="mb-4">
|
|
27
|
+
<a
|
|
28
|
+
href={googleMapsHref}
|
|
29
|
+
className="inline-block pt-1"
|
|
30
|
+
target="_blank"
|
|
31
|
+
rel="noreferrer noopener"
|
|
32
|
+
>
|
|
33
|
+
<Icon symbol="map" size="medium" fill={IconFillColors.primary} className={"pr-2"} />
|
|
34
|
+
{mapString}
|
|
35
|
+
</a>
|
|
36
|
+
</p>
|
|
37
|
+
</>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { ContactAddress as default, ContactAddress }
|
|
@@ -3,10 +3,11 @@ import { t } from "../../../helpers/translator"
|
|
|
3
3
|
import { Button } from "../../../actions/Button"
|
|
4
4
|
import { LinkButton } from "../../../actions/LinkButton"
|
|
5
5
|
import { AppearanceStyleType } from "../../../global/AppearanceTypes"
|
|
6
|
-
import { Address } from "../../../helpers/
|
|
7
|
-
import {
|
|
6
|
+
import { Address } from "../../../helpers/MultiLineAddress"
|
|
7
|
+
import { ContactAddress } from "./ContactAddress"
|
|
8
8
|
import { OrDivider } from "./OrDivider"
|
|
9
|
-
import {
|
|
9
|
+
import { Heading } from "../../../headers/Heading"
|
|
10
|
+
import Markdown from "markdown-to-jsx"
|
|
10
11
|
|
|
11
12
|
export interface PaperApplication {
|
|
12
13
|
fileURL: string
|
|
@@ -14,26 +15,30 @@ export interface PaperApplication {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export interface ApplicationsProps {
|
|
17
|
-
|
|
18
|
+
/** The pickup address for the application */
|
|
19
|
+
applicationPickUpAddress?: Address
|
|
20
|
+
/** Office hours for the agent at the pickup address */
|
|
21
|
+
applicationPickUpAddressOfficeHours?: string
|
|
22
|
+
/** Whether or not applications are currently open */
|
|
18
23
|
applicationsOpen: boolean
|
|
24
|
+
/** The date applications open */
|
|
19
25
|
applicationsOpenDate?: string
|
|
26
|
+
/** The URL for an online applications */
|
|
27
|
+
onlineApplicationURL?: string
|
|
28
|
+
/** Any number of paper application objects, including their URL and language */
|
|
20
29
|
paperApplications?: PaperApplication[]
|
|
30
|
+
/** Whether or not there is a paper application method */
|
|
21
31
|
paperMethod?: boolean
|
|
32
|
+
/** The date mailed applications must be received by */
|
|
22
33
|
postmarkedApplicationsReceivedByDate?: string
|
|
23
|
-
|
|
24
|
-
applicationPickUpAddress?: Address
|
|
34
|
+
/** Whether or not to hide actionable application buttons */
|
|
25
35
|
preview?: boolean
|
|
26
|
-
listingStatus?: ListingStatus
|
|
27
36
|
}
|
|
28
|
-
|
|
37
|
+
/** Displays information regarding how to apply, including an online application link button, paper application downloads, and a paper application pickup address */
|
|
29
38
|
const GetApplication = (props: ApplicationsProps) => {
|
|
30
39
|
const [showDownload, setShowDownload] = useState(false)
|
|
31
40
|
const toggleDownload = () => setShowDownload(!showDownload)
|
|
32
41
|
|
|
33
|
-
if (props.listingStatus === ListingStatus.closed) {
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
|
|
37
42
|
return (
|
|
38
43
|
<section className="aside-block">
|
|
39
44
|
<h2 className="text-caps-underline">{t("listings.apply.howToApply")}</h2>
|
|
@@ -95,11 +100,26 @@ const GetApplication = (props: ApplicationsProps) => {
|
|
|
95
100
|
{props.applicationsOpen && (props.onlineApplicationURL || props.paperMethod) && (
|
|
96
101
|
<OrDivider bgColor="white" />
|
|
97
102
|
)}
|
|
98
|
-
<
|
|
99
|
-
|
|
103
|
+
<Heading priority={3} style={"sidebarSubHeader"}>
|
|
104
|
+
{t("listings.apply.pickUpAnApplication")}
|
|
105
|
+
</Heading>
|
|
106
|
+
<ContactAddress
|
|
100
107
|
address={props.applicationPickUpAddress}
|
|
101
|
-
|
|
108
|
+
mapString={t("t.getDirections")}
|
|
102
109
|
/>
|
|
110
|
+
{props.applicationPickUpAddressOfficeHours && (
|
|
111
|
+
<>
|
|
112
|
+
<Heading priority={3} style={"sidebarSubHeader"}>
|
|
113
|
+
{t("leasingAgent.officeHours")}
|
|
114
|
+
</Heading>
|
|
115
|
+
<p className="text-gray-800 text-tiny markdown">
|
|
116
|
+
<Markdown
|
|
117
|
+
children={props.applicationPickUpAddressOfficeHours}
|
|
118
|
+
options={{ disableParsingRawHTML: true }}
|
|
119
|
+
/>
|
|
120
|
+
</p>
|
|
121
|
+
</>
|
|
122
|
+
)}
|
|
103
123
|
</>
|
|
104
124
|
)}
|
|
105
125
|
</section>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
export interface QuantityRow {
|
|
4
|
+
amount: number | null
|
|
5
|
+
text: string
|
|
6
|
+
emphasized?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface QuantityRowSectionProps {
|
|
10
|
+
/** Any amount of number/text combinations, rendered in a list */
|
|
11
|
+
quantityRows: QuantityRow[]
|
|
12
|
+
strings: {
|
|
13
|
+
sectionTitle: string
|
|
14
|
+
description?: string | React.ReactNode
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const QuantityRowSection = ({ quantityRows, strings }: QuantityRowSectionProps) => {
|
|
19
|
+
const getRow = (row: QuantityRow) => {
|
|
20
|
+
return (
|
|
21
|
+
<li
|
|
22
|
+
key={row.text}
|
|
23
|
+
className={`uppercase text-gray-800 ${
|
|
24
|
+
row.emphasized ? "font-bold" : "font-normal"
|
|
25
|
+
} font-alt-sans leading-7`}
|
|
26
|
+
>
|
|
27
|
+
<span className="text-right w-12 inline-block pr-2.5 text-base">{row.amount}</span>
|
|
28
|
+
<span className={"text-sm"}>{row.text}</span>
|
|
29
|
+
</li>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<section className="aside-block is-tinted">
|
|
35
|
+
<h4 className="text-caps-tiny">{strings.sectionTitle}</h4>
|
|
36
|
+
<div>
|
|
37
|
+
{strings.description && (
|
|
38
|
+
<p className="text-tiny text-gray-800 pb-3">{strings.description}</p>
|
|
39
|
+
)}
|
|
40
|
+
{quantityRows.length && <ul>{quantityRows.map((row) => getRow(row))}</ul>}
|
|
41
|
+
</div>
|
|
42
|
+
</section>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { QuantityRowSection as default, QuantityRowSection }
|
|
@@ -1,78 +1,73 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
-
import
|
|
3
|
-
import { Address } from "../../../helpers/
|
|
4
|
-
import {
|
|
2
|
+
import Markdown from "markdown-to-jsx"
|
|
3
|
+
import { Address } from "../../../helpers/MultiLineAddress"
|
|
4
|
+
import { ContactAddress } from "./ContactAddress"
|
|
5
5
|
import { OrDivider } from "./OrDivider"
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
export interface PostmarkedApplication {
|
|
9
|
-
postmarkedApplicationsReceivedByDate: string | null
|
|
10
|
-
developer: string
|
|
11
|
-
applicationsDueDate: string | null
|
|
12
|
-
}
|
|
6
|
+
import { Heading } from "../../../headers/Heading"
|
|
13
7
|
|
|
14
8
|
export interface ApplicationAddressesProps {
|
|
15
|
-
|
|
9
|
+
/** The dropoff address for paper applications */
|
|
16
10
|
applicationDropOffAddress?: Address
|
|
11
|
+
/** Office hours for developers at the dropoff address for paper applications */
|
|
17
12
|
applicationDropOffAddressOfficeHours?: string
|
|
13
|
+
/** The mailing address for paper applications */
|
|
14
|
+
applicationMailingAddress?: Address
|
|
15
|
+
/** The application organization, often the developer */
|
|
18
16
|
applicationOrganization?: string
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
!(props.applicationMailingAddress || props.applicationDropOffAddress)
|
|
27
|
-
) {
|
|
28
|
-
return null
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const getPostmarkString = () => {
|
|
32
|
-
const applicationDueDate = props.postmarkedApplicationData?.applicationsDueDate
|
|
33
|
-
const postmarkReceivedByDate =
|
|
34
|
-
props.postmarkedApplicationData?.postmarkedApplicationsReceivedByDate
|
|
35
|
-
const developer = props.postmarkedApplicationData?.developer
|
|
36
|
-
if (applicationDueDate) {
|
|
37
|
-
return postmarkReceivedByDate
|
|
38
|
-
? t("listings.apply.submitPaperDueDatePostMark", {
|
|
39
|
-
applicationDueDate,
|
|
40
|
-
postmarkReceivedByDate,
|
|
41
|
-
developer,
|
|
42
|
-
})
|
|
43
|
-
: t("listings.apply.submitPaperDueDateNoPostMark", {
|
|
44
|
-
applicationDueDate,
|
|
45
|
-
developer,
|
|
46
|
-
})
|
|
47
|
-
} else {
|
|
48
|
-
return postmarkReceivedByDate
|
|
49
|
-
? t("listings.apply.submitPaperNoDueDatePostMark", { postmarkReceivedByDate, developer })
|
|
50
|
-
: t("listings.apply.submitPaperNoDueDateNoPostMark", { developer })
|
|
51
|
-
}
|
|
17
|
+
strings: {
|
|
18
|
+
postmark?: string
|
|
19
|
+
mailHeader?: string
|
|
20
|
+
sectionHeader?: string
|
|
21
|
+
dropOffHeader?: string
|
|
22
|
+
officeHoursHeader?: string
|
|
23
|
+
mapString: string
|
|
52
24
|
}
|
|
25
|
+
}
|
|
53
26
|
|
|
27
|
+
/** Displays information regarding paper applications, including two sections: (1) how to mail in applications and (2) how to drop off applications */
|
|
28
|
+
const SubmitApplication = ({
|
|
29
|
+
applicationDropOffAddress,
|
|
30
|
+
applicationDropOffAddressOfficeHours,
|
|
31
|
+
applicationMailingAddress,
|
|
32
|
+
applicationOrganization,
|
|
33
|
+
strings,
|
|
34
|
+
}: ApplicationAddressesProps) => {
|
|
54
35
|
return (
|
|
55
36
|
<>
|
|
56
37
|
<section className="aside-block is-tinted bg-gray-100">
|
|
57
|
-
<div className="text-serif-lg">{
|
|
58
|
-
{
|
|
38
|
+
<div className="text-serif-lg">{strings.sectionHeader}</div>
|
|
39
|
+
{applicationMailingAddress && (
|
|
59
40
|
<>
|
|
60
|
-
<
|
|
41
|
+
<Heading priority={3} style={"sidebarSubHeader"}>
|
|
42
|
+
{strings.mailHeader}
|
|
43
|
+
</Heading>
|
|
61
44
|
<>
|
|
62
|
-
<p className="text-gray-700">{
|
|
63
|
-
<
|
|
45
|
+
<p className="text-gray-700">{applicationOrganization}</p>
|
|
46
|
+
<ContactAddress address={applicationMailingAddress} mapString={strings.mapString} />
|
|
64
47
|
</>
|
|
65
|
-
<p className="mt-4 text-tiny text-gray-750">{
|
|
48
|
+
{strings.postmark && <p className="mt-4 text-tiny text-gray-750">{strings.postmark}</p>}
|
|
66
49
|
</>
|
|
67
50
|
)}
|
|
68
|
-
{
|
|
51
|
+
{applicationDropOffAddress && (
|
|
69
52
|
<>
|
|
70
|
-
{
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
{applicationMailingAddress && <OrDivider bgColor="gray-100" />}
|
|
54
|
+
<Heading priority={3} style={"sidebarSubHeader"}>
|
|
55
|
+
{strings.dropOffHeader}
|
|
56
|
+
</Heading>
|
|
57
|
+
<ContactAddress address={applicationDropOffAddress} mapString={strings.mapString} />
|
|
58
|
+
{applicationDropOffAddressOfficeHours && (
|
|
59
|
+
<>
|
|
60
|
+
<Heading priority={3} style={"sidebarSubHeader"}>
|
|
61
|
+
{strings.officeHoursHeader}
|
|
62
|
+
</Heading>
|
|
63
|
+
<p className="mt-4 text-tiny text-gray-750">
|
|
64
|
+
<Markdown
|
|
65
|
+
children={applicationDropOffAddressOfficeHours}
|
|
66
|
+
options={{ disableParsingRawHTML: true }}
|
|
67
|
+
/>
|
|
68
|
+
</p>
|
|
69
|
+
</>
|
|
70
|
+
)}
|
|
76
71
|
</>
|
|
77
72
|
)}
|
|
78
73
|
</section>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
+
import Heading from "../../../../headers/Heading"
|
|
2
3
|
|
|
3
4
|
export type EventType = {
|
|
4
5
|
timeString?: string
|
|
@@ -19,9 +20,9 @@ const EventSection = (props: EventSectionProps) => {
|
|
|
19
20
|
return (
|
|
20
21
|
<section className="aside-block">
|
|
21
22
|
{props.headerText && (
|
|
22
|
-
<
|
|
23
|
+
<Heading priority={4} style={props.sectionHeader ? "sidebarHeader" : "sidebarSubHeader"}>
|
|
23
24
|
{props.headerText}
|
|
24
|
-
</
|
|
25
|
+
</Heading>
|
|
25
26
|
)}
|
|
26
27
|
{props.events.map((event, index) => (
|
|
27
28
|
<div key={`events-${index}`} className={`${index !== props.events.length - 1 && "pb-3"}`}>
|
|
@@ -112,7 +112,7 @@ const FormSignIn = ({
|
|
|
112
112
|
</Form>
|
|
113
113
|
</div>
|
|
114
114
|
{showRegisterBtn && (
|
|
115
|
-
<div className="form-card__group text-center">
|
|
115
|
+
<div className="form-card__group text-center border-t">
|
|
116
116
|
<h2 className="mb-6">{t("authentication.createAccount.noAccount")}</h2>
|
|
117
117
|
|
|
118
118
|
<LinkButton href="/create-account">{t("account.createAccount")}</LinkButton>
|
|
@@ -6,9 +6,8 @@ import {
|
|
|
6
6
|
Form,
|
|
7
7
|
Field,
|
|
8
8
|
emailRegex,
|
|
9
|
-
NavigationContext,
|
|
10
9
|
} from "@bloom-housing/ui-components"
|
|
11
|
-
import React, { useEffect, useMemo
|
|
10
|
+
import React, { useEffect, useMemo } from "react"
|
|
12
11
|
import { useForm } from "react-hook-form"
|
|
13
12
|
|
|
14
13
|
export type ResendConfirmationModalProps = {
|
|
@@ -30,7 +29,6 @@ const ResendConfirmationModal = ({
|
|
|
30
29
|
onClose,
|
|
31
30
|
onSubmit,
|
|
32
31
|
}: ResendConfirmationModalProps) => {
|
|
33
|
-
const { router } = useContext(NavigationContext)
|
|
34
32
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
35
33
|
const { register, errors, reset, getValues, trigger } = useForm({
|
|
36
34
|
defaultValues: useMemo(() => {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { StackedTable, StackedTableProps } from "./StackedTable"
|
|
3
|
+
import { Heading } from "../headers/Heading"
|
|
4
|
+
|
|
5
|
+
export interface CategoryTableSection {
|
|
6
|
+
/** The header text for a category */
|
|
7
|
+
header: string
|
|
8
|
+
/** The table data for a category */
|
|
9
|
+
tableData: StackedTableProps
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CategoryTableProps {
|
|
13
|
+
/** The table data passed as category section strings associated with a table data set */
|
|
14
|
+
categoryData: CategoryTableSection[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const CategoryTable = (props: CategoryTableProps) => {
|
|
18
|
+
const tables = props.categoryData.map((category, index) => {
|
|
19
|
+
return (
|
|
20
|
+
<div key={index}>
|
|
21
|
+
<Heading priority={3} style={"categoryHeader"}>
|
|
22
|
+
{category.header}
|
|
23
|
+
</Heading>
|
|
24
|
+
<hr className={"my-2"} />
|
|
25
|
+
<StackedTable {...category.tableData} className={"category-table mb-2 md:mb-6"} />
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
return <>{tables}</>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { CategoryTable as default, CategoryTable }
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { nanoid } from "nanoid"
|
|
3
|
-
import { Cell, StandardTableProps } from "./StandardTable"
|
|
3
|
+
import { Cell, StandardTableData, StandardTableProps } from "./StandardTable"
|
|
4
4
|
|
|
5
5
|
export interface GroupedTableGroup {
|
|
6
6
|
header?: string | React.ReactNode
|
|
7
7
|
className?: string
|
|
8
|
-
data:
|
|
8
|
+
data: StandardTableData
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface GroupedTableProps extends Omit<StandardTableProps, "data"> {
|
|
@@ -43,16 +43,16 @@ export const GroupedTable = (props: GroupedTableProps) => {
|
|
|
43
43
|
)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
groupData.forEach((row
|
|
46
|
+
groupData.forEach((row, groupDataIndex) => {
|
|
47
47
|
const rowKey = row["id"]
|
|
48
|
-
? `row-${row["id"] as string}`
|
|
48
|
+
? `row-${row["id"].content as string}`
|
|
49
49
|
: process.env.NODE_ENV === "test"
|
|
50
50
|
? `groupedrow-${dataIndex}-${groupDataIndex}`
|
|
51
51
|
: nanoid()
|
|
52
52
|
const cols = Object.keys(headers).map((colKey, colIndex) => {
|
|
53
53
|
const uniqKey = process.env.NODE_ENV === "test" ? `col-${colIndex}` : nanoid()
|
|
54
54
|
const header = headers[colKey]
|
|
55
|
-
const cell = row[colKey]
|
|
55
|
+
const cell = row[colKey]?.content
|
|
56
56
|
return (
|
|
57
57
|
<Cell key={uniqKey} headerLabel={header} className={cellClassName}>
|
|
58
58
|
{cell}
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
-
import { TableHeaders, StandardTable } from "./StandardTable"
|
|
2
|
+
import { TableHeaders, StandardTable, StandardTableData } from "./StandardTable"
|
|
3
3
|
|
|
4
4
|
export interface MinimalTableProps {
|
|
5
|
+
/** If the table should be sortable through dragging */
|
|
5
6
|
draggable?: boolean
|
|
7
|
+
/** A set state function tied to the table's data, used if the table is draggable */
|
|
6
8
|
setData?: (data: unknown[]) => void
|
|
9
|
+
/** The headers for the table passed as text content with optional settings */
|
|
7
10
|
headers: TableHeaders
|
|
8
|
-
data
|
|
11
|
+
/** The table data passed as records of column name to cell data with optional settings */
|
|
12
|
+
data?: StandardTableData
|
|
13
|
+
/** Removes cell margins on the left of every row's first cell */
|
|
9
14
|
flushLeft?: boolean
|
|
15
|
+
/** Removes cell margins on the right of every row's last cell */
|
|
10
16
|
flushRight?: boolean
|
|
17
|
+
/** If the table should collapse on mobile views to show repeating columns on the left for every row */
|
|
11
18
|
responsiveCollapse?: boolean
|
|
19
|
+
/** A class name applied to the root of the table */
|
|
12
20
|
className?: string
|
|
21
|
+
/** A class name applied to the cells of the table */
|
|
13
22
|
cellClassName?: string
|
|
23
|
+
/** An id applied to the table */
|
|
14
24
|
id?: string
|
|
15
25
|
}
|
|
16
26
|
|