diff --git a/src/assets/images/ajax-loader.gif b/src/assets/images/ajax-loader.gif
deleted file mode 100644
index c35c951..0000000
Binary files a/src/assets/images/ajax-loader.gif and /dev/null differ
diff --git a/src/config.js b/src/config.js
index 43576a3..84d5d3b 100644
--- a/src/config.js
+++ b/src/config.js
@@ -6,11 +6,11 @@ var development = {
}
var production = {
- stripe: {
- publicKey: "pk_live_xrnwdLNXbt20LMxpIDffJnnC"
- }
+ stripe: {
+ publicKey: "pk_live_xrnwdLNXbt20LMxpIDffJnnC"
+ }
}
-module.exports = function() {
+module.exports = function () {
return process.env.ENV == 'development' ? development : production;
}
diff --git a/src/js/components/inviteForm.js b/src/js/components/inviteForm.js
index 768b235..07317ea 100644
--- a/src/js/components/inviteForm.js
+++ b/src/js/components/inviteForm.js
@@ -5,7 +5,7 @@ var React = require('react');
var classSet = require('classnames');
var api = require('../api');
-
+var Loader = require('./loader');
module.exports = React.createClass({
getInitialState() {
return {
@@ -99,11 +99,11 @@ module.exports = React.createClass({
disabled={this.state.error || this.state.submitted}>
⏎
-
-
+
+
+
{feedbackMessage}
- )
+ );
}
});
diff --git a/src/js/components/loader.js b/src/js/components/loader.js
new file mode 100644
index 0000000..80f58c2
--- /dev/null
+++ b/src/js/components/loader.js
@@ -0,0 +1,12 @@
+const React = require('react');
+
+module.exports = function Loader() {
+ return (
+
+ );
+};
diff --git a/src/js/components/membershipForm.js b/src/js/components/membershipForm.js
index d40287c..ceb30be 100644
--- a/src/js/components/membershipForm.js
+++ b/src/js/components/membershipForm.js
@@ -1,50 +1,34 @@
-'use strict';
-
-var classSet = require('classnames');
-var React = require('react');
-var request = require('axios');
-
-var MembershipInfoForm = require('./membershipInfoForm.js');
-var StripeCheckout = require('./stripeCheckout.js');
+import React from 'react';
+import MembershipInfoForm from './membershipInfoForm';
module.exports = React.createClass({
getInitialState() {
return {
- infoFormSuccess: false,
- paymentSuccess: false,
- userInfo: null
+ paymentSuccess: false
};
},
-
handlePaymentSuccess() {
this.setState({ paymentSuccess: true });
},
-
- handleInfoFormSuccess(userInfo) {
- this.setState({
- infoFormSuccess: true,
- userInfo: userInfo
- });
- },
-
render() {
- if(!this.state.infoFormSuccess) {
- return
-
- } else if (!this.state.paymentSuccess) {
+ if(!this.state.paymentSuccess) {
return (
-
- )
-
- } else {
- return (
-
-
Maksu ja rekisteröityminen onnistui.
-
Tervetuloa Koodiklinikka ry:n jäseneksi!
-
- )
+
+ );
}
+ return (
+
+
+
Maksu ja rekisteröityminen onnistui.
+
Tervetuloa Koodiklinikka ry:n jäseneksi!
+
+ );
}
});
diff --git a/src/js/components/membershipInfoForm.js b/src/js/components/membershipInfoForm.js
index 2363ec3..f9774a4 100644
--- a/src/js/components/membershipInfoForm.js
+++ b/src/js/components/membershipInfoForm.js
@@ -1,21 +1,23 @@
'use strict';
-var _ = require('lodash');
-var request = require('axios');
-var React = require('react');
+var _ = require('lodash');
+var request = require('axios');
+var React = require('react');
var classSet = require('classnames');
+var StripeCheckout = require('react-stripe-checkout').default;
-var api = require('../api');
-var StripeCheckout = require('./stripeCheckout.js');
+var api = require('../api');
+var Loader = require('./loader');
+var config = require('../../config.js')();
var fieldNameTranslations = {
- address: { fi: "Osoite" },
- city: { fi: "Paikkakunta" },
- email: { fi: "Sähköpostiosoite" },
- handle: { fi: "Slack-käyttäjätunnus "},
- name: { fi: "Koko nimi "},
- postcode: { fi: "Postinumero" }
-}
+ address: { fi: 'Osoite' },
+ city: { fi: 'Paikkakunta' },
+ email: { fi: 'Sähköpostiosoite' },
+ handle: { fi: 'Slack-käyttäjätunnus ' },
+ name: { fi: 'Koko nimi ' },
+ postcode: { fi: 'Postinumero' }
+};
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
@@ -24,54 +26,51 @@ function validateEmail(email) {
const fieldNames = ['name', 'email', 'handle', 'address', 'city', 'postcode'];
+function getUserInfo(state) {
+ return _.pick(state, fieldNames);
+}
+
module.exports = React.createClass({
getInitialState() {
return {
- address: '',
- city: '',
- email: '',
- errors: [],
- handle: '',
- name: '',
- postcode: '',
- sending: false
+ address: '',
+ city: '',
+ email: '',
+ handle: '',
+ name: '',
+ postcode: '',
+ sending: false,
+ pristineFields: fieldNames
};
},
- onSubmit(e) {
- e.preventDefault();
-
+ onSubmit(token) {
this.setState({
sending: true,
- errors: []
+ error: null
});
- if (this.getDataErrors().length) {
- this.setState({
- sending: false,
- errors: userInfoErrors
+ request.post(api('membership'), {
+ userInfo: getUserInfo(this.state),
+ stripeToken: token.id
+ })
+ .then(() => {
+ this.setState({ sending: false });
+ this.props.onPaymentSuccess();
+ })
+ .catch((err) => {
+ this.setState({ error: err, sending: false });
});
- } else {
- this.props.onSuccess({
- email: this.state.email,
- name: this.state.name,
- handle: this.state.handle,
- address: this.state.address,
- postcode: this.state.postcode,
- city: this.state.city,
- });
- }
- },
- handleError(err) {
- this.setState({ error: err, sending: false });
},
onChange(e) {
- if (e.target.value === this.state[e.target.name]) {
+ var name = e.target.name;
+ if (e.target.value === this.state[name]) {
return;
}
this.setState({
[e.target.name]: e.target.value,
+ pristineFields: this.state.pristineFields.filter((fieldName) => fieldName !== name),
errors: []
});
},
@@ -80,84 +79,117 @@ module.exports = React.createClass({
var foundErrors = [];
fieldNames.forEach((fieldName) => {
- if(!this.state[fieldName])
- foundErrors.push({ field: fieldName, type: 'missing' })
- })
+ if (!this.state[fieldName]) {
+ foundErrors.push({ field: fieldName, type: 'missing' });
+ }
+ });
- if(this.state.email && !validateEmail(this.state.email))
+ if (this.state.email && !validateEmail(this.state.email)) {
foundErrors.push({ field: 'email', type: 'invalid' });
+ }
return foundErrors;
},
render() {
+ const inputErrors = this.getDataErrors();
+
var formClasses = classSet({
- 'form': true,
+ 'form': true,
'membership-form': true,
- 'has-error': this.state.errors.length,
- 'sending': this.state.sending
+ 'has-error': inputErrors.length !== 0 || this.state.error,
+ 'sending': this.state.sending
});
-
- /* generate error messages */
- var feedbackMessages = [];
- var fieldsWithErrors = [];
-
- this.state.errors.forEach((err, i) => {
+ function getErrorMessage(err) {
var feedbackText;
- fieldsWithErrors.push(err.field);
-
- if(err.type == 'missing') {
- feedbackText = `${ fieldNameTranslations[err.field].fi } on pakollinen.`
- } else if (err.type == 'invalid') {
- feedbackText = `${ fieldNameTranslations[err.field].fi } on virheellinen.`
+ if (err.type === 'missing') {
+ feedbackText = `${fieldNameTranslations[err.field].fi} on pakollinen.`;
+ } else if (err.type === 'invalid') {
+ feedbackText = `${fieldNameTranslations[err.field].fi} on virheellinen.`;
}
- feedbackMessages.push(({ feedbackText }
))
- });
+ return {feedbackText}
;
+ }
+ /* generate error messages */
+ var visibleErrors = inputErrors
+ .filter((error) => this.state.pristineFields.indexOf(error.field) === -1);
- /* generate input fields */
- var inputFields = [];
+ var fieldsWithErrors = visibleErrors.map(({ field }) => field);
- fieldNames.forEach((fieldName) => {
+ var inputFields = fieldNames.map((fieldName) => {
var inputClasses = classSet({
'input': true,
'has-error': _.includes(fieldsWithErrors, fieldName),
- 'half': fieldName == 'city' || fieldName == 'postcode',
- 'left': fieldName == 'city'
+ 'half': fieldName === 'city' || fieldName === 'postcode',
+ 'left': fieldName === 'city'
});
- inputFields.push((
-
- ))
- })
+ function showsErrorFor(field) {
+ if (fieldName === 'city') {
+ return false;
+ }
+ return field === fieldName || fieldName === 'postcode' && field === 'city';
+ }
+
+ return (
+
+
+ {
+ visibleErrors
+ .filter(({ field }) => showsErrorFor(field))
+ .map(getErrorMessage)
+
+ }
+
+ );
+ });
+ if (this.state.sending) {
+ return (
+
+
+
+ );
+ }
return (
- )
+ );
}
});
diff --git a/src/js/components/stripeCheckout.js b/src/js/components/stripeCheckout.js
deleted file mode 100644
index 98da3fb..0000000
--- a/src/js/components/stripeCheckout.js
+++ /dev/null
@@ -1,66 +0,0 @@
-'use strict';
-
-var request = require('axios');
-var React = require('react');
-var classSet = require('classnames');
-import StripeCheckout from 'react-stripe-checkout';
-
-var api = require('../api');
-var config = require('../../config.js')();
-
-module.exports = React.createClass({
- getInitialState() {
- return {
- error: null,
- sending: false
- };
- },
-
- onSubmit(token) {
- this.setState({
- error: null,
- sending: true
- });
-
- request.post(api('membership'), {
- userInfo: this.props.userInfo,
- stripeToken: token.id
- })
- .then(() => {
- this.setState({
- sending: false
- });
- this.props.onPaymentSuccess();
- })
- .catch((e) => {
- this.setState({
- error: e,
- sending: false
- });
- });
- },
-
- render() {
- if (this.state.error) {
- return Virhe maksaessa! Ota yhteyttä info@koodiklinikka.fi
- } else if (this.state.sending) {
- return
- } else {
- return (
-
- )
- }
- }
-});
diff --git a/src/styles/_header.styl b/src/styles/_header.styl
index baddeb7..db74ade 100644
--- a/src/styles/_header.styl
+++ b/src/styles/_header.styl
@@ -35,13 +35,33 @@ headerHeight = 400px
top 20px
a
color white
- font-size 18px
+ font-size 16px
+ font-weight bold
margin-left 16px
text-shadow 0 2px 0 rgba(0,0,0,0.1)
- @media screen and (max-width: 1030px)
+ position relative
+ @media screen and (max-width: 420px)
display block
margin-top 5px
+ &:last-child
+ margin-left 1.5em
+ &:before
+ content '!'
+ font-size 11px
+ line-height 15px
+ width 14px
+ height 14px
+ text-align center
+ background #ec3d3d
+ display inline-block
+ margin-right 5px
+ top -5px
+ right -15px
+ border-radius 100%
+ position absolute
+ text-shadow none
+
.header__headline
display table-cell
padding 0 1em
diff --git a/src/styles/_loader.styl b/src/styles/_loader.styl
new file mode 100644
index 0000000..71eb8f5
--- /dev/null
+++ b/src/styles/_loader.styl
@@ -0,0 +1,55 @@
+.sk-folding-cube
+ margin auto
+ width 100%
+ height 100%
+ position relative
+ transform rotateZ(45deg)
+
+.sk-folding-cube .sk-cube
+ float left
+ width 50%
+ height 50%
+ position relative
+ transform scale(1.1)
+
+.sk-folding-cube .sk-cube:before
+ content ''
+ position absolute
+ top 0
+ left 0
+ width 100%
+ height 100%
+ background-color linkColor
+ animation sk-foldCubeAngle 2.4s infinite linear both
+ transform-origin 100% 100%
+
+.sk-folding-cube .sk-cube2
+ transform scale(1.1) rotateZ(90deg)
+
+.sk-folding-cube .sk-cube3
+ transform scale(1.1) rotateZ(180deg)
+
+.sk-folding-cube .sk-cube4
+ transform scale(1.1) rotateZ(270deg)
+
+.sk-folding-cube .sk-cube2:before
+ animation-delay 0.3s
+
+.sk-folding-cube .sk-cube3:before
+ animation-delay 0.6s
+
+.sk-folding-cube .sk-cube4:before
+ animation-delay 0.9s
+
+@keyframes sk-foldCubeAngle
+ 0%, 10%
+ transform perspective(140px) rotateX(-180deg)
+ opacity 0
+ 25%, 75%
+ transform perspective(140px) rotateX(0deg)
+ opacity 1
+ 90%, 100%
+ transform perspective(140px) rotateY(180deg)
+ opacity 0
+
+
diff --git a/src/styles/style.styl b/src/styles/style.styl
index d3a67f0..8538de8 100644
--- a/src/styles/style.styl
+++ b/src/styles/style.styl
@@ -6,6 +6,7 @@ footerHeight = 50px
@require '_input'
@require '_button'
@require '_header'
+@require '_loader'
body, html
margin 0
@@ -112,28 +113,16 @@ section:first-child
&:first-child
margin-top 0
-.loader
- background transparent url('../images/ajax-loader.gif') no-repeat center center
- width 28px
- height 28px
-
.form
.btn
background linkColor
border-bottom 2px solid #117280
color rgba(255, 255, 255, 0.9)
- .loader
- display none
&.sending
.btn
display none
- .loader
+ .invite-form__loader
display block
- margin-left auto
- margin-right auto
- width 28px
- height 28px
-
.invite-form
position relative
.btn
@@ -146,15 +135,13 @@ section:first-child
color rgba(255, 255, 255, 0.5)
&:active
border-bottom 0
- .loader
+ .invite-form__loader
+ display none
+ width 20px
+ height 20px
position absolute
- right 9px
- top 9px
- &.sending
- .loader
- display block
- width 28px
- height 28px
+ right 14px
+ top 14px
.membership-form
.input
@@ -162,6 +149,11 @@ section:first-child
.btn
margin-top 12px
+.membership-form__loader
+ width 70px
+ height 70px
+ margin auto
+
.stripe-form
margin 20px 0px
.name
@@ -218,7 +210,7 @@ footer
justify-content space-between
flex-wrap wrap
text-align center
- @media screen and (max-width: 760px)
+ @media screen and (max-width: 940px)
display block
i
margin 0 0.30em
@@ -230,7 +222,7 @@ footer
display flex
flex-direction column
justify-content center
- @media screen and (max-width: 760px)
+ @media screen and (max-width: 940px)
margin-top 1em
.sponsors
@@ -251,16 +243,17 @@ footer
height 40px
margin-right 1em
vertical-align middle
- @media screen and (max-width: 760px)
+ @media screen and (max-width: 940px)
margin-top 1em
-
.sponsor__futurice, .sponsor__metosin, .sponsor__leonidas
height 30px
.sponsor__nordea
height 25px
- margin-top: -3px
+ margin-top -3px
+ @media screen and (max-width: 940px)
+ margin-top 1em
.feed
width feedWidth
@@ -353,11 +346,12 @@ footer
.bread-img
background url('../images/hp3_bw.jpg')
- background-size 120%
- background-position-y 80%
- border-radius 0px
+ background-size cover
+ border-radius 160px
opacity 0.85
+ width 320px
height 320px
+ margin auto
@media screen and (max-width: 700px)
.bread-img
diff --git a/src/yhdistys.jade b/src/yhdistys.jade
index bc097a9..f7e441a 100644
--- a/src/yhdistys.jade
+++ b/src/yhdistys.jade
@@ -32,7 +32,6 @@ block content
.row
.bread
.column.column1-2
- h3 Liity jäseneksi
#membership-form.form
.column.column1-2