@thecb/components 2.2.1 → 2.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/.tool-versions +1 -0
- package/dist/index.cjs.js +30904 -3033
- package/package.json +15 -35
- package/rollup.config.js +3 -1
- package/src/components/atoms/button-with-action/ButtonWithAction.js +25 -4
- package/src/components/atoms/button-with-action/ButtonWithAction.theme.js +64 -234
- package/src/components/atoms/formatted-credit-card/FormattedCreditCard.js +53 -0
- package/src/components/atoms/formatted-credit-card/FormattedCreditCard.theme.js +9 -0
- package/src/components/atoms/formatted-credit-card/index.js +3 -0
- package/src/components/atoms/icons/AccountNumberImage.js +95 -0
- package/src/components/atoms/icons/BankIcon.js +82 -0
- package/src/components/atoms/icons/CheckmarkIcon.js +55 -0
- package/src/components/atoms/icons/GenericCard.js +39 -0
- package/src/components/atoms/icons/PaymentIcon.js +50 -0
- package/src/components/atoms/icons/RoutingNumberImage.js +95 -0
- package/src/components/atoms/icons/index.js +14 -1
- package/src/components/atoms/index.js +3 -0
- package/src/components/atoms/jumbo/Jumbo.js +76 -0
- package/src/components/atoms/jumbo/index.js +3 -0
- package/src/components/atoms/loading/Loading.js +17 -0
- package/src/components/atoms/loading/index.js +3 -0
- package/src/components/atoms/nav-header/NavHeader.js +1 -1
- package/src/components/index.js +1 -0
- package/src/components/molecules/account-and-routing-modal/AccountAndRoutingModal.js +75 -0
- package/src/components/molecules/account-and-routing-modal/AccountAndRoutingModal.theme.js +24 -0
- package/src/components/molecules/account-and-routing-modal/index.js +3 -0
- package/src/components/molecules/address-form/AddressForm.js +2 -1
- package/src/components/molecules/address-form/index.js +6 -6
- package/src/components/molecules/change-password-form/ChangePasswordForm.js +2 -1
- package/src/components/molecules/change-password-form/index.js +1 -1
- package/src/components/molecules/edit-name-form/EditNameForm.js +2 -1
- package/src/components/molecules/edit-name-form/index.js +1 -1
- package/src/components/molecules/editable-list/EditableList.js +139 -0
- package/src/components/molecules/editable-list/EditableList.styled.js +31 -0
- package/src/components/molecules/editable-list/index.js +3 -0
- package/src/components/molecules/editable-table/EditableTable.js +30 -0
- package/src/components/molecules/editable-table/EditableTable.styled.js +80 -0
- package/src/components/molecules/editable-table/TableListItem.js +64 -0
- package/src/components/molecules/editable-table/index.js +4 -0
- package/src/components/molecules/email-form/EmailForm.js +2 -1
- package/src/components/molecules/email-form/index.js +1 -1
- package/src/components/molecules/forgot-password-form/ForgotPasswordForm.js +2 -1
- package/src/components/molecules/forgot-password-form/index.js +1 -1
- package/src/components/molecules/index.js +5 -0
- package/src/components/molecules/login-form/LoginForm.js +2 -1
- package/src/components/molecules/login-form/index.js +1 -1
- package/src/components/molecules/module/Module.js +1 -3
- package/src/components/molecules/partial-amount-form/PartialAmountForm.js +73 -0
- package/src/components/molecules/partial-amount-form/PartialAmountForm.state.js +51 -0
- package/src/components/molecules/partial-amount-form/index.js +4 -0
- package/src/components/molecules/payment-form-ach/PaymentFormACH.js +189 -0
- package/src/components/molecules/payment-form-ach/PaymentFormACH.state.js +38 -0
- package/src/components/molecules/payment-form-ach/index.js +11 -0
- package/src/components/molecules/payment-form-card/PaymentFormCard.js +132 -0
- package/src/components/molecules/payment-form-card/PaymentFormCard.state.js +39 -0
- package/src/components/molecules/payment-form-card/index.js +11 -0
- package/src/components/molecules/phone-form/PhoneForm.js +2 -1
- package/src/components/molecules/phone-form/index.js +1 -1
- package/src/components/molecules/registration-form/RegistrationForm.js +2 -1
- package/src/components/molecules/registration-form/index.js +1 -1
- package/src/components/molecules/reset-password-form/ResetPasswordForm.js +3 -1
- package/src/components/molecules/reset-password-form/index.js +1 -1
- package/src/constants/index.js +2 -0
- package/src/index.js +2 -1
- package/src/util/formats.js +54 -2
- package/src/util/general.js +27 -4
- package/src/util/index.js +2 -0
- package/src/util/inputValidationUtils.js +0 -167
- package/stats.html +2652 -0
- package/src/util/router-utils.js +0 -23
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
2
|
import { required, isProbablyEmail } from "redux-freeform";
|
|
3
3
|
import { FormInput, FormInputColumn } from "../../atoms/form-layouts";
|
|
4
|
+
import { noop } from "../../../util/general";
|
|
4
5
|
|
|
5
6
|
const LoginForm = ({
|
|
6
7
|
clearOnDismount,
|
|
7
8
|
fields,
|
|
8
9
|
actions,
|
|
9
10
|
showErrors,
|
|
10
|
-
handleSubmit
|
|
11
|
+
handleSubmit = noop
|
|
11
12
|
}) => {
|
|
12
13
|
if (clearOnDismount) {
|
|
13
14
|
useEffect(() => () => actions.form.clear(), []);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { required, numberGreaterThan, numberLessThan } from "redux-freeform";
|
|
3
|
+
import { displayCurrency } from "../../../util/general";
|
|
4
|
+
import Text from "../../atoms/text";
|
|
5
|
+
import {
|
|
6
|
+
FormInput,
|
|
7
|
+
FormInputColumn,
|
|
8
|
+
FormContainer
|
|
9
|
+
} from "../../atoms/form-layouts";
|
|
10
|
+
import { moneyFormat } from "../../../util/formats";
|
|
11
|
+
|
|
12
|
+
const PartialAmountForm = ({
|
|
13
|
+
variant = "default",
|
|
14
|
+
lineItems,
|
|
15
|
+
maximum,
|
|
16
|
+
minimum = 1,
|
|
17
|
+
clearOnDismount,
|
|
18
|
+
fields,
|
|
19
|
+
actions,
|
|
20
|
+
showErrors = false
|
|
21
|
+
}) => {
|
|
22
|
+
if (clearOnDismount) {
|
|
23
|
+
useEffect(() => () => actions.form.clear(), []);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const amountErrors = {
|
|
27
|
+
[required.error]: "Amount is required",
|
|
28
|
+
[numberGreaterThan.error]: `Your total payment must be greater than ${displayCurrency(
|
|
29
|
+
minimum
|
|
30
|
+
)}`,
|
|
31
|
+
[numberLessThan.error]: `Your total payment must be less than ${displayCurrency(
|
|
32
|
+
maximum
|
|
33
|
+
)}`
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const lineItemsNew = Array.isArray(lineItems) ? lineItems : [];
|
|
37
|
+
return (
|
|
38
|
+
<FormContainer variant={variant} role="form" aria-label="Partial amount">
|
|
39
|
+
<Text variant="p">
|
|
40
|
+
Pay a portion of your current bill. The remaining balance will still be
|
|
41
|
+
due on the same due date.
|
|
42
|
+
</Text>
|
|
43
|
+
<div style={{ height: "16px" }}></div>
|
|
44
|
+
<FormInputColumn>
|
|
45
|
+
{lineItemsNew.map(li => (
|
|
46
|
+
<FormInput
|
|
47
|
+
labelTextWhenNoError={li.description}
|
|
48
|
+
key={li.id}
|
|
49
|
+
field={fields[li.id]}
|
|
50
|
+
fieldActions={actions.fields[li.id]}
|
|
51
|
+
showErrors={showErrors}
|
|
52
|
+
errorMessages={amountErrors}
|
|
53
|
+
style={{ textAlign: "right" }}
|
|
54
|
+
placeholder="$0.00"
|
|
55
|
+
formatter={moneyFormat}
|
|
56
|
+
decorator={
|
|
57
|
+
<Text variant="p">
|
|
58
|
+
Amount owed:{" "}
|
|
59
|
+
{
|
|
60
|
+
<Text variant="p" weight="600">
|
|
61
|
+
{displayCurrency(li.amount)}
|
|
62
|
+
</Text>
|
|
63
|
+
}
|
|
64
|
+
</Text>
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
67
|
+
))}
|
|
68
|
+
</FormInputColumn>
|
|
69
|
+
</FormContainer>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default PartialAmountForm;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFormState,
|
|
3
|
+
required,
|
|
4
|
+
onlyNaturals,
|
|
5
|
+
validateSum,
|
|
6
|
+
numberGreaterThan,
|
|
7
|
+
numberLessThan
|
|
8
|
+
} from "redux-freeform";
|
|
9
|
+
|
|
10
|
+
export const createPartialAmountFormState = (
|
|
11
|
+
lineItems,
|
|
12
|
+
maximum,
|
|
13
|
+
minimum = 1
|
|
14
|
+
) => {
|
|
15
|
+
const formConfig = lineItems.reduce((acc, item) => {
|
|
16
|
+
const validators = [
|
|
17
|
+
required(),
|
|
18
|
+
validateSum(
|
|
19
|
+
numberGreaterThan(minimum),
|
|
20
|
+
lineItems
|
|
21
|
+
.filter(lineItem => lineItem != item)
|
|
22
|
+
.reduce((acc, curr) => [...acc, curr.id], [])
|
|
23
|
+
)
|
|
24
|
+
];
|
|
25
|
+
if (!!maximum) {
|
|
26
|
+
validators.push(
|
|
27
|
+
validateSum(
|
|
28
|
+
numberLessThan(maximum),
|
|
29
|
+
lineItems
|
|
30
|
+
.filter(lineItem => lineItem != item)
|
|
31
|
+
.reduce((acc, curr) => [...acc, curr.id], [])
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...acc,
|
|
37
|
+
[item.id]: {
|
|
38
|
+
validators: validators,
|
|
39
|
+
constraints: [onlyNaturals()]
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}, {});
|
|
43
|
+
const { reducer, mapStateToProps, mapDispatchToProps } = createFormState(
|
|
44
|
+
formConfig
|
|
45
|
+
);
|
|
46
|
+
return {
|
|
47
|
+
partialAmountFormReducer: reducer,
|
|
48
|
+
partialAmountFormMapStateToProps: mapStateToProps,
|
|
49
|
+
partialAmountFormMapDispatchToProps: mapDispatchToProps
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import styled from "styled-components";
|
|
3
|
+
import { ifElse, isNil, always } from "ramda";
|
|
4
|
+
import Checkbox from "../../atoms/checkbox";
|
|
5
|
+
import {
|
|
6
|
+
required,
|
|
7
|
+
matchesField,
|
|
8
|
+
hasLength,
|
|
9
|
+
isRoutingNumber
|
|
10
|
+
} from "redux-freeform";
|
|
11
|
+
import FormSelect from "../../atoms/form-select";
|
|
12
|
+
import {
|
|
13
|
+
FormInput,
|
|
14
|
+
FormInputColumn,
|
|
15
|
+
FormContainer
|
|
16
|
+
} from "../../atoms/form-layouts";
|
|
17
|
+
import Alert from "../../atoms/alert";
|
|
18
|
+
import AccountAndRoutingModal from "../account-and-routing-modal";
|
|
19
|
+
import { displayCurrency, noop } from "../../../util/general";
|
|
20
|
+
|
|
21
|
+
const ModalWrapper = styled.div``;
|
|
22
|
+
|
|
23
|
+
const PaymentFormACH = ({
|
|
24
|
+
variant = "default",
|
|
25
|
+
defaultMethod,
|
|
26
|
+
toggleCheckbox,
|
|
27
|
+
hideDefaultPayment = true,
|
|
28
|
+
allowBankAccountType,
|
|
29
|
+
clearOnDismount,
|
|
30
|
+
fields,
|
|
31
|
+
actions,
|
|
32
|
+
showErrors,
|
|
33
|
+
fees,
|
|
34
|
+
handleSubmit = noop
|
|
35
|
+
}) => {
|
|
36
|
+
if (clearOnDismount) {
|
|
37
|
+
useEffect(() => () => actions.form.clear(), []);
|
|
38
|
+
}
|
|
39
|
+
const [showRouting, toggleShowRouting] = useState(false);
|
|
40
|
+
const [showAccount, toggleShowAccount] = useState(false);
|
|
41
|
+
|
|
42
|
+
const nameErrors = {
|
|
43
|
+
[required.error]: "Name is required"
|
|
44
|
+
};
|
|
45
|
+
const routingNumberErrors = {
|
|
46
|
+
[required.error]: "Routing number is required",
|
|
47
|
+
[hasLength.error]: "Routing number must be 9 digits",
|
|
48
|
+
[isRoutingNumber.error]: "Invalid routing number"
|
|
49
|
+
};
|
|
50
|
+
const confirmRoutingNumberErrors = {
|
|
51
|
+
[matchesField.error]:
|
|
52
|
+
"Confirm routing number field must match routing number"
|
|
53
|
+
};
|
|
54
|
+
const accountNumberErrors = {
|
|
55
|
+
[required.error]: "Account number is required",
|
|
56
|
+
[hasLength.error]: "Account number must be between 6 and 17 digits"
|
|
57
|
+
};
|
|
58
|
+
const confirmAccountNumberErrors = {
|
|
59
|
+
[matchesField.error]:
|
|
60
|
+
"Confirm account number field must match account number"
|
|
61
|
+
};
|
|
62
|
+
const accountTypeErrors = {
|
|
63
|
+
[required.error]: "Account type is required"
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<FormContainer variant={variant} role="form" aria-label="ACH Payment">
|
|
68
|
+
<FormInputColumn>
|
|
69
|
+
<FormInput
|
|
70
|
+
labelTextWhenNoError="Name on bank account"
|
|
71
|
+
errorMessages={nameErrors}
|
|
72
|
+
field={fields.name}
|
|
73
|
+
fieldActions={actions.fields.name}
|
|
74
|
+
showErrors={showErrors}
|
|
75
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
76
|
+
/>
|
|
77
|
+
<FormInput
|
|
78
|
+
labelTextWhenNoError="Routing number"
|
|
79
|
+
errorMessages={routingNumberErrors}
|
|
80
|
+
field={fields.routingNumber}
|
|
81
|
+
fieldActions={actions.fields.routingNumber}
|
|
82
|
+
showErrors={showErrors}
|
|
83
|
+
helperModal={() => (
|
|
84
|
+
<ModalWrapper
|
|
85
|
+
tabIndex="0"
|
|
86
|
+
onKeyDown={e =>
|
|
87
|
+
e.key === "Enter" && toggleShowAccount(!showRouting)
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
<AccountAndRoutingModal
|
|
91
|
+
link="What is this?"
|
|
92
|
+
title="Where is my routing number?"
|
|
93
|
+
content="Your routing number is the 9-digit number in the bottom left
|
|
94
|
+
corner of your check."
|
|
95
|
+
imageType="Routing"
|
|
96
|
+
isOpen={showRouting}
|
|
97
|
+
toggleOpen={toggleShowRouting}
|
|
98
|
+
/>
|
|
99
|
+
</ModalWrapper>
|
|
100
|
+
)}
|
|
101
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
102
|
+
/>
|
|
103
|
+
<FormInput
|
|
104
|
+
labelTextWhenNoError="Confirm routing number"
|
|
105
|
+
errorMessages={confirmRoutingNumberErrors}
|
|
106
|
+
field={fields.confirmRoutingNumber}
|
|
107
|
+
fieldActions={actions.fields.confirmRoutingNumber}
|
|
108
|
+
showErrors={showErrors}
|
|
109
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
110
|
+
/>
|
|
111
|
+
<FormInput
|
|
112
|
+
labelTextWhenNoError="Account number"
|
|
113
|
+
errorMessages={accountNumberErrors}
|
|
114
|
+
field={fields.accountNumber}
|
|
115
|
+
fieldActions={actions.fields.accountNumber}
|
|
116
|
+
showErrors={showErrors}
|
|
117
|
+
helperModal={() => (
|
|
118
|
+
<ModalWrapper
|
|
119
|
+
tabIndex="0"
|
|
120
|
+
onKeyDown={e =>
|
|
121
|
+
e.key === "Enter" && toggleShowAccount(!showAccount)
|
|
122
|
+
}
|
|
123
|
+
>
|
|
124
|
+
<AccountAndRoutingModal
|
|
125
|
+
link="What is this?"
|
|
126
|
+
title="Where is my account number?"
|
|
127
|
+
content="Your account number is usually the 10-digit number in the bottom
|
|
128
|
+
of your check to the right of the routing number."
|
|
129
|
+
imageType="Account"
|
|
130
|
+
isOpen={showAccount}
|
|
131
|
+
toggleOpen={toggleShowAccount}
|
|
132
|
+
/>
|
|
133
|
+
</ModalWrapper>
|
|
134
|
+
)}
|
|
135
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
136
|
+
/>
|
|
137
|
+
<FormInput
|
|
138
|
+
labelTextWhenNoError="Confirm account number"
|
|
139
|
+
errorMessages={confirmAccountNumberErrors}
|
|
140
|
+
field={fields.confirmAccountNumber}
|
|
141
|
+
fieldActions={actions.fields.confirmAccountNumber}
|
|
142
|
+
showErrors={showErrors}
|
|
143
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
144
|
+
/>
|
|
145
|
+
{allowBankAccountType && (
|
|
146
|
+
<FormSelect
|
|
147
|
+
labelTextWhenNoError="Account type"
|
|
148
|
+
options={[
|
|
149
|
+
{ text: "Select account type", value: "" },
|
|
150
|
+
{ text: "Checking", value: "CHECKING" },
|
|
151
|
+
{ text: "Savings", value: "SAVINGS" }
|
|
152
|
+
]}
|
|
153
|
+
fieldActions={actions.fields.accountType}
|
|
154
|
+
showErrors={showErrors}
|
|
155
|
+
errorMessages={accountTypeErrors}
|
|
156
|
+
field={fields.accountType}
|
|
157
|
+
/>
|
|
158
|
+
)}
|
|
159
|
+
{!hideDefaultPayment && (
|
|
160
|
+
<Checkbox
|
|
161
|
+
title="Save as Default Payment Method"
|
|
162
|
+
name="default-payment-ach"
|
|
163
|
+
onChange={toggleCheckbox}
|
|
164
|
+
checked={defaultMethod.value}
|
|
165
|
+
hidden={hideDefaultPayment}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
{!!fees?.value && (
|
|
169
|
+
<Alert
|
|
170
|
+
heading="Processing Fee"
|
|
171
|
+
text={`There is a processing fee of ${
|
|
172
|
+
fees.type === "FLAT"
|
|
173
|
+
? `${displayCurrency(fees.value)}`
|
|
174
|
+
: `${fees.value * 100}%`
|
|
175
|
+
} ${ifElse(
|
|
176
|
+
isNil,
|
|
177
|
+
always(""),
|
|
178
|
+
a => `with a minimum of ${displayCurrency(a)} `
|
|
179
|
+
)(fees.minimumInCents)}on all bank account payments.`}
|
|
180
|
+
variant="info"
|
|
181
|
+
showQuitLink={false}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
184
|
+
</FormInputColumn>
|
|
185
|
+
</FormContainer>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export default PaymentFormACH;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFormState,
|
|
3
|
+
required,
|
|
4
|
+
matchesField,
|
|
5
|
+
onlyIntegers,
|
|
6
|
+
hasLength,
|
|
7
|
+
isRoutingNumber
|
|
8
|
+
} from "redux-freeform";
|
|
9
|
+
|
|
10
|
+
const formConfig = {
|
|
11
|
+
name: {
|
|
12
|
+
validators: [required()]
|
|
13
|
+
},
|
|
14
|
+
routingNumber: {
|
|
15
|
+
validators: [required(), hasLength(9, 9), isRoutingNumber()],
|
|
16
|
+
constraints: [onlyIntegers(), hasLength(0, 9)]
|
|
17
|
+
},
|
|
18
|
+
confirmRoutingNumber: {
|
|
19
|
+
validators: [matchesField("routingNumber")],
|
|
20
|
+
constraints: [onlyIntegers(), hasLength(0, 9)]
|
|
21
|
+
},
|
|
22
|
+
accountNumber: {
|
|
23
|
+
validators: [required(), hasLength(6, 17)],
|
|
24
|
+
constraints: [onlyIntegers(), hasLength(0, 17)]
|
|
25
|
+
},
|
|
26
|
+
confirmAccountNumber: {
|
|
27
|
+
validators: [matchesField("accountNumber")],
|
|
28
|
+
constraints: [onlyIntegers(), hasLength(0, 17)]
|
|
29
|
+
},
|
|
30
|
+
accountType: {
|
|
31
|
+
defaultValue: "CHECKING",
|
|
32
|
+
validators: [required()]
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const { reducer, mapStateToProps, mapDispatchToProps } = createFormState(
|
|
37
|
+
formConfig
|
|
38
|
+
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import PaymentFormACH from "./PaymentFormACH";
|
|
2
|
+
import {
|
|
3
|
+
reducer,
|
|
4
|
+
mapStateToProps,
|
|
5
|
+
mapDispatchToProps
|
|
6
|
+
} from "./PaymentFormACH.state";
|
|
7
|
+
|
|
8
|
+
PaymentFormACH.reducer = reducer;
|
|
9
|
+
PaymentFormACH.mapStateToProps = mapStateToProps;
|
|
10
|
+
PaymentFormACH.mapDispatchToProps = mapDispatchToProps;
|
|
11
|
+
export default PaymentFormACH;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { required, hasLength, matchesRegex } from "redux-freeform";
|
|
3
|
+
import { ifElse, isNil, always } from "ramda";
|
|
4
|
+
import { checkCardBrand, displayCurrency, noop } from "../../../util/general";
|
|
5
|
+
import { expirationDateFormat, creditCardFormat } from "../../../util/formats";
|
|
6
|
+
import {
|
|
7
|
+
FormInput,
|
|
8
|
+
FormInputColumn,
|
|
9
|
+
FormContainer,
|
|
10
|
+
FormInputRow
|
|
11
|
+
} from "../../atoms/form-layouts";
|
|
12
|
+
import { Box } from "../../atoms/layouts";
|
|
13
|
+
import Alert from "../../atoms/alert";
|
|
14
|
+
|
|
15
|
+
const nameOnCardErrors = {
|
|
16
|
+
[required.error]: "Name is required"
|
|
17
|
+
};
|
|
18
|
+
const creditCardNumberErrors = {
|
|
19
|
+
[required.error]: "Credit card number is required",
|
|
20
|
+
[hasLength.error]: "Credit card number is invalid"
|
|
21
|
+
};
|
|
22
|
+
const expirationDateErrors = {
|
|
23
|
+
[required.error]: "Expiration date is required",
|
|
24
|
+
[hasLength.error]: "Expiration date is invalid",
|
|
25
|
+
[matchesRegex.error]: "Expiration date is invalid"
|
|
26
|
+
};
|
|
27
|
+
const cvvErrors = {
|
|
28
|
+
[required.error]: "CVV is required",
|
|
29
|
+
[hasLength.error]: "CVV is invalid"
|
|
30
|
+
};
|
|
31
|
+
const zipCodeErrors = {
|
|
32
|
+
[required.error]: "Zip code is required",
|
|
33
|
+
[hasLength.error]: "Zip code is invalid"
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const PaymentFormCard = ({
|
|
37
|
+
variant = "default",
|
|
38
|
+
hideZipCode = false,
|
|
39
|
+
clearOnDismount,
|
|
40
|
+
fields,
|
|
41
|
+
actions,
|
|
42
|
+
showErrors,
|
|
43
|
+
fees,
|
|
44
|
+
handleSubmit = noop,
|
|
45
|
+
isMobile
|
|
46
|
+
}) => {
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (clearOnDismount) {
|
|
49
|
+
return () => actions.form.clear();
|
|
50
|
+
}
|
|
51
|
+
}, []);
|
|
52
|
+
return (
|
|
53
|
+
<FormContainer variant={variant} role="form" aria-label="Card payment">
|
|
54
|
+
<FormInputColumn>
|
|
55
|
+
<FormInput
|
|
56
|
+
labelTextWhenNoError="Name on card"
|
|
57
|
+
errorMessages={nameOnCardErrors}
|
|
58
|
+
field={fields.nameOnCard}
|
|
59
|
+
fieldActions={actions.fields.nameOnCard}
|
|
60
|
+
showErrors={showErrors}
|
|
61
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
62
|
+
/>
|
|
63
|
+
<FormInput
|
|
64
|
+
labelTextWhenNoError="Credit card number"
|
|
65
|
+
errorMessages={creditCardNumberErrors}
|
|
66
|
+
field={fields.creditCardNumber}
|
|
67
|
+
fieldActions={actions.fields.creditCardNumber}
|
|
68
|
+
showErrors={showErrors}
|
|
69
|
+
formatter={creditCardFormat}
|
|
70
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
71
|
+
/>
|
|
72
|
+
<FormInputRow breakpoint="20rem">
|
|
73
|
+
<FormInput
|
|
74
|
+
labelTextWhenNoError="Expiration date (MM/YY)"
|
|
75
|
+
errorMessages={expirationDateErrors}
|
|
76
|
+
field={fields.expirationDate}
|
|
77
|
+
fieldActions={actions.fields.expirationDate}
|
|
78
|
+
showErrors={showErrors}
|
|
79
|
+
formatter={expirationDateFormat}
|
|
80
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
81
|
+
/>
|
|
82
|
+
<FormInput
|
|
83
|
+
labelTextWhenNoError="CVV"
|
|
84
|
+
errorMessages={cvvErrors}
|
|
85
|
+
field={fields.cvv}
|
|
86
|
+
fieldActions={actions.fields.cvv}
|
|
87
|
+
showErrors={showErrors}
|
|
88
|
+
background={
|
|
89
|
+
checkCardBrand(fields.creditCardNumber.rawValue) == "AMEX"
|
|
90
|
+
? "/AmexCVVHint.svg"
|
|
91
|
+
: "/CVVHint.svg"
|
|
92
|
+
}
|
|
93
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
94
|
+
/>
|
|
95
|
+
</FormInputRow>
|
|
96
|
+
{!hideZipCode && (
|
|
97
|
+
<Box
|
|
98
|
+
padding={isMobile ? "0" : "0 0.5rem 0 0"}
|
|
99
|
+
width={isMobile ? "100%" : "50%"}
|
|
100
|
+
>
|
|
101
|
+
<FormInput
|
|
102
|
+
labelTextWhenNoError="Zip code"
|
|
103
|
+
errorMessages={zipCodeErrors}
|
|
104
|
+
field={fields.zipCode}
|
|
105
|
+
fieldActions={actions.fields.zipCode}
|
|
106
|
+
showErrors={showErrors}
|
|
107
|
+
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
108
|
+
/>
|
|
109
|
+
</Box>
|
|
110
|
+
)}
|
|
111
|
+
{!!fees?.value && (
|
|
112
|
+
<Alert
|
|
113
|
+
heading="Processing Fee"
|
|
114
|
+
text={`There is a processing fee of ${
|
|
115
|
+
fees.type === "FLAT"
|
|
116
|
+
? `${displayCurrency(fees.value)}`
|
|
117
|
+
: `${fees.value * 100}%`
|
|
118
|
+
} ${ifElse(
|
|
119
|
+
isNil,
|
|
120
|
+
always(""),
|
|
121
|
+
a => `with a minimum of ${displayCurrency(a)} `
|
|
122
|
+
)(fees.minimumInCents)} on all card payments.`}
|
|
123
|
+
variant="info"
|
|
124
|
+
showQuitLink={false}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</FormInputColumn>
|
|
128
|
+
</FormContainer>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default PaymentFormCard;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFormState,
|
|
3
|
+
required,
|
|
4
|
+
onlyIntegers,
|
|
5
|
+
hasLength,
|
|
6
|
+
matchesRegex
|
|
7
|
+
} from "redux-freeform";
|
|
8
|
+
|
|
9
|
+
//TODO: Will make zip code able to have more than 5 digits once we add in the FormattedInput because it will have issues with format of 60606-1111.
|
|
10
|
+
|
|
11
|
+
const formConfig = {
|
|
12
|
+
nameOnCard: {
|
|
13
|
+
validators: [required()]
|
|
14
|
+
},
|
|
15
|
+
creditCardNumber: {
|
|
16
|
+
validators: [required(), hasLength(15, 16)],
|
|
17
|
+
constraints: [onlyIntegers(), hasLength(0, 16)]
|
|
18
|
+
},
|
|
19
|
+
expirationDate: {
|
|
20
|
+
validators: [
|
|
21
|
+
required(),
|
|
22
|
+
hasLength(4, 4),
|
|
23
|
+
matchesRegex(/^(01|02|03|04|05|06|07|08|09|10|11|12)[2-9]\d$/)
|
|
24
|
+
],
|
|
25
|
+
constraints: [onlyIntegers(), hasLength(0, 4)]
|
|
26
|
+
},
|
|
27
|
+
cvv: {
|
|
28
|
+
validators: [required(), hasLength(3, 4)],
|
|
29
|
+
constraints: [onlyIntegers(), hasLength(0, 4)]
|
|
30
|
+
},
|
|
31
|
+
zipCode: {
|
|
32
|
+
validators: [required(), hasLength(5, 5)],
|
|
33
|
+
constraints: [onlyIntegers(), hasLength(0, 5)]
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const { reducer, mapStateToProps, mapDispatchToProps } = createFormState(
|
|
38
|
+
formConfig
|
|
39
|
+
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import PaymentFormCard from "./PaymentFormCard";
|
|
2
|
+
import {
|
|
3
|
+
reducer,
|
|
4
|
+
mapStateToProps,
|
|
5
|
+
mapDispatchToProps
|
|
6
|
+
} from "./PaymentFormCard.state";
|
|
7
|
+
|
|
8
|
+
PaymentFormCard.reducer = reducer;
|
|
9
|
+
PaymentFormCard.mapStateToProps = mapStateToProps;
|
|
10
|
+
PaymentFormCard.mapDispatchToProps = mapDispatchToProps;
|
|
11
|
+
export default PaymentFormCard;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
FormContainer,
|
|
8
8
|
FormInputColumn
|
|
9
9
|
} from "../../atoms/form-layouts";
|
|
10
|
+
import { noop } from "../../../util/general";
|
|
10
11
|
|
|
11
12
|
const PhoneForm = ({
|
|
12
13
|
variant = "default",
|
|
@@ -14,7 +15,7 @@ const PhoneForm = ({
|
|
|
14
15
|
actions,
|
|
15
16
|
clearOnDismount,
|
|
16
17
|
showErrors,
|
|
17
|
-
handleSubmit
|
|
18
|
+
handleSubmit = noop
|
|
18
19
|
}) => {
|
|
19
20
|
if (clearOnDismount) {
|
|
20
21
|
useEffect(() => () => actions.form.clear(), []);
|
|
@@ -12,12 +12,13 @@ import {
|
|
|
12
12
|
import PasswordRequirements from "../../atoms/password-requirements";
|
|
13
13
|
import { Box } from "../../atoms/layouts";
|
|
14
14
|
import { FormInput, FormInputColumn } from "../../atoms/form-layouts";
|
|
15
|
+
import { noop } from "../../../util/general";
|
|
15
16
|
|
|
16
17
|
const RegistrationForm = ({
|
|
17
18
|
clearOnDismount,
|
|
18
19
|
fields,
|
|
19
20
|
actions,
|
|
20
|
-
handleSubmit,
|
|
21
|
+
handleSubmit = noop,
|
|
21
22
|
showErrors,
|
|
22
23
|
isMobile
|
|
23
24
|
}) => {
|
|
@@ -11,8 +11,10 @@ import {
|
|
|
11
11
|
import PasswordRequirements from "../../atoms/password-requirements";
|
|
12
12
|
import { Box } from "../../atoms/layouts";
|
|
13
13
|
import { FormInput, FormInputColumn } from "../../atoms/form-layouts";
|
|
14
|
+
import { noop } from "../../../util/general";
|
|
15
|
+
|
|
14
16
|
const ResetPasswordForm = ({
|
|
15
|
-
handleSubmit,
|
|
17
|
+
handleSubmit = noop,
|
|
16
18
|
clearOnDismount,
|
|
17
19
|
fields,
|
|
18
20
|
actions,
|
package/src/index.js
CHANGED