Start migration to NextJS
This commit is contained in:
@@ -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
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
+33
-6
@@ -1,10 +1,37 @@
|
||||
.idea/
|
||||
dist/
|
||||
node_modules/
|
||||
src/**/*.jsx
|
||||
.vscode/
|
||||
tests/jest/__coverage__/
|
||||
tests/jest/**/*.jsx
|
||||
tests/testcafe/screenshots
|
||||
.vscode/
|
||||
.env
|
||||
# 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
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
target: "serverless",
|
||||
experimental: {
|
||||
jsconfigPaths: true
|
||||
},
|
||||
env: {
|
||||
}
|
||||
}
|
||||
Generated
+2426
-9804
File diff suppressed because it is too large
Load Diff
+6
-44
@@ -20,7 +20,7 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://sik.ayy.fi",
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production npm-run-all build:client build:server",
|
||||
"build": "next build",
|
||||
"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",
|
||||
@@ -28,8 +28,8 @@
|
||||
"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-dev": "next dev",
|
||||
"serve": "next start",
|
||||
"start-prod": "npm run build && npm run serve",
|
||||
"test": "npm run test:e2e:verbose",
|
||||
"test:e2e": "npm-run-all -p -r serve test:e2e:testcafe",
|
||||
@@ -45,26 +45,14 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "24.0.22",
|
||||
"@types/js-cookie": "2.2.4",
|
||||
"@types/node": "10.14.7",
|
||||
"@types/react": "16.8.18",
|
||||
"@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/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",
|
||||
"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",
|
||||
@@ -74,51 +62,28 @@
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"date-fns": "2.0.0-alpha.27",
|
||||
"js-cookie": "2.2.0",
|
||||
"lodash": "4.17.20",
|
||||
"next": "^10.0.3",
|
||||
"normalize.css": "8.0.1",
|
||||
"query-string": "6.5.0",
|
||||
"react-beautiful-dnd": "10.1.1",
|
||||
@@ -126,10 +91,7 @@
|
||||
"react-jsonschema-form": "^1.8.1",
|
||||
"react-markdown": "4.3.1",
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -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,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);
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { HashLink } from "react-router-hash-link";
|
||||
import Link from "next/link";
|
||||
|
||||
interface AnchorProps extends React.HTMLAttributes<HTMLAnchorElement> {
|
||||
to: string;
|
||||
@@ -9,11 +8,7 @@ interface AnchorProps extends React.HTMLAttributes<HTMLAnchorElement> {
|
||||
const Anchor: React.FC<AnchorProps> = ({ to, ...props }) => {
|
||||
if (to.startsWith("/")) {
|
||||
return (
|
||||
<Link to={to} {...props} />
|
||||
);
|
||||
} else if (to.startsWith("#")) {
|
||||
return (
|
||||
<HashLink to={to} {...props} />
|
||||
<Link href={to} {...props} />
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { render } from "react-dom";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { AppContainer } from "react-hot-loader";
|
||||
import Routes from "./routes";
|
||||
import "./index.scss";
|
||||
|
||||
console.log("Using API URL: ", process.env.API_URL);
|
||||
|
||||
const rootEl = document.getElementById("root");
|
||||
|
||||
render(
|
||||
<AppContainer>
|
||||
<BrowserRouter>
|
||||
<Routes />
|
||||
</BrowserRouter>
|
||||
</AppContainer>,
|
||||
rootEl
|
||||
);
|
||||
|
||||
// Hot Module Replacement API
|
||||
declare const module: { hot: any };
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept("./routes", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const NewRoutes = require("./routes").default;
|
||||
|
||||
render(
|
||||
<AppContainer>
|
||||
<BrowserRouter>
|
||||
<NewRoutes />
|
||||
</BrowserRouter>
|
||||
</AppContainer>,
|
||||
rootEl
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { Event, getEvents } from "@models/Event";
|
||||
import { Post, getFeed } from "@models/Feed";
|
||||
import { StaticContext } from "@server/StaticContext";
|
||||
import FrontPageView from "@views/FrontPage/FrontPageView";
|
||||
|
||||
interface FrontPageProps {
|
||||
staticContext: StaticContext;
|
||||
}
|
||||
|
||||
interface FrontPageState {
|
||||
events: Event[];
|
||||
feed: Post[];
|
||||
}
|
||||
|
||||
class FrontPage extends React.Component<FrontPageProps, FrontPageState> {
|
||||
constructor(props: FrontPageProps) {
|
||||
super(props);
|
||||
const { staticContext } = props;
|
||||
|
||||
if (staticContext) {
|
||||
/* The static context is an object that manages promises when
|
||||
rendering on the server. If staticContext exists, that means
|
||||
we have to store all promises in it. Otherwise, operate
|
||||
normally. See server/index.ts. */
|
||||
if (staticContext.resolutions.getEvents) {
|
||||
const events = staticContext.resolutions.getEvents as Event[];
|
||||
const feed = staticContext.resolutions.getFeed as Post[];
|
||||
this.state = {
|
||||
events,
|
||||
feed,
|
||||
};
|
||||
} else {
|
||||
this.state = {
|
||||
events: [],
|
||||
feed: [],
|
||||
};
|
||||
const promiseEvents = this.fetchEvents();
|
||||
const promiseFeed = this.fetchFeed();
|
||||
staticContext.promises.getEvents = promiseEvents;
|
||||
staticContext.promises.getFeed = promiseFeed;
|
||||
}
|
||||
} else {
|
||||
this.state = {
|
||||
events: [],
|
||||
feed: [],
|
||||
};
|
||||
this.fetchEvents();
|
||||
this.fetchFeed();
|
||||
}
|
||||
}
|
||||
|
||||
fetchEvents = () => {
|
||||
const getEventsPromise = getEvents({
|
||||
onlyNonPast: true,
|
||||
limit: 4,
|
||||
});
|
||||
getEventsPromise.then(events => {
|
||||
this.setState({
|
||||
events,
|
||||
});
|
||||
});
|
||||
return getEventsPromise;
|
||||
}
|
||||
|
||||
fetchFeed = () => {
|
||||
const getFeedPromise = getFeed();
|
||||
getFeedPromise.then(feed => {
|
||||
this.setState({
|
||||
feed,
|
||||
});
|
||||
});
|
||||
return getFeedPromise;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { events, feed } = this.state;
|
||||
return (
|
||||
<>
|
||||
<Helmet />
|
||||
<FrontPageView events={events} feed={feed} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default FrontPage;
|
||||
@@ -0,0 +1,171 @@
|
||||
import React from "react";
|
||||
// import App from "next/app";
|
||||
import type { AppProps /*, AppContext'*/} from "next/app"
|
||||
import Head from "next/head";
|
||||
import styled, { createGlobalStyle } from "styled-components";
|
||||
import { colors } from "@theme/colors";
|
||||
|
||||
const fontFamily = "'Montserrat', sans-serif;"
|
||||
|
||||
const GlobalCommonStyles = createGlobalStyle`
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: ${fontFamily};
|
||||
font-size: 12pt;
|
||||
background-color: ${colors.darkBlue};
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 300;
|
||||
|
||||
&.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 200;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
|
||||
&.large {
|
||||
font-size: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 200;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
|
||||
&.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
|
||||
&.large {
|
||||
font-size: 2.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.1em;
|
||||
font-weight: 800;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: List item style » */
|
||||
li {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
|
||||
&.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: ${colors.blue1};
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const AppContainer = styled.div`
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
color: ${colors.black};
|
||||
background-color: ${colors.white};
|
||||
`;
|
||||
|
||||
const Web20App = ({ Component, pageProps }: AppProps) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,800,900&display=swap" rel="stylesheet" />
|
||||
{/* <link rel="icon" href="/favicon.ico" /> */}
|
||||
{/* <meta httpEquiv="X-UA-Compatible" content="IE=edge" /> */}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{/* <meta name="description" content="SIK turns 100 years old!" /> */}
|
||||
{/* <meta name="keywords" content="SIK100" /> */}
|
||||
{/* <title>SIK100</title> */}
|
||||
{/* <link rel="manifest" href="/manifest.json" /> */}
|
||||
{/* <link rel="apple-touch-icon" href="/logo192.png" /> */}
|
||||
{/* <meta name="theme-color" content={theme.colors.darkBlue2} /> */}
|
||||
</Head>
|
||||
<GlobalCommonStyles />
|
||||
<AppContainer>
|
||||
<Component {...pageProps} />
|
||||
</AppContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Only uncomment this method if you have blocking data requirements for
|
||||
// every single page in your application. This disables the ability to
|
||||
// perform automatic static optimization, causing every page in your app to
|
||||
// be server-side rendered.
|
||||
//
|
||||
// export const getInitialProps = async (appContext: AppContext) => {
|
||||
// // calls page's `getInitialProps` and fills `appProps.pageProps`
|
||||
// const appProps = await App.getInitialProps(appContext);
|
||||
// return { ...appProps }
|
||||
// }
|
||||
|
||||
export default Web20App;
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import Document, {
|
||||
Html, Head, Main, NextScript, DocumentContext,
|
||||
} from "next/document";
|
||||
import { ServerStyleSheet } from "styled-components";
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
try {
|
||||
ctx.renderPage = () => originalRenderPage({
|
||||
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
|
||||
});
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
<>
|
||||
{initialProps.styles}
|
||||
{sheet.getStyleElement()}
|
||||
</>
|
||||
),
|
||||
};
|
||||
} finally {
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { styleTags } = this.props as any;
|
||||
return (
|
||||
<Html lang="fi">
|
||||
<Head />
|
||||
<body>
|
||||
{styleTags}
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { getEvents } from "@models/Event";
|
||||
import { getFeed } from "@models/Feed";
|
||||
import FrontPageView from "@views/FrontPage/FrontPageView";
|
||||
|
||||
const FrontPage: NextPage<React.ComponentProps<typeof FrontPageView>> = (props) => (
|
||||
<FrontPageView {...props} />
|
||||
);
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
const events = await getEvents({
|
||||
onlyNonPast: true,
|
||||
limit: 4,
|
||||
});
|
||||
const feed = await getFeed();
|
||||
return {
|
||||
props: {
|
||||
events,
|
||||
feed,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default FrontPage;
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { Switch, Route } from "react-router-dom";
|
||||
import { Helmet } from "react-helmet";
|
||||
import FrontPage from "./pages/FrontPage";
|
||||
import FrontPage from "./pages";
|
||||
import GuildPage from "./pages/GuildPage";
|
||||
import NotFoundPage from "./pages/NotFoundPage";
|
||||
import CommonPage from "./pages/CommonPage";
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from "react";
|
||||
import { StaticRouter } from "react-router-dom";
|
||||
import Routes from "../routes";
|
||||
|
||||
export default ({ url, context }) => (
|
||||
<StaticRouter context={context} location={url}>
|
||||
<Routes />
|
||||
</StaticRouter>
|
||||
);
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface StaticContext {
|
||||
resolutions: { [key: string]: any };
|
||||
promises: { [key: string]: Promise<any> };
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import express from "express";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import morgan from "morgan";
|
||||
import helmet from "helmet";
|
||||
import compression from "compression";
|
||||
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
console.log(`API_URL: ${process.env.API_URL}`);
|
||||
|
||||
import App from "./App";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { StaticContext } from "./StaticContext";
|
||||
import { ServerStyleSheet } from "styled-components";
|
||||
|
||||
const port = 3000;
|
||||
const server = express();
|
||||
|
||||
const indexHtml = fs.readFileSync(path.resolve("./dist/index.html"), "utf-8");
|
||||
const html = (body, styles, title, meta) => {
|
||||
const withBody = indexHtml.replace("<div id=\"root\"></div>", `<div id="root">${body}</div>`);
|
||||
const withHead = withBody.replace("<head>", `<head>${title}${meta}${styles}`);
|
||||
return withHead;
|
||||
};
|
||||
|
||||
server.use(morgan("short"));
|
||||
server.use(helmet());
|
||||
server.use(helmet.hidePoweredBy());
|
||||
server.use(compression());
|
||||
server.use("/assets", express.static("dist/assets"));
|
||||
server.use("/js", express.static("dist/js"));
|
||||
|
||||
server.get("*", async (req, res) => {
|
||||
const context: StaticContext = {
|
||||
resolutions: {}, promises: {},
|
||||
};
|
||||
|
||||
// Styled-components Step 1: Create an instance of ServerStyleSheet
|
||||
const sheet = new ServerStyleSheet();
|
||||
|
||||
// Styled-components Step 2: Retrieve styles from components in the page
|
||||
const firstPassRenderResult = renderToString(
|
||||
sheet.collectStyles(
|
||||
React.createElement(App, { url: req.url, context })
|
||||
)
|
||||
);
|
||||
|
||||
const promiseKeys = Object.keys(context.promises);
|
||||
let result: string;
|
||||
if (promiseKeys.length === 0) {
|
||||
/* No promises to resolve on the first pass. Render html normally. */
|
||||
result = firstPassRenderResult;
|
||||
} else {
|
||||
/* Some promises have to be resolved before rendering the page. */
|
||||
const promiseEntries = promiseKeys.map(async key => {
|
||||
const promise = context.promises[key];
|
||||
return { key, value: await promise };
|
||||
});
|
||||
|
||||
/* Resolve all promises. */
|
||||
const awaitedEntries = await Promise.all(promiseEntries);
|
||||
|
||||
/* Store all resolutions in the context. */
|
||||
awaitedEntries.forEach(entry => {
|
||||
context.resolutions[entry.key] = entry.value;
|
||||
});
|
||||
|
||||
/* Render a second time with all resolved data. */
|
||||
// Styled-components Step 2: Retrieve styles from components in the page
|
||||
const secondPassRenderResult = renderToString(
|
||||
sheet.collectStyles(
|
||||
React.createElement(App, { url: req.url, context })
|
||||
)
|
||||
);
|
||||
result = secondPassRenderResult;
|
||||
}
|
||||
|
||||
// Styled-components Step 3: Extract the styles as <style> tags
|
||||
const styles = sheet.getStyleTags();
|
||||
|
||||
const head = Helmet.rewind();
|
||||
res.send(
|
||||
html(result, styles, head.title, head.meta)
|
||||
);
|
||||
});
|
||||
|
||||
server.listen(3000, () => console.log("React SSR express server listening on port 3000!"));
|
||||
+19
-3
@@ -10,10 +10,15 @@
|
||||
// "resolveJsonModule": true,
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"lib": ["es5", "es6", "es7", "dom"],
|
||||
"jsx": "preserve",
|
||||
"lib": [
|
||||
"es5",
|
||||
"es6",
|
||||
"es7",
|
||||
"dom"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./types",
|
||||
"./types"
|
||||
],
|
||||
"types": [
|
||||
"node",
|
||||
@@ -55,9 +60,20 @@
|
||||
"src/utils/*"
|
||||
]
|
||||
},
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*",
|
||||
"./types/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user