Merge branch 'master' into 'production'
Prod deploy See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!45
@@ -1 +1,2 @@
|
||||
NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
NEXT_PUBLIC_SITE_URL=https://dev.sahkoinsinoorikilta.fi
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
NEXT_PUBLIC_API_URL=https://api.sahkoinsinoorikilta.fi/api
|
||||
NEXT_PUBLIC_API_URL=https://api.sahkoinsinoorikilta.fi/api
|
||||
NEXT_PUBLIC_SITE_URL=https://sahkoinsinoorikilta.fi
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
NEXT_PUBLIC_SITE_URL=https://dev.sahkoinsinoorikilta.fi
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
.next
|
||||
# don't lint nyc coverage output
|
||||
coverage
|
||||
next-env.d.ts
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
@@ -12,8 +14,7 @@
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"airbnb-typescript",
|
||||
"airbnb/hooks"
|
||||
"airbnb-typescript"
|
||||
],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
@@ -28,10 +29,7 @@
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"plugins": [],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
@@ -41,7 +39,7 @@
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
"max-len": [
|
||||
"warn",
|
||||
160
|
||||
240
|
||||
],
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
|
||||
@@ -36,3 +36,7 @@ yarn-error.log*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# SEO
|
||||
public/robots.txt
|
||||
public/sitemap.xml
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
_
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run lint
|
||||
@@ -3,18 +3,19 @@ FROM node:14-alpine as builder
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY tsconfig.json next-env.d.ts .babelrc next.config.js ./
|
||||
COPY tsconfig.json next-env.d.ts .babelrc next.config.js next-sitemap.js ./
|
||||
COPY src src/
|
||||
COPY public public/
|
||||
COPY types types/
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NEXT_PUBLIC_API_URL=https://api.dev.sahkoinsinoorikilta.fi/api
|
||||
ENV NEXT_PUBLIC_SITE_URL=https://dev.sahkoinsinoorikilta.fi
|
||||
RUN npm run build
|
||||
|
||||
FROM node:14-alpine as server
|
||||
WORKDIR /www
|
||||
COPY package.json package-lock.json next.config.js ./
|
||||
COPY package.json package-lock.json next.config.js next-sitemap.js ./
|
||||
COPY --from=builder .next .next
|
||||
COPY --from=builder node_modules node_modules
|
||||
COPY --from=builder public public
|
||||
|
||||
@@ -3,18 +3,19 @@ FROM node:14-alpine as builder
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY tsconfig.json next-env.d.ts .babelrc next.config.js ./
|
||||
COPY tsconfig.json next-env.d.ts .babelrc next.config.js next-sitemap.js ./
|
||||
COPY src src/
|
||||
COPY public public/
|
||||
COPY types types/
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NEXT_PUBLIC_API_URL=https://api.sahkoinsinoorikilta.fi/api
|
||||
ENV NEXT_PUBLIC_SITE_URL=https://sahkoinsinoorikilta.fi
|
||||
RUN npm run build
|
||||
|
||||
FROM node:14-alpine as server
|
||||
WORKDIR /www
|
||||
COPY package.json package-lock.json next.config.js ./
|
||||
COPY package.json package-lock.json next.config.js next-sitemap.js ./
|
||||
COPY --from=builder .next .next
|
||||
COPY --from=builder node_modules node_modules
|
||||
COPY --from=builder public public
|
||||
|
||||
@@ -21,28 +21,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
React Webpack Typescript Starter
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Viktor Persson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@@ -1,46 +1,41 @@
|
||||
# Web 2.0 Frontend
|
||||
|
||||
Minimal starter kit with hot module replacement (HMR) for rapid development.
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
* **[React](https://facebook.github.io/react/)** (16.x)
|
||||
* **[Webpack](https://webpack.js.org/)** (4.x)
|
||||
* **[Typescript](https://www.typescriptlang.org/)** (2.x)
|
||||
* **[Hot Module Replacement (HMR)](https://webpack.js.org/concepts/hot-module-replacement/)** using [React Hot Loader](https://github.com/gaearon/react-hot-loader) (4.x)
|
||||
* [Babel](http://babeljs.io/) (6.x)
|
||||
* [SASS](http://sass-lang.com/)
|
||||
* [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 [TS-Loader](https://webpack.js.org/guides/typescript/)
|
||||
* Code quality (linting) for Typescript and SASS/CSS.
|
||||
* **[React](https://facebook.github.io/react/)** (17.x)
|
||||
* **[Typescript](https://www.typescriptlang.org/)** (4.x)
|
||||
* **[Next.js](https://nextjs.org/)** (10.x)
|
||||
* [Testcafe](https://devexpress.github.io/testcafe/) - E2E Testing framework
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone/download repo
|
||||
2. Install node v14
|
||||
2. Install node v14 ([`nvm`](https://github.com/nvm-sh/nvm))
|
||||
3. `npm install`
|
||||
|
||||
## Usage
|
||||
## Getting Started
|
||||
|
||||
### Development
|
||||
|
||||
`npm start`
|
||||
Run the dev-server:
|
||||
|
||||
* Build app continously (HMR enabled)
|
||||
* App served @ `http://localhost:3000`
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
### Create new component
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
`npm run plop`
|
||||
|
||||
* Create a new component interactively using plop templates
|
||||
You can start editing the website by modifying source files. The page auto-updates as you edit the file.
|
||||
|
||||
### Production
|
||||
|
||||
`npm run start-prod`
|
||||
```bash
|
||||
npm run build
|
||||
npm run start-prod
|
||||
```
|
||||
|
||||
* Build app once (HMR disabled)
|
||||
* App served @ `http://localhost:3000`
|
||||
* App served @ `http://localhost:80`
|
||||
|
||||
---
|
||||
|
||||
@@ -48,22 +43,25 @@ Minimal starter kit with hot module replacement (HMR) for rapid development.
|
||||
|
||||
Command | Description
|
||||
--- | ---
|
||||
`npm run start-dev` | Build app continously (HMR enabled) and serve @ `http://localhost:3000`
|
||||
`npm run start-prod` | Build app once (HMR disabled) and serve @ `http://localhost:3000`
|
||||
`npm run build` | Build app to `/dist/`
|
||||
`npm run test` | Run e2e and unit tests
|
||||
`npm run test:unit` | Run unit tests with Jest
|
||||
`npm run test:e2e` | Run end-to-end tests with TestCafé
|
||||
`npm run lint` | Run Typescript and SASS linter
|
||||
`npm run lint:ts` | Run Typescript linter
|
||||
`npm run lint:sass` | Run SASS linter
|
||||
`npm run start` | (alias of `npm run start-dev`)
|
||||
`npm run dev` | Build app continously (HMR enabled) and serve @ `http://localhost:3000`
|
||||
`npm run start` | (alias of `npm run dev`)
|
||||
`npm run build` | Build app to `/.next/`
|
||||
`npm run start-prod` | Serve built app (HMR disabled) @ `http://localhost:80`
|
||||
`npm run test` | Run e2e tests verbose with Chrome
|
||||
`npm run test:e2e` | Run end-to-end tests with Headless Chrome
|
||||
`npm run lint` | Run Typescript and CSS linter
|
||||
`npm run lint:es` | Run Typescript linter (ESLint)
|
||||
`npm run lint:css` | Run Stylelint (CSS linter)
|
||||
|
||||
## See also
|
||||
## Learn More
|
||||
|
||||
* [React Webpack Babel Starter](https://github.com/vikpe/react-webpack-babel-starter)
|
||||
* [Isomorphic Webapp Starter](https://github.com/vikpe/isomorphic-webapp-starter)
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
* [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
* [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Copyright
|
||||
|
||||
Aalto-yliopiston Sähköinsinöörikilta ry
|
||||
Aalto-yliopiston Sähköinsinöörikilta ry
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
siteUrl: process.env.NEXT_PUBLIC_SITE_URL || "https://sahkoinsinoorikilta.fi",
|
||||
generateRobotsTxt: true,
|
||||
exclude: ["/events/*", "/feed/*", "/signup/*", "/admin/*"]
|
||||
}
|
||||
@@ -8,23 +8,22 @@
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"webpack",
|
||||
"next.js",
|
||||
"typescript",
|
||||
"babel",
|
||||
"sass",
|
||||
"hmr",
|
||||
"starter",
|
||||
"boilerplate"
|
||||
"styled-components"
|
||||
],
|
||||
"author": "Jan Tuomi",
|
||||
"author": "Aarni Halinen",
|
||||
"license": "MIT",
|
||||
"homepage": "https://sik.ayy.fi",
|
||||
"homepage": "https://sahkoinsinoorikilta.fi",
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"postbuild": "next-sitemap",
|
||||
"export": "next export",
|
||||
"lint": "npm run lint:es && npm run lint:css",
|
||||
"lint:es": "eslint \"./src/**/*.{ts,tsx}\"",
|
||||
"lint:es:fix": "eslint --fix \"./src/**/*.{ts,tsx}\"",
|
||||
"lint:es": "eslint . --ext .ts,.tsx",
|
||||
"lint:es:fix": "eslint --fix . --ext .ts,.tsx",
|
||||
"lint:css": "stylelint \"./src/**/*.{ts,tsx}\"",
|
||||
"dev": "next dev",
|
||||
"start": "next dev",
|
||||
"start-prod": "next start --port ${SERVER_PORT:=80}",
|
||||
"serve": "next start --port 3000",
|
||||
@@ -33,59 +32,55 @@
|
||||
"test:e2e:verbose": "npm-run-all -p -r serve testcafe:verbose",
|
||||
"testcafe": "testcafe --skip-js-errors -S -s 'e2e-screenshots' --app-init-delay 2000 chrome:headless tests/testcafe",
|
||||
"testcafe:verbose": "testcafe --skip-js-errors -S -s 'e2e-screenshots' --app-init-delay 2000 chrome tests/testcafe",
|
||||
"build-analyze": "ANALYZE=true npm run build"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-push": "npm run lint"
|
||||
}
|
||||
"build-analyze": "ANALYZE=true npm run build",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-cookie": "2.2.4",
|
||||
"@types/react": "16.8.18",
|
||||
"@types/js-cookie": "2.2.6",
|
||||
"@types/react": "17.0.2",
|
||||
"@types/react-beautiful-dnd": "13.0.0",
|
||||
"@types/react-csv": "1.1.1",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-jsonschema-form": "1.7.3",
|
||||
"@types/react-dom": "17.0.1",
|
||||
"@types/react-jsonschema-form": "1.7.4",
|
||||
"@types/shortid": "0.0.29",
|
||||
"@types/styled-components": "5.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.15.0",
|
||||
"@typescript-eslint/parser": "4.15.0",
|
||||
"babel-plugin-styled-components": "1.10.7",
|
||||
"eslint": "7.19.0",
|
||||
"@types/styled-components": "5.1.7",
|
||||
"@typescript-eslint/eslint-plugin": "4.16.1",
|
||||
"@typescript-eslint/parser": "4.16.1",
|
||||
"babel-plugin-styled-components": "1.12.0",
|
||||
"eslint": "7.21.0",
|
||||
"eslint-config-airbnb-typescript": "12.3.1",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||
"eslint-plugin-react": "7.22.0",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"husky": "1.3.1",
|
||||
"husky": "5.1.3",
|
||||
"next-sitemap": "^1.6.9",
|
||||
"npm-run-all": "4.1.5",
|
||||
"stylelint": "13.8.0",
|
||||
"stylelint": "13.11.0",
|
||||
"stylelint-config-recommended": "3.0.0",
|
||||
"stylelint-config-styled-components": "0.1.1",
|
||||
"stylelint-processor-styled-components": "1.10.0",
|
||||
"testcafe": "1.10.1",
|
||||
"typescript": "4.1.5",
|
||||
"typescript-plugin-styled-components": "1.4.4"
|
||||
"testcafe": "1.12.0",
|
||||
"typescript": "4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/bundle-analyzer": "10.0.5",
|
||||
"@next/bundle-analyzer": "10.0.7",
|
||||
"axios": "0.21.1",
|
||||
"date-fns": "2.0.0-alpha.27",
|
||||
"js-cookie": "2.2.0",
|
||||
"lodash": "4.17.20",
|
||||
"next": "10.0.7",
|
||||
"date-fns": "2.18.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"js-cookie": "2.2.1",
|
||||
"lodash": "4.17.21",
|
||||
"next": "10.0.8",
|
||||
"normalize.css": "8.0.1",
|
||||
"query-string": "6.5.0",
|
||||
"react": "17.0.1",
|
||||
"react-beautiful-dnd": "13.0.0",
|
||||
"react-csv": "2.0.3",
|
||||
"react-dom": "17.0.1",
|
||||
"react-jsonschema-form": "1.8.1",
|
||||
"react-markdown": "5.0.3",
|
||||
"react-mde": "11.0.0",
|
||||
"shortid": "2.2.14",
|
||||
"react-mde": "11.0.6",
|
||||
"shortid": "2.2.16",
|
||||
"styled-components": "5.2.1",
|
||||
"swr": "0.3.11"
|
||||
"swr": "0.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 402 KiB |
|
After Width: | Height: | Size: 402 KiB |
|
After Width: | Height: | Size: 593 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 315 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 556 KiB |
|
After Width: | Height: | Size: 865 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 458 KiB |
|
After Width: | Height: | Size: 914 KiB |
|
After Width: | Height: | Size: 574 KiB |
|
After Width: | Height: | Size: 407 KiB |
|
After Width: | Height: | Size: 715 KiB |
|
After Width: | Height: | Size: 954 KiB |
|
After Width: | Height: | Size: 865 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 954 KiB |
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from "axios";
|
||||
import Event from "@models/Event";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
export const URL = `${process.env.NEXT_PUBLIC_API_URL}/events/`;
|
||||
|
||||
export interface Options {
|
||||
onlyNonPast?: boolean;
|
||||
limit?: number;
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
class EventApi {
|
||||
static async getEvent(id: number, auth = false): Promise<Event> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}${id}/`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async getEvents(options: Options = {}): Promise<Event[]> {
|
||||
const { onlyNonPast, limit, auth } = options;
|
||||
try {
|
||||
const params = {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
};
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}`, {
|
||||
headers,
|
||||
params,
|
||||
});
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async createEvent(data: Event): Promise<Event> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async updateEvent(data: Event): Promise<Event> {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteEvent(id: number) {
|
||||
try {
|
||||
const resp = await axios.delete(`${URL}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default EventApi;
|
||||
@@ -0,0 +1,67 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from "axios";
|
||||
import Post from "@models/Feed";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
export const URL = `${process.env.NEXT_PUBLIC_API_URL}/feed/`;
|
||||
|
||||
export interface Options {
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
class FeedApi {
|
||||
static async getFeed(options: Options = {}): Promise<Post[]> {
|
||||
const { auth } = options;
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
try {
|
||||
const resp = await axios.get(URL, { headers });
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async getPost(id: number, options: Options = {}): Promise<Post> {
|
||||
const { auth } = options;
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
try {
|
||||
const resp = await axios.get(`${URL}${id}/`, { headers });
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async createPost(data: Post): Promise<Post> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async updatePost(data: Post): Promise<Post> {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FeedApi;
|
||||
@@ -0,0 +1,77 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from "axios";
|
||||
import JobAd from "@models/JobAd";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
export const URL = `${process.env.NEXT_PUBLIC_API_URL}/jobads/`;
|
||||
|
||||
export interface Options {
|
||||
onlyNonPast?: boolean;
|
||||
limit?: number;
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
class JobAdApi {
|
||||
static async getJobAds(options: Options = {}): Promise<JobAd[]> {
|
||||
const { onlyNonPast, limit, auth } = options;
|
||||
try {
|
||||
const params = {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
};
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}`, {
|
||||
headers,
|
||||
params,
|
||||
});
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async getJobAd(id: number, auth = false): Promise<JobAd> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}${id}/`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async createJobAd(data: JobAd): Promise<JobAd> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async updateJobAd(data: JobAd): Promise<JobAd> {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default JobAdApi;
|
||||
@@ -0,0 +1,181 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from "axios";
|
||||
import { Signup, SignupForm } from "@models/Signup";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
export const URL = `${process.env.NEXT_PUBLIC_API_URL}/signup/`;
|
||||
export const FORM_URL = `${process.env.NEXT_PUBLIC_API_URL}/signupForm/`;
|
||||
|
||||
export interface Options {
|
||||
onlyNonPast?: boolean;
|
||||
limit?: number;
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
class SignupApi {
|
||||
static async getSignup(id: number): Promise<Signup> {
|
||||
try {
|
||||
const resp = await axios.get(`${URL}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async createSignup(data: Signup): Promise<Signup> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data);
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async 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;
|
||||
}
|
||||
}
|
||||
|
||||
static async 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;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteSignup(id: number) {
|
||||
try {
|
||||
const resp = await axios.delete(`${URL}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async getForms(auth = false): Promise<SignupForm[]> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(FORM_URL, {
|
||||
headers,
|
||||
});
|
||||
const { results } = resp.data;
|
||||
return results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async getForm(id: number, auth = false): Promise<SignupForm> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${FORM_URL}${id}/`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async createForm(data: SignupForm): Promise<SignupForm> {
|
||||
try {
|
||||
const resp = await axios.post(FORM_URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async updateForm(data: SignupForm): Promise<SignupForm> {
|
||||
try {
|
||||
const putUrl = `${FORM_URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteForm(id: number) {
|
||||
try {
|
||||
const resp = await axios.delete(`${FORM_URL}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async signupFormSendEmail(data: any, id: number): Promise<any> {
|
||||
try {
|
||||
const resp = await axios.post(`${FORM_URL}${id}/sendemail/`, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async getSignups(id: number): Promise<Signup[]> {
|
||||
try {
|
||||
const resp = await axios.get(`${FORM_URL}${id}/signups/`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SignupApi;
|
||||
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from "axios";
|
||||
import Tag from "@models/Tag";
|
||||
|
||||
export const URL = `${process.env.NEXT_PUBLIC_API_URL}/tags/`;
|
||||
|
||||
export interface Options {
|
||||
onlyNonPast?: boolean;
|
||||
limit?: number;
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
class TagApi {
|
||||
static async getTags(): Promise<Tag[]> {
|
||||
try {
|
||||
const resp = await axios.get(URL);
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TagApi;
|
||||
@@ -38,15 +38,13 @@ const Container = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Panel = styled.div<{ visible?: boolean }>`
|
||||
margin-top: ${(p) => (p.visible ? "0" : "-100%")};
|
||||
|
||||
const Panel = styled.div<{ $visible?: boolean }>`
|
||||
max-height: ${((p) => (p.$visible ? "1000vh" : "0"))};
|
||||
transition: max-height 400ms ease-in-out;
|
||||
display: flex;
|
||||
max-height: 15rem;
|
||||
transition: margin-top 400ms ease-in-out;
|
||||
|
||||
& > * {
|
||||
padding-left: 1em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -67,7 +65,7 @@ const Accordion: React.FC<AccordionProps> = ({ title, children }) => {
|
||||
<h5>{title}</h5>
|
||||
</button>
|
||||
<div>
|
||||
<Panel visible={isOpen}>
|
||||
<Panel $visible={isOpen}>
|
||||
{children}
|
||||
</Panel>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import { createGlobalStyle } from "styled-components";
|
||||
|
||||
const GlobalBlock = createGlobalStyle`
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
const BlockScroll: React.FC = () => (
|
||||
<GlobalBlock />
|
||||
);
|
||||
|
||||
export default BlockScroll;
|
||||
@@ -69,7 +69,7 @@ const StyledCard = styled.article`
|
||||
const WrappedCard: React.FC<WrappedCardProps> = ({
|
||||
title, text, link, image, imageAlt, start_time, buttonOnClick, ...props
|
||||
}) => {
|
||||
const options = {
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
day: "numeric",
|
||||
month: "numeric",
|
||||
year: "numeric",
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import React from "react";
|
||||
import Image, { ImageProps } from "next/image";
|
||||
import styled, { keyframes, Keyframes } from "styled-components";
|
||||
|
||||
interface CrossFadeImagesProps {
|
||||
width: ImageProps["width"];
|
||||
height: ImageProps["height"];
|
||||
images: string[];
|
||||
presentationTime: number;
|
||||
fadeTime: number;
|
||||
}
|
||||
|
||||
const AnimatedImage = styled(Image)<{ layout: string; $delay: number }>`
|
||||
animation-delay: ${(p) => p.$delay}s;
|
||||
`;
|
||||
|
||||
const Container = styled.div<{ $animation: Keyframes; $duration: number; }>`
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
|
||||
.not-first {
|
||||
margin-left: -100%;
|
||||
}
|
||||
|
||||
& > div {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
flex: none;
|
||||
|
||||
img {
|
||||
animation-name: ${(p) => p.$animation};
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: infinite;
|
||||
animation-duration: ${(p) => p.$duration}s;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CrossFadeImages: React.FC<CrossFadeImagesProps> = ({
|
||||
width, height, images, presentationTime, fadeTime,
|
||||
}) => {
|
||||
const len = images.length;
|
||||
const SINGLE_IMAGE_TIME = presentationTime + fadeTime;
|
||||
const TOTAL_TIME = SINGLE_IMAGE_TIME * len;
|
||||
|
||||
const animation = keyframes`
|
||||
0% {
|
||||
opacity:1;
|
||||
}
|
||||
${(presentationTime / TOTAL_TIME) * 100}% {
|
||||
opacity:1;
|
||||
}
|
||||
${(1 / len) * 100}% {
|
||||
opacity:0;
|
||||
}
|
||||
${100 - ((fadeTime / TOTAL_TIME) * 100)}% {
|
||||
opacity:0;
|
||||
}
|
||||
100% {
|
||||
opacity:1;
|
||||
}
|
||||
`;
|
||||
|
||||
const delays = images.map((_, idx) => idx * SINGLE_IMAGE_TIME).reverse();
|
||||
|
||||
return (
|
||||
<Container
|
||||
$animation={animation}
|
||||
$duration={len * SINGLE_IMAGE_TIME}
|
||||
>
|
||||
{ images.map((image, idx) => (
|
||||
<div className={idx > 0 ? "not-first" : undefined}>
|
||||
<AnimatedImage
|
||||
key={image}
|
||||
src={image}
|
||||
objectFit="cover"
|
||||
width={width}
|
||||
height={height}
|
||||
layout="responsive"
|
||||
$delay={delays[idx]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default CrossFadeImages;
|
||||
@@ -84,9 +84,8 @@ const FooterContent: React.FC = () => (
|
||||
</div>
|
||||
<div>
|
||||
<Link to="https://api.sahkoinsinoorikilta.fi/members/application/">Jäseneksi</Link>
|
||||
<Link to="/palaute">Palaute</Link>
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi">Arkisto</Link>
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi">Materiaalipankki</Link>
|
||||
<Link to="mailto:hallitus@sahkoinsinoorikilta.fi">Palaute</Link>
|
||||
<Link to="https://static.sahkoinsinoorikilta.fi">Dokumenttiarkisto</Link>
|
||||
</div>
|
||||
</Columns>
|
||||
</MarginSpace>
|
||||
@@ -94,6 +93,7 @@ const FooterContent: React.FC = () => (
|
||||
|
||||
<Map>
|
||||
<iframe
|
||||
// eslint-disable-next-line max-len
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d1983.6122518000927!2d24.81667815176689!3d60.187150048900186!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x468df5eb3cb4ecf1%3A0x3480cbfeedcc07b6!2sMaarintie+8%2C+02150+Espoo!5e0!3m2!1sfi!2sfi!4v1542413548247"
|
||||
width="100%"
|
||||
height="100%"
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default (): JSX.Element => (
|
||||
// eslint-disable-next-line react/no-danger
|
||||
<head dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
`<!--
|
||||
|
||||
@@ -6,6 +6,7 @@ import breakpoints from "@theme/breakpoints";
|
||||
import Navigation from "./Navigation";
|
||||
import NavigationMobile from "./NavigationMobile";
|
||||
import HeaderLogo from "./HeaderLogo";
|
||||
import BlockScroll from "./BlockScroll";
|
||||
|
||||
const StyledHeader = styled.header`
|
||||
display: flex;
|
||||
@@ -15,7 +16,7 @@ const StyledHeader = styled.header`
|
||||
}
|
||||
`;
|
||||
|
||||
const Sticky = styled.div<{ $isHidden?: boolean }>`
|
||||
const Sticky = styled.div<{ $isHidden?: boolean; $mobileMenuOpen?: boolean }>`
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
@@ -24,6 +25,8 @@ const Sticky = styled.div<{ $isHidden?: boolean }>`
|
||||
background-color: ${colors.darkBlue};
|
||||
transition: all 200ms ease-out;
|
||||
|
||||
height: ${(p) => (p.$mobileMenuOpen ? "100vh" : "unset")};
|
||||
|
||||
${(p) => (p.$isHidden ? (`
|
||||
transition: all 200ms ease-in;
|
||||
transform: translateY(-100%);
|
||||
@@ -56,7 +59,10 @@ const Header: React.FC = () => {
|
||||
}, [mobileMenuOpen]);
|
||||
|
||||
return (
|
||||
<Sticky $isHidden={isHidden}>
|
||||
<Sticky $isHidden={isHidden} $mobileMenuOpen={mobileMenuOpen}>
|
||||
{ mobileMenuOpen && (
|
||||
<BlockScroll />
|
||||
)}
|
||||
<StyledHeader>
|
||||
<HeaderLogo />
|
||||
<Navigation onMobileMenuOpen={handleMobileMenuClick} />
|
||||
|
||||
@@ -12,7 +12,7 @@ export enum IconType {
|
||||
interface IconProps {
|
||||
name: IconType;
|
||||
link?: string;
|
||||
onClick?: (event?: any) => void;
|
||||
onClick?: React.MouseEventHandler<HTMLSpanElement>;
|
||||
}
|
||||
|
||||
const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
@@ -24,6 +24,7 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Facebook icon</title>
|
||||
{/* eslint-disable-next-line max-len */}
|
||||
<path d="M22.676 0H1.324C.593 0 0 .593 0 1.324v21.352C0 23.408.593 24 1.324 24h11.494v-9.294H9.689v-3.621h3.129V8.41c0-3.099 1.894-4.785 4.659-4.785 1.325 0 2.464.097 2.796.141v3.24h-1.921c-1.5 0-1.792.721-1.792 1.771v2.311h3.584l-.465 3.63H16.56V24h6.115c.733 0 1.325-.592 1.325-1.324V1.324C24 .593 23.408 0 22.676 0" />
|
||||
</svg>
|
||||
);
|
||||
@@ -36,6 +37,7 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Instagram icon</title>
|
||||
{/* eslint-disable-next-line max-len */}
|
||||
<path d="M12 0C8.74 0 8.333.015 7.053.072 5.775.132 4.905.333 4.14.63c-.789.306-1.459.717-2.126 1.384S.935 3.35.63 4.14C.333 4.905.131 5.775.072 7.053.012 8.333 0 8.74 0 12s.015 3.667.072 4.947c.06 1.277.261 2.148.558 2.913.306.788.717 1.459 1.384 2.126.667.666 1.336 1.079 2.126 1.384.766.296 1.636.499 2.913.558C8.333 23.988 8.74 24 12 24s3.667-.015 4.947-.072c1.277-.06 2.148-.262 2.913-.558.788-.306 1.459-.718 2.126-1.384.666-.667 1.079-1.335 1.384-2.126.296-.765.499-1.636.558-2.913.06-1.28.072-1.687.072-4.947s-.015-3.667-.072-4.947c-.06-1.277-.262-2.149-.558-2.913-.306-.789-.718-1.459-1.384-2.126C21.319 1.347 20.651.935 19.86.63c-.765-.297-1.636-.499-2.913-.558C15.667.012 15.26 0 12 0zm0 2.16c3.203 0 3.585.016 4.85.071 1.17.055 1.805.249 2.227.415.562.217.96.477 1.382.896.419.42.679.819.896 1.381.164.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.015 3.585-.074 4.85c-.061 1.17-.256 1.805-.421 2.227-.224.562-.479.96-.899 1.382-.419.419-.824.679-1.38.896-.42.164-1.065.36-2.235.413-1.274.057-1.649.07-4.859.07-3.211 0-3.586-.015-4.859-.074-1.171-.061-1.816-.256-2.236-.421-.569-.224-.96-.479-1.379-.899-.421-.419-.69-.824-.9-1.38-.165-.42-.359-1.065-.42-2.235-.045-1.26-.061-1.649-.061-4.844 0-3.196.016-3.586.061-4.861.061-1.17.255-1.814.42-2.234.21-.57.479-.96.9-1.381.419-.419.81-.689 1.379-.898.42-.166 1.051-.361 2.221-.421 1.275-.045 1.65-.06 4.859-.06l.045.03zm0 3.678c-3.405 0-6.162 2.76-6.162 6.162 0 3.405 2.76 6.162 6.162 6.162 3.405 0 6.162-2.76 6.162-6.162 0-3.405-2.76-6.162-6.162-6.162zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm7.846-10.405c0 .795-.646 1.44-1.44 1.44-.795 0-1.44-.646-1.44-1.44 0-.794.646-1.439 1.44-1.439.793-.001 1.44.645 1.44 1.439z" />
|
||||
</svg>
|
||||
);
|
||||
@@ -48,6 +50,7 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>LinkedIn icon</title>
|
||||
{/* eslint-disable-next-line max-len */}
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
</svg>
|
||||
);
|
||||
@@ -60,6 +63,7 @@ const nameToIcon = (name: IconType): JSX.Element | string => {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Menu</title>
|
||||
{/* eslint-disable-next-line max-len */}
|
||||
<path d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
interface JsonLDProps {
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const JsonLD: React.FC<JsonLDProps> = ({ data }) => (
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify(data),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default JsonLD;
|
||||
@@ -12,7 +12,7 @@ const renderNavigationDesktopItems = () => (
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/kunnia">Kunnianosoitukset</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sahkoinsinoorikilta.fi">Arkisto</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sahkoinsinoorikilta.fi">Dokumenttiarkisto</NavbarChildLink>
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/opinnot_ja_ura" text="Opinnot ja ura" />
|
||||
<NavbarDropdownLink to="/yritysyhteistyo" text="Yritysyhteistyö" />
|
||||
|
||||
@@ -10,7 +10,7 @@ const renderNavigationMobileItems = () => (
|
||||
<NavbarChildLink to="/kilta/toiminta">Toiminta</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/fuksi">Fuksi</NavbarChildLink>
|
||||
<NavbarChildLink to="/kilta/kunnia">Kunnianosoitukset</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sahkoinsinoorikilta.fi">Arkisto</NavbarChildLink>
|
||||
<NavbarChildLink to="https://static.sahkoinsinoorikilta.fi">Dokumenttiarkisto</NavbarChildLink>
|
||||
</NavbarDropdownLink>
|
||||
<NavbarDropdownLink to="/opinnot_ja_ura" text="Opinnot ja ura" exploded />
|
||||
<NavbarDropdownLink to="/yritysyhteistyo" text="Yritysyhteistyö" exploded />
|
||||
|
||||
@@ -11,7 +11,7 @@ const Section = styled.section<{ colors: string }>`
|
||||
text-align: center;
|
||||
align-items: baseline;
|
||||
padding: 2rem 0;
|
||||
|
||||
border: 0.5rem colors.sik100Gold;
|
||||
flex-flow: row wrap;
|
||||
|
||||
a {
|
||||
@@ -29,7 +29,8 @@ const Section = styled.section<{ colors: string }>`
|
||||
|
||||
& > h1 {
|
||||
a {
|
||||
text-decoration: underline;
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
@@ -37,7 +38,7 @@ const Section = styled.section<{ colors: string }>`
|
||||
}
|
||||
`;
|
||||
|
||||
type Colors = "orange1" | "lightBlue" | "darkBlue" | "blue1" | "lightTurquoise";
|
||||
type Colors = "orange1" | "lightBlue" | "darkBlue" | "blue1" | "lightTurquoise" | "sik100Blue";
|
||||
|
||||
interface CTASectionProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
bgColor?: Colors;
|
||||
@@ -54,7 +55,10 @@ a:hover {
|
||||
color: ${colors.darkBlue};
|
||||
}
|
||||
`;
|
||||
|
||||
case "sik100Blue": return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.sik100Gold};
|
||||
`;
|
||||
case "darkBlue": return `
|
||||
background-color: ${colors[bgColor]};
|
||||
color: ${colors.white};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { WidgetProps } from "react-jsonschema-form";
|
||||
import MarkdownStyles from "@views/common/MarkdownStyles";
|
||||
|
||||
type MarkdownEditorWidgetProps = Omit<WidgetProps, "options"> & {
|
||||
options: any;
|
||||
options: unknown;
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
|
||||
@@ -13,7 +13,6 @@ const getIconByLabel = (label: string) => {
|
||||
if (label === "English") {
|
||||
return <Icon name={IconType.GBFlag} />;
|
||||
}
|
||||
console.error(`No icon found for label: ${label}`);
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ interface OptionsWidgetProps {
|
||||
}
|
||||
|
||||
class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
handleListOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
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());
|
||||
@@ -19,15 +19,15 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleTextOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
handleTextOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = val;
|
||||
questions[index].options = val as unknown as string[]; // TODO: Check type
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleIntegerOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||
handleIntegerOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
if (val !== "") {
|
||||
@@ -43,10 +43,9 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleRequiredChange = (questions: Question[], index: number) => (event) => {
|
||||
handleRequiredChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val: boolean = event.target.checked;
|
||||
console.log(val);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].required = val;
|
||||
onChange(questions);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { ReactNode } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import { colors } from "@theme/colors";
|
||||
@@ -17,8 +17,8 @@ const WidgetRow = styled.div`
|
||||
|
||||
interface QuestionListProps {
|
||||
questions: Question[];
|
||||
innerRef: any;
|
||||
placeholder: any;
|
||||
innerRef: React.Ref<HTMLDivElement>;
|
||||
placeholder: ReactNode;
|
||||
onChange: (value: Question[]) => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,3 +9,4 @@ export { default as FullWidthSection } from "./Sections/FullWidthSection";
|
||||
export { default as InfoBox } from "./InfoBox";
|
||||
export { default as Accordion } from "./Accordion/Accordion";
|
||||
export { default as Link } from "./Link";
|
||||
export { default as CrossFadeImages } from "./CrossFadeImages";
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useRef } from "react";
|
||||
import useSWR from "swr";
|
||||
import isDeepEqual from "fast-deep-equal/react";
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import Event from "@models/Event";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { URL, Options } from "@api/eventApi";
|
||||
|
||||
const fetcher = (url: string, config: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data);
|
||||
|
||||
const generateFetchParams = (id = "", options: Options = {}) => {
|
||||
const url = `${URL}${id}`;
|
||||
const { auth, onlyNonPast, limit } = options;
|
||||
|
||||
return {
|
||||
url,
|
||||
config: {
|
||||
params: {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
},
|
||||
headers: auth ? { Authorization: getAuthHeader() } : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface FetchArguments {
|
||||
initialData?: Event | Event[],
|
||||
id?: string;
|
||||
options?: Options
|
||||
}
|
||||
|
||||
const useFetchEvents = ({
|
||||
initialData,
|
||||
id = "",
|
||||
options = {},
|
||||
}: FetchArguments) => {
|
||||
const { url, config } = generateFetchParams(id, options);
|
||||
|
||||
// Use ref, since config dependency is non-primitive => without this we have infinite fetch loop
|
||||
const configRef = useRef(config);
|
||||
if (!isDeepEqual(configRef.current, config)) {
|
||||
configRef.current = config;
|
||||
}
|
||||
|
||||
const { data, error } = useSWR([url, configRef.current], fetcher, { initialData });
|
||||
return {
|
||||
data: data?.results || data,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFetchEvents;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useRef } from "react";
|
||||
import useSWR from "swr";
|
||||
import isDeepEqual from "fast-deep-equal/react";
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import Post from "@models/Feed";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { URL, Options } from "@api/feedApi";
|
||||
|
||||
const feedFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data);
|
||||
|
||||
const generateFetchParams = (id = "", options: Options = {}) => {
|
||||
const url = `${URL}${id}`;
|
||||
const { auth } = options;
|
||||
|
||||
return {
|
||||
url,
|
||||
config: {
|
||||
headers: auth ? { Authorization: getAuthHeader() } : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface FetchArguments {
|
||||
initialData?: Post | Post[],
|
||||
id?: string;
|
||||
options?: Options
|
||||
}
|
||||
|
||||
const useFetchFeed = ({
|
||||
initialData,
|
||||
id = "",
|
||||
options = {},
|
||||
}: FetchArguments) => {
|
||||
const { url, config } = generateFetchParams(id, options);
|
||||
|
||||
// Use ref, since config dependency is non-primitive => without this we have infinite fetch loop
|
||||
const configRef = useRef(config);
|
||||
if (!isDeepEqual(configRef.current, config)) {
|
||||
configRef.current = config;
|
||||
}
|
||||
|
||||
const { data, error } = useSWR([url, configRef.current], feedFetcher, { initialData });
|
||||
return {
|
||||
data: data?.results || data,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFetchFeed;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useRef } from "react";
|
||||
import useSWR from "swr";
|
||||
import isDeepEqual from "fast-deep-equal/react";
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import JobAd from "@models/JobAd";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { URL, Options } from "@api/jobAdApi";
|
||||
|
||||
const jobAdFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data);
|
||||
|
||||
const generateFetchParams = (id = "", options: Options = {}) => {
|
||||
const url = `${URL}${id}`;
|
||||
const { auth } = options;
|
||||
|
||||
return {
|
||||
url,
|
||||
config: {
|
||||
headers: auth ? { Authorization: getAuthHeader() } : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface FetchArguments {
|
||||
initialData?: JobAd | JobAd[],
|
||||
id?: string;
|
||||
options?: Options;
|
||||
}
|
||||
|
||||
const useFetchJobAds = ({
|
||||
initialData,
|
||||
id = "",
|
||||
options = {},
|
||||
}: FetchArguments) => {
|
||||
const { url, config } = generateFetchParams(id, options);
|
||||
|
||||
// Use ref, since config dependency is non-primitive => without this we have infinite fetch loop
|
||||
const configRef = useRef(config);
|
||||
if (!isDeepEqual(configRef.current, config)) {
|
||||
configRef.current = config;
|
||||
}
|
||||
|
||||
const { data, error } = useSWR([url, configRef.current], jobAdFetcher, { initialData });
|
||||
return {
|
||||
data: data?.results || data,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFetchJobAds;
|
||||
@@ -1,13 +1,7 @@
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import useSWR from "swr";
|
||||
import qs from "query-string";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { Tag } from "./Tag";
|
||||
import { SignupForm } from "./SignupForm";
|
||||
import Tag from "./Tag";
|
||||
import { SignupForm } from "./Signup";
|
||||
|
||||
const URL = `${process.env.NEXT_PUBLIC_API_URL}/events/`;
|
||||
|
||||
export interface Event {
|
||||
interface Event {
|
||||
id: number;
|
||||
title_fi: string;
|
||||
title_en: string;
|
||||
@@ -25,120 +19,4 @@ export interface Event {
|
||||
signupForm: SignupForm[];
|
||||
}
|
||||
|
||||
interface Options {
|
||||
onlyNonPast?: boolean;
|
||||
limit?: number;
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
export async function getEvents(options: Options = {}): Promise<Event[]> {
|
||||
const { onlyNonPast, limit, auth } = options;
|
||||
try {
|
||||
const params = {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
};
|
||||
const search = qs.stringify(params);
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}?${search}`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEvent(id: number, auth = false): Promise<Event> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}${id}/`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createEvent(data): Promise<Event> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateEvent(data): Promise<Event> {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteEvent(id: number) {
|
||||
try {
|
||||
const resp = await axios.delete(`${URL}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export const eventFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data);
|
||||
|
||||
export const generateFetchParams = (id = "", options: Options = {}) => {
|
||||
const url = `${URL}${id}`;
|
||||
const { auth, onlyNonPast, limit } = options;
|
||||
|
||||
return {
|
||||
url,
|
||||
config: {
|
||||
params: {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
},
|
||||
headers: auth ? { Authorization: getAuthHeader() } : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface FetchArguments {
|
||||
initialData?: Event | Event[],
|
||||
id?: string;
|
||||
options?: Options
|
||||
}
|
||||
|
||||
export const useFetchEvents = ({
|
||||
initialData,
|
||||
id = "",
|
||||
options = {},
|
||||
}: FetchArguments) => {
|
||||
const { url, config } = generateFetchParams(id, options);
|
||||
const { data, error } = useSWR([url, config], eventFetcher, { initialData });
|
||||
return {
|
||||
data: data?.results || data,
|
||||
error,
|
||||
};
|
||||
};
|
||||
export default Event;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import useSWR from "swr";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { Tag } from "./Tag";
|
||||
import Tag from "./Tag";
|
||||
|
||||
const URL = `${process.env.NEXT_PUBLIC_API_URL}/feed/`;
|
||||
|
||||
export interface Post {
|
||||
interface Post {
|
||||
id: number;
|
||||
tags: Tag[];
|
||||
visible: boolean;
|
||||
@@ -21,92 +16,4 @@ export interface Post {
|
||||
autohide_enabled: boolean;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
export async function getFeed(options: Options = {}): Promise<Post[]> {
|
||||
const { auth } = options;
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
try {
|
||||
const resp = await axios.get(URL, { headers });
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPost(id: number, options: Options = {}): Promise<Post> {
|
||||
const { auth } = options;
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
try {
|
||||
const resp = await axios.get(`${URL}${id}/`, { headers });
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createPost(data): Promise<Post> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePost(data): Promise<Post> {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export const feedFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data);
|
||||
|
||||
export const generateFetchParams = (id = "", options: Options = {}) => {
|
||||
const url = `${URL}${id}`;
|
||||
const { auth } = options;
|
||||
|
||||
return {
|
||||
url,
|
||||
config: {
|
||||
headers: auth ? { Authorization: getAuthHeader() } : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface FetchArguments {
|
||||
initialData?: Post | Post[],
|
||||
id?: string;
|
||||
options?: Options
|
||||
}
|
||||
|
||||
export const useFetchFeed = ({
|
||||
initialData,
|
||||
id = "",
|
||||
options = {},
|
||||
}: FetchArguments) => {
|
||||
const { url, config } = generateFetchParams(id, options);
|
||||
const { data, error } = useSWR([url, config], feedFetcher, { initialData });
|
||||
return {
|
||||
data: data?.results || data,
|
||||
error,
|
||||
};
|
||||
};
|
||||
export default Post;
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import useSWR from "swr";
|
||||
import qs from "query-string";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
const URL = `${process.env.NEXT_PUBLIC_API_URL}/jobads/`;
|
||||
|
||||
export interface JobAd {
|
||||
interface JobAd {
|
||||
id: number;
|
||||
title_fi: string;
|
||||
title_en: string;
|
||||
@@ -17,106 +10,4 @@ export interface JobAd {
|
||||
autohide_enabled: boolean;
|
||||
}
|
||||
|
||||
export interface GetJobAdsOptions {
|
||||
onlyNonPast?: boolean;
|
||||
limit?: number;
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
export const getJobAds = async (options: GetJobAdsOptions = {}): Promise<JobAd[]> => {
|
||||
const { onlyNonPast, limit, auth } = options;
|
||||
try {
|
||||
const params = {
|
||||
since: onlyNonPast ? (new Date()).toISOString() : undefined,
|
||||
limit,
|
||||
};
|
||||
const search = qs.stringify(params);
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}?${search}`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const getJobAd = async (id: number, auth = false): Promise<JobAd> => {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}${id}/`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const createJobAd = async (data: any): Promise<JobAd> => {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateJobAd = async (data: any): Promise<JobAd> => {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const jobAdFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data);
|
||||
|
||||
export const generateFetchParams = (id = "", options: Options = {}) => {
|
||||
const url = `${URL}${id}`;
|
||||
const { auth } = options;
|
||||
|
||||
return {
|
||||
url,
|
||||
config: {
|
||||
headers: auth ? { Authorization: getAuthHeader() } : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface Options {
|
||||
auth?: boolean;
|
||||
}
|
||||
|
||||
interface FetchArguments {
|
||||
initialData?: JobAd | JobAd[],
|
||||
id?: string;
|
||||
options?: Options;
|
||||
}
|
||||
|
||||
export const useFetchJobAds = ({
|
||||
initialData,
|
||||
id = "",
|
||||
options = {},
|
||||
}: FetchArguments) => {
|
||||
const { url, config } = generateFetchParams(id, options);
|
||||
const { data, error } = useSWR([url, config], jobAdFetcher, { initialData });
|
||||
return {
|
||||
data: data?.results || data,
|
||||
error,
|
||||
};
|
||||
};
|
||||
export default JobAd;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
|
||||
const url = `${process.env.NEXT_PUBLIC_API_URL}/signup/`;
|
||||
import { Question } from "@components/Widgets/SignupQuestionsWidget/common";
|
||||
|
||||
export interface Signup {
|
||||
id?: number;
|
||||
@@ -9,68 +6,21 @@ export interface Signup {
|
||||
answer: string;
|
||||
}
|
||||
|
||||
export const getSignup = async (id: number): Promise<Signup> => {
|
||||
try {
|
||||
const resp = await axios.get(`${url}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const createSignup = async (data: Signup): Promise<Signup> => {
|
||||
try {
|
||||
const resp = await axios.post(url, data);
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateSignup = async (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 const getSignupUUID = async (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;
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteSignup = async (id: number) => {
|
||||
try {
|
||||
const resp = await axios.delete(`${url}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
export interface SignupForm {
|
||||
id?: number;
|
||||
title_fi: string;
|
||||
title_en: string;
|
||||
visible: boolean;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
questions: Question[];
|
||||
signups: string[];
|
||||
quota: number;
|
||||
schema: {
|
||||
title?: string;
|
||||
type: string;
|
||||
required: string[];
|
||||
properties: unknown;
|
||||
minProperties?: number;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "@utils/auth";
|
||||
import { Question } from "@components/Widgets/SignupQuestionsWidget/common";
|
||||
import { Signup } from "./Signup";
|
||||
|
||||
const URL = `${process.env.NEXT_PUBLIC_API_URL}/signupForm/`;
|
||||
|
||||
export interface SignupForm {
|
||||
id?: number;
|
||||
title_fi: string;
|
||||
title_en: string;
|
||||
visible: boolean;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
questions: Question[];
|
||||
signups: string[];
|
||||
quota: number;
|
||||
schema: {
|
||||
title?: string;
|
||||
type: string;
|
||||
required: string[];
|
||||
properties: any;
|
||||
minProperties?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getForms(auth = false): Promise<SignupForm[]> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(URL, {
|
||||
headers,
|
||||
});
|
||||
const { results } = resp.data;
|
||||
return results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getForm(id: number, auth = false): Promise<SignupForm> {
|
||||
try {
|
||||
const headers = auth ? { Authorization: getAuthHeader() } : null;
|
||||
const resp = await axios.get(`${URL}${id}/`, {
|
||||
headers,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createForm(data): Promise<SignupForm> {
|
||||
try {
|
||||
const resp = await axios.post(URL, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateForm(data): Promise<SignupForm> {
|
||||
try {
|
||||
const putUrl = `${URL}${data.id}/`;
|
||||
const resp = await axios.put(putUrl, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteForm(id: number) {
|
||||
try {
|
||||
const resp = await axios.delete(`${URL}${id}`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export const signupFormSendEmail = async (data, id): Promise<any> => {
|
||||
try {
|
||||
const resp = await axios.post(`${URL}${id}/sendemail/`, data, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const getSignups = async (id): Promise<Signup[]> => {
|
||||
try {
|
||||
const resp = await axios.get(`${URL}${id}/signups/`, {
|
||||
headers: {
|
||||
Authorization: getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,4 @@
|
||||
import axios from "axios";
|
||||
|
||||
const url = `${process.env.NEXT_PUBLIC_API_URL}/tags/`;
|
||||
|
||||
export interface Tag {
|
||||
interface Tag {
|
||||
id: number;
|
||||
name_fi: string;
|
||||
name_en: string;
|
||||
@@ -10,12 +6,4 @@ export interface Tag {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export async function getTags(): Promise<Tag[]> {
|
||||
try {
|
||||
const resp = await axios.get(url);
|
||||
return resp.data.results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
export default Tag;
|
||||
|
||||
@@ -16,7 +16,6 @@ const NotFound = styled.main`
|
||||
const NotFoundPage: NextPage = () => (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/404" />
|
||||
<title>404 | Ei vaan löydy</title>
|
||||
<meta name="robots" content="noindex" />
|
||||
</Head>
|
||||
|
||||
@@ -131,7 +131,10 @@ const Web20App = ({ Component, pageProps }: AppProps) => (
|
||||
<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>
|
||||
<meta name="description" content="Aalto-yliopiston Sähköinsinöörikilta ry on Otaniemessä vaikuttava opiskelijajärjestö, joka on perustettu vuonna 1921. Kilta järjestää kaikenlaista toimintaa liittyen opintoihin ja vapaa-ajan viettoon." />
|
||||
<meta
|
||||
name="description"
|
||||
content="Aalto-yliopiston Sähköinsinöörikilta ry on Otaniemessä vaikuttava opiskelijajärjestö, joka on perustettu vuonna 1921. Kilta järjestää kaikenlaista toimintaa liittyen opintoihin ja vapaa-ajan viettoon."
|
||||
/>
|
||||
<meta name="keywords" content="SIK AYY" />
|
||||
</Head>
|
||||
<GlobalCommonStyles />
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ServerStyleSheet } from "styled-components";
|
||||
import Favicons from "@components/Favicons";
|
||||
import HTMLLogo from "@components/HTMLLogo";
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
export default class MyDocument extends Document<{ styleTags: unknown }> {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
@@ -30,7 +30,7 @@ export default class MyDocument extends Document {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { styleTags } = this.props as any;
|
||||
const { styleTags } = this.props;
|
||||
return (
|
||||
<Html lang="fi">
|
||||
<Head>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import AdminCreateCommon from "@views/admin/AdminCreateCommon";
|
||||
import { Tag, getTags } from "@models/Tag";
|
||||
import { SignupForm, getForms } from "@models/SignupForm";
|
||||
import {
|
||||
Event, createEvent, getEvent, updateEvent,
|
||||
} from "@models/Event";
|
||||
import Tag from "@models/Tag";
|
||||
import TagApi from "@api/tagApi";
|
||||
import { SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
import Event from "@models/Event";
|
||||
import EventApi from "@api/eventApi";
|
||||
import DatetimeWidget from "@components/Widgets/DatetimeWidget";
|
||||
import SectionDividerWidget from "@components/Widgets/SectionDividerWidget";
|
||||
import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget";
|
||||
@@ -177,17 +177,17 @@ const EventCreatePage: NextPage = () => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getTags()
|
||||
TagApi.getTags()
|
||||
.then((res) => setTags(res))
|
||||
.catch((err) => setError(err));
|
||||
|
||||
getForms(true)
|
||||
SignupApi.getForms(true)
|
||||
.then((res) => setSignupForms(res))
|
||||
.catch((err) => setError(err));
|
||||
|
||||
const eventId = id && Number(id);
|
||||
if (eventId !== undefined) {
|
||||
getEvent(eventId, true)
|
||||
EventApi.getEvent(eventId, true)
|
||||
.then((res) => setFormData({
|
||||
...res,
|
||||
tags: (res.tags).map((inst) => inst.id) as any,
|
||||
@@ -197,7 +197,7 @@ const EventCreatePage: NextPage = () => {
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const onSubmit = async (data: any) => {
|
||||
try {
|
||||
const payload = data.formData;
|
||||
payload.signup_id = payload.signupForm;
|
||||
@@ -205,7 +205,7 @@ const EventCreatePage: NextPage = () => {
|
||||
if (typeof payload.image === "string" && payload.image.startsWith("http")) payload.image = undefined;
|
||||
|
||||
if (payload.id === undefined) {
|
||||
const resp = await createEvent(payload);
|
||||
const resp = await EventApi.createEvent(payload);
|
||||
// TODO: Backend return old data because of Prefetch (used for filtering invisble signupForms from GET)
|
||||
// Copy from old state instead...
|
||||
// resp.tags = (resp.tags as any).map(inst => inst.id);
|
||||
@@ -215,7 +215,7 @@ const EventCreatePage: NextPage = () => {
|
||||
setStatusMessage("Event created successfully");
|
||||
setFormData(resp);
|
||||
} else {
|
||||
const resp = await updateEvent(payload);
|
||||
const resp = await EventApi.updateEvent(payload);
|
||||
// TODO: Backend return old data because of Prefetch (used for filtering invisble signupForms from GET)
|
||||
// Copy from old state instead...
|
||||
// resp.tags = (resp.tags as any).map(inst => inst.id);
|
||||
@@ -230,33 +230,26 @@ const EventCreatePage: NextPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const onChange = (data) => setFormData(data.formData);
|
||||
const onChange = (data: any) => setFormData(data.formData);
|
||||
const onFocus = () => setStatusMessage(null);
|
||||
const title = formData?.id
|
||||
? `Edit Event "${formData.title_fi}"`
|
||||
: "Create Event";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin/events/create" />
|
||||
</Head>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
schema={buildSchema(formData, signupForms, tags)}
|
||||
UISchema={buildUISchema()}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onSubmit={onSubmit}
|
||||
statusMessage={statusMessage}
|
||||
error={error}
|
||||
widgets={widgets}
|
||||
/>
|
||||
</>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
schema={buildSchema(formData, signupForms, tags)}
|
||||
UISchema={buildUISchema()}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onSubmit={onSubmit}
|
||||
statusMessage={statusMessage}
|
||||
error={error}
|
||||
widgets={widgets}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default EventCreatePage;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import React from "react";
|
||||
import { NextPage } from "next";
|
||||
import { formatRelative } from "date-fns";
|
||||
import AdminListCommon from "@views/admin/AdminListCommon";
|
||||
import { Link } from "@components/index";
|
||||
import AddLink from "@components/AddLink";
|
||||
import { Event, getEvents } from "@models/Event";
|
||||
import Event from "@models/Event";
|
||||
import useFetchEvents from "@hooks/useFetchEvents";
|
||||
|
||||
const URL = "/admin/events";
|
||||
|
||||
@@ -36,27 +36,14 @@ const renderData = (events: Event[]) => {
|
||||
);
|
||||
};
|
||||
const AdminEventPage: NextPage = () => {
|
||||
const [events, setEvents] = useState<Event[]>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getEvents({ auth: true })
|
||||
.then((res) => setEvents(res));
|
||||
}, []);
|
||||
|
||||
const { data } = useFetchEvents({ options: { auth: true } });
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`https://sik.ayy.fi/${URL}`} />
|
||||
</Head>
|
||||
<AdminListCommon>
|
||||
<h1>Events</h1>
|
||||
<AddLink text="Create event" to={`${URL}/create`} data-e2e="create-event" />
|
||||
{renderData(events)}
|
||||
</AdminListCommon>
|
||||
</>
|
||||
<AdminListCommon>
|
||||
<h1>Events</h1>
|
||||
<AddLink text="Create event" to={`${URL}/create`} data-e2e="create-event" />
|
||||
{renderData(data)}
|
||||
</AdminListCommon>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default AdminEventPage;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import AdminCreateCommon from "@views/admin/AdminCreateCommon";
|
||||
import { Tag, getTags } from "@models/Tag";
|
||||
import {
|
||||
Post, createPost, getPost, updatePost,
|
||||
} from "@models/Feed";
|
||||
import Tag from "@models/Tag";
|
||||
import TagApi from "@api/tagApi";
|
||||
import Post from "@models/Feed";
|
||||
import FeedApi from "@api/feedApi";
|
||||
import DatetimeWidget from "@components/Widgets/DatetimeWidget";
|
||||
import SectionDividerWidget from "@components/Widgets/SectionDividerWidget";
|
||||
import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget";
|
||||
@@ -145,13 +144,13 @@ const FeedCreatePage: NextPage = () => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getTags()
|
||||
TagApi.getTags()
|
||||
.then((res) => setTags(res))
|
||||
.catch((err) => setError(err));
|
||||
|
||||
const feedId = id && Number(id);
|
||||
if (feedId !== undefined) {
|
||||
getPost(feedId, { auth: true })
|
||||
FeedApi.getPost(feedId, { auth: true })
|
||||
.then((res) => setFormData({
|
||||
...res,
|
||||
tags: (res.tags).map((inst) => inst.id) as any,
|
||||
@@ -166,12 +165,12 @@ const FeedCreatePage: NextPage = () => {
|
||||
payload.tag_id = payload.tags;
|
||||
payload.autohide = payload.autohide || new Date();
|
||||
if (payload.id === undefined) {
|
||||
const resp = await createPost(payload);
|
||||
const resp = await FeedApi.createPost(payload);
|
||||
// resp.tags = resp.tags;
|
||||
setStatusMessage("Post created successfully");
|
||||
setFormData(resp);
|
||||
} else {
|
||||
const resp = await updatePost(payload);
|
||||
const resp = await FeedApi.updatePost(payload);
|
||||
// resp.tags = resp.tag_id;
|
||||
setStatusMessage("Post updated successfully");
|
||||
setFormData(resp);
|
||||
@@ -189,26 +188,19 @@ const FeedCreatePage: NextPage = () => {
|
||||
: "Create Post";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin/feed/create" />
|
||||
</Head>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
schema={buildSchema(formData, tags)}
|
||||
UISchema={buildUISchema(formData)}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onSubmit={onSubmit}
|
||||
statusMessage={statusMessage}
|
||||
error={error}
|
||||
widgets={widgets}
|
||||
/>
|
||||
</>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
schema={buildSchema(formData, tags)}
|
||||
UISchema={buildUISchema(formData)}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onSubmit={onSubmit}
|
||||
statusMessage={statusMessage}
|
||||
error={error}
|
||||
widgets={widgets}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default FeedCreatePage;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import React from "react";
|
||||
import { NextPage } from "next";
|
||||
import { formatRelative } from "date-fns";
|
||||
import AdminListCommon from "@views/admin/AdminListCommon";
|
||||
import { Link } from "@components/index";
|
||||
import AddLink from "@components/AddLink";
|
||||
import { Post, getFeed } from "@models/Feed";
|
||||
import Post from "@models/Feed";
|
||||
import useFetchFeed from "@hooks/useFetchFeed";
|
||||
|
||||
const URL = "/admin/feed";
|
||||
|
||||
@@ -37,27 +37,14 @@ const renderData = (feed: Post[]) => {
|
||||
};
|
||||
|
||||
const AdminFeedPage: NextPage = () => {
|
||||
const [forms, setForms] = useState<Post[]>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getFeed({ auth: true })
|
||||
.then((res) => setForms(res));
|
||||
}, []);
|
||||
|
||||
const { data } = useFetchFeed({ options: { auth: true } });
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`https://sik.ayy.fi/${URL}`} />
|
||||
</Head>
|
||||
<AdminListCommon>
|
||||
<h1>Feed</h1>
|
||||
<AddLink text="Create news post" to={`${URL}/create`} />
|
||||
{renderData(forms)}
|
||||
</AdminListCommon>
|
||||
</>
|
||||
<AdminListCommon>
|
||||
<h1>Feed</h1>
|
||||
<AddLink text="Create news post" to={`${URL}/create`} />
|
||||
{renderData(data)}
|
||||
</AdminListCommon>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default AdminFeedPage;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import AdminPageWrapper from "@views/common/AdminPageWrapper";
|
||||
|
||||
const AdminFrontPage: NextPage = () => (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin" />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/admin`} />
|
||||
</Head>
|
||||
<AdminPageWrapper requiresAuthentication>
|
||||
<div data-e2e="admin-front-page">
|
||||
@@ -17,5 +17,3 @@ const AdminFrontPage: NextPage = () => (
|
||||
);
|
||||
|
||||
export default AdminFrontPage;
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import AdminCreateCommon from "@views/admin/AdminCreateCommon";
|
||||
import {
|
||||
JobAd, getJobAd, createJobAd, updateJobAd,
|
||||
} from "@models/JobAd";
|
||||
import JobAd from "@models/JobAd";
|
||||
import JobAdApi from "@api/jobAdApi";
|
||||
import DatetimeWidget from "@components/Widgets/DatetimeWidget";
|
||||
import SectionDividerWidget from "@components/Widgets/SectionDividerWidget";
|
||||
import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget";
|
||||
@@ -123,7 +121,7 @@ const JobAdCreatePage: NextPage = () => {
|
||||
useEffect(() => {
|
||||
const jobId = id && Number(id);
|
||||
if (jobId !== undefined) {
|
||||
getJobAd(jobId, true)
|
||||
JobAdApi.getJobAd(jobId, true)
|
||||
.then((res) => setFormData(res))
|
||||
.catch((err) => setError(err));
|
||||
}
|
||||
@@ -133,11 +131,11 @@ const JobAdCreatePage: NextPage = () => {
|
||||
try {
|
||||
const payload = data.formData;
|
||||
if (payload.id === undefined) {
|
||||
const resp = await createJobAd(payload);
|
||||
const resp = await JobAdApi.createJobAd(payload);
|
||||
setStatusMessage("Post created successfully");
|
||||
setFormData(resp);
|
||||
} else {
|
||||
const resp = await updateJobAd(payload);
|
||||
const resp = await JobAdApi.updateJobAd(payload);
|
||||
setStatusMessage("Post updated successfully");
|
||||
setFormData(resp);
|
||||
}
|
||||
@@ -154,26 +152,19 @@ const JobAdCreatePage: NextPage = () => {
|
||||
: "Create Ad";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin/jobads/create" />
|
||||
</Head>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
schema={buildSchema(formData)}
|
||||
UISchema={buildUISchema(formData)}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onSubmit={onSubmit}
|
||||
statusMessage={statusMessage}
|
||||
error={error}
|
||||
widgets={widgets}
|
||||
/>
|
||||
</>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
schema={buildSchema(formData)}
|
||||
UISchema={buildUISchema(formData)}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onSubmit={onSubmit}
|
||||
statusMessage={statusMessage}
|
||||
error={error}
|
||||
widgets={widgets}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default JobAdCreatePage;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import React from "react";
|
||||
import { NextPage } from "next";
|
||||
import { formatRelative } from "date-fns";
|
||||
import AdminListCommon from "@views/admin/AdminListCommon";
|
||||
import { Link } from "@components/index";
|
||||
import AddLink from "@components/AddLink";
|
||||
import { JobAd, getJobAds } from "@models/JobAd";
|
||||
import JobAd from "@models/JobAd";
|
||||
import useFetchJobAds from "@hooks/useFetchJobAds";
|
||||
|
||||
const URL = "/admin/jobads";
|
||||
|
||||
@@ -41,27 +41,14 @@ const renderData = (jobAds: JobAd[]) => {
|
||||
};
|
||||
|
||||
const AdminJobAdPage: NextPage = () => {
|
||||
const [jobAds, setAds] = useState<JobAd[]>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getJobAds({ auth: true })
|
||||
.then((res) => setAds(res));
|
||||
}, []);
|
||||
|
||||
const { data } = useFetchJobAds({ options: { auth: true } });
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`https://sik.ayy.fi/${URL}`} />
|
||||
</Head>
|
||||
<AdminListCommon>
|
||||
<h1>Job advertisements</h1>
|
||||
<AddLink text="Create job ad" to={`${URL}/create`} />
|
||||
{renderData(jobAds)}
|
||||
</AdminListCommon>
|
||||
</>
|
||||
<AdminListCommon>
|
||||
<h1>Job advertisements</h1>
|
||||
<AddLink text="Create job ad" to={`${URL}/create`} />
|
||||
{renderData(data)}
|
||||
</AdminListCommon>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default AdminJobAdPage;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import styled from "styled-components";
|
||||
import { generateToken, setTokenCookie, isAuthenticated } from "@utils/auth";
|
||||
@@ -40,52 +39,45 @@ const AdminLoginPage: NextPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin/login" />
|
||||
</Head>
|
||||
<AdminPageWrapper requiresAuthentication={false}>
|
||||
<Main>
|
||||
<h1>Log in to SIK Admin</h1>
|
||||
{next && next !== DEFAULT_REDIRECT && (
|
||||
<div className="error">You have to log in first.</div>
|
||||
)}
|
||||
<form className="admin-login-form" onSubmit={handleSubmit}>
|
||||
<label>Username
|
||||
<input
|
||||
id="login-username"
|
||||
type="text"
|
||||
name="username"
|
||||
value={username}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<label>Password
|
||||
<input
|
||||
id="login-password"
|
||||
type="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<input id="login-submit" type="submit" value="Log in" />
|
||||
</form>
|
||||
{error && (
|
||||
<div className="error">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</Main>
|
||||
</AdminPageWrapper>
|
||||
</>
|
||||
<AdminPageWrapper requiresAuthentication={false}>
|
||||
<Main>
|
||||
<h1>Log in to SIK Admin</h1>
|
||||
{next && next !== DEFAULT_REDIRECT && (
|
||||
<div className="error">You have to log in first.</div>
|
||||
)}
|
||||
<form className="admin-login-form" onSubmit={handleSubmit}>
|
||||
<label>Username
|
||||
<input
|
||||
id="login-username"
|
||||
type="text"
|
||||
name="username"
|
||||
value={username}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<label>Password
|
||||
<input
|
||||
id="login-password"
|
||||
type="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<input id="login-submit" type="submit" value="Log in" />
|
||||
</form>
|
||||
{error && (
|
||||
<div className="error">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</Main>
|
||||
</AdminPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default AdminLoginPage;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import AdminCreateCommon from "@views/admin/AdminCreateCommon";
|
||||
import {
|
||||
SignupForm, createForm, getForm, updateForm,
|
||||
} from "@models/SignupForm";
|
||||
import { SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
import DatetimeWidget from "@components/Widgets/DatetimeWidget";
|
||||
import SignupQuestionsWidget from "@components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget";
|
||||
import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget";
|
||||
@@ -113,7 +111,7 @@ const SignupCreatePage: NextPage = () => {
|
||||
useEffect(() => {
|
||||
const suId = id && Number(id);
|
||||
if (suId !== undefined) {
|
||||
getForm(suId, true)
|
||||
SignupApi.getForm(suId, true)
|
||||
.then((res) => {
|
||||
setFormData({
|
||||
...res,
|
||||
@@ -134,14 +132,14 @@ const SignupCreatePage: NextPage = () => {
|
||||
};
|
||||
|
||||
if (payload.id === undefined) {
|
||||
const resp = await createForm(payload);
|
||||
const resp = await SignupApi.createForm(payload);
|
||||
setStatusMessage("Sign-up created successfully");
|
||||
setFormData({
|
||||
...resp,
|
||||
questions: JSON.stringify(resp.questions) as any,
|
||||
});
|
||||
} else {
|
||||
const resp = await updateForm(payload);
|
||||
const resp = await SignupApi.updateForm(payload);
|
||||
setStatusMessage("Sign-up updated successfully");
|
||||
setFormData({
|
||||
...resp,
|
||||
@@ -162,9 +160,6 @@ const SignupCreatePage: NextPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin/signups/create" />
|
||||
</Head>
|
||||
<AdminCreateCommon
|
||||
title={title}
|
||||
formData={formData}
|
||||
@@ -184,6 +179,4 @@ const SignupCreatePage: NextPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default SignupCreatePage;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import AdminCreateCommon from "@views/admin/AdminCreateCommon";
|
||||
import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget";
|
||||
import { SignupForm, getForm, signupFormSendEmail } from "@models/SignupForm";
|
||||
import { SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
|
||||
const widgets = {
|
||||
markdownEditor: MarkdownEditorWidget,
|
||||
@@ -43,27 +44,31 @@ const buildUISchema = () => ({
|
||||
},
|
||||
});
|
||||
|
||||
const SignupEmailPage: NextPage = () => {
|
||||
const useInitializeData = (id: string) => {
|
||||
const [signupForm, setSignupForm] = useState<SignupForm>(null);
|
||||
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
|
||||
useEffect(() => {
|
||||
const formId = Number(id);
|
||||
if (formId !== undefined) {
|
||||
getForm(formId, true)
|
||||
SignupApi.getForm(formId, true)
|
||||
.then((res) => setSignupForm(res));
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return signupForm;
|
||||
};
|
||||
|
||||
const SignupEmailPage: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
const signupForm = useInitializeData(id as string);
|
||||
|
||||
const [error, setError] = useState<string>(null);
|
||||
const [statusMessage, setStatusMessage] = useState<string>(null);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
try {
|
||||
const payload = data.formData;
|
||||
await signupFormSendEmail(payload, id);
|
||||
await SignupApi.signupFormSendEmail(payload, Number(id));
|
||||
setStatusMessage("Email sent successfully");
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
@@ -91,6 +96,4 @@ const SignupEmailPage: NextPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default SignupEmailPage;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import styled from "styled-components";
|
||||
import { CSVLink } from "react-csv";
|
||||
import AdminListCommon from "@views/admin/AdminListCommon";
|
||||
import { SignupForm, getForm, getSignups } from "@models/SignupForm";
|
||||
import { Signup, deleteSignup } from "@models/Signup";
|
||||
import { Signup, SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
import { Button } from "@components/index";
|
||||
import noop from "@utils/noop";
|
||||
|
||||
const StyledButton = styled(Button) <{ colorOverride: "red" | "green" }>`
|
||||
background-color: ${(p: any) => p.colorOverride};
|
||||
const StyledButton = styled(Button) <{ $colorOverride: "red" | "green" }>`
|
||||
background-color: ${(p) => p.$colorOverride};
|
||||
`;
|
||||
|
||||
const SignupEmailPage: NextPage = () => {
|
||||
@@ -22,16 +22,16 @@ const SignupEmailPage: NextPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const formId = Number(id);
|
||||
getForm(formId, true)
|
||||
SignupApi.getForm(formId, true)
|
||||
.then((res) => setSignupForm(res));
|
||||
|
||||
getSignups(formId).then((res) => setSignups(res));
|
||||
SignupApi.getSignups(formId).then((res) => setSignups(res));
|
||||
}, [id]);
|
||||
|
||||
const confirmDelete = async (signup: Signup, question: any) => {
|
||||
if (window.confirm(`Delete: ${signup.id}: ${signup.answer[question.id]}; Are you sure?`) === true) {
|
||||
try {
|
||||
await deleteSignup(signup.id);
|
||||
await SignupApi.deleteSignup(signup.id);
|
||||
setSignups(signups.filter((s) => s.id !== signup.id));
|
||||
} catch (err) {
|
||||
window.alert("Delete failed!");
|
||||
@@ -64,7 +64,7 @@ const SignupEmailPage: NextPage = () => {
|
||||
))}
|
||||
<th>
|
||||
<CSVLink data={CSVData} headers={questions.map((q) => q.title)} separator=";">
|
||||
<StyledButton colorOverride="green" buttonStyle="filled" onClick={noop}>
|
||||
<StyledButton $colorOverride="green" buttonStyle="filled" onClick={noop}>
|
||||
Download CSV
|
||||
</StyledButton>
|
||||
</CSVLink>
|
||||
@@ -81,7 +81,7 @@ const SignupEmailPage: NextPage = () => {
|
||||
</td>
|
||||
))}
|
||||
<td>
|
||||
<StyledButton colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(s, questions[0])}>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(s, questions[0])}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
@@ -93,6 +93,4 @@ const SignupEmailPage: NextPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default SignupEmailPage;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { NextPage } from "next";
|
||||
import { formatRelative } from "date-fns";
|
||||
import AdminListCommon from "@views/admin/AdminListCommon";
|
||||
import { Link } from "@components/index";
|
||||
import AddLink from "@components/AddLink";
|
||||
import { SignupForm, getForms } from "@models/SignupForm";
|
||||
import { SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
|
||||
const URL = "/admin/signups";
|
||||
|
||||
@@ -44,24 +44,17 @@ const AdminSignupPage: NextPage = () => {
|
||||
const [forms, setForms] = useState<SignupForm[]>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getForms(true)
|
||||
SignupApi.getForms(true)
|
||||
.then((res) => setForms(res));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`https://sik.ayy.fi/${URL}`} />
|
||||
</Head>
|
||||
<AdminListCommon>
|
||||
<h1>Sign-up forms</h1>
|
||||
<AddLink text="Create signup form" to={`${URL}/create`} data-e2e="create-signup" />
|
||||
{renderData(forms)}
|
||||
</AdminListCommon>
|
||||
</>
|
||||
<AdminListCommon>
|
||||
<h1>Sign-up forms</h1>
|
||||
<AddLink text="Create signup form" to={`${URL}/create`} data-e2e="create-signup" />
|
||||
{renderData(forms)}
|
||||
</AdminListCommon>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => ({ props: {} });
|
||||
|
||||
export default AdminSignupPage;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { NextPage, GetStaticProps, GetStaticPaths } from "next";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
Event, useFetchEvents, eventFetcher, generateFetchParams,
|
||||
} from "@models/Event";
|
||||
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";
|
||||
@@ -16,14 +16,16 @@ interface InitialProps {
|
||||
const EventPage: NextPage<InitialProps> = ({ initialEvent }) => {
|
||||
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 (!data) return <LoadingView />;
|
||||
|
||||
if (!data || router.isFallback) return <LoadingView />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`https://sik.ayy.fi/events/${id}`} />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/events/${id}`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<EventPageView event={data as Event} />
|
||||
@@ -32,14 +34,28 @@ const EventPage: NextPage<InitialProps> = ({ initialEvent }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<InitialProps> = async (context) => {
|
||||
const { id } = context.query;
|
||||
const { url, config } = generateFetchParams(id as string);
|
||||
const initialEvent = await eventFetcher(url, config);
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const allEvents = await EventApi.getEvents();
|
||||
const paths = allEvents.map((e: Event) => ({
|
||||
params: {
|
||||
id: String(e.id),
|
||||
},
|
||||
}
|
||||
));
|
||||
return {
|
||||
paths,
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
|
||||
const { id } = params;
|
||||
const initialEvent = await EventApi.getEvent(Number(id));
|
||||
return {
|
||||
props: {
|
||||
initialEvent,
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import React from "react";
|
||||
import { NextPage, GetStaticProps, GetStaticPaths } from "next";
|
||||
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";
|
||||
|
||||
interface InitialProps {
|
||||
initialPost: Post;
|
||||
}
|
||||
|
||||
const FeedPage: NextPage<InitialProps> = ({ initialPost }) => {
|
||||
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 (!data || router.isFallback) return <LoadingView />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/feed/${id}`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<FeedPageView post={data} />
|
||||
</PageWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const feed = await FeedApi.getFeed();
|
||||
const paths = feed.map((post: Post) => ({
|
||||
params: {
|
||||
id: String(post.id),
|
||||
},
|
||||
}
|
||||
));
|
||||
return {
|
||||
paths,
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
|
||||
const { id } = params;
|
||||
const initialPost = await FeedApi.getPost(Number(id));
|
||||
return {
|
||||
props: {
|
||||
initialPost,
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
};
|
||||
|
||||
export default FeedPage;
|
||||
@@ -1,18 +1,51 @@
|
||||
import React from "react";
|
||||
import { NextPage } from "next";
|
||||
import { NextPage, GetStaticProps } from "next";
|
||||
import Head from "next/head";
|
||||
import Event from "@models/Event";
|
||||
import EventApi from "@api/eventApi";
|
||||
import useFetchEvents from "@hooks/useFetchEvents";
|
||||
import Post from "@models/Feed";
|
||||
import FeedApi from "@api/feedApi";
|
||||
import useFetchFeed from "@hooks/useFetchFeed";
|
||||
import InEnglishPageView from "@views/InEnglishPage/InEnglishPageView";
|
||||
import PageWrapper from "@views/common/PageWrapper";
|
||||
|
||||
const InEnglishPage: NextPage = () => (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/in_english" />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<InEnglishPageView />
|
||||
</PageWrapper>
|
||||
</>
|
||||
);
|
||||
const eventOptions = {
|
||||
onlyNonPast: true,
|
||||
limit: 4,
|
||||
};
|
||||
|
||||
interface InitialProps {
|
||||
initialEvents: Event[];
|
||||
initialFeed: Post[];
|
||||
}
|
||||
|
||||
const InEnglishPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
|
||||
const eventResult = useFetchEvents({ initialData: initialEvents, options: eventOptions });
|
||||
const feedResult = useFetchFeed({ initialData: initialFeed });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/in_english`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<InEnglishPageView events={eventResult.data as Event[]} feed={feedResult.data} />
|
||||
</PageWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async () => {
|
||||
const initialEvents = await EventApi.getEvents(eventOptions);
|
||||
const initialFeed = await FeedApi.getFeed();
|
||||
return {
|
||||
props: {
|
||||
initialEvents,
|
||||
initialFeed,
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
};
|
||||
|
||||
export default InEnglishPage;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { NextPage, GetStaticProps } from "next";
|
||||
import Head from "next/head";
|
||||
import {
|
||||
Event, useFetchEvents, eventFetcher, generateFetchParams as eventParams,
|
||||
} from "@models/Event";
|
||||
import {
|
||||
Post, useFetchFeed, feedFetcher, generateFetchParams as feedParams,
|
||||
} from "@models/Feed";
|
||||
import Event from "@models/Event";
|
||||
import EventApi from "@api/eventApi";
|
||||
import useFetchEvents from "@hooks/useFetchEvents";
|
||||
import Post from "@models/Feed";
|
||||
import FeedApi from "@api/feedApi";
|
||||
import useFetchFeed from "@hooks/useFetchFeed";
|
||||
import FrontPageView from "@views/FrontPage/FrontPageView";
|
||||
import PageWrapper from "@views/common/PageWrapper";
|
||||
|
||||
@@ -27,7 +27,7 @@ const FrontPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/" />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<FrontPageView events={eventResult.data as Event[]} feed={feedResult.data} />
|
||||
@@ -36,18 +36,15 @@ const FrontPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<InitialProps> = async () => {
|
||||
let url: string;
|
||||
let config: any;
|
||||
({ url, config } = eventParams("", eventOptions));
|
||||
const initialEvents = await eventFetcher(url, config);
|
||||
({ url, config } = feedParams(""));
|
||||
const initialFeed = await feedFetcher(url, config);
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async () => {
|
||||
const initialEvents = await EventApi.getEvents(eventOptions);
|
||||
const initialFeed = await FeedApi.getFeed();
|
||||
return {
|
||||
props: {
|
||||
initialEvents,
|
||||
initialFeed,
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import PageWrapper from "@views/common/PageWrapper";
|
||||
const FreshmenPage: NextPage = () => (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/kilta/fuksit" />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/kilta/fuksi`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<FreshmenPageView />
|
||||
|
||||
@@ -7,7 +7,7 @@ import PageWrapper from "@views/common/PageWrapper";
|
||||
const GuildPage: NextPage = () => (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/kilta" />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/kilta`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<GuildPageView />
|
||||
|
||||
@@ -7,7 +7,7 @@ import PageWrapper from "@views/common/PageWrapper";
|
||||
const HonoraryPage: NextPage = () => (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/kilta/kunnia" />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/kilta/kunnia`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<HonoraryPageView />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import { NextPage, GetServerSideProps } from "next";
|
||||
import { NextPage, GetStaticProps } from "next";
|
||||
import Head from "next/head";
|
||||
import {
|
||||
Event, useFetchEvents, eventFetcher, generateFetchParams as eventParams,
|
||||
} from "@models/Event";
|
||||
import {
|
||||
Post, useFetchFeed, feedFetcher, generateFetchParams as feedParams,
|
||||
} from "@models/Feed";
|
||||
import Event from "@models/Event";
|
||||
import EventApi from "@api/eventApi";
|
||||
import useFetchEvents from "@hooks/useFetchEvents";
|
||||
import Post from "@models/Feed";
|
||||
import FeedApi from "@api/feedApi";
|
||||
import useFetchFeed from "@hooks/useFetchFeed";
|
||||
import ActualPageView from "@views/ActualPage/ActualPageView";
|
||||
import PageWrapper from "@views/common/PageWrapper";
|
||||
|
||||
@@ -26,7 +26,7 @@ const ActualPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/kilta/toiminta" />
|
||||
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/kilta/toiminta`} />
|
||||
</Head>
|
||||
<PageWrapper>
|
||||
<ActualPageView events={eventResult.data} feed={feedResult.data} />
|
||||
@@ -35,18 +35,15 @@ const ActualPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<InitialProps> = async () => {
|
||||
let url: string;
|
||||
let config: any;
|
||||
({ url, config } = eventParams("", eventOptions));
|
||||
const initialEvents = await eventFetcher(url, config);
|
||||
({ url, config } = feedParams(""));
|
||||
const initialFeed = await feedFetcher(url, config);
|
||||
export const getStaticProps: GetStaticProps<InitialProps> = async () => {
|
||||
const initialEvents = await EventApi.getEvents(eventOptions);
|
||||
const initialFeed = await FeedApi.getFeed();
|
||||
return {
|
||||
props: {
|
||||
initialEvents,
|
||||
initialFeed,
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||