@bloom-housing/ui-components 4.0.1-alpha.9 → 4.0.3

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.
@@ -18,14 +18,39 @@ export interface ListingCardHeaderProps {
18
18
  }
19
19
  export interface ListingCardProps {
20
20
  imageCardProps: ImageCardProps
21
+ children?: React.ReactElement
21
22
  seeDetailsLink?: string
22
23
  tableHeaderProps?: ListingCardHeaderProps
23
- tableProps: ListingCardTableProps
24
+ tableProps?: ListingCardTableProps
24
25
  detailsLinkClass?: string
25
26
  }
26
27
 
27
28
  const ListingCard = (props: ListingCardProps) => {
28
- const { imageCardProps, tableProps, detailsLinkClass, tableHeaderProps } = props
29
+ const { imageCardProps, tableProps, detailsLinkClass, tableHeaderProps, children } = props
30
+
31
+ const tableHeader = () => {
32
+ return (
33
+ <h3
34
+ className={`listings-row_title ${
35
+ tableHeaderProps?.tableHeaderClass && tableHeaderProps?.tableHeaderClass
36
+ }`}
37
+ >
38
+ {tableHeaderProps?.tableHeader}
39
+ </h3>
40
+ )
41
+ }
42
+
43
+ const tableSubHeader = () => {
44
+ return (
45
+ <h4
46
+ className={`listings-row_subtitle ${
47
+ tableHeaderProps?.tableSubHeaderClass && tableHeaderProps?.tableSubHeaderClass
48
+ }`}
49
+ >
50
+ {tableHeaderProps?.tableSubHeader}
51
+ </h4>
52
+ )
53
+ }
29
54
 
30
55
  return (
31
56
  <article className="listings-row" data-test-id={"listing-card-component"}>
@@ -33,26 +58,11 @@ const ListingCard = (props: ListingCardProps) => {
33
58
  <ImageCard {...imageCardProps} />
34
59
  </div>
35
60
  <div className="listings-row_content">
36
- {tableHeaderProps?.tableHeader && (
37
- <h3
38
- className={`listings-row_title ${
39
- tableHeaderProps.tableHeaderClass && tableHeaderProps.tableHeaderClass
40
- }`}
41
- >
42
- {tableHeaderProps?.tableHeader}
43
- </h3>
44
- )}
45
- {tableHeaderProps?.tableSubHeader && (
46
- <h4
47
- className={`listings-row_subtitle ${
48
- tableHeaderProps.tableSubHeaderClass && tableHeaderProps.tableSubHeaderClass
49
- }`}
50
- >
51
- {tableHeaderProps?.tableSubHeader}
52
- </h4>
53
- )}
61
+ {tableHeaderProps?.tableHeader && tableHeader()}
62
+ {tableHeaderProps?.tableSubHeader && tableSubHeader()}
54
63
  <div className="listings-row_table">
55
- {(tableProps.data || tableProps.stackedData) && (
64
+ {children && children}
65
+ {tableProps && (tableProps.data || tableProps.stackedData) && (
56
66
  <>
57
67
  {tableHeaderProps?.stackedTable ? (
58
68
  <StackedTable {...(tableProps as StackedTableProps)} />
@@ -6,44 +6,71 @@
6
6
 
7
7
  .listings-group__header {
8
8
  @apply flex;
9
- @apply flex-row;
10
- @apply flex-wrap;
9
+ @apply flex-col;
11
10
  @apply max-w-5xl;
12
11
  @apply m-auto;
13
12
  @apply mt-6;
14
13
  @apply mb-8;
15
14
  @apply p-3;
15
+ @apply justify-between;
16
+
17
+ @screen md {
18
+ @apply flex-row;
19
+ }
16
20
  }
17
21
 
18
22
  .listings-group__icon {
19
23
  @apply hidden;
20
- @apply pt-2;
21
24
  @apply pr-5;
25
+ @apply mr-5;
26
+ @apply border-r-4;
27
+ @apply border-gray-700;
28
+ @apply items-center;
29
+ @apply hidden;
30
+ @screen md {
31
+ @apply flex;
32
+ }
33
+ }
34
+
35
+ .listings-group__content {
36
+ @apply flex;
37
+ @apply flex-row;
38
+ flex-grow: 2;
39
+ margin-right: 15px;
22
40
 
41
+ @apply max-w-full;
23
42
  @screen md {
24
- @apply inline-block;
43
+ max-width: 70%;
25
44
  }
26
45
  }
27
46
 
28
47
  .listings-group__header-group {
29
- @apply w-full;
48
+ max-width: inherit;
30
49
  @apply flex;
31
- @apply items-center;
32
- @apply mb-4;
50
+ @apply flex-col;
51
+ @apply items-start;
52
+ @apply justify-center;
53
+ @apply h-full;
54
+ }
33
55
 
56
+ .listings-group__info {
57
+ @apply text-gray-750;
58
+ @apply pt-1;
59
+ @apply pt-2;
34
60
  @screen md {
35
- @apply w-7/12;
36
- @apply mb-0;
61
+ @apply pt-0;
37
62
  }
38
63
  }
39
64
 
40
65
  .listings-group__button {
41
- @apply w-full;
42
66
  @apply flex;
43
67
  @apply items-center;
44
-
68
+ flex-shrink: 0;
69
+ @apply ml-0;
70
+ @apply pt-5;
45
71
  @screen md {
46
- @apply w-4/12;
72
+ @apply ml-6;
73
+ @apply pt-0;
47
74
  }
48
75
  }
49
76
 
@@ -51,15 +78,13 @@
51
78
  @apply uppercase;
52
79
  @apply font-alt-sans;
53
80
  @apply font-black;
54
- @apply my-1;
55
81
  @apply tracking-widest;
56
82
  @apply border-b-4;
57
- @apply border-gray-600;
83
+ @apply border-gray-700;
58
84
  @apply pb-1;
59
85
 
60
86
  @screen md {
61
- @apply px-4;
62
- @apply border-b-0;
63
- @apply border-l-4;
87
+ @apply pb-0;
88
+ @apply border-0;
64
89
  }
65
90
  }
@@ -1,43 +1,41 @@
1
1
  import React, { useState } from "react"
2
2
  import { Button } from "../../actions/Button"
3
- import { Icon } from "../../icons/Icon"
3
+ import { Icon, IconTypes } from "../../icons/Icon"
4
4
  import "./ListingsGroup.scss"
5
5
 
6
6
  export interface ListingsGroupProps {
7
7
  children?: React.ReactNode
8
- listingsCount: number
9
8
  header: string
9
+ hideButtonText: string
10
+ icon?: IconTypes
10
11
  info?: string
12
+ listingsCount: number
11
13
  showButtonText: string
12
- hideButtonText: string
13
14
  }
14
15
 
15
16
  const ListingsGroup = (props: ListingsGroupProps) => {
16
17
  const [showListings, setShowListings] = useState(false)
17
18
  const toggleListings = () => setShowListings(!showListings)
18
19
 
19
- let buttonText
20
-
21
20
  const listingsCount = ` (${props.listingsCount})`
22
- if (showListings) {
23
- buttonText = props.hideButtonText + listingsCount
24
- } else {
25
- buttonText = props.showButtonText + listingsCount
26
- }
27
21
 
28
22
  return (
29
23
  <div className="listings-group">
30
24
  <div className="listings-group__header">
31
- <div className="listings-group__icon">
32
- <Icon size="xlarge" symbol="clock" />
33
- </div>
34
- <div className="listings-group__header-group">
35
- <h2 className="listings-group__title">{props.header}</h2>
36
- {props.info && <div className="px-4 my-2">{props.info}</div>}
25
+ <div className={"listings-group__content"}>
26
+ <div className="listings-group__icon">
27
+ <Icon size="xlarge" symbol={props.icon ?? `clock`} />
28
+ </div>
29
+ <div className="listings-group__header-group">
30
+ <h2 className="listings-group__title">{props.header}</h2>
31
+ {props.info && <div className="listings-group__info">{props.info}</div>}
32
+ </div>
37
33
  </div>
38
34
  <div className="listings-group__button">
39
35
  <Button className="w-full" onClick={() => toggleListings()}>
40
- {buttonText}
36
+ {showListings
37
+ ? props.hideButtonText + listingsCount
38
+ : props.showButtonText + listingsCount}
41
39
  </Button>
42
40
  </div>
43
41
  </div>
@@ -5,7 +5,6 @@ import { LinkButton } from "../../../actions/LinkButton"
5
5
  import { AppearanceStyleType } from "../../../global/AppearanceTypes"
6
6
  import { Address } from "../../../helpers/address"
7
7
  import { SidebarAddress } from "./SidebarAddress"
8
- import { NumberedHeader } from "./NumberedHeader"
9
8
  import { OrDivider } from "./OrDivider"
10
9
  import { ListingStatus } from "@bloom-housing/backend-core/types"
11
10
 
@@ -16,7 +15,6 @@ export interface PaperApplication {
16
15
 
17
16
  export interface ApplicationsProps {
18
17
  onlineApplicationURL?: string
19
- applicationsDueDate?: string
20
18
  applicationsOpen: boolean
21
19
  applicationsOpenDate?: string
22
20
  paperApplications?: PaperApplication[]
@@ -67,7 +65,7 @@ const GetApplication = (props: ApplicationsProps) => {
67
65
  {props.applicationsOpen && props.paperMethod && (
68
66
  <>
69
67
  {props.onlineApplicationURL && <OrDivider bgColor="white" />}
70
- <NumberedHeader num={1} text={t("listings.apply.getAPaperApplication")} />
68
+ <div className="text-serif-lg">{t("listings.apply.getAPaperApplication")}</div>
71
69
  <Button
72
70
  styleType={
73
71
  !props.preview && props.onlineApplicationURL ? AppearanceStyleType.primary : undefined
@@ -1,6 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { t } from "../../../helpers/translator"
3
- import moment from "moment"
3
+ import dayjs from "dayjs"
4
4
 
5
5
  interface ListingUpdatedProps {
6
6
  listingUpdated: Date
@@ -11,7 +11,7 @@ const ListingUpdated = (props: ListingUpdatedProps) => {
11
11
  return (
12
12
  <section className="aside-block">
13
13
  <p className="text-tiny text-gray-800">
14
- {`${t("listings.listingUpdated")}: ${moment(listingUpdated).format("MMMM DD, YYYY")}`}
14
+ {`${t("listings.listingUpdated")}: ${dayjs(listingUpdated).format("MMMM DD, YYYY")}`}
15
15
  </p>
16
16
  </section>
17
17
  )
@@ -2,14 +2,13 @@ import * as React from "react"
2
2
  import { t } from "../../../helpers/translator"
3
3
  import { Address } from "../../../helpers/address"
4
4
  import { SidebarAddress } from "./SidebarAddress"
5
- import { NumberedHeader } from "./NumberedHeader"
6
5
  import { OrDivider } from "./OrDivider"
7
6
  import { ListingStatus } from "@bloom-housing/backend-core/types"
8
7
 
9
8
  export interface PostmarkedApplication {
10
- postmarkedApplicationsReceivedByDate: string
9
+ postmarkedApplicationsReceivedByDate: string | null
11
10
  developer: string
12
- applicationsDueDate: string
11
+ applicationsDueDate: string | null
13
12
  }
14
13
 
15
14
  export interface ApplicationAddressesProps {
@@ -22,50 +21,61 @@ export interface ApplicationAddressesProps {
22
21
  }
23
22
 
24
23
  const SubmitApplication = (props: ApplicationAddressesProps) => {
25
- if (props.listingStatus === ListingStatus.closed) {
24
+ if (
25
+ props.listingStatus === ListingStatus.closed ||
26
+ !(props.applicationMailingAddress || props.applicationDropOffAddress)
27
+ ) {
26
28
  return null
27
29
  }
28
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
+ }
52
+ }
53
+
29
54
  return (
30
55
  <>
31
- {(props.applicationMailingAddress ||
32
- props.applicationDropOffAddress ||
33
- props.postmarkedApplicationData) && (
34
- <section className="aside-block is-tinted bg-gray-100">
35
- <NumberedHeader num={2} text={t("listings.apply.submitAPaperApplication")} />
36
- {(props.applicationMailingAddress || props.postmarkedApplicationData) && (
56
+ <section className="aside-block is-tinted bg-gray-100">
57
+ <div className="text-serif-lg">{t("listings.apply.submitAPaperApplication")}</div>
58
+ {props.applicationMailingAddress && (
59
+ <>
60
+ <h3 className="text-caps-tiny">{t("listings.apply.sendByUsMail")}</h3>
37
61
  <>
38
- <h3 className="text-caps-tiny">{t("listings.apply.sendByUsMail")}</h3>
39
62
  <p className="text-gray-700">{props.applicationOrganization}</p>
40
- {props.applicationMailingAddress && (
41
- <SidebarAddress address={props.applicationMailingAddress} />
42
- )}
43
- <p className="mt-4 text-tiny text-gray-750">
44
- {props.postmarkedApplicationData?.postmarkedApplicationsReceivedByDate
45
- ? t("listings.apply.postmarkedApplicationsMustBeReceivedByDate", {
46
- applicationDueDate: props.postmarkedApplicationData?.applicationsDueDate,
47
- postmarkReceivedByDate:
48
- props.postmarkedApplicationData?.postmarkedApplicationsReceivedByDate,
49
- developer: props.postmarkedApplicationData?.developer,
50
- })
51
- : t("listings.apply.applicationsMustBeReceivedByDeadline")}
52
- </p>
53
- </>
54
- )}
55
- {props.applicationMailingAddress && props.applicationDropOffAddress && (
56
- <OrDivider bgColor="gray-100" />
57
- )}
58
- {props.applicationDropOffAddress && (
59
- <>
60
- <h3 className="text-caps-tiny">{t("listings.apply.dropOffApplication")}</h3>
61
- <SidebarAddress
62
- address={props.applicationDropOffAddress}
63
- officeHours={props.applicationDropOffAddressOfficeHours}
64
- />
63
+ <SidebarAddress address={props.applicationMailingAddress} />
65
64
  </>
66
- )}
67
- </section>
68
- )}
65
+ <p className="mt-4 text-tiny text-gray-750">{getPostmarkString()}</p>
66
+ </>
67
+ )}
68
+ {props.applicationDropOffAddress && (
69
+ <>
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
+ />
76
+ </>
77
+ )}
78
+ </section>
69
79
  </>
70
80
  )
71
81
  }
@@ -1,7 +1,7 @@
1
1
  import * as React from "react"
2
2
  import { ListingEvent } from "@bloom-housing/backend-core/types"
3
3
  import { t } from "../../../../helpers/translator"
4
- import moment from "moment"
4
+ import dayjs from "dayjs"
5
5
 
6
6
  const DownloadLotteryResults = (props: { event: ListingEvent; pdfUrl: string }) => {
7
7
  const { event, pdfUrl } = props
@@ -12,7 +12,7 @@ const DownloadLotteryResults = (props: { event: ListingEvent; pdfUrl: string })
12
12
  <section className="aside-block text-center">
13
13
  <h2 className="text-caps pb-4">{t("listings.lotteryResults.header")}</h2>
14
14
  <p className="uppercase text-gray-800 text-tiny font-semibold pb-4">
15
- {moment(event.startTime).format("MMMM D, YYYY")}
15
+ {dayjs(event.startTime).format("MMMM D, YYYY")}
16
16
  </p>
17
17
  <a
18
18
  className="button is-primary w-full mb-2"
@@ -1,20 +1,21 @@
1
1
  import * as React from "react"
2
2
  import { ListingEvent } from "@bloom-housing/backend-core/types"
3
- import moment from "moment"
3
+ import dayjs from "dayjs"
4
4
 
5
5
  const EventDateSection = (props: { event: ListingEvent }) => {
6
+ const getRangeString = () => {
7
+ const startTime = dayjs(props.event.startTime).format("hh:mma")
8
+ const endTime = dayjs(props.event.endTime).format("hh:mma")
9
+ return startTime === endTime ? startTime : `${startTime} - ${endTime}`
10
+ }
6
11
  return (
7
12
  <>
8
13
  {props.event.startTime && (
9
14
  <p className="text text-gray-800 pb-3 flex justify-between items-center">
10
15
  <span className="inline-block text-tiny uppercase">
11
- {moment(props.event.startTime).format("MMMM D, YYYY")}
12
- </span>
13
- <span className="inline-block text-sm font-bold">
14
- {moment(props.event.startTime).format("hh:mma") +
15
- "-" +
16
- moment(props.event.endTime).format("hh:mma")}
16
+ {dayjs(props.event.startTime).format("MMMM D, YYYY")}
17
17
  </span>
18
+ <span className="inline-block text-sm font-bold">{getRangeString()}</span>
18
19
  </p>
19
20
  )}
20
21
  </>
@@ -1,7 +1,7 @@
1
1
  import * as React from "react"
2
2
  import { ListingEvent } from "@bloom-housing/backend-core/types"
3
3
  import { t } from "../../../../helpers/translator"
4
- import moment from "moment"
4
+ import dayjs from "dayjs"
5
5
 
6
6
  const LotteryResultsEvent = (props: { event: ListingEvent }) => {
7
7
  const { event } = props
@@ -9,13 +9,13 @@ const LotteryResultsEvent = (props: { event: ListingEvent }) => {
9
9
  <section className="aside-block">
10
10
  <h4 className="text-caps-underline">{t("listings.lotteryResults.header")}</h4>
11
11
  <p className="text text-gray-800 pb-3 flex justify-between items-center">
12
- <span className="inline-block">{moment(props.event.startTime).format("MMMM D, YYYY")}</span>
12
+ <span className="inline-block">{dayjs(props.event.startTime).format("MMMM D, YYYY")}</span>
13
13
  </p>
14
14
  {event.note && <p className="text text-gray-600">{event.note}</p>}
15
15
  {!event.note && (
16
16
  <p className="text-tiny text-gray-600">
17
17
  {t("listings.lotteryResults.completeResultsWillBePosted", {
18
- hour: moment(props.event.startTime).format("h a"),
18
+ hour: dayjs(props.event.startTime).format("h a"),
19
19
  })}
20
20
  </p>
21
21
  )}
@@ -1,5 +1,4 @@
1
- import React from "react"
2
- import Link from "next/link"
1
+ import React, { useContext } from "react"
3
2
  import {
4
3
  AppearanceStyleType,
5
4
  Button,
@@ -15,7 +14,16 @@ import {
15
14
  ErrorMessage,
16
15
  } from "@bloom-housing/ui-components"
17
16
  import type { UseFormMethods } from "react-hook-form"
18
- import type { NetworkErrorValue, NetworkErrorReset } from "@bloom-housing/shared-helpers"
17
+ import { NavigationContext } from "../../config/NavigationContext"
18
+
19
+ export type NetworkErrorValue = {
20
+ title: string
21
+ content: string
22
+ } | null
23
+
24
+ export type NetworkErrorDetermineError = (status: number, error: Error) => void
25
+
26
+ export type NetworkErrorReset = () => void
19
27
 
20
28
  export type FormSignInProps = {
21
29
  control: FormSignInControl
@@ -49,6 +57,7 @@ const FormSignIn = ({
49
57
  const onError = () => {
50
58
  window.scrollTo(0, 0)
51
59
  }
60
+ const { LinkComponent } = useContext(NavigationContext)
52
61
 
53
62
  return (
54
63
  <FormCard>
@@ -90,9 +99,9 @@ const FormSignIn = ({
90
99
  />
91
100
 
92
101
  <aside className="float-right text-tiny font-semibold">
93
- <Link href="/forgot-password">
102
+ <LinkComponent href="/forgot-password">
94
103
  <a>{t("authentication.signIn.forgotPassword")}</a>
95
- </Link>
104
+ </LinkComponent>
96
105
  </aside>
97
106
 
98
107
  <Field