@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.
Files changed (75) hide show
  1. package/lib/components/settings/company-details/index.d.ts +10 -9
  2. package/lib/components/settings/company-details/index.d.ts.map +1 -1
  3. package/lib/components/settings/company-details/index.js.map +1 -1
  4. package/lib/components/settings/company-email-footer/company-email-footer.stories.js +2 -2
  5. package/lib/components/settings/company-email-footer/company-email-footer.stories.js.map +1 -1
  6. package/lib/components/settings/company-email-footer/index.d.ts +4 -4
  7. package/lib/components/settings/company-email-footer/index.d.ts.map +1 -1
  8. package/lib/components/settings/company-email-reply-to/index.d.ts +1 -1
  9. package/lib/components/settings/company-email-reply-to/index.d.ts.map +1 -1
  10. package/lib/components/settings/company-email-sender/company-email-sender.stories.d.ts +3 -1
  11. package/lib/components/settings/company-email-sender/company-email-sender.stories.d.ts.map +1 -1
  12. package/lib/components/settings/company-email-sender/company-email-sender.stories.js +7 -3
  13. package/lib/components/settings/company-email-sender/company-email-sender.stories.js.map +1 -1
  14. package/lib/components/settings/company-email-sender/custom-domain-sender.d.ts +12 -0
  15. package/lib/components/settings/company-email-sender/custom-domain-sender.d.ts.map +1 -0
  16. package/lib/components/settings/company-email-sender/custom-domain-sender.js +29 -0
  17. package/lib/components/settings/company-email-sender/custom-domain-sender.js.map +1 -0
  18. package/lib/components/settings/company-email-sender/index.d.ts +2 -11
  19. package/lib/components/settings/company-email-sender/index.d.ts.map +1 -1
  20. package/lib/components/settings/company-email-sender/index.js +2 -28
  21. package/lib/components/settings/company-email-sender/index.js.map +1 -1
  22. package/lib/components/settings/company-email-sender/simple-sender.d.ts +8 -0
  23. package/lib/components/settings/company-email-sender/simple-sender.d.ts.map +1 -0
  24. package/lib/components/settings/company-email-sender/simple-sender.js +18 -0
  25. package/lib/components/settings/company-email-sender/simple-sender.js.map +1 -0
  26. package/lib/components/settings/company-trade-checkbox/index.d.ts +2 -2
  27. package/lib/components/settings/company-trade-checkbox/index.d.ts.map +1 -1
  28. package/lib/components/settings/company-trade-checkbox/index.js.map +1 -1
  29. package/lib/components/settings/company-trades-picker/index.d.ts +3 -3
  30. package/lib/components/settings/company-trades-picker/index.d.ts.map +1 -1
  31. package/lib/components/settings/company-trades-picker/index.js.map +1 -1
  32. package/lib/components/settings/double-opt-in/double-opt-in.stories.js +2 -2
  33. package/lib/components/settings/double-opt-in/double-opt-in.stories.js.map +1 -1
  34. package/lib/components/settings/double-opt-in/index.d.ts +5 -5
  35. package/lib/components/settings/double-opt-in/index.d.ts.map +1 -1
  36. package/lib/components/settings/email-validation/email-validation.stories.d.ts +13 -4
  37. package/lib/components/settings/email-validation/email-validation.stories.d.ts.map +1 -1
  38. package/lib/components/settings/email-validation/email-validation.stories.js +50 -23
  39. package/lib/components/settings/email-validation/email-validation.stories.js.map +1 -1
  40. package/lib/components/settings/email-validation/index.d.ts +5 -5
  41. package/lib/components/settings/email-validation/index.d.ts.map +1 -1
  42. package/lib/components/settings/email-validation/index.js +3 -2
  43. package/lib/components/settings/email-validation/index.js.map +1 -1
  44. package/lib/components/settings/form-errors-list/index.d.ts +7 -0
  45. package/lib/components/settings/form-errors-list/index.d.ts.map +1 -0
  46. package/lib/components/settings/form-errors-list/index.js +15 -0
  47. package/lib/components/settings/form-errors-list/index.js.map +1 -0
  48. package/lib/components/settings/index.d.ts +2 -0
  49. package/lib/components/settings/index.d.ts.map +1 -1
  50. package/lib/components/settings/index.js +2 -0
  51. package/lib/components/settings/index.js.map +1 -1
  52. package/lib/components/settings/opt-out-message/index.d.ts +6 -6
  53. package/lib/components/settings/opt-out-message/index.d.ts.map +1 -1
  54. package/lib/components/settings/opt-out-message/opt-out-message.stories.js +2 -2
  55. package/lib/components/settings/opt-out-message/opt-out-message.stories.js.map +1 -1
  56. package/package.json +3 -9
  57. package/src/components/settings/company-details/index.tsx +10 -9
  58. package/src/components/settings/company-email-footer/company-email-footer.stories.tsx +2 -2
  59. package/src/components/settings/company-email-footer/index.tsx +4 -4
  60. package/src/components/settings/company-email-reply-to/index.tsx +1 -1
  61. package/src/components/settings/company-email-sender/company-email-sender.stories.tsx +11 -3
  62. package/src/components/settings/company-email-sender/custom-domain-sender.tsx +129 -0
  63. package/src/components/settings/company-email-sender/index.tsx +2 -129
  64. package/src/components/settings/company-email-sender/simple-sender.tsx +71 -0
  65. package/src/components/settings/company-trade-checkbox/index.tsx +3 -3
  66. package/src/components/settings/company-trades-picker/index.tsx +4 -4
  67. package/src/components/settings/double-opt-in/double-opt-in.stories.tsx +2 -2
  68. package/src/components/settings/double-opt-in/index.tsx +5 -5
  69. package/src/components/settings/email-validation/email-validation.stories.tsx +56 -27
  70. package/src/components/settings/email-validation/index.tsx +14 -11
  71. package/src/components/settings/form-errors-list/index.tsx +39 -0
  72. package/src/components/settings/index.ts +2 -0
  73. package/src/components/settings/opt-out-message/index.tsx +6 -6
  74. package/src/components/settings/opt-out-message/opt-out-message.stories.tsx +2 -2
  75. package/tsconfig.tsbuildinfo +1 -1
@@ -1,129 +1,2 @@
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
-
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 &nbsp;&nbsp;
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&nbsp;
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 &nbsp;&nbsp;
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 InputFieldState('Jump in right now! Please!'),
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: InputFieldState<string>;
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
- type TType = 'high' | 'medium' | 'low';
8
- type TResult = 'ok' | 'fail';
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
- export const EmailValidation = emailValidationHoc<TType, TResult>({
11
- resultToText: new Map([
12
- ['ok', 'Valid'],
13
- ['fail', 'Invalid'],
14
- ]),
47
+ const EmailValidationComponent = emailValidationHoc<EmailValidationLevel, EmailValidationResult>({
48
+ resultToText,
15
49
  levelTypeMap: {
16
- high: 'high',
17
- medium: 'medium',
18
- low: 'low',
50
+ high: EmailValidationLevel.High,
51
+ medium: EmailValidationLevel.Medium,
52
+ low: EmailValidationLevel.Low,
19
53
  },
20
- levelsResultMap: new Map([
21
- ['high', ['ok', 'fail']],
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: EmailValidation,
64
+ component: EmailValidationComponent,
38
65
  parameters: {},
39
66
  };
40
67
 
41
- export function EmailValidationSimple() {
42
- const [type, setType] = useState<TType>('high');
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<TResult>>({
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<EmailValidationTableRecord<TResult>>([
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: 'ok',
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: 'fail',
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
- <EmailValidation
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: TEmailValidationType;
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
- levelsResultMap: Map<TEmailValidationType, TEmailValidationResult[]>;
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
- levelsResultMap,
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 levelsResultMap.get(level)?.map(r => <div key={r}>{r}</div>);
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: InputFieldState<string>;
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 InputFieldState('Sorry to see you go!'),
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),