@servicetitan/mpa-components 0.1.0 → 0.2.1
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/company-details-form.stories.js +1 -1
- package/lib/components/settings/company-details/company-details-form.stories.js.map +1 -1
- package/lib/components/settings/email-validation/email-validation.stories.d.ts +11 -0
- package/lib/components/settings/email-validation/email-validation.stories.d.ts.map +1 -0
- package/lib/components/settings/email-validation/email-validation.stories.js +84 -0
- package/lib/components/settings/email-validation/email-validation.stories.js.map +1 -0
- package/lib/components/settings/email-validation/index.d.ts +32 -0
- package/lib/components/settings/email-validation/index.d.ts.map +1 -0
- package/lib/components/settings/email-validation/index.js +39 -0
- package/lib/components/settings/email-validation/index.js.map +1 -0
- package/lib/components/settings/result-definitions-modal/header.d.ts +3 -0
- package/lib/components/settings/result-definitions-modal/header.d.ts.map +1 -0
- package/lib/components/settings/result-definitions-modal/header.js +5 -0
- package/lib/components/settings/result-definitions-modal/header.js.map +1 -0
- package/lib/components/settings/result-definitions-modal/index.d.ts +7 -0
- package/lib/components/settings/result-definitions-modal/index.d.ts.map +1 -0
- package/lib/components/settings/result-definitions-modal/index.js +8 -0
- package/lib/components/settings/result-definitions-modal/index.js.map +1 -0
- package/lib/components/settings/result-definitions-modal/result-definitions-modal.module.less +17 -0
- package/lib/components/settings/result-definitions-modal/row.d.ts +14 -0
- package/lib/components/settings/result-definitions-modal/row.d.ts.map +1 -0
- package/lib/components/settings/result-definitions-modal/row.js +21 -0
- package/lib/components/settings/result-definitions-modal/row.js.map +1 -0
- package/lib/components/settings/settings-section/index.d.ts +1 -2
- package/lib/components/settings/settings-section/index.d.ts.map +1 -1
- package/lib/components/settings/settings-section/settings-section.stories.d.ts +9 -0
- package/lib/components/settings/settings-section/settings-section.stories.d.ts.map +1 -0
- package/lib/components/settings/settings-section/settings-section.stories.js +12 -0
- package/lib/components/settings/settings-section/settings-section.stories.js.map +1 -0
- package/package.json +2 -2
- package/src/components/settings/company-details/company-details-form.stories.tsx +1 -1
- package/src/components/settings/email-validation/email-validation.stories.tsx +89 -0
- package/src/components/settings/email-validation/index.tsx +267 -0
- package/src/components/settings/result-definitions-modal/header.tsx +27 -0
- package/src/components/settings/result-definitions-modal/index.tsx +127 -0
- package/src/components/settings/result-definitions-modal/result-definitions-modal.module.less +17 -0
- package/src/components/settings/result-definitions-modal/result-definitions-modal.module.less.d.ts +5 -0
- package/src/components/settings/result-definitions-modal/row.tsx +48 -0
- package/src/components/settings/settings-section/index.tsx +1 -1
- package/src/components/settings/settings-section/settings-section.stories.tsx +16 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import classnames from 'classnames';
|
|
2
|
+
import { action } from 'mobx';
|
|
3
|
+
import { FC, useCallback } from 'react';
|
|
4
|
+
import { observer, useLocalStore } from 'mobx-react';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
BodyText,
|
|
8
|
+
Button,
|
|
9
|
+
Card,
|
|
10
|
+
Container,
|
|
11
|
+
Form,
|
|
12
|
+
Grid,
|
|
13
|
+
Layout,
|
|
14
|
+
Link,
|
|
15
|
+
Stack,
|
|
16
|
+
Text,
|
|
17
|
+
Tooltip,
|
|
18
|
+
} from '@servicetitan/design-system';
|
|
19
|
+
import {
|
|
20
|
+
DateRangeColumnMenuFilter,
|
|
21
|
+
multiSelectColumnMenuFilter,
|
|
22
|
+
StandardColumnMenuFilter,
|
|
23
|
+
Table,
|
|
24
|
+
TableColumn,
|
|
25
|
+
TableState,
|
|
26
|
+
TableCellProps,
|
|
27
|
+
} from '@servicetitan/table';
|
|
28
|
+
|
|
29
|
+
import { ResultDefinitionsModal } from '../result-definitions-modal';
|
|
30
|
+
|
|
31
|
+
interface EmailValidationLocalStore {
|
|
32
|
+
open: boolean;
|
|
33
|
+
setOpen(state: boolean): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface EmailValidationTableRecord<T> {
|
|
37
|
+
id: number;
|
|
38
|
+
active: boolean;
|
|
39
|
+
createdOn: Date;
|
|
40
|
+
email?: string | undefined;
|
|
41
|
+
result: T;
|
|
42
|
+
dateAdded?: Date | undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface EmailValidationProps<
|
|
46
|
+
TEmailValidationType extends string = string,
|
|
47
|
+
TEmailValidationResult extends string = string
|
|
48
|
+
> {
|
|
49
|
+
className?: string;
|
|
50
|
+
loading: boolean;
|
|
51
|
+
resultGrid: TableState<EmailValidationTableRecord<TEmailValidationResult>>;
|
|
52
|
+
validationRiskType: TEmailValidationType;
|
|
53
|
+
handleDownload(): void;
|
|
54
|
+
setValidationRiskType(type: TEmailValidationType): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface EmailValidationHocOptions<
|
|
58
|
+
TEmailValidationType extends string = string,
|
|
59
|
+
TEmailValidationResult extends string = string
|
|
60
|
+
> {
|
|
61
|
+
resultToText: Map<TEmailValidationResult, string>;
|
|
62
|
+
levelTypeMap: {
|
|
63
|
+
high: TEmailValidationType;
|
|
64
|
+
medium: TEmailValidationType;
|
|
65
|
+
low: TEmailValidationType;
|
|
66
|
+
};
|
|
67
|
+
levelsResultMap: Map<TEmailValidationType, TEmailValidationResult[]>;
|
|
68
|
+
resultToTooltipText: Map<TEmailValidationResult, string>;
|
|
69
|
+
TenantDateCell: FC<TableCellProps>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function emailValidationHoc<
|
|
73
|
+
TEmailValidationType extends string = string,
|
|
74
|
+
TEmailValidationResult extends string = string
|
|
75
|
+
>({
|
|
76
|
+
resultToText,
|
|
77
|
+
levelTypeMap,
|
|
78
|
+
resultToTooltipText,
|
|
79
|
+
levelsResultMap,
|
|
80
|
+
TenantDateCell,
|
|
81
|
+
}: EmailValidationHocOptions<TEmailValidationType, TEmailValidationResult>) {
|
|
82
|
+
const ResultColumnMenuFilter = multiSelectColumnMenuFilter(
|
|
83
|
+
[...resultToText.keys()],
|
|
84
|
+
result => resultToText.get(result)!
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const ResultCell: FC<TableCellProps> = props => {
|
|
88
|
+
const { result } = props.dataItem as { result: TEmailValidationResult };
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<td className="qa-result-cell of-visible-i">
|
|
92
|
+
<Tooltip text={resultToTooltipText.get(result)} className="qa-result-cell-tooltip">
|
|
93
|
+
{resultToText.get(result)}
|
|
94
|
+
</Tooltip>
|
|
95
|
+
</td>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function EmailValidationImpl({
|
|
100
|
+
className,
|
|
101
|
+
handleDownload,
|
|
102
|
+
loading,
|
|
103
|
+
resultGrid,
|
|
104
|
+
setValidationRiskType,
|
|
105
|
+
validationRiskType,
|
|
106
|
+
}: EmailValidationProps<TEmailValidationType>) {
|
|
107
|
+
const localStore: EmailValidationLocalStore = useLocalStore(() => ({
|
|
108
|
+
open: false,
|
|
109
|
+
setOpen: action(function (this: EmailValidationLocalStore, state: boolean) {
|
|
110
|
+
this.open = state;
|
|
111
|
+
}),
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
const handleCheck = useCallback(
|
|
115
|
+
(riskType: TEmailValidationType) => () => {
|
|
116
|
+
setValidationRiskType(riskType);
|
|
117
|
+
},
|
|
118
|
+
[setValidationRiskType]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const handleClickLearnMore = () => {
|
|
122
|
+
localStore.setOpen(true);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleClose = () => {
|
|
126
|
+
localStore.setOpen(false);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const getResultsItemsForLevel = (level: TEmailValidationType) => {
|
|
130
|
+
return levelsResultMap.get(level)?.map(r => <div key={r}>{r}</div>);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<Container className={classnames(className, 'p-b-5 qa-email-validation')}>
|
|
135
|
+
<Stack justifyContent="space-between" alignItems="center" className="m-b-2">
|
|
136
|
+
<Text size={4} className="m-r-3">
|
|
137
|
+
Email Validation
|
|
138
|
+
</Text>
|
|
139
|
+
<Button
|
|
140
|
+
small
|
|
141
|
+
primary
|
|
142
|
+
onClick={handleDownload}
|
|
143
|
+
className="qa-email-validation-download-csv"
|
|
144
|
+
iconName="file_download"
|
|
145
|
+
iconPosition="left"
|
|
146
|
+
>
|
|
147
|
+
Download CSV
|
|
148
|
+
</Button>
|
|
149
|
+
</Stack>
|
|
150
|
+
<Layout type="support" direction="right">
|
|
151
|
+
<Layout.Section>
|
|
152
|
+
<Text size={2} className="m-b-4 c-neutral-90">
|
|
153
|
+
Email Validation acts like a filter that will block emails from going to
|
|
154
|
+
bad email addresses. You won't necessarily see these emails in your
|
|
155
|
+
suppression list, but it will effect your number “Sent”.
|
|
156
|
+
</Text>
|
|
157
|
+
</Layout.Section>
|
|
158
|
+
<Layout.Section />
|
|
159
|
+
</Layout>
|
|
160
|
+
<Grid relaxed>
|
|
161
|
+
<Grid.Column width={3}>
|
|
162
|
+
<Card>
|
|
163
|
+
<Card.Section light>
|
|
164
|
+
<Text bold size={3} className="m-b-1">
|
|
165
|
+
Set Your Preference
|
|
166
|
+
</Text>
|
|
167
|
+
<Text size={2} className="m-b-1">
|
|
168
|
+
Choose what level you want to enable for auto-suppression.
|
|
169
|
+
</Text>
|
|
170
|
+
<Link
|
|
171
|
+
primary
|
|
172
|
+
onClick={handleClickLearnMore}
|
|
173
|
+
className="qa-email-validation-learn-more"
|
|
174
|
+
>
|
|
175
|
+
Learn More
|
|
176
|
+
</Link>
|
|
177
|
+
</Card.Section>
|
|
178
|
+
<Card.Section>
|
|
179
|
+
<Form.Radio
|
|
180
|
+
label={
|
|
181
|
+
<div className="m-l-1">
|
|
182
|
+
<Text bold size={3} className="m-b-1">
|
|
183
|
+
High
|
|
184
|
+
<span className="c-neutral-90 fs-1 m-l-1">
|
|
185
|
+
(recommended)
|
|
186
|
+
</span>
|
|
187
|
+
</Text>
|
|
188
|
+
<BodyText size="small" className="c-neutral-90">
|
|
189
|
+
{getResultsItemsForLevel(levelTypeMap.high)}
|
|
190
|
+
</BodyText>
|
|
191
|
+
</div>
|
|
192
|
+
}
|
|
193
|
+
checked={validationRiskType === levelTypeMap.high}
|
|
194
|
+
onChange={handleCheck(levelTypeMap.high)}
|
|
195
|
+
className="qa-email-validation-high"
|
|
196
|
+
/>
|
|
197
|
+
</Card.Section>
|
|
198
|
+
<Card.Section>
|
|
199
|
+
<Form.Radio
|
|
200
|
+
label={
|
|
201
|
+
<div className="m-l-1">
|
|
202
|
+
<Text bold size={3} className="m-b-1">
|
|
203
|
+
Medium
|
|
204
|
+
</Text>
|
|
205
|
+
<BodyText size="small" className="c-neutral-90">
|
|
206
|
+
{getResultsItemsForLevel(levelTypeMap.medium)}
|
|
207
|
+
</BodyText>
|
|
208
|
+
</div>
|
|
209
|
+
}
|
|
210
|
+
checked={validationRiskType === levelTypeMap.medium}
|
|
211
|
+
onChange={handleCheck(levelTypeMap.medium)}
|
|
212
|
+
className="qa-email-validation-medium"
|
|
213
|
+
/>
|
|
214
|
+
</Card.Section>
|
|
215
|
+
<Card.Section>
|
|
216
|
+
<Form.Radio
|
|
217
|
+
label={
|
|
218
|
+
<div className="m-l-1">
|
|
219
|
+
<Text bold size={3} className="m-b-1">
|
|
220
|
+
Low
|
|
221
|
+
</Text>
|
|
222
|
+
<BodyText size="small" className="c-neutral-90">
|
|
223
|
+
{getResultsItemsForLevel(levelTypeMap.low)}
|
|
224
|
+
</BodyText>
|
|
225
|
+
</div>
|
|
226
|
+
}
|
|
227
|
+
checked={validationRiskType === levelTypeMap.low}
|
|
228
|
+
onChange={handleCheck(levelTypeMap.low)}
|
|
229
|
+
className="qa-email-validation-low"
|
|
230
|
+
/>
|
|
231
|
+
</Card.Section>
|
|
232
|
+
</Card>
|
|
233
|
+
</Grid.Column>
|
|
234
|
+
<Grid.Column width={9}>
|
|
235
|
+
<Table
|
|
236
|
+
tableState={resultGrid}
|
|
237
|
+
sortable
|
|
238
|
+
loading={loading}
|
|
239
|
+
className="qa-email-validation-table"
|
|
240
|
+
scrollable="none"
|
|
241
|
+
>
|
|
242
|
+
<TableColumn
|
|
243
|
+
field="email"
|
|
244
|
+
title="Email Address"
|
|
245
|
+
columnMenu={StandardColumnMenuFilter}
|
|
246
|
+
/>
|
|
247
|
+
<TableColumn
|
|
248
|
+
field="result"
|
|
249
|
+
title="Result"
|
|
250
|
+
cell={ResultCell}
|
|
251
|
+
columnMenu={ResultColumnMenuFilter}
|
|
252
|
+
/>
|
|
253
|
+
<TableColumn
|
|
254
|
+
field="dateAdded"
|
|
255
|
+
title="Date Added"
|
|
256
|
+
cell={TenantDateCell}
|
|
257
|
+
columnMenu={DateRangeColumnMenuFilter}
|
|
258
|
+
/>
|
|
259
|
+
</Table>
|
|
260
|
+
</Grid.Column>
|
|
261
|
+
</Grid>
|
|
262
|
+
{localStore.open && <ResultDefinitionsModal onClose={handleClose} />}
|
|
263
|
+
</Container>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
return observer(EmailValidationImpl);
|
|
267
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { Grid, Text } from '@servicetitan/design-system';
|
|
3
|
+
|
|
4
|
+
import * as Styles from './result-definitions-modal.module.less';
|
|
5
|
+
|
|
6
|
+
export const Header: FC = () => (
|
|
7
|
+
<div className="position-sticky position-t-0 z-popover">
|
|
8
|
+
<Grid columns={3} className="p-y-1 bg-white">
|
|
9
|
+
<Grid.Column>
|
|
10
|
+
<Text size={1} className="c-neutral-90 tt-uppercase">
|
|
11
|
+
Result
|
|
12
|
+
</Text>
|
|
13
|
+
</Grid.Column>
|
|
14
|
+
<Grid.Column>
|
|
15
|
+
<Text size={1} className="c-neutral-90 tt-uppercase">
|
|
16
|
+
Risk
|
|
17
|
+
</Text>
|
|
18
|
+
</Grid.Column>
|
|
19
|
+
<Grid.Column>
|
|
20
|
+
<Text size={1} className="c-neutral-90 tt-uppercase">
|
|
21
|
+
Recommendation
|
|
22
|
+
</Text>
|
|
23
|
+
</Grid.Column>
|
|
24
|
+
</Grid>
|
|
25
|
+
<hr className={Styles.divider} />
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import classnames from 'classnames';
|
|
3
|
+
import { Modal, Text } from '@servicetitan/design-system';
|
|
4
|
+
|
|
5
|
+
import { Header } from './header';
|
|
6
|
+
import { Row, RiskType } from './row';
|
|
7
|
+
|
|
8
|
+
import * as Styles from './result-definitions-modal.module.less';
|
|
9
|
+
|
|
10
|
+
interface ResultDefinitionsModalProps {
|
|
11
|
+
onClose(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ResultDefinitionsModal: FC<ResultDefinitionsModalProps> = ({ onClose }) => (
|
|
15
|
+
<Modal
|
|
16
|
+
open
|
|
17
|
+
closable
|
|
18
|
+
title="Email Validation Result Definitions"
|
|
19
|
+
onClose={onClose}
|
|
20
|
+
className={classnames(Styles.modal, 'qa-result-definitions-modal')}
|
|
21
|
+
>
|
|
22
|
+
<Header />
|
|
23
|
+
<div className={Styles.wall} />
|
|
24
|
+
<Row result="Undeliverable" risk={RiskType.VeryHigh} recommendation="Suppress" />
|
|
25
|
+
<hr className={Styles.divider} />
|
|
26
|
+
<Text size={2} className="m-y-2">
|
|
27
|
+
Address was determined invalid after performing multiple checks including MX, SMTP,
|
|
28
|
+
etc., likely due to:
|
|
29
|
+
<ul className="m-y-0">
|
|
30
|
+
<li>The receiving recipient domain is not configured to accept email</li>
|
|
31
|
+
<li>
|
|
32
|
+
The SMTP commands issued resulted in a hard bounce, i.e., "user does not exist"
|
|
33
|
+
or "mailbox full" errors
|
|
34
|
+
</li>
|
|
35
|
+
<li>
|
|
36
|
+
The recipient address historically hard-bounced or appeared on suppression lists
|
|
37
|
+
</li>
|
|
38
|
+
<li>
|
|
39
|
+
Mailbox providers require specific standard for an email address including
|
|
40
|
+
min/max number of characters, max number of periods, etc.
|
|
41
|
+
</li>
|
|
42
|
+
</ul>
|
|
43
|
+
</Text>
|
|
44
|
+
<hr className={Styles.divider} />
|
|
45
|
+
<Row result="Risky" risk={RiskType.Medium} recommendation="Proceed With Caution" />
|
|
46
|
+
<hr className={Styles.divider} />
|
|
47
|
+
<Text size={2} className="m-y-2">
|
|
48
|
+
Address was determined risky based on historical analysis or problematic results, likely
|
|
49
|
+
due to:
|
|
50
|
+
<ul className="m-y-0">
|
|
51
|
+
<li>
|
|
52
|
+
The receiving address may be valid, but was found on one or more suppression
|
|
53
|
+
lists Addresses matching a known spam trap are not ideal for sending campaigns
|
|
54
|
+
to and can impact reputation
|
|
55
|
+
</li>
|
|
56
|
+
</ul>
|
|
57
|
+
</Text>
|
|
58
|
+
<hr className={Styles.divider} />
|
|
59
|
+
<Row result="Typo" risk={RiskType.High} recommendation="Suppress" />
|
|
60
|
+
<hr className={Styles.divider} />
|
|
61
|
+
<Text size={2} className="m-y-2">
|
|
62
|
+
Address was possibly typed incorrectly during sign-up, or matches known typo domains,
|
|
63
|
+
likely due to:
|
|
64
|
+
<ul className="m-y-0">
|
|
65
|
+
<li>
|
|
66
|
+
Typos are mistyped address domains like "gmil.com" instead of "gmail.com," and
|
|
67
|
+
are often undeliverable. Address is checked against database of common domain
|
|
68
|
+
misspellings. These kinds of addresses are usually not good for sending.
|
|
69
|
+
</li>
|
|
70
|
+
</ul>
|
|
71
|
+
</Text>
|
|
72
|
+
<hr className={Styles.divider} />
|
|
73
|
+
<Row result="Accept-All" risk={RiskType.Medium} recommendation="Proceed With Caution" />
|
|
74
|
+
<hr className={Styles.divider} />
|
|
75
|
+
<Text size={2} className="m-y-2">
|
|
76
|
+
The receiving domain of the address accepts all mail, indicating it may be a spam trap,
|
|
77
|
+
likely due to:
|
|
78
|
+
<ul className="m-y-0">
|
|
79
|
+
<li>
|
|
80
|
+
Some domains do not distinguish between existing and non-existing addresses and
|
|
81
|
+
accept all mail
|
|
82
|
+
</li>
|
|
83
|
+
</ul>
|
|
84
|
+
</Text>
|
|
85
|
+
<hr className={Styles.divider} />
|
|
86
|
+
<Row result="Dispose" risk={RiskType.Medium} recommendation="Proceed With Caution" />
|
|
87
|
+
<hr className={Styles.divider} />
|
|
88
|
+
<Text size={2} className="m-y-2">
|
|
89
|
+
The receiving domain of the address accepts all mail, indicating it may be a spam trap,
|
|
90
|
+
likely due to:
|
|
91
|
+
<ul className="m-y-0">
|
|
92
|
+
<li>
|
|
93
|
+
Some domains do not distinguish between existing and non-existing addresses and
|
|
94
|
+
accept all mail
|
|
95
|
+
</li>
|
|
96
|
+
</ul>
|
|
97
|
+
</Text>
|
|
98
|
+
<hr className={Styles.divider} />
|
|
99
|
+
<Row result="Role" risk={RiskType.Medium} recommendation="Proceed With Caution" />
|
|
100
|
+
<hr className={Styles.divider} />
|
|
101
|
+
<Text size={2} className="m-y-2">
|
|
102
|
+
Address likely is not a particular person, but rather a group or department, likely due
|
|
103
|
+
to:
|
|
104
|
+
<ul className="m-y-0">
|
|
105
|
+
<li>
|
|
106
|
+
Role addresses belong to the end user and are deliverable, but include multiple
|
|
107
|
+
end recipients as part of a larger distribution list. These kinds of addreses
|
|
108
|
+
are usually not good for sending campaigns
|
|
109
|
+
</li>
|
|
110
|
+
</ul>
|
|
111
|
+
</Text>
|
|
112
|
+
<hr className={Styles.divider} />
|
|
113
|
+
<Row result="Unknown" risk={RiskType.Medium} recommendation="Proceed With Caution" />
|
|
114
|
+
<hr className={Styles.divider} />
|
|
115
|
+
<Text size={2} className="m-y-2">
|
|
116
|
+
A connection problem or unresponsive SMTP server prevented validation, likely due to:
|
|
117
|
+
<ul className="m-y-0">
|
|
118
|
+
<li>Unable to connect and resolve to service during SMTP connection</li>
|
|
119
|
+
<li>Unable to successfully get a response from SMTP server</li>
|
|
120
|
+
<li>
|
|
121
|
+
Address was outside known result definitions and criteria and was unable to be
|
|
122
|
+
classified
|
|
123
|
+
</li>
|
|
124
|
+
</ul>
|
|
125
|
+
</Text>
|
|
126
|
+
</Modal>
|
|
127
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@import (reference) '@servicetitan/tokens/dist/tokens.less';
|
|
2
|
+
|
|
3
|
+
.modal {
|
|
4
|
+
:global(.Modal__content) {
|
|
5
|
+
padding-top: @spacing-0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.divider {
|
|
9
|
+
margin: @spacing-1 @spacing-0;
|
|
10
|
+
border: 0;
|
|
11
|
+
border-bottom: 1px solid @color-neutral-60;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.wall {
|
|
15
|
+
height: 1px;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Grid, Text, Tag } from '@servicetitan/design-system';
|
|
4
|
+
import tokens from '@servicetitan/tokens';
|
|
5
|
+
|
|
6
|
+
export enum RiskType {
|
|
7
|
+
Medium,
|
|
8
|
+
High,
|
|
9
|
+
VeryHigh,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const riskTypeToColor = new Map<RiskType, string>([
|
|
13
|
+
[RiskType.Medium, 'warning'],
|
|
14
|
+
[RiskType.High, tokens.colorRed500],
|
|
15
|
+
[RiskType.VeryHigh, tokens.colorRed600],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const riskTypeToText = new Map<RiskType, string>([
|
|
19
|
+
[RiskType.Medium, 'Medium'],
|
|
20
|
+
[RiskType.High, 'High'],
|
|
21
|
+
[RiskType.VeryHigh, 'Very High'],
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
interface RowProps {
|
|
25
|
+
result: string;
|
|
26
|
+
risk: RiskType;
|
|
27
|
+
recommendation: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const Row: FC<RowProps> = ({ result, risk, recommendation }) => (
|
|
31
|
+
<Grid columns={3} className="m-y-1">
|
|
32
|
+
<Grid.Column>
|
|
33
|
+
<Text size={2} bold>
|
|
34
|
+
{result}
|
|
35
|
+
</Text>
|
|
36
|
+
</Grid.Column>
|
|
37
|
+
<Grid.Column>
|
|
38
|
+
<Tag color={riskTypeToColor.get(risk)} compact>
|
|
39
|
+
{riskTypeToText.get(risk)}
|
|
40
|
+
</Tag>
|
|
41
|
+
</Grid.Column>
|
|
42
|
+
<Grid.Column>
|
|
43
|
+
<Text size={2} bold>
|
|
44
|
+
{recommendation}
|
|
45
|
+
</Text>
|
|
46
|
+
</Grid.Column>
|
|
47
|
+
</Grid>
|
|
48
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Headline } from '@servicetitan/design-system';
|
|
2
|
+
import { SettingsSection as Component } from '.';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'MPA Components/settings/SettingsSection',
|
|
6
|
+
component: Component,
|
|
7
|
+
parameters: {},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const SettingsSection = () => {
|
|
11
|
+
return (
|
|
12
|
+
<Component title="Hey! The test">
|
|
13
|
+
<Headline>The body</Headline>
|
|
14
|
+
</Component>
|
|
15
|
+
);
|
|
16
|
+
};
|