@servicetitan/mpa-components 0.1.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 (141) hide show
  1. package/lib/components/settings/company-details/company-details-form.stories.d.ts +9 -0
  2. package/lib/components/settings/company-details/company-details-form.stories.d.ts.map +1 -0
  3. package/lib/components/settings/company-details/company-details-form.stories.js +45 -0
  4. package/lib/components/settings/company-details/company-details-form.stories.js.map +1 -0
  5. package/lib/components/settings/company-details/index.d.ts +20 -0
  6. package/lib/components/settings/company-details/index.d.ts.map +1 -0
  7. package/lib/components/settings/company-details/index.js +11 -0
  8. package/lib/components/settings/company-details/index.js.map +1 -0
  9. package/lib/components/settings/company-email-footer/company-email-footer.stories.d.ts +9 -0
  10. package/lib/components/settings/company-email-footer/company-email-footer.stories.d.ts.map +1 -0
  11. package/lib/components/settings/company-email-footer/company-email-footer.stories.js +38 -0
  12. package/lib/components/settings/company-email-footer/company-email-footer.stories.js.map +1 -0
  13. package/lib/components/settings/company-email-footer/index.d.ts +11 -0
  14. package/lib/components/settings/company-email-footer/index.d.ts.map +1 -0
  15. package/lib/components/settings/company-email-footer/index.js +10 -0
  16. package/lib/components/settings/company-email-footer/index.js.map +1 -0
  17. package/lib/components/settings/company-email-reply-to/company-email-reply-to.stories.d.ts +9 -0
  18. package/lib/components/settings/company-email-reply-to/company-email-reply-to.stories.d.ts.map +1 -0
  19. package/lib/components/settings/company-email-reply-to/company-email-reply-to.stories.js +36 -0
  20. package/lib/components/settings/company-email-reply-to/company-email-reply-to.stories.js.map +1 -0
  21. package/lib/components/settings/company-email-reply-to/index.d.ts +7 -0
  22. package/lib/components/settings/company-email-reply-to/index.d.ts.map +1 -0
  23. package/lib/components/settings/company-email-reply-to/index.js +12 -0
  24. package/lib/components/settings/company-email-reply-to/index.js.map +1 -0
  25. package/lib/components/settings/company-email-sender/company-email-sender.module.less +7 -0
  26. package/lib/components/settings/company-email-sender/company-email-sender.stories.d.ts +9 -0
  27. package/lib/components/settings/company-email-sender/company-email-sender.stories.d.ts.map +1 -0
  28. package/lib/components/settings/company-email-sender/company-email-sender.stories.js +38 -0
  29. package/lib/components/settings/company-email-sender/company-email-sender.stories.js.map +1 -0
  30. package/lib/components/settings/company-email-sender/index.d.ts +12 -0
  31. package/lib/components/settings/company-email-sender/index.d.ts.map +1 -0
  32. package/lib/components/settings/company-email-sender/index.js +29 -0
  33. package/lib/components/settings/company-email-sender/index.js.map +1 -0
  34. package/lib/components/settings/company-trade-checkbox/company-trade-checkbox.module.less +13 -0
  35. package/lib/components/settings/company-trade-checkbox/index.d.ts +10 -0
  36. package/lib/components/settings/company-trade-checkbox/index.d.ts.map +1 -0
  37. package/lib/components/settings/company-trade-checkbox/index.js +14 -0
  38. package/lib/components/settings/company-trade-checkbox/index.js.map +1 -0
  39. package/lib/components/settings/company-trades-picker/company-trades-picker.stories.d.ts +10 -0
  40. package/lib/components/settings/company-trades-picker/company-trades-picker.stories.d.ts.map +1 -0
  41. package/lib/components/settings/company-trades-picker/company-trades-picker.stories.js +68 -0
  42. package/lib/components/settings/company-trades-picker/company-trades-picker.stories.js.map +1 -0
  43. package/lib/components/settings/company-trades-picker/index.d.ts +19 -0
  44. package/lib/components/settings/company-trades-picker/index.d.ts.map +1 -0
  45. package/lib/components/settings/company-trades-picker/index.js +16 -0
  46. package/lib/components/settings/company-trades-picker/index.js.map +1 -0
  47. package/lib/components/settings/double-opt-in/double-opt-in.module.less +3 -0
  48. package/lib/components/settings/double-opt-in/double-opt-in.stories.d.ts +9 -0
  49. package/lib/components/settings/double-opt-in/double-opt-in.stories.d.ts.map +1 -0
  50. package/lib/components/settings/double-opt-in/double-opt-in.stories.js +42 -0
  51. package/lib/components/settings/double-opt-in/double-opt-in.stories.js.map +1 -0
  52. package/lib/components/settings/double-opt-in/index.d.ts +16 -0
  53. package/lib/components/settings/double-opt-in/index.d.ts.map +1 -0
  54. package/lib/components/settings/double-opt-in/index.js +23 -0
  55. package/lib/components/settings/double-opt-in/index.js.map +1 -0
  56. package/lib/components/settings/email-preview/email-preview.d.ts +13 -0
  57. package/lib/components/settings/email-preview/email-preview.d.ts.map +1 -0
  58. package/lib/components/settings/email-preview/email-preview.js +9 -0
  59. package/lib/components/settings/email-preview/email-preview.js.map +1 -0
  60. package/lib/components/settings/email-preview/email-preview.module.less +62 -0
  61. package/lib/components/settings/email-preview/opt-in-email-preview.d.ts +10 -0
  62. package/lib/components/settings/email-preview/opt-in-email-preview.d.ts.map +1 -0
  63. package/lib/components/settings/email-preview/opt-in-email-preview.js +10 -0
  64. package/lib/components/settings/email-preview/opt-in-email-preview.js.map +1 -0
  65. package/lib/components/settings/email-preview/opt-out-email-preview.d.ts +9 -0
  66. package/lib/components/settings/email-preview/opt-out-email-preview.d.ts.map +1 -0
  67. package/lib/components/settings/email-preview/opt-out-email-preview.js +9 -0
  68. package/lib/components/settings/email-preview/opt-out-email-preview.js.map +1 -0
  69. package/lib/components/settings/index.d.ts +10 -0
  70. package/lib/components/settings/index.d.ts.map +1 -0
  71. package/lib/components/settings/index.js +10 -0
  72. package/lib/components/settings/index.js.map +1 -0
  73. package/lib/components/settings/logo-picker/index.d.ts +32 -0
  74. package/lib/components/settings/logo-picker/index.d.ts.map +1 -0
  75. package/lib/components/settings/logo-picker/index.js +88 -0
  76. package/lib/components/settings/logo-picker/index.js.map +1 -0
  77. package/lib/components/settings/logo-picker/logo-picker.module.less +21 -0
  78. package/lib/components/settings/logo-picker/logo-picker.stories.d.ts +9 -0
  79. package/lib/components/settings/logo-picker/logo-picker.stories.d.ts.map +1 -0
  80. package/lib/components/settings/logo-picker/logo-picker.stories.js +13 -0
  81. package/lib/components/settings/logo-picker/logo-picker.stories.js.map +1 -0
  82. package/lib/components/settings/opt-out-message/index.d.ts +17 -0
  83. package/lib/components/settings/opt-out-message/index.d.ts.map +1 -0
  84. package/lib/components/settings/opt-out-message/index.js +22 -0
  85. package/lib/components/settings/opt-out-message/index.js.map +1 -0
  86. package/lib/components/settings/opt-out-message/opt-out-message.module.less +20 -0
  87. package/lib/components/settings/opt-out-message/opt-out-message.stories.d.ts +9 -0
  88. package/lib/components/settings/opt-out-message/opt-out-message.stories.d.ts.map +1 -0
  89. package/lib/components/settings/opt-out-message/opt-out-message.stories.js +44 -0
  90. package/lib/components/settings/opt-out-message/opt-out-message.stories.js.map +1 -0
  91. package/lib/components/settings/settings-section/index.d.ts +11 -0
  92. package/lib/components/settings/settings-section/index.d.ts.map +1 -0
  93. package/lib/components/settings/settings-section/index.js +6 -0
  94. package/lib/components/settings/settings-section/index.js.map +1 -0
  95. package/lib/index.d.ts +3 -0
  96. package/lib/index.d.ts.map +1 -0
  97. package/lib/index.js +3 -0
  98. package/lib/index.js.map +1 -0
  99. package/lib/utils/helpers.d.ts +7 -0
  100. package/lib/utils/helpers.d.ts.map +1 -0
  101. package/lib/utils/helpers.js +24 -0
  102. package/lib/utils/helpers.js.map +1 -0
  103. package/package.json +36 -0
  104. package/src/components/settings/company-details/company-details-form.stories.tsx +39 -0
  105. package/src/components/settings/company-details/index.tsx +157 -0
  106. package/src/components/settings/company-email-footer/company-email-footer.stories.tsx +26 -0
  107. package/src/components/settings/company-email-footer/index.tsx +79 -0
  108. package/src/components/settings/company-email-reply-to/company-email-reply-to.stories.tsx +23 -0
  109. package/src/components/settings/company-email-reply-to/index.tsx +38 -0
  110. package/src/components/settings/company-email-sender/company-email-sender.module.less +7 -0
  111. package/src/components/settings/company-email-sender/company-email-sender.module.less.d.ts +3 -0
  112. package/src/components/settings/company-email-sender/company-email-sender.stories.tsx +26 -0
  113. package/src/components/settings/company-email-sender/index.tsx +129 -0
  114. package/src/components/settings/company-trade-checkbox/company-trade-checkbox.module.less +13 -0
  115. package/src/components/settings/company-trade-checkbox/company-trade-checkbox.module.less.d.ts +4 -0
  116. package/src/components/settings/company-trade-checkbox/index.tsx +43 -0
  117. package/src/components/settings/company-trades-picker/company-trades-picker.stories.tsx +78 -0
  118. package/src/components/settings/company-trades-picker/index.tsx +78 -0
  119. package/src/components/settings/double-opt-in/double-opt-in.module.less +3 -0
  120. package/src/components/settings/double-opt-in/double-opt-in.module.less.d.ts +3 -0
  121. package/src/components/settings/double-opt-in/double-opt-in.stories.tsx +28 -0
  122. package/src/components/settings/double-opt-in/index.tsx +143 -0
  123. package/src/components/settings/email-preview/email-preview.module.less +62 -0
  124. package/src/components/settings/email-preview/email-preview.module.less.d.ts +8 -0
  125. package/src/components/settings/email-preview/email-preview.tsx +69 -0
  126. package/src/components/settings/email-preview/opt-in-email-preview.tsx +30 -0
  127. package/src/components/settings/email-preview/opt-out-email-preview.tsx +31 -0
  128. package/src/components/settings/index.ts +9 -0
  129. package/src/components/settings/logo-picker/index.tsx +256 -0
  130. package/src/components/settings/logo-picker/logo-picker.module.less +21 -0
  131. package/src/components/settings/logo-picker/logo-picker.module.less.d.ts +4 -0
  132. package/src/components/settings/logo-picker/logo-picker.stories.tsx +21 -0
  133. package/src/components/settings/opt-out-message/index.tsx +154 -0
  134. package/src/components/settings/opt-out-message/opt-out-message.module.less +20 -0
  135. package/src/components/settings/opt-out-message/opt-out-message.module.less.d.ts +5 -0
  136. package/src/components/settings/opt-out-message/opt-out-message.stories.tsx +31 -0
  137. package/src/components/settings/settings-section/index.tsx +35 -0
  138. package/src/index.ts +3 -0
  139. package/src/utils/helpers.ts +31 -0
  140. package/tsconfig.json +11 -0
  141. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,129 @@
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
+ );
@@ -0,0 +1,13 @@
1
+ @import (reference) '~@servicetitan/tokens/dist/tokens.less';
2
+
3
+ .card {
4
+ position: relative;
5
+
6
+ .checkbox {
7
+ position: absolute;
8
+ top: -@spacing-1;
9
+ left: -@spacing-1;
10
+ color: @color-blue;
11
+ background-color: @color-white;
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ export const __esModule: true;
2
+ export const card: string;
3
+ export const checkbox: string;
4
+
@@ -0,0 +1,43 @@
1
+ import { useCallback } from 'react';
2
+ import classnames from 'classnames';
3
+
4
+ import { Card, Icon } from '@servicetitan/design-system';
5
+
6
+ import * as Styles from './company-trade-checkbox.module.less';
7
+
8
+ export interface CompanyTradeCheckboxProps<TradesType extends string = string> {
9
+ label?: JSX.Element | string;
10
+ value?: TradesType;
11
+ className?: string;
12
+ active?: boolean;
13
+ onClick?(item?: TradesType): void;
14
+ }
15
+
16
+ export function CompanyTradeCheckbox({
17
+ label,
18
+ value,
19
+ className,
20
+ active,
21
+ onClick,
22
+ }: CompanyTradeCheckboxProps) {
23
+ const handleClick = useCallback(() => {
24
+ if (onClick) {
25
+ onClick(value);
26
+ }
27
+ }, [onClick, value]);
28
+
29
+ return (
30
+ <Card
31
+ onClick={handleClick}
32
+ active={active}
33
+ className={classnames(Styles.card, className)}
34
+ sharp
35
+ thin
36
+ >
37
+ <Card.Section>
38
+ {active && <Icon name="check_circle" className={Styles.checkbox} size={24} />}
39
+ {label}
40
+ </Card.Section>
41
+ </Card>
42
+ );
43
+ }
@@ -0,0 +1,78 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { CompanyTradesPicker as Component, Trade } from '.';
3
+
4
+ export default {
5
+ title: 'MPA Components/settings/CompanyTradesPicker',
6
+ component: Component,
7
+ parameters: {},
8
+ };
9
+
10
+ enum TradeType {
11
+ first = 'FIRST',
12
+ second = 'SECOND',
13
+ third = 'THIRD',
14
+ fourth = 'FOURTH',
15
+ }
16
+
17
+ export function CompanyTradesPicker() {
18
+ const [trades, setTrades] = useState<Trade<TradeType>[]>([
19
+ {
20
+ qaKey: 'first',
21
+ name: 'first',
22
+ value: TradeType.first,
23
+ active: true,
24
+ asset: {
25
+ main: 'https://place-hold.it/64x64',
26
+ x2: 'https://place-hold.it/128x128',
27
+ x3: 'https://place-hold.it/192x192',
28
+ },
29
+ },
30
+ {
31
+ qaKey: 'second',
32
+ name: 'second',
33
+ value: TradeType.second,
34
+ active: false,
35
+ asset: {
36
+ main: 'https://place-hold.it/64x64',
37
+ x2: 'https://place-hold.it/128x128',
38
+ x3: 'https://place-hold.it/192x192',
39
+ },
40
+ },
41
+ {
42
+ qaKey: 'third',
43
+ name: 'third',
44
+ value: TradeType.third,
45
+ active: false,
46
+ asset: {
47
+ main: 'https://place-hold.it/64x64',
48
+ x2: 'https://place-hold.it/128x128',
49
+ x3: 'https://place-hold.it/192x192',
50
+ },
51
+ },
52
+ {
53
+ qaKey: 'fourth',
54
+ name: 'fourth',
55
+ value: TradeType.fourth,
56
+ active: false,
57
+ asset: {
58
+ main: 'https://place-hold.it/64x64',
59
+ x2: 'https://place-hold.it/128x128',
60
+ x3: 'https://place-hold.it/192x192',
61
+ },
62
+ },
63
+ ]);
64
+
65
+ const selectTrade = useCallback(
66
+ (t: TradeType) => {
67
+ setTrades(
68
+ trades.map(trade => ({
69
+ ...trade,
70
+ active: trade.value === t ? !trade.active : trade.active,
71
+ }))
72
+ );
73
+ },
74
+ [trades]
75
+ );
76
+
77
+ return <Component<TradeType> trades={trades} onTradeChange={selectTrade} />;
78
+ }
@@ -0,0 +1,78 @@
1
+ import { useMemo } from 'react';
2
+ import chunk from 'lodash/chunk';
3
+
4
+ import { Grid, Text } from '@servicetitan/design-system';
5
+
6
+ import { SettingsSection } from '../settings-section';
7
+ import { CompanyTradeCheckbox } from '../company-trade-checkbox';
8
+
9
+ export interface Trade<TradeType extends string> {
10
+ qaKey?: string;
11
+ name: string;
12
+ value: TradeType;
13
+ active: boolean;
14
+ asset: {
15
+ main: string;
16
+ x2: string;
17
+ x3: string;
18
+ alt?: string;
19
+ };
20
+ }
21
+
22
+ export interface CompanyTradesPickerProps<TradeType extends string = string> {
23
+ trades: Trade<TradeType>[];
24
+ onTradeChange(t?: TradeType): void;
25
+ }
26
+
27
+ export function CompanyTradesPicker<T extends string = string>({
28
+ trades,
29
+ onTradeChange,
30
+ }: CompanyTradesPickerProps<T>) {
31
+ const rows = useMemo(
32
+ () => chunk(trades, 4).map((row, idx) => ({ row, key: `row=${idx}` })),
33
+ [trades]
34
+ );
35
+
36
+ return (
37
+ <SettingsSection
38
+ qaPrefix="qa-settings-select-your-trades"
39
+ title="Select Your Trades"
40
+ text="Choose which trades describe your business so we can give you relevant content and merge tags."
41
+ >
42
+ <Grid columns={4}>
43
+ {rows.map(({ row, key }) => {
44
+ return (
45
+ <Grid.Row key={key} stretched className="p-0">
46
+ {row.map(trade => {
47
+ return (
48
+ <Grid.Column key={trade.value}>
49
+ <CompanyTradeCheckbox
50
+ className={`qa-settings-select-your-trades-${
51
+ trade.qaKey ?? trade.value
52
+ }`}
53
+ active={trade.active}
54
+ onClick={onTradeChange}
55
+ value={trade.value}
56
+ label={
57
+ <div className="ta-center">
58
+ <img
59
+ src={trade.asset.main}
60
+ srcSet={`${trade.asset.x3} 3x, ${trade.asset.x2} 2x`}
61
+ alt={trade.asset.alt ?? ''}
62
+ />
63
+ <Text bold className="m-t-2" size={2}>
64
+ {trade.name}
65
+ </Text>
66
+ </div>
67
+ }
68
+ />
69
+ </Grid.Column>
70
+ );
71
+ })}
72
+ </Grid.Row>
73
+ );
74
+ })}
75
+ </Grid>
76
+ </SettingsSection>
77
+ );
78
+ }
@@ -0,0 +1,3 @@
1
+ .button-input {
2
+ width: 160px;
3
+ }
@@ -0,0 +1,3 @@
1
+ export const __esModule: true;
2
+ export const buttonInput: string;
3
+
@@ -0,0 +1,28 @@
1
+ import { CheckboxFieldState, InputFieldState } from '@servicetitan/form';
2
+ import { injectable, provide, useDependencies } from '@servicetitan/react-ioc';
3
+ import { FormState } from 'formstate';
4
+ import { DoubleOptIn as Component } from '.';
5
+
6
+ export default {
7
+ title: 'MPA Components/settings/DoubleOptIn',
8
+ component: Component,
9
+ parameters: {},
10
+ };
11
+
12
+ @injectable()
13
+ class DoubleOptInStore {
14
+ form = new FormState({
15
+ emailSubject: new InputFieldState('Jump in'),
16
+ emailHeader: new InputFieldState('Right now'),
17
+ emailBody: new InputFieldState('Jump in right now! Please!'),
18
+ emailButtonText: new InputFieldState('JUMP IN'),
19
+ enabled: new CheckboxFieldState(true),
20
+ });
21
+ }
22
+
23
+ export const DoubleOptIn = provide({
24
+ singletons: [DoubleOptInStore],
25
+ })(() => {
26
+ const [store] = useDependencies(DoubleOptInStore);
27
+ return <Component formState={store.form.$} />;
28
+ });
@@ -0,0 +1,143 @@
1
+ import { FC, Fragment, useCallback, useState } from 'react';
2
+ import { observer } from 'mobx-react';
3
+ import classnames from 'classnames';
4
+
5
+ import { CheckboxFieldState, InputFieldState } from '@servicetitan/form';
6
+ import { Form, Text, ToggleSwitch, Button } from '@servicetitan/design-system';
7
+ import { useConfirm } from '@servicetitan/confirm';
8
+
9
+ import { SettingsSection } from '../settings-section';
10
+ import { OptInEmailPreview } from '../email-preview/opt-in-email-preview';
11
+
12
+ import * as Styles from './double-opt-in.module.less';
13
+
14
+ export const footerText =
15
+ 'If you’ve received this email by mistake, no action is needed, just delete this message.';
16
+
17
+ export interface DoubleOptInPropsFormState {
18
+ emailSubject: InputFieldState<string>;
19
+ emailHeader: InputFieldState<string>;
20
+ emailBody: InputFieldState<string>;
21
+ emailButtonText: InputFieldState<string>;
22
+ enabled: CheckboxFieldState;
23
+ }
24
+ export interface DoubleOptInProps {
25
+ formState: DoubleOptInPropsFormState;
26
+ onHandleClickEnable?(checkd: boolean): void;
27
+ }
28
+
29
+ export const DoubleOptIn: FC<DoubleOptInProps> = observer(({ onHandleClickEnable, formState }) => {
30
+ const { emailSubject, emailHeader, emailBody, emailButtonText, enabled } = formState;
31
+ const [isPreviewOpen, setIsPreviewOpen] = useState(false);
32
+ const openPreview = useCallback(() => setIsPreviewOpen(true), [setIsPreviewOpen]);
33
+ const closePreview = useCallback(() => setIsPreviewOpen(false), [setIsPreviewOpen]);
34
+
35
+ const handleClickEnable = useCallback(
36
+ (_0: any, checked: boolean) => {
37
+ onHandleClickEnable?.(checked);
38
+ enabled.onChange(checked);
39
+ },
40
+ [enabled, onHandleClickEnable]
41
+ );
42
+
43
+ const [Confirm, handleConfirmed] = useConfirm(handleClickEnable);
44
+
45
+ return (
46
+ <SettingsSection
47
+ qaPrefix="qa-double-opt-in"
48
+ title="Double Opt-In"
49
+ text={
50
+ <Fragment>
51
+ <div>
52
+ Double opt-in ensures that your emails will go to SPAM at a much lower rate.
53
+ The opt-in confirmation happens after a job has been completed.
54
+ </div>
55
+ <ToggleSwitch
56
+ label="Enable"
57
+ onChange={handleConfirmed}
58
+ checked={enabled.value}
59
+ className="m-t-2 qa-double-opt-in-enable"
60
+ />
61
+ </Fragment>
62
+ }
63
+ >
64
+ <Text bold className="m-b-2">
65
+ Double Opt-In Email Content
66
+ </Text>
67
+ <Form className="m-b-0-i">
68
+ <Form.Input
69
+ className="m-b-2-i qa-double-opt-in-subject-line"
70
+ value={emailSubject.value}
71
+ onChange={emailSubject.onChangeHandler}
72
+ error={emailSubject.hasError}
73
+ label="Subject Line"
74
+ fluid
75
+ disabled={!enabled.value}
76
+ />
77
+ <Form.Input
78
+ className="m-b-2-i qa-double-opt-in-header"
79
+ value={emailHeader.value}
80
+ onChange={emailHeader.onChangeHandler}
81
+ error={emailHeader.hasError}
82
+ label="Header"
83
+ fluid
84
+ disabled={!enabled.value}
85
+ />
86
+ <Form.TextArea
87
+ className="m-b-2-i qa-double-opt-in-body-copy"
88
+ label="Body Copy"
89
+ value={emailBody.value}
90
+ onChange={emailBody.onChangeHandler}
91
+ error={emailBody.hasError}
92
+ rows={2}
93
+ disabled={!enabled.value}
94
+ />
95
+ <Form.Input
96
+ className={classnames(
97
+ Styles.buttonInput,
98
+ 'm-b-2-i qa-double-opt-in-button-text'
99
+ )}
100
+ value={emailButtonText.value}
101
+ onChange={emailButtonText.onChangeHandler}
102
+ error={emailButtonText.hasError}
103
+ label="Button Text"
104
+ disabled={!enabled.value}
105
+ />
106
+ <Form.Field
107
+ className="m-b-3-i"
108
+ label={
109
+ <Fragment>
110
+ Footer&nbsp;
111
+ <Text inline size={2} subdued>
112
+ (automatically added)
113
+ </Text>
114
+ </Fragment>
115
+ }
116
+ >
117
+ <Text>{footerText}</Text>
118
+ </Form.Field>
119
+ <Button
120
+ className="qa-double-opt-in-preview-email"
121
+ primary
122
+ onClick={openPreview}
123
+ outline
124
+ small
125
+ disabled={!enabled.value}
126
+ >
127
+ Preview Email
128
+ </Button>
129
+ </Form>
130
+ <Confirm
131
+ title="Disable Double Opt-In"
132
+ message="Disabling double opt-in could potentially lead to worse email performance and higher spam rates. Are you sure?"
133
+ when={enabled.value}
134
+ />
135
+ <OptInEmailPreview
136
+ {...formState}
137
+ open={isPreviewOpen}
138
+ footerText={footerText}
139
+ onClose={closePreview}
140
+ />
141
+ </SettingsSection>
142
+ );
143
+ });
@@ -0,0 +1,62 @@
1
+ @import (reference) '@servicetitan/tokens/dist/tokens.less';
2
+
3
+ .preview {
4
+ max-width: 600px;
5
+ margin: @spacing-0 auto;
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: center;
9
+ padding: @spacing-2 @spacing-0;
10
+ min-height: 400px;
11
+ max-height: 70vh;
12
+
13
+ .card {
14
+ background: @color-neutral-10;
15
+ border: 1px solid @color-neutral-60;
16
+ border-radius: @border-radius-2;
17
+ padding: @spacing-5;
18
+ width: 100%;
19
+ box-sizing: border-box;
20
+
21
+ .title {
22
+ font-family: Arial, sans-serif;
23
+ font-weight: bold;
24
+ font-size: 30px;
25
+ line-height: 34px;
26
+ color: @color-neutral-400;
27
+ margin-bottom: @spacing-0;
28
+ }
29
+
30
+ .text {
31
+ font-family: Helvetica, sans-serif;
32
+ font-size: @typescale-4;
33
+ line-height: 23px;
34
+ color: @color-neutral-200;
35
+ margin-top: 20px;
36
+ margin-bottom: @spacing-0;
37
+ }
38
+
39
+ .button {
40
+ background: @color-blue-400;
41
+ border-radius: 4px;
42
+ font-family: Helvetica, sans-serif;
43
+ font-size: @typescale-4;
44
+ line-height: 23px;
45
+ text-align: center;
46
+ color: @color-neutral-0;
47
+ padding: 13px @spacing-2;
48
+ cursor: pointer;
49
+ margin-top: 30px;
50
+ display: inline-block;
51
+ }
52
+
53
+ .footer {
54
+ font-family: Helvetica, sans-serif;
55
+ font-size: @typescale-3;
56
+ line-height: 18px;
57
+ color: @color-neutral-200;
58
+ margin-top: 30px;
59
+ margin-bottom: @spacing-0;
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,8 @@
1
+ export const __esModule: true;
2
+ export const preview: string;
3
+ export const card: string;
4
+ export const title: string;
5
+ export const text: string;
6
+ export const button: string;
7
+ export const footer: string;
8
+
@@ -0,0 +1,69 @@
1
+ import { FC } from 'react';
2
+ import classnames from 'classnames';
3
+
4
+ import { Modal, ModalProps } from '@servicetitan/design-system';
5
+
6
+ import * as Styles from './email-preview.module.less';
7
+
8
+ interface EmailPreviewProps {
9
+ title: string;
10
+ header?: string;
11
+ body?: string;
12
+ buttonText?: string;
13
+ footer?: string;
14
+ onClose: ModalProps['onClose'];
15
+ }
16
+
17
+ export const EmailPreview: FC<EmailPreviewProps> = ({
18
+ title,
19
+ header,
20
+ body,
21
+ buttonText,
22
+ footer,
23
+ onClose,
24
+ }) => {
25
+ const isCardEmpty = !header && !body && !buttonText;
26
+
27
+ return (
28
+ <Modal
29
+ open
30
+ size={Modal.Sizes.M}
31
+ closable
32
+ title={title}
33
+ onClose={onClose}
34
+ className="qa-email-preview"
35
+ >
36
+ <div className={Styles.preview}>
37
+ {!isCardEmpty && (
38
+ <div className={Styles.card}>
39
+ {header && (
40
+ <h1 className={classnames(Styles.title, 'qa-email-preview-header')}>
41
+ {header}
42
+ </h1>
43
+ )}
44
+ {body && (
45
+ <p className={classnames(Styles.text, 'qa-email-preview-body-copy')}>
46
+ {body}
47
+ </p>
48
+ )}
49
+ {buttonText && (
50
+ <a
51
+ className={classnames(
52
+ Styles.button,
53
+ 'qa-email-preview-button-text'
54
+ )}
55
+ >
56
+ {buttonText}
57
+ </a>
58
+ )}
59
+ {footer && (
60
+ <p className={classnames(Styles.footer, 'qa-email-preview-footer')}>
61
+ {footer}
62
+ </p>
63
+ )}
64
+ </div>
65
+ )}
66
+ </div>
67
+ </Modal>
68
+ );
69
+ };