Merge branch 'master' into 'production'
Prod deploy * NextJS * Contact Page * CSS fixes See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!26
@@ -1,33 +1,16 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", {"modules": false}],
|
||||
"react"
|
||||
"next/babel"
|
||||
],
|
||||
"plugins": [
|
||||
"react-hot-loader/babel",
|
||||
[
|
||||
"babel-plugin-styled-components",
|
||||
{ "ssr": true, "displayName": true, "preprocess": false }
|
||||
]
|
||||
],
|
||||
"env": {
|
||||
"production": {
|
||||
"plugins": [
|
||||
[
|
||||
[
|
||||
"babel-plugin-styled-components",
|
||||
{ "ssr": true, "displayName": true, "preprocess": false }
|
||||
]
|
||||
],
|
||||
"presets": ["minify"]
|
||||
},
|
||||
"test": {
|
||||
"plugins": [
|
||||
[
|
||||
"babel-plugin-styled-components",
|
||||
{ "ssr": true, "displayName": true, "preprocess": false }
|
||||
]
|
||||
],
|
||||
"presets": ["env", "react"]
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"ssr": true,
|
||||
"displayName": true,
|
||||
"preprocess": false,
|
||||
"pure": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,15 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.env
|
||||
.next
|
||||
.vscode
|
||||
e2e-screenshots
|
||||
node_modules
|
||||
tests
|
||||
.env*
|
||||
.eslintrc.json
|
||||
.gitattributes
|
||||
.gitignore
|
||||
.gitlab-ci.yml
|
||||
.nvmrc
|
||||
.stylelintrc
|
||||
LICENSE
|
||||
README*
|
||||
stack-compose*
|
||||
@@ -0,0 +1 @@
|
||||
NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
@@ -1 +0,0 @@
|
||||
API_URL=https://api.dev.sik.party/api
|
||||
@@ -5,7 +5,8 @@
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"airbnb",
|
||||
"airbnb/hooks",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
@@ -32,73 +33,37 @@
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"arrow-parens": ["off", "always"],
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
{
|
||||
"arrays": "ignore",
|
||||
"objects": "ignore",
|
||||
"imports": "ignore",
|
||||
"exports": "ignore",
|
||||
"functions": "ignore"
|
||||
}
|
||||
],
|
||||
"consistent-return": "warn",
|
||||
"global-require": "warn",
|
||||
"linebreak-style": "off",
|
||||
"camelcase": "off",
|
||||
"max-len": [
|
||||
"off",
|
||||
"warn",
|
||||
160
|
||||
],
|
||||
"no-console": "warn",
|
||||
"no-extra-boolean-cast": "warn",
|
||||
"no-param-reassign": "error",
|
||||
"no-shadow": "warn",
|
||||
"no-unused-vars": "off",
|
||||
"no-useless-constructor": "warn",
|
||||
"object-curly-newline": "error",
|
||||
"prefer-destructuring": "warn",
|
||||
"prefer-template": "error",
|
||||
"quote-props": ["error", "consistent"],
|
||||
"quotes": [
|
||||
],
|
||||
"no-mixed-operators": "off",
|
||||
"no-shadow": "off",
|
||||
"no-use-before-define": "off",
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"import/no-unresolved": "off",
|
||||
"import/first": "off",
|
||||
"import/order": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/newline-after-import": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"indent": ["error", 2],
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/alt-text": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/iframe-has-title": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
"jsx-a11y/label-has-for": "off",
|
||||
"react/display-name": "off",
|
||||
"react/destructuring-assignment": "off",
|
||||
"react/jsx-closing-bracket-location": "off",
|
||||
"react/jsx-closing-tag-location": "off",
|
||||
"react/jsx-curly-brace-presence": "off",
|
||||
"react/jsx-first-prop-new-line": "off",
|
||||
"react/jsx-filename-extension": "off",
|
||||
"react/jsx-indent-props": "off",
|
||||
"react/jsx-one-expression-per-line": "off",
|
||||
"react/jsx-wrap-multilines": "off",
|
||||
"react/no-access-state-in-setstate": "warn",
|
||||
"react/prop-types": "off",
|
||||
"react/prefer-stateless-function": "off",
|
||||
"react/self-closing-comp": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/indent": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn"
|
||||
],
|
||||
"import/extensions": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/order": "off",
|
||||
"import/prefer-default-export": "warn",
|
||||
"operator-linebreak": "off",
|
||||
"no-unused-vars": "off",
|
||||
"react/jsx-filename-extension": "off",
|
||||
"react/jsx-one-expression-per-line": "off",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react/prop-types": "off",
|
||||
"@typescript-eslint/no-shadow": "error",
|
||||
// Temp
|
||||
"react/no-array-index-key": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"jsx-a11y/alt-text": "off",
|
||||
"jsx-a11y/iframe-has-title": "off",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,40 @@
|
||||
.idea/
|
||||
dist/
|
||||
node_modules/
|
||||
src/**/*.jsx
|
||||
.vscode/
|
||||
tests/jest/__coverage__/
|
||||
tests/jest/**/*.jsx
|
||||
tests/testcafe/screenshots
|
||||
.vscode/
|
||||
e2e-screenshots/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
/public/favicons/
|
||||
@@ -1,5 +1,6 @@
|
||||
stages:
|
||||
- setup
|
||||
- audit
|
||||
- lint
|
||||
- build
|
||||
- test
|
||||
@@ -7,7 +8,7 @@ stages:
|
||||
- deploy
|
||||
|
||||
install:
|
||||
image: node:12
|
||||
image: node:14
|
||||
stage: setup
|
||||
script:
|
||||
- npm ci
|
||||
@@ -16,53 +17,67 @@ install:
|
||||
- node_modules
|
||||
expire_in: 1 week
|
||||
|
||||
audit:
|
||||
image: node:14
|
||||
needs: ["install"]
|
||||
stage: audit
|
||||
script:
|
||||
- npm audit --audit-level=critical
|
||||
|
||||
es:lint:
|
||||
image: node:12
|
||||
image: node:14
|
||||
needs: ["install"]
|
||||
stage: lint
|
||||
script:
|
||||
- npm run lint:es
|
||||
|
||||
sass:lint:
|
||||
image: node:12
|
||||
css:lint:
|
||||
image: node:14
|
||||
needs: ["install"]
|
||||
stage: lint
|
||||
script:
|
||||
- npm run lint:sass
|
||||
- npm run lint:css
|
||||
|
||||
# test:unit:
|
||||
# image: node:12
|
||||
# image: node:14
|
||||
# stage: test
|
||||
# script:
|
||||
# - npm run test:unit
|
||||
|
||||
build:
|
||||
image: node:12
|
||||
image: node:14
|
||||
needs: ["install"]
|
||||
stage: build
|
||||
script:
|
||||
- API_URL=https://api.dev.sik.party/api npm run build
|
||||
- NODE_ENV=test npm run build
|
||||
dependencies:
|
||||
- install
|
||||
artifacts:
|
||||
paths:
|
||||
- dist
|
||||
- .next
|
||||
expire_in: 1 week
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
paths:
|
||||
- node_modules/
|
||||
- .next/cache/
|
||||
|
||||
test:e2e:
|
||||
image: circleci/node:12-browsers
|
||||
image: circleci/node:14-browsers
|
||||
needs: ["install", "build"]
|
||||
stage: test
|
||||
only:
|
||||
- master
|
||||
script:
|
||||
- npm run test:e2e
|
||||
artifacts:
|
||||
paths:
|
||||
- e2e-screenshots
|
||||
expire_in: 1 week
|
||||
when: on_failure
|
||||
|
||||
publish:dev:
|
||||
stage: publish
|
||||
image: docker:stable
|
||||
needs: ["build", "test:e2e", "es:lint", "sass:lint"]
|
||||
needs: ["build", "test:e2e", "es:lint", "css:lint"]
|
||||
services:
|
||||
- docker:stable-dind
|
||||
only:
|
||||
@@ -93,7 +108,7 @@ deploy:dev:
|
||||
- master
|
||||
environment:
|
||||
name: dev
|
||||
url: dev.sik.party
|
||||
url: dev.sahkoinsinoorikilta.fi
|
||||
variables:
|
||||
DOCKER_HOST: $DEV_CI_DOCKER_HOST
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
@@ -115,7 +130,7 @@ deploy:prod:
|
||||
- production
|
||||
environment:
|
||||
name: production
|
||||
url: sika.sik.party
|
||||
url: prod.sahkoinsinoorikilta.fi
|
||||
when: manual
|
||||
variables:
|
||||
DOCKER_HOST: $CI_DOCKER_HOST
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
{
|
||||
"extends": "stylelint-config-recommended-scss"
|
||||
"processors": [
|
||||
"stylelint-processor-styled-components"
|
||||
],
|
||||
"extends": [
|
||||
"stylelint-config-recommended",
|
||||
"stylelint-config-styled-components"
|
||||
],
|
||||
"syntax": "css"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
FROM node:12-alpine as builder
|
||||
FROM node:14-alpine as builder
|
||||
|
||||
RUN apk add --no-cache libpng-dev gcc make g++ zlib-dev bash lcms2-dev autoconf automake libtool nasm
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --only-prod
|
||||
RUN npm install
|
||||
|
||||
COPY . ./
|
||||
ENV API_URL https://api.dev.sik.party/api
|
||||
COPY tsconfig.json next-env.d.ts .babelrc next.config.js ./
|
||||
COPY src src/
|
||||
COPY public public/
|
||||
COPY types types/
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
RUN npm run build
|
||||
|
||||
FROM fnichol/uhttpd AS server
|
||||
FROM node:14-alpine as server
|
||||
WORKDIR /www
|
||||
COPY package.json package-lock.json next.config.js ./
|
||||
COPY --from=builder .next .next
|
||||
COPY --from=builder node_modules node_modules
|
||||
COPY --from=builder public public
|
||||
|
||||
RUN npm prune --production
|
||||
|
||||
EXPOSE 3000
|
||||
COPY --from=builder /app/dist /www
|
||||
ENTRYPOINT ["/usr/sbin/run_uhttpd", "-f", "-p", "3000", "-h", "/www", "-E", "/"]
|
||||
ENTRYPOINT ["npm", "run", "serve"]
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
FROM node:12-alpine as builder
|
||||
FROM node:14-alpine as builder
|
||||
|
||||
RUN apk add --no-cache libpng-dev gcc make g++ zlib-dev bash lcms2-dev autoconf automake libtool nasm
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --only-prod
|
||||
RUN npm install
|
||||
|
||||
COPY . ./
|
||||
ENV API_URL https://sika.sik.party/api
|
||||
COPY tsconfig.json next-env.d.ts .babelrc next.config.js ./
|
||||
COPY src src/
|
||||
COPY public public/
|
||||
COPY types types/
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NEXT_PUBLIC_API_URL=https://api.sahkoinsinoorikilta.fi/api
|
||||
RUN npm run build
|
||||
|
||||
FROM fnichol/uhttpd AS server
|
||||
FROM node:14-alpine as server
|
||||
WORKDIR /www
|
||||
COPY package.json package-lock.json next.config.js ./
|
||||
COPY --from=builder .next .next
|
||||
COPY --from=builder node_modules node_modules
|
||||
COPY --from=builder public public
|
||||
|
||||
RUN npm prune --production
|
||||
|
||||
EXPOSE 3000
|
||||
COPY --from=builder /app/dist /www
|
||||
ENTRYPOINT ["/usr/sbin/run_uhttpd", "-f", "-p", "3000", "-h", "/www", "-E", "/"]
|
||||
ENTRYPOINT ["npm", "run", "serve"]
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@@ -17,7 +17,8 @@ Minimal starter kit with hot module replacement (HMR) for rapid development.
|
||||
## Installation
|
||||
|
||||
1. Clone/download repo
|
||||
2. `npm install`
|
||||
2. Install node v14
|
||||
3. `npm install`
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Shared config (dev and prod)
|
||||
const {resolve} = require("path");
|
||||
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
|
||||
const StyleLintPlugin = require("stylelint-webpack-plugin");
|
||||
const webpack = require('webpack');
|
||||
const Dotenv = require("dotenv-webpack");
|
||||
|
||||
module.exports = function (env, argv) {
|
||||
const config = {};
|
||||
config.output = {
|
||||
publicPath: "/",
|
||||
};
|
||||
|
||||
config.resolve = {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
||||
plugins: [new TsconfigPathsPlugin()]
|
||||
};
|
||||
config.context = resolve(__dirname, "../../src");
|
||||
config.module = {
|
||||
rules: [],
|
||||
};
|
||||
config.module.rules.push({
|
||||
test: /\.js$/,
|
||||
use: ["babel-loader", "source-map-loader"],
|
||||
exclude: /node_modules/
|
||||
});
|
||||
config.module.rules.push({
|
||||
test: /\.tsx?$/,
|
||||
use: ["babel-loader", "ts-loader"]
|
||||
});
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loaders: [
|
||||
"file-loader?hash=sha512&digest=hex&name=assets/img/[hash].[ext]",
|
||||
"image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false"
|
||||
]
|
||||
});
|
||||
|
||||
const envVars = {};
|
||||
Object.keys(process.env).forEach((key) => {
|
||||
envVars[`process.env.${key}`] = JSON.stringify(process.env[key]);
|
||||
});
|
||||
|
||||
config.plugins = [
|
||||
new webpack.DefinePlugin(envVars),
|
||||
new StyleLintPlugin(),
|
||||
new Dotenv({
|
||||
path: "./.env"
|
||||
}),
|
||||
];
|
||||
|
||||
config.performance = {
|
||||
hints: false
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
@@ -1,72 +0,0 @@
|
||||
// Development config
|
||||
const merge = require("webpack-merge");
|
||||
const webpack = require("webpack");
|
||||
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
|
||||
const commonConfig = require("./common.js");
|
||||
|
||||
module.exports = function (env, argv) {
|
||||
const base = commonConfig(env, argv);
|
||||
base.mode = "development";
|
||||
base.entry = [
|
||||
"react-hot-loader/patch", // Activate HMR for React
|
||||
"webpack-dev-server/client?http://0.0.0.0:3000", // Bundle the client for webpack-dev-server and connect to the provided endpoint
|
||||
"webpack/hot/only-dev-server", // Bundle the client for hot reloading, only- means to only hot reload for successful updates
|
||||
"./index.tsx" // The entry point of our app
|
||||
];
|
||||
base.devServer = {
|
||||
hot: true, // Enable HMR on the server
|
||||
historyApiFallback: true,
|
||||
host: '0.0.0.0',
|
||||
port: '3000',
|
||||
allowedHosts: [
|
||||
'.sik.party',
|
||||
'.sahkoinsinoorikilta.fi',
|
||||
],
|
||||
};
|
||||
base.devtool = "cheap-module-eval-source-map";
|
||||
base.plugins = base.plugins.concat([
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: "./assets/img/favicon.png",
|
||||
prefix: "assets/icons/",
|
||||
icons: {
|
||||
android: false,
|
||||
appleIcon: false,
|
||||
appleStartup: false,
|
||||
coast: false,
|
||||
favicons: true,
|
||||
firefox: false,
|
||||
opengraph: false,
|
||||
twitter: false,
|
||||
yandex: false,
|
||||
windows: false
|
||||
}
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(), // Enable HMR globally
|
||||
new webpack.NamedModulesPlugin(), // Prints more readable module names in the browser console on HMR updates
|
||||
new HtmlWebpackPlugin({template: "index.html.ejs"}),
|
||||
]);
|
||||
|
||||
base.module.rules.push({
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", {loader: "css-loader", options: {importLoaders: 1}}, "postcss-loader"]
|
||||
});
|
||||
base.module.rules.push({
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
"style-loader",
|
||||
{loader: "css-loader", options: {importLoaders: 1}},
|
||||
"postcss-loader",
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
// Prefer `dart-sass`
|
||||
implementation: require('sass'),
|
||||
},
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
return base;
|
||||
};
|
||||
@@ -1,127 +0,0 @@
|
||||
// Production config
|
||||
const path = require("path");
|
||||
const merge = require("webpack-merge");
|
||||
|
||||
const resolve = path.resolve;
|
||||
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
|
||||
const nodeExternals = require('webpack-node-externals');
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const WebpackCdnPlugin = require('webpack-cdn-plugin');
|
||||
|
||||
/* NOTE: This is a list of all routes that are prerendered for production use.
|
||||
Please list all routes that contain search engine accessible content, i.e.,
|
||||
stuff that you would like to find with a Google Search. */
|
||||
|
||||
const commonConfig = require("./common");
|
||||
|
||||
module.exports = function (env, argv) {
|
||||
const base = commonConfig(env, argv);
|
||||
base.mode = "production";
|
||||
base.entry = "./index.tsx";
|
||||
base.output = {
|
||||
path: resolve(__dirname, "../../dist"),
|
||||
publicPath: "/",
|
||||
};
|
||||
base.devtool = "source-map";
|
||||
base.plugins.push(
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: "assets/styles.css?[hash]",
|
||||
}),
|
||||
);
|
||||
|
||||
if (env.platform === 'server') {
|
||||
base.entry = "./server/index.ts";
|
||||
base.output.filename = 'js/server.js?[hash]';
|
||||
base.target = 'node';
|
||||
base.externals = [nodeExternals()];
|
||||
|
||||
base.module.rules.push({
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{loader: "css-loader", options: {importLoaders: 1}},
|
||||
"postcss-loader"
|
||||
],
|
||||
});
|
||||
|
||||
base.module.rules.push({
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{loader: "css-loader", options: {importLoaders: 1}},
|
||||
"postcss-loader",
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
// Prefer `dart-sass`
|
||||
implementation: require('sass'),
|
||||
},
|
||||
},
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
else if (env.platform === 'client') {
|
||||
base.entry = './client/index.ts';
|
||||
base.output.filename = 'js/client.js?[hash]';
|
||||
base.target = 'web';
|
||||
|
||||
base.module.rules.push({
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{loader: "css-loader", options: {importLoaders: 1}},
|
||||
"postcss-loader"
|
||||
],
|
||||
});
|
||||
|
||||
base.module.rules.push({
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{loader: "css-loader", options: {importLoaders: 1}},
|
||||
"postcss-loader",
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
// Prefer `dart-sass`
|
||||
implementation: require('sass'),
|
||||
},
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
base.plugins.push(
|
||||
new HtmlWebpackPlugin({
|
||||
template: "index.html.ejs",
|
||||
}),
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: "./assets/img/favicon.png",
|
||||
prefix: "assets/icons/",
|
||||
icons: {
|
||||
android: true,
|
||||
appleIcon: true,
|
||||
appleStartup: true,
|
||||
coast: true,
|
||||
favicons: true,
|
||||
firefox: true,
|
||||
opengraph: true,
|
||||
twitter: true,
|
||||
yandex: true,
|
||||
windows: true
|
||||
}
|
||||
}),
|
||||
new WebpackCdnPlugin({
|
||||
modules: [
|
||||
{ name: 'react', var: 'React', path: `umd/react.${process.env.NODE_ENV}.min.js` },
|
||||
{ name: 'react-dom', var: 'ReactDOM', path: `umd/react-dom.${process.env.NODE_ENV}.min.js` },
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return base;
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
frontend:
|
||||
build: .
|
||||
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-frontend/frontend-prod
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
API_URL: ${API_URL}
|
||||
command: npm run start-prod
|
||||
@@ -1,17 +0,0 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
frontend:
|
||||
build: .
|
||||
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-frontend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
API_URL: ${API_URL}
|
||||
|
||||
mock-backend:
|
||||
build: .
|
||||
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-frontend/frontend-mock-backend
|
||||
ports:
|
||||
- "1234:1234"
|
||||
command: npm run mock-backend
|
||||
@@ -0,0 +1,2 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
@@ -0,0 +1,30 @@
|
||||
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
|
||||
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
});
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
target: "server",
|
||||
images: {
|
||||
domains: [
|
||||
"sahkoinsinoorikilta.fi",
|
||||
"prod.sahkoinsinoorikilta.fi",
|
||||
"api.sahkoinsinoorikilta.fi",
|
||||
"static.sahkoinsinoorikilta.fi",
|
||||
"api.dev.sahkoinsinoorikilta.fi",
|
||||
"placehold.it"
|
||||
],
|
||||
},
|
||||
webpack: (config) => {
|
||||
config.plugins.push(
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: "./public/favicon.png",
|
||||
outputPath: "../public/favicons",
|
||||
prefix: "/favicons",
|
||||
inject: false
|
||||
})
|
||||
);
|
||||
return config;
|
||||
}
|
||||
});
|
||||
@@ -20,118 +20,73 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://sik.ayy.fi",
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production npm-run-all build:client build:server",
|
||||
"build:server": "webpack -p --config=configs/webpack/prod.js --env.platform=server",
|
||||
"build:client": "webpack -p --config=configs/webpack/prod.js --env.platform=client",
|
||||
"lint": "npm run lint:es && npm run lint:sass",
|
||||
"build": "next build",
|
||||
"lint": "npm run lint:es && npm run lint:css",
|
||||
"lint:es": "eslint \"./src/**/*.{ts,tsx}\"",
|
||||
"lint:es:fix": "eslint --fix \"./src/**/*.{ts,tsx}\"",
|
||||
"lint:sass": "stylelint \"./src/**/*.{scss,css}\"",
|
||||
"start": "npm run start-dev",
|
||||
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js",
|
||||
"serve": "node dist/js/server.js",
|
||||
"start-prod": "npm run build && npm run serve",
|
||||
"lint:css": "stylelint \"./src/**/*.{ts,tsx}\"",
|
||||
"start": "next dev",
|
||||
"start-prod": "next start --port ${SERVER_PORT:=80}",
|
||||
"serve": "next start --port 3000",
|
||||
"test": "npm run test:e2e:verbose",
|
||||
"test:e2e": "npm-run-all -p -r serve test:e2e:testcafe",
|
||||
"test:e2e:verbose": "npm-run-all -p -r serve test:e2e:testcafe:verbose",
|
||||
"test:e2e:testcafe": "testcafe -S -s 'tests/testcafe/screenshots' --app-init-delay 2000 chrome:headless tests/testcafe",
|
||||
"test:e2e:testcafe:verbose": "testcafe -S -s 'tests/testcafe/screenshots' --app-init-delay 2000 chrome tests/testcafe"
|
||||
"test:e2e": "testcafe --list-browsers && npm-run-all -p -r serve testcafe",
|
||||
"test:e2e:verbose": "npm-run-all -p -r serve testcafe:verbose",
|
||||
"testcafe": "testcafe --skip-js-errors -S -s 'e2e-screenshots' --app-init-delay 2000 chrome:headless tests/testcafe",
|
||||
"testcafe:verbose": "testcafe --skip-js-errors -S -s 'e2e-screenshots' --app-init-delay 2000 chrome tests/testcafe",
|
||||
"build-analyze": "ANALYZE=true npm run build"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-push": "npm-run-all -s lint build test"
|
||||
"pre-push": "npm run lint"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "24.0.22",
|
||||
"@types/js-cookie": "2.2.4",
|
||||
"@types/node": "10.14.7",
|
||||
"@types/react": "16.8.18",
|
||||
"@types/react-beautiful-dnd": "13.0.0",
|
||||
"@types/react-csv": "1.1.1",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-helmet": "6.0.0",
|
||||
"@types/react-jsonschema-form": "1.7.3",
|
||||
"@types/react-router-dom": "5.1.5",
|
||||
"@types/shortid": "0.0.29",
|
||||
"@types/styled-components": "5.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.8.2",
|
||||
"@typescript-eslint/parser": "^4.8.2",
|
||||
"babel-cli": "6.26.0",
|
||||
"babel-core": "6.26.3",
|
||||
"babel-loader": "7.1.5",
|
||||
"@typescript-eslint/eslint-plugin": "4.8.2",
|
||||
"@typescript-eslint/parser": "4.8.2",
|
||||
"babel-plugin-styled-components": "1.10.7",
|
||||
"babel-preset-env": "1.7.0",
|
||||
"babel-preset-minify": "0.4.3",
|
||||
"babel-preset-react": "6.24.1",
|
||||
"compression": "1.7.4",
|
||||
"css-loader": "2.1.1",
|
||||
"dotenv": "6.2.0",
|
||||
"dotenv-webpack": "1.7.0",
|
||||
"eslint": "^7.14.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"express": "4.17.0",
|
||||
"favicons-webpack-plugin": "1.0.2",
|
||||
"file-loader": "4.2.0",
|
||||
"fs-extra": "6.0.1",
|
||||
"helmet": "3.21.2",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"eslint": "7.14.0",
|
||||
"eslint-config-airbnb": "18.2.1",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||
"eslint-plugin-react": "7.21.5",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"husky": "1.3.1",
|
||||
"image-webpack-loader": "6.0.0",
|
||||
"mini-css-extract-plugin": "0.4.5",
|
||||
"module-to-cdn": "3.1.2",
|
||||
"morgan": "1.9.1",
|
||||
"npm-run-all": "4.1.5",
|
||||
"postcss-loader": "2.1.6",
|
||||
"react": "16.8.6",
|
||||
"react-addons-test-utils": "15.6.2",
|
||||
"react-dom": "16.8.6",
|
||||
"react-hot-loader": "4.8.8",
|
||||
"sass": "1.29.0",
|
||||
"sass-loader": "7.1.0",
|
||||
"serve": "11.3.2",
|
||||
"style-loader": "0.21.0",
|
||||
"stylelint": "11.1.1",
|
||||
"stylelint-config-recommended-scss": "4.0.0",
|
||||
"stylelint-config-standard": "19.0.0",
|
||||
"stylelint-scss": "3.12.1",
|
||||
"stylelint-webpack-plugin": "1.0.3",
|
||||
"testcafe": "1.6.1",
|
||||
"testcafe-react-selectors": "2.1.0",
|
||||
"ts-loader": "7.0.5",
|
||||
"tsconfig-paths-webpack-plugin": "3.2.0",
|
||||
"stylelint": "13.8.0",
|
||||
"stylelint-config-recommended": "3.0.0",
|
||||
"stylelint-config-styled-components": "0.1.1",
|
||||
"stylelint-processor-styled-components": "1.10.0",
|
||||
"testcafe": "1.10.1",
|
||||
"typescript": "3.9.5",
|
||||
"typescript-plugin-styled-components": "1.4.4",
|
||||
"uglifyjs-webpack-plugin": "1.3.0",
|
||||
"webpack": "4.41.2",
|
||||
"webpack-cdn-plugin": "3.2.0",
|
||||
"webpack-cli": "3.3.10",
|
||||
"webpack-dev-middleware": "3.7.2",
|
||||
"webpack-dev-server": "3.11.0",
|
||||
"webpack-merge": "4.2.2",
|
||||
"webpack-node-externals": "1.7.2"
|
||||
"typescript-plugin-styled-components": "1.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.19.0",
|
||||
"@next/bundle-analyzer": "10.0.5",
|
||||
"axios": "0.21.1",
|
||||
"date-fns": "2.0.0-alpha.27",
|
||||
"favicons-webpack-plugin": "4.2.0",
|
||||
"js-cookie": "2.2.0",
|
||||
"lodash": "4.17.20",
|
||||
"next": "10.0.5",
|
||||
"normalize.css": "8.0.1",
|
||||
"query-string": "6.5.0",
|
||||
"react-beautiful-dnd": "10.1.1",
|
||||
"react": "17.0.1",
|
||||
"react-beautiful-dnd": "13.0.0",
|
||||
"react-csv": "2.0.3",
|
||||
"react-helmet": "5.2.1",
|
||||
"react-jsonschema-form": "^1.8.1",
|
||||
"react-markdown": "4.3.1",
|
||||
"react-dom": "17.0.1",
|
||||
"react-jsonschema-form": "1.8.1",
|
||||
"react-markdown": "5.0.3",
|
||||
"react-mde": "11.0.0",
|
||||
"react-router-dom": "4.3.1",
|
||||
"react-router-hash-link": "1.2.1",
|
||||
"shortid": "2.2.14",
|
||||
"styled-components": "5.1.1"
|
||||
},
|
||||
"postcss": {}
|
||||
"styled-components": "5.1.1",
|
||||
"swr": "0.3.11"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
@@ -1,4 +1,3 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
-` o` .s h` -///.
|
||||
.o+/o `d m /s```y: -+:.
|
||||
@@ -45,14 +44,4 @@
|
||||
:y`/Nm. /do/- /M` Nm/.M: sd-`/M:`hy++d+
|
||||
/- .y+oN: sd NyhhM: om/-+m- .:-`
|
||||
`-:- o+ h/ /h: -/+:`
|
||||
-->
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,800,900" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
-->
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 425 B |
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 615 B After Width: | Height: | Size: 615 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"name_fi": "Hallitus",
|
||||
"name_en": "Board",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Puheenjohtaja",
|
||||
"name_en": "Chairman",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Johannes Ora"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Sihteeri",
|
||||
"name_en": "Secretary",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Salla Lyytikäinen",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Rahastonhoitaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Santeri Huhtala",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Fuksitoimikunnan Puheenjohtaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Toni Ojala",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Fuksitoimikunnan puheenjohtajan adjutantti",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Toni Lyttinen",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Hovimestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Eveliina Ahonen",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Hovineuvos",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Melissa Dönmez",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "ISOvastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Heidi Mäkitalo",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Hyvinvointimestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Sauli Norja",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Opintomestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Simo Hakanummi",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Teknologiamestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Oskari Ponkala",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Ulkomestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Oliver Hiekkamies",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Yrityssuhdemestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Otto Julkunen",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name_fi": "Hyvinvointitoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Hyvinvointimestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Sauli Norja" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Kulttuurivastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Juha Anttila" },
|
||||
{ "name": "Aino Suomi" },
|
||||
{ "name": "Nestori Ylönjoki" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Liikuntavastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Elmeri Pälikkö" },
|
||||
{ "name": "Joel Wickström" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Kiltahuonevastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Ilari Ojakorpi" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Kiltapäiväkerhovastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Samuel Laine" },
|
||||
{ "name": "Aleksanteri Vesala" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Retkivastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Jarno Mustonen" },
|
||||
{ "name": "Suvi Karanta" },
|
||||
{ "name": "Jesse Räisänen" },
|
||||
{ "name": "Mikko Suhonen" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name_fi": "Mediatoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Puheenjohtaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{
|
||||
"name": "Salla Lyytikäinen",
|
||||
"phone_number": null,
|
||||
"email": null,
|
||||
"image": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Päätoimittaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Sasu Salasti" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Toimittaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Tuukka Syrjänen" },
|
||||
{ "name": "Ilmari Kasvi" },
|
||||
{ "name": "Elias Hirvonen" },
|
||||
{ "name": "Miika Koskela" },
|
||||
{ "name": "Taneli Myllykangas" },
|
||||
{ "name": "Emmaleena Ahonen" },
|
||||
{ "name": "Ville-Pekka Laakkonen" },
|
||||
{ "name": "Sofia Öhman" },
|
||||
{ "name": "Nestori Yrjönkoski" },
|
||||
{ "name": "Jami Hyytiäinen" },
|
||||
{ "name": "Aleksanteri Vesala" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Toimittaja & Valokuvaaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Kiia-Einola" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Taittaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Aino Suomi" },
|
||||
{ "name": "Olli Komulainen" },
|
||||
{ "name": "Emilia Kortelainen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Taittaja & Valokuvaaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Jonna Tammikivi" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Valokuvaaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Suvi Karanta" },
|
||||
{ "name": "Mikko Haaparanta" },
|
||||
{ "name": "Johannes Viirimäki" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Valokuvaaja & Graafikko",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Kalle Petäjäaho" },
|
||||
{ "name": "Maria Pöllä" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Videokuvaaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Aaro Rasilainen" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name_fi": "Opintotoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Opintomestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Simo Hakanummi" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Opintovastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Miina-Maija Simonen" },
|
||||
{ "name": "Tomi Valkonen" },
|
||||
{ "name": "Leo Lahti" },
|
||||
{ "name": "Ville-Pekka Laakkonen" },
|
||||
{ "name": "Samu Nyman" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Abimarkkinointi Vastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Ilkka Huttu" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name_fi": "Ohjelmatoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Hovimestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Eveliina Ahonen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Hovineuvos",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Melissa Dönmez" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Emäntä",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Oona Karjalainen" },
|
||||
{ "name": "Emilia Kortemäki" },
|
||||
{ "name": "Venla Vastamäki" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Isäntä",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Henry Jaakkola" },
|
||||
{ "name": "Sakke Kangas" },
|
||||
{ "name": "Otto Torkkeli" },
|
||||
{ "name": "Tommi Oinen" },
|
||||
{ "name": "Eero Ketonen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Lukkari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Tuomo Leino" },
|
||||
{ "name": "Hami Hyytinen" },
|
||||
{ "name": "Tuomas Pajunpää" },
|
||||
{ "name": "Samuel Laine" },
|
||||
{ "name": "Toni Miilunpalo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Lukkarikisällit",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Jesse Räisänen" },
|
||||
{ "name": "Eino Laakso" },
|
||||
{ "name": "Sakari Harjunpää" },
|
||||
{ "name": "Niilo Ojala" },
|
||||
{ "name": "Ilkka Huttu" },
|
||||
{ "name": "Akseli Järvinen" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name_fi": "Pajatoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Pajavastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Karl Lipping" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Pajakisälli",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Tommi Sytelä" },
|
||||
{ "name": "Eerikki Eskola" },
|
||||
{ "name": "Arkadii Kolchin" },
|
||||
{ "name": "Samu Nyman" },
|
||||
{ "name": "Konsta Langi" },
|
||||
{ "name": "Johannes Viirimäki" },
|
||||
{ "name": "Justus Ojala" },
|
||||
{ "name": "Ville Tujunen" },
|
||||
{ "name": "Antti Tarkka" },
|
||||
{ "name": "Pyry Vaara" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name_fi": "SIK100-toimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Puheenjohtaja",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Erna Virtanen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Webivastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Jaakko Koskela" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Markkinointivastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Sasu Salasti" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Yritysvastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Juuli Leppänen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Seminaarivastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Sini Huhtinen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Kevätkarnevaalimajuri",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Olli Komulainen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "PoTa100-pääjuhlatirehtööri",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Emmaleena Ahonen" },
|
||||
{ "name": "Jonna Tammikivi" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "PoTa100-jatkokuvernööri",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Mikael Liimatainen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "PoTa100-sillistirehtööri",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Tuomo Leino" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name_fi": "Teknologiatoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Teknologiamestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Oskari Ponkala" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Tekniikkavastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Antti Mäki" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Web-Kisälli",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Ilari Ojakorpi" },
|
||||
{ "name": "Leo Lahti" },
|
||||
{ "name": "Jyri Korhonen" },
|
||||
{ "name": "Tuukka Syrjänen" },
|
||||
{ "name": "Emmaleena Ahonen" },
|
||||
{ "name": "Mikko Suhonen" },
|
||||
{ "name": "Jaakko Koskela" },
|
||||
{ "name": "Justus Ojala" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name_fi": "Ulkotoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Ulkomestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Oliver Hiekkamies" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Kv-ISOvastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Elias Hirvonen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "International Helper",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Ville-Pekka Laakkonen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Ulkosuhdevastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Leo Muller" },
|
||||
{ "name": "Eino Tyrvänen" },
|
||||
{ "name": "Pekka Aho" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Ulkowanhus",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Jyri Korhonen" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name_fi": "Yrityssuhdetoimikunta",
|
||||
"name_en": "",
|
||||
"roles": [
|
||||
{
|
||||
"name_fi": "Yrityssuhdemestari",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Otto Julkunen" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Excursiopäävastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Henry Gustafsson" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name_fi": "Yrityssuhdevastaava",
|
||||
"name_en": "",
|
||||
"representatives": [
|
||||
{ "name": "Ilkka Huttu" },
|
||||
{ "name": "Arkadii Kolchin" },
|
||||
{ "name": "Oskari Luukkonen" },
|
||||
{ "name": "Niilo Ojala" },
|
||||
{ "name": "Emma Reinikainen" },
|
||||
{ "name": "Melina Sundell" },
|
||||
{ "name": "Elma Tuohimetsä" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
$font: 'Montserrat', sans-serif;
|
||||
|
||||
$colors: (
|
||||
// Name Color ?Hover?
|
||||
dark-blue: 'dark-blue' #002d3a,
|
||||
light-blue: 'light-blue' #bfdbd9,
|
||||
white1: 'white1' #fff,
|
||||
black1: 'black1' #000,
|
||||
grey1: 'grey1' #d4d0c7,
|
||||
grey2: 'grey2' #efece4,
|
||||
orange1: 'orange1' #d57a2d,
|
||||
orange2: 'orange2' #dd934e,
|
||||
blue1: 'blue1' #57b2df,
|
||||
light-turquoise: 'light-turquoise' #beddeb,
|
||||
green1: 'green1' #c0dcd9,
|
||||
sand: 'sand' #fdf9d7
|
||||
);
|
||||
|
||||
@function color($label) {
|
||||
$color: map-get($colors, $label);
|
||||
|
||||
@if $color {
|
||||
@return nth($color, 2);
|
||||
}
|
||||
|
||||
@warn 'No specified color for '#{$label}'; add your own to _colors.scss';
|
||||
@return null;
|
||||
}
|
||||
|
||||
@function hover($label) {
|
||||
$color: map-get($colors, $label);
|
||||
|
||||
@if $color {
|
||||
@return nth($color, 2);
|
||||
}
|
||||
|
||||
@warn 'No specified hover color for '#{$label}'; add your own to _colors.scss';
|
||||
@return lighten($color, 20%);
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from "react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import Routes from "../routes";
|
||||
|
||||
export default () => (
|
||||
<BrowserRouter>
|
||||
<Routes />
|
||||
</BrowserRouter>
|
||||
);
|
||||
@@ -1,7 +0,0 @@
|
||||
import React from "react";
|
||||
import { hydrate } from "react-dom";
|
||||
import App from "./App";
|
||||
|
||||
const elem = document.getElementById("root");
|
||||
|
||||
hydrate(React.createElement(App), elem);
|
||||
@@ -39,7 +39,7 @@ const Container = styled.div`
|
||||
`;
|
||||
|
||||
const Panel = styled.div<{visible?: boolean}>`
|
||||
margin-top: ${(p) => p.visible ? "0" : "-100%"};
|
||||
margin-top: ${(p) => (p.visible ? "0" : "-100%")};
|
||||
|
||||
display: flex;
|
||||
max-height: 15rem;
|
||||
@@ -59,7 +59,7 @@ const Accordion: React.FC<AccordionProps> = ({ title, children }) => {
|
||||
|
||||
const handleClick = () => {
|
||||
setOpen(!isOpen);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Container>
|
||||
<button type="button" onClick={handleClick}>
|
||||
@@ -73,6 +73,6 @@ const Accordion: React.FC<AccordionProps> = ({ title, children }) => {
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Accordion;
|
||||
|
||||
@@ -10,14 +10,14 @@ const Icon = styled.div<AccordionIconProps>`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${(p) => p.open ? colors.orange1 : colors.blue1};
|
||||
background-color: ${(p) => (p.open ? colors.orange1 : colors.blue1)};
|
||||
color: ${colors.white};
|
||||
min-width: 40px;
|
||||
max-width: 40px;
|
||||
min-height: 40px;
|
||||
max-height: 40px;
|
||||
min-width: 2.5rem;
|
||||
max-width: 2.5rem;
|
||||
min-height: 2.5rem;
|
||||
max-height: 2.5rem;
|
||||
margin: 0.2em;
|
||||
font-size: 40px;
|
||||
font-size: 2.5rem;
|
||||
|
||||
${(p) => p.open && (`
|
||||
span {
|
||||
@@ -27,7 +27,7 @@ const Icon = styled.div<AccordionIconProps>`
|
||||
|
||||
`;
|
||||
|
||||
const AccordionIcon: React.FC<AccordionIconProps> = ({ open } ) => (
|
||||
const AccordionIcon: React.FC<AccordionIconProps> = ({ open }) => (
|
||||
<Icon open={open}>
|
||||
<span>+</span>
|
||||
</Icon>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
const Icon = "/img/add-icon.png";
|
||||
|
||||
const AddIcon: React.FC = () => (
|
||||
<Image
|
||||
src={Icon}
|
||||
width={20}
|
||||
height={20}
|
||||
alt="Add"
|
||||
/>
|
||||
);
|
||||
|
||||
export default AddIcon;
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { ComponentProps } from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors }from "@theme/colors";
|
||||
import Anchor from "@components/Anchor";
|
||||
import AddIcon from "@assets/img/add-icon.png";
|
||||
import { colors } from "@theme/colors";
|
||||
import { Link } from "@components/index";
|
||||
import AddIcon from "@components/AddIcon";
|
||||
|
||||
const Link = styled(Anchor)`
|
||||
const StyledLink = styled(Link)`
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
@@ -14,22 +14,21 @@ const Link = styled(Anchor)`
|
||||
color: ${colors.orange2};
|
||||
}
|
||||
|
||||
img {
|
||||
margin-right: 8px;
|
||||
margin-top: -2px;
|
||||
width: 20px;
|
||||
& > div {
|
||||
margin-right: 8px !important;
|
||||
margin-top: -2px !important;
|
||||
}
|
||||
`;
|
||||
|
||||
type AddLinkProps = ComponentProps<typeof Anchor> & {
|
||||
type AddLinkProps = ComponentProps<typeof Link> & {
|
||||
text: string;
|
||||
}
|
||||
|
||||
const AddLink: React.FC<AddLinkProps> = ({ text, ...props }) => (
|
||||
<Link {...props}>
|
||||
<img src={AddIcon} />
|
||||
<StyledLink passHref {...props}>
|
||||
<AddIcon />
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
</StyledLink>
|
||||
);
|
||||
|
||||
export default AddLink;
|
||||
export default AddLink;
|
||||
|
||||
@@ -5,14 +5,16 @@ import { colors } from "@theme/colors";
|
||||
|
||||
const Header = styled.header`
|
||||
background-color: ${colors.darkBlue};
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
flex: 1 0 auto;
|
||||
margin: 0.5rem auto;
|
||||
max-width: 600px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: 0.5rem auto;
|
||||
padding: 2rem;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
fill: ${colors.white};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Anchor from "@components/Anchor";
|
||||
import { colors } from "@theme/colors";
|
||||
import { Link } from "@components/index";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
interface AdminSidebarProps {
|
||||
path: string;
|
||||
@@ -13,23 +14,23 @@ const SideBar = styled.nav`
|
||||
margin-right: 1rem;
|
||||
background-color: ${colors.blue1};
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
margin-right: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Anchor)<{path: string}>`
|
||||
const StyledLink = styled(Link)<{$path: string}>`
|
||||
padding: 1rem 3rem 1rem 1rem;
|
||||
letter-spacing: 3px;
|
||||
text-transform: uppercase;
|
||||
line-height: 20px;
|
||||
line-height: 1.25;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
color: ${colors.white};
|
||||
border-left: 4px solid transparent;
|
||||
|
||||
${p => p.path === p.to && `
|
||||
${(p) => p.$path === p.to && `
|
||||
border-left: 4px solid ${colors.white};
|
||||
`}
|
||||
|
||||
@@ -37,20 +38,20 @@ const StyledLink = styled(Anchor)<{path: string}>`
|
||||
border-left: 4px solid ${colors.white};
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
`;
|
||||
|
||||
const AdminSidebar: React.FC<AdminSidebarProps> = ({ path }) => (
|
||||
<SideBar>
|
||||
<StyledLink to="/admin" path={path}>Home ›</StyledLink>
|
||||
<StyledLink to="/admin/events" path={path}>Events ›</StyledLink>
|
||||
<StyledLink to="/admin/feed" path={path}>Feed ›</StyledLink>
|
||||
<StyledLink to="/admin/signups" path={path}>Signup forms ›</StyledLink>
|
||||
<StyledLink to="/admin/jobads" path={path}>Job advertisements ›</StyledLink>
|
||||
<StyledLink to="https://static.sika.sik.party/admin" path={path}>Files ›</StyledLink>
|
||||
<StyledLink id="admin-sidebar-logout" to="/admin/logout" path={path}>Logout ›</StyledLink>
|
||||
<StyledLink to="/admin" passHref $path={path}>Home ›</StyledLink>
|
||||
<StyledLink to="/admin/events" passHref $path={path}>Events ›</StyledLink>
|
||||
<StyledLink to="/admin/feed" passHref $path={path}>Feed ›</StyledLink>
|
||||
<StyledLink to="/admin/signups" passHref $path={path}>Signup forms ›</StyledLink>
|
||||
<StyledLink to="/admin/jobads" passHref $path={path}>Job advertisements ›</StyledLink>
|
||||
<StyledLink to="https://static.sahkoinsinoorikilta.fi/admin" passHref $path={path}>Files ›</StyledLink>
|
||||
<StyledLink data-e2e="admin-sidebar-logout" to="/admin/logout" passHref $path={path}>Logout ›</StyledLink>
|
||||
</SideBar>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { HashLink } from "react-router-hash-link";
|
||||
|
||||
interface AnchorProps extends React.HTMLAttributes<HTMLAnchorElement> {
|
||||
to: string;
|
||||
}
|
||||
|
||||
const Anchor: React.FC<AnchorProps> = ({ to, ...props }) => {
|
||||
if (to.startsWith("/")) {
|
||||
return (
|
||||
<Link to={to} {...props} />
|
||||
);
|
||||
} else if (to.startsWith("#")) {
|
||||
return (
|
||||
<HashLink to={to} {...props} />
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<a href={to} {...props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Anchor;
|
||||
@@ -4,22 +4,18 @@ import { colors } from "@theme/colors";
|
||||
|
||||
interface ButtonProps {
|
||||
onClick: () => void;
|
||||
type: "hero" | "filled" | "filter" | "bordered";
|
||||
buttonStyle: "hero" | "filled" | "filter" | "bordered";
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
const StyledButton = styled.button<ButtonProps>`
|
||||
const StyledButton = styled.button<{ $selected: boolean }>`
|
||||
border-radius: none;
|
||||
padding: 0.8rem 2rem;
|
||||
margin: 0.5rem;
|
||||
font-size: 13px;
|
||||
font-size: 0.8rem;
|
||||
background: none;
|
||||
text-transform: uppercase;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&.hero {
|
||||
background-color: ${colors.darkBlue};
|
||||
color: ${colors.blue1};
|
||||
@@ -38,7 +34,7 @@ const StyledButton = styled.button<ButtonProps>`
|
||||
}
|
||||
|
||||
&.bordered {
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
color: ${colors.blue1};
|
||||
border: 1px solid ${colors.blue1};
|
||||
@@ -50,7 +46,7 @@ const StyledButton = styled.button<ButtonProps>`
|
||||
font-weight: 300;
|
||||
border: 2px solid ${colors.grey1};
|
||||
|
||||
${(p) => p.selected && (`
|
||||
${(p) => p.$selected && (`
|
||||
background-color: ${colors.grey1};
|
||||
color: ${colors.white};
|
||||
`)}
|
||||
@@ -66,8 +62,10 @@ const StyledButton = styled.button<ButtonProps>`
|
||||
}
|
||||
`;
|
||||
|
||||
const Button: React.FC<ButtonProps> = ({ type, selected, onClick, ...props }) => (
|
||||
<StyledButton type="button" onClick={onClick} className={type} selected={selected} {...props} />
|
||||
)
|
||||
const Button: React.FC<ButtonProps> = ({
|
||||
buttonStyle, selected, onClick, ...props
|
||||
}) => (
|
||||
<StyledButton type="button" onClick={onClick} className={buttonStyle} $selected={selected} {...props} />
|
||||
);
|
||||
|
||||
export default Button;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import Anchor from "@components/Anchor";
|
||||
import { Link } from "@components/index";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
interface WrappedCardProps {
|
||||
title: string;
|
||||
@@ -13,41 +15,27 @@ interface WrappedCardProps {
|
||||
buttonOnClick?: () => void;
|
||||
}
|
||||
|
||||
interface CardProps {
|
||||
title: string;
|
||||
start_time: string;
|
||||
text: string;
|
||||
img: JSX.Element;
|
||||
button: JSX.Element;
|
||||
}
|
||||
|
||||
const StyledCard = styled.article`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: ${colors.black};
|
||||
margin: 1rem;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
max-height: 300px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-size: 1rem;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 0 0.5rem;
|
||||
font-weight: 200;
|
||||
line-height: 22px;
|
||||
line-height: 1.375;
|
||||
}
|
||||
|
||||
p:first-of-type {
|
||||
color: ${colors.orange1};
|
||||
font-size: 0.9rem !important;
|
||||
font-weight: 600 !important;
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
@media screen and (max-width: ${breakpoints.medium}) {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,62 +46,58 @@ const StyledCard = styled.article`
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
a {
|
||||
margin-top: auto;
|
||||
|
||||
padding: 0.8rem 2rem;
|
||||
margin: 0.5rem;
|
||||
font-size: 13px;
|
||||
background: none;
|
||||
text-transform: uppercase;
|
||||
background-color: ${colors.blue1};
|
||||
color: ${colors.white};
|
||||
font-weight: 800;
|
||||
letter-spacing: 1.5px;
|
||||
border: none;
|
||||
button {
|
||||
cursor: pointer;
|
||||
|
||||
padding: 0.8rem 2rem;
|
||||
margin: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
background: none;
|
||||
text-transform: uppercase;
|
||||
background-color: ${colors.blue1};
|
||||
color: ${colors.white};
|
||||
font-weight: 800;
|
||||
letter-spacing: 1.5px;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Card: React.FC<CardProps> = ({ title, start_time, text, img, button }) => (
|
||||
<StyledCard>
|
||||
{img}
|
||||
<p>{start_time}</p>
|
||||
<h3>{title}</h3>
|
||||
<p>{text}</p>
|
||||
{button}
|
||||
</StyledCard>
|
||||
);
|
||||
|
||||
const WrappedCard: React.FC<WrappedCardProps> = ({ title, text, link, image, imageAlt, start_time, buttonOnClick }) => {
|
||||
const WrappedCard: React.FC<WrappedCardProps> = ({
|
||||
title, text, link, image, imageAlt, start_time, buttonOnClick, ...props
|
||||
}) => {
|
||||
const options = {
|
||||
day: "numeric",
|
||||
month: "numeric",
|
||||
year: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit"
|
||||
minute: "2-digit",
|
||||
};
|
||||
const datetime = new Date(start_time).toLocaleString("fi-FI", options);
|
||||
const img = image ? (
|
||||
<img src={image} alt={imageAlt} />
|
||||
<Image src={image} alt={imageAlt} layout="responsive" width={0} height={0} objectFit="scale-down" />
|
||||
) : null;
|
||||
|
||||
const button = (
|
||||
<Anchor to={link}>
|
||||
<button onClick={buttonOnClick}>
|
||||
Lue lisää ›
|
||||
<Link to={link}>
|
||||
<button type="button" onClick={buttonOnClick}>
|
||||
Lue lisää ›
|
||||
</button>
|
||||
</Anchor>
|
||||
</Link>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={title}
|
||||
start_time={datetime}
|
||||
text={text}
|
||||
img={img}
|
||||
button={button}
|
||||
/>
|
||||
<StyledCard {...props}>
|
||||
{img}
|
||||
<p>{datetime}</p>
|
||||
<h3>{title}</h3>
|
||||
<p>{text}</p>
|
||||
{button}
|
||||
</StyledCard>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default WrappedCard;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Occupation } from "@models/Contacts";
|
||||
import ContactCard from "./ContactCard";
|
||||
import { Committee } from "@views/ContactsPage/ContactsPageView";
|
||||
import { colors } from "@theme/colors";
|
||||
|
||||
const blank_profile = "/img/blank_profile.png";
|
||||
|
||||
interface CommitteeContainerProps {
|
||||
name_fi: string;
|
||||
name_en: string;
|
||||
contacts: Occupation[];
|
||||
committee: Committee;
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
color: ${colors.darkBlue};
|
||||
|
||||
@@ -21,36 +22,34 @@ const Container = styled.div`
|
||||
justify-content: center;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3px;
|
||||
line-height: 14px;
|
||||
line-height: 0.75;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const CommitteeContainer: React.FC<CommitteeContainerProps> = ({ name_fi, name_en, contacts }) => (
|
||||
const CommitteeContainer: React.FC<CommitteeContainerProps> = ({ committee }) => (
|
||||
<Container>
|
||||
<p>
|
||||
{name_fi}
|
||||
</p>
|
||||
<p>
|
||||
{name_en}
|
||||
{committee.name_fi || committee.name_en}
|
||||
</p>
|
||||
<div>
|
||||
{contacts.map(occupation => (
|
||||
occupation.officials.map(official => (
|
||||
{committee.roles.map((role) => (
|
||||
role.representatives.map((representative) => (
|
||||
<ContactCard
|
||||
key={official.first_name}
|
||||
first_name={official.first_name}
|
||||
last_name={official.last_name}
|
||||
phone={official.phone_number}
|
||||
email={official.email}
|
||||
image={official.image}
|
||||
role={occupation.role}
|
||||
key={representative.name}
|
||||
name={representative.name}
|
||||
phone={representative.phone_number}
|
||||
email={representative.email}
|
||||
// conditional image for dev
|
||||
image={!(committee.name_en === "Board") ? representative.image : blank_profile}
|
||||
role_fi={role.name_fi}
|
||||
role_en={role.name_en}
|
||||
/>
|
||||
))
|
||||
))}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import styled from "styled-components";
|
||||
import blank_profile from "@assets/img/blank_profile.png";
|
||||
import { Role } from "@models/Contacts";
|
||||
import { colors } from "@theme/colors";
|
||||
|
||||
const Card = styled.article`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-flow: row nowrap;
|
||||
padding: 0.5rem;
|
||||
color: ${colors.darkBlue};
|
||||
margin: 1rem;
|
||||
width: 19rem;
|
||||
`;
|
||||
|
||||
const ImageContainer = styled.div`
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
|
||||
& > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
img {
|
||||
padding: 0.5rem !important;
|
||||
border-radius: 50%;
|
||||
}
|
||||
`;
|
||||
@@ -30,41 +30,51 @@ const ImageContainer = styled.div`
|
||||
const Info = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
justify-content: flex-start;
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
align-items: flex-start;
|
||||
padding: 0.25rem;
|
||||
color: ${colors.darkBlue};
|
||||
|
||||
& > p {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& > h1 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
interface ContactCardProps {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
phone: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
image: string;
|
||||
role: Role;
|
||||
role_fi: string;
|
||||
role_en: string;
|
||||
}
|
||||
|
||||
const ContactCard: React.FC<ContactCardProps> = ({ first_name, last_name, phone, email, image, role }) => {
|
||||
const fullName = `${first_name} ${last_name}`;
|
||||
return(
|
||||
<Card>
|
||||
const ContactCard: React.FC<ContactCardProps> = ({
|
||||
name, phone, email, image, role_fi, role_en,
|
||||
}) => (
|
||||
<Card>
|
||||
{image ? (
|
||||
<ImageContainer>
|
||||
<img
|
||||
src={image || blank_profile}
|
||||
alt={fullName}
|
||||
<Image
|
||||
src={image}
|
||||
alt={name}
|
||||
layout="fill"
|
||||
objectFit="scale-down"
|
||||
/>
|
||||
</ImageContainer>
|
||||
<Info>
|
||||
<p>{fullName}</p>
|
||||
<p>{phone}</p>
|
||||
<p>{email}</p>
|
||||
<p>{role.name_fi}</p>
|
||||
<p>{role.name_en}</p>
|
||||
</Info>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
) : null}
|
||||
<Info>
|
||||
<h1>{name}</h1>
|
||||
<p>{role_fi || role_en}</p>
|
||||
{phone ? <p>{phone}</p> : null}
|
||||
{email ? <p>{email}</p> : null}
|
||||
</Info>
|
||||
</Card>
|
||||
);
|
||||
|
||||
export default ContactCard;
|
||||
|
||||
@@ -13,7 +13,7 @@ const Box = styled.div`
|
||||
margin-top: 0.8rem;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 30px;
|
||||
top: 2.5rem;
|
||||
z-index: 20;
|
||||
|
||||
a {
|
||||
@@ -23,7 +23,9 @@ const Box = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const DropDownBox: React.FC<DropDownBoxProps> = ({ children, onMouseEnter, onMouseLeave, visible }) => (
|
||||
const DropDownBox: React.FC<DropDownBoxProps> = ({
|
||||
children, onMouseEnter, onMouseLeave, visible,
|
||||
}) => (
|
||||
<Box
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default (): JSX.Element => (
|
||||
<>
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicons/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="48x48" href="/favicons/favicon-48x48.png" />
|
||||
<link rel="manifest" href="/favicons/manifest.json" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="theme-color" content="#fff" />
|
||||
<meta name="application-name" content="web2.0-frontend" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/favicons/apple-touch-icon-57x57.png" />
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/favicons/apple-touch-icon-60x60.png" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/favicons/apple-touch-icon-72x72.png" />
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/favicons/apple-touch-icon-76x76.png" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/favicons/apple-touch-icon-114x114.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/favicons/apple-touch-icon-120x120.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/favicons/apple-touch-icon-144x144.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/favicons/apple-touch-icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="167x167" href="/favicons/apple-touch-icon-167x167.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon-180x180.png" />
|
||||
<link rel="apple-touch-icon" sizes="1024x1024" href="/favicons/apple-touch-icon-1024x1024.png" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="web2.0-frontend" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-640x1136.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-750x1334.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-828x1792.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1125x2436.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1242x2208.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1242x2688.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1536x2048.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1668x2224.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1668x2388.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-2048x2732.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/favicons/apple-touch-startup-image-1620x2160.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-1136x640.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-1334x750.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-1792x828.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2436x1125.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2208x1242.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2688x1242.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2048x1536.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2224x1668.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2388x1668.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2732x2048.png" />
|
||||
<link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/favicons/apple-touch-startup-image-2160x1620.png" />
|
||||
<link rel="icon" type="image/png" sizes="228x228" href="/favicons/coast-228x228.png" />
|
||||
<meta name="msapplication-TileColor" content="#fff" />
|
||||
<meta name="msapplication-TileImage" content="/favicons/mstile-144x144.png" />
|
||||
<meta name="msapplication-config" content="/favicons/browserconfig.xml" />
|
||||
<link rel="yandex-tableau-widget" href="/favicons/yandex-browser-manifest.json" />
|
||||
</>
|
||||
);
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
import FooterContent from "./FooterContent";
|
||||
|
||||
const StyledFooter = styled.footer`
|
||||
@@ -16,27 +17,27 @@ const CopyRight = styled.div`
|
||||
background-color: ${colors.black};
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
padding: 1rem 0;
|
||||
|
||||
p {
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
text-decoration: underline;
|
||||
padding: 0.4rem 0;
|
||||
font-size: 14px;
|
||||
font-size: 0.8rem;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
`;
|
||||
@@ -49,6 +50,6 @@ const Footer: React.FC = () => (
|
||||
<a href="mailto:sik-vtmk@list.ayy.fi">webmaster: sik-vtmk@list.ayy.fi</a>
|
||||
</CopyRight>
|
||||
</StyledFooter>
|
||||
)
|
||||
);
|
||||
|
||||
export default Footer;
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Anchor from "../Anchor";
|
||||
import { colors } from "@theme/colors";
|
||||
import { Link } from "@components/index";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
const Columns = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
& > div > div {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
display: flex;
|
||||
|
||||
& > div:first-of-type {
|
||||
padding: 48px 0;
|
||||
padding: 3rem 0;
|
||||
flex: 2 1;
|
||||
|
||||
& > * {
|
||||
padding: 0 24px;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: ${colors.lightBlue};
|
||||
padding: 24px 0;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -25,7 +34,7 @@ const Content = styled.div`
|
||||
display: block;
|
||||
text-decoration: underline;
|
||||
padding: 0.4rem 0;
|
||||
font-size: 14px;
|
||||
font-size: 0.8rem;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
@@ -36,26 +45,18 @@ const Content = styled.div`
|
||||
color: ${colors.white};
|
||||
margin: 0;
|
||||
padding: 0.4rem 0;
|
||||
font-size: 14px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const MarginSpace = styled.div`
|
||||
max-width: 600px;
|
||||
max-width: 67%;
|
||||
margin: auto;
|
||||
`;
|
||||
|
||||
const Columns = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
& > div > div {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const Map = styled.div`
|
||||
flex: 1;
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
display: none;
|
||||
}
|
||||
iframe {
|
||||
@@ -78,14 +79,14 @@ const FooterContent: React.FC = () => (
|
||||
<div>
|
||||
<p>Y-tunnus: 1627010-1</p>
|
||||
<p>sik-hallitus@list.ayy.fi</p>
|
||||
<Anchor to="/yhteystiedot">Yhteystiedot</Anchor>
|
||||
<Link to="/yhteystiedot">Yhteystiedot</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Anchor to="/jaseneksi">Jäseneksi</Anchor>
|
||||
<Anchor to="/palaute">Palaute</Anchor>
|
||||
<Anchor to="https://static.sika.sik.party">Arkisto</Anchor>
|
||||
<Anchor to="https://static.sika.sik.party">Materiaalipankki</Anchor>
|
||||
<Link to="/jaseneksi">Jäseneksi</Link>
|
||||
<Link to="/palaute">Palaute</Link>
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi">Arkisto</Link>
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi">Materiaalipankki</Link>
|
||||
</div>
|
||||
</Columns>
|
||||
</MarginSpace>
|
||||
@@ -99,6 +100,6 @@ const FooterContent: React.FC = () => (
|
||||
/>
|
||||
</Map>
|
||||
</Content>
|
||||
)
|
||||
);
|
||||
|
||||
export default FooterContent;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default (): JSX.Element => (
|
||||
<head dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
`<!--
|
||||
-\` o\` .s h\` -///.
|
||||
.o+/o \`d m /s\`\`\`y: -+:.
|
||||
.///. \`-. -m\` m::/ \`d /s.\`.y: \`h..o/
|
||||
/o.\`.y- ..\` :\` \`\`\`\` \` .://. ho+/o- \`y.
|
||||
./+ +o\` .y- . \` .y- \`+//\`
|
||||
/+y/ :/+/. .-::/+++++//:--\` o. \`.h--/
|
||||
\` \`/s. hNNMMMMMMMMMMNNy o::d\` -o-
|
||||
:+. . .\` mMMMMMMMMMMMMMMd --- \`/y++
|
||||
-o+-o: \`-/oNMMMMMMMMMMMMMMNo:-\` :o:\` .
|
||||
\`:--..\`-/\` \`-+ymNMMMMMMMMMMMMMMMMMMMMNmy+-\` \`\` \`\` -++++.
|
||||
\`h+/y/: \`\` .odNMMMMMMMMMNNmmmmmmNNMMMMMMMMMNdo..:sdd/ d. .h
|
||||
\`sh\` \`+mds:.:yNMMMMMMMmds+:-...\`\`...-:+ydmMMMMMMMNmMmh+- . o/--+o
|
||||
\`\`\`\`\`\`+\` .hMMMMMNMMMMMMMms:. .:yMMMMNds:.\`-+hms .::. \`--
|
||||
\`yo/y+/ :mMMMMMMMMMMMNy:\` \`\`... \`.+ymMNh+-\`.:sdMNds:\`\` -/oom:
|
||||
.oos /NMMMMMMMMMMNy- \`-oydmNNM :h+:odNNms/.\`.+hmMNh+-\`.:sh- .\`\`y/.:\`
|
||||
-s\` /MMMMMMMMMMMd: \`-smMMMMMMMM /MMMNh+-\`\`:sdMNms:.\`.+hNMNh: hy+/-
|
||||
.NMMMMMMMMMMy\` -hMMMMMMMMMMM /MMo.\`./ymMNh+-\`\`:sdMNms:\`\`./\` \`
|
||||
yMMMMMMMMMMy \`sMMMMMMMMMMMMM /MM+odNNmy/.\`.+yNMNh+- \`-odMNo
|
||||
\`:odMMMMMMd\` \`hMMMMMMMMMMMMMM /MMMNdo-\`\`-odMMms/\` \`/ymMMdo-
|
||||
\`NMMMMM- sMMMMMMMMMMMMMMM /MMN+\`\`/yNMNdo- \`-odMMMMMN\`
|
||||
/MMMMMd .MMMMMMMMMMMMMMMM /MMMMNMMNy/\` \`/ymMMdmMMMMM/
|
||||
sMMMMMo +MMMMMMMMMMMMMMMM /MMMMdmMMdsodMMNy/\` oMMMMMs
|
||||
yMMMMM+ +MMMMMMMMMMMMMNdh /MMm/ \`oNMMMMMy. +MMMMMy
|
||||
oMMMMMs .MMMMMMMMMMMs- /MMMMNMMNy/:smMMdo: sMMMMMo
|
||||
/MMMMMd oMMMMMMMMN- /MMMMdmMMmo:\` .+hNMNNMMMMM/
|
||||
\`+ /hy. \`NMMMMM: oMMMMMMM+ /MMm/\` .ohNMNy/. \`/ymMMMMM\` .-/+o-
|
||||
.N\`m+oh \`/smMMMMMMm\` /NMMMMMo /MMMMNy/. \`/ymMNdo:\`\`-ohNMNy/. sNysd.
|
||||
+yh..- yMMMMMMMMMMh\` .sNMMMN/ /MM//ymMNdo:\`\`-ohNMNy/.\`\`/ymMo \`-smh.
|
||||
::--..:- .NMMMMMMMMMMh. .sNMMMh: /MMs:\`\`-ohNNmy/.\`./ymMNdo:\`\`-\` \`h- \`/.
|
||||
/oNodm:s :NMMMMMMMMMMm/ \`+hNMNms: /MMNNmy/.\`./ymMNdo:\`\`-ohNNmo smys/-
|
||||
\`dds. :NMMMMMMMMMMMh: \`.+hNMM :s-./ymMNdo-\`\`-ohNNmy/.\`./y- \`s.\`-/+
|
||||
.s. ./s. -mMMMMMMMMMMMNh/. .:s .\` \`-odNNmy/.\`./ymMNdo\` -\` -.
|
||||
/yhN:\`\` .yMMMMMmNMMMMMMmy/-\` \`:odN/ \`./ymMNdo-\`\`-ods\` \`oyy//d.
|
||||
--\`ydyy- /ddo-\`-smMMMMMMMNmho/-\` \`mMNdo. \`:odNNmy/ \` oo-\`-+y-
|
||||
oyo-.:s\` \`\` ./hmMMMMMMMMMNmhs+y/.\`.\` \`./yh/ :-./yy+
|
||||
\` \`/hNy/:. ./sdmMMMMMMMMMM\`-+hm/ . ho\`\`\`-/
|
||||
.s:my::-\`\`.-\` .-/NMMMMMMMdNMMM/ \`\`yydmsso\`
|
||||
m- .sysho+ mMMMMMMMMMMMN: /h:\`:yd-
|
||||
. hh\` :M- \`\` hNNNMMMMMmh+- \`-+o+\`:dy. -\`
|
||||
/h+/yh\`.h+ .\` \`.-::://:. --/\` ym:/M+ \`+:
|
||||
\`-:- -ms .md\`\`/\` .\`: syyys.\`ydhos/
|
||||
s+ \`dymoyd\`-yss/ \`: .\` .\` \`syho- .M: yd oo
|
||||
:y\`/Nm. /do/- /M\` Nm/.M: sd-\`/M:\`hy++d+
|
||||
/- .y+oN: sd NyhhM: om/-+m- .:-\`
|
||||
\`-:- o+ h/ /h: -/+:\`
|
||||
-->`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -2,19 +2,20 @@ import React, { useState, useEffect, useRef } from "react";
|
||||
import Navigation from "./Navigation";
|
||||
import throttle from "lodash/throttle";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
import NavigationMobile from "./NavigationMobile";
|
||||
import HeaderLogo from "./HeaderLogo";
|
||||
|
||||
const StyledHeader = styled.header<{isHidden?: boolean}>`
|
||||
const StyledHeader = styled.header`
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
@media screen and (max-width: 600px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
`;
|
||||
|
||||
const Sticky = styled.div`
|
||||
const Sticky = styled.div<{$isHidden?: boolean}>`
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
@@ -23,50 +24,46 @@ const Sticky = styled.div`
|
||||
background-color: ${colors.darkBlue};
|
||||
transition: all 200ms ease-out;
|
||||
|
||||
${(p) => p.isHidden ? (`
|
||||
${(p) => (p.$isHidden ? (`
|
||||
transition: all 200ms ease-in;
|
||||
transform: translateY(-100%);
|
||||
`) : null}
|
||||
`) : null)}
|
||||
`;
|
||||
|
||||
|
||||
const PREVENT_IS_HIDDEN_Y = 150;
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [isHidden, setHidden] = useState(false);
|
||||
const yCoord = useRef(0);
|
||||
|
||||
const handleScroll = () => {
|
||||
const newCoord = window.pageYOffset;
|
||||
if (!mobileMenuOpen && newCoord > yCoord.current && newCoord > PREVENT_IS_HIDDEN_Y) {
|
||||
setHidden(true);
|
||||
} else {
|
||||
setHidden(false);
|
||||
}
|
||||
yCoord.current = newCoord;
|
||||
};
|
||||
|
||||
const handleMobileMenuClick = () => setMobileMenuOpen(!mobileMenuOpen);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const newCoord = window.pageYOffset;
|
||||
if (!mobileMenuOpen && newCoord > yCoord.current && newCoord > PREVENT_IS_HIDDEN_Y) {
|
||||
setHidden(true);
|
||||
} else {
|
||||
setHidden(false);
|
||||
}
|
||||
yCoord.current = newCoord;
|
||||
};
|
||||
|
||||
const func = throttle(handleScroll, 200);
|
||||
// Prevents hide when clicking mobileMenuOpen
|
||||
handleScroll();
|
||||
window.addEventListener("scroll", func);
|
||||
return () => window.removeEventListener("scroll", func);
|
||||
}, [isHidden, mobileMenuOpen]);
|
||||
|
||||
}, [mobileMenuOpen]);
|
||||
|
||||
return (
|
||||
<Sticky>
|
||||
<StyledHeader isHidden={isHidden}>
|
||||
<Sticky $isHidden={isHidden}>
|
||||
<StyledHeader>
|
||||
<HeaderLogo />
|
||||
<Navigation onMobileMenuOpen={handleMobileMenuClick} />
|
||||
</StyledHeader>
|
||||
<NavigationMobile mobileMenuOpen={mobileMenuOpen} />
|
||||
</Sticky>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
import TitleImage from "@assets/img/SIK_RGB_W_side.png";
|
||||
import { Link } from "@components/index";
|
||||
|
||||
const Logo = styled.img`
|
||||
max-width: 300px;
|
||||
margin: 1rem 1rem;
|
||||
const TitleImage = "/img/SIK_RGB_W_side.png";
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
max-width: 100% !important;
|
||||
margin: 1rem 0 !important;
|
||||
}
|
||||
const Logo = styled(Link)`
|
||||
position: relative;
|
||||
min-width: 200px;
|
||||
min-height: 100px;
|
||||
margin: 1rem 0 !important;
|
||||
flex: 1 0 auto;
|
||||
`;
|
||||
|
||||
const HeaderLogo: React.FC = () => (
|
||||
<Link to="/">
|
||||
<Logo
|
||||
<Logo to="/">
|
||||
<Image
|
||||
src={TitleImage}
|
||||
alt="logo"
|
||||
layout="fill"
|
||||
objectFit="scale-down"
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
</Logo>
|
||||
);
|
||||
|
||||
export default HeaderLogo;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -13,16 +14,18 @@ const Container = styled.div`
|
||||
|
||||
section {
|
||||
padding: 2rem 6rem;
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
max-width: 50%;
|
||||
padding: 3rem 6rem;
|
||||
align-items: center;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
max-width: unset;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +34,7 @@ const Container = styled.div`
|
||||
flex: 8;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -46,6 +49,6 @@ const Hero: React.FC = ({ children }) => (
|
||||
<Container>
|
||||
{children}
|
||||
</Container>
|
||||
)
|
||||
);
|
||||
|
||||
export default Hero;
|
||||
export default Hero;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import Anchor from "@components/Anchor";
|
||||
import { Link } from "@components/index";
|
||||
|
||||
interface HeroAsideItemProps {
|
||||
header: string;
|
||||
@@ -14,19 +14,21 @@ const Article = styled.article`
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
export const HeroAsideItem: React.FC<HeroAsideItemProps> = ({ header, text, link, linkText }) => (
|
||||
export const HeroAsideItem: React.FC<HeroAsideItemProps> = ({
|
||||
header, text, link, linkText,
|
||||
}) => (
|
||||
<Article>
|
||||
<h2>{header}</h2>
|
||||
{text && (
|
||||
<p>{text}</p>
|
||||
)}
|
||||
<Anchor to={link}>
|
||||
<Link to={link}>
|
||||
<h6>
|
||||
{linkText}
|
||||
</h6>
|
||||
</Anchor>
|
||||
</Link>
|
||||
</Article>
|
||||
)
|
||||
);
|
||||
|
||||
type Colors = "darkBlue" | "lightTurquoise";
|
||||
|
||||
@@ -43,8 +45,6 @@ const Aside = styled.aside<{ colors: string }>`
|
||||
justify-content: center;
|
||||
|
||||
& > div {
|
||||
max-width: 350px;
|
||||
|
||||
h2 {
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
@@ -75,27 +75,27 @@ const Aside = styled.aside<{ colors: string }>`
|
||||
`;
|
||||
|
||||
const textColors = (bgColor: Colors) => {
|
||||
switch(bgColor) {
|
||||
case "darkBlue":
|
||||
return `
|
||||
switch (bgColor) {
|
||||
case "darkBlue":
|
||||
return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.lightBlue};
|
||||
`;
|
||||
case "lightTurquoise":
|
||||
return `
|
||||
case "lightTurquoise":
|
||||
return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.darkBlue};
|
||||
`;
|
||||
default: return ""
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const HeroAside: React.FC<HeroAsideProps> = ({ bgColor, children}) => (
|
||||
const HeroAside: React.FC<HeroAsideProps> = ({ bgColor, children }) => (
|
||||
<Aside colors={textColors(bgColor)}>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
</Aside>
|
||||
)
|
||||
);
|
||||
|
||||
export default HeroAside;
|
||||
|
||||
@@ -6,7 +6,7 @@ const Buttons = styled.div<{row?: boolean}>`
|
||||
max-width: fit-content;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-flow: ${(p) => p.row ? "row" : "column"} wrap;
|
||||
flex-flow: ${(p) => (p.row ? "row" : "column")} wrap;
|
||||
|
||||
a {
|
||||
display: contents;
|
||||
@@ -36,4 +36,4 @@ const Buttons = styled.div<{row?: boolean}>`
|
||||
}
|
||||
`;
|
||||
|
||||
export default Buttons;
|
||||
export default Buttons;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
interface HeroPrimarySectionProps {
|
||||
header: string;
|
||||
@@ -8,17 +9,15 @@ interface HeroPrimarySectionProps {
|
||||
}
|
||||
|
||||
const Section = styled.section`
|
||||
max-width: 50%;
|
||||
margin: 10vh auto 0;
|
||||
max-width: 800px;
|
||||
text-align: center;
|
||||
line-height: 1.5rem;
|
||||
|
||||
h1 {
|
||||
line-height: 40px;
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 400px;
|
||||
margin: 1em auto;
|
||||
font-weight: 200;
|
||||
}
|
||||
@@ -43,6 +42,6 @@ const HeroPrimarySection: React.FC<HeroPrimarySectionProps> = ({ header, text, c
|
||||
)}
|
||||
{children}
|
||||
</Section>
|
||||
)
|
||||
);
|
||||
|
||||
export default HeroPrimarySection;
|
||||
export default HeroPrimarySection;
|
||||
|
||||
@@ -10,12 +10,7 @@ const Note = styled.span`
|
||||
text-transform: uppercase;
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
margin-right: 2rem;
|
||||
margin-top: -0.5rem;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
margin: -1rem 2rem 3rem;
|
||||
`;
|
||||
|
||||
const Item = styled.div`
|
||||
@@ -29,14 +24,14 @@ interface HeroSecondarySectionItemProps {
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export const HeroSecondarySectionItem: React.FC<HeroSecondarySectionItemProps> = ({note, children}) => (
|
||||
export const HeroSecondarySectionItem: React.FC<HeroSecondarySectionItemProps> = ({ note, children }) => (
|
||||
<Item>
|
||||
<Note>
|
||||
{note}
|
||||
</Note>
|
||||
{children}
|
||||
</Item>
|
||||
)
|
||||
);
|
||||
|
||||
const Section = styled.section`
|
||||
background-color: ${colors.green1};
|
||||
@@ -64,6 +59,6 @@ const HeroSecondarySection: React.FC<HeroSecondarySectionProps> = ({ heading, ch
|
||||
{children}
|
||||
</Items>
|
||||
</Section>
|
||||
)
|
||||
);
|
||||
|
||||
export default HeroSecondarySection;
|
||||
export default HeroSecondarySection;
|
||||
|
||||
@@ -3,5 +3,5 @@ export { default as HeroPrimarySection } from "./HeroPrimarySection";
|
||||
export { default as HeroSecondarySection } from "./HeroSecondarySection";
|
||||
export { HeroSecondarySectionItem } from "./HeroSecondarySection";
|
||||
export { default as HeroAside } from "./HeroAside";
|
||||
export { HeroAsideItem as HeroAsideItem } from "./HeroAside";
|
||||
export { default as HeroPrimaryButtons } from "./HeroPrimaryButtons";
|
||||
export { HeroAsideItem } from "./HeroAside";
|
||||
export { default as HeroPrimaryButtons } from "./HeroPrimaryButtons";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
export enum IconType {
|
||||
Facebook,
|
||||
@@ -19,9 +18,11 @@ interface IconProps {
|
||||
const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
if (name === IconType.Facebook) {
|
||||
return (
|
||||
<svg role="img"
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Facebook icon</title>
|
||||
<path d="M22.676 0H1.324C.593 0 0 .593 0 1.324v21.352C0 23.408.593 24 1.324 24h11.494v-9.294H9.689v-3.621h3.129V8.41c0-3.099 1.894-4.785 4.659-4.785 1.325 0 2.464.097 2.796.141v3.24h-1.921c-1.5 0-1.792.721-1.792 1.771v2.311h3.584l-.465 3.63H16.56V24h6.115c.733 0 1.325-.592 1.325-1.324V1.324C24 .593 23.408 0 22.676 0" />
|
||||
</svg>
|
||||
@@ -29,9 +30,11 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
}
|
||||
if (name === IconType.Instagram) {
|
||||
return (
|
||||
<svg role="img"
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Instagram icon</title>
|
||||
<path d="M12 0C8.74 0 8.333.015 7.053.072 5.775.132 4.905.333 4.14.63c-.789.306-1.459.717-2.126 1.384S.935 3.35.63 4.14C.333 4.905.131 5.775.072 7.053.012 8.333 0 8.74 0 12s.015 3.667.072 4.947c.06 1.277.261 2.148.558 2.913.306.788.717 1.459 1.384 2.126.667.666 1.336 1.079 2.126 1.384.766.296 1.636.499 2.913.558C8.333 23.988 8.74 24 12 24s3.667-.015 4.947-.072c1.277-.06 2.148-.262 2.913-.558.788-.306 1.459-.718 2.126-1.384.666-.667 1.079-1.335 1.384-2.126.296-.765.499-1.636.558-2.913.06-1.28.072-1.687.072-4.947s-.015-3.667-.072-4.947c-.06-1.277-.262-2.149-.558-2.913-.306-.789-.718-1.459-1.384-2.126C21.319 1.347 20.651.935 19.86.63c-.765-.297-1.636-.499-2.913-.558C15.667.012 15.26 0 12 0zm0 2.16c3.203 0 3.585.016 4.85.071 1.17.055 1.805.249 2.227.415.562.217.96.477 1.382.896.419.42.679.819.896 1.381.164.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.015 3.585-.074 4.85c-.061 1.17-.256 1.805-.421 2.227-.224.562-.479.96-.899 1.382-.419.419-.824.679-1.38.896-.42.164-1.065.36-2.235.413-1.274.057-1.649.07-4.859.07-3.211 0-3.586-.015-4.859-.074-1.171-.061-1.816-.256-2.236-.421-.569-.224-.96-.479-1.379-.899-.421-.419-.69-.824-.9-1.38-.165-.42-.359-1.065-.42-2.235-.045-1.26-.061-1.649-.061-4.844 0-3.196.016-3.586.061-4.861.061-1.17.255-1.814.42-2.234.21-.57.479-.96.9-1.381.419-.419.81-.689 1.379-.898.42-.166 1.051-.361 2.221-.421 1.275-.045 1.65-.06 4.859-.06l.045.03zm0 3.678c-3.405 0-6.162 2.76-6.162 6.162 0 3.405 2.76 6.162 6.162 6.162 3.405 0 6.162-2.76 6.162-6.162 0-3.405-2.76-6.162-6.162-6.162zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm7.846-10.405c0 .795-.646 1.44-1.44 1.44-.795 0-1.44-.646-1.44-1.44 0-.794.646-1.439 1.44-1.439.793-.001 1.44.645 1.44 1.439z" />
|
||||
</svg>
|
||||
@@ -39,9 +42,11 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
}
|
||||
if (name === IconType.LinkedIn) {
|
||||
return (
|
||||
<svg role="img"
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>LinkedIn icon</title>
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
</svg>
|
||||
@@ -49,7 +54,8 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
}
|
||||
if (name === IconType.HamburgerMenu) {
|
||||
return (
|
||||
<svg role="img"
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 32 32"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
@@ -58,49 +64,33 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
if (name == IconType.FinlandFlag) {
|
||||
if (name === IconType.FinlandFlag) {
|
||||
return "🇫🇮";
|
||||
}
|
||||
if (name == IconType.GBFlag) {
|
||||
if (name === IconType.GBFlag) {
|
||||
return "🇬🇧";
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const SomeIcon = styled.a`
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
margin: 1em;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const NormalIcon = styled.span`
|
||||
/* stylelint-disable-next-line no-empty-source */
|
||||
`;
|
||||
|
||||
const Icon: React.FC<IconProps> = ({ link, name, onClick }) => {
|
||||
const elem = nameToIcon(name);
|
||||
if (link) {
|
||||
return (
|
||||
<SomeIcon
|
||||
<a
|
||||
href={link}
|
||||
onClick={onClick}
|
||||
>
|
||||
{elem}
|
||||
</SomeIcon>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NormalIcon role="img" onClick={onClick}>
|
||||
<span role="img" onClick={onClick}>
|
||||
{elem}
|
||||
</NormalIcon>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
|
||||
@@ -10,6 +10,6 @@ const InfoBox: React.FC = ({ children }) => (
|
||||
<Box>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
|
||||
export default InfoBox;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from "react";
|
||||
import NextJSLink, { LinkProps } from "next/link";
|
||||
|
||||
interface Props extends Omit<LinkProps, "href" | "as"> {
|
||||
to: string;
|
||||
template?: string;
|
||||
target?: string;
|
||||
onClick?: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||
onMouseEnter?: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||
onMouseLeave?: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
const Link: React.FC<Props> = ({
|
||||
to, template, passHref, onClick, onMouseEnter, onMouseLeave, ...props
|
||||
}) => {
|
||||
if (template) {
|
||||
return (
|
||||
<NextJSLink href={template} passHref={passHref} as={to} {...props}>
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
|
||||
<a onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} {...props} />
|
||||
</NextJSLink>
|
||||
);
|
||||
}
|
||||
if (to.startsWith("/") || to.startsWith("#")) {
|
||||
return (
|
||||
<NextJSLink href={to} passHref={passHref} {...props}>
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
|
||||
<a onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} {...props} />
|
||||
</NextJSLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a href={to} onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} {...props} />
|
||||
);
|
||||
};
|
||||
|
||||
export default Link;
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
|
||||
const Loader = styled((props) => (
|
||||
<div {...props}>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
))<{ $color?: typeof colors }>`
|
||||
overflow: hidden;
|
||||
font-size: 200px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
@keyframes rotation {
|
||||
0% { transform: rotate(0deg) }
|
||||
50% { transform: rotate(180deg) }
|
||||
100% { transform: rotate(360deg) }
|
||||
}
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
transform: translateZ(0) scale(1);
|
||||
backface-visibility: hidden;
|
||||
transform-origin: 0 0;
|
||||
|
||||
& > div {
|
||||
box-sizing: content-box;
|
||||
position: absolute;
|
||||
animation: rotation 1s linear infinite;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0.02em 0 0 ${({ $color }) => $color || colors.white};
|
||||
transform-origin: 50% 51%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Loader;
|
||||
@@ -1,34 +1,35 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Anchor from "./Anchor";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
import { Link } from "@components/index";
|
||||
|
||||
interface NavbarChildLinkProps {
|
||||
to: string;
|
||||
}
|
||||
|
||||
const StyledLink = styled(Anchor)`
|
||||
const StyledLink = styled(Link)`
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
letter-spacing: 1.5px;
|
||||
padding-right: 4rem;
|
||||
font-weight: 400;
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
@media screen and (max-width: ${breakpoints.medium}) {
|
||||
border-bottom: 1px dotted ${colors.lightBlue};
|
||||
margin-left: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@media screen and (min-width: 1200px) {
|
||||
@media screen and (min-width: ${breakpoints.medium}) {
|
||||
background-color: ${colors.lightBlue};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const NavbarChildLink: React.FC<NavbarChildLinkProps> = (props) => (
|
||||
<StyledLink {...props} />
|
||||
)
|
||||
<StyledLink passHref {...props} />
|
||||
);
|
||||
|
||||
export default NavbarChildLink;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import DropDownBox from "./DropDownBox";
|
||||
import Anchor from "./Anchor";
|
||||
import { colors } from "@theme/colors";
|
||||
import { Link } from "@components/index";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
const Container = styled.div`
|
||||
position: relative;
|
||||
@@ -11,7 +12,7 @@ const Container = styled.div`
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
bottom: 0.4rem;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-bottom: 4px solid ${colors.lightBlue};
|
||||
@@ -19,16 +20,16 @@ const Container = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Anchor)`
|
||||
const StyledLink = styled(Link)`
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
padding: 20px 0;
|
||||
padding: 1.25rem 0;
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
@media screen and (max-width: ${breakpoints.medium}) {
|
||||
border-bottom: 1px solid ${colors.lightBlue};
|
||||
}
|
||||
`;
|
||||
@@ -39,7 +40,9 @@ interface NavbarDropdownLinkProps {
|
||||
exploded?: boolean; // if exploded, show items directly underneath without a dropdown menu
|
||||
}
|
||||
|
||||
const NavbarDropdownLink: React.FC<NavbarDropdownLinkProps> = ({ to, text, exploded, children }) => {
|
||||
const NavbarDropdownLink: React.FC<NavbarDropdownLinkProps> = ({
|
||||
to, text, exploded, children,
|
||||
}) => {
|
||||
const [mouseOverLink, setMouseOverLink] = useState(false);
|
||||
const [mouseOverBox, setMouseOverBox] = useState(false);
|
||||
|
||||
@@ -65,6 +68,7 @@ const NavbarDropdownLink: React.FC<NavbarDropdownLinkProps> = ({ to, text, explo
|
||||
<Container>
|
||||
<StyledLink
|
||||
to={to}
|
||||
passHref
|
||||
onMouseEnter={handleMouseEnterLink}
|
||||
onMouseLeave={handleMouseLeaveLink}
|
||||
>
|
||||
@@ -79,6 +83,6 @@ const NavbarDropdownLink: React.FC<NavbarDropdownLinkProps> = ({ to, text, explo
|
||||
</DropDownBox>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default NavbarDropdownLink;
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
import Icon, { IconType } from "./Icon";
|
||||
import NavbarDropdownLink from "./NavbarDropdownLink";
|
||||
import NavbarChildLink from "./NavbarChildLink";
|
||||
|
||||
const renderNavigationDesktopItems = () => {
|
||||
return (
|
||||
<>
|
||||
<NavbarDropdownLink to="/kilta" text="Kilta ›">
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sika.sik.party">Arkisto</NavbarChildLink>
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/opinnot_ja_ura" text="Opinnot ja ura"></NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/yritysyhteistyo" text="Yritysyhteistyö"></NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/yhteystiedot" text="Yhteystiedot">
|
||||
{/* <NavbarChildLink to="https://en.wikipedia.org/wiki/Gay">Simo Höglund</NavbarChildLink> */}
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/in_english" text="In English"></NavbarDropdownLink>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const renderNavigationDesktopItems = () => (
|
||||
<>
|
||||
<NavbarDropdownLink to="/kilta" text="Kilta ›">
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/kunnia">Kunnianosoitukset</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sahkoinsinoorikilta.fi">Arkisto</NavbarChildLink>
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/opinnot_ja_ura" text="Opinnot ja ura" />
|
||||
<NavbarDropdownLink to="/yritysyhteistyo" text="Yritysyhteistyö" />
|
||||
<NavbarDropdownLink to="/yhteystiedot" text="Yhteystiedot">
|
||||
{/* <NavbarChildLink to="https://en.wikipedia.org/wiki/Gay">Simo Höglund</NavbarChildLink> */}
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/in_english" text="In English" />
|
||||
</>
|
||||
);
|
||||
|
||||
const Nav = styled.div`
|
||||
flex: 1 0 auto;
|
||||
@@ -31,7 +30,7 @@ const Nav = styled.div`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
font-size: 14px;
|
||||
font-size: 0.8rem;
|
||||
color: ${colors.lightBlue};
|
||||
margin-left: 5rem;
|
||||
|
||||
@@ -41,14 +40,22 @@ const Nav = styled.div`
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) and (max-width: 1200px) {
|
||||
@media screen and (min-width: ${breakpoints.mobile}) and (max-width: ${breakpoints.medium}) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
justify-content: center;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
|
||||
fill: ${colors.white};
|
||||
color: ${colors.white};
|
||||
}
|
||||
`;
|
||||
|
||||
const DesktopContainer = styled.div`
|
||||
@@ -57,7 +64,7 @@ const DesktopContainer = styled.div`
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
@media screen and (max-width: ${breakpoints.medium}) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
@@ -67,8 +74,10 @@ const SomeContainer = styled.div`
|
||||
flex-flow: row nowrap;
|
||||
|
||||
a {
|
||||
fill: ${colors.white};
|
||||
color: ${colors.white};
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
margin: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -81,20 +90,13 @@ const MobileMenu = styled.div`
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
@media screen and (min-width: ${breakpoints.medium}) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
fill: ${colors.white};
|
||||
color: ${colors.white};
|
||||
}
|
||||
`;
|
||||
|
||||
interface NavigationProps {
|
||||
@@ -117,6 +119,7 @@ const Navigation: React.FC<NavigationProps> = ({ onMobileMenuOpen }) => {
|
||||
<Icon name={IconType.HamburgerMenu} onClick={onMobileMenuOpen} />
|
||||
</MobileMenu>
|
||||
</Nav>
|
||||
)};
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
|
||||
@@ -4,23 +4,22 @@ import { colors } from "@theme/colors";
|
||||
import NavbarDropdownLink from "./NavbarDropdownLink";
|
||||
import NavbarChildLink from "./NavbarChildLink";
|
||||
|
||||
const renderNavigationMobileItems = () => {
|
||||
return (
|
||||
<>
|
||||
<NavbarDropdownLink to="/kilta" text="Kilta ›" exploded>
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sika.sik.party">Arkisto</NavbarChildLink>
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/opinnot_ja_ura" text="Opinnot ja ura" exploded />
|
||||
<NavbarDropdownLink to="/yritysyhteistyo" text="Yritysyhteistyö" exploded />
|
||||
<NavbarDropdownLink to="/yhteystiedot" text="Yhteystiedot" exploded>
|
||||
{/* <NavbarChildLink to="https://en.wikipedia.org/wiki/Gay">Simo Höglund</NavbarChildLink> */}
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/in_english" text="In English" exploded />
|
||||
</>
|
||||
);
|
||||
}
|
||||
const renderNavigationMobileItems = () => (
|
||||
<>
|
||||
<NavbarDropdownLink to="/kilta" text="Kilta ›" exploded>
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/kunnia">Kunnianosoitukset</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sahkoinsinoorikilta.fi">Arkisto</NavbarChildLink>
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/opinnot_ja_ura" text="Opinnot ja ura" exploded />
|
||||
<NavbarDropdownLink to="/yritysyhteistyo" text="Yritysyhteistyö" exploded />
|
||||
<NavbarDropdownLink to="/yhteystiedot" text="Yhteystiedot" exploded>
|
||||
{/* <NavbarChildLink to="https://en.wikipedia.org/wiki/Gay">Simo Höglund</NavbarChildLink> */}
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/in_english" text="In English" exploded />
|
||||
</>
|
||||
);
|
||||
|
||||
const Nav = styled.nav`
|
||||
padding: 1rem 2rem;
|
||||
@@ -40,6 +39,6 @@ const NavigationMobile: React.FC<NavigationMobileProps> = ({ mobileMenuOpen }) =
|
||||
<Nav hidden={!mobileMenuOpen}>
|
||||
{renderNavigationMobileItems()}
|
||||
</Nav>
|
||||
)
|
||||
);
|
||||
|
||||
export default NavigationMobile;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import Anchor from "@components/Anchor";
|
||||
import { Link } from "@components/index";
|
||||
|
||||
interface PageLinkProps {
|
||||
to: string;
|
||||
@@ -43,7 +43,7 @@ const StyledPageLink = styled.div`
|
||||
const PageLink: React.FC<PageLinkProps> = ({ to, desc, children }) => (
|
||||
<StyledPageLink>
|
||||
<p>{children}</p>
|
||||
<Anchor to={to}>{desc}</Anchor>
|
||||
<Link to={to}>{desc}</Link>
|
||||
</StyledPageLink>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import Anchor from "@components/Anchor";
|
||||
import { Link } from "@components/index";
|
||||
|
||||
const Section = styled.section<{ colors: string }>`
|
||||
${(p) => p.colors}
|
||||
@@ -47,8 +46,8 @@ interface CTASectionProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
}
|
||||
|
||||
const textColors = (bgColor: Colors) => {
|
||||
switch(bgColor) {
|
||||
case "orange1": return `
|
||||
switch (bgColor) {
|
||||
case "orange1": return `
|
||||
color: ${colors.white};
|
||||
background-color: ${colors[bgColor]};
|
||||
a:hover {
|
||||
@@ -56,12 +55,12 @@ a:hover {
|
||||
}
|
||||
`;
|
||||
|
||||
case "darkBlue": return `
|
||||
case "darkBlue": return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.white};
|
||||
`;
|
||||
|
||||
case "lightBlue": return `
|
||||
case "lightBlue": return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.darkBlue};
|
||||
a:hover {
|
||||
@@ -69,33 +68,35 @@ a:hover {
|
||||
}
|
||||
`;
|
||||
|
||||
case "lightTurquoise": return `
|
||||
case "lightTurquoise": return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.darkBlue};
|
||||
a:hover {
|
||||
color: ${colors.white};
|
||||
}
|
||||
`;
|
||||
case "blue1": return `
|
||||
case "blue1": return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.white};
|
||||
a:hover {
|
||||
color: ${colors.darkBlue};
|
||||
}
|
||||
`;
|
||||
default: return ""
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CTASection: React.FC<CTASectionProps> = ({ bgColor = "orange1", link, linkText, children, ...props }) => (
|
||||
const CTASection: React.FC<CTASectionProps> = ({
|
||||
bgColor = "orange1", link, linkText, children, ...props
|
||||
}) => (
|
||||
<Section colors={textColors(bgColor)} {...props}>
|
||||
<h1>{children}</h1>
|
||||
{link && (
|
||||
<Anchor to={link}>
|
||||
<Link to={link}>
|
||||
<h4>{linkText}</h4>
|
||||
</Anchor>
|
||||
</Link>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
|
||||
export default CTASection;
|
||||
export default CTASection;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
const CardSection = styled.section`
|
||||
|
||||
@@ -11,7 +12,7 @@ const CardSection = styled.section`
|
||||
flex-flow: row wrap;
|
||||
padding: 2rem 1rem;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
@@ -22,12 +23,12 @@ const CardSection = styled.section`
|
||||
& > * {
|
||||
width: calc(25% - 2rem);
|
||||
|
||||
@media screen and (min-width: 800px) and (max-width: 1200px) {
|
||||
@media screen and (min-width: ${breakpoints.mobile}) and (max-width: ${breakpoints.medium}) {
|
||||
width: calc(50% - 2rem);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
width: 100%;
|
||||
margin: 0 0 3rem 0;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ import styled from "styled-components";
|
||||
|
||||
const StyledSection = styled.section`
|
||||
display: grid;
|
||||
padding: 24px;
|
||||
padding: 1.5rem;
|
||||
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas:
|
||||
"title"
|
||||
"content";
|
||||
@@ -22,11 +23,16 @@ const StyledSection = styled.section`
|
||||
& > h6 {
|
||||
text-align: center;
|
||||
grid-area: title;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const FullWidthSection: React.FC<React.HTMLAttributes<HTMLDivElement>> = (props) => (
|
||||
<StyledSection {...props} />
|
||||
)
|
||||
);
|
||||
|
||||
export default FullWidthSection;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
import colors from "@theme/colors";
|
||||
import breakpoints from "@theme/breakpoints";
|
||||
|
||||
const StyledSection = styled.section`
|
||||
display: grid;
|
||||
padding: 24px;
|
||||
padding: 1.5rem;
|
||||
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
@@ -12,10 +13,10 @@ const StyledSection = styled.section`
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
grid-template-rows: 1fr auto;
|
||||
grid-template-areas:
|
||||
"title title title"
|
||||
". title ."
|
||||
"leftaside content rightaside";
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr auto auto auto;
|
||||
grid-template-areas:
|
||||
@@ -37,13 +38,15 @@ const StyledSection = styled.section`
|
||||
& > h6 {
|
||||
text-align: center;
|
||||
grid-area: title;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > div, p {
|
||||
grid-area: content;
|
||||
max-width: 1000px;
|
||||
|
||||
}
|
||||
|
||||
& > aside {
|
||||
@@ -51,11 +54,11 @@ const StyledSection = styled.section`
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
align-items: center;
|
||||
max-width: unset;
|
||||
margin-left: unset;
|
||||
margin-top: 48px;
|
||||
margin-top: 3rem;
|
||||
|
||||
* {
|
||||
flex: 1;
|
||||
@@ -65,15 +68,15 @@ const StyledSection = styled.section`
|
||||
|
||||
& > aside:first-of-type {
|
||||
grid-area: rightaside;
|
||||
padding-left: 24px;
|
||||
@media screen and (max-width: 800px) {
|
||||
padding-left: 1.5rem;
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
& > aside:nth-of-type(2) {
|
||||
grid-area: leftaside;
|
||||
padding-right: 24px;
|
||||
@media screen and (max-width: 800px) {
|
||||
padding-right: 1.5rem;
|
||||
@media screen and (max-width: ${breakpoints.mobile}) {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
@@ -81,6 +84,6 @@ const StyledSection = styled.section`
|
||||
|
||||
const TextSection: React.FC<React.HTMLAttributes<HTMLDivElement>> = (props) => (
|
||||
<StyledSection {...props} />
|
||||
)
|
||||
);
|
||||
|
||||
export default TextSection;
|
||||
|
||||
@@ -32,7 +32,7 @@ const CustomCBoxElement = styled.span<{checked?: boolean}>`
|
||||
left: 0;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
background-color: ${(props) => props.checked ? colors.blue1 : colors.grey2};
|
||||
background-color: ${(props) => (props.checked ? colors.blue1 : colors.grey2)};
|
||||
|
||||
&:focus &:before {
|
||||
transition: box-shadow 150ms ease;
|
||||
@@ -64,7 +64,7 @@ type CheckboxProps = Omit<
|
||||
"type"
|
||||
>;
|
||||
|
||||
const Checkbox: React.FC<CheckboxProps> = ({children, checked, ...props}) => (
|
||||
const Checkbox: React.FC<CheckboxProps> = ({ children, checked, ...props }) => (
|
||||
<Container>
|
||||
{children}
|
||||
<HiddenDefaultElement
|
||||
@@ -77,6 +77,6 @@ const Checkbox: React.FC<CheckboxProps> = ({children, checked, ...props}) => (
|
||||
{checked && (<Checkmark />)}
|
||||
</CustomCBoxElement>
|
||||
</Container>
|
||||
)
|
||||
);
|
||||
|
||||
export default Checkbox;
|
||||
export default Checkbox;
|
||||
|
||||
@@ -14,7 +14,7 @@ function selectValue(value, selected, all) {
|
||||
}
|
||||
|
||||
function deselectValue(value, selected) {
|
||||
return selected.filter(v => v !== value);
|
||||
return selected.filter((v) => v !== value);
|
||||
}
|
||||
|
||||
type CheckboxesProps = Omit<WidgetProps, "options"> & {
|
||||
@@ -25,14 +25,16 @@ const CheckboxContainer = styled.div`
|
||||
margin-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
const Checkboxes: React.FC<CheckboxesProps> = ({id, disabled, options, value, autofocus, readonly, onChange}) => {
|
||||
const Checkboxes: React.FC<CheckboxesProps> = ({
|
||||
id, disabled, options, value, autofocus, readonly, onChange,
|
||||
}) => {
|
||||
const { enumOptions, enumDisabled, inline } = options;
|
||||
return (
|
||||
<div className="checkboxes" id={id}>
|
||||
{enumOptions.map((option, index) => {
|
||||
const checked = value.indexOf(option.value) !== -1;
|
||||
const itemDisabled =
|
||||
enumDisabled && enumDisabled.indexOf(option.value) != -1;
|
||||
enumDisabled && enumDisabled.indexOf(option.value) !== -1;
|
||||
const disabledCls =
|
||||
disabled || itemDisabled || readonly ? "disabled" : "";
|
||||
const checkbox = (
|
||||
@@ -41,8 +43,8 @@ const Checkboxes: React.FC<CheckboxesProps> = ({id, disabled, options, value, au
|
||||
checked={checked}
|
||||
disabled={disabled || itemDisabled || readonly}
|
||||
autoFocus={autofocus && index === 0}
|
||||
onChange={event => {
|
||||
const all = enumOptions.map(({ value }) => value);
|
||||
onChange={(event) => {
|
||||
const all = enumOptions.map(({ val }) => val);
|
||||
if (event.target.checked) {
|
||||
onChange(selectValue(option.value, value, all));
|
||||
} else {
|
||||
@@ -64,15 +66,14 @@ const Checkboxes: React.FC<CheckboxesProps> = ({id, disabled, options, value, au
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Checkboxes.defaultProps = {
|
||||
autofocus: false,
|
||||
options: {
|
||||
inline: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export default Checkboxes;
|
||||
export default Checkboxes;
|
||||
|
||||
@@ -22,7 +22,9 @@ interface DatetimeWidgetProps {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const DatetimeWidget: React.FC<DatetimeWidgetProps> = ({ value, onChange, onFocus, onBlur, required, disabled }) => {
|
||||
const DatetimeWidget: React.FC<DatetimeWidgetProps> = ({
|
||||
value, onChange, onFocus, onBlur, required, disabled,
|
||||
}) => {
|
||||
let date;
|
||||
let time;
|
||||
if (value && value.length !== 0) {
|
||||
@@ -44,14 +46,16 @@ const DatetimeWidget: React.FC<DatetimeWidgetProps> = ({ value, onChange, onFocu
|
||||
type="date"
|
||||
onChange={(event) => onChange(`${event.target.value}T${time}`)}
|
||||
value={date}
|
||||
{...commonProps} />
|
||||
{...commonProps}
|
||||
/>
|
||||
<input
|
||||
type="time"
|
||||
onChange={(event) => onChange(`${date}T${event.target.value}:00`)}
|
||||
value={time}
|
||||
{...commonProps} />
|
||||
{...commonProps}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default DatetimeWidget;
|
||||
|
||||