@bloom-housing/ui-components 4.2.2-alpha.23 → 4.2.2-alpha.26

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 CHANGED
@@ -3,6 +3,44 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [4.2.2-alpha.26](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.2.2-alpha.25...@bloom-housing/ui-components@4.2.2-alpha.26) (2022-05-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * use appropriate copy for FCFS vs Lottery in Application Confirmation screen ([#2702](https://github.com/bloom-housing/bloom/issues/2702)) ([5289504](https://github.com/bloom-housing/bloom/commit/52895044cba64eeb7789c68f0a5eb957785055b5))
12
+
13
+
14
+
15
+
16
+
17
+ ## [4.2.2-alpha.25](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.2.2-alpha.24...@bloom-housing/ui-components@4.2.2-alpha.25) (2022-05-04)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * footer alignment issue with overflowing links ([#2713](https://github.com/bloom-housing/bloom/issues/2713)) ([b87be1d](https://github.com/bloom-housing/bloom/commit/b87be1da10d59df2616f5df51629a8201baa6f86))
23
+
24
+
25
+
26
+
27
+
28
+ ## [4.2.2-alpha.24](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.2.2-alpha.23...@bloom-housing/ui-components@4.2.2-alpha.24) (2022-05-04)
29
+
30
+
31
+ ### Code Refactoring
32
+
33
+ * remove backend dependencies from sidebar application components ([#2675](https://github.com/bloom-housing/bloom/issues/2675)) ([d2ebf87](https://github.com/bloom-housing/bloom/commit/d2ebf87c34af3f5b6168fa4e08663fea0a4a872c))
34
+
35
+
36
+ ### BREAKING CHANGES
37
+
38
+ * the LeasingAgent component has been renamed to Contact with a new generalized prop set, the SidebarAddress component has been renamed to ContactAddress with a new generalized prop set
39
+
40
+
41
+
42
+
43
+
6
44
  ## [4.2.2-alpha.23](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.2.2-alpha.22...@bloom-housing/ui-components@4.2.2-alpha.23) (2022-05-04)
7
45
 
8
46
 
package/index.ts CHANGED
@@ -52,23 +52,24 @@ export * from "./src/headers/SiteHeader"
52
52
  export * from "./src/headers/Heading"
53
53
 
54
54
  /* Helpers */
55
- export * from "./src/helpers/address"
56
55
  export * from "./src/helpers/capitalize"
57
56
  export * from "./src/helpers/dateToString"
57
+ export * from "./src/helpers/debounce"
58
58
  export * from "./src/helpers/formOptions"
59
+ export * from "./src/helpers/formatYesNoLabel"
60
+ export * from "./src/helpers/getTranslationWithArguments"
59
61
  export * from "./src/helpers/links"
60
62
  export * from "./src/helpers/mergeDeep"
63
+ export * from "./src/helpers/MultiLineAddress"
61
64
  export * from "./src/helpers/numberOrdinal"
62
- export * from "./src/helpers/translator"
63
- export * from "./src/helpers/debounce"
64
- export * from "./src/helpers/validators"
65
- export * from "./src/helpers/formatYesNoLabel"
66
- export * from "./src/helpers/getTranslationWithArguments"
65
+ export * from "./src/helpers/OneLineAddress"
67
66
  export * from "./src/helpers/preferences"
68
67
  export * from "./src/helpers/resolveObject"
68
+ export * from "./src/helpers/tableSummaries"
69
+ export * from "./src/helpers/translator"
69
70
  export * from "./src/helpers/useIntersect"
70
71
  export * from "./src/helpers/useMutate"
71
- export * from "./src/helpers/tableSummaries"
72
+ export * from "./src/helpers/validators"
72
73
 
73
74
  /* Icons */
74
75
  export * from "./src/icons/HeaderBadge"
@@ -108,12 +109,12 @@ export * from "./src/page_components/listing/ListingMap"
108
109
  export * from "./src/page_components/listing/ListingsGroup"
109
110
  export * from "./src/page_components/listing/UnitTables"
110
111
  export * from "./src/page_components/listing/listing_sidebar/GetApplication"
111
- export * from "./src/page_components/listing/listing_sidebar/LeasingAgent"
112
+ export * from "./src/page_components/listing/listing_sidebar/Contact"
112
113
  export * from "./src/page_components/listing/listing_sidebar/ListingUpdated"
113
114
  export * from "./src/page_components/listing/listing_sidebar/NumberedHeader"
114
115
  export * from "./src/page_components/listing/listing_sidebar/OrDivider"
115
116
  export * from "./src/page_components/listing/listing_sidebar/ReferralApplication"
116
- export * from "./src/page_components/listing/listing_sidebar/SidebarAddress"
117
+ export * from "./src/page_components/listing/listing_sidebar/ContactAddress"
117
118
  export * from "./src/page_components/listing/listing_sidebar/SubmitApplication"
118
119
  export * from "./src/page_components/listing/listing_sidebar/QuantityRowSection"
119
120
  export * from "./src/page_components/listing/listing_sidebar/WhatToExpect"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloom-housing/ui-components",
3
- "version": "4.2.2-alpha.23",
3
+ "version": "4.2.2-alpha.26",
4
4
  "author": "Sean Albert <sean.albert@exygy.com>",
5
5
  "description": "Shared user interface components for Bloom affordable housing system",
6
6
  "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components",
@@ -102,5 +102,5 @@
102
102
  "tailwindcss": "2.2.10",
103
103
  "typesafe-actions": "^5.1.0"
104
104
  },
105
- "gitHead": "004be94722a0b7bccd6fa7528f2a0be2a5e8529b"
105
+ "gitHead": "401d36c47d66757a78c54681ed0adce78e8ebc0c"
106
106
  }
@@ -88,7 +88,7 @@ h1.title {
88
88
  }
89
89
 
90
90
  .text-caps-tiny {
91
- @apply mb-4;
91
+ @apply mb-3;
92
92
  @apply text-gray-750;
93
93
  @apply uppercase;
94
94
  @apply font-sans;
@@ -16,6 +16,7 @@ const HeaderStyleMap = {
16
16
  tableSubheader: "table-subheader",
17
17
  sidebarHeader: "text-caps-underline",
18
18
  categoryHeader: "category-header",
19
+ sidebarSubHeader: "text-caps-tiny",
19
20
  }
20
21
 
21
22
  const Heading = (props: HeadingProps) => {
@@ -0,0 +1,37 @@
1
+ import * as React from "react"
2
+
3
+ export interface Address {
4
+ city?: string
5
+ latitude?: number
6
+ longitude?: number
7
+ placeName?: string
8
+ state?: string
9
+ street2?: string
10
+ street?: string
11
+ zipCode?: string
12
+ }
13
+
14
+ export interface MultiLineAddressProps {
15
+ address: Address
16
+ }
17
+
18
+ const MultiLineAddress = ({ address }: MultiLineAddressProps) => {
19
+ if (!address) return null
20
+
21
+ return (
22
+ <>
23
+ {address.placeName && (
24
+ <>
25
+ {address.placeName}
26
+ <br />
27
+ </>
28
+ )}
29
+ {address.street} {address.street2}
30
+ {(address.street || address.street2) && <br />}
31
+ {address.city}
32
+ {address.city && (address.state || address.zipCode) && ","} {address.state} {address.zipCode}
33
+ </>
34
+ )
35
+ }
36
+
37
+ export { MultiLineAddress as default, MultiLineAddress }
@@ -0,0 +1,21 @@
1
+ import * as React from "react"
2
+ import { Address } from "./MultiLineAddress"
3
+
4
+ export interface OneLineAddressProps {
5
+ address: Address
6
+ }
7
+
8
+ const OneLineAddress = ({ address }: OneLineAddressProps) => {
9
+ if (!address) return null
10
+
11
+ return (
12
+ <>
13
+ {address.street} {address.street2}
14
+ {address.street && `, `}
15
+ {address.city}
16
+ {address.city && (address.state || address.zipCode) && ","} {address.state} {address.zipCode}
17
+ </>
18
+ )
19
+ }
20
+
21
+ export { OneLineAddress as default, OneLineAddress }
@@ -375,7 +375,10 @@
375
375
  "application.review.confirmation.whatExpectFirstParagraph.refer": "Please refer to the listing for the lottery results date.",
376
376
  "application.review.confirmation.whatExpectSecondparagraph": "Applicants will be contacted in order until vacancies are filled. Should your application be chosen, be prepared to fill out a more detailed application and provide required supporting documents.",
377
377
  "application.review.confirmation.whatExpectTitle": "What to expect next",
378
- "application.review.confirmation.whatHappensNext": "### What happens next?\n\n* After all applications are submitted, the property manager will begin processing applications.\n\n* Eligibile applicants will be contacted by on a **first come first serve** basis until vacancies are filled\n\n* If you are contacted for an interview, you will need to fill out a more detailed application and provide supporting documents.",
378
+ "application.review.confirmation.whatHappensNext": "### What happens next?\n\n* After all applications are submitted, the property manager will begin processing applications.\n\n* %{reviewOrder}\n\n* If you are contacted for an interview, you will need to fill out a more detailed application and provide supporting documents.",
379
+ "application.review.confirmation.eligibleApplicants.FCFS": "Eligibile applicants will be contacted by on a **first come first serve** basis until vacancies are filled",
380
+ "application.review.confirmation.eligibleApplicants.lotteryDate": "The lottery will be held on **%{lotteryDate}**.",
381
+ "application.review.confirmation.eligibleApplicants.lottery": "Eligible applicants will be contacted by the agent in lottery rank order until vacancies are filled",
379
382
  "application.review.demographics.ethnicityLabel": "Which best describes your ethnicity?",
380
383
  "application.review.demographics.ethnicityOptions.hispanicLatino": "Hispanic / Latino",
381
384
  "application.review.demographics.ethnicityOptions.notHispanicLatino": "Not Hispanic / Latino",
@@ -22,26 +22,30 @@
22
22
  .footer-sock__inner {
23
23
  @apply max-w-5xl;
24
24
  @apply m-auto;
25
-
25
+ justify-content: space-between;
26
26
  @screen lg {
27
27
  @apply flex;
28
28
  }
29
29
  }
30
30
 
31
31
  .footer-copyright {
32
+ width: auto;
32
33
  @screen lg {
33
- @apply w-6/12;
34
34
  @apply text-left;
35
35
  }
36
36
  }
37
37
 
38
38
  .footer-nav {
39
39
  @apply mt-5;
40
+ width: auto;
40
41
 
41
42
  @screen lg {
42
43
  @apply mt-0;
43
- @apply w-6/12;
44
44
  @apply text-right;
45
+ display: flex;
46
+ align-items: flex-start;
47
+ flex-wrap: wrap;
48
+ justify-content: end;
45
49
  }
46
50
  }
47
51
  }
@@ -1,10 +1,11 @@
1
- import React, { useState, useCallback, useEffect, useMemo } from "react"
1
+ import React, { useState, useCallback, useEffect } from "react"
2
2
  import "mapbox-gl/dist/mapbox-gl.css"
3
3
  import MapGL, { Marker } from "react-map-gl"
4
4
 
5
5
  import "./ListingMap.scss"
6
- import { MultiLineAddress, Address } from "../../helpers/address"
6
+ import { MultiLineAddress, Address } from "../../helpers/MultiLineAddress"
7
7
  import { useIntersect } from "../../.."
8
+ import { Heading } from "../../headers/Heading"
8
9
 
9
10
  export interface ListingMapProps {
10
11
  address?: Address
@@ -107,7 +108,11 @@ const ListingMap = (props: ListingMapProps) => {
107
108
  return (
108
109
  <div className="listing-map" ref={setIntersectingElement}>
109
110
  <div className="addressPopup">
110
- {props.listingName && <h3 className="text-caps-tiny">{props.listingName}</h3>}
111
+ {props.listingName && (
112
+ <Heading priority={3} style={"sidebarSubHeader"}>
113
+ {props.listingName}
114
+ </Heading>
115
+ )}
111
116
  <MultiLineAddress address={props.address} />
112
117
  </div>
113
118
  {(process.env.mapBoxToken || process.env.MAPBOX_TOKEN) && hasIntersected && (
@@ -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/address"
7
- import { SidebarAddress } from "./SidebarAddress"
6
+ import { Address } from "../../../helpers/MultiLineAddress"
7
+ import { ContactAddress } from "./ContactAddress"
8
8
  import { OrDivider } from "./OrDivider"
9
- import { ListingStatus } from "@bloom-housing/backend-core/types"
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
- onlineApplicationURL?: string
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
- applicationPickUpAddressOfficeHours?: string
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
- <h3 className="text-caps-tiny">{t("listings.apply.pickUpAnApplication")}</h3>
99
- <SidebarAddress
103
+ <Heading priority={3} style={"sidebarSubHeader"}>
104
+ {t("listings.apply.pickUpAnApplication")}
105
+ </Heading>
106
+ <ContactAddress
100
107
  address={props.applicationPickUpAddress}
101
- officeHours={props.applicationPickUpAddressOfficeHours}
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>
@@ -1,78 +1,73 @@
1
1
  import * as React from "react"
2
- import { t } from "../../../helpers/translator"
3
- import { Address } from "../../../helpers/address"
4
- import { SidebarAddress } from "./SidebarAddress"
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 { ListingStatus } from "@bloom-housing/backend-core/types"
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
- applicationMailingAddress?: Address
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
- postmarkedApplicationData?: PostmarkedApplication
20
- listingStatus?: ListingStatus
21
- }
22
-
23
- const SubmitApplication = (props: ApplicationAddressesProps) => {
24
- if (
25
- props.listingStatus === ListingStatus.closed ||
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">{t("listings.apply.submitAPaperApplication")}</div>
58
- {props.applicationMailingAddress && (
38
+ <div className="text-serif-lg">{strings.sectionHeader}</div>
39
+ {applicationMailingAddress && (
59
40
  <>
60
- <h3 className="text-caps-tiny">{t("listings.apply.sendByUsMail")}</h3>
41
+ <Heading priority={3} style={"sidebarSubHeader"}>
42
+ {strings.mailHeader}
43
+ </Heading>
61
44
  <>
62
- <p className="text-gray-700">{props.applicationOrganization}</p>
63
- <SidebarAddress address={props.applicationMailingAddress} />
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">{getPostmarkString()}</p>
48
+ {strings.postmark && <p className="mt-4 text-tiny text-gray-750">{strings.postmark}</p>}
66
49
  </>
67
50
  )}
68
- {props.applicationDropOffAddress && (
51
+ {applicationDropOffAddress && (
69
52
  <>
70
- {props.applicationMailingAddress && <OrDivider bgColor="gray-100" />}
71
- <h3 className="text-caps-tiny">{t("listings.apply.dropOffApplication")}</h3>
72
- <SidebarAddress
73
- address={props.applicationDropOffAddress}
74
- officeHours={props.applicationDropOffAddressOfficeHours}
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
- <h4 className={props.sectionHeader ? "text-caps-underline" : "text-caps-tiny"}>
23
+ <Heading priority={4} style={props.sectionHeader ? "sidebarHeader" : "sidebarSubHeader"}>
23
24
  {props.headerText}
24
- </h4>
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"}`}>
@@ -1,47 +0,0 @@
1
- import * as React from "react"
2
-
3
- export interface Address {
4
- city?: string
5
- latitude?: number
6
- longitude?: number
7
- placeName?: string
8
- state?: string
9
- street2?: string
10
- street?: string
11
- zipCode?: string
12
- }
13
-
14
- interface AddressProps {
15
- address: Address
16
- }
17
-
18
- export const OneLineAddress = (props: AddressProps) => {
19
- if (!props.address) return null
20
-
21
- return (
22
- <>
23
- {props.address.street},{` `}
24
- {props.address.city}, {props.address.state} {props.address.zipCode}
25
- </>
26
- )
27
- }
28
-
29
- export const MultiLineAddress = (props: AddressProps) => {
30
- if (!props.address) return null
31
-
32
- return (
33
- <>
34
- {props.address.placeName && (
35
- <>
36
- {props.address.placeName}
37
- <br />
38
- </>
39
- )}
40
- {props.address.street} {props.address.street2}
41
- {(props.address.street || props.address.street2) && <br />}
42
- {props.address.city}
43
- {props.address.city && (props.address.state || props.address.zipCode) && ","}{" "}
44
- {props.address.state} {props.address.zipCode}
45
- </>
46
- )
47
- }
@@ -1,72 +0,0 @@
1
- import * as React from "react"
2
- import { SidebarAddress } from "./SidebarAddress"
3
- import { t } from "../../../helpers/translator"
4
- import { Icon, IconFillColors } from "../../../icons/Icon"
5
- import { Listing } from "@bloom-housing/backend-core/types"
6
-
7
- interface LeasingAgentProps {
8
- listing: Listing
9
- managementCompany?: { name: string; website: string }
10
- }
11
-
12
- const LeasingAgent = (props: LeasingAgentProps) => {
13
- const listing = props.listing
14
-
15
- const phoneNumber = listing.leasingAgentPhone
16
- ? `tel:${listing.leasingAgentPhone.replace(/[-()]/g, "")}`
17
- : ""
18
-
19
- let managementWebsite = props.managementCompany?.website
20
- if (managementWebsite && !managementWebsite.startsWith("http")) {
21
- managementWebsite = `http://${managementWebsite}`
22
- }
23
-
24
- return (
25
- <section className="aside-block">
26
- <h4 className="text-caps-underline">{t("leasingAgent.contact")}</h4>
27
-
28
- {listing.leasingAgentName && <p className="text-xl">{listing.leasingAgentName}</p>}
29
- {listing.leasingAgentTitle && <p className="text-gray-700">{listing.leasingAgentTitle}</p>}
30
- {props.managementCompany?.name && (
31
- <p className="text-gray-700">{props.managementCompany.name}</p>
32
- )}
33
-
34
- {listing.leasingAgentPhone && (
35
- <>
36
- <p className="mt-5">
37
- <a href={phoneNumber}>
38
- <Icon symbol="phone" size="medium" fill={IconFillColors.primary} /> {t("t.call")}{" "}
39
- {listing.leasingAgentPhone}
40
- </a>
41
- </p>
42
- <p className="text-sm text-gray-700">{t("leasingAgent.dueToHighCallVolume")}</p>
43
- </>
44
- )}
45
-
46
- {listing.leasingAgentEmail && (
47
- <p className="my-5">
48
- <a href={`mailto:${listing.leasingAgentEmail}`}>
49
- <Icon symbol="mail" size="medium" fill={IconFillColors.primary} /> {t("t.email")}
50
- </a>
51
- </p>
52
- )}
53
-
54
- {managementWebsite && (
55
- <p className="my-5">
56
- <a href={managementWebsite} target="_blank" rel="noreferrer noopener">
57
- <Icon symbol="globe" size="medium" fill={IconFillColors.primary} /> {t("t.website")}
58
- </a>
59
- </p>
60
- )}
61
-
62
- {listing.leasingAgentAddress && (
63
- <SidebarAddress
64
- address={listing.leasingAgentAddress}
65
- officeHours={listing.leasingAgentOfficeHours}
66
- />
67
- )}
68
- </section>
69
- )
70
- }
71
-
72
- export { LeasingAgent as default, LeasingAgent }
@@ -1,56 +0,0 @@
1
- import * as React from "react"
2
- import ReactDOMServer from "react-dom/server"
3
- import { Icon, IconFillColors } from "../../../icons/Icon"
4
- import { OneLineAddress, MultiLineAddress, Address } from "../../../helpers/address"
5
- import { t } from "../../../helpers/translator"
6
- import Markdown from "markdown-to-jsx"
7
-
8
- export interface SidebarAddressProps {
9
- address?: Address
10
- officeHours?: string
11
- }
12
-
13
- const SidebarAddress = (props: SidebarAddressProps) => {
14
- const { address, officeHours } = props
15
- let mainAddress = null
16
- let googleMapsHref = ""
17
- let hours = <></>
18
-
19
- if (address?.street) {
20
- const oneLineAddress = <OneLineAddress address={address} />
21
- mainAddress = <MultiLineAddress address={address} />
22
-
23
- googleMapsHref =
24
- "https://www.google.com/maps/place/" + ReactDOMServer.renderToStaticMarkup(oneLineAddress)
25
- }
26
-
27
- if (officeHours) {
28
- hours = (
29
- <>
30
- <h3 className="text-caps-tiny ">{t("leasingAgent.officeHours")}</h3>
31
- <div className="text-gray-800 text-tiny markdown">
32
- <Markdown children={officeHours} options={{ disableParsingRawHTML: true }} />
33
- </div>
34
- </>
35
- )
36
- }
37
-
38
- return (
39
- <>
40
- {address?.street && (
41
- <>
42
- <p className="text-gray-700 mb-1">{mainAddress}</p>
43
- <p className="mb-4">
44
- <a href={googleMapsHref} className="inline-block pt-1" target="_blank">
45
- <Icon symbol="map" size="medium" fill={IconFillColors.primary} />{" "}
46
- {t("t.getDirections")}
47
- </a>
48
- </p>
49
- </>
50
- )}
51
- {hours}
52
- </>
53
- )
54
- }
55
-
56
- export { SidebarAddress as default, SidebarAddress }