Merge branch 'master' into 'production'
Signup modifications See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!9
This commit is contained in:
@@ -4,13 +4,29 @@
|
||||
"react"
|
||||
],
|
||||
"plugins": [
|
||||
"react-hot-loader/babel"
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
|
||||
+15
-15
@@ -88,25 +88,25 @@ publish:prod:
|
||||
|
||||
deploy:dev:
|
||||
stage: deploy
|
||||
image: alpine:latest
|
||||
environment:
|
||||
name: dev
|
||||
url: http://web.sik.party:3000
|
||||
image: docker:stable
|
||||
only:
|
||||
- master
|
||||
environment:
|
||||
name: dev
|
||||
url: dev.sik.party
|
||||
variables:
|
||||
DOCKER_HOST: $DEV_CI_DOCKER_HOST
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
before_script:
|
||||
- pwd
|
||||
- apk add --update openssh
|
||||
- ssh -V
|
||||
- mkdir -p ~/.ssh
|
||||
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
||||
- chmod 600 ~/.ssh/id_rsa
|
||||
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
|
||||
- mkdir -p ~/.docker
|
||||
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
|
||||
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem
|
||||
- echo "$DEV_TLSKEY" > ~/.docker/key.pem
|
||||
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
|
||||
script:
|
||||
- scp docker-compose.yml $DEV_SSH_USER@$DEV_SSH_HOST:~/deployment-frontend/docker-compose.yml
|
||||
- scp .deploy_dev.sh $DEV_SSH_USER@$DEV_SSH_HOST:~/deployment-frontend/deploy_dev.sh
|
||||
- ssh $DEV_SSH_USER@$DEV_SSH_HOST "docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY"
|
||||
- ssh $DEV_SSH_USER@$DEV_SSH_HOST "bash ~/deployment-frontend/deploy_dev.sh \"$IMAGE_NAME:latest\""
|
||||
- docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME"
|
||||
after_script:
|
||||
- docker logout "$CI_REGISTRY"
|
||||
|
||||
deploy:prod:
|
||||
stage: deploy
|
||||
|
||||
+7
-3
@@ -1,11 +1,15 @@
|
||||
FROM node:12-alpine
|
||||
FROM node:12-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
|
||||
RUN npm ci --only-prod
|
||||
|
||||
COPY . ./
|
||||
ENV API_URL https://api.dev.sik.party/api
|
||||
RUN npm run build
|
||||
|
||||
FROM fnichol/uhttpd AS server
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
COPY --from=builder /app/dist /www
|
||||
ENTRYPOINT ["/usr/sbin/run_uhttpd", "-f", "-p", "3000", "-h", "/www", "-E", "/"]
|
||||
|
||||
@@ -11,7 +11,7 @@ Minimal starter kit with hot module replacement (HMR) for rapid development.
|
||||
* [Jest](https://facebook.github.io/jest/) - Testing framework for React applications
|
||||
* Production build script
|
||||
* Image loading/minification using [Image Webpack Loader](https://github.com/tcoopman/image-webpack-loader)
|
||||
* Typescript compiling using [Awesome Typescript Loader](https://github.com/s-panferov/awesome-typescript-loader) (5.x)
|
||||
* Typescript compiling using [TS-Loader](https://webpack.js.org/guides/typescript/)
|
||||
* Code quality (linting) for Typescript and SASS/CSS.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Shared config (dev and prod)
|
||||
const {resolve} = require("path");
|
||||
const {CheckerPlugin} = require("awesome-typescript-loader");
|
||||
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
|
||||
const StyleLintPlugin = require("stylelint-webpack-plugin");
|
||||
const webpack = require('webpack');
|
||||
const Dotenv = require("dotenv-webpack");
|
||||
@@ -12,7 +12,8 @@ module.exports = function (env, argv) {
|
||||
};
|
||||
|
||||
config.resolve = {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx"]
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
||||
plugins: [new TsconfigPathsPlugin()]
|
||||
};
|
||||
config.context = resolve(__dirname, "../../src");
|
||||
config.module = {
|
||||
@@ -25,7 +26,7 @@ module.exports = function (env, argv) {
|
||||
});
|
||||
config.module.rules.push({
|
||||
test: /\.tsx?$/,
|
||||
use: ["babel-loader", "awesome-typescript-loader"]
|
||||
use: ["babel-loader", "ts-loader"]
|
||||
});
|
||||
|
||||
config.module.rules.push({
|
||||
@@ -43,7 +44,6 @@ module.exports = function (env, argv) {
|
||||
|
||||
config.plugins = [
|
||||
new webpack.DefinePlugin(envVars),
|
||||
new CheckerPlugin(),
|
||||
new StyleLintPlugin(),
|
||||
new Dotenv({
|
||||
path: "./.env"
|
||||
|
||||
Generated
+2504
-1023
File diff suppressed because it is too large
Load Diff
+22
-12
@@ -24,9 +24,9 @@
|
||||
"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",
|
||||
"lint:es": "eslint './src/**/*.ts*'",
|
||||
"lint:es:fix": "eslint --fix './src/**/*.ts*'",
|
||||
"lint:sass": "stylelint ./src/**/**/*.scss ./src/**/*.scss ./src/*.scss",
|
||||
"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",
|
||||
@@ -45,17 +45,22 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/classnames": "2.2.10",
|
||||
"@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": "2.6.1",
|
||||
"@typescript-eslint/parser": "2.6.1",
|
||||
"awesome-typescript-loader": "5.2.1",
|
||||
"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",
|
||||
@@ -79,7 +84,7 @@
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "1.3.1",
|
||||
"image-webpack-loader": "6.0.0",
|
||||
"json-server": "0.14.2",
|
||||
"json-server": "0.16.1",
|
||||
"mini-css-extract-plugin": "0.4.5",
|
||||
"module-to-cdn": "3.1.2",
|
||||
"morgan": "1.9.1",
|
||||
@@ -90,9 +95,9 @@
|
||||
"react-addons-test-utils": "15.6.2",
|
||||
"react-dom": "16.8.6",
|
||||
"react-hot-loader": "4.8.8",
|
||||
"sass": "^1.25.0",
|
||||
"sass": "^1.26.8",
|
||||
"sass-loader": "7.1.0",
|
||||
"serve": "11.2.0",
|
||||
"serve": "11.3.2",
|
||||
"style-loader": "0.21.0",
|
||||
"stylelint": "11.1.1",
|
||||
"stylelint-config-recommended-scss": "4.0.0",
|
||||
@@ -101,31 +106,36 @@
|
||||
"stylelint-webpack-plugin": "1.0.3",
|
||||
"testcafe": "1.6.1",
|
||||
"testcafe-react-selectors": "2.1.0",
|
||||
"typescript": "3.7.2",
|
||||
"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.9.0",
|
||||
"webpack-dev-server": "3.11.0",
|
||||
"webpack-merge": "4.2.2",
|
||||
"webpack-node-externals": "1.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.19.0",
|
||||
"classnames": "2.2.6",
|
||||
"date-fns": "2.0.0-alpha.27",
|
||||
"js-cookie": "2.2.0",
|
||||
"lodash": "4.17.15",
|
||||
"mobx": "5.9.4",
|
||||
"mobx-react": "5.4.4",
|
||||
"normalize.css": "8.0.1",
|
||||
"query-string": "6.5.0",
|
||||
"react-beautiful-dnd": "10.1.1",
|
||||
"react-helmet": "5.2.1",
|
||||
"react-jsonschema-form": "^1.8.0",
|
||||
"react-router": "4.3.1",
|
||||
"react-jsonschema-form": "^1.8.1",
|
||||
"react-router-dom": "4.3.1",
|
||||
"react-router-hash-link": "1.2.1",
|
||||
"shortid": "2.2.14"
|
||||
"shortid": "2.2.14",
|
||||
"styled-components": "5.1.1"
|
||||
},
|
||||
"postcss": {}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
{{#if observer}}
|
||||
import { observer } from "mobx-react";
|
||||
import {{ camelCase store_name }} from "../../stores/{{ properCase store_name }}";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import "./{{ properCase name}}.scss";
|
||||
|
||||
export interface {{ properCase name }}Props {}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import Routes from "../routes";
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import { hydrate } from "react-dom";
|
||||
import App from "./App";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./Accordion.scss";
|
||||
import AccordionIcon from "../AccordionIcon";
|
||||
|
||||
@@ -26,9 +26,9 @@ class Accordion extends React.Component<AccordionProps, AccordionState> {
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen,
|
||||
});
|
||||
this.setState((prevState) => ({
|
||||
isOpen: !prevState.isOpen,
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./AccordionIcon.scss";
|
||||
|
||||
export interface AccordionIconProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as TitleImage from "../../assets/img/SIK_RGB_W_side.png";
|
||||
import TitleImage from "@assets/img/SIK_RGB_W_side.png";
|
||||
import "./AdminHeader.scss";
|
||||
|
||||
export interface AdminHeaderProps { }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./AdminSidebar.scss";
|
||||
import AdminSidebarLink from "../AdminSidebarLink";
|
||||
|
||||
@@ -16,7 +16,7 @@ class AdminSidebar extends React.Component<AdminSidebarProps, AdminSidebarState>
|
||||
<AdminSidebarLink to="/admin/events" path={path}>Events</AdminSidebarLink>
|
||||
<AdminSidebarLink to="/admin/feed" path={path}>Feed</AdminSidebarLink>
|
||||
<AdminSidebarLink to="/admin/signups" path={path}>Signup forms</AdminSidebarLink>
|
||||
<AdminSidebarLink to="https://static.sika.sik.party/admin" path={path}>Files</AdminSidebarLink>
|
||||
<AdminSidebarLink to="https:https://static.sika.sik.party/admin" path={path}>Files</AdminSidebarLink>
|
||||
<AdminSidebarLink id="admin-sidebar-logout" to="/admin/logout" path={path}>Logout</AdminSidebarLink>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import Anchor from "../Anchor";
|
||||
import "./AdminSidebarLink.scss";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { HashLink } from "react-router-hash-link";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./AsideSection.scss";
|
||||
import ColorDiv, { ColorDivProps } from "../ColorDiv/ColorDiv";
|
||||
|
||||
|
||||
@@ -31,6 +31,25 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.bordered {
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: color(blue1);
|
||||
border: 1px solid color(blue1);
|
||||
}
|
||||
|
||||
&.filter {
|
||||
text-transform: none;
|
||||
color: color(grey1);
|
||||
font-weight: 300;
|
||||
border: 2px solid color(grey1);
|
||||
|
||||
&.selected {
|
||||
background-color: color(grey1);
|
||||
color: color(white1);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import "./Button.scss";
|
||||
|
||||
export enum ButtonType {
|
||||
Hero,
|
||||
Filled,
|
||||
}
|
||||
|
||||
const buttonClassNames = new Map<ButtonType, string>([
|
||||
[ButtonType.Hero, "hero"],
|
||||
[ButtonType.Filled, "filled"],
|
||||
]);
|
||||
|
||||
export interface ButtonProps {
|
||||
interface ButtonProps {
|
||||
onClick: () => void;
|
||||
type: ButtonType;
|
||||
type: "hero" | "filled" | "filter" | "bordered";
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export default class Button extends React.Component<ButtonProps, undefined> {
|
||||
render() {
|
||||
const { type } = this.props;
|
||||
const className = `button ${buttonClassNames.get(type)}`;
|
||||
const { type, selected } = this.props;
|
||||
const classes = classNames(
|
||||
"button",
|
||||
type,
|
||||
{ "selected": selected }
|
||||
)
|
||||
return (
|
||||
<button type="button" onClick={this.props.onClick} className={className}>
|
||||
<button type="button" onClick={this.props.onClick} className={classes}>
|
||||
{this.props.children}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
import Button, { ButtonType } from "./Button";
|
||||
export default Button;
|
||||
export { ButtonType };
|
||||
import Button from "./Button";
|
||||
export default Button;
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./Card.scss";
|
||||
import Anchor from "../Anchor";
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Container = styled.label`
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.5rem; /* 24px */
|
||||
user-select: none;
|
||||
|
||||
/* On mouse-over, add a grey background color */
|
||||
&:hover input ~ .custom-cbox {
|
||||
background-color: #d4d0c7; /* grey1 */
|
||||
}
|
||||
`;
|
||||
|
||||
/* Hide the browser's default checkbox */
|
||||
const HiddenDefaultElement = styled.input`
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
`;
|
||||
|
||||
/* Create a custom checkbox */
|
||||
const CustomCBoxElement = styled.span<{checked?: boolean}>`
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
background-color: ${(props) => props.checked ? "#57b2df" : "#efece4"}; /* blue1 or grey2 */
|
||||
|
||||
&:focus &:before {
|
||||
transition: box-shadow 150ms ease;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
bottom: -4px;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid color(blue);
|
||||
}
|
||||
`;
|
||||
|
||||
const Checkmark = styled.div`
|
||||
position: absolute;
|
||||
left: 0.375em;
|
||||
top: calc(0.25em - 1px);
|
||||
width: 0.25em;
|
||||
height: 0.5em;
|
||||
border: solid #fff;
|
||||
border-width: 0 0.125em 0.125em 0;
|
||||
transform: rotate(45deg);
|
||||
`;
|
||||
|
||||
type CheckboxProps = Omit<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
"type"
|
||||
>;
|
||||
|
||||
const Checkbox: React.FC<CheckboxProps> = ({children, checked, ...props}) => (
|
||||
<Container>
|
||||
{children}
|
||||
<HiddenDefaultElement
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
{...props}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<CustomCBoxElement tabIndex={0} checked={checked} className="custom-cbox">
|
||||
{checked && (<Checkmark />)}
|
||||
</CustomCBoxElement>
|
||||
</Container>
|
||||
)
|
||||
|
||||
export default Checkbox;
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { WidgetProps } from "react-jsonschema-form";
|
||||
import Checkbox from "./Checkbox";
|
||||
|
||||
// See https://github.com/rjsf-team/react-jsonschema-form/blob/master/packages/core/src/components/widgets/CheckboxesWidget.js
|
||||
|
||||
function selectValue(value, selected, all) {
|
||||
const at = all.indexOf(value);
|
||||
const updated = selected.slice(0, at).concat(value, selected.slice(at));
|
||||
// As inserting values at predefined index positions doesn't work with empty
|
||||
// arrays, we need to reorder the updated selection to match the initial order
|
||||
return updated.sort((a, b) => all.indexOf(a) > all.indexOf(b));
|
||||
}
|
||||
|
||||
function deselectValue(value, selected) {
|
||||
return selected.filter(v => v !== value);
|
||||
}
|
||||
|
||||
type CheckboxesProps = Omit<WidgetProps, "options"> & {
|
||||
options: any;
|
||||
};
|
||||
|
||||
const CheckboxContainer = styled.div`
|
||||
margin-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
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;
|
||||
const disabledCls =
|
||||
disabled || itemDisabled || readonly ? "disabled" : "";
|
||||
const checkbox = (
|
||||
<Checkbox
|
||||
id={`${id}_${index}`}
|
||||
checked={checked}
|
||||
disabled={disabled || itemDisabled || readonly}
|
||||
autoFocus={autofocus && index === 0}
|
||||
onChange={event => {
|
||||
const all = enumOptions.map(({ value }) => value);
|
||||
if (event.target.checked) {
|
||||
onChange(selectValue(option.value, value, all));
|
||||
} else {
|
||||
onChange(deselectValue(option.value, value));
|
||||
}
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</Checkbox>
|
||||
);
|
||||
return inline ? (
|
||||
<label key={index} className={`checkbox-inline ${disabledCls}`}>
|
||||
{checkbox}
|
||||
</label>
|
||||
) : (
|
||||
<CheckboxContainer key={index} className={disabledCls}>
|
||||
{checkbox}
|
||||
</CheckboxContainer>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Checkboxes.defaultProps = {
|
||||
autofocus: false,
|
||||
options: {
|
||||
inline: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Checkboxes;
|
||||
@@ -1,51 +1,13 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./ColorDiv.scss";
|
||||
|
||||
export enum ColorEnum {
|
||||
DarkBlue,
|
||||
LightBlue,
|
||||
White,
|
||||
Black,
|
||||
Grey1,
|
||||
Grey2,
|
||||
Orange1,
|
||||
Orange2,
|
||||
Blue,
|
||||
LightTurquoise,
|
||||
Green,
|
||||
Sand,
|
||||
Transparent,
|
||||
Inherit,
|
||||
}
|
||||
|
||||
const colors = new Map<ColorEnum, string>([
|
||||
[ColorEnum.DarkBlue, "dark-blue"],
|
||||
[ColorEnum.LightBlue, "light-blue"],
|
||||
[ColorEnum.White, "white1"],
|
||||
[ColorEnum.Black, "black1"],
|
||||
[ColorEnum.Grey1, "grey1"],
|
||||
[ColorEnum.Grey2, "grey2"],
|
||||
[ColorEnum.Orange1, "orange1"],
|
||||
[ColorEnum.Orange2, "orange2"],
|
||||
[ColorEnum.Blue, "blue1"],
|
||||
[ColorEnum.LightTurquoise, "light-turquoise"],
|
||||
[ColorEnum.Green, "green1"],
|
||||
[ColorEnum.Sand, "sand"],
|
||||
[ColorEnum.Transparent, "transparent"],
|
||||
[ColorEnum.Inherit, "inherit"]
|
||||
]);
|
||||
|
||||
export const getColor = (color: ColorEnum): string => `color-div__${colors.get(color)}`;
|
||||
export const getBgColor = (color: ColorEnum): string => `color-div__background_${colors.get(color)}`;
|
||||
export const getHoverColor = (color: ColorEnum): string => `color-div__${colors.get(color)}Hoverable`;
|
||||
export const getBgHoverColor = (color: ColorEnum): string => `color-div__background_${colors.get(color)}Hoverable`;
|
||||
|
||||
import { Colors, colorToClass, bgColorToClass, hoverColorToClass, bgHoverColorToClass } from "@theme/colors";
|
||||
import classNames from "classnames";
|
||||
|
||||
export interface ColorDivProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
textColor?: ColorEnum;
|
||||
backgroundColor?: ColorEnum;
|
||||
hoverColor?: ColorEnum;
|
||||
backgroundHoverColor?: ColorEnum;
|
||||
textColor?: Colors;
|
||||
backgroundColor?: Colors;
|
||||
hoverColor?: Colors;
|
||||
backgroundHoverColor?: Colors;
|
||||
}
|
||||
export interface ColorDivState { }
|
||||
|
||||
@@ -53,14 +15,16 @@ export interface ColorDivState { }
|
||||
class ColorDiv extends React.Component<ColorDivProps, ColorDivState> {
|
||||
render() {
|
||||
const { children, className, textColor, backgroundColor, hoverColor, backgroundHoverColor, ...props } = this.props;
|
||||
const classNames = [];
|
||||
if (className) classNames.push(className);
|
||||
if (textColor !== undefined) classNames.push(getColor(textColor));
|
||||
if (backgroundColor !== undefined) classNames.push(getBgColor(backgroundColor));
|
||||
if (hoverColor !== undefined) classNames.push(getHoverColor(hoverColor));
|
||||
if (backgroundHoverColor !== undefined) classNames.push(getBgHoverColor(backgroundHoverColor));
|
||||
const classes = classNames(
|
||||
className,
|
||||
colorToClass(textColor),
|
||||
bgColorToClass(backgroundColor),
|
||||
hoverColorToClass(hoverColor),
|
||||
bgHoverColorToClass(backgroundHoverColor)
|
||||
);
|
||||
|
||||
return (
|
||||
<div {...props} className={classNames.join(" ")}>
|
||||
<div {...props} className={classes}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
import ColorDiv from "./ColorDiv";
|
||||
export default ColorDiv;
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./CommitteeContainer.scss";
|
||||
import { Occupation } from "../../models/Contacts";
|
||||
import { Occupation } from "@models/Contacts";
|
||||
import ContactCard from "../ContactCard/ContactCard";
|
||||
|
||||
export interface CommitteeContainerProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./ContactCard.scss";
|
||||
import * as blank_profile from "../../assets/img/blank_profile.png";
|
||||
import { Role } from "../../models/Contacts";
|
||||
import blank_profile from "@assets/img/blank_profile.png";
|
||||
import { Role } from "@models/Contacts";
|
||||
|
||||
export interface ContactCardProps {
|
||||
first_name: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./DatetimeWidget.scss";
|
||||
|
||||
export interface DatetimeWidgetProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./DropDownBox.scss";
|
||||
|
||||
export interface DropDownBoxProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import FooterMap from "../FooterMap";
|
||||
import Anchor from "../Anchor";
|
||||
import "./Footer.scss";
|
||||
@@ -28,8 +28,8 @@ class Footer extends React.Component<FooterProps, FooterState> {
|
||||
<div className="footer__links">
|
||||
<Anchor to="/jaseneksi">Jäseneksi</Anchor>
|
||||
<Anchor to="/palaute">Palaute</Anchor>
|
||||
<Anchor to="/arkisto">Arkisto</Anchor>
|
||||
<Anchor to="/materiaalipankki">Materiaalipankki</Anchor>
|
||||
<Anchor to="https://static.sika.sik.party">Arkisto</Anchor>
|
||||
<Anchor to="https://static.sika.sik.party">Materiaalipankki</Anchor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./FooterMap.scss";
|
||||
|
||||
const AnyReactComponent = ({ text }) => <div>{text}</div>;
|
||||
|
||||
@@ -2,17 +2,26 @@
|
||||
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
background-color: color(dark-blue);
|
||||
transition: all 200ms ease-out;
|
||||
|
||||
&.hidden {
|
||||
transition: all 200ms ease-in;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0 1rem;
|
||||
background-color: color(dark-blue);
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px - 1px) {
|
||||
flex-flow: column nowrap;
|
||||
|
||||
@@ -1,88 +1,105 @@
|
||||
import * as React from "react";
|
||||
import React, { FC, useState, useEffect, useRef } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./Header.scss";
|
||||
import NavbarDropdownLink from "../NavbarDropdownLink/NavbarDropdownLink";
|
||||
import NavbarChildLink from "../NavbarChildLink/NavbarChildLink";
|
||||
import Navigation from "../Navigation";
|
||||
import * as TitleImage from "../../assets/img/SIK_RGB_W_side.png";
|
||||
import TitleImage from "@assets/img/SIK_RGB_W_side.png";
|
||||
import classNames from "classnames";
|
||||
import throttle from "lodash/throttle";
|
||||
|
||||
export interface HeaderProps { }
|
||||
export interface HeaderState {
|
||||
mobileMenuOpen: boolean;
|
||||
}
|
||||
|
||||
class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
mobileMenuOpen: false,
|
||||
};
|
||||
}
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderNavigationDesktopItems = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<NavbarDropdownLink to="/kilta" text="Kilta ›">
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/arkisto">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>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderNavigationMobileItems = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<NavbarDropdownLink to="/kilta" text="Kilta ›" exploded>
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/arkisto">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 />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
const PREVENT_IS_HIDDEN_Y = 150;
|
||||
|
||||
handleMobileMenuClick = (): void => {
|
||||
this.setState(prevState => ({
|
||||
mobileMenuOpen: !prevState.mobileMenuOpen,
|
||||
}));
|
||||
}
|
||||
const Header: FC<HeaderProps> = () => {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [isHidden, setHidden] = useState(false);
|
||||
const yCoord = useRef(0);
|
||||
|
||||
render() {
|
||||
const desktopItems = this.renderNavigationDesktopItems();
|
||||
const mobileItems = this.renderNavigationMobileItems();
|
||||
const { mobileMenuOpen } = this.state;
|
||||
const handleScroll = () => {
|
||||
const newCoord = window.pageYOffset;
|
||||
if (!mobileMenuOpen && newCoord > yCoord.current && newCoord > PREVENT_IS_HIDDEN_Y) {
|
||||
setHidden(true);
|
||||
} else {
|
||||
setHidden(false);
|
||||
}
|
||||
yCoord.current = newCoord;
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="header">
|
||||
<Link to="/">
|
||||
<img className="logo" src={TitleImage} />
|
||||
</Link>
|
||||
<Navigation
|
||||
onMobileMenuOpen={this.handleMobileMenuClick}
|
||||
items={desktopItems}
|
||||
/>
|
||||
</div>
|
||||
<div className="navigation-mobile-menu" hidden={!mobileMenuOpen}>
|
||||
{mobileItems}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
const handleMobileMenuClick = () => setMobileMenuOpen(!mobileMenuOpen);
|
||||
|
||||
useEffect(() => {
|
||||
const func = throttle(handleScroll, 200);
|
||||
// Prevents hide when clicking mobileMenuOpen
|
||||
handleScroll();
|
||||
window.addEventListener("scroll", func);
|
||||
return () => window.removeEventListener("scroll", func);
|
||||
}, [isHidden, mobileMenuOpen]);
|
||||
|
||||
const desktopItems = renderNavigationDesktopItems();
|
||||
const mobileItems = renderNavigationMobileItems();
|
||||
|
||||
const classes = classNames(
|
||||
"header",
|
||||
{ "hidden": isHidden }
|
||||
)
|
||||
|
||||
return (
|
||||
<header className={classes}>
|
||||
<div className="nav-container">
|
||||
<Link to="/">
|
||||
<img className="logo" src={TitleImage} />
|
||||
</Link>
|
||||
<Navigation
|
||||
onMobileMenuOpen={handleMobileMenuClick}
|
||||
items={desktopItems}
|
||||
/>
|
||||
</div>
|
||||
<div className="navigation-mobile-menu" hidden={!mobileMenuOpen}>
|
||||
{mobileItems}
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
@import "../../assets/scss/globals";
|
||||
@import "../../../assets/scss/globals";
|
||||
|
||||
|
||||
.hero-aside-item {
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import "./HeroAsideItem.scss";
|
||||
import Anchor from "../../Anchor";
|
||||
|
||||
interface HeroAsideItemProps {
|
||||
title: string;
|
||||
linkText: string;
|
||||
linkHref: string;
|
||||
}
|
||||
|
||||
const HeroAsideItem: React.FC<HeroAsideItemProps> = ({ title, linkText, linkHref, children }) => (
|
||||
<div className="hero-aside-item">
|
||||
<h2>{title}</h2>
|
||||
<p>{children}</p>
|
||||
<Anchor to={linkHref}>
|
||||
<h6>{linkText} ›</h6>
|
||||
</Anchor>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default HeroAsideItem;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
@import "../../assets/scss/globals";
|
||||
@import "../../../assets/scss/globals";
|
||||
|
||||
|
||||
.hero-aside-section {
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./HeroAsideSection.scss";
|
||||
import ColorDiv, { ColorDivProps } from "../ColorDiv/ColorDiv";
|
||||
import ColorDiv, { ColorDivProps } from "../../ColorDiv/ColorDiv";
|
||||
|
||||
|
||||
export interface HeroAsideSectionProps { }
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: center;
|
||||
margin-top: 15vh;
|
||||
flex: 6;
|
||||
text-align: center;
|
||||
font-weight: 100;
|
||||
line-height: 24px;
|
||||
|
||||
& > div {
|
||||
padding: 2rem 1rem 2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
max-width: 600px;
|
||||
line-height: 40px;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 400px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.hero-button-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 20%;
|
||||
|
||||
&-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const HeroMainSection: React.FC = ({children}) => {
|
||||
return (
|
||||
<Container>
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default HeroMainSection;
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import ColorDiv from "../../ColorDiv/ColorDiv";
|
||||
|
||||
interface HeroSecondarySectionItemProps {
|
||||
note?: string;
|
||||
}
|
||||
|
||||
const Note = styled.span`
|
||||
color: white;
|
||||
transform-origin: right;
|
||||
transform: rotate(-90deg);
|
||||
height: fit-content;
|
||||
text-transform: uppercase;
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
margin-right: 2rem;
|
||||
margin-top: -0.5rem;
|
||||
`;
|
||||
|
||||
const Item = styled.div`
|
||||
display: flex;
|
||||
text-align: left;
|
||||
flex: 1 0;
|
||||
margin: 1rem 2rem 1rem;
|
||||
`;
|
||||
|
||||
export const HeroSecondarySectionItem: React.FC<HeroSecondarySectionItemProps> = ({note, children}) => (
|
||||
<Item>
|
||||
<Note>
|
||||
{note}
|
||||
</Note>
|
||||
{children}
|
||||
</Item>
|
||||
)
|
||||
|
||||
const Container = styled(ColorDiv)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h1 {
|
||||
padding: 1em 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Items = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
interface HeroSecondarySectionProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const HeroSecondarySection: React.FC<HeroSecondarySectionProps> = ({title, children}) => (
|
||||
<Container textColor="dark-blue" backgroundColor="green1">
|
||||
<h1>{title}</h1>
|
||||
<Items>
|
||||
{children}
|
||||
</Items>
|
||||
</Container>
|
||||
)
|
||||
|
||||
export default HeroSecondarySection;
|
||||
@@ -1,30 +0,0 @@
|
||||
import * as React from "react";
|
||||
import "./HeroAsideItem.scss";
|
||||
import Anchor from "../Anchor";
|
||||
|
||||
export interface HeroAsideItemProps {
|
||||
title: string;
|
||||
linkText: string;
|
||||
linkHref: string;
|
||||
}
|
||||
export interface HeroAsideItemState { }
|
||||
|
||||
class HeroAsideItem extends React.Component<
|
||||
HeroAsideItemProps,
|
||||
HeroAsideItemState
|
||||
> {
|
||||
render() {
|
||||
const { title, linkText, linkHref, children } = this.props;
|
||||
return (
|
||||
<div className="hero-aside-item">
|
||||
<h2>{title}</h2>
|
||||
<p>{children}</p>
|
||||
<Anchor to={linkHref}>
|
||||
<h6>{linkText} ›</h6>
|
||||
</Anchor>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HeroAsideItem;
|
||||
@@ -1,2 +0,0 @@
|
||||
import HeroAsideItem from "./HeroAsideItem";
|
||||
export default HeroAsideItem;
|
||||
@@ -1,2 +0,0 @@
|
||||
import HeroAsideSection from "./HeroAsideSection";
|
||||
export default HeroAsideSection;
|
||||
@@ -1,33 +0,0 @@
|
||||
@import "../../assets/scss/globals";
|
||||
|
||||
.hero-main-section {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: center;
|
||||
margin-top: 15vh;
|
||||
flex: 6;
|
||||
text-align: center;
|
||||
font-weight: 100;
|
||||
padding: 2rem 1rem 2rem;
|
||||
line-height: 24px;
|
||||
|
||||
h1 {
|
||||
max-width: 600px;
|
||||
line-height: 40px;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 400px;
|
||||
font-weight: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-button-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 20%;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import * as React from "react";
|
||||
import "./HeroMainSection.scss";
|
||||
|
||||
export interface HeroMainSectionProps {}
|
||||
export interface HeroMainSectionState {}
|
||||
|
||||
class HeroMainSection extends React.Component<HeroMainSectionProps, HeroMainSectionState> {
|
||||
render() {
|
||||
return (
|
||||
<div className="hero-main-section">
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HeroMainSection;
|
||||
@@ -1,2 +0,0 @@
|
||||
import HeroMainSection from "./HeroMainSection";
|
||||
export default HeroMainSection;
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./Icon.scss";
|
||||
|
||||
export enum IconType {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./InfoBox.scss";
|
||||
|
||||
export interface InfoBoxProps {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
|
||||
export interface JsonLDProps {
|
||||
data: object;
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
flex-flow: column;
|
||||
padding: 0 3rem;
|
||||
|
||||
h3 {
|
||||
& > h3 {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h6 {
|
||||
& > h6 {
|
||||
color: color(orange1);
|
||||
}
|
||||
|
||||
p {
|
||||
& > p {
|
||||
margin-block-start: 0.5rem;
|
||||
margin-block-end: 1rem;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./MainSection.scss";
|
||||
import ColorDiv, { ColorDivProps } from "../ColorDiv/ColorDiv";
|
||||
import classNames from "classnames";
|
||||
|
||||
export interface MainSectionProps { }
|
||||
export interface MainSectionState { }
|
||||
|
||||
class MainSection extends React.Component<MainSectionProps & ColorDivProps, MainSectionState> {
|
||||
constructor(props: MainSectionProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, className, ...props } = this.props;
|
||||
const classNames = [
|
||||
const classes = classNames(
|
||||
"main-section",
|
||||
];
|
||||
if (className) classNames.push(className);
|
||||
className
|
||||
);
|
||||
|
||||
return (
|
||||
<ColorDiv className={classNames.join(" ")} {...props}>
|
||||
<ColorDiv className={classes} {...props}>
|
||||
{children}
|
||||
</ColorDiv>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./NavbarChildLink.scss";
|
||||
import Anchor from "../Anchor";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./NavbarDropdownLink.scss";
|
||||
import DropDownBox from "../DropDownBox/DropDownBox";
|
||||
import Anchor from "../Anchor";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./Navigation.scss";
|
||||
import NavbarDropdownLink from "../NavbarDropdownLink/NavbarDropdownLink";
|
||||
import NavbarChildLink from "../NavbarChildLink/NavbarChildLink";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./PageLink.scss";
|
||||
import TextAnchor from "../TextAnchor";
|
||||
import { TextSize } from "../TextAnchor/TextAnchor";
|
||||
|
||||
export interface PageLinkProps {
|
||||
to: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./PageSection.scss";
|
||||
import ColorDiv, { ColorDivProps } from "../ColorDiv/ColorDiv";
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Container = styled.label`
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.5rem; /* 24px */
|
||||
user-select: none;
|
||||
|
||||
/* On mouse-over, add a grey background color */
|
||||
&:hover input ~ .custom-radio {
|
||||
background-color: #d4d0c7; /* grey1 */
|
||||
}
|
||||
`;
|
||||
|
||||
const HiddenDefaultElement = styled.input`
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
`;
|
||||
|
||||
const CustomRadioElement = styled.span<{checked?: boolean}>`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
background-color: ${(props) => props.checked ? "#57b2df" : "#efece4"}; /* blue1 or grey2 */
|
||||
border-radius: 50%;
|
||||
`;
|
||||
|
||||
const Indicator = styled.div`
|
||||
position: absolute;
|
||||
top: 0.25em;
|
||||
left: 0.25em;
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
`;
|
||||
|
||||
type RadioButtonProps = Omit<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
"type"
|
||||
>;
|
||||
|
||||
const RadioButton: React.FC<RadioButtonProps> = ({
|
||||
checked,
|
||||
children,
|
||||
...props
|
||||
}) => (
|
||||
<Container>
|
||||
{children}
|
||||
<HiddenDefaultElement
|
||||
type="radio"
|
||||
checked={checked}
|
||||
{...props}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<CustomRadioElement tabIndex={0} checked={checked} className="custom-radio">
|
||||
{checked && (<Indicator />)}
|
||||
</CustomRadioElement>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default RadioButton;
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { WidgetProps } from "react-jsonschema-form";
|
||||
import RadioButton from "./RadioButton";
|
||||
|
||||
type RadioButtonWidgetProps = Omit<WidgetProps, "options"> & {
|
||||
options: any;
|
||||
};
|
||||
|
||||
const RadioButtonContainer = styled.div`
|
||||
margin-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
const RadioButtonWidget: React.FC<RadioButtonWidgetProps> = (props) => {
|
||||
const {
|
||||
options,
|
||||
value,
|
||||
required,
|
||||
disabled,
|
||||
readonly,
|
||||
autofocus,
|
||||
onBlur,
|
||||
onFocus,
|
||||
onChange,
|
||||
id,
|
||||
} = props;
|
||||
// Generating a unique field name to identify this set of radio buttons
|
||||
const name = Math.random().toString();
|
||||
const { enumOptions, enumDisabled, inline } = options;
|
||||
// checked={checked} has been moved above name={name}, As mentioned in #349;
|
||||
// this is a temporary fix for radio button rendering bug in React, facebook/react#7630.
|
||||
return (
|
||||
<div className="field-radio-group" id={id}>
|
||||
{enumOptions.map((option, i) => {
|
||||
const checked = option.value === value;
|
||||
const itemDisabled =
|
||||
enumDisabled && enumDisabled.indexOf(option.value) != -1;
|
||||
const disabledCls =
|
||||
disabled || itemDisabled || readonly ? "disabled" : "";
|
||||
const radio = (
|
||||
<RadioButton
|
||||
checked={checked}
|
||||
name={name}
|
||||
required={required}
|
||||
value={option.value}
|
||||
disabled={disabled || itemDisabled || readonly}
|
||||
autoFocus={autofocus && i === 0}
|
||||
onChange={() => onChange(option.value)}
|
||||
onBlur={onBlur && (event => onBlur(id, event.target.value))}
|
||||
onFocus={onFocus && (event => onFocus(id, event.target.value))}
|
||||
>
|
||||
{option.label}
|
||||
</RadioButton>
|
||||
);
|
||||
|
||||
return inline ? (
|
||||
<label key={i} className={`radio-inline ${disabledCls}`}>
|
||||
{radio}
|
||||
</label>
|
||||
) : (
|
||||
<RadioButtonContainer key={i} className={disabledCls}>
|
||||
{radio}
|
||||
</RadioButtonContainer>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
RadioButtonWidget.defaultProps = {
|
||||
autofocus: false,
|
||||
};
|
||||
|
||||
export default RadioButtonWidget;
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./Ribbon.scss";
|
||||
|
||||
export interface RibbonProps {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./SectionDividerWidget.scss";
|
||||
import Icon from "../Icon";
|
||||
import { IconType } from "../Icon/Icon";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import * as shortid from "shortid";
|
||||
import React from "react";
|
||||
import { Question, InputProps, optionTypes, SignupQuestionError } from "./index";
|
||||
import Checkbox from "@components/Checkbox/Checkbox";
|
||||
|
||||
export interface OptionsWidgetProps {
|
||||
inputProps: InputProps;
|
||||
@@ -9,7 +9,7 @@ export interface OptionsWidgetProps {
|
||||
export interface OptionsWidgetState { }
|
||||
|
||||
class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWidgetState> {
|
||||
handleRadiobuttonOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
handleListOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
const lst = val.split(",").map(p => p.trimLeft());
|
||||
@@ -17,14 +17,44 @@ class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWidgetSta
|
||||
onChange(questions);
|
||||
}
|
||||
|
||||
handleCheckboxOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
handleTextOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
const lst = val.split(",").map(p => p.trimLeft());
|
||||
questions[index].options = lst;
|
||||
questions[index].options = val;
|
||||
onChange(questions);
|
||||
}
|
||||
|
||||
handleIntegerOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
if (val !== "") {
|
||||
const lst = val.split(",").map(p => p.trimLeft());
|
||||
// Ignore everything else but the two first values
|
||||
questions[index].options = lst.splice(0, 2);
|
||||
} else {
|
||||
questions[index].options = []
|
||||
}
|
||||
|
||||
onChange(questions);
|
||||
}
|
||||
|
||||
handleRequiredChange = (questions: Question[], index: number) => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val: boolean = event.target.checked;
|
||||
console.log(val);
|
||||
questions[index].required = val;
|
||||
onChange(questions);
|
||||
}
|
||||
|
||||
requiredField() {
|
||||
const { inputProps } = this.props;
|
||||
const { questions, index } = inputProps;
|
||||
return <Checkbox
|
||||
checked={questions[index].required}
|
||||
onChange={this.handleRequiredChange(questions, index)}
|
||||
>Required?</Checkbox>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { inputProps } = this.props;
|
||||
const { type, value, questions, index } = inputProps;
|
||||
@@ -32,28 +62,63 @@ class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWidgetSta
|
||||
throw new SignupQuestionError(`Question widget type "${type}" not in types array.`);
|
||||
}
|
||||
|
||||
if (type === "text") {
|
||||
return null;
|
||||
if (type === "text" || type === "email" || type === "name") {
|
||||
return this.requiredField();
|
||||
}
|
||||
|
||||
if (type === "info") {
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Write something informative"
|
||||
value={questions[index].options}
|
||||
onChange={this.handleTextOptionsChange(questions, index)} />
|
||||
{this.requiredField()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "integer") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(",");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Minimum,Maximum"
|
||||
value={joinedValue}
|
||||
onChange={this.handleIntegerOptionsChange(questions, index)} />
|
||||
{this.requiredField()}
|
||||
</>);
|
||||
}
|
||||
|
||||
if (type === "radiobutton") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(",");
|
||||
return <input
|
||||
type="text"
|
||||
placeholder="Yes,no,maybe"
|
||||
value={joinedValue}
|
||||
onChange={this.handleRadiobuttonOptionsChange(questions, index)} />;
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Yes,no,maybe"
|
||||
value={joinedValue}
|
||||
onChange={this.handleListOptionsChange(questions, index)} />
|
||||
{this.requiredField()}
|
||||
</>);
|
||||
}
|
||||
|
||||
if (type === "checkbox") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(",");
|
||||
return <input
|
||||
type="text"
|
||||
placeholder="A,B,C"
|
||||
value={joinedValue}
|
||||
onChange={this.handleCheckboxOptionsChange(questions, index)} />;
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="A,B,C"
|
||||
value={joinedValue}
|
||||
onChange={this.handleListOptionsChange(questions, index)} />
|
||||
{this.requiredField()}
|
||||
</>);
|
||||
}
|
||||
|
||||
throw new SignupQuestionError(`Unrecognized question widget type "${type}"`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
|
||||
export interface QuestionProps {
|
||||
children: any;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from "react";
|
||||
import * as shortid from "shortid";
|
||||
import React from "react";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import { Question, InputProps, optionTypes, SignupQuestionError } from "./index";
|
||||
import { Question, InputProps } from "./index";
|
||||
import OptionsWidget from "./OptionsWidget";
|
||||
import TypeWidget from "./TypeWidget";
|
||||
import QuestionElement from "./Question";
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import * as React from "react";
|
||||
import * as shortid from "shortid";
|
||||
import OptionsWidget from "./OptionsWidget";
|
||||
import React from "react";
|
||||
import shortid from "shortid";
|
||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||
import { Question, InputProps, optionTypes } from "."
|
||||
import * as AddIcon from "../../assets/img/add-icon.png";
|
||||
import { Question } from "."
|
||||
import AddIcon from "@assets/img/add-icon.png";
|
||||
import "./SignupQuestionsWidget.scss";
|
||||
import TypeWidget from "./TypeWidget";
|
||||
import QuestionList from "./QuestionList";
|
||||
|
||||
export interface SignupQuestionsWidgetProps {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import * as shortid from "shortid";
|
||||
import { Question, InputProps, optionTypes, SignupQuestionError } from "./index";
|
||||
import React from "react";
|
||||
import { Question, InputProps, optionTypes } from "./index";
|
||||
|
||||
export interface TypeWidgetProps {
|
||||
inputProps: InputProps;
|
||||
|
||||
@@ -3,8 +3,9 @@ import SignupQuestionsWidget from "./SignupQuestionsWidget";
|
||||
export interface Question {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
type: OptionTypes;
|
||||
options: string[];
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface InputProps {
|
||||
@@ -14,10 +15,23 @@ export interface InputProps {
|
||||
type: string;
|
||||
}
|
||||
|
||||
type OptionTypes =
|
||||
"text" |
|
||||
"info" |
|
||||
"integer" |
|
||||
"radiobutton" |
|
||||
"checkbox" |
|
||||
"email" |
|
||||
"name";
|
||||
|
||||
export const optionTypes = [
|
||||
"text",
|
||||
"info",
|
||||
"integer",
|
||||
"radiobutton",
|
||||
"checkbox",
|
||||
"email",
|
||||
"name"
|
||||
];
|
||||
|
||||
export class SignupQuestionError extends Error { }
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./SponsorReel.scss";
|
||||
import TextAnchor from "../TextAnchor";
|
||||
import Anchor from "../Anchor";
|
||||
import { ColorEnum } from "../ColorDiv/ColorDiv";
|
||||
|
||||
export interface SponsorReelProps { }
|
||||
export interface SponsorReelState { }
|
||||
@@ -19,7 +18,7 @@ class SponsorReel extends React.Component<SponsorReelProps, SponsorReelState> {
|
||||
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
|
||||
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
|
||||
</div>
|
||||
<TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.LightTurquoise} to="/yritysyhteistyo">Haluatko kuulla lisää yhteistyöstä kanssamme?</TextAnchor>
|
||||
<TextAnchor textColor="blue1" hoverColor="light-turquoise" to="/yritysyhteistyo">Haluatko kuulla lisää yhteistyöstä kanssamme?</TextAnchor>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import "./TextAnchor.scss";
|
||||
import { ColorEnum, getColor, getHoverColor } from "../ColorDiv/ColorDiv";
|
||||
import { Colors, colorToClass, hoverColorToClass } from "@theme/colors";
|
||||
import Anchor from "../Anchor";
|
||||
import classNames from "classnames";
|
||||
|
||||
|
||||
export enum TextSize {
|
||||
Normal,
|
||||
Small,
|
||||
Large,
|
||||
Ribbon,
|
||||
SmallRibbon,
|
||||
}
|
||||
export type TextSize =
|
||||
"normal" |
|
||||
"small" |
|
||||
"large" |
|
||||
"ribbon" |
|
||||
"small-ribbon";
|
||||
|
||||
const sizes = new Map<TextSize, string>([
|
||||
[TextSize.Normal, ""],
|
||||
[TextSize.Small, "text-anchor__small"],
|
||||
[TextSize.Large, "text-anchor__large"],
|
||||
[TextSize.SmallRibbon, "text-anchor__no-weight text-anchor__small"],
|
||||
[TextSize.Ribbon, "text-anchor__no-weight"],
|
||||
|
||||
const textSizeToClassName = new Map<TextSize, string>([
|
||||
["normal", ""],
|
||||
["small", "text-anchor__small"],
|
||||
["large", "text-anchor__large"],
|
||||
["small-ribbon", "text-anchor__no-weight text-anchor__small"],
|
||||
["ribbon", "text-anchor__no-weight"],
|
||||
]);
|
||||
|
||||
export interface TextAnchorProps {
|
||||
size?: TextSize;
|
||||
to: string;
|
||||
textColor?: ColorEnum;
|
||||
hoverColor?: ColorEnum;
|
||||
textColor?: Colors;
|
||||
hoverColor?: Colors;
|
||||
}
|
||||
export interface TextAnchorState { }
|
||||
|
||||
class TextAnchor extends React.Component<TextAnchorProps, TextAnchorState> {
|
||||
render() {
|
||||
const { children, size, to, textColor, hoverColor } = this.props;
|
||||
const classColor = textColor !== undefined ? getColor(textColor) : getColor(ColorEnum.DarkBlue);
|
||||
const classHoverColor = hoverColor !== undefined ? getHoverColor(hoverColor) : getHoverColor(ColorEnum.Blue);
|
||||
const classSize = size !== undefined ? sizes.get(size) : sizes.get(TextSize.Normal);
|
||||
const className = `text-anchor ${classSize} ${classColor} ${classHoverColor}`;
|
||||
const classes = classNames(
|
||||
"text-anchor",
|
||||
colorToClass(textColor),
|
||||
hoverColorToClass(hoverColor),
|
||||
textSizeToClassName.get(size)
|
||||
)
|
||||
return (
|
||||
<Anchor to={to} className={className}>
|
||||
<Anchor to={to} className={classes}>
|
||||
{children}
|
||||
</Anchor>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,6 @@ body {
|
||||
height: 100%;
|
||||
font-family: $font;
|
||||
color: color(white1);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#root {
|
||||
@@ -25,7 +24,6 @@ body {
|
||||
body {
|
||||
padding: 0;
|
||||
margin: auto !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import { render } from "react-dom";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { AppContainer } from "react-hot-loader";
|
||||
|
||||
+13
-6
@@ -1,7 +1,7 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "../auth";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { Tag } from "./Tag";
|
||||
import * as qs from "query-string";
|
||||
import qs from "query-string";
|
||||
import { SignupForm } from "./SignupForm";
|
||||
const url = `${process.env.API_URL}/events/`;
|
||||
|
||||
@@ -15,6 +15,7 @@ export interface Event {
|
||||
content_en: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
image: string;
|
||||
tags: Tag[];
|
||||
tag_id?: number[];
|
||||
visible: boolean;
|
||||
@@ -23,14 +24,17 @@ export interface Event {
|
||||
}
|
||||
|
||||
export async function getEvents(options: any = {}): Promise<Event[]> {
|
||||
const { onlyNonPast, limit } = options;
|
||||
const { onlyNonPast, limit, auth } = options;
|
||||
try {
|
||||
const params = {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
};
|
||||
const search = qs.stringify(params);
|
||||
const resp = await axios.get(`${url}?${search}`);
|
||||
const headers = auth ? { "Authorization": getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${url}?${search}`, {
|
||||
headers
|
||||
});
|
||||
return resp.data["results"];
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
@@ -38,9 +42,12 @@ export async function getEvents(options: any = {}): Promise<Event[]> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEvent(id: number): Promise<Event> {
|
||||
export async function getEvent(id: number, auth = false): Promise<Event> {
|
||||
try {
|
||||
const resp = await axios.get(`${url}${id}/`);
|
||||
const headers = auth ? { "Authorization": getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${url}${id}/`, {
|
||||
headers
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "../auth";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
const url = `${process.env.API_URL}/feed/`;
|
||||
|
||||
|
||||
+42
-6
@@ -1,8 +1,6 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "../auth";
|
||||
import * as qs from "query-string";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
const url = `${process.env.API_URL}/signup/`;
|
||||
import { Question } from "../components/SignupQuestionsWidget";
|
||||
|
||||
export interface Signup {
|
||||
id?: number;
|
||||
@@ -10,11 +8,11 @@ export interface Signup {
|
||||
answer: string;
|
||||
}
|
||||
|
||||
export async function createSignup(data: Signup): Promise<Signup> {
|
||||
export async function getSignup(id: number): Promise<Signup> {
|
||||
try {
|
||||
const resp = await axios.post(url, data, {
|
||||
const resp = await axios.get(`${url}${id}`, {
|
||||
headers: {
|
||||
"Authorization": getAuthHeader(),
|
||||
"Authorization": getAuthHeader()
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
@@ -23,3 +21,41 @@ export async function createSignup(data: Signup): Promise<Signup> {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createSignup(data: Signup): Promise<Signup> {
|
||||
try {
|
||||
const resp = await axios.post(url, data);
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateSignup(data: Signup, uuid: string): Promise<Signup> {
|
||||
try {
|
||||
const { id } = data;
|
||||
if (!id) throw new Error("SignupId required!");
|
||||
const resp = await axios.put(`${url}${id}/edit/`, data, {
|
||||
params: { uuid }
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSignupUUID(id: number, uuid: string): Promise<Signup> {
|
||||
try {
|
||||
const resp = await axios.get(`${url}${id}/edit/`, {
|
||||
params: {
|
||||
uuid
|
||||
}
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "../auth";
|
||||
import * as qs from "query-string";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
const url = `${process.env.API_URL}/signupForm/`;
|
||||
import { Question } from "../components/SignupQuestionsWidget";
|
||||
import { Question } from "@components/SignupQuestionsWidget";
|
||||
|
||||
export interface SignupForm {
|
||||
id: number;
|
||||
id?: number;
|
||||
title: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
questions: string;
|
||||
questions: Question[];
|
||||
signups: string[];
|
||||
quota: number;
|
||||
schema: {
|
||||
title?: string;
|
||||
type: string;
|
||||
required: string[];
|
||||
properties: any;
|
||||
minProperties?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getForms(): Promise<SignupForm[]> {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { StaticContext } from "@server/StaticContext";
|
||||
import ActualPageView from "@views/ActualPage/ActualPageView";
|
||||
import { Event, getEvents } from "@models/Event";
|
||||
import { Post, getFeed } from "@models/Feed";
|
||||
|
||||
interface ActualPageProps {
|
||||
staticContext: StaticContext;
|
||||
}
|
||||
|
||||
interface ActualPageState {
|
||||
events: Event[];
|
||||
feed: Post[];
|
||||
}
|
||||
|
||||
|
||||
class ActualPage extends React.Component<ActualPageProps, ActualPageState> {
|
||||
constructor(props: ActualPageProps) {
|
||||
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,
|
||||
});
|
||||
getEventsPromise.then(events => {
|
||||
this.setState({
|
||||
events,
|
||||
});
|
||||
});
|
||||
return getEventsPromise;
|
||||
}
|
||||
|
||||
fetchFeed = () => {
|
||||
const getFeedPromise = getFeed();
|
||||
getFeedPromise.then(feed => {
|
||||
this.setState({
|
||||
feed,
|
||||
});
|
||||
});
|
||||
return getFeedPromise;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/kilta/toiminta" />
|
||||
</Helmet>
|
||||
<ActualPageView events={this.state.events} feed={this.state.feed} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ActualPage;
|
||||
@@ -1,5 +0,0 @@
|
||||
.actual-page {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./ActualPage.scss";
|
||||
|
||||
export interface ActualPageProps {}
|
||||
export interface ActualPageState {}
|
||||
|
||||
class ActualPage extends React.Component<ActualPageProps, ActualPageState> {
|
||||
render() {
|
||||
return (
|
||||
<div className="actual-page">
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/kilta/toiminta" />
|
||||
</Helmet>
|
||||
Actual Page
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ActualPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import ActualPage from "./ActualPage";
|
||||
export default ActualPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminCommonPage from "./AdminCommonPage";
|
||||
export default AdminCommonPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminEventPage from "./AdminEventPage";
|
||||
export default AdminEventPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminFeedPage from "./AdminFeedPage";
|
||||
export default AdminFeedPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminFrontPage from "./AdminFrontPage";
|
||||
export default AdminFrontPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminLoginPage from "./AdminLoginPage";
|
||||
export default AdminLoginPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminLogoutPage from "./AdminLogoutPage";
|
||||
export default AdminLogoutPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import AdminSignupPage from "./AdminSignupPage";
|
||||
export default AdminSignupPage;
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import Header from "../../components/Header";
|
||||
import Footer from "../../components/Footer";
|
||||
import React from "react";
|
||||
import Header from "@components/Header";
|
||||
import Footer from "@components/Footer";
|
||||
|
||||
export interface CommonPageProps {
|
||||
page: any;
|
||||
@@ -1,2 +0,0 @@
|
||||
import CommonPage from "./CommonPage";
|
||||
export default CommonPage;
|
||||
@@ -1,12 +1,8 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./ContactsPage.scss";
|
||||
import { ColorEnum, getColor, getHoverColor } from "../../components/ColorDiv/ColorDiv";
|
||||
import { StaticContext } from "../../server/StaticContext";
|
||||
import PageSection from "../../components/PageSection";
|
||||
import { getContacts, Occupation, Committee, getCommittees } from "../../models/Contacts";
|
||||
import CommitteeContainer from "../../components/CommitteeContainer";
|
||||
import Anchor from "../../components/Anchor/index";
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { StaticContext } from "@server/StaticContext";
|
||||
import { getContacts, Occupation, Committee, getCommittees } from "@models/Contacts";
|
||||
import ContactsPageView from "@views/ContactsPage/ContactsPageView";
|
||||
|
||||
interface ContactsPageProps {
|
||||
staticContext: StaticContext;
|
||||
@@ -76,39 +72,13 @@ class ContactsPage extends React.Component<ContactsPageProps, ContactsPageState>
|
||||
|
||||
render() {
|
||||
const { contacts, committees } = this.state;
|
||||
const board = contacts.filter(x => x.role.is_board);
|
||||
return (
|
||||
<div className="contacts-page">
|
||||
<>
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/INSERT_PATH_HERE!" />
|
||||
</Helmet>
|
||||
<PageSection backgroundColor={ColorEnum.White} textColor={ColorEnum.DarkBlue} center>
|
||||
<p>
|
||||
Asiaa olisi, mutta kehen ottaa yhteyttä?<br />
|
||||
Tämä sivu yrittää valottaa sen oikean ihmisen puhelinnumeroa ja sähköpostiosoitetta.
|
||||
</p>
|
||||
</PageSection>
|
||||
<PageSection backgroundColor={ColorEnum.White} textColor={ColorEnum.DarkBlue} bottomBorder center>
|
||||
<div>
|
||||
<CommitteeContainer name_fi="Hallitus" name_en="Board" contacts={board} />
|
||||
<p>
|
||||
Hallitukseen saa yhteyden lähettämällä sähköpostia <Anchor
|
||||
className={`${getColor(ColorEnum.Blue)} ${getHoverColor(ColorEnum.LightBlue)}`}
|
||||
to="mailto:sik-hallitus@list.ayy.fi">
|
||||
sik-hallitus@list.ayy.fi
|
||||
</Anchor>
|
||||
</p>
|
||||
</div>
|
||||
</PageSection>
|
||||
{committees.map((committee, index) => {
|
||||
const order = committee.name_fi === "Toimikunnattomat" ? 1 : 0;
|
||||
return (
|
||||
<PageSection key={index} style={{order}} backgroundColor={ColorEnum.White} center>
|
||||
<CommitteeContainer name_fi={committee.name_fi} name_en={committee.name_en} contacts={contacts.filter(x => x.role.committee.name_fi === committee.name_fi)} />
|
||||
</PageSection>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<ContactsPageView contacts={contacts} committees={committees} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import ContactsPage from "./ContactsPage";
|
||||
export default ContactsPage;
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./CorporatePage.scss";
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import CorporatePageView from "@views/CorporatePage/CorporatePageView";
|
||||
|
||||
export interface CorporatePageProps {}
|
||||
export interface CorporatePageState {}
|
||||
@@ -8,12 +8,12 @@ export interface CorporatePageState {}
|
||||
class CorporatePage extends React.Component<CorporatePageProps, CorporatePageState> {
|
||||
render() {
|
||||
return (
|
||||
<div className="corporate-page">
|
||||
<>
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/yritysyhteistyo" />
|
||||
</Helmet>
|
||||
Corporate Page
|
||||
</div>
|
||||
<CorporatePageView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import CorporatePage from "./CorporatePage";
|
||||
export default CorporatePage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import EventCreatePage from "./EventCreatePage";
|
||||
export default EventCreatePage;
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import { Event, getEvent } from "@models/Event";
|
||||
import EventPageView from "@views/EventPage/EventPageView";
|
||||
|
||||
interface MatchParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface EventPageState {
|
||||
event?: Event;
|
||||
}
|
||||
|
||||
type EventPageProps = RouteComponentProps<MatchParams>
|
||||
|
||||
class EventPage extends React.Component<EventPageProps, EventPageState> {
|
||||
constructor(props: EventPageProps) {
|
||||
super(props);
|
||||
const { id } = this.props.match.params;
|
||||
this.state = {
|
||||
event: null
|
||||
}
|
||||
this.fetchEvent(Number(id));
|
||||
}
|
||||
|
||||
|
||||
fetchEvent(id: number) {
|
||||
const eventPromise = getEvent(id);
|
||||
eventPromise.then(event => {
|
||||
this.setState({
|
||||
event,
|
||||
});
|
||||
});
|
||||
return eventPromise;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { event } = this.state;
|
||||
if (!event) return <div>Loading</div>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/INSERT_PATH_HERE!" />
|
||||
</Helmet>
|
||||
<EventPageView event={event} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EventPage;
|
||||
@@ -1,18 +0,0 @@
|
||||
.event-page {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.event-banner {
|
||||
width: 300px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.event-signup-buttons {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./EventPage.scss";
|
||||
import { Event, getEvent } from "../../models/Event";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import Button, { ButtonType } from "../../components/Button";
|
||||
import Anchor from "../../components/Anchor";
|
||||
import PageSection from "../../components/PageSection";
|
||||
import { ColorEnum } from "../../components/ColorDiv/ColorDiv";
|
||||
import MainSection from "../../components/MainSection";
|
||||
import AsideSection from "../../components/AsideSection/AsideSection";
|
||||
|
||||
interface MatchParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface EventPageOwnProps {}
|
||||
|
||||
export interface EventPageState {
|
||||
event?: Event;
|
||||
}
|
||||
|
||||
type EventPageProps = EventPageOwnProps & RouteComponentProps<MatchParams>
|
||||
|
||||
class EventPage extends React.Component<EventPageProps, EventPageState> {
|
||||
constructor(props: EventPageProps) {
|
||||
super(props);
|
||||
const { id } = this.props.match.params;
|
||||
this.state = {
|
||||
event: null
|
||||
}
|
||||
this.fetchEvent(Number(id));
|
||||
}
|
||||
|
||||
|
||||
fetchEvent(id: number) {
|
||||
const eventPromise = getEvent(id);
|
||||
eventPromise.then(event => {
|
||||
this.setState({
|
||||
event,
|
||||
});
|
||||
});
|
||||
return eventPromise;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { event } = this.state;
|
||||
if (!event) return <div>Loading</div>
|
||||
|
||||
return (
|
||||
<div className="event-page">
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/INSERT_PATH_HERE!" />
|
||||
</Helmet>
|
||||
<PageSection backgroundColor={ColorEnum.White}>
|
||||
<AsideSection textColor={ColorEnum.Black} />
|
||||
<MainSection textColor={ColorEnum.Black}>
|
||||
<img className="event-banner" src={event.tags[0].icon} alt={event.title_fi} ></img>
|
||||
<h1 className="event-title">{event.title_fi}</h1>
|
||||
<p>
|
||||
{event.description_fi}
|
||||
</p>
|
||||
<p>
|
||||
{event.content_fi}
|
||||
</p>
|
||||
{/* We may have multiple signup forms. Generate own Button for each one */}
|
||||
<div className="event-signup-buttons">
|
||||
{event.signupForm.map(sf => (
|
||||
<Anchor key={sf.id} to={`/signup/${sf.id}`}>
|
||||
<Button type={ButtonType.Filled} onClick={() => {}}>
|
||||
{sf.title}
|
||||
</Button>
|
||||
</Anchor>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</MainSection>
|
||||
<AsideSection backgroundColor={ColorEnum.White} textColor={ColorEnum.Black} />
|
||||
</PageSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EventPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import EventPage from "./EventPage";
|
||||
export default EventPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import FeedCreatePage from "./FeedCreatePage";
|
||||
export default FeedCreatePage;
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import FreshmenPageView from "@views/FreshmenPage/FreshmenPageView";
|
||||
|
||||
export interface FreshmenPageProps {}
|
||||
export interface FreshmenPageState {}
|
||||
|
||||
const FreshmenPage: React.FC = () => (
|
||||
<>
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/toiminta/fuksit" />
|
||||
</Helmet>
|
||||
<FreshmenPageView />
|
||||
</>
|
||||
)
|
||||
|
||||
export default FreshmenPage;
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./FreshmenPage.scss";
|
||||
|
||||
export interface FreshmenPageProps {}
|
||||
export interface FreshmenPageState {}
|
||||
|
||||
class FreshmenPage extends React.Component<FreshmenPageProps, FreshmenPageState> {
|
||||
render() {
|
||||
return (
|
||||
<div className="freshmen-page">
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/toiminta/fuksit" />
|
||||
</Helmet>
|
||||
Freshmen Page
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FreshmenPage;
|
||||
@@ -1,2 +0,0 @@
|
||||
import FreshmenPage from "./FreshmenPage";
|
||||
export default FreshmenPage;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user