@bloom-housing/ui-components 4.2.2-alpha.0 → 4.2.2-alpha.11
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 +111 -0
- package/index.ts +3 -0
- package/package.json +3 -3
- package/src/authentication/timeout.tsx +1 -0
- package/src/blocks/FormCard.tsx +1 -1
- package/src/forms/FieldGroup.tsx +18 -17
- package/src/global/markdown.scss +20 -0
- package/src/global/tables.scss +236 -58
- package/src/global/text.scss +7 -0
- package/src/headers/Heading.tsx +1 -0
- package/src/headers/SiteHeader.tsx +4 -1
- package/src/helpers/tableSummaries.tsx +34 -23
- package/src/locales/general.json +9 -2
- package/src/navigation/FooterNav.scss +2 -1
- package/src/overlays/Drawer.tsx +11 -3
- package/src/overlays/Modal.tsx +16 -7
- package/src/overlays/Overlay.tsx +4 -3
- package/src/page_components/ApplicationTimeline.scss +36 -0
- package/src/page_components/ApplicationTimeline.tsx +33 -0
- package/src/page_components/listing/UnitTables.tsx +19 -18
- package/src/page_components/sign-in/FormSignIn.tsx +1 -0
- package/src/page_components/sign-in/ResendConfirmationModal.tsx +108 -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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { t } from "./translator"
|
|
3
3
|
import { UnitSummary } from "@bloom-housing/backend-core/types"
|
|
4
|
+
import { StandardTableData } from "../tables/StandardTable"
|
|
4
5
|
|
|
5
|
-
export const unitSummariesTable = (summaries: UnitSummary[]) => {
|
|
6
|
+
export const unitSummariesTable = (summaries: UnitSummary[]): StandardTableData => {
|
|
6
7
|
const unitSummaries = summaries?.map((unitSummary) => {
|
|
7
8
|
const unitPluralization = unitSummary.totalAvailable == 1 ? t("t.unit") : t("t.units")
|
|
8
9
|
const minIncome =
|
|
@@ -10,7 +11,8 @@ export const unitSummariesTable = (summaries: UnitSummary[]) => {
|
|
|
10
11
|
<strong>{unitSummary.minIncomeRange.min}</strong>
|
|
11
12
|
) : (
|
|
12
13
|
<>
|
|
13
|
-
<strong>{unitSummary.minIncomeRange.min}</strong>
|
|
14
|
+
<strong>{unitSummary.minIncomeRange.min}</strong>
|
|
15
|
+
{` ${t("t.to")} `}
|
|
14
16
|
<strong>{unitSummary.minIncomeRange.max}</strong>
|
|
15
17
|
</>
|
|
16
18
|
)
|
|
@@ -24,7 +26,9 @@ export const unitSummariesTable = (summaries: UnitSummary[]) => {
|
|
|
24
26
|
</>
|
|
25
27
|
) : (
|
|
26
28
|
<>
|
|
27
|
-
<strong>{rentMin}</strong>
|
|
29
|
+
<strong>{rentMin}</strong>
|
|
30
|
+
{` ${t("t.to")} `}
|
|
31
|
+
<strong>{rentMax}</strong>
|
|
28
32
|
{unit}
|
|
29
33
|
</>
|
|
30
34
|
)
|
|
@@ -40,32 +44,39 @@ export const unitSummariesTable = (summaries: UnitSummary[]) => {
|
|
|
40
44
|
: getRent(unitSummary.rentRange.min, unitSummary.rentRange.max)
|
|
41
45
|
|
|
42
46
|
return {
|
|
43
|
-
unitType:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
unitType: {
|
|
48
|
+
content: <strong>{t(`listings.unitTypes.${unitSummary.unitType?.name}`)}</strong>,
|
|
49
|
+
},
|
|
50
|
+
minimumIncome: {
|
|
51
|
+
content: (
|
|
52
|
+
<span>
|
|
53
|
+
{minIncome}
|
|
54
|
+
{` ${t("t.perMonth")}`}
|
|
55
|
+
</span>
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
rent: { content: <span>{rent}</span> },
|
|
59
|
+
availability: {
|
|
60
|
+
content: (
|
|
61
|
+
<span>
|
|
62
|
+
{unitSummary.totalAvailable > 0 ? (
|
|
63
|
+
<>
|
|
64
|
+
<strong>{unitSummary.totalAvailable}</strong> {unitPluralization}
|
|
65
|
+
</>
|
|
66
|
+
) : (
|
|
67
|
+
<span className="uppercase">{t("listings.waitlist.label")}</span>
|
|
68
|
+
)}
|
|
69
|
+
</span>
|
|
70
|
+
),
|
|
71
|
+
},
|
|
61
72
|
}
|
|
62
73
|
})
|
|
63
74
|
|
|
64
75
|
return unitSummaries
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
export const getSummariesTable = (summaries: UnitSummary[]) => {
|
|
68
|
-
let unitSummaries = []
|
|
78
|
+
export const getSummariesTable = (summaries: UnitSummary[]): StandardTableData => {
|
|
79
|
+
let unitSummaries: StandardTableData = []
|
|
69
80
|
|
|
70
81
|
if (summaries?.length > 0) {
|
|
71
82
|
unitSummaries = unitSummariesTable(summaries)
|
package/src/locales/general.json
CHANGED
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"application.contact.yourPhoneNumber": "Your Phone Number",
|
|
99
99
|
"application.contact.zip": "Zip",
|
|
100
100
|
"application.contact.zipCode": "Zipcode",
|
|
101
|
+
"application.details.adaPriorities": "ADA Priorities Selected",
|
|
101
102
|
"application.edited": "Edited",
|
|
102
103
|
"application.financial.income.instruction1": "Add up your total gross (pre-tax) household income from wages, benefits and other sources from all household members.",
|
|
103
104
|
"application.financial.income.instruction2": "You only need to provide an estimated total right now. The actual total will be calculated if you are selected.",
|
|
@@ -353,22 +354,28 @@
|
|
|
353
354
|
"application.referralApplication.furtherInformation": "For further information",
|
|
354
355
|
"application.referralApplication.instructions": "The permanent supportive housing units are referred directly through <JURISDICTION> Coordinated Entry System. Households experiencing homelessness can call <PHONE_NUMBER> in order to get connected to an Access Point to learn more about the coordinated entry system and access housing-related resources and information.",
|
|
355
356
|
"application.referralApplication.phoneNumber": "211",
|
|
357
|
+
"application.review.confirmation.applicationReceived": "Application \nreceived",
|
|
358
|
+
"application.review.confirmation.applicationsClosed": "Application \nclosed",
|
|
359
|
+
"application.review.confirmation.applicationsRanked": "Application \nranked",
|
|
356
360
|
"application.review.confirmation.browseMore": "Browse more listings",
|
|
357
361
|
"application.review.confirmation.createAccountParagraph": "Creating an account will save your information for future applications, and you can check the status of this application anytime.",
|
|
362
|
+
"application.review.confirmation.createAccount": "### Would you like to create an account?\n\nCreating an account will save your information for future applications, and you can check the status of this application anytime.",
|
|
358
363
|
"application.review.confirmation.createAccountTitle": "Would you like to create an account?",
|
|
359
364
|
"application.review.confirmation.doNotSubmitTitle": "Do not submit another application for this listing.",
|
|
360
365
|
"application.review.confirmation.imdone": "No thanks, I'm done.",
|
|
361
|
-
"application.review.confirmation.lotteryNumber": "
|
|
366
|
+
"application.review.confirmation.lotteryNumber": "Your confirmation number",
|
|
362
367
|
"application.review.confirmation.needToUpdate": "If you need to update information on your application, do not apply again. Contact the agent if you did not receive an email confirmation.",
|
|
363
|
-
"application.review.confirmation.pleaseWriteNumber": "Please write down your application number and keep it in a safe place. We have also emailed this number to you if you provided an email address.",
|
|
368
|
+
"application.review.confirmation.pleaseWriteNumber": "Please write down your application number and keep it in a safe place. We have also emailed this number to you if you have provided an email address.",
|
|
364
369
|
"application.review.confirmation.print": "View submitted application and print a copy.",
|
|
365
370
|
"application.review.confirmation.title": "Thanks. We have received your application for ",
|
|
371
|
+
"application.review.confirmation.needToMakeUpdates": "### Need to make updates?\n\nIf you need to update information on your application, do not apply again. Instead, contact the agent for this listing.\n\n**%{agentName}** \n%{agentPhone} \n%{agentEmail}\n\n**Office Hours** \n%{agentOfficeHours}\n\nContact the agent if you did not receive an email confirmation.",
|
|
366
372
|
"application.review.confirmation.whatExpectFirstParagraph.attend": " You do not need to attend the housing lottery. Results will be posted ",
|
|
367
373
|
"application.review.confirmation.whatExpectFirstParagraph.held": "The lottery will be held on ",
|
|
368
374
|
"application.review.confirmation.whatExpectFirstParagraph.listing": "on the listing. ",
|
|
369
375
|
"application.review.confirmation.whatExpectFirstParagraph.refer": "Please refer to the listing for the lottery results date.",
|
|
370
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.",
|
|
371
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.",
|
|
372
379
|
"application.review.demographics.ethnicityLabel": "Which best describes your ethnicity?",
|
|
373
380
|
"application.review.demographics.ethnicityOptions.hispanicLatino": "Hispanic / Latino",
|
|
374
381
|
"application.review.demographics.ethnicityOptions.notHispanicLatino": "Not Hispanic / Latino",
|
package/src/overlays/Drawer.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React, { useRef } from "react"
|
|
2
2
|
import "./Drawer.scss"
|
|
3
3
|
import { Icon } from "../icons/Icon"
|
|
4
4
|
import { Overlay, OverlayProps } from "./Overlay"
|
|
@@ -6,6 +6,7 @@ import { Tag } from "../text/Tag"
|
|
|
6
6
|
import { AppearanceStyleType, AppearanceSizeType } from "../global/AppearanceTypes"
|
|
7
7
|
import { AlertTypes } from "../notifications/alertTypes"
|
|
8
8
|
import { AlertBox } from "../notifications"
|
|
9
|
+
import { nanoid } from "nanoid"
|
|
9
10
|
|
|
10
11
|
export enum DrawerSide {
|
|
11
12
|
left = "left",
|
|
@@ -28,18 +29,25 @@ const Drawer = (props: DrawerProps) => {
|
|
|
28
29
|
const drawerClasses = ["drawer"]
|
|
29
30
|
if (props.className) drawerClasses.push(props.className)
|
|
30
31
|
|
|
32
|
+
const uniqueIdRef = useRef(nanoid())
|
|
33
|
+
|
|
31
34
|
return (
|
|
32
35
|
<Overlay
|
|
33
|
-
|
|
36
|
+
ariaLabelledBy={uniqueIdRef.current}
|
|
34
37
|
ariaDescription={props.ariaDescription}
|
|
35
38
|
open={props.open}
|
|
36
39
|
onClose={props.onClose}
|
|
37
40
|
backdrop={props.backdrop}
|
|
38
41
|
className={"has-drawer" + (props.direction == DrawerSide.left ? " is-direction-left" : "")}
|
|
42
|
+
role="dialog"
|
|
39
43
|
>
|
|
40
44
|
<div className={drawerClasses.join(" ")}>
|
|
41
45
|
<header className="drawer__header">
|
|
42
|
-
{props.title &&
|
|
46
|
+
{props.title && (
|
|
47
|
+
<h1 className="drawer__title" id={uniqueIdRef.current}>
|
|
48
|
+
{props.title}
|
|
49
|
+
</h1>
|
|
50
|
+
)}
|
|
43
51
|
{props.headerTag && (
|
|
44
52
|
<Tag
|
|
45
53
|
pillStyle={true}
|
package/src/overlays/Modal.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React from "react"
|
|
1
|
+
import React, { useRef } from "react"
|
|
2
2
|
import "./Modal.scss"
|
|
3
3
|
import { Icon } from "../icons/Icon"
|
|
4
4
|
import { Overlay, OverlayProps } from "./Overlay"
|
|
5
|
+
import { nanoid } from "nanoid"
|
|
5
6
|
|
|
6
7
|
export interface ModalProps extends Omit<OverlayProps, "children"> {
|
|
7
8
|
title: string
|
|
@@ -9,12 +10,17 @@ export interface ModalProps extends Omit<OverlayProps, "children"> {
|
|
|
9
10
|
hideCloseIcon?: boolean
|
|
10
11
|
children?: React.ReactNode
|
|
11
12
|
slim?: boolean
|
|
13
|
+
role?: string
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
const ModalHeader = (props: { title: string }) => (
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
16
|
+
const ModalHeader = (props: { title: string; uniqueId?: string }) => (
|
|
17
|
+
<>
|
|
18
|
+
<header className="modal__inner">
|
|
19
|
+
<h1 className="modal__title" id={props.uniqueId}>
|
|
20
|
+
{props.title}
|
|
21
|
+
</h1>
|
|
22
|
+
</header>
|
|
23
|
+
</>
|
|
18
24
|
)
|
|
19
25
|
|
|
20
26
|
const ModalFooter = (props: { actions: React.ReactNode[] }) => (
|
|
@@ -28,17 +34,20 @@ const ModalFooter = (props: { actions: React.ReactNode[] }) => (
|
|
|
28
34
|
)
|
|
29
35
|
|
|
30
36
|
export const Modal = (props: ModalProps) => {
|
|
37
|
+
const uniqueIdRef = useRef(nanoid())
|
|
38
|
+
|
|
31
39
|
return (
|
|
32
40
|
<Overlay
|
|
33
|
-
|
|
41
|
+
ariaLabelledBy={uniqueIdRef.current}
|
|
34
42
|
ariaDescription={props.ariaDescription}
|
|
35
43
|
open={props.open}
|
|
36
44
|
onClose={props.onClose}
|
|
37
45
|
backdrop={props.backdrop}
|
|
38
46
|
slim={props.slim}
|
|
47
|
+
role={props.role ? props.role : "dialog"}
|
|
39
48
|
>
|
|
40
49
|
<div className="modal">
|
|
41
|
-
<ModalHeader title={props.title} />
|
|
50
|
+
<ModalHeader title={props.title} uniqueId={uniqueIdRef.current} />
|
|
42
51
|
|
|
43
52
|
<section className="modal__inner">
|
|
44
53
|
{typeof props.children === "string" ? (
|
package/src/overlays/Overlay.tsx
CHANGED
|
@@ -8,13 +8,14 @@ import { CSSTransition } from "react-transition-group"
|
|
|
8
8
|
|
|
9
9
|
export type OverlayProps = {
|
|
10
10
|
open?: boolean
|
|
11
|
-
|
|
11
|
+
ariaLabelledBy?: string
|
|
12
12
|
ariaDescription?: string
|
|
13
13
|
className?: string
|
|
14
14
|
backdrop?: boolean
|
|
15
15
|
onClose?: () => void
|
|
16
16
|
children: React.ReactNode
|
|
17
17
|
slim?: boolean
|
|
18
|
+
role?: string
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const OverlayInner = (props: OverlayProps) => {
|
|
@@ -31,8 +32,8 @@ const OverlayInner = (props: OverlayProps) => {
|
|
|
31
32
|
return (
|
|
32
33
|
<div
|
|
33
34
|
className={classNames.join(" ")}
|
|
34
|
-
role=
|
|
35
|
-
aria-labelledby={props.
|
|
35
|
+
role={props.role}
|
|
36
|
+
aria-labelledby={props.ariaLabelledBy}
|
|
36
37
|
aria-describedby={props.ariaDescription}
|
|
37
38
|
onClick={(e) => {
|
|
38
39
|
if (e.target === e.currentTarget) closeHandler()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.markdown ul.application-timeline {
|
|
2
|
+
@apply mb-8;
|
|
3
|
+
@apply ml-0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.application-timeline {
|
|
7
|
+
.progress-nav__item {
|
|
8
|
+
@apply pt-0;
|
|
9
|
+
|
|
10
|
+
text-transform: unset;
|
|
11
|
+
|
|
12
|
+
.absolute {
|
|
13
|
+
margin-top: 0.45rem;
|
|
14
|
+
z-index: 2;
|
|
15
|
+
margin-left: -0.4rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
p {
|
|
19
|
+
padding-top: 3.8em;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.progress-nav__item.is-active::before,
|
|
24
|
+
.progress-nav__item.is-disabled::before {
|
|
25
|
+
height: 2rem;
|
|
26
|
+
width: 2rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.progress-nav__item.is-active::before {
|
|
30
|
+
@apply bg-green-700;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.progress-nav__item::after {
|
|
34
|
+
top: 1rem;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import Markdown from "markdown-to-jsx"
|
|
3
|
+
import { Icon } from "../icons/Icon"
|
|
4
|
+
import { t } from "../helpers/translator"
|
|
5
|
+
import "./ApplicationTimeline.scss"
|
|
6
|
+
|
|
7
|
+
const ApplicationTimeline = () => (
|
|
8
|
+
<ul
|
|
9
|
+
className="progress-nav application-timeline"
|
|
10
|
+
aria-label="Steps of processing your application"
|
|
11
|
+
>
|
|
12
|
+
<li className="progress-nav__item is-active" aria-current="step">
|
|
13
|
+
<span className="text-white absolute">
|
|
14
|
+
<Icon symbol="check" size="base" />
|
|
15
|
+
</span>
|
|
16
|
+
<Markdown className="font-bold" options={{ disableParsingRawHTML: true }}>
|
|
17
|
+
{t("application.review.confirmation.applicationReceived")}
|
|
18
|
+
</Markdown>
|
|
19
|
+
</li>
|
|
20
|
+
<li className="progress-nav__item is-disabled">
|
|
21
|
+
<Markdown options={{ disableParsingRawHTML: true }}>
|
|
22
|
+
{t("application.review.confirmation.applicationsClosed")}
|
|
23
|
+
</Markdown>
|
|
24
|
+
</li>
|
|
25
|
+
<li className="progress-nav__item is-disabled">
|
|
26
|
+
<Markdown options={{ disableParsingRawHTML: true }}>
|
|
27
|
+
{t("application.review.confirmation.applicationsRanked")}
|
|
28
|
+
</Markdown>
|
|
29
|
+
</li>
|
|
30
|
+
</ul>
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
export { ApplicationTimeline }
|
|
@@ -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,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppearanceStyleType,
|
|
3
|
+
Button,
|
|
4
|
+
Modal,
|
|
5
|
+
t,
|
|
6
|
+
Form,
|
|
7
|
+
Field,
|
|
8
|
+
emailRegex,
|
|
9
|
+
NavigationContext,
|
|
10
|
+
} from "@bloom-housing/ui-components"
|
|
11
|
+
import React, { useEffect, useMemo, useContext } from "react"
|
|
12
|
+
import { useForm } from "react-hook-form"
|
|
13
|
+
|
|
14
|
+
export type ResendConfirmationModalProps = {
|
|
15
|
+
isOpen: boolean
|
|
16
|
+
initialEmailValue: string
|
|
17
|
+
onClose: () => void
|
|
18
|
+
onSubmit: (email: string) => void
|
|
19
|
+
loading: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ResendConfirmationModalForm = {
|
|
23
|
+
onSubmit: (email: string) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ResendConfirmationModal = ({
|
|
27
|
+
isOpen,
|
|
28
|
+
initialEmailValue,
|
|
29
|
+
loading,
|
|
30
|
+
onClose,
|
|
31
|
+
onSubmit,
|
|
32
|
+
}: ResendConfirmationModalProps) => {
|
|
33
|
+
const { router } = useContext(NavigationContext)
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
35
|
+
const { register, errors, reset, getValues, trigger } = useForm({
|
|
36
|
+
defaultValues: useMemo(() => {
|
|
37
|
+
return {
|
|
38
|
+
emailResend: initialEmailValue,
|
|
39
|
+
}
|
|
40
|
+
}, [initialEmailValue]),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
reset({
|
|
45
|
+
emailResend: initialEmailValue,
|
|
46
|
+
})
|
|
47
|
+
}, [initialEmailValue, reset])
|
|
48
|
+
|
|
49
|
+
const onFormSubmit = async () => {
|
|
50
|
+
const isValid = await trigger()
|
|
51
|
+
if (!isValid) return
|
|
52
|
+
|
|
53
|
+
const { emailResend } = getValues()
|
|
54
|
+
onSubmit(emailResend)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Modal
|
|
59
|
+
open={isOpen}
|
|
60
|
+
title={t("authentication.signIn.yourAccountIsNotConfirmed")}
|
|
61
|
+
ariaDescription={t("authentication.createAccount.linkExpired")}
|
|
62
|
+
onClose={() => {
|
|
63
|
+
onClose()
|
|
64
|
+
window.scrollTo(0, 0)
|
|
65
|
+
}}
|
|
66
|
+
actions={[
|
|
67
|
+
<Button
|
|
68
|
+
type="button"
|
|
69
|
+
styleType={AppearanceStyleType.primary}
|
|
70
|
+
onClick={() => onFormSubmit()}
|
|
71
|
+
loading={loading}
|
|
72
|
+
>
|
|
73
|
+
{t("authentication.createAccount.resendTheEmail")}
|
|
74
|
+
</Button>,
|
|
75
|
+
<Button
|
|
76
|
+
type="button"
|
|
77
|
+
styleType={AppearanceStyleType.alert}
|
|
78
|
+
onClick={() => {
|
|
79
|
+
onClose()
|
|
80
|
+
window.scrollTo(0, 0)
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{t("t.cancel")}
|
|
84
|
+
</Button>,
|
|
85
|
+
]}
|
|
86
|
+
>
|
|
87
|
+
<>
|
|
88
|
+
<Form>
|
|
89
|
+
<Field
|
|
90
|
+
caps={true}
|
|
91
|
+
type="email"
|
|
92
|
+
name="emailResend"
|
|
93
|
+
label={t("authentication.createAccount.resendAnEmailTo")}
|
|
94
|
+
placeholder="example@web.com"
|
|
95
|
+
validation={{ required: true, pattern: emailRegex }}
|
|
96
|
+
error={!!errors.emailResend}
|
|
97
|
+
errorMessage={t("authentication.signIn.loginError")}
|
|
98
|
+
register={register}
|
|
99
|
+
/>
|
|
100
|
+
</Form>
|
|
101
|
+
|
|
102
|
+
<p className="pt-4">{t("authentication.createAccount.resendEmailInfo")}</p>
|
|
103
|
+
</>
|
|
104
|
+
</Modal>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export { ResendConfirmationModal as default, ResendConfirmationModal }
|
|
@@ -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
|
|