Merge branch 'master' into 'production'
dev to prod: styling, added fopas etc See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!74
This commit is contained in:
@@ -5,3 +5,4 @@ node_modules
|
||||
# don't lint nyc coverage output
|
||||
coverage
|
||||
next-env.d.ts
|
||||
.eslintrc.js
|
||||
@@ -0,0 +1,33 @@
|
||||
module.exports = {
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json"
|
||||
},
|
||||
extends: [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"airbnb-typescript",
|
||||
"next",
|
||||
"next/core-web-vitals"
|
||||
],
|
||||
rules: {
|
||||
"max-len": [
|
||||
"warn",
|
||||
240
|
||||
],
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"import/prefer-default-export": "warn",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/jsx-one-expression-per-line": "off",
|
||||
"eslintreact/jsx-one-expression-per-line": "off",
|
||||
// Temp
|
||||
"import/no-cycle": "warn",
|
||||
"react/no-array-index-key": "warn",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"airbnb-typescript"
|
||||
],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": [],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
"max-len": [
|
||||
"warn",
|
||||
240
|
||||
],
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"import/prefer-default-export": "warn",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react/prop-types": "off",
|
||||
// Temp
|
||||
"react/jsx-one-expression-per-line": "off",
|
||||
"react/no-array-index-key": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off"
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@ install:
|
||||
stage: setup
|
||||
script:
|
||||
- npm ci
|
||||
after_script:
|
||||
- node -v
|
||||
- npm -v
|
||||
artifacts:
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
Vendored
+1
@@ -1,2 +1,3 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
+1
-4
@@ -10,8 +10,5 @@ module.exports = withBundleAnalyzer({
|
||||
"static.sahkoinsinoorikilta.fi",
|
||||
"api.dev.sahkoinsinoorikilta.fi",
|
||||
],
|
||||
},
|
||||
future: {
|
||||
webpack5: true,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Generated
+1833
-1457
File diff suppressed because it is too large
Load Diff
+9
-13
@@ -20,8 +20,8 @@
|
||||
"postbuild": "next-sitemap",
|
||||
"export": "next export",
|
||||
"lint": "npm run lint:es && npm run lint:css",
|
||||
"lint:es": "eslint . --ext .ts,.tsx",
|
||||
"lint:es:fix": "eslint --fix . --ext .ts,.tsx",
|
||||
"lint:es": "next lint",
|
||||
"lint:es:fix": "next lint --fix",
|
||||
"lint:css": "stylelint \"./src/**/*.{ts,tsx}\"",
|
||||
"dev": "next dev",
|
||||
"start": "next dev",
|
||||
@@ -37,21 +37,17 @@
|
||||
"@types/react": "^17.0.11",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-csv": "^1.1.1",
|
||||
"@types/react-dom": "^17.0.7",
|
||||
"@types/react-dom": "^17.0.8",
|
||||
"@types/react-jsonschema-form": "^1.7.5",
|
||||
"@types/shortid": "^0.0.29",
|
||||
"@types/styled-components": "^5.1.10",
|
||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
"@typescript-eslint/parser": "^4.27.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.1",
|
||||
"babel-plugin-styled-components": "^1.12.0",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-airbnb-typescript": "^12.3.1",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-config-next": "^11.0.1",
|
||||
"husky": "^6.0.0",
|
||||
"next-sitemap": "^1.6.116",
|
||||
"next-sitemap": "^1.6.124",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
@@ -60,13 +56,13 @@
|
||||
"typescript": "^4.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/bundle-analyzer": "^10.2.3",
|
||||
"@next/bundle-analyzer": "^11.0.1",
|
||||
"axios": "^0.21.1",
|
||||
"date-fns": "^2.22.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "^10.2.3",
|
||||
"next": "^11.0.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default (): JSX.Element => (
|
||||
const Icons = (): JSX.Element => (
|
||||
<>
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png" />
|
||||
@@ -54,3 +53,5 @@ export default (): JSX.Element => (
|
||||
<link rel="yandex-tableau-widget" href="/favicons/yandex-browser-manifest.json" />
|
||||
</>
|
||||
);
|
||||
|
||||
export default Icons;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default (): JSX.Element => (
|
||||
const Logo = (): JSX.Element => (
|
||||
// eslint-disable-next-line react/no-danger
|
||||
<head dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
@@ -55,3 +54,5 @@ export default (): JSX.Element => (
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Logo;
|
||||
|
||||
@@ -8,6 +8,7 @@ const Container = styled.label`
|
||||
padding-left: 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.5rem; /* 24px */
|
||||
line-height: 1;
|
||||
user-select: none;
|
||||
|
||||
/* On mouse-over, add a grey background color */
|
||||
|
||||
@@ -22,7 +22,7 @@ type CheckboxesProps = Omit<WidgetProps, "options"> & {
|
||||
};
|
||||
|
||||
const CheckboxContainer = styled.div`
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
const Checkboxes: React.FC<CheckboxesProps> = ({
|
||||
|
||||
@@ -7,6 +7,7 @@ const Container = styled.label`
|
||||
padding-left: 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.5rem; /* 24px */
|
||||
line-height: 1;
|
||||
user-select: none;
|
||||
|
||||
/* On mouse-over, add a grey background color */
|
||||
|
||||
@@ -8,7 +8,7 @@ type RadioButtonWidgetProps = Omit<WidgetProps, "options"> & {
|
||||
};
|
||||
|
||||
const RadioButtonContainer = styled.div`
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
const RadioButtonWidget: React.FC<RadioButtonWidgetProps> = (props) => {
|
||||
|
||||
@@ -27,7 +27,9 @@ const Heading = styled.h3`
|
||||
|
||||
const SectionDividerWidget: React.FC<SectionDividerWidgetProps> = ({ label }) => (
|
||||
<Heading>
|
||||
{label} {getIconByLabel(label)}
|
||||
{label}
|
||||
|
||||
{getIconByLabel(label)}
|
||||
</Heading>
|
||||
);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
handleListOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
const lst = val.split(",").map((p) => p.trimLeft());
|
||||
const lst = val.split(";").map((p) => p.trimLeft());
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = lst;
|
||||
onChange(questions);
|
||||
@@ -31,7 +31,7 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
if (val !== "") {
|
||||
const lst = val.split(",").map((p) => p.trimLeft());
|
||||
const lst = val.split(";").map((p) => p.trimLeft());
|
||||
// Ignore everything else but the two first values
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = lst.splice(0, 2);
|
||||
@@ -51,19 +51,20 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
requiredField() {
|
||||
requiredField(): JSX.Element {
|
||||
const { inputProps } = this.props;
|
||||
const { questions, index } = inputProps;
|
||||
return (
|
||||
<Checkbox
|
||||
checked={questions[index].required}
|
||||
onChange={this.handleRequiredChange(questions, index)}
|
||||
>Required?
|
||||
>
|
||||
Required?
|
||||
</Checkbox>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const { inputProps } = this.props;
|
||||
const {
|
||||
type, value, questions, index,
|
||||
@@ -92,12 +93,12 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
|
||||
if (type === "integer") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(",");
|
||||
const joinedValue = lst.join(";");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Minimum,Maximum"
|
||||
placeholder="Minimum;Maximum"
|
||||
value={joinedValue}
|
||||
onChange={this.handleIntegerOptionsChange(questions, index)}
|
||||
/>
|
||||
@@ -108,12 +109,12 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
|
||||
if (type === "radiobutton") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(",");
|
||||
const joinedValue = lst.join(";");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Yes,no,maybe"
|
||||
placeholder="Yes;no;maybe"
|
||||
value={joinedValue}
|
||||
onChange={this.handleListOptionsChange(questions, index)}
|
||||
/>
|
||||
@@ -124,12 +125,12 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
|
||||
if (type === "checkbox") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(",");
|
||||
const joinedValue = lst.join(";");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="A,B,C"
|
||||
placeholder="A;B;C"
|
||||
value={joinedValue}
|
||||
onChange={this.handleListOptionsChange(questions, index)}
|
||||
/>
|
||||
|
||||
+3
-1
@@ -26,7 +26,9 @@ const NotFoundPage: NextPage = () => (
|
||||
<Header />
|
||||
<NotFound id="not-found">
|
||||
<p>
|
||||
<strong>404</strong> | Ei vaan löydy
|
||||
<strong>404</strong>
|
||||
{" "}
|
||||
| Ei vaan löydy
|
||||
</p>
|
||||
</NotFound>
|
||||
</>
|
||||
|
||||
+1
-2
@@ -126,11 +126,10 @@ const AppContainer = styled.div`
|
||||
background-color: ${colors.white};
|
||||
`;
|
||||
|
||||
const Web20App = ({ Component, pageProps }: AppProps) => (
|
||||
const Web20App = ({ Component, pageProps }: AppProps): JSX.Element => (
|
||||
<>
|
||||
<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" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Aalto-yliopiston Sähköinsinöörikilta ry</title>
|
||||
|
||||
@@ -29,12 +29,13 @@ export default class MyDocument extends Document<{ styleTags: unknown }> {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const { styleTags } = this.props;
|
||||
return (
|
||||
<Html lang="fi">
|
||||
<Head>
|
||||
<HTMLLogo />
|
||||
<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="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css" />
|
||||
<Favicons />
|
||||
</Head>
|
||||
|
||||
@@ -46,7 +46,8 @@ const AdminLoginPage: NextPage = () => {
|
||||
<div className="error">You have to log in first.</div>
|
||||
)}
|
||||
<form className="admin-login-form" onSubmit={handleSubmit}>
|
||||
<label>Username
|
||||
<label>
|
||||
Username
|
||||
<input
|
||||
id="login-username"
|
||||
type="text"
|
||||
@@ -57,7 +58,8 @@ const AdminLoginPage: NextPage = () => {
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<label>Password
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
id="login-password"
|
||||
type="password"
|
||||
|
||||
@@ -61,7 +61,10 @@ const SignupEmailPage: NextPage = () => {
|
||||
|
||||
return (
|
||||
<AdminListCommon>
|
||||
<h1>{title}: Sign-ups</h1>
|
||||
<h1>
|
||||
{title}
|
||||
: Sign-ups
|
||||
</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -4,38 +4,27 @@ import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import Event from "@models/Event";
|
||||
import EventApi from "@api/eventApi";
|
||||
import useFetchEvents from "@hooks/useFetchEvents";
|
||||
import EventPageView from "@views/EventPage/EventPageView";
|
||||
import PageWrapper from "@views/common/PageWrapper";
|
||||
import LoadingView from "@views/common/LoadingView";
|
||||
import NotFoundPage from "@pages/404";
|
||||
|
||||
interface InitialProps {
|
||||
initialEvent: Event;
|
||||
event: Event;
|
||||
}
|
||||
|
||||
const EventPage: NextPage<InitialProps> = ({ initialEvent }) => {
|
||||
const EventPage: NextPage<InitialProps> = ({ event }) => {
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { data, error } = useFetchEvents({ initialData: initialEvent, id: id as string });
|
||||
|
||||
if (router.isFallback) return <LoadingView />;
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<NotFoundPage />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/events/${id}`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<EventPageView event={data as Event} />
|
||||
<EventPageView event={event} />
|
||||
</PageWrapper>
|
||||
</>
|
||||
);
|
||||
@@ -58,17 +47,17 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
|
||||
const { id } = params;
|
||||
let notFound = false;
|
||||
let initialEvent: Event;
|
||||
let event: Event;
|
||||
try {
|
||||
initialEvent = await EventApi.getEvent(Number(id));
|
||||
event = await EventApi.getEvent(Number(id));
|
||||
} catch (err) {
|
||||
notFound = true;
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
initialEvent,
|
||||
event,
|
||||
},
|
||||
revalidate: 10,
|
||||
revalidate: 10, // Required for deleting hidden pages
|
||||
notFound,
|
||||
};
|
||||
};
|
||||
|
||||
+7
-18
@@ -4,38 +4,27 @@ import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import Post from "@models/Feed";
|
||||
import FeedApi from "@api/feedApi";
|
||||
import useFetchFeed from "@hooks/useFetchFeed";
|
||||
import FeedPageView from "@views/FeedPage/FeedPageView";
|
||||
import PageWrapper from "@views/common/PageWrapper";
|
||||
import LoadingView from "@views/common/LoadingView";
|
||||
import NotFoundPage from "@pages/404";
|
||||
|
||||
interface InitialProps {
|
||||
initialPost: Post;
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const FeedPage: NextPage<InitialProps> = ({ initialPost }) => {
|
||||
const FeedPage: NextPage<InitialProps> = ({ post }) => {
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { data, error } = useFetchFeed({ initialData: initialPost, id: id as string });
|
||||
|
||||
if (router.isFallback) return <LoadingView />;
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<NotFoundPage />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/feed/${id}`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<FeedPageView post={data} />
|
||||
<FeedPageView post={post} />
|
||||
</PageWrapper>
|
||||
</>
|
||||
);
|
||||
@@ -58,18 +47,18 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
|
||||
const { id } = params;
|
||||
let notFound = false;
|
||||
let initialPost: Post;
|
||||
let post: Post;
|
||||
try {
|
||||
initialPost = await FeedApi.getPost(Number(id));
|
||||
post = await FeedApi.getPost(Number(id));
|
||||
} catch (err) {
|
||||
notFound = true;
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialPost,
|
||||
post,
|
||||
},
|
||||
revalidate: 10,
|
||||
revalidate: 10, // Required for deleting hidden pages
|
||||
notFound,
|
||||
};
|
||||
};
|
||||
|
||||
+30
-17
@@ -2,7 +2,10 @@ import React from "react";
|
||||
import { NextPage, GetStaticProps, GetStaticPaths } from "next";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { ISubmitEvent } from "react-jsonschema-form";
|
||||
import { toast } from "react-toastify";
|
||||
import axios from "axios";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { Signup, SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
import SignUpPageView from "@views/SignUpPage/SignUpPageView";
|
||||
@@ -12,34 +15,44 @@ import noop from "@utils/noop";
|
||||
import NotFoundPage from "@pages/404";
|
||||
|
||||
type InitialProps = {
|
||||
form: SignupForm;
|
||||
initialForm: SignupForm;
|
||||
};
|
||||
|
||||
const SignUpPage: NextPage<InitialProps> = ({ form }) => {
|
||||
const router = useRouter();
|
||||
const FORM_URL = `${process.env.NEXT_PUBLIC_API_URL}/signupForm/`;
|
||||
|
||||
if (router.isFallback) {
|
||||
const SignUpPage: NextPage<InitialProps> = ({ initialForm }) => {
|
||||
const router = useRouter();
|
||||
const id = String(initialForm?.id ?? "");
|
||||
const URL = `${FORM_URL}${id}/`;
|
||||
const { data, error } = useSWR<SignupForm>(URL, (url) => axios.get(url).then((res) => res.data), { initialData: initialForm });
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
// TODO: Shows LoadingView on client-side fetch error. Maybe something else preferred?
|
||||
if (router.isFallback || error) {
|
||||
return <LoadingView />;
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
if (!data) {
|
||||
return (
|
||||
<NotFoundPage />
|
||||
);
|
||||
}
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const onSubmit = async ({ formData }: ISubmitEvent<string>) => {
|
||||
const payload: Signup = {
|
||||
signupForm_id: form.id,
|
||||
answer: data.formData,
|
||||
signupForm_id: data.id,
|
||||
answer: formData,
|
||||
};
|
||||
|
||||
try {
|
||||
await SignupApi.createSignup(payload);
|
||||
toast.success("Sign-up submitted successfully 😎");
|
||||
router.push(window.location.href); // TODO: Fetch/update signup list, so user sees the signup in the list
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
mutate(URL);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error("Uh oh! Sign-up failed! 😟");
|
||||
}
|
||||
};
|
||||
@@ -47,11 +60,11 @@ const SignUpPage: NextPage<InitialProps> = ({ form }) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/signup/${form.id}`} />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/signup/${data.id}`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<SignUpPageView
|
||||
signUpForm={form}
|
||||
signUpForm={data}
|
||||
formData={{}}
|
||||
onChange={noop}
|
||||
onSubmit={onSubmit}
|
||||
@@ -78,17 +91,17 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
|
||||
const { id } = params;
|
||||
let notFound = false;
|
||||
let form: SignupForm;
|
||||
let initialForm: SignupForm;
|
||||
try {
|
||||
form = await SignupApi.getForm(Number(id));
|
||||
initialForm = await SignupApi.getForm(Number(id));
|
||||
} catch {
|
||||
notFound = true;
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
form,
|
||||
initialForm,
|
||||
},
|
||||
revalidate: 10,
|
||||
revalidate: 10, // Required for deleting hidden pages
|
||||
notFound,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -60,7 +60,6 @@ const EditSignUpPage: NextPage = () => {
|
||||
|
||||
try {
|
||||
await SignupApi.updateSignup(payload, uuid);
|
||||
// TODO: Update signup list, so user sees possible changes in the list
|
||||
toast.success("Sign-up updated successfully 😎");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -30,7 +30,7 @@ const orderedCommittees = [
|
||||
Others,
|
||||
];
|
||||
|
||||
const blank_profile = "/img/blank_profile.png";
|
||||
const blankProfile = "/img/blank_profile.png";
|
||||
|
||||
const BlueLink = styled(Link)`
|
||||
color: ${colors.blue1};
|
||||
@@ -91,7 +91,7 @@ const CommitteeContainer: React.FC<{
|
||||
name={representative.name}
|
||||
phone={representative.phone_number}
|
||||
email={representative.email}
|
||||
image={(committee.name_en === "Board") ? (representative.image || blank_profile) : null}
|
||||
image={(committee.name_en === "Board") ? (representative.image || blankProfile) : null}
|
||||
role_fi={role.name_fi}
|
||||
role_en={role.name_en}
|
||||
/>
|
||||
@@ -126,7 +126,8 @@ const ContactsPageView: React.FC = () => (
|
||||
<TextSection>
|
||||
<h1>Yhteystiedot</h1>
|
||||
<p>
|
||||
Asiaa olisi, mutta kehen ottaa yhteyttä?<br />
|
||||
Asiaa olisi, mutta kehen ottaa yhteyttä?
|
||||
<br />
|
||||
Tämä sivu yrittää valottaa sen oikean ihmisen puhelinnumeroa ja sähköpostiosoitetta.
|
||||
</p>
|
||||
<aside>
|
||||
|
||||
@@ -11,6 +11,11 @@ const FreshmenPageHero: React.FC = () => (
|
||||
/>
|
||||
|
||||
<HeroAside bgColor="lightTurquoise">
|
||||
<HeroAsideItem
|
||||
header="Lue killan fuksiopas"
|
||||
link="https://static.sahkoinsinoorikilta.fi/FTMK/Fuksiopas2021.pdf"
|
||||
linkText="lue fuksiopas täältä!"
|
||||
/>
|
||||
<HeroAsideItem
|
||||
header="Seuraa killan tiedotusta"
|
||||
link="https://t.me/joinchat/rKg3rCtAVkkyNTdk"
|
||||
|
||||
@@ -15,6 +15,17 @@ const EMAIL_LINK_MAILTO = `mailto:${EMAIL_LINK}`;
|
||||
const ImageContainer = styled.div`
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin: auto;
|
||||
`;
|
||||
|
||||
const QRImages = styled.img`
|
||||
width: 10em;
|
||||
height: 10em;
|
||||
`;
|
||||
|
||||
const FopasImage = styled.img`
|
||||
width: 15em;
|
||||
margin-bottom: 1em;
|
||||
`;
|
||||
|
||||
const FreshmenPageView: React.FC = () => (
|
||||
@@ -92,6 +103,16 @@ const FreshmenPageView: React.FC = () => (
|
||||
</div>
|
||||
<div>
|
||||
<InfoBox>
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi/FTMK/Fuksiopas2021.pdf">
|
||||
<FopasImage
|
||||
src="https://static.sahkoinsinoorikilta.fi/FTMK/Fuksiopas2021-kansi.png"
|
||||
/>
|
||||
</Link>
|
||||
<h6>Killan Fuksiopas</h6>
|
||||
<p>Ennen opintojen alkua on hyvä tutustua killan fuksioppaaseen. Sitä pääset selailemaan
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi/FTMK/Fuksiopas2021.pdf"> tästä.</Link>
|
||||
</p>
|
||||
<br/>
|
||||
<h6>Telegram?</h6>
|
||||
<p>
|
||||
Telegram on pikaviestinpalvelu, jota käytetään otaniemessä paljon.
|
||||
@@ -101,23 +122,13 @@ const FreshmenPageView: React.FC = () => (
|
||||
<p>
|
||||
SIK:n fukseilla on oma Telegram-ryhmä, jonne pääset liitymään tästä:
|
||||
</p>
|
||||
<Image
|
||||
<QRImages
|
||||
src="https://static.sahkoinsinoorikilta.fi/FTMK/sik-fuksit-2021-tg.png"
|
||||
alt="SIK-Fuksit 2021 TG"
|
||||
layout="responsive"
|
||||
width={0}
|
||||
height={0}
|
||||
objectFit="contain"
|
||||
/>
|
||||
<p>tai <Link to="https://tinyurl.com/sik-fuksit-2021">tästä</Link></p>
|
||||
<p>Liity myös samalla SIK-fuksien tiedotuskanavalle tästä:</p>
|
||||
<Image
|
||||
<QRImages
|
||||
src="https://static.sahkoinsinoorikilta.fi/FTMK/sik-fuksit-2021-tiedotus-tg.png"
|
||||
alt="SIK-Fuksit 2021 Tiedotus TG"
|
||||
layout="responsive"
|
||||
width={0}
|
||||
height={0}
|
||||
objectFit="contain"
|
||||
/>
|
||||
<p>tai <Link to="https://tinyurl.com/sik-fuksit-2021-tiedotus">tästä</Link></p>
|
||||
</InfoBox>
|
||||
|
||||
@@ -76,7 +76,7 @@ const SignUpPageView: React.FC<SignUpPageViewProps> = ({
|
||||
<h6>
|
||||
{t("Ilmoittautuneet")} {signUpForm.quota > 0 && (` (${signUpForm.signups.length}/${signUpForm.quota})`)}:
|
||||
</h6>
|
||||
<ol>
|
||||
<ol data-e2e="signup-list">
|
||||
{signUpForm.signups.map((s, idx) => (
|
||||
<li key={idx} className={signUpForm.quota && idx + 1 > signUpForm.quota ? "reserved" : ""}>{s}</li>
|
||||
))}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"src": "tests/testcafe",
|
||||
"browsers": "all",
|
||||
"browsers": "chrome",
|
||||
"concurrency": 1,
|
||||
"screenshots": {
|
||||
"path": "e2e-screenshots/",
|
||||
|
||||
@@ -58,4 +58,6 @@ test("User signups to event from front page", async (t) => {
|
||||
.expect(
|
||||
statusMessage.innerText,
|
||||
).eql("Sign-up submitted successfully 😎");
|
||||
|
||||
await t.expect(Selector("[data-e2e=\"signup-list\"] > li").nth(0).innerText).eql("Testi Testeri");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user