Switch to being a website instead of a Chrome app.
Update our CNAME to be the more appropriate "prototype.useducky.com" when we
deploy.
Create a homepage and a non-onboarding page template. This mostly consisted of
setting up a header component and the associated styles, and then a logged-in
vs. guest flavor of said header, changing the links appropriately.
We also created a simple homepage that describes what Ducky is and does, and
gave a jumping-off point for it.
Stubbed out a basic links page, just to get an idea for what the homepage would
be like when a logged-in user navigated to the homepage (e.g., not the marketing
copy).
Updated our login page to _actually work_, and redirected it to the new URL for
the payment setup page.
Updated the payment page to actually create a subscription, and moved it from
/register/payment to just /payment.
Fixed a bug in our registration page that was looking for an invalid_form error
when it really meant an invalid_format error. Ooops. Also, updated it to point
to the new /payment endpoint instead of /register/payment.
Updated our router to use the new homepage, the new links page, and updated the
URL for the payment page.
Updated our button styles so they should all have the right font color, padding,
and border-radius, but could've potentially screwed something up. Oops.
Updated our backgrounds all over to have a transparent-y white background behind
the content, and a simple pattern for the rest of the body. Not sure how I feel
about it just yet, but I'm not going to keep futzing with it.
1 import app from 'ampersand-app'
2 import React from 'react'
3 import localLinks from 'local-links'
4 import LaddaButton from 'react-ladda'
5 import LaddaCSS from '../../node_modules/ladda/dist/ladda.min.css'
6 import HeroUnit from '../components/hero'
7 import ValidationError from '../components/validation-error'
8 import onboardingStyles from '../styles/onboarding.scss'
9 import flashStyles from '../styles/_flashes.scss'
11 export default React.createClass({
12 displayName: 'LoginPage',
13 mixins: [React.addons.LinkedStateMixin],
25 emailValidationOutputs: {
26 'missing': 'Oops! Gotta enter your email, so we know who you are.',
29 passphraseValidationOutputs: {
30 'missing': 'You didn’t enter a passphrase. You should do that.',
33 catchAllValidationOutputs: {
34 'act_of_god': 'Hm, something went wrong. Try again? Or let support know.',
35 'invalid_format': 'Uh oh, things went really wrong. Let support know you saw the dreaded invalid_format error!',
36 'access_denied': 'Oops, something’s gone awry. Let support know your client isn’t working.',
37 'invalid_value': 'Hm, those credentials seem wrong. Are you sure they’re right?',
40 validate (email, passphrase) {
42 if (!email || !email.length) {
43 errors.push({'error': 'missing', 'field': '/email'})
45 if (!passphrase || !passphrase.length) {
46 errors.push({'error': 'missing', 'field': '/passphrase'})
52 event.preventDefault()
53 const errors = this.validate(this.state.email, this.state.passphrase)
54 this.setState({clientErrors: errors, serverErrors: []})
55 return errors.length <= 0
59 event.preventDefault()
60 if (!this.validateForm()) {
63 app.me.login(this.state.email, this.state.passphrase)
67 event.preventDefault()
71 componentDidMount () {
72 app.me.on('request', (moc, xhr, options) => {
73 this.setState({active: true})
75 app.me.on('sync', (moc, xhr, options) => {
76 this.setState({active: false})
77 app.router.navigate('/payment')
79 app.me.on('error', (moc, xhr, options) => {
80 let state = {active: false}
82 if (xhr && xhr.response) {
83 resp = JSON.parse(xhr.response)
86 if (resp.errors && resp.errors.length) {
87 state.serverErrors = resp.errors
88 } else if (resp.error && resp.error == 'invalid_client') {
89 state.serverErrors = [{'error': 'access_denied'}]
90 } else if (resp.error && resp.error == 'invalid_grant') {
91 state.serverErrors = [{'error': 'invalid_value'}]
93 state.serverErrors = [{'error': 'act_of_god'}]
101 <div className='container'>
102 <HeroUnit title='Welcome Back'>We missed you.</HeroUnit>
103 <article className='onboarding login'>
104 <form onSubmit={this.login}>
106 <label htmlFor='emailLoginInput'>Email</label>
107 <input id='emailLoginInput' type='email' valueLink={this.linkState('email')} disabled={this.state.active} />
108 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} field='/email' outputs={this.emailValidationOutputs} />
110 <label htmlFor='passwordLoginInput'>Passphrase</label>
111 <input id='passwordLoginInput' type='password' valueLink={this.linkState('passphrase')} disabled={this.state.active} />
112 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} field='/passphrase' outputs={this.passphraseValidationOutputs} />
114 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} notFields={['/email', '/passphrase']} notHeaders={[]} notParams={[]} outputs={this.catchAllValidationOutputs} />
116 <div className='actionbuttons'>
117 <button onClick={this.onBackClick} disabled={this.state.active} type='button' className='ladda-button'>Back</button>
118 <LaddaButton style='expand-right' active={this.state.active}>
119 <button type='submit' disabled={this.state.active} className='primary'>Login</button>