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