aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/auth/SetupAssistant.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/auth/SetupAssistant.tsx')
-rw-r--r--src/components/auth/SetupAssistant.tsx337
1 files changed, 337 insertions, 0 deletions
diff --git a/src/components/auth/SetupAssistant.tsx b/src/components/auth/SetupAssistant.tsx
new file mode 100644
index 000000000..c5fb79919
--- /dev/null
+++ b/src/components/auth/SetupAssistant.tsx
@@ -0,0 +1,337 @@
1import { Component } from 'react';
2import { observer } from 'mobx-react';
3import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
4import withStyles, { WithStylesProps } from 'react-jss';
5import classnames from 'classnames';
6import Input from '../ui/input/index';
7import Button from '../ui/button';
8import Badge from '../ui/badge';
9import Modal from '../ui/Modal';
10import Infobox from '../ui/Infobox';
11import Appear from '../ui/effects/Appear';
12import globalMessages from '../../i18n/globalMessages';
13import { CDN_URL } from '../../config';
14import { H1, H2 } from '../ui/headline';
15
16const SLACK_ID = 'slack';
17
18const messages = defineMessages({
19 headline: {
20 id: 'setupAssistant.headline',
21 defaultMessage: "Let's get started",
22 },
23 subHeadline: {
24 id: 'setupAssistant.subheadline',
25 defaultMessage:
26 'Choose from our most used services and get back on top of your messaging now.',
27 },
28 submitButtonLabel: {
29 id: 'setupAssistant.submit.label',
30 defaultMessage: "Let's go",
31 },
32 skipButtonLabel: {
33 id: 'setupAssistant.skip.label',
34 defaultMessage: 'Skip',
35 },
36 inviteSuccessInfo: {
37 id: 'invite.successInfo',
38 defaultMessage: 'Invitations sent successfully',
39 },
40});
41
42const transition =
43 window && window.matchMedia('(prefers-reduced-motion: no-preference)')
44 ? 'all 0.25s'
45 : 'none';
46
47const styles = theme => ({
48 root: {
49 width: '500px !important',
50 textAlign: 'center',
51 padding: 20,
52
53 '& h1': {},
54 },
55 servicesGrid: {
56 display: 'flex',
57 flexWrap: 'wrap',
58 justifyContent: 'space-between',
59 },
60 serviceContainer: {
61 background: theme.colorBackground,
62 position: 'relative',
63 width: '32%',
64 display: 'flex',
65 alignItems: 'center',
66 flexDirection: 'column',
67 justifyContent: 'center',
68 padding: 20,
69 borderRadius: theme.borderRadius,
70 marginBottom: 10,
71 opacity: 0.5,
72 transition,
73 border: [3, 'solid', 'transparent'],
74
75 '& h2': {
76 margin: [10, 0, 0],
77 color: theme.colorText,
78 },
79
80 '&:hover': {
81 border: [3, 'solid', theme.brandPrimary],
82 '& $serviceIcon': {},
83 },
84 },
85 selected: {
86 border: [3, 'solid', theme.brandPrimary],
87 background: `${theme.brandPrimary}47`,
88 opacity: 1,
89 },
90 serviceIcon: {
91 width: 50,
92 transition,
93 },
94
95 slackModalContent: {
96 textAlign: 'center',
97
98 '& img': {
99 width: 50,
100 marginBottom: 20,
101 },
102 },
103 modalActionContainer: {
104 display: 'flex',
105 flexDirection: 'column',
106 justifyContent: 'center',
107 alignItems: 'center',
108 },
109 ctaCancel: {
110 background: 'none !important',
111 },
112 slackBadge: {
113 position: 'absolute',
114 bottom: 4,
115 height: 'auto',
116 padding: '0px 4px',
117 borderRadius: theme.borderRadiusSmall,
118 margin: 0,
119 display: 'flex',
120 overflow: 'hidden',
121 },
122 clearSlackWorkspace: {
123 background: theme.inputPrefixColor,
124 marginLeft: 5,
125 height: '100%',
126 color: theme.colorText,
127 display: 'inline-flex',
128 justifyContent: 'center',
129 alignItems: 'center',
130 marginRight: -4,
131 padding: [0, 5],
132 },
133});
134
135interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps {
136 onSubmit: (...args: any[]) => void;
137 isInviteSuccessful?: boolean;
138 services: any; // TODO - [TS DEBT] check legacy services type
139 isSettingUpServices: boolean;
140}
141
142interface IState {
143 services: any[];
144 isSlackModalOpen: boolean;
145 slackWorkspace: string;
146 showSuccessInfo: boolean;
147}
148
149@observer
150class SetupAssistant extends Component<IProps, IState> {
151 constructor(props: IProps) {
152 super(props);
153
154 this.state = {
155 services: [],
156 isSlackModalOpen: false,
157 showSuccessInfo: false,
158 slackWorkspace: '',
159 };
160 }
161
162 slackWorkspaceHandler(): void {
163 const { slackWorkspace = '', services } = this.state;
164 const sanitizedWorkspace = slackWorkspace
165 .trim()
166 .replace(/^https?:\/\//, '');
167
168 if (sanitizedWorkspace) {
169 const index = services.findIndex(s => s.id === SLACK_ID);
170 if (index === -1) {
171 const newServices = services;
172 newServices.push({ id: SLACK_ID, team: sanitizedWorkspace });
173 this.setState({ services: newServices });
174 }
175 }
176
177 this.setState({
178 isSlackModalOpen: false,
179 slackWorkspace: sanitizedWorkspace,
180 });
181 }
182
183 render() {
184 const {
185 classes,
186 isInviteSuccessful = false,
187 onSubmit,
188 services,
189 isSettingUpServices,
190 intl,
191 } = this.props;
192 const {
193 isSlackModalOpen,
194 slackWorkspace,
195 services: addedServices,
196 } = this.state;
197
198 return (
199 <div className={`auth__container ${classes.root}`}>
200 {this.state.showSuccessInfo && isInviteSuccessful && (
201 <Appear>
202 <Infobox
203 type="success"
204 icon="checkbox-marked-circle-outline"
205 dismissible
206 >
207 {intl.formatMessage(messages.inviteSuccessInfo)}
208 </Infobox>
209 </Appear>
210 )}
211
212 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
213 <H1>{intl.formatMessage(messages.headline)}</H1>
214 <H2>{intl.formatMessage(messages.subHeadline)}</H2>
215 <div className={classnames('grid', classes.servicesGrid)}>
216 {Object.keys(services).map(id => {
217 const service = services[id];
218 return (
219 <button
220 className={classnames({
221 [classes.serviceContainer]: true,
222 [classes.selected]:
223 this.state.services.findIndex(s => s.id === id) !== -1,
224 })}
225 key={id}
226 onClick={() => {
227 const index = this.state.services.findIndex(s => s.id === id);
228 if (index === -1) {
229 if (id === SLACK_ID) {
230 this.setState({ isSlackModalOpen: true });
231 } else {
232 addedServices.push({ id });
233 }
234 } else {
235 addedServices.splice(index, 1);
236 if (id === SLACK_ID) {
237 this.setState({
238 slackWorkspace: '',
239 });
240 }
241 }
242
243 this.setState({ services: addedServices });
244 }}
245 type="button"
246 >
247 <img
248 src={`${CDN_URL}/recipes/dist/${id}/src/icon.svg`}
249 className={classes.serviceIcon}
250 alt=""
251 />
252 <H2>{service.name}</H2>
253 {id === SLACK_ID && slackWorkspace && (
254 <Badge type="secondary" className={classes.slackBadge}>
255 {slackWorkspace}
256 <button
257 type="button"
258 className={classes.clearSlackWorkspace}
259 onClick={() => {
260 this.setState({
261 slackWorkspace: '',
262 });
263 }}
264 >
265 x
266 </button>
267 </Badge>
268 )}
269 </button>
270 );
271 })}
272 </div>
273 <Modal
274 isOpen={isSlackModalOpen}
275 // isBlocking={false}
276 close={() => this.setState({ isSlackModalOpen: false })}
277 >
278 <div className={classes.slackModalContent}>
279 <img src={`${CDN_URL}/recipes/dist/slack/src/icon.svg`} alt="" />
280 <H1>Create your first Slack workspace</H1>
281 <form
282 onSubmit={e => {
283 e.preventDefault();
284 this.slackWorkspaceHandler();
285 }}
286 >
287 <Input
288 suffix=".slack.com"
289 placeholder="workspace-url"
290 onChange={e =>
291 this.setState({ slackWorkspace: e.target.value })
292 }
293 value={slackWorkspace}
294 />
295 <div className={classes.modalActionContainer}>
296 <Button
297 type="submit"
298 label={intl.formatMessage(globalMessages.save)}
299 />
300 <Button
301 type="button"
302 buttonType="secondary"
303 label={intl.formatMessage(globalMessages.cancel)}
304 className={classes.ctaCancel}
305 onClick={e => {
306 e.preventDefault();
307 this.setState({ slackWorkspace: '' });
308 }}
309 />
310 </div>
311 </form>
312 </div>
313 </Modal>
314 <Button
315 type="button"
316 className="auth__button"
317 // disabled={!atLeastOneEmailAddress}
318 label={intl.formatMessage(messages.submitButtonLabel)}
319 onClick={() => onSubmit(this.state.services)}
320 busy={isSettingUpServices}
321 disabled={isSettingUpServices || addedServices.length === 0}
322 />
323 <Button
324 type="button"
325 className="auth__button auth__button--skip"
326 label={intl.formatMessage(messages.skipButtonLabel)}
327 onClick={() => onSubmit([])}
328 buttonType="secondary"
329 />
330 </div>
331 );
332 }
333}
334
335export default injectIntl(
336 withStyles(styles, { injectTheme: true })(SetupAssistant),
337);