diff --git a/package.json b/package.json index 516dcad..efc0db8 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "scripts": { "start": "rm -rf public && gulp", "build": "rm -rf public && gulp build", + "dev": "SERVER=http://localhost:9000/ ENV=development npm start", + "prod": "ENV=production npm start", "lint": "eslint src", "test": "mocha src/**/__tests__/*.js --compilers js:babel-core/register --require test/test-helper" }, @@ -22,8 +24,10 @@ "http-server": "^0.8.0", "lodash": "^3.9.1", "parse-github-event": "^0.2.0", + "prop-types": "^15.5.10", "react": "^0.14.3", "react-dom": "^0.14.3", + "react-stripe-checkout": "^2.4.0", "timeago": "^0.2.0", "twitter-text": "^1.11.0" }, 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/assets/images/hp3_bw.jpg b/src/assets/images/hp3_bw.jpg new file mode 100644 index 0000000..c8ba56b Binary files /dev/null and b/src/assets/images/hp3_bw.jpg differ diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..84d5d3b --- /dev/null +++ b/src/config.js @@ -0,0 +1,16 @@ + +var development = { + stripe: { + publicKey: "pk_test_OmNve9H1OuORlmD4rblpjgzh" + } +} + +var production = { + stripe: { + publicKey: "pk_live_xrnwdLNXbt20LMxpIDffJnnC" + } +} + +module.exports = function () { + return process.env.ENV == 'development' ? development : production; +} diff --git a/src/index.jade b/src/index.jade index 665b339..831583f 100644 --- a/src/index.jade +++ b/src/index.jade @@ -1,150 +1,79 @@ -doctype html -html - head - title Koodiklinikka - // inject:css - // endinject - meta(name='description', content='Koodiklinikka on suomalainen Slack-yhteisö ohjelmistoalan harrastajille ja ammattilaisille.') - meta(name='keywords', content='ohjelmointi,frontend,open source,devaus,suomi,javascript,clojure,go,java,node.js,io.js,angular.js,web') - meta(charset='utf-8') - meta(name='viewport', content='width=device-width, initial-scale=1') - meta(name='apple-mobile-web-app-capable', content='yes') +extends templates/head - script. - if(location.hostname === 'koodiklinikka.fi' && location.protocol !== 'https:') { - location.protocol = 'https'; - } - link(rel='apple-touch-icon', sizes='57x57', href='icons/apple-touch-icon-57x57.png') - link(rel='apple-touch-icon', sizes='114x114', href='icons/apple-touch-icon-114x114.png') - link(rel='apple-touch-icon', sizes='72x72', href='icons/apple-touch-icon-72x72.png') - link(rel='apple-touch-icon', sizes='144x144', href='icons/apple-touch-icon-144x144.png') - link(rel='apple-touch-icon', sizes='60x60', href='icons/apple-touch-icon-60x60.png') - link(rel='apple-touch-icon', sizes='120x120', href='icons/apple-touch-icon-120x120.png') - link(rel='apple-touch-icon', sizes='76x76', href='icons/apple-touch-icon-76x76.png') - link(rel='apple-touch-icon', sizes='152x152', href='icons/apple-touch-icon-152x152.png') - link(rel='apple-touch-icon', sizes='180x180', href='icons/apple-touch-icon-180x180.png') - link(rel='icon', type='image/png', href='icons/favicon-192x192.png', sizes='192x192') - link(rel='icon', type='image/png', href='icons/favicon-160x160.png', sizes='160x160') - link(rel='icon', type='image/png', href='icons/favicon-96x96.png', sizes='96x96') - link(rel='icon', type='image/png', href='icons/favicon-16x16.png', sizes='16x16') - link(rel='icon', type='image/png', href='icons/favicon-32x32.png', sizes='32x32') - link(rel='shortcut icon', href='icons/favicon.ico') - link(rel='icon', href='icons/favicon.ico') - meta(name='msapplication-TileColor', content='#10558c') - meta(name='msapplication-TileImage', content='icons/mstile-144x144.png') - meta(property='og:image', content='images/logo.png') - script(src='//use.typekit.net/scb5xny.js') - script. - try{Typekit.load();}catch(e){}; +block title + | Koodiklinikka - body - .site - .container - .header - video(autoplay, loop, poster='images/poster.jpg', class='header__video-bg') - source(src='videos/jumbo.mp4', type='video/mp4') - .header__container - .header__headline - .header__logo - h1.header__title Slack-yhteisö kaikille ohjelmoinnista ja ohjelmistoalasta kiinnostuneille harrastajille ja ammattilaisille. +block header_content + video(autoplay, loop, poster='images/poster.jpg', class='header__video-bg') + source(src='videos/jumbo.mp4', type='video/mp4') + .header__container + .header__nav + a(href='/') etusivu + a(href='/yhdistys.html') yhdistys + .header__headline + .header__logo + h1.header__title Slack-yhteisö kaikille ohjelmoinnista ja ohjelmistoalasta kiinnostuneille harrastajille ja ammattilaisille. - .content - section - .row - h3 Tule mukaan - #invite-form +block content + .content.with-feed + section + .row + h3 Tule mukaan + #invite-form.form - section - .row - .bread - .column.column1-2 - h3 Yhteisö ohjelmoinnista kiinnostuneille - p. - Koodiklinikka on Suomen suurin ohjelmistoalan yhteisö, joka kokoaa työntekijät, harrastajat ja vasta-alkajat yhteen. - Tarkoituksenamme on yhdistää ja kasvattaa suomalaista ohjelmointiyhteisöä, sekä tarjota apua ja uusia kontakteja ohjelmoinnista innostuneille nuorille. - p. - Mukaan liittyminen on ilmaista ja helppoa. Jätä sähköpostiosoitteesi ylläolevaan kenttään ja lähetämme sinulle kutsun Slack-yhteisöömme. - .column.column1-2 - a(href='images/slack.png', target='_blank') - img(src='images/slack.png') + section + .row + .bread + .column.column1-2 + h3 Yhteisö ohjelmoinnista kiinnostuneille + p. + Koodiklinikka on Suomen suurin ohjelmistoalan yhteisö, joka kokoaa työntekijät, harrastajat ja vasta-alkajat yhteen. + Tarkoituksenamme on yhdistää ja kasvattaa suomalaista ohjelmointiyhteisöä, sekä tarjota apua ja uusia kontakteja ohjelmoinnista innostuneille nuorille. + p. + Mukaan liittyminen on ilmaista ja helppoa. Jätä sähköpostiosoitteesi ylläolevaan kenttään ja lähetämme sinulle kutsun Slack-yhteisöömme. + .column.column1-2 + a(href='images/slack.png', target='_blank') + img(src='images/slack.png') - .row - .bread - .column.column2-5 - img(src='images/octocat.png') + .row + .bread + .column.column2-5 + img(src='images/octocat.png') - .column.column3-5 - h3 Avoin lähdekoodi - p - |Suosimme avointa lähdekoodia ja kaikki käyttämämme koodi on vapaasti saatavilla ja hyödynnettävissä Github-organisaatiomme sivulta. - |Organisaation jäseneksi otamme kaikki Slack-yhteisömme jäsenet ja kontribuutio projekteihimme otetaan lämpimästi vastaan. + .column.column3-5 + h3 Avoin lähdekoodi + p + |Suosimme avointa lähdekoodia ja kaikki käyttämämme koodi on vapaasti saatavilla ja hyödynnettävissä Github-organisaatiomme sivulta. + |Organisaation jäseneksi otamme kaikki Slack-yhteisömme jäsenet ja kontribuutio projekteihimme otetaan lämpimästi vastaan. - #members + #members - .row - h2 Potilaiden projekteja - .bread - .column.column2-5 - a(href='https://redom.js.org', target='_blank') - img(src='images/redom.svg') + .row + h2 Potilaiden projekteja + .bread + .column.column2-5 + a(href='https://redom.js.org', target='_blank') + img(src='images/redom.svg') - .column.column3-5 - h4 RE:DOM + .column.column3-5 + h4 RE:DOM - p. - Tiny (2 KB) turboboosted JavaScript library for creating user interfaces. - Develop web apps with 100 % JavaScript and web standards. + p. + Tiny (2 KB) turboboosted JavaScript library for creating user interfaces. + Develop web apps with 100 % JavaScript and web standards. - .bread - .column.column2-5 - a(href='https://codestats.net/', target='_blank') - img.project-image__codestats(src='images/codestats.png') + .bread + .column.column2-5 + a(href='https://codestats.net/', target='_blank') + img.project-image__codestats(src='images/codestats.png') - .column.column3-5 - h4 Code::Stats + .column.column3-5 + h4 Code::Stats - p. - Code::Stats is a free stats tracking service for programmers + p. + Code::Stats is a free stats tracking service for programmers - #members + #members - #feed - - - footer - .sponsors - .sponsors__label Yhteistyössä - a(href='http://futurice.com/', target='_blank') - img.sponsor.sponsor__futurice(src='images/futurice.svg') - a(href='http://www.metosin.fi/', target='_blank') - img.sponsor.sponsor__metosin(src='images/metosin.svg') - a(href='https://www.solita.fi/', target='_blank') - img.sponsor(src='images/solita.svg') - a(href='http://leonidasoy.fi/', target='_blank') - img.sponsor.sponsor__leonidas(src='images/leonidas.png') - a(href='https://www.nordea.com/', target='_blank') - img.sponsor.sponsor__nordea(src='images/nordea.png') - - .contacts - div - a(href='https://koodiklinikka.slack.com') - i.fa.fa-slack - - a(href='https://github.com/koodiklinikka/koodiklinikka.fi') - i.fa.fa-github - - a(href='https://twitter.com/koodiklinikka') - i.fa.fa-twitter - - a(href='https://www.linkedin.com/groups/12025476') - i.fa.fa-linkedin - - a(href='https://www.facebook.com/koodiklinikka') - i.fa.fa-facebook - div#email - - - #fader - // inject:js - // endinject + #feed diff --git a/src/js/components/inviteForm.js b/src/js/components/inviteForm.js index 357419c..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 { @@ -48,6 +48,7 @@ module.exports = React.createClass({ }, render() { var formClasses = classSet({ + 'form': true, 'invite-form': true, 'has-success': this.state.submitted, 'has-error': this.state.error, @@ -76,7 +77,7 @@ module.exports = React.createClass({ } feedbackMessage = ( -
+
{messageText}
); @@ -98,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/members.js b/src/js/components/members.js index e0e4013..a907016 100644 --- a/src/js/components/members.js +++ b/src/js/components/members.js @@ -7,7 +7,7 @@ var _ = require('lodash'); var api = require('../api'); module.exports = React.createClass({ - getInitialState() { + getInitialState() { return { members: [] }; diff --git a/src/js/components/membershipForm.js b/src/js/components/membershipForm.js new file mode 100644 index 0000000..ceb30be --- /dev/null +++ b/src/js/components/membershipForm.js @@ -0,0 +1,34 @@ +import React from 'react'; +import MembershipInfoForm from './membershipInfoForm'; + +module.exports = React.createClass({ + getInitialState() { + return { + paymentSuccess: false + }; + }, + handlePaymentSuccess() { + this.setState({ paymentSuccess: true }); + }, + render() { + if(!this.state.paymentSuccess) { + return ( + + ); + } + 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 new file mode 100644 index 0000000..185bdd8 --- /dev/null +++ b/src/js/components/membershipInfoForm.js @@ -0,0 +1,199 @@ +'use strict'; + +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 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' } +}; + +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,}))$/; + return re.test(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: '', + handle: '', + name: '', + postcode: '', + sending: false, + pristineFields: fieldNames + }; + }, + onSubmit(token) { + this.setState({ + sending: true, + error: null + }); + + 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 }); + }); + }, + onChange(e) { + 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: [] + }); + }, + + getDataErrors() { + var foundErrors = []; + + fieldNames.forEach((fieldName) => { + if (!this.state[fieldName]) { + foundErrors.push({ field: fieldName, type: 'missing' }); + } + }); + + 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, + 'membership-form': true, + 'has-error': inputErrors.length !== 0 || this.state.error, + 'sending': this.state.sending + }); + + function getErrorMessage(err) { + var feedbackText; + + if (err.type === 'missing') { + feedbackText = `${fieldNameTranslations[err.field].fi} on pakollinen.`; + } else if (err.type === 'invalid') { + feedbackText = `${fieldNameTranslations[err.field].fi} on virheellinen.`; + } + + return
{feedbackText}
; + } + + /* generate error messages */ + var visibleErrors = inputErrors + .filter((error) => this.state.pristineFields.indexOf(error.field) === -1); + + var fieldsWithErrors = visibleErrors.map(({ field }) => field); + + var inputFields = fieldNames.map((fieldName) => { + var inputClasses = classSet({ + 'input': true, + 'has-error': _.includes(fieldsWithErrors, fieldName), + 'half': fieldName === 'city' || fieldName === 'postcode', + 'left': fieldName === 'city' + }); + + 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 ( +
+

Liity jäseneksi

+
+ {inputFields} + {this.state.error && ( +
+ Jotain meni pieleen! Ota yhteyttä info@koodiklinikka.fi +
+ )} +
+ + + +

+ Seuraava vuosimaksu veloitetaan automaattisesti
kortiltasi vuoden kuluttua.
+

+
+
+ ); + } +}); diff --git a/src/js/main.js b/src/js/main.js index d5b35bc..ba585f8 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -3,31 +3,49 @@ require('./ga'); let ReactDOM = require('react-dom'); var React = require('react'); - var inviteForm = React.createFactory(require('./components/inviteForm')); var fader = React.createFactory(require('./components/fader')); var members = React.createFactory(require('./components/members')); var feed = React.createFactory(require('./components/feed')); var email = React.createFactory(require('./components/email')); +var membershipForm = React.createFactory(require('./components/membershipForm')); -ReactDOM.render( - inviteForm(), - document.getElementById('invite-form')); +const pathName = window.location.pathname; -ReactDOM.render( - fader(), - document.getElementById('fader')); +document.querySelectorAll('.email').forEach((element) => + ReactDOM.render(email(), element) +); +if (pathName == '/') { + ReactDOM.render( + inviteForm(), + document.getElementById('invite-form')); -ReactDOM.render( - members(), - document.getElementById('members')); + ReactDOM.render( + fader(), + document.getElementById('fader')); + ReactDOM.render( + members(), + document.getElementById('members')); -ReactDOM.render( - feed(), - document.getElementById('feed')); + ReactDOM.render( + feed(), + document.getElementById('feed')); -ReactDOM.render( - email(), - document.getElementById('email')); +} else if (pathName == '/yhdistys.html') { + ReactDOM.render( + membershipForm(), + document.getElementById('membership-form')); + + ReactDOM.render( + fader(), + document.getElementById('fader')); + + ReactDOM.render( + React.createElement('div', {}, [ + members({ key: 0 }), + members({ key: 1 }) + ]), + document.getElementById('members')); +} diff --git a/src/styles/_header.styl b/src/styles/_header.styl index 0bffafa..db74ade 100644 --- a/src/styles/_header.styl +++ b/src/styles/_header.styl @@ -1,8 +1,10 @@ +headerHeight = 400px + .header background url('../images/jumbo.jpg') background-position bottom center background-size cover - height 400px + height headerHeight overflow hidden position relative width 100% @@ -23,6 +25,43 @@ vertical-align middle width 100% +.header__nav + position absolute + right 60px + top 32px + + @media screen and (max-width: 1030px) + right 40px + top 20px + a + color white + font-size 16px + font-weight bold + margin-left 16px + text-shadow 0 2px 0 rgba(0,0,0,0.1) + 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 @@ -85,3 +124,59 @@ @media (min-aspect-ratio: 1/2) width 100% height auto + +@keyframes spin + 0% + top 0 + 50% + top -(headerHeight) + 50.0001% + top headerHeight + 100% + top 0 + +@keyframes spin2 + 0% + top (headerHeight) + 50% + top 0 + 99.99999% + top -(headerHeight) + 100% + top (headerHeight) + +.header__members + width 100% + height 100% + z-index -1 + position absolute + top 0 + left 0 + + .member + margin 0 + border-radius 0 + width (100/18)% + + @media screen and (min-width: 2000px) + width 5% + @media screen and (max-width: 1200px) + width (100/15)% + @media screen and (max-width: 810px) + width 10% + @media screen and (max-width: 450px) + width 20% + + .members + position absolute + overflow hidden + top 0 + left 0 + right 0 + height headerHeight + animation spin 40s infinite linear + &:first-child + z-index 1 + &:last-child + animation spin2 40s infinite linear + diff --git a/src/styles/_input.styl b/src/styles/_input.styl index 36a182d..808e2f8 100644 --- a/src/styles/_input.styl +++ b/src/styles/_input.styl @@ -30,3 +30,8 @@ .input.has-error border-color rgba(226, 33, 112, 0.6) color rgb(226, 33, 112) + +.input.half + width 48% + &.left + margin-right 4% 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 c1a6b38..1b1747f 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 @@ -25,6 +26,9 @@ h1, h2, h3 h2 margin-bottom 1em +h3 + font-size 1.25em + p margin-top 1em line-height 1.5em @@ -51,13 +55,17 @@ section .content z-index 2 position relative - padding-right feedWidth min-height 50vh box-sizing border-box @media screen and (max-width: 700px) h3 margin-top 0 + + &.with-feed + padding-right feedWidth + + section:first-child box-shadow -1px -1px 1px rgba(0, 0, 0, 0.05) border-bottom 1px solid #EEEEEE @@ -104,35 +112,55 @@ section:first-child padding 0 &:first-child margin-top 0 + +.form + .btn + background linkColor + border-bottom 2px solid #117280 + color rgba(255, 255, 255, 0.9) + &.sending + .btn + display none + .invite-form__loader + display block .invite-form position relative .btn width 40px height 32px - background linkColor padding 0 position absolute right 8px top 7px - - border-bottom 2px solid #117280 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 - width 0px - height 0px - background transparent url('../images/ajax-loader.gif') no-repeat center center + right 14px + top 14px + +.membership-form + .input + margin 8px 0px + .btn + margin-top 12px + +.membership-form__loader + width 70px + height 70px + margin auto + +.stripe-form + margin 20px 0px + .name + margin-top 20px + text-align left display block - &.sending - .btn - display none - .loader - width 28px - height 28px + color rgba(0, 0, 0, 0.4) @keyframes drop 0% @@ -146,7 +174,7 @@ section:first-child 100% transform rotateX(0deg) -.invite-form--message +.form--message background linkColor color #fff line-height 40px @@ -156,8 +184,8 @@ section:first-child transform-origin 100% 0 animation drop 0.6s linear -.invite-form.has-error - .invite-form--message +.form.has-error + .form--message background rgb(226, 33, 112) .members @@ -182,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 @@ -194,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 @@ -215,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 @@ -242,7 +271,7 @@ footer @media screen and (max-width: 980px) .feed width 0 - .content + .content.with-feed padding-right 0 .message @@ -314,3 +343,24 @@ footer display block .column display block + +.bread-img + background url('../images/hp3_bw.jpg') + background-size cover + border-radius 160px + opacity 0.85 + width 320px + height 320px + margin auto + +@media screen and (max-width: 700px) + .bread-img + display none + +.organization + padding-top 3em + +.membership-information.column + vertical-align initial + p:first-child + margin-top 37px diff --git a/src/templates/head.jade b/src/templates/head.jade new file mode 100644 index 0000000..b714c52 --- /dev/null +++ b/src/templates/head.jade @@ -0,0 +1,84 @@ +doctype html +html + head + title + block title + // inject:css + // endinject + meta(name='description', content='Koodiklinikka on suomalainen Slack-yhteisö ohjelmistoalan harrastajille ja ammattilaisille.') + meta(name='keywords', content='ohjelmointi,frontend,open source,devaus,suomi,javascript,clojure,go,java,node.js,io.js,angular.js,web') + meta(charset='utf-8') + meta(name='viewport', content='width=device-width, initial-scale=1') + meta(name='apple-mobile-web-app-capable', content='yes') + + script. + if(location.hostname === 'koodiklinikka.fi' && location.protocol !== 'https:') { + location.protocol = 'https'; + } + link(rel='apple-touch-icon', sizes='57x57', href='icons/apple-touch-icon-57x57.png') + link(rel='apple-touch-icon', sizes='114x114', href='icons/apple-touch-icon-114x114.png') + link(rel='apple-touch-icon', sizes='72x72', href='icons/apple-touch-icon-72x72.png') + link(rel='apple-touch-icon', sizes='144x144', href='icons/apple-touch-icon-144x144.png') + link(rel='apple-touch-icon', sizes='60x60', href='icons/apple-touch-icon-60x60.png') + link(rel='apple-touch-icon', sizes='120x120', href='icons/apple-touch-icon-120x120.png') + link(rel='apple-touch-icon', sizes='76x76', href='icons/apple-touch-icon-76x76.png') + link(rel='apple-touch-icon', sizes='152x152', href='icons/apple-touch-icon-152x152.png') + link(rel='apple-touch-icon', sizes='180x180', href='icons/apple-touch-icon-180x180.png') + link(rel='icon', type='image/png', href='icons/favicon-192x192.png', sizes='192x192') + link(rel='icon', type='image/png', href='icons/favicon-160x160.png', sizes='160x160') + link(rel='icon', type='image/png', href='icons/favicon-96x96.png', sizes='96x96') + link(rel='icon', type='image/png', href='icons/favicon-16x16.png', sizes='16x16') + link(rel='icon', type='image/png', href='icons/favicon-32x32.png', sizes='32x32') + link(rel='shortcut icon', href='icons/favicon.ico') + link(rel='icon', href='icons/favicon.ico') + meta(name='msapplication-TileColor', content='#10558c') + meta(name='msapplication-TileImage', content='icons/mstile-144x144.png') + meta(property='og:image', content='images/logo.png') + script(src='https://js.stripe.com/v3/') + script(src='//use.typekit.net/scb5xny.js') + script. + try{Typekit.load();}catch(e){}; + + body + .site + .container + .header + block header_content + + block content + + footer + .sponsors + .sponsors__label Yhteistyössä + a(href='http://futurice.com/', target='_blank') + img.sponsor.sponsor__futurice(src='images/futurice.svg') + a(href='http://www.metosin.fi/', target='_blank') + img.sponsor.sponsor__metosin(src='images/metosin.svg') + a(href='https://www.solita.fi/', target='_blank') + img.sponsor(src='images/solita.svg') + a(href='http://leonidasoy.fi/', target='_blank') + img.sponsor.sponsor__leonidas(src='images/leonidas.png') + a(href='https://www.nordea.com/', target='_blank') + img.sponsor.sponsor__nordea(src='images/nordea.png') + + .contacts + div + a(href='https://koodiklinikka.slack.com') + i.fa.fa-slack + + a(href='https://github.com/koodiklinikka/koodiklinikka.fi') + i.fa.fa-github + + a(href='https://twitter.com/koodiklinikka') + i.fa.fa-twitter + + a(href='https://www.linkedin.com/groups/12025476') + i.fa.fa-linkedin + + a(href='https://www.facebook.com/koodiklinikka') + i.fa.fa-facebook + div#email.email + + #fader + // inject:js + // endinject diff --git a/src/yhdistys.jade b/src/yhdistys.jade new file mode 100644 index 0000000..028541a --- /dev/null +++ b/src/yhdistys.jade @@ -0,0 +1,48 @@ +extends templates/head + +block title + | Yhdistys + +block header_content + .header__container + .header__nav + a(href='/') etusivu + a(href='/yhdistys.html') yhdistys + .header__headline + .header__logo + h1.header__title Koodiklinikka ry + .header__members + #members + +block content + .content + section.organization + .row + .bread + .column.column1-2 + h3 Rekisteröity yhdistys + p. + Koodiklinikka on nyt rekisteröity yhdistys! + p. + Lähes nelivuotisen taipaleensa aikana Koodiklinikka on kasvanut lähes tuhannen rekisteröityneen jäsenen yhteisöiksi ja näin saavuttanut aseman Suomen suurimpana ohjelmointiaiheisena yhteisönä! Liittymällä Koodiklinikka ry:n jäseneksi tuet toimintaamme, ja tulevaisuudessa yhdistyksen jäsenyys oikeuttaa etuihin Koodiklinikan tapahtumissa. + p. + Yhdistyksen jäsenyys ei ole, eikä tule olemaan, pakollinen Koodiklinikan käyttäjille tai meetuppeihin osallistujille. + .column.column1-2 + .bread-img + .row + .bread + .column.column1-2 + #membership-form.form + + .column.column1-2.membership-information + p. + Kuka tahansa Slack:iin rekisteröitynyt käyttäjä voi liittyä yhdistyksen jäseneksi maksamalla 10€ vuosittaisen jäsenmaksun. + p. + Jäsenmaksulla tulemme tekemään tapahtumistamme vieläkin mielenkiintoisempia ja kattamaan kustannuksia jäsenmäärämme kasvaessa. + p. + Jos tarvitset Slack-kutsun, siirry etusivulle. + p. + Jäsenrekisterin rekisteriseloste luettavissa Google Drivessä. + p. + Lisätietoa yhdistyksen jäsenyydestä tai vuosimaksusta emaililla +