@servicetitan/mpa-components 0.2.2 → 0.4.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/lib/components/settings/company-details/index.d.ts +10 -9
- package/lib/components/settings/company-details/index.d.ts.map +1 -1
- package/lib/components/settings/company-details/index.js.map +1 -1
- package/lib/components/settings/company-email-footer/company-email-footer.stories.js +2 -2
- package/lib/components/settings/company-email-footer/company-email-footer.stories.js.map +1 -1
- package/lib/components/settings/company-email-footer/index.d.ts +4 -4
- package/lib/components/settings/company-email-footer/index.d.ts.map +1 -1
- package/lib/components/settings/company-email-reply-to/index.d.ts +1 -1
- package/lib/components/settings/company-email-reply-to/index.d.ts.map +1 -1
- package/lib/components/settings/company-email-sender/company-email-sender.stories.d.ts +3 -1
- package/lib/components/settings/company-email-sender/company-email-sender.stories.d.ts.map +1 -1
- package/lib/components/settings/company-email-sender/company-email-sender.stories.js +7 -3
- package/lib/components/settings/company-email-sender/company-email-sender.stories.js.map +1 -1
- package/lib/components/settings/company-email-sender/custom-domain-sender.d.ts +12 -0
- package/lib/components/settings/company-email-sender/custom-domain-sender.d.ts.map +1 -0
- package/lib/components/settings/company-email-sender/custom-domain-sender.js +29 -0
- package/lib/components/settings/company-email-sender/custom-domain-sender.js.map +1 -0
- package/lib/components/settings/company-email-sender/index.d.ts +2 -11
- package/lib/components/settings/company-email-sender/index.d.ts.map +1 -1
- package/lib/components/settings/company-email-sender/index.js +2 -28
- package/lib/components/settings/company-email-sender/index.js.map +1 -1
- package/lib/components/settings/company-email-sender/simple-sender.d.ts +8 -0
- package/lib/components/settings/company-email-sender/simple-sender.d.ts.map +1 -0
- package/lib/components/settings/company-email-sender/simple-sender.js +18 -0
- package/lib/components/settings/company-email-sender/simple-sender.js.map +1 -0
- package/lib/components/settings/company-trade-checkbox/index.d.ts +2 -2
- package/lib/components/settings/company-trade-checkbox/index.d.ts.map +1 -1
- package/lib/components/settings/company-trade-checkbox/index.js.map +1 -1
- package/lib/components/settings/company-trades-picker/index.d.ts +3 -3
- package/lib/components/settings/company-trades-picker/index.d.ts.map +1 -1
- package/lib/components/settings/company-trades-picker/index.js.map +1 -1
- package/lib/components/settings/double-opt-in/double-opt-in.stories.js +2 -2
- package/lib/components/settings/double-opt-in/double-opt-in.stories.js.map +1 -1
- package/lib/components/settings/double-opt-in/index.d.ts +5 -5
- package/lib/components/settings/double-opt-in/index.d.ts.map +1 -1
- package/lib/components/settings/email-validation/email-validation.stories.d.ts +13 -4
- package/lib/components/settings/email-validation/email-validation.stories.d.ts.map +1 -1
- package/lib/components/settings/email-validation/email-validation.stories.js +50 -23
- package/lib/components/settings/email-validation/email-validation.stories.js.map +1 -1
- package/lib/components/settings/email-validation/index.d.ts +5 -5
- package/lib/components/settings/email-validation/index.d.ts.map +1 -1
- package/lib/components/settings/email-validation/index.js +3 -2
- package/lib/components/settings/email-validation/index.js.map +1 -1
- package/lib/components/settings/form-errors-list/index.d.ts +7 -0
- package/lib/components/settings/form-errors-list/index.d.ts.map +1 -0
- package/lib/components/settings/form-errors-list/index.js +15 -0
- package/lib/components/settings/form-errors-list/index.js.map +1 -0
- package/lib/components/settings/index.d.ts +2 -0
- package/lib/components/settings/index.d.ts.map +1 -1
- package/lib/components/settings/index.js +2 -0
- package/lib/components/settings/index.js.map +1 -1
- package/lib/components/settings/opt-out-message/index.d.ts +6 -6
- package/lib/components/settings/opt-out-message/index.d.ts.map +1 -1
- package/lib/components/settings/opt-out-message/opt-out-message.stories.js +2 -2
- package/lib/components/settings/opt-out-message/opt-out-message.stories.js.map +1 -1
- package/package.json +3 -9
- package/src/components/settings/company-details/index.tsx +10 -9
- package/src/components/settings/company-email-footer/company-email-footer.stories.tsx +2 -2
- package/src/components/settings/company-email-footer/index.tsx +4 -4
- package/src/components/settings/company-email-reply-to/index.tsx +1 -1
- package/src/components/settings/company-email-sender/company-email-sender.stories.tsx +11 -3
- package/src/components/settings/company-email-sender/custom-domain-sender.tsx +129 -0
- package/src/components/settings/company-email-sender/index.tsx +2 -129
- package/src/components/settings/company-email-sender/simple-sender.tsx +71 -0
- package/src/components/settings/company-trade-checkbox/index.tsx +3 -3
- package/src/components/settings/company-trades-picker/index.tsx +4 -4
- package/src/components/settings/double-opt-in/double-opt-in.stories.tsx +2 -2
- package/src/components/settings/double-opt-in/index.tsx +5 -5
- package/src/components/settings/email-validation/email-validation.stories.tsx +56 -27
- package/src/components/settings/email-validation/index.tsx +14 -11
- package/src/components/settings/form-errors-list/index.tsx +39 -0
- package/src/components/settings/index.ts +2 -0
- package/src/components/settings/opt-out-message/index.tsx +6 -6
- package/src/components/settings/opt-out-message/opt-out-message.stories.tsx +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,129 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { Form, Text } from '@servicetitan/design-system';
|
|
5
|
-
import { InputFieldState } from '@servicetitan/form';
|
|
6
|
-
|
|
7
|
-
import { SettingsSection } from '../settings-section';
|
|
8
|
-
import { convertDomainName } from '../../../utils/helpers';
|
|
9
|
-
|
|
10
|
-
import * as Styles from './company-email-sender.module.less';
|
|
11
|
-
|
|
12
|
-
export interface CompanyEmailSenderProps {
|
|
13
|
-
formState: {
|
|
14
|
-
senderName: InputFieldState<string>;
|
|
15
|
-
senderDomain: InputFieldState<string>;
|
|
16
|
-
senderEmail: InputFieldState<string>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
senderTld: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const CompanyEmailSender: FC<CompanyEmailSenderProps> = observer(
|
|
23
|
-
({ formState: { senderDomain, senderName, senderEmail }, senderTld }) => {
|
|
24
|
-
const handleSenderNameChange = useCallback(
|
|
25
|
-
(_0: SyntheticEvent<HTMLInputElement>, data: { value: string }) => {
|
|
26
|
-
if (data.value.length > 80) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
senderName.onChange(data.value);
|
|
31
|
-
},
|
|
32
|
-
[senderName]
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
const handleSenderDomainChange = useCallback(
|
|
36
|
-
(_0: SyntheticEvent<HTMLInputElement>, data: { value: string }) => {
|
|
37
|
-
if (data.value.length > 30) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
senderDomain.onChange(data.value);
|
|
42
|
-
},
|
|
43
|
-
[senderDomain]
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const handleSenderEmailChange = useCallback(
|
|
47
|
-
(_0: SyntheticEvent<HTMLInputElement>, data: { value: string }) => {
|
|
48
|
-
if (data.value.length > 50) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
senderEmail.onChange(data.value);
|
|
53
|
-
},
|
|
54
|
-
[senderEmail]
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<SettingsSection
|
|
59
|
-
title="Sender"
|
|
60
|
-
text="Configure sender name and email."
|
|
61
|
-
qaPrefix="qa-settings-sender"
|
|
62
|
-
>
|
|
63
|
-
<Form className="m-b-0-i">
|
|
64
|
-
<Form.Group widths="equal" className="m-b-2-i">
|
|
65
|
-
<Form.Input
|
|
66
|
-
width={6}
|
|
67
|
-
className="m-b-0-i qa-settings-sender-name"
|
|
68
|
-
value={senderName.value}
|
|
69
|
-
onChange={handleSenderNameChange}
|
|
70
|
-
error={senderName.hasError}
|
|
71
|
-
label={
|
|
72
|
-
<Fragment>
|
|
73
|
-
Sender Name
|
|
74
|
-
<Text inline size={2} subdued>
|
|
75
|
-
max 80 characters
|
|
76
|
-
</Text>
|
|
77
|
-
</Fragment>
|
|
78
|
-
}
|
|
79
|
-
placeholder="John Doe"
|
|
80
|
-
fluid
|
|
81
|
-
/>
|
|
82
|
-
<Form.Input
|
|
83
|
-
width={6}
|
|
84
|
-
className="m-b-0-i qa-settings-sender-domain"
|
|
85
|
-
value={senderDomain.value}
|
|
86
|
-
onChange={handleSenderDomainChange}
|
|
87
|
-
error={senderDomain.hasError}
|
|
88
|
-
label="Sender Domain"
|
|
89
|
-
placeholder="Your Company"
|
|
90
|
-
fluid
|
|
91
|
-
/>
|
|
92
|
-
</Form.Group>
|
|
93
|
-
<Form.Group className="m-b-0-i">
|
|
94
|
-
<Form.Input
|
|
95
|
-
width={6}
|
|
96
|
-
className="m-b-0-i qa-settings-sender-email"
|
|
97
|
-
value={senderEmail.value}
|
|
98
|
-
onChange={handleSenderEmailChange}
|
|
99
|
-
error={senderEmail.hasError}
|
|
100
|
-
label={
|
|
101
|
-
<Fragment>
|
|
102
|
-
Sender Email
|
|
103
|
-
<Text inline size={2} subdued>
|
|
104
|
-
(domain added automatically)
|
|
105
|
-
</Text>
|
|
106
|
-
</Fragment>
|
|
107
|
-
}
|
|
108
|
-
placeholder="john.doe"
|
|
109
|
-
fluid
|
|
110
|
-
/>
|
|
111
|
-
<Form.Field label={'\u00A0'}>
|
|
112
|
-
<div className={Styles.domain}>
|
|
113
|
-
<Text
|
|
114
|
-
size={3}
|
|
115
|
-
bold
|
|
116
|
-
subdued
|
|
117
|
-
className="qa-settings-sender-generated-domain"
|
|
118
|
-
>
|
|
119
|
-
@{senderDomain.$ ? convertDomainName(senderDomain.$) : 'domain'}
|
|
120
|
-
.{senderTld}
|
|
121
|
-
</Text>
|
|
122
|
-
</div>
|
|
123
|
-
</Form.Field>
|
|
124
|
-
</Form.Group>
|
|
125
|
-
</Form>
|
|
126
|
-
</SettingsSection>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
);
|
|
1
|
+
export { CompanyEmailSender as CompanyEmailSenderCustomDomain } from './custom-domain-sender';
|
|
2
|
+
export { CompanyEmailSender as CompanyEmailSender } from './simple-sender';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { SyntheticEvent, Fragment, useCallback, FC } from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
|
|
4
|
+
import { Form, Text } from '@servicetitan/design-system';
|
|
5
|
+
import { InputFieldState } from '@servicetitan/form';
|
|
6
|
+
import { SettingsSection } from '../settings-section';
|
|
7
|
+
|
|
8
|
+
export interface CompanyEmailSender {
|
|
9
|
+
senderName: InputFieldState<string | undefined>;
|
|
10
|
+
senderEmail: InputFieldState<string | undefined>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const CompanyEmailSender: FC<CompanyEmailSender> = observer(
|
|
14
|
+
({ senderName, senderEmail }) => {
|
|
15
|
+
const handleSenderNameChange = useCallback(
|
|
16
|
+
(_0: SyntheticEvent<HTMLInputElement, Event>, data: { value: string }) => {
|
|
17
|
+
if (data.value.length > 80) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
senderName.onChange(data.value);
|
|
22
|
+
},
|
|
23
|
+
[senderName]
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const handleSenderEmailBlur = useCallback(() => {
|
|
27
|
+
senderEmail.validate();
|
|
28
|
+
}, [senderEmail]);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<SettingsSection
|
|
32
|
+
title="Sender"
|
|
33
|
+
text="Configure sender name and email."
|
|
34
|
+
qaPrefix="qa-settings-sender"
|
|
35
|
+
>
|
|
36
|
+
<Form className="m-b-0-i">
|
|
37
|
+
<Form.Group widths="equal" className="m-b-0-i">
|
|
38
|
+
<Form.Input
|
|
39
|
+
width={6}
|
|
40
|
+
className="m-b-0-i qa-settings-sender-name"
|
|
41
|
+
value={senderName.value}
|
|
42
|
+
onChange={handleSenderNameChange}
|
|
43
|
+
error={senderName.hasError}
|
|
44
|
+
label={
|
|
45
|
+
<Fragment>
|
|
46
|
+
Sender Name
|
|
47
|
+
<Text inline size={2} subdued>
|
|
48
|
+
max 80 characters
|
|
49
|
+
</Text>
|
|
50
|
+
</Fragment>
|
|
51
|
+
}
|
|
52
|
+
placeholder="John Doe"
|
|
53
|
+
fluid
|
|
54
|
+
/>
|
|
55
|
+
<Form.Input
|
|
56
|
+
width={6}
|
|
57
|
+
className="m-b-0-i qa-settings-sender-email"
|
|
58
|
+
value={senderEmail.value}
|
|
59
|
+
onChange={senderEmail.onChangeHandler}
|
|
60
|
+
error={senderEmail.hasError}
|
|
61
|
+
onBlur={handleSenderEmailBlur}
|
|
62
|
+
label="Sender Email"
|
|
63
|
+
placeholder="name@company.com"
|
|
64
|
+
fluid
|
|
65
|
+
/>
|
|
66
|
+
</Form.Group>
|
|
67
|
+
</Form>
|
|
68
|
+
</SettingsSection>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
@@ -5,7 +5,7 @@ import { Card, Icon } from '@servicetitan/design-system';
|
|
|
5
5
|
|
|
6
6
|
import * as Styles from './company-trade-checkbox.module.less';
|
|
7
7
|
|
|
8
|
-
export interface CompanyTradeCheckboxProps<TradesType extends string = string> {
|
|
8
|
+
export interface CompanyTradeCheckboxProps<TradesType extends string | number = string> {
|
|
9
9
|
label?: JSX.Element | string;
|
|
10
10
|
value?: TradesType;
|
|
11
11
|
className?: string;
|
|
@@ -13,13 +13,13 @@ export interface CompanyTradeCheckboxProps<TradesType extends string = string> {
|
|
|
13
13
|
onClick?(item?: TradesType): void;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function CompanyTradeCheckbox({
|
|
16
|
+
export function CompanyTradeCheckbox<T extends string | number = string>({
|
|
17
17
|
label,
|
|
18
18
|
value,
|
|
19
19
|
className,
|
|
20
20
|
active,
|
|
21
21
|
onClick,
|
|
22
|
-
}: CompanyTradeCheckboxProps) {
|
|
22
|
+
}: CompanyTradeCheckboxProps<T>) {
|
|
23
23
|
const handleClick = useCallback(() => {
|
|
24
24
|
if (onClick) {
|
|
25
25
|
onClick(value);
|
|
@@ -6,7 +6,7 @@ import { Grid, Text } from '@servicetitan/design-system';
|
|
|
6
6
|
import { SettingsSection } from '../settings-section';
|
|
7
7
|
import { CompanyTradeCheckbox } from '../company-trade-checkbox';
|
|
8
8
|
|
|
9
|
-
export interface Trade<TradeType extends string> {
|
|
9
|
+
export interface Trade<TradeType extends string | number> {
|
|
10
10
|
qaKey?: string;
|
|
11
11
|
name: string;
|
|
12
12
|
value: TradeType;
|
|
@@ -19,12 +19,12 @@ export interface Trade<TradeType extends string> {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export interface CompanyTradesPickerProps<TradeType extends string = string> {
|
|
22
|
+
export interface CompanyTradesPickerProps<TradeType extends string | number = string> {
|
|
23
23
|
trades: Trade<TradeType>[];
|
|
24
24
|
onTradeChange(t?: TradeType): void;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function CompanyTradesPicker<T extends string = string>({
|
|
27
|
+
export function CompanyTradesPicker<T extends string | number = string>({
|
|
28
28
|
trades,
|
|
29
29
|
onTradeChange,
|
|
30
30
|
}: CompanyTradesPickerProps<T>) {
|
|
@@ -46,7 +46,7 @@ export function CompanyTradesPicker<T extends string = string>({
|
|
|
46
46
|
{row.map(trade => {
|
|
47
47
|
return (
|
|
48
48
|
<Grid.Column key={trade.value}>
|
|
49
|
-
<CompanyTradeCheckbox
|
|
49
|
+
<CompanyTradeCheckbox<T>
|
|
50
50
|
className={`qa-settings-select-your-trades-${
|
|
51
51
|
trade.qaKey ?? trade.value
|
|
52
52
|
}`}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CheckboxFieldState, InputFieldState } from '@servicetitan/form';
|
|
1
|
+
import { CheckboxFieldState, InputFieldState, TextAreaFieldState } from '@servicetitan/form';
|
|
2
2
|
import { injectable, provide, useDependencies } from '@servicetitan/react-ioc';
|
|
3
3
|
import { FormState } from 'formstate';
|
|
4
4
|
import { DoubleOptIn as Component } from '.';
|
|
@@ -14,7 +14,7 @@ class DoubleOptInStore {
|
|
|
14
14
|
form = new FormState({
|
|
15
15
|
emailSubject: new InputFieldState('Jump in'),
|
|
16
16
|
emailHeader: new InputFieldState('Right now'),
|
|
17
|
-
emailBody: new
|
|
17
|
+
emailBody: new TextAreaFieldState('Jump in right now! Please!'),
|
|
18
18
|
emailButtonText: new InputFieldState('JUMP IN'),
|
|
19
19
|
enabled: new CheckboxFieldState(true),
|
|
20
20
|
});
|
|
@@ -2,7 +2,7 @@ import { FC, Fragment, useCallback, useState } from 'react';
|
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
|
|
5
|
-
import { CheckboxFieldState, InputFieldState } from '@servicetitan/form';
|
|
5
|
+
import { CheckboxFieldState, InputFieldState, TextAreaFieldState } from '@servicetitan/form';
|
|
6
6
|
import { Form, Text, ToggleSwitch, Button } from '@servicetitan/design-system';
|
|
7
7
|
import { useConfirm } from '@servicetitan/confirm';
|
|
8
8
|
|
|
@@ -15,10 +15,10 @@ export const footerText =
|
|
|
15
15
|
'If you’ve received this email by mistake, no action is needed, just delete this message.';
|
|
16
16
|
|
|
17
17
|
export interface DoubleOptInPropsFormState {
|
|
18
|
-
emailSubject: InputFieldState<string>;
|
|
19
|
-
emailHeader: InputFieldState<string>;
|
|
20
|
-
emailBody:
|
|
21
|
-
emailButtonText: InputFieldState<string>;
|
|
18
|
+
emailSubject: InputFieldState<string | undefined>;
|
|
19
|
+
emailHeader: InputFieldState<string | undefined>;
|
|
20
|
+
emailBody: TextAreaFieldState<string | undefined>;
|
|
21
|
+
emailButtonText: InputFieldState<string | undefined>;
|
|
22
22
|
enabled: CheckboxFieldState;
|
|
23
23
|
}
|
|
24
24
|
export interface DoubleOptInProps {
|
|
@@ -4,28 +4,55 @@ import { InMemoryDataSource } from '@servicetitan/data-query';
|
|
|
4
4
|
|
|
5
5
|
import { emailValidationHoc, EmailValidationTableRecord } from '.';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
enum EmailValidationLevel {
|
|
8
|
+
Low = 1,
|
|
9
|
+
Medium = 2,
|
|
10
|
+
High = 3,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
enum EmailValidationResult {
|
|
14
|
+
Undeliverable = 0,
|
|
15
|
+
Typo = 1,
|
|
16
|
+
Risky = 2,
|
|
17
|
+
Pending = -1,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const levelResultMap = new Map([
|
|
21
|
+
[EmailValidationLevel.High, [EmailValidationResult.Undeliverable]],
|
|
22
|
+
[EmailValidationLevel.Medium, [EmailValidationResult.Undeliverable]],
|
|
23
|
+
[EmailValidationLevel.Low, [EmailValidationResult.Undeliverable]],
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const resultToText = new Map<EmailValidationResult, string>([
|
|
27
|
+
[EmailValidationResult.Risky, 'Risky'],
|
|
28
|
+
[EmailValidationResult.Typo, 'Typo'],
|
|
29
|
+
[EmailValidationResult.Undeliverable, 'Undeliverable'],
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const resultToTooltipText = new Map<EmailValidationResult, string>([
|
|
33
|
+
[
|
|
34
|
+
EmailValidationResult.Risky,
|
|
35
|
+
'Address was determined risky based on historical analysis or problematic results.',
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
EmailValidationResult.Typo,
|
|
39
|
+
'Address was possibly typed incorrectly during sign-up, or matches known typo domains',
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
EmailValidationResult.Undeliverable,
|
|
43
|
+
'Address was determined invalid after performing multiple checks including MX, SMTP, etc.',
|
|
44
|
+
],
|
|
45
|
+
]);
|
|
9
46
|
|
|
10
|
-
|
|
11
|
-
resultToText
|
|
12
|
-
['ok', 'Valid'],
|
|
13
|
-
['fail', 'Invalid'],
|
|
14
|
-
]),
|
|
47
|
+
const EmailValidationComponent = emailValidationHoc<EmailValidationLevel, EmailValidationResult>({
|
|
48
|
+
resultToText,
|
|
15
49
|
levelTypeMap: {
|
|
16
|
-
high:
|
|
17
|
-
medium:
|
|
18
|
-
low:
|
|
50
|
+
high: EmailValidationLevel.High,
|
|
51
|
+
medium: EmailValidationLevel.Medium,
|
|
52
|
+
low: EmailValidationLevel.Low,
|
|
19
53
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
['medium', ['ok', 'fail']],
|
|
23
|
-
['high', ['ok']],
|
|
24
|
-
]),
|
|
25
|
-
resultToTooltipText: new Map([
|
|
26
|
-
['ok', 'Valid'],
|
|
27
|
-
['fail', 'Invalid'],
|
|
28
|
-
]),
|
|
54
|
+
levelResultMap,
|
|
55
|
+
resultToTooltipText,
|
|
29
56
|
TenantDateCell: (props: TableCellProps) => {
|
|
30
57
|
const value = props.dataItem[props.field!];
|
|
31
58
|
return <td className={props.className}>{value.toString()}</td>;
|
|
@@ -34,15 +61,15 @@ export const EmailValidation = emailValidationHoc<TType, TResult>({
|
|
|
34
61
|
|
|
35
62
|
export default {
|
|
36
63
|
title: 'MPA Components/settings/EmailValidation',
|
|
37
|
-
component:
|
|
64
|
+
component: EmailValidationComponent,
|
|
38
65
|
parameters: {},
|
|
39
66
|
};
|
|
40
67
|
|
|
41
|
-
export function
|
|
42
|
-
const [type, setType] = useState<
|
|
68
|
+
export function EmailValidation() {
|
|
69
|
+
const [type, setType] = useState<EmailValidationLevel>(EmailValidationLevel.High);
|
|
43
70
|
const [loading, setLoading] = useState(true);
|
|
44
71
|
|
|
45
|
-
const grid = new TableState<EmailValidationTableRecord<
|
|
72
|
+
const grid = new TableState<EmailValidationTableRecord<EmailValidationResult>>({
|
|
46
73
|
dataSource: null,
|
|
47
74
|
pageSize: 15,
|
|
48
75
|
});
|
|
@@ -53,12 +80,14 @@ export function EmailValidationSimple() {
|
|
|
53
80
|
return;
|
|
54
81
|
}
|
|
55
82
|
setLoading(true);
|
|
56
|
-
const dataSource = new InMemoryDataSource<
|
|
83
|
+
const dataSource = new InMemoryDataSource<
|
|
84
|
+
EmailValidationTableRecord<EmailValidationResult>
|
|
85
|
+
>([
|
|
57
86
|
{
|
|
58
87
|
id: 1,
|
|
59
88
|
active: false,
|
|
60
89
|
createdOn: new Date(),
|
|
61
|
-
result:
|
|
90
|
+
result: EmailValidationResult.Risky,
|
|
62
91
|
email: 'varg@vikernes.com',
|
|
63
92
|
dateAdded: new Date(),
|
|
64
93
|
},
|
|
@@ -66,7 +95,7 @@ export function EmailValidationSimple() {
|
|
|
66
95
|
id: 2,
|
|
67
96
|
active: true,
|
|
68
97
|
createdOn: new Date(),
|
|
69
|
-
result:
|
|
98
|
+
result: EmailValidationResult.Typo,
|
|
70
99
|
email: 'joe@biden.com',
|
|
71
100
|
dateAdded: new Date(),
|
|
72
101
|
},
|
|
@@ -78,7 +107,7 @@ export function EmailValidationSimple() {
|
|
|
78
107
|
});
|
|
79
108
|
|
|
80
109
|
return (
|
|
81
|
-
<
|
|
110
|
+
<EmailValidationComponent
|
|
82
111
|
loading={loading}
|
|
83
112
|
resultGrid={grid}
|
|
84
113
|
validationRiskType={type}
|
|
@@ -43,20 +43,20 @@ export interface EmailValidationTableRecord<T> {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export interface EmailValidationProps<
|
|
46
|
-
TEmailValidationType extends string = string,
|
|
47
|
-
TEmailValidationResult extends string = string
|
|
46
|
+
TEmailValidationType extends string | number = string,
|
|
47
|
+
TEmailValidationResult extends string | number = string
|
|
48
48
|
> {
|
|
49
49
|
className?: string;
|
|
50
50
|
loading: boolean;
|
|
51
51
|
resultGrid: TableState<EmailValidationTableRecord<TEmailValidationResult>>;
|
|
52
|
-
validationRiskType
|
|
52
|
+
validationRiskType?: TEmailValidationType;
|
|
53
53
|
handleDownload(): void;
|
|
54
54
|
setValidationRiskType(type: TEmailValidationType): void;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
interface EmailValidationHocOptions<
|
|
58
|
-
TEmailValidationType extends string = string,
|
|
59
|
-
TEmailValidationResult extends string = string
|
|
58
|
+
TEmailValidationType extends string | number = string,
|
|
59
|
+
TEmailValidationResult extends string | number = string
|
|
60
60
|
> {
|
|
61
61
|
resultToText: Map<TEmailValidationResult, string>;
|
|
62
62
|
levelTypeMap: {
|
|
@@ -64,19 +64,19 @@ interface EmailValidationHocOptions<
|
|
|
64
64
|
medium: TEmailValidationType;
|
|
65
65
|
low: TEmailValidationType;
|
|
66
66
|
};
|
|
67
|
-
|
|
67
|
+
levelResultMap: Map<TEmailValidationType, TEmailValidationResult[]>;
|
|
68
68
|
resultToTooltipText: Map<TEmailValidationResult, string>;
|
|
69
69
|
TenantDateCell: FC<TableCellProps>;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export function emailValidationHoc<
|
|
73
|
-
TEmailValidationType extends string = string,
|
|
74
|
-
TEmailValidationResult extends string = string
|
|
73
|
+
TEmailValidationType extends string | number = string,
|
|
74
|
+
TEmailValidationResult extends string | number = string
|
|
75
75
|
>({
|
|
76
76
|
resultToText,
|
|
77
77
|
levelTypeMap,
|
|
78
78
|
resultToTooltipText,
|
|
79
|
-
|
|
79
|
+
levelResultMap,
|
|
80
80
|
TenantDateCell,
|
|
81
81
|
}: EmailValidationHocOptions<TEmailValidationType, TEmailValidationResult>) {
|
|
82
82
|
const ResultColumnMenuFilter = multiSelectColumnMenuFilter(
|
|
@@ -103,7 +103,7 @@ export function emailValidationHoc<
|
|
|
103
103
|
resultGrid,
|
|
104
104
|
setValidationRiskType,
|
|
105
105
|
validationRiskType,
|
|
106
|
-
}: EmailValidationProps<TEmailValidationType>) {
|
|
106
|
+
}: EmailValidationProps<TEmailValidationType, TEmailValidationResult>) {
|
|
107
107
|
const localStore: EmailValidationLocalStore = useLocalStore(() => ({
|
|
108
108
|
open: false,
|
|
109
109
|
setOpen: action(function (this: EmailValidationLocalStore, state: boolean) {
|
|
@@ -127,7 +127,10 @@ export function emailValidationHoc<
|
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
const getResultsItemsForLevel = (level: TEmailValidationType) => {
|
|
130
|
-
return
|
|
130
|
+
return levelResultMap
|
|
131
|
+
.get(level)
|
|
132
|
+
?.map(r => resultToText.get(r))
|
|
133
|
+
.map(r => <div key={r}>{r}</div>);
|
|
131
134
|
};
|
|
132
135
|
|
|
133
136
|
return (
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import { ComposibleValidatable, FormState, ValidatableMapOrArray } from 'formstate';
|
|
4
|
+
|
|
5
|
+
import { Banner } from '@servicetitan/design-system';
|
|
6
|
+
|
|
7
|
+
export interface FormErrorsListProps<T extends ValidatableMapOrArray = ValidatableMapOrArray> {
|
|
8
|
+
form: FormState<T>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const FormErrorsList: FC<FormErrorsListProps> = observer(({ form }: FormErrorsListProps) => {
|
|
12
|
+
const getErrors = () => {
|
|
13
|
+
return Object.entries(form.$).map(
|
|
14
|
+
([key, field]: [key: string, field: ComposibleValidatable<unknown>]) => {
|
|
15
|
+
return field.hasError ? (
|
|
16
|
+
<li key={key} className="qa-settings-email-error-list-item">
|
|
17
|
+
{field.error}
|
|
18
|
+
</li>
|
|
19
|
+
) : null;
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (!form.hasError) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Banner
|
|
30
|
+
status="critical"
|
|
31
|
+
title="Missing Fields"
|
|
32
|
+
icon
|
|
33
|
+
className="m-b-2 qa-settings-email-error"
|
|
34
|
+
>
|
|
35
|
+
<p>Please complete all required fields:</p>
|
|
36
|
+
<ul className="qa-settings-email-error-list">{getErrors()}</ul>
|
|
37
|
+
</Banner>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
@@ -4,6 +4,8 @@ export * from './company-email-reply-to';
|
|
|
4
4
|
export * from './company-email-sender';
|
|
5
5
|
export * from './company-trades-picker';
|
|
6
6
|
export * from './double-opt-in';
|
|
7
|
+
export * from './email-validation';
|
|
8
|
+
export * from './form-errors-list';
|
|
7
9
|
export * from './logo-picker';
|
|
8
10
|
export * from './opt-out-message';
|
|
9
11
|
export * from './settings-section';
|
|
@@ -2,7 +2,7 @@ import { observer } from 'mobx-react';
|
|
|
2
2
|
import { FC, Fragment, useCallback, useState } from 'react';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
|
|
5
|
-
import { CheckboxFieldState, InputFieldState } from '@servicetitan/form';
|
|
5
|
+
import { CheckboxFieldState, InputFieldState, TextAreaFieldState } from '@servicetitan/form';
|
|
6
6
|
import { Button, Form, Stack, Text, ToggleSwitch } from '@servicetitan/design-system';
|
|
7
7
|
import { useConfirm } from '@servicetitan/confirm';
|
|
8
8
|
|
|
@@ -12,11 +12,11 @@ import { OptOutEmailPreview } from '../email-preview/opt-out-email-preview';
|
|
|
12
12
|
import * as Styles from './opt-out-message.module.less';
|
|
13
13
|
|
|
14
14
|
export interface OptOutMessageState {
|
|
15
|
-
subjectLine: InputFieldState<string>;
|
|
16
|
-
header: InputFieldState<string>;
|
|
17
|
-
body:
|
|
18
|
-
buttonText: InputFieldState<string>;
|
|
19
|
-
monthsLimit: InputFieldState<number>;
|
|
15
|
+
subjectLine: InputFieldState<string | undefined>;
|
|
16
|
+
header: InputFieldState<string | undefined>;
|
|
17
|
+
body: TextAreaFieldState<string | undefined>;
|
|
18
|
+
buttonText: InputFieldState<string | undefined>;
|
|
19
|
+
monthsLimit: InputFieldState<number | undefined>;
|
|
20
20
|
enabled: CheckboxFieldState;
|
|
21
21
|
autoSuppressionEnabled: CheckboxFieldState;
|
|
22
22
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CheckboxFieldState, InputFieldState } from '@servicetitan/form';
|
|
1
|
+
import { CheckboxFieldState, InputFieldState, TextAreaFieldState } from '@servicetitan/form';
|
|
2
2
|
import { injectable, provide, useDependencies } from '@servicetitan/react-ioc';
|
|
3
3
|
import { FormState } from 'formstate';
|
|
4
4
|
import { OptOutMessage as Component } from '.';
|
|
@@ -14,7 +14,7 @@ class OptOutStore {
|
|
|
14
14
|
form = new FormState({
|
|
15
15
|
subjectLine: new InputFieldState('Good bye'),
|
|
16
16
|
header: new InputFieldState('Opt out of emails'),
|
|
17
|
-
body: new
|
|
17
|
+
body: new TextAreaFieldState('Sorry to see you go!'),
|
|
18
18
|
buttonText: new InputFieldState('Bye'),
|
|
19
19
|
monthsLimit: new InputFieldState(6),
|
|
20
20
|
enabled: new CheckboxFieldState(true),
|