Merge pull request #42 from akx/nextjs

Become next.js
This commit is contained in:
Aarni Koskela
2019-10-24 17:35:17 +03:00
committed by GitHub
84 changed files with 5769 additions and 11277 deletions

View File

@@ -1,3 +0,0 @@
{
"presets": ["es2015", "react"]
}

View File

@@ -1,8 +1,22 @@
{
"extends": "airbnb",
"ecmaFeatures": {
"jsx": true,
"modules": true
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"prettier"
],
"plugins": [
"react"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true,
"modules": true
}
},
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
@@ -10,13 +24,11 @@
},
"parser": "babel-eslint",
"rules": {
"quotes": [2, "single"],
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2,
"react/jsx-no-target-blank": 1,
"comma-dangle": [2, "never"],
"space-after-keywords": [2, "never"],
"react/jsx-quotes": [2, "single"],
"react/prop-types": 0,
"no-use-before-define": 0,
"padded-blocks": 0,
@@ -24,10 +36,7 @@
"min": 3,
"max": 30,
"properties": "never",
"exceptions": ["x", "y", "vx", "vy", "id", "i", "e", "fn"]
"exceptions": ["x", "y", "vx", "vy", "id", "i", "e", "fn", "_"]
}]
},
"plugins": [
"react"
]
}
}

8
.gitignore vendored
View File

@@ -1,5 +1,5 @@
npm-debug.log
node_modules
public
*.log
.DS_Store
.next
node_modules
out

View File

@@ -4,16 +4,16 @@ node_js:
- "11.6.0"
install:
- npm install
deploy:
local_dir: public
provider: pages
fqdn: koodiklinikka.fi
skip_cleanup: true
github_token: "$GITHUB_TOKEN"
repo: koodiklinikka/koodiklinikka.github.io
target_branch: master
on:
branch: master
after_success:
- chmod ugo+x ./deploy-surge.sh
- "./deploy-surge.sh"
#deploy:
# local_dir: public
# provider: pages
# fqdn: koodiklinikka.fi
# skip_cleanup: true
# github_token: "$GITHUB_TOKEN"
# repo: koodiklinikka/koodiklinikka.github.io
# target_branch: master
# on:
# branch: master
#after_success:
# - chmod ugo+x ./deploy-surge.sh
# - "./deploy-surge.sh"

View File

@@ -32,13 +32,13 @@
npm start
```
- Avaa selaimessasi: [`http://localhost:9001`](http://localhost:9001)
- Avaa selaimessasi: [`http://localhost:3000`](http://localhost:3000)
## Komennot
* `npm install`
* asentaa projektin riippuvuudet
* `npm start`
* kääntää lähdetiedostot ja palvelee sovellusta porttiin `9001`
* kääntää lähdetiedostot ja palvelee sovellusta porttiin `3000`
* `npm run build`
* kääntää lähdetiedostot
* kääntää lähdetiedostot -> `out/`

6
TODO.md Normal file
View File

@@ -0,0 +1,6 @@
* Stripe
* Test ID `pk_test_OmNve9H1OuORlmD4rblpjgzh`
* Prod ID `pk_live_xrnwdLNXbt20LMxpIDffJnnC`
* API integration (test backend `https://lit-plateau-4689.herokuapp.com/`)
* Hero video
* Deployment

View File

@@ -0,0 +1,5 @@
import React from "react";
export default function EmailComponent() {
return <a href="mailto:info@koodiklinikka.fi">info@koodiklinikka.fi</a>;
}

42
components/Fader.jsx Normal file
View File

@@ -0,0 +1,42 @@
import React from "react";
function clamp(min, max, value) {
return Math.min(Math.max(value, min), max);
}
export default class Fader extends React.Component {
static defaultProps = {
threshold: 100
};
state = {
opacity: 0
};
onScroll = () => {
var scrollableDistance = document.body.scrollHeight - window.innerHeight,
scrollTop = window.pageYOffset || document.documentElement.scrollTop,
distanceToBottom = scrollableDistance - scrollTop;
this.setState({
opacity: clamp(0, 1, distanceToBottom / this.props.threshold)
});
};
componentDidMount() {
window.addEventListener("scroll", this.onScroll);
this.onScroll();
}
componentWillUnmount() {
window.removeEventListener("scroll", this.onScroll);
}
render() {
var style = {
opacity: this.state.opacity
};
return <div className="fader" style={style}></div>;
}
}

80
components/Feed.jsx Normal file
View File

@@ -0,0 +1,80 @@
import _ from "lodash";
import React from "react";
import request from "axios";
import timeago from "timeago";
import api from "./api";
import transformers from "./feed-transformers";
function throwError(err) {
setTimeout(() => {
console.log(err.stack);
throw err;
});
}
export default class Feed extends React.Component {
state = {
messages: []
};
componentDidMount() {
request
.get(api("feeds"))
.then(res => {
const messages = _(res.data)
.map((messages, type) => transformers[type](messages))
.flatten()
.value();
this.setState({
messages: _(messages)
.sortBy("timestamp")
.reverse()
.value()
.slice(0, 40)
});
})
.catch(throwError);
}
render() {
var messages = this.state.messages.map((message, i) => {
var image = <img src={message.image} />;
if (message.imageLink) {
image = (
<a target="_blank" href={message.imageLink}>
{image}
</a>
);
}
return (
<div className="message" key={i}>
<div className="message__image">{image}</div>
<div className="message__content">
<div className="message__user">
<a href={message.userLink}>{message.user}</a>
</div>
<div
className="message__body"
dangerouslySetInnerHTML={{ __html: message.body }}
></div>
<div className="message__icon">
<i className={`fa fa-${message.type}`}></i>
</div>
<div className="message__details">
<span className="message__timestamp">
{timeago(message.timestamp)}
</span>
<span className="message__meta">{message.meta}</span>
</div>
</div>
</div>
);
});
return <div className="feed">{messages}</div>;
}
}

61
components/Footer.jsx Normal file
View File

@@ -0,0 +1,61 @@
import React from "react";
import EmailComponent from "./EmailComponent";
export function Footer() {
return (
<footer>
<div className="sponsors">
<div className="sponsors__label">Yhteistyössä</div>
<a href="http://futurice.com/" target="_blank">
<img
src="/static/images/futurice.svg"
className="sponsor sponsor__futurice"
/>
</a>
<a href="http://www.metosin.fi/" target="_blank">
<img
src="/static/images/metosin.svg"
className="sponsor sponsor__metosin"
/>
</a>
<a href="https://www.solita.fi/" target="_blank">
<img src="/static/images/solita.svg" className="sponsor" />
</a>
<a href="http://wakeone.co/" target="_blank">
<img
src="/static/images/wakeone.svg"
className="sponsor sponsor__wakeone"
/>
</a>
<a href="https://www.nordea.com/" target="_blank">
<img
src="/static/images/nordea.png"
className="sponsor sponsor__nordea"
/>
</a>
</div>
<div className="contacts">
<div>
<a href="https://koodiklinikka.slack.com">
<i className="fa fa-slack" />
</a>
<a href="https://github.com/koodiklinikka/koodiklinikka.fi">
<i className="fa fa-github" />
</a>
<a href="https://twitter.com/koodiklinikka">
<i className="fa fa-twitter" />
</a>
<a href="https://www.linkedin.com/groups/12025476">
<i className="fa fa-linkedin" />
</a>
<a href="https://www.facebook.com/koodiklinikka">
<i className="fa fa-facebook" />
</a>
<div id="email">
<EmailComponent />
</div>
</div>
</div>
</footer>
);
}

115
components/InviteForm.jsx Normal file
View File

@@ -0,0 +1,115 @@
import request from "axios";
import React from "react";
import classSet from "classnames";
import api from "./api";
import Loader from "./Loader";
export default class InviteForm extends React.Component {
state = {
email: "",
submitted: false,
sending: false,
error: null
};
onSubmit = e => {
e.preventDefault();
this.setState({
submitted: false,
sending: true,
error: null
});
request
.post(api("invites"), {
email: this.state.email.trim()
})
.then(this.handleSuccess)
.catch(this.handleError);
};
handleSuccess = () => {
this.setState({ submitted: true, sending: false });
};
handleError = err => {
this.setState({ error: err, sending: false });
};
onChange = e => {
if (e.target.value === this.state.email) {
return;
}
this.setState({
email: e.target.value,
error: null,
submitted: false
});
};
render() {
var formClasses = classSet({
form: true,
"invite-form": true,
"has-success": this.state.submitted,
"has-error": this.state.error,
sending: this.state.sending
});
var inputClasses = classSet({
input: true,
"has-success": this.state.submitted,
"has-error": this.state.error
});
var feedbackMessage;
if (this.state.error || this.state.submitted) {
let messageText;
if (this.state.submitted) {
messageText = "Kutsu lähetetty antamaasi sähköpostiosoitteeseen.";
} else if (
this.state.error.status === 400 &&
this.state.error.data === "invalid_email"
) {
messageText = "Tarkasta syöttämäsi sähköpostiosoite";
} else if (
this.state.error.status === 400 &&
this.state.error.data === "already_invited"
) {
messageText = "Sähköpostiosoitteeseen on jo lähetetty kutsu";
} else {
messageText = "Jotain meni pieleen. Yritä hetken päästä uudelleen.";
}
feedbackMessage = <div className="form--message">{messageText}</div>;
}
return (
<form className={formClasses} onSubmit={this.onSubmit}>
<input
className={inputClasses}
type="text"
name="email"
placeholder="Email"
value={this.state.email}
onChange={this.onChange}
/>
<button
className="btn btn__submit"
type="submit"
title="Lähetä"
disabled={this.state.error || this.state.submitted}
>
Lähetä
</button>
<div className="invite-form__loader">
<Loader />
</div>
{feedbackMessage}
</form>
);
}
}

12
components/Loader.jsx Normal file
View File

@@ -0,0 +1,12 @@
import React from "react";
export default function Loader() {
return (
<div className="sk-folding-cube">
<div className="sk-cube1 sk-cube" />
<div className="sk-cube2 sk-cube" />
<div className="sk-cube4 sk-cube" />
<div className="sk-cube3 sk-cube" />
</div>
);
}

35
components/Members.jsx Normal file
View File

@@ -0,0 +1,35 @@
import React from "react";
import request from "axios";
import _ from "lodash";
import api from "./api";
export default class Members extends React.Component {
state = {
members: []
};
componentDidMount() {
request.get(api("members")).then(
function(res) {
this.setState({
members: _.shuffle(res.data)
});
}.bind(this)
);
}
render() {
var members = this.state.members.map(function(member, i) {
var src = `${member.avatar_url}&s=120`;
return <img className="member" key={i} src={src} />;
});
return (
<div className="members">
<a href="https://github.com/koodiklinikka" target="_blank">
{members}
</a>
</div>
);
}
}

5
components/api.js Normal file
View File

@@ -0,0 +1,5 @@
var host = process.env.SERVER || "https://lit-plateau-4689.herokuapp.com/";
export default function(path) {
return host + path;
}

View File

@@ -1,23 +1,21 @@
'use strict';
import _ from "lodash";
import githubEvent from "parse-github-event";
import twitterText from "twitter-text";
var _ = require('lodash');
var githubEvent = require('parse-github-event');
var twitterText = require('twitter-text');
const isVisibleGithubEvent = ({ type }) =>
type !== "PushEvent" && type !== "DeleteEvent";
const isVisibleGithubEvent = ({type}) => type !== 'PushEvent' && type !== 'DeleteEvent';
module.exports = {
export default {
github(items) {
return items.filter(isVisibleGithubEvent).map((item) => {
return items.filter(isVisibleGithubEvent).map(item => {
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
var template = _.template(githubEvent.parse(item).text);
var repository = `https://github.com/${item.repo.name}`;
var branch;
if(item.payload.ref) {
branch = item.payload.ref.replace('refs/heads/', '');
if (item.payload.ref) {
branch = item.payload.ref.replace("refs/heads/", "");
}
var message = template({
@@ -38,13 +36,13 @@ module.exports = {
body: message,
timestamp: new Date(item.created_at),
url: message.url,
type: 'github'
type: "github"
};
});
},
twitter(items) {
return items.map((item) => {
if(item.retweeted) {
return items.map(item => {
if (item.retweeted) {
item = item.retweeted_status;
}
@@ -57,7 +55,7 @@ module.exports = {
imageLink: url,
body: twitterText.autoLink(item.text),
timestamp: new Date(item.created_at),
type: 'twitter'
type: "twitter"
};
});
}

View File

@@ -0,0 +1,39 @@
import React from "react";
import MembershipInfoForm from "./MembershipInfoForm";
export default class MembershipForm extends React.Component {
state = {
signupSuccess: false
};
handleSignupSuccess = () => {
this.setState({ signupSuccess: true });
};
render() {
if (!this.state.signupSuccess) {
return <MembershipInfoForm onSignupSuccess={this.handleSignupSuccess} />;
}
return (
<div>
<svg
height="50"
width="50"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="#349c4a"
d="M256 6.998c-137.533 0-249 111.467-249 249 0 137.534 111.467 249 249 249s249-111.467 249-249c0-137.534-111.467-249-249-249zm0 478.08c-126.31 0-229.08-102.77-229.08-229.08 0-126.31 102.77-229.08 229.08-229.08 126.31 0 229.08 102.77 229.08 229.08 0 126.31-102.77 229.08-229.08 229.08z"
/>
<path
fill="#349c4a"
d="M384.235 158.192L216.92 325.518 127.86 236.48l-14.142 14.144 103.2 103.18 181.36-181.47"
/>
</svg>
<p> Rekisteröityminen onnistui. Tervetuloa jäseneksi!</p>
<p> Tervetuloa Koodiklinikka ry:n jäseneksi!</p>
</div>
);
}
}

View File

@@ -1,13 +1,9 @@
"use strict";
var _ = require("lodash");
var request = require("axios");
var React = require("react");
var classSet = require("classnames");
var api = require("../api");
var Loader = require("./loader");
var config = require("../../config.js")();
import _ from "lodash";
import request from "axios";
import React from "react";
import classSet from "classnames";
import api from "../api";
import Loader from "../Loader";
var fieldNameTranslations = {
address: { fi: "Osoite" },
@@ -18,9 +14,10 @@ var fieldNameTranslations = {
postcode: { fi: "Postinumero" }
};
const mailValidateRe = /^(([^<>()[\]\\.,;:\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,}))$/;
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);
return mailValidateRe.test(email);
}
const fieldNames = ["name", "email", "handle", "address", "postcode", "city"];
@@ -29,20 +26,19 @@ function getUserInfo(state) {
return _.pick(state, fieldNames);
}
module.exports = React.createClass({
getInitialState() {
return {
address: "",
city: "",
email: "",
handle: "",
name: "",
postcode: "",
sending: false,
pristineFields: fieldNames
};
},
onSubmit() {
export default class MembershipInfoForm extends React.Component {
state = {
address: "",
city: "",
email: "",
handle: "",
name: "",
postcode: "",
sending: false,
pristineFields: fieldNames
};
onSubmit = () => {
this.setState({
sending: true,
error: null
@@ -59,8 +55,9 @@ module.exports = React.createClass({
.catch(err => {
this.setState({ error: err, sending: false });
});
},
onChange(e) {
};
onChange = e => {
var name = e.target.name;
if (e.target.value === this.state[name]) {
return;
@@ -73,9 +70,9 @@ module.exports = React.createClass({
),
errors: []
});
},
};
getDataErrors() {
getDataErrors = () => {
var foundErrors = [];
fieldNames.forEach(fieldName => {
@@ -89,7 +86,7 @@ module.exports = React.createClass({
}
return foundErrors;
},
};
render() {
const inputErrors = this.getDataErrors();
@@ -107,9 +104,7 @@ module.exports = React.createClass({
if (err.type === "missing") {
feedbackText = `${fieldNameTranslations[err.field].fi} on pakollinen.`;
} else if (err.type === "invalid") {
feedbackText = `${
fieldNameTranslations[err.field].fi
} on virheellinen.`;
feedbackText = `${fieldNameTranslations[err.field].fi} on virheellinen.`;
}
return (
@@ -190,4 +185,4 @@ module.exports = React.createClass({
</div>
);
}
});
}

View File

@@ -1,193 +0,0 @@
import browserify from 'browserify';
import browserSync from 'browser-sync';
import duration from 'gulp-duration';
import es from 'event-stream';
import exorcist from 'exorcist';
import gulp from 'gulp';
import gutil from 'gulp-util';
import inject from 'gulp-inject';
import jade from 'gulp-jade';
import less from 'gulp-less';
import notifier from 'node-notifier';
import path from 'path';
import prefix from 'gulp-autoprefixer';
import rev from 'gulp-rev';
import source from 'vinyl-source-stream';
import sourcemaps from 'gulp-sourcemaps';
import streamify from 'gulp-streamify';
import stylus from 'gulp-stylus';
import transform from 'vinyl-transform';
import uglify from 'gulp-uglify';
import watch from 'gulp-watch';
import watchify from 'watchify';
/*eslint "no-process-env":0 */
const production = process.env.NODE_ENV === 'production';
const config = {
source: './src',
destination: './public',
scripts: {
source: './src/js/main.js',
destination: './public/js/',
extensions: ['.jsx'],
filename: 'bundle.js'
},
templates: {
source: './src/*.jade',
watch: './src/*.jade',
destination: './public/',
revision: './public/**/*.html'
},
styles: {
source: './src/styles/style.styl',
watch: './src/styles/**/*.styl',
icons: './src/styles/icons.less',
destination: './public/css/'
},
assets: {
source: ['./src/assets/**/*.*', './node_modules/font-awesome/fonts*/*.*'],
watch: './src/assets/**/*.*',
destination: './public/'
},
inject: {
resources: ['./public/**/*.css', './public/**/*.js']
}
};
const browserifyConfig = {
entries: [config.scripts.source],
extensions: config.scripts.extensions,
debug: !production,
cache: {},
packageCache: {}
};
function handleError(err) {
gutil.log(err.message);
gutil.beep();
notifier.notify({
title: 'Compile Error',
message: err.message
});
return this.emit('end');
}
gulp.task('scripts', () => {
let pipeline = browserify(browserifyConfig)
.bundle()
.on('error', handleError)
.pipe(source(config.scripts.filename));
if(production) {
pipeline = pipeline
.pipe(streamify(uglify()))
.pipe(streamify(rev()));
} else {
pipeline = pipeline.pipe(transform(() => {
return exorcist(path.join(config.scripts.destination, config.scripts.filename) + '.map');
}));
}
return pipeline.pipe(gulp.dest(config.scripts.destination));
});
gulp.task('templates', ['styles', 'scripts'], () => {
const resources = gulp.src(config.inject.resources, {read: false});
const pipeline = gulp.src(config.templates.source)
.pipe(jade({
pretty: !production
}))
.on('error', handleError)
.pipe(inject(resources, {ignorePath: 'public', removeTags: true}))
.pipe(gulp.dest(config.templates.destination));
if(production) {
return pipeline;
}
return pipeline.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('styles', () => {
let pipeline = gulp.src(config.styles.source);
if(!production) {
pipeline = pipeline.pipe(sourcemaps.init());
}
const icons = gulp.src(config.styles.icons)
.pipe(less());
pipeline = es.merge(icons, pipeline.pipe(stylus({
'include css': true,
paths: ['node_modules', path.join(__dirname, config.source)],
compress: production
})))
.on('error', handleError)
.pipe(prefix('last 2 versions', 'Chrome 34', 'Firefox 28', 'iOS 7'));
if(production) {
pipeline = pipeline.pipe(rev());
} else {
pipeline = pipeline.pipe(sourcemaps.write('.'));
}
pipeline = pipeline.pipe(gulp.dest(config.styles.destination));
if(production) {
return pipeline;
}
return pipeline.pipe(browserSync.stream({
match: '**/*.css'
}));
});
gulp.task('assets', () => {
return gulp.src(config.assets.source)
.pipe(gulp.dest(config.assets.destination));
});
gulp.task('server', () => {
return browserSync({
open: false,
port: 9001,
notify: false,
ghostMode: false,
server: {
baseDir: config.destination
}
});
});
gulp.task('watch', () => {
['templates', 'styles', 'assets'].forEach((watched) => {
watch(config[watched].watch, () => {
gulp.start(watched);
});
});
const bundle = watchify(browserify(browserifyConfig));
bundle.on('update', () => {
const build = bundle.bundle()
.on('error', handleError)
.pipe(source(config.scripts.filename));
build
.pipe(transform(() => {
return exorcist(config.scripts.destination + config.scripts.filename + '.map');
}))
.pipe(gulp.dest(config.scripts.destination))
.pipe(duration('Rebundling browserify bundle'))
.pipe(browserSync.reload({stream: true}));
}).emit('update');
});
gulp.task('build', ['styles', 'assets', 'scripts', 'templates']);
gulp.task('default', ['styles', 'assets', 'templates', 'watch', 'server']);

4
next.config.js Normal file
View File

@@ -0,0 +1,4 @@
const withStylus = require('@zeit/next-stylus');
const withLess = require('@zeit/next-less');
const withFonts = require('next-fonts');
module.exports = withFonts(withLess(withStylus()));

15273
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,76 +4,38 @@
"description": "Koodiklinikka homepage",
"author": "Riku Rouvila <riku.rouvila@leonidasoy.fi>",
"license": "MIT",
"main": "gulpfile.js",
"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"
"start": "next",
"build": "next build && next export",
"dev": "SERVER=http://localhost:9000/ ENV=development next",
"prod": "ENV=production next build && next export",
"lint": "eslint --ext .jsx --ext .js ."
},
"keywords": [
"gulp",
"template"
],
"dependencies": {
"axios": "^0.4.2",
"@zeit/next-css": "^1.0.1",
"@zeit/next-less": "^1.0.1",
"@zeit/next-stylus": "^1.0.1",
"axios": "^0.19.0",
"classnames": "^2.2.1",
"font-awesome": "^4.4.0",
"http-server": "^0.11.1",
"less": "^3.10.3",
"lodash": "^3.9.1",
"next": "^9.0.6",
"next-fonts": "^0.18.0",
"next-ga": "^2.3.4",
"parse-github-event": "^0.2.0",
"prop-types": "^15.5.10",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-stripe-checkout": "^2.4.0",
"stylus": "^0.54.7",
"timeago": "^0.2.0",
"twitter-text": "^1.11.0"
},
"devDependencies": {
"babel": "^6.1.18",
"babel-core": "^6.2.1",
"babel-eslint": "^4.1.3",
"babel-preset-es2015": "^6.1.18",
"babel-preset-react": "^6.1.18",
"babelify": "^7.2.0",
"browser-sync": "^2.9.4",
"browserify": "^10.2.1",
"chai": "^3.0.0",
"envify": "^3.4.0",
"eslint": "^1.5.1",
"eslint-config-airbnb": "0.0.9",
"eslint-plugin-react": "^3.4.2",
"event-stream": "4.0.1",
"exorcist": "^0.4.0",
"gulp": "3.9.0",
"gulp-autoprefixer": "1.0.1",
"gulp-duration": "0.0.0",
"gulp-inject": "^3.0.0",
"gulp-jade": "^1.1.0",
"gulp-less": "^3.0.5",
"gulp-replace": "^0.5.3",
"gulp-rev": "^4.0.0",
"gulp-sourcemaps": "^1.3.0",
"gulp-streamify": "0.0.5",
"gulp-stylus": "^2.6.0",
"gulp-uglify": "~1.0.1",
"gulp-util": "~3.0.1",
"gulp-watch": "^4.3.4",
"jsdom": "^5.6.0",
"mocha": "^2.2.5",
"node-notifier": "^4.2.1",
"rimraf": "^2.3.4",
"surge": "^0.20.1",
"vinyl-source-stream": "~1.0.0",
"vinyl-transform": "^1.0.0",
"watchify": "^3.2.1"
},
"browserify": {
"transform": [
"babelify",
"envify"
]
"babel-eslint": "^10.0.3",
"eslint": "^6.4.0",
"eslint-config-prettier": "^6.3.0",
"eslint-plugin-react": "^7.14.3",
"prettier": "^1.18.2"
}
}

13
pages/_app.jsx Normal file
View File

@@ -0,0 +1,13 @@
import React from "react";
import App from "next/app";
import Router from "next/router";
import withGA from "next-ga";
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return <Component {...pageProps} />;
}
}
export default withGA("UA-58806132-1", Router)(MyApp);

65
pages/_document.jsx Normal file
View File

@@ -0,0 +1,65 @@
import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import { Footer } from "../components/Footer";
import Fader from '../components/Fader';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html>
<Head>
<meta
name="description"
content="Koodiklinikka on suomalainen 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" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/static/icons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/static/icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/static/icons/favicon-16x16.png"
/>
<link rel="manifest" href="/static/cons/site.webmanifest" />
<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){};'}</script>
</Head>
<body>
<div className="site">
<div className="container">
<Main />
<NextScript />
</div>
<Footer />
</div>
<Fader />
</body>
</Html>
);
}
}
export default MyDocument;

185
pages/index.jsx Normal file
View File

@@ -0,0 +1,185 @@
import React from "react";
import "../styles/style.styl";
import "../styles/icons.less";
import Head from "next/head";
import InviteForm from '../components/InviteForm';
import Members from '../components/Members';
import Feed from '../components/Feed';
const Hero = () => (
<div className="header">
<video
autoPlay
loop
poster="/static/images/poster.jpg"
className="header__video-bg"
>
<source src="/static/videos/jumbo.mp4" type="video/mp4" />
</video>
<div className="header__container">
<div className="header__nav">
<a href="/">
<img src="/static/images/logo-new.svg" alt="Etusivu" />
</a>
</div>
<div className="header__headline">
<h1 className="header__title">
Yhteisö kaikille ohjelmoinnista ja ohjelmistoalasta kiinnostuneille
harrastajille ja ammattilaisille.
</h1>
</div>
</div>
</div>
);
const IndexContent = () => (
<>
<div className="content with-feed">
<section>
<div className="row">
<h3>
Tule mukaan{" "}
<a target="_blank" href="https://slack.com/">
Slack
</a>
-yhteisöömme
</h3>
<div className="form"><InviteForm /></div>
<p className="code-of-conduct">
Ennen liittymistä yhteisöömme varmista, että olet lukenut yhteisön{" "}
<a
target="_blank"
href="https://github.com/koodiklinikka/code-of-conduct/blob/master/README.md"
>
käyttäytymissäännöt
</a>
.
</p>
</div>
</section>
<section>
<div className="row">
<div className="bread">
<div className="column column1-2">
<h3>Yhteisö ohjelmoinnista kiinnostuneille</h3>
<p>
Koodiklinikka on Suomen suurin ohjelmistoalan yhteisö, joka
kokoaa työntekijät, harrastajat ja vasta-alkajat yhteen.{"\n"}
Tarkoituksenamme on yhdistää ja kasvattaa suomalaista
ohjelmointiyhteisöä, sekä tarjota apua ja uusia kontakteja
ohjelmoinnista innostuneille nuorille.
</p>
<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.
</p>
</div>
<div className="column column1-2">
<a href="/static/images/slack.png" target="_blank">
<img src="/static/images/slack.png" />
</a>
</div>
</div>
</div>
<div className="row">
<div className="bread">
<div className="column column2-5">
<img src="/static/images/octocat.png" />
</div>
<div className="column column3-5">
<h3>Avoin lähdekoodi</h3>
<p>
Suosimme avointa lähdekoodia ja kaikki käyttämämme koodi on
vapaasti saatavilla ja hyödynnettävissä{" "}
<a href="https://github.com/koodiklinikka">
Github-organisaatiomme sivulta
</a>
. Organisaation jäseneksi otamme kaikki Slack-yhteisömme
jäsenet. Koodiklinikan projekteihin voi osallistua kuka tahansa
ja muutosideat ovat aina lämpimästi tervetulleita.
</p>
<div id="members">
<Members />
</div>
</div>
</div>
</div>
<div className="row">
<h2>Potilaiden projekteja</h2>
<div className="bread">
<div className="column column2-5">
<a href="https://redom.js.org" target="_blank">
<img src="/static/images/redom.svg" />
</a>
</div>
<div className="column column3-5">
<h4>RE:DOM</h4>
<p>
Tiny (2 KB) turboboosted JavaScript library for creating user
interfaces. Develop web apps with 100 % JavaScript and web
standards.
</p>
</div>
</div>
<div className="bread">
<div className="column column2-5">
<a href="https://codestats.net/" target="_blank">
<img
src="/static/images/codestats.png"
className="project-image__codestats"
/>
</a>
</div>
<div className="column column3-5">
<h4>Code::Stats</h4>
<p>
Code::Stats is a free stats tracking service for programmers
</p>
</div>
</div>
<div className="bread">
<div className="column column2-5">
<a href="https://reactabular.js.org/" target="_blank">
<img
src="/static/images/reactabular.png"
className="project-image__codestats"
/>
</a>
</div>
<div className="column column3-5">
<h4>Reactabular</h4>
<p>A framework for building the React table you need</p>
</div>
</div>
<div className="bread">
<div className="column column2-5">
<a href="https://avain.app" target="_blank">
<img src="/static/images/avain.svg" style={{ width: "7rem" }} />
</a>
</div>
<div className="column column3-5">
<h4>Avain.app</h4>
<p>Secure one-time password manager (PWA + Web Crypto)</p>
</div>
</div>
</div>
</section>
<div id="feed">
<Feed />
</div>
</div>
</>
);
const Index = () => (
<React.Fragment>
<Head>
<title>Koodiklinikka</title>
</Head>
<Hero />
<IndexContent />
</React.Fragment>
);
export default Index;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" stroke="#979797" fill="#FF5500" x="0.5" y="0.5" width="511" height="511"></rect>
<path d="M416,206.000625 C416,266.751875 366.75125,316 306,316 C298.9875,316 292.13125,315.33625 285.483125,314.081875 L270.475625,330.965625 C267.629106,334.167983 263.548972,336.000154 259.264375,336 L236,336 L236,361 C236,369.284375 229.284375,376 221,376 L196,376 L196,401 C196,409.284375 189.284375,416 181,416 L111,416 C102.715625,416 96,409.284375 96,401 L96,352.213125 C96,348.235 97.580625,344.419375 100.393125,341.60625 L201.519375,240.48 C197.9425,229.63375 196,218.044375 196,206 C196,145.24875 245.248125,96.000625 305.999375,96 C366.93,95.999375 416,145.069375 416,206.000625 Z M306,176 C306,192.56875 319.43125,206 336,206 C352.56875,206 366,192.56875 366,176 C366,159.43125 352.56875,146 336,146 C319.43125,146 306,159.43125 306,176 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="206" viewBox="0 0 960 206"><title>futurice-logo--green</title><desc>Created with Sketch.</desc><g fill="#349C4A" fill-rule="evenodd"><path d="M635.7 65.9h37.4v136.4h-37.4V65.9zM198.4 65.9v73.7c0 18.1-8.3 28.7-24.9 28.7-16.6 0-24.9-10.6-24.9-28.7V65.9h-37.8V149c0 35.1 26.1 55.9 63.1 55.9S237 184 237 149V65.9h-38.6zM461.1 65.9v73.7c0 18.1-8.3 28.7-24.9 28.7-16.6 0-24.9-10.6-24.9-28.7V65.9h-37.8V149c0 35.1 26.1 55.9 63.1 55.9s63.1-20.8 63.1-55.9V65.9h-38.6z"/><circle cx="653.8" cy="23.8" r="23.8"/><path d="M522.3 65.9v136.4h37.4V99.5h52.2V65.9h-89.6zM918.8 159.7c-6.8 7.6-16.3 12.5-27.2 12.5h-.4c-19.7 0-31.7-9.8-36.3-24.9H960v-13.7c0-39.3-28.7-70.7-70.3-70.7-39.3 0-71.1 31.7-71.1 71.1 0 39.4 29.5 71.1 71.8 71.1h.8c21.9 0 39.3-8.3 51.4-20.8l-23.8-24.6zm-28.7-63.9c15.9 0 27.6 7.2 32.5 21.5h-66.1c5.6-12.8 17.7-21.5 33.6-21.5zM787.3 159.7c-6.4 6-14.7 9.8-24.2 9.8h-.8c-19.3-.4-34-15.9-34-35.5 0-19.3 14.7-35.1 34-35.5h.8c10.2 0 18.9 4.5 25.3 11.3l25.7-25.7c-12.5-13.2-30.2-21.2-51.4-21.2h-.4c-38.9.4-69.9 31.7-69.9 70.7s31 70.7 69.9 70.7h.4c20 0 37.4-7.6 49.9-19.7l-25.3-24.9zM297.1 65.9v-51h-37.4V142.7c0 44.6 14.4 59.3 57.4 59.3h32.1v-34.8h-26.8c-23.1 0-25.7-3.4-25.7-24.6v-43h52.5V66c0-.1-52.1-.1-52.1-.1zM37.4 56.9c0-12.9 7.9-20.4 18.5-20.4 6.8 0 12.1 3.8 14.7 7.9l22.7-22.7C81.6 5.4 66.1.2 51 .2 23.4.2.7 24.4 0 56.5v145.9h37.4v-104h48.4V65.9H37.4v-9z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1 +0,0 @@
<svg width="143" height="129" viewBox="0 0 143 129" xmlns="http://www.w3.org/2000/svg"><title>REDOM</title><desc>Created with Sketch.</desc><g fill="none" fill-rule="evenodd"><path d="M125 75l12.5 25h-25L125 75zm0-50l12.5 25h-25L125 25zm-25 50l12.5 25h-25L100 75zm12.5-25L125 75h-25l12.5-25zm-100 0L25 75H0l12.5-25zm25-50L50 25H25L37.5 0zm-25 0L25 25H0L12.5 0zM25 25l12.5 25h-25L25 25zm25 50l12.5 25h-25L50 75zm12.5-25L75 75H50l12.5-25zM75 25l12.5 25h-25L75 25zm12.5 25L100 75H75l12.5-25z" id="Combined-Shape" fill="#E8DC64"/><path d="M25 75l12.5-25h-25L25 75zm25-50L62.5 0h-25L50 25zm-25 0L37.5 0h-25L25 25zm12.5 25L50 25H25l12.5 25zm-25 0L25 25H0l12.5 25zm50 50L75 75H50l12.5 25zM100 75l12.5-25h-25L100 75zm12.5 25L125 75h-25l12.5 25zM125 75l12.5-25h-25L125 75zm0-50l12.5-25h-25L125 25zM12.5 100L25 75H0l12.5 25zm75-50L100 25H75l12.5 25z" id="Combined-Shape" fill="#FDF06F"/><path d="M25 75l12.5 25h-25L25 75zm50 0l12.5 25h-25L75 75zM37.5 50L50 75H25l12.5-25zM50 25l12.5 25h-25L50 25zM87.5 0L100 25H75L87.5 0zm25 0L125 25h-25l12.5-25zM100 25l12.5 25h-25L100 25zM62.5 0L75 25H50L62.5 0zm25 50L100 75H75l12.5-25z" id="Combined-Shape" fill="#BA152B"/><path d="M100 25l12.5-25h-25L100 25zm-62.5 75L50 75H25l12.5 25zM50 75l12.5-25h-25L50 75zm25-50L87.5 0h-25L75 25zm12.5 75L100 75H75l12.5 25zm25-50L125 25h-25l12.5 25zM75 75l12.5-25h-25L75 75zM62.5 50L75 25H50l12.5 25z" fill="#D31B33"/><path d="M21.78 104.67c1.76 0 3.218.186 4.373.56 1.156.374 2.074.902 2.756 1.584.68.682 1.16 1.485 1.434 2.41.276.923.413 1.946.413 3.068 0 1.54-.324 2.915-.974 4.125-.65 1.21-1.765 2.123-3.35 2.74l4.82 8.843h-5.644l-4.324-8.085h-3.828V128h-5.214v-23.33h9.537zm-4.322 11.12h3.762c.858 0 1.562-.077 2.112-.23.55-.155.99-.38 1.32-.678.33-.297.56-.66.693-1.09.132-.428.198-.917.198-1.467 0-.528-.066-1.006-.198-1.436-.132-.43-.363-.787-.693-1.073-.33-.286-.77-.506-1.32-.66-.55-.154-1.254-.23-2.112-.23h-3.762v6.863zM34.915 128v-23.33H51.48v4.29H40.13v5.114h9.833v4.323H40.13v5.28h11.35V128H34.916zm26.532-2.41c0 .793-.225 1.453-.677 1.98-.45.53-1.193.793-2.227.793-1.034 0-1.77-.258-2.21-.775-.44-.518-.66-1.183-.66-1.997 0-.77.22-1.412.66-1.93.44-.517 1.176-.775 2.21-.775s1.776.258 2.227.775c.452.518.677 1.16.677 1.93zm0-11.714c0 .792-.225 1.452-.677 1.98-.45.528-1.193.792-2.227.792-1.034 0-1.77-.253-2.21-.76-.44-.505-.66-1.176-.66-2.012 0-.77.22-1.413.66-1.93.44-.518 1.176-.776 2.21-.776s1.776.258 2.227.775c.452.518.677 1.16.677 1.93zm4.52-9.207h8.482c2.22 0 4.07.3 5.543.906 1.474.606 2.65 1.436 3.53 2.492.88 1.056 1.508 2.293 1.882 3.713.374 1.42.56 2.954.56 4.604 0 1.606-.19 3.113-.577 4.52-.385 1.41-1.022 2.64-1.913 3.697-.892 1.057-2.074 1.887-3.548 2.493-1.474.605-3.3.907-5.478.907h-8.482v-23.33zm9.24 19.007c.99 0 1.838-.18 2.542-.545.703-.363 1.275-.874 1.715-1.534.44-.66.764-1.435.974-2.326.208-.892.312-1.865.312-2.92 0-1.057-.1-2.036-.297-2.938-.198-.902-.517-1.683-.957-2.343-.44-.66-1.017-1.176-1.733-1.55-.715-.374-1.59-.56-2.623-.56h-3.96v14.717h4.026zm24.685 4.686c-1.804 0-3.388-.286-4.752-.858-1.364-.572-2.508-1.375-3.432-2.41-.924-1.033-1.622-2.28-2.096-3.745-.473-1.463-.71-3.096-.71-4.9 0-1.848.243-3.52.727-5.016.483-1.496 1.198-2.772 2.144-3.828.946-1.056 2.117-1.87 3.514-2.442 1.398-.572 2.998-.858 4.802-.858 1.804 0 3.393.28 4.77.84 1.374.563 2.528 1.365 3.463 2.41.936 1.046 1.645 2.3 2.13 3.763.483 1.462.725 3.095.725 4.9 0 1.803-.242 3.453-.726 4.95-.484 1.495-1.204 2.776-2.162 3.843-.957 1.068-2.133 1.892-3.53 2.475-1.398.584-3.02.875-4.868.875zm.198-4.29c1.848 0 3.29-.65 4.323-1.947 1.034-1.298 1.55-3.245 1.55-5.84 0-2.553-.51-4.472-1.533-5.76-1.024-1.287-2.47-1.93-4.34-1.93-1.914 0-3.388.654-4.422 1.963-1.034 1.31-1.55 3.228-1.55 5.758 0 2.574.527 4.51 1.583 5.808 1.057 1.298 2.52 1.947 4.39 1.947zm21.846-19.404l5.445 13.265 5.446-13.266h5.907l1.815 23.33H135.3l-1.055-13.464-.033-2.442-5.016 11.748h-3.63l-4.983-11.715-.066 2.376L119.494 128h-5.28l1.815-23.33h5.906z" fill="#222"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="789" height="306" viewBox="0 0 789 306"><path fill="#EF4A8D" d="M0 0h789v306H0z"/><defs><path id="a" d="M0 0h789v306H0z"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path clip-path="url(#b)" fill-rule="evenodd" clip-rule="evenodd" fill="#fff" d="M338.2 153.174c0-35.93-22.308-57.872-60.155-57.872-38.03 0-60.153 21.94-60.153 57.872s22.122 57.874 60.153 57.874c37.847 0 60.154-21.942 60.154-57.874m-20.114 0c0 33.23-19.014 41.52-40.04 41.52-21.21 0-40.225-8.29-40.225-41.52 0-33.07 19.2-41.52 40.225-41.52 21.027 0 40.04 8.45 40.04 41.52"/><path clip-path="url(#b)" fill-rule="evenodd" clip-rule="evenodd" fill="#fff" d="M462.438 208.874v-98.366c0-12.954-9.273-12.903-19.36-12.903 0 51.327-.02 111.27-.02 111.27h19.38zM574.804 199.382c-3.07 7.115-6.05 8.8-13.717 9.076h-9.834l48.27-111.295h25.6l48.086 111.295-9.023-.03c-8.885.006-12.202-2.037-15.41-9.046l-36.087-87.18h-2.013l-35.873 87.18z"/><path clip-path="url(#b)" fill="#fff" d="M371.765 195.36h46.394c13.388 0 11.947 13.514 11.947 13.514l-58.342-.013-19.38.014V97.58c10.087 0 19.38-.038 19.38 12.916v84.864z"/><path clip-path="url(#b)" fill-rule="evenodd" clip-rule="evenodd" fill="#fff" d="M207.712 175.412c0-25.2-28.054-27.605-49.092-33.223-9.967-2.73-20.486-6.42-20.486-17.174 0-13.48 15.302-16.1 28.59-16.1 5.375 0 11.845 1.294 17.835 2.157 10.497 2.4 13.74-11.17 13.74-11.17-4.206-1.932-17.718-5.217-30.637-5.217-27.5 0-48.17 9.63-48.17 31.293 0 26.8 26.023 29.53 48.54 35.31 9.965 2.566 20.3 6.096 20.3 16.686 0 11.97-11.82 17.05-27.274 17.05-10.19 0-17.11-.746-27.104-4.403-11.754-4.296-18.284 10.15-18.284 10.15 9.998 6.24 25.114 10.106 41.135 10.106 41.567-.002 50.905-21.345 50.905-35.468"/><path clip-path="url(#b)" fill="#fff" d="M509.315 111.095H487.25c-13.393 0-11.953-13.515-11.953-13.515l34.02.012 19.38-.012H562.7s1.437 13.515-11.95 13.515h-22.067l.012 97.78H509.3l.015-97.78z"/></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1165.46 128" style="enable-background:new 0 0 1165.46 128;" xml:space="preserve">
<style type="text/css">
.st0{fill:#231F20;}
</style>
<polygon class="st0" points="498.93,125.55 534.27,125.55 474.27,45.91 525.8,2.21 505.97,2.21 429.59,66.97 429.59,2.21
401.4,2.21 401.4,125.55 429.59,125.55 429.59,83.79 452.71,64.19 "/>
<polygon class="st0" points="224.52,2.21 175.63,96.23 127.6,2.21 96.27,2.21 112.25,33.5 79.49,96.49 31.33,2.21 0,2.21 63,125.55
78.22,125.55 119.1,46.92 159.27,125.55 174.22,125.55 238.35,2.21 "/>
<polygon class="st0" points="592.97,114.07 592.97,66.59 664.49,66.59 664.49,55.11 592.97,55.11 592.97,13.69 671.88,13.69
680.88,2.21 564.79,2.21 564.79,125.55 680.88,125.55 671.88,114.07 "/>
<polygon class="st0" points="1079.54,114.07 1079.54,66.59 1151.07,66.59 1151.07,55.11 1079.54,55.11 1079.54,13.69 1165.46,13.69
1165.46,2.21 1051.36,2.21 1051.36,125.55 1165.46,125.55 1165.46,114.07 "/>
<path class="st0" d="M342.23,125.55h31.33l-63-123.33h-17.05l-64.13,123.33h13.83l12.83-24.67h73.59L342.23,125.55z M262.02,89.4
l31.15-59.91l30.6,59.91H262.02z"/>
<polygon class="st0" points="995.75,2.21 995.75,86.77 914.85,2.21 881.08,2.21 881.08,125.55 893.7,125.55 893.7,21.22
993.52,125.55 1008.37,125.55 1008.37,2.21 "/>
<path class="st0" d="M847.65,61.69C846.5,17.95,813.12,0,772.08,0h-0.01c-41.76,0-75.62,18.57-75.62,64s33.86,64,75.62,64v0
c0,0,0.01,0,0.01,0c40.64,0,73.79-17.6,75.54-60.43c0.05-1.16,0.08-2.34,0.08-3.57C847.7,63.2,847.68,62.43,847.65,61.69
M772.08,116.78c-29.19,0-44.84-8.8-44.84-52.78s15.66-52.79,44.84-52.79c29.19,0,44.84,8.8,44.84,52.79
S801.26,116.78,772.08,116.78"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,16 +0,0 @@
var development = {
stripe: {
publicKey: "pk_test_OmNve9H1OuORlmD4rblpjgzh"
}
}
var production = {
stripe: {
publicKey: "pk_live_xrnwdLNXbt20LMxpIDffJnnC"
}
}
module.exports = function () {
return process.env.ENV == 'development' ? development : production;
}

View File

@@ -1,98 +0,0 @@
extends templates/head
block title
| Koodiklinikka
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='/')
img(src="images/logo-new.svg", alt="Etusivu")
.header__headline
h1.header__title Yhteisö kaikille ohjelmoinnista ja ohjelmistoalasta kiinnostuneille harrastajille ja&nbsp;ammattilaisille.
block content
.content.with-feed
section
.row
h3 Tule mukaan <a target="_blank" href="https://slack.com/">Slack</a>-yhteisöömme
#invite-form.form
p.code-of-conduct Ennen liittymistä yhteisöömme varmista, että olet lukenut yhteisön <a target="_blank" href="https://github.com/koodiklinikka/code-of-conduct/blob/master/README.md">käyttäytymissäännöt</a>.
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')
.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ä <a href="https://github.com/koodiklinikka">Github-organisaatiomme sivulta</a>.
|Organisaation jäseneksi otamme kaikki Slack-yhteisömme jäsenet. Koodiklinikan projekteihin voi osallistua kuka tahansa ja muutosideat ovat aina lämpimästi tervetulleita.
#members
.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
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')
.column.column3-5
h4 Code::Stats
p.
Code::Stats is a free stats tracking service for programmers
.bread
.column.column2-5
a(href='https://reactabular.js.org/', target='_blank')
img.project-image__codestats(src='images/reactabular.png')
.column.column3-5
h4 Reactabular
p.
A framework for building the React table you need
.bread
.column.column2-5
a(href='https://avain.app', target='_blank')
img(src='images/avain.svg' style='width: 7rem')
.column.column3-5
h4 Avain.app
p.
Secure one-time password manager (PWA + Web Crypto)
#feed

View File

@@ -1,5 +0,0 @@
var host = process.env.SERVER || 'https://lit-plateau-4689.herokuapp.com/';
module.exports = function(path) {
return host + path;
}

View File

@@ -1,9 +0,0 @@
'use strict';
var React = require('react');
module.exports = React.createClass({
render() {
return <a href="mailto:info@koodiklinikka.fi">info@koodiklinikka.fi</a>;
}
});

View File

@@ -1,45 +0,0 @@
'use strict';
var React = require('react');
function clamp(min, max, value) {
return Math.min(Math.max(value, min), max);
}
module.exports = React.createClass({
getDefaultProps() {
return {
threshold: 100
};
},
getInitialState() {
return {
opacity: 0
};
},
onScroll() {
var scrollableDistance = document.body.scrollHeight - window.innerHeight,
scrollTop = window.pageYOffset || document.documentElement.scrollTop,
distanceToBottom = scrollableDistance - scrollTop;
this.setState({
opacity: clamp(0, 1, distanceToBottom / this.props.threshold)
});
},
componentDidMount() {
window.addEventListener('scroll', this.onScroll);
this.onScroll();
},
componentWillUnmount() {
window.removeEventListener('scroll', this.onScroll);
},
render() {
var style = {
opacity: this.state.opacity
};
return (
<div className="fader" style={style}></div>
)
}
});

View File

@@ -1,73 +0,0 @@
'use strict';
var React = require('react');
var request = require('axios');
var _ = require('lodash');
var transformers = require('../util');
var api = require('../api');
function throwError(err) {
setTimeout(() => {
console.log(err.stack);
throw err;
});
}
module.exports = React.createClass({
getInitialState() {
return {
messages: []
};
},
componentDidMount() {
request.get(api('feeds'))
.then((res) => {
const messages = _(res.data)
.map((messages, type) => transformers[type](messages))
.flatten()
.value();
this.setState({
messages: _(messages).sortBy('timestamp').reverse().value().slice(0, 40)
});
}).catch(throwError);
},
render() {
var messages = this.state.messages.map((message, i) => {
var image = <img src={message.image} />;
if(message.imageLink) {
image = <a target="_blank" href={message.imageLink}>{image}</a>;
}
return (
<div className="message" key={i}>
<div className="message__image">{image}</div>
<div className="message__content">
<div className="message__user">
<a href={message.userLink}>{message.user}</a>
</div>
<div className="message__body" dangerouslySetInnerHTML={{__html:message.body}}></div>
<div className="message__icon">
<i className={`fa fa-${message.type}`}></i>
</div>
<div className="message__details">
<span className="message__timestamp">
{require('timeago')(message.timestamp)}
</span>
<span className="message__meta">{message.meta}</span>
</div>
</div>
</div>
)
});
return (
<div className="feed">{messages}</div>
)
}
});

View File

@@ -1,109 +0,0 @@
'use strict';
var request = require('axios');
var React = require('react');
var classSet = require('classnames');
var api = require('../api');
var Loader = require('./loader');
module.exports = React.createClass({
getInitialState() {
return {
email: '',
submitted: false,
sending: false,
error: null
};
},
onSubmit(e) {
e.preventDefault();
this.setState({
submitted: false,
sending: true,
error: null
});
request.post(api('invites'), {
email: this.state.email.trim()
})
.then(this.handleSuccess)
.catch(this.handleError);
},
handleSuccess() {
this.setState({submitted: true, sending: false});
},
handleError(err) {
this.setState({error: err, sending: false});
},
onChange(e) {
if(e.target.value === this.state.email) {
return;
}
this.setState({
email: e.target.value,
error: null,
submitted: false
});
},
render() {
var formClasses = classSet({
'form': true,
'invite-form': true,
'has-success': this.state.submitted,
'has-error': this.state.error,
'sending': this.state.sending
});
var inputClasses = classSet({
'input': true,
'has-success': this.state.submitted,
'has-error': this.state.error
});
var feedbackMessage;
if(this.state.error || this.state.submitted) {
let messageText;
if(this.state.submitted) {
messageText = 'Kutsu lähetetty antamaasi sähköpostiosoitteeseen.';
} else if(this.state.error.status === 400 && this.state.error.data === 'invalid_email') {
messageText = 'Tarkasta syöttämäsi sähköpostiosoite';
} else if(this.state.error.status === 400 && this.state.error.data === 'already_invited') {
messageText = 'Sähköpostiosoitteeseen on jo lähetetty kutsu';
} else {
messageText = 'Jotain meni pieleen. Yritä hetken päästä uudelleen.';
}
feedbackMessage = (
<div className='form--message'>
{messageText}
</div>
);
}
return (
<form className={formClasses} onSubmit={this.onSubmit}>
<input
className={inputClasses}
type='text'
name='email'
placeholder='Email'
value={this.state.email}
onChange={this.onChange} />
<button
className='btn btn__submit'
type='submit'
title='Lähetä'
disabled={this.state.error || this.state.submitted}>
Lähetä
</button>
<div className='invite-form__loader'>
<Loader />
</div>
{feedbackMessage}
</form>
);
}
});

View File

@@ -1,12 +0,0 @@
const React = require('react');
module.exports = function Loader() {
return (
<div className='sk-folding-cube'>
<div className='sk-cube1 sk-cube'></div>
<div className='sk-cube2 sk-cube'></div>
<div className='sk-cube4 sk-cube'></div>
<div className='sk-cube3 sk-cube'></div>
</div>
);
};

View File

@@ -1,39 +0,0 @@
'use strict';
var React = require('react');
var request = require('axios');
var _ = require('lodash');
var api = require('../api');
module.exports = React.createClass({
getInitialState() {
return {
members: []
};
},
componentDidMount() {
request.get(api('members')).then(function(res) {
this.setState({
members: _.shuffle(res.data)
});
}.bind(this));
},
render() {
var members = this.state.members.map(function(member, i) {
var src = `${member.avatar_url}&s=120`;
return (
<img className="member" key={i} src={src} />
);
});
return (
<div className='members'>
<a href='https://github.com/koodiklinikka' target='_blank'>
{members}
</a>
</div>
)
}
});

View File

@@ -1,34 +0,0 @@
import React from 'react';
import MembershipInfoForm from './membershipInfoForm';
module.exports = React.createClass({
getInitialState() {
return {
signupSuccess: false
};
},
handleSignupSuccess() {
this.setState({ signupSuccess: true });
},
render() {
if (!this.state.signupSuccess) {
return (
<MembershipInfoForm onSignupSuccess={this.handleSignupSuccess} />
);
}
return (
<div>
<svg height='50' width='50' viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'>
<path
fill='#349c4a'
d='M256 6.998c-137.533 0-249 111.467-249 249 0 137.534 111.467 249 249 249s249-111.467 249-249c0-137.534-111.467-249-249-249zm0 478.08c-126.31 0-229.08-102.77-229.08-229.08 0-126.31 102.77-229.08 229.08-229.08 126.31 0 229.08 102.77 229.08 229.08 0 126.31-102.77 229.08-229.08 229.08z' />
<path
fill='#349c4a'
d='M384.235 158.192L216.92 325.518 127.86 236.48l-14.142 14.144 103.2 103.18 181.36-181.47' />
</svg>
<p> Rekisteröityminen onnistui. Tervetuloa jäseneksi!</p>
<p> Tervetuloa Koodiklinikka ry:n jäseneksi!</p>
</div>
);
}
});

View File

@@ -1,7 +0,0 @@
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-58806132-1', 'auto');
ga('send', 'pageview');

View File

@@ -1,51 +0,0 @@
'use strict';
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'));
const pathName = window.location.pathname;
document.querySelectorAll('.email').forEach((element) =>
ReactDOM.render(email(), element)
);
if (pathName == '/') {
ReactDOM.render(
inviteForm(),
document.getElementById('invite-form'));
ReactDOM.render(
fader(),
document.getElementById('fader'));
ReactDOM.render(
members(),
document.getElementById('members'));
ReactDOM.render(
feed(),
document.getElementById('feed'));
} 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'));
}

View File

@@ -1,9 +0,0 @@
@import '../../node_modules/font-awesome/less/variables.less';
@import '../../node_modules/font-awesome/less/path.less';
@import '../../node_modules/font-awesome/less/core.less';
.@{fa-css-prefix}-github:before { content: @fa-var-github; }
.@{fa-css-prefix}-slack:before { content: @fa-var-slack; }
.@{fa-css-prefix}-twitter:before { content: @fa-var-twitter; }
.@{fa-css-prefix}-linkedin:before { content: @fa-var-linkedin; }
.@{fa-css-prefix}-facebook:before { content: @fa-var-facebook; }

View File

@@ -1,70 +0,0 @@
doctype html
html
head
title
block title
// inject:css
// endinject
meta(name='description', content='Koodiklinikka on suomalainen 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='180x180', href='icons/apple-touch-icon.png')
link(rel='icon', type='image/png', sizes='32x32', href='icons/favicon-32x32.png')
link(rel='icon', type='image/png', sizes='16x16', href='icons/favicon-16x16.png')
link(rel='manifest', href='icons/site.webmanifest')
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://wakeone.co/', target='_blank')
img.sponsor.sponsor__wakeone(src='images/wakeone.svg')
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
static/icons/apple-touch-icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/icons/favicon-16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

BIN
static/icons/favicon-32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

1
static/images/avain.svg Normal file
View File

@@ -0,0 +1 @@
<svg height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m.5.5h511v511h-511z" fill="#f50" stroke="#979797"/><path d="m416 206.000625c0 60.75125-49.24875 109.999375-110 109.999375-7.0125 0-13.86875-.66375-20.516875-1.918125l-15.0075 16.88375c-2.846519 3.202358-6.926653 5.034529-11.21125 5.034375h-23.264375v25c0 8.284375-6.715625 15-15 15h-25v25c0 8.284375-6.715625 15-15 15h-70c-8.284375 0-15-6.715625-15-15v-48.786875c0-3.978125 1.580625-7.79375 4.393125-10.606875l101.12625-101.12625c-3.576875-10.84625-5.519375-22.435625-5.519375-34.48 0-60.75125 49.248125-109.999375 109.999375-110 60.930625-.000625 110.000625 49.069375 110.000625 110.000625zm-110-30.000625c0 16.56875 13.43125 30 30 30s30-13.43125 30-30-13.43125-30-30-30-30 13.43125-30 30z" fill="#fff" fill-rule="nonzero"/></g></svg>

After

Width:  |  Height:  |  Size: 866 B

BIN
static/images/codestats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1 @@
<svg height="206" viewBox="0 0 960 206" width="960" xmlns="http://www.w3.org/2000/svg"><g fill="#349c4a" fill-rule="evenodd"><path d="m635.7 65.9h37.4v136.4h-37.4zm-437.3 0v73.7c0 18.1-8.3 28.7-24.9 28.7s-24.9-10.6-24.9-28.7v-73.7h-37.8v83.1c0 35.1 26.1 55.9 63.1 55.9s63.1-20.9 63.1-55.9v-83.1zm262.7 0v73.7c0 18.1-8.3 28.7-24.9 28.7s-24.9-10.6-24.9-28.7v-73.7h-37.8v83.1c0 35.1 26.1 55.9 63.1 55.9s63.1-20.8 63.1-55.9v-83.1z"/><circle cx="653.8" cy="23.8" r="23.8"/><path d="m522.3 65.9v136.4h37.4v-102.8h52.2v-33.6zm396.5 93.8c-6.8 7.6-16.3 12.5-27.2 12.5h-.4c-19.7 0-31.7-9.8-36.3-24.9h105.1v-13.7c0-39.3-28.7-70.7-70.3-70.7-39.3 0-71.1 31.7-71.1 71.1s29.5 71.1 71.8 71.1h.8c21.9 0 39.3-8.3 51.4-20.8zm-28.7-63.9c15.9 0 27.6 7.2 32.5 21.5h-66.1c5.6-12.8 17.7-21.5 33.6-21.5zm-102.8 63.9c-6.4 6-14.7 9.8-24.2 9.8h-.8c-19.3-.4-34-15.9-34-35.5 0-19.3 14.7-35.1 34-35.5h.8c10.2 0 18.9 4.5 25.3 11.3l25.7-25.7c-12.5-13.2-30.2-21.2-51.4-21.2h-.4c-38.9.4-69.9 31.7-69.9 70.7s31 70.7 69.9 70.7h.4c20 0 37.4-7.6 49.9-19.7zm-490.2-93.8v-51h-37.4v127.8c0 44.6 14.4 59.3 57.4 59.3h32.1v-34.8h-26.8c-23.1 0-25.7-3.4-25.7-24.6v-43h52.5v-33.6c0-.1-52.1-.1-52.1-.1zm-259.7-9c0-12.9 7.9-20.4 18.5-20.4 6.8 0 12.1 3.8 14.7 7.9l22.7-22.7c-11.7-16.3-27.2-21.5-42.3-21.5-27.6 0-50.3 24.2-51 56.3v145.9h37.4v-104h48.4v-32.5h-48.4z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
static/images/hp3_bw.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

View File

Before

Width:  |  Height:  |  Size: 293 KiB

After

Width:  |  Height:  |  Size: 293 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

1
static/images/redom.svg Normal file
View File

@@ -0,0 +1 @@
<svg height="129" viewBox="0 0 143 129" width="143" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m125 75 12.5 25h-25zm0-50 12.5 25h-25zm-25 50 12.5 25h-25zm12.5-25 12.5 25h-25zm-100 0 12.5 25h-25zm25-50 12.5 25h-25zm-25 0 12.5 25h-25zm12.5 25 12.5 25h-25zm25 50 12.5 25h-25zm12.5-25 12.5 25h-25zm12.5-25 12.5 25h-25zm12.5 25 12.5 25h-25z" fill="#e8dc64"/><path d="m25 75 12.5-25h-25zm25-50 12.5-25h-25zm-25 0 12.5-25h-25zm12.5 25 12.5-25h-25zm-25 0 12.5-25h-25zm50 50 12.5-25h-25zm37.5-25 12.5-25h-25zm12.5 25 12.5-25h-25zm12.5-25 12.5-25h-25zm0-50 12.5-25h-25zm-112.5 75 12.5-25h-25zm75-50 12.5-25h-25z" fill="#fdf06f"/><path d="m25 75 12.5 25h-25zm50 0 12.5 25h-25zm-37.5-25 12.5 25h-25zm12.5-25 12.5 25h-25zm37.5-25 12.5 25h-25zm25 0 12.5 25h-25zm-12.5 25 12.5 25h-25zm-37.5-25 12.5 25h-25zm25 50 12.5 25h-25z" fill="#ba152b"/><path d="m100 25 12.5-25h-25zm-62.5 75 12.5-25h-25zm12.5-25 12.5-25h-25zm25-50 12.5-25h-25zm12.5 75 12.5-25h-25zm25-50 12.5-25h-25zm-37.5 25 12.5-25h-25zm-12.5-25 12.5-25h-25z" fill="#d31b33"/><path d="m21.78 104.67c1.76 0 3.218.186 4.373.56 1.156.374 2.074.902 2.756 1.584.68.682 1.16 1.485 1.434 2.41.276.923.413 1.946.413 3.068 0 1.54-.324 2.915-.974 4.125s-1.765 2.123-3.35 2.74l4.82 8.843h-5.644l-4.324-8.085h-3.828v8.085h-5.214v-23.33h9.537zm-4.322 11.12h3.762c.858 0 1.562-.077 2.112-.23.55-.155.99-.38 1.32-.678.33-.297.56-.66.693-1.09.132-.428.198-.917.198-1.467 0-.528-.066-1.006-.198-1.436s-.363-.787-.693-1.073-.77-.506-1.32-.66-1.254-.23-2.112-.23h-3.762v6.863zm17.457 12.21v-23.33h16.565v4.29h-11.35v5.114h9.833v4.323h-9.833v5.28h11.35v4.323h-16.564zm26.532-2.41c0 .793-.225 1.453-.677 1.98-.45.53-1.193.793-2.227.793s-1.77-.258-2.21-.775c-.44-.518-.66-1.183-.66-1.997 0-.77.22-1.412.66-1.93.44-.517 1.176-.775 2.21-.775s1.776.258 2.227.775c.452.518.677 1.16.677 1.93zm0-11.714c0 .792-.225 1.452-.677 1.98-.45.528-1.193.792-2.227.792s-1.77-.253-2.21-.76c-.44-.505-.66-1.176-.66-2.012 0-.77.22-1.413.66-1.93.44-.518 1.176-.776 2.21-.776s1.776.258 2.227.775c.452.518.677 1.16.677 1.93zm4.52-9.207h8.482c2.22 0 4.07.3 5.543.906 1.474.606 2.65 1.436 3.53 2.492s1.508 2.293 1.882 3.713.56 2.954.56 4.604c0 1.606-.19 3.113-.577 4.52-.385 1.41-1.022 2.64-1.913 3.697-.892 1.057-2.074 1.887-3.548 2.493-1.474.605-3.3.907-5.478.907h-8.482v-23.33zm9.24 19.007c.99 0 1.838-.18 2.542-.545.703-.363 1.275-.874 1.715-1.534s.764-1.435.974-2.326c.208-.892.312-1.865.312-2.92 0-1.057-.1-2.036-.297-2.938-.198-.902-.517-1.683-.957-2.343s-1.017-1.176-1.733-1.55c-.715-.374-1.59-.56-2.623-.56h-3.96v14.717h4.026zm24.685 4.686c-1.804 0-3.388-.286-4.752-.858s-2.508-1.375-3.432-2.41c-.924-1.033-1.622-2.28-2.096-3.745-.473-1.463-.71-3.096-.71-4.9 0-1.848.243-3.52.727-5.016.483-1.496 1.198-2.772 2.144-3.828s2.117-1.87 3.514-2.442c1.398-.572 2.998-.858 4.802-.858s3.393.28 4.77.84c1.374.563 2.528 1.365 3.463 2.41.936 1.046 1.645 2.3 2.13 3.763.483 1.462.725 3.095.725 4.9 0 1.803-.242 3.453-.726 4.95-.484 1.495-1.204 2.776-2.162 3.843-.957 1.068-2.133 1.892-3.53 2.475-1.398.584-3.02.875-4.868.875zm.198-4.29c1.848 0 3.29-.65 4.323-1.947 1.034-1.298 1.55-3.245 1.55-5.84 0-2.553-.51-4.472-1.533-5.76-1.024-1.287-2.47-1.93-4.34-1.93-1.914 0-3.388.654-4.422 1.963-1.034 1.31-1.55 3.228-1.55 5.758 0 2.574.527 4.51 1.583 5.808 1.057 1.298 2.52 1.947 4.39 1.947zm21.846-19.404 5.445 13.265 5.446-13.266h5.907l1.815 23.33h-5.249l-1.055-13.464-.033-2.442-5.016 11.748h-3.63l-4.983-11.715-.066 2.376-1.023 13.5h-5.28l1.815-23.33h5.906z" fill="#222"/></g></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

1
static/images/solita.svg Normal file
View File

@@ -0,0 +1 @@
<svg height="306" viewBox="0 0 789 306" width="789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h789v306h-789z"/></clipPath><path d="m0 0h789v306h-789z" fill="#ef4a8d"/><g fill="#fff"><path clip-path="url(#a)" clip-rule="evenodd" d="m338.2 153.174c0-35.93-22.308-57.872-60.155-57.872-38.03 0-60.153 21.94-60.153 57.872s22.122 57.874 60.153 57.874c37.847 0 60.154-21.942 60.154-57.874m-20.114 0c0 33.23-19.014 41.52-40.04 41.52-21.21 0-40.225-8.29-40.225-41.52 0-33.07 19.2-41.52 40.225-41.52 21.027 0 40.04 8.45 40.04 41.52" fill-rule="evenodd"/><path clip-path="url(#a)" clip-rule="evenodd" d="m462.438 208.874v-98.366c0-12.954-9.273-12.903-19.36-12.903 0 51.327-.02 111.27-.02 111.27h19.38zm112.366-9.492c-3.07 7.115-6.05 8.8-13.717 9.076h-9.834l48.27-111.295h25.6l48.086 111.295-9.023-.03c-8.885.006-12.202-2.037-15.41-9.046l-36.087-87.18h-2.013l-35.873 87.18z" fill-rule="evenodd"/><path clip-path="url(#a)" d="m371.765 195.36h46.394c13.388 0 11.947 13.514 11.947 13.514l-58.342-.013-19.38.014v-111.295c10.087 0 19.38-.038 19.38 12.916v84.864z"/><path clip-path="url(#a)" clip-rule="evenodd" d="m207.712 175.412c0-25.2-28.054-27.605-49.092-33.223-9.967-2.73-20.486-6.42-20.486-17.174 0-13.48 15.302-16.1 28.59-16.1 5.375 0 11.845 1.294 17.835 2.157 10.497 2.4 13.74-11.17 13.74-11.17-4.206-1.932-17.718-5.217-30.637-5.217-27.5 0-48.17 9.63-48.17 31.293 0 26.8 26.023 29.53 48.54 35.31 9.965 2.566 20.3 6.096 20.3 16.686 0 11.97-11.82 17.05-27.274 17.05-10.19 0-17.11-.746-27.104-4.403-11.754-4.296-18.284 10.15-18.284 10.15 9.998 6.24 25.114 10.106 41.135 10.106 41.567-.002 50.905-21.345 50.905-35.468" fill-rule="evenodd"/><path clip-path="url(#a)" d="m509.315 111.095h-22.065c-13.393 0-11.953-13.515-11.953-13.515l34.02.012 19.38-.012h34.003s1.437 13.515-11.95 13.515h-22.067l.012 97.78h-19.395z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1 @@
<svg enable-background="new 0 0 1165.46 128" viewBox="0 0 1165.46 128" xmlns="http://www.w3.org/2000/svg"><g fill="#231f20"><path d="m498.93 125.55h35.34l-60-79.64 51.53-43.7h-19.83l-76.38 64.76v-64.76h-28.19v123.34h28.19v-41.76l23.12-19.6z"/><path d="m224.52 2.21-48.89 94.02-48.03-94.02h-31.33l15.98 31.29-32.76 62.99-48.16-94.28h-31.33l63 123.34h15.22l40.88-78.63 40.17 78.63h14.95l64.13-123.34z"/><path d="m592.97 114.07v-47.48h71.52v-11.48h-71.52v-41.42h78.91l9-11.48h-116.09v123.34h116.09l-9-11.48z"/><path d="m1079.54 114.07v-47.48h71.53v-11.48h-71.53v-41.42h85.92v-11.48h-114.1v123.34h114.1v-11.48z"/><path d="m342.23 125.55h31.33l-63-123.33h-17.05l-64.13 123.33h13.83l12.83-24.67h73.59zm-80.21-36.15 31.15-59.91 30.6 59.91z"/><path d="m995.75 2.21v84.56l-80.9-84.56h-33.77v123.34h12.62v-104.33l99.82 104.33h14.85v-123.34z"/><path d="m847.65 61.69c-1.15-43.74-34.53-61.69-75.57-61.69h-.01c-41.76 0-75.62 18.57-75.62 64s33.86 64 75.62 64h.01c40.64 0 73.79-17.6 75.54-60.43.05-1.16.08-2.34.08-3.57 0-.8-.02-1.57-.05-2.31m-75.57 55.09c-29.19 0-44.84-8.8-44.84-52.78s15.66-52.79 44.84-52.79c29.19 0 44.84 8.8 44.84 52.79s-15.66 52.78-44.84 52.78"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,7 +1,7 @@
headerHeight = 400px
.header
background url('../images/jumbo.jpg')
background url('/static/images/jumbo.jpg')
background-position bottom center
background-size cover
height headerHeight
@@ -42,7 +42,7 @@ headerHeight = 400px
@media screen and (max-width: 810px)
margin-left auto
margin-right auto
img
width: 100%;

28
styles/icons.less Normal file
View File

@@ -0,0 +1,28 @@
@import "../node_modules/font-awesome/less/variables.less";
@import "../node_modules/font-awesome/less/core.less";
@font-face {
font-family: "FontAwesome";
src: url("../node_modules/font-awesome/fonts/fontawesome-webfont.woff2?v=@{fa-version}")
format("woff2"),
url("../node_modules/font-awesome/fonts/fontawesome-webfont.woff?v=@{fa-version}")
format("woff");
font-weight: normal;
font-style: normal;
}
.@{fa-css-prefix}-github:before {
content: @fa-var-github;
}
.@{fa-css-prefix}-slack:before {
content: @fa-var-slack;
}
.@{fa-css-prefix}-twitter:before {
content: @fa-var-twitter;
}
.@{fa-css-prefix}-linkedin:before {
content: @fa-var-linkedin;
}
.@{fa-css-prefix}-facebook:before {
content: @fa-var-facebook;
}

View File

@@ -351,7 +351,7 @@ footer
display block
.bread-img
background url('../images/hp3_bw.jpg')
background url('/static/images/hp3_bw.jpg')
background-size cover
border-radius 160px
opacity 0.85

View File

@@ -1,7 +0,0 @@
'use strict';
import {jsdom} from 'jsdom';
const document = global.document = jsdom('<html><head></head><body></body></html>');
const window = global.window = document.defaultView;
global.navigator = window.navigator = {};