Merge branch 'master' into 'production'
Master See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!115
This commit is contained in:
@@ -23,5 +23,5 @@ export default styled(ChangeLanguageButton)`
|
||||
font-size: 4rem;
|
||||
background: none;
|
||||
border: none;
|
||||
width: fit-content;
|
||||
width: 2cm;
|
||||
`;
|
||||
|
||||
+24
-6
@@ -70,16 +70,34 @@ const nameToIcon = (name: IconType): JSX.Element | null => {
|
||||
}
|
||||
if (name === IconType.FinlandFlag) {
|
||||
return (
|
||||
<span role="img">
|
||||
🇫🇮
|
||||
</span>
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 640 480"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Finland flag</title>
|
||||
<path fill="#fff" d="M0 0h640v480H0z" />
|
||||
<path fill="#002f6c" d="M0 174.5h640v131H0z" />
|
||||
<path fill="#002f6c" d="M175.5 0h130.9v480h-131z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
if (name === IconType.GBFlag) {
|
||||
return (
|
||||
<span role="img">
|
||||
🇬🇧
|
||||
</span>
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 640 480"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>GB flag</title>
|
||||
<path fill="#012169" d="M0 0h640v480H0z" />
|
||||
<path fill="#FFF" d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0h75z" />
|
||||
<path fill="#C8102E" d="m424 281 216 159v40L369 281h55zm-184 20 6 35L54 480H0l240-179zM640 0v3L391 191l2-44L590 0h50zM0 0l239 176h-60L0 42V0z" />
|
||||
<path fill="#FFF" d="M241 0v480h160V0H241zM0 160v160h640V160H0z" />
|
||||
<path fill="#C8102E" d="M0 193v96h640v-96H0zM273 0v480h96V0h-96z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
padding: 0.25rem;
|
||||
margin: 0.5rem;
|
||||
`;
|
||||
|
||||
const SelectWrapper = styled.div`
|
||||
padding: 0.5rem;
|
||||
`;
|
||||
|
||||
export { StyledSelect, SelectWrapper };
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { NextPage } from "next";
|
||||
import useSWR from "swr";
|
||||
import { formatRelative } from "date-fns";
|
||||
@@ -10,6 +10,7 @@ import AddLink from "@components/AddLink";
|
||||
import Event from "@models/Event";
|
||||
import EventApi from "@api/eventApi";
|
||||
import { fetcher, APIPath, API } from "@api/backend";
|
||||
import { StyledSelect, SelectWrapper } from "@components/Select";
|
||||
|
||||
const URL = "/admin/events";
|
||||
|
||||
@@ -37,42 +38,100 @@ const Renderer: React.FC = () => {
|
||||
const api: API = { path: APIPath.EVENTS, authenticated: true };
|
||||
const { data: events, error } = useSWR<Event[]>(api, fetcher);
|
||||
|
||||
const [sort, setSort] = useState<string>("start_time");
|
||||
const [order, setOrder] = useState<string>("descending");
|
||||
const [filter, setFilter] = useState<string>("all");
|
||||
|
||||
const eventSort = (a, b) => {
|
||||
let result = 0;
|
||||
if (order === "descending") {
|
||||
if (["start_time", "end_time"].includes(sort)) {
|
||||
result = new Date(b[sort]).getTime() - new Date(a[sort]).getTime();
|
||||
} else if (sort === "id") {
|
||||
result = b[sort] - a[sort];
|
||||
}
|
||||
} else if (order === "ascending") {
|
||||
if (["start_time", "end_time"].includes(sort)) {
|
||||
result = new Date(a[sort]).getTime() - new Date(b[sort]).getTime();
|
||||
} else if (sort === "id") {
|
||||
result = a[sort] - b[sort];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const dateFilter = (a) => {
|
||||
let result = true;
|
||||
|
||||
if (filter === "upcoming") {
|
||||
result = new Date(a.end_time).getTime() > Date.now();
|
||||
} else if (filter === "past") {
|
||||
result = new Date(a.end_time).getTime() < Date.now();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
}, [sort, order, filter, events]);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
return (
|
||||
<div>
|
||||
Failed loading events
|
||||
Failed loading events.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!events?.length) {
|
||||
return <div>No events.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Start time</th>
|
||||
<th>End time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{events.map((event) => (
|
||||
<tr key={event.id}>
|
||||
<td><Link to={`${URL}/${event.id}`}>{event.title_fi}</Link></td>
|
||||
<td>{formatRelative(new Date(event.start_time), new Date())}</td>
|
||||
<td>{formatRelative(new Date(event.end_time), new Date())}</td>
|
||||
<td>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(event)}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
<div>
|
||||
<SelectWrapper>
|
||||
Sort by:
|
||||
<StyledSelect name="" onChange={(e) => setSort(e.target.value)}>
|
||||
<option value="start_time">Start time</option>
|
||||
<option value="end_time">End time</option>
|
||||
<option value="id">Creation order</option>
|
||||
</StyledSelect>
|
||||
Order:
|
||||
<StyledSelect name="" onChange={(e) => setOrder(e.target.value)}>
|
||||
<option value="descending">Descending</option>
|
||||
<option value="ascending">Ascending</option>
|
||||
</StyledSelect>
|
||||
Filter:
|
||||
<StyledSelect name="" onChange={(e) => setFilter(e.target.value)}>
|
||||
<option value="all">All</option>
|
||||
<option value="upcoming">Upcoming</option>
|
||||
<option value="past">Past</option>
|
||||
</StyledSelect>
|
||||
</SelectWrapper>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Start time</th>
|
||||
<th>End time</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{events.sort(eventSort).filter(dateFilter).map((event) => (
|
||||
<tr key={event.id}>
|
||||
<td><Link to={`${URL}/${event.id}`}>{event.title_fi}</Link></td>
|
||||
<td>{formatRelative(new Date(event.start_time), new Date())}</td>
|
||||
<td>{formatRelative(new Date(event.end_time), new Date())}</td>
|
||||
<td>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(event)}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { NextPage } from "next";
|
||||
import useSWR from "swr";
|
||||
import { formatRelative } from "date-fns";
|
||||
@@ -10,6 +10,7 @@ import AddLink from "@components/AddLink";
|
||||
import Post from "@models/Feed";
|
||||
import PostApi from "@api/feedApi";
|
||||
import { fetcher, APIPath, API } from "@api/backend";
|
||||
import { SelectWrapper, StyledSelect } from "@components/Select";
|
||||
|
||||
const URL = "/admin/feed";
|
||||
|
||||
@@ -37,6 +38,21 @@ const Renderer: React.FC = () => {
|
||||
const api: API = { path: APIPath.FEED, authenticated: true };
|
||||
const { data: feed, error } = useSWR<Post[]>(api, fetcher);
|
||||
|
||||
const [order, setOrder] = useState<string>("descending");
|
||||
|
||||
const feedSort = (a, b) => {
|
||||
let result = 0;
|
||||
if (order === "descending") {
|
||||
result = new Date(b.publish_time).getTime() - new Date(a.publish_time).getTime();
|
||||
} else if (order === "ascending") {
|
||||
result = new Date(a.publish_time).getTime() - new Date(b.publish_time).getTime();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
}, [order, feed]);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
return (
|
||||
@@ -52,29 +68,38 @@ const Renderer: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Publish time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{feed.map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td><Link to={`${URL}/${post.id}`}>{post.title_fi}</Link></td>
|
||||
<td>{post.description_fi}</td>
|
||||
<td>{formatRelative(new Date(post.publish_time), new Date())}</td>
|
||||
<td>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(post)}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
<div>
|
||||
<SelectWrapper>
|
||||
Order:
|
||||
<StyledSelect name="" onChange={(e) => setOrder(e.target.value)}>
|
||||
<option value="descending">Descending</option>
|
||||
<option value="ascending">Ascending</option>
|
||||
</StyledSelect>
|
||||
</SelectWrapper>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Publish time</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{feed.sort(feedSort).map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td><Link to={`${URL}/${post.id}`}>{post.title_fi}</Link></td>
|
||||
<td>{post.description_fi}</td>
|
||||
<td>{formatRelative(new Date(post.publish_time), new Date())}</td>
|
||||
<td>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(post)}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { NextPage } from "next";
|
||||
import useSWR from "swr";
|
||||
import { formatRelative } from "date-fns";
|
||||
import { toast } from "react-toastify";
|
||||
import styled from "styled-components";
|
||||
@@ -8,6 +9,8 @@ import { Button, Link } from "@components/index";
|
||||
import AddLink from "@components/AddLink";
|
||||
import { SignupForm } from "@models/Signup";
|
||||
import SignupApi from "@api/signupApi";
|
||||
import { fetcher, APIPath, API } from "@api/backend";
|
||||
import { SelectWrapper, StyledSelect } from "@components/Select";
|
||||
|
||||
const URL = "/admin/signups";
|
||||
|
||||
@@ -31,57 +34,117 @@ const confirmDelete = async (signup: SignupForm) => {
|
||||
}
|
||||
};
|
||||
|
||||
const renderData = (signupForms: SignupForm[]) => {
|
||||
if (!signupForms || signupForms.length === 0) {
|
||||
const Renderer: React.FC = () => {
|
||||
const api: API = { path: APIPath.SIGNUP_FORMS, authenticated: true };
|
||||
const { data: signupForms, error } = useSWR<SignupForm[]>(api, fetcher);
|
||||
|
||||
const [sort, setSort] = useState<string>("start_time");
|
||||
const [order, setOrder] = useState<string>("descending");
|
||||
const [filter, setFilter] = useState<string>("all");
|
||||
|
||||
const signupFormSort = (a, b) => {
|
||||
let result = 0;
|
||||
if (order === "descending") {
|
||||
if (["start_time", "end_time"].includes(sort)) {
|
||||
result = new Date(b[sort]).getTime() - new Date(a[sort]).getTime();
|
||||
} else if (sort === "id") {
|
||||
result = b[sort] - a[sort];
|
||||
}
|
||||
} else if (order === "ascending") {
|
||||
if (["start_time", "end_time"].includes(sort)) {
|
||||
result = new Date(a[sort]).getTime() - new Date(b[sort]).getTime();
|
||||
} else if (sort === "id") {
|
||||
result = a[sort] - b[sort];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const dateFilter = (a) => {
|
||||
let result = true;
|
||||
|
||||
if (filter === "upcoming") {
|
||||
result = new Date(a.end_time).getTime() > Date.now();
|
||||
} else if (filter === "past") {
|
||||
result = new Date(a.end_time).getTime() < Date.now();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
}, [sort, order, filter, signupForms]);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
return (
|
||||
<div>
|
||||
Failed loading events.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!signupForms?.length) {
|
||||
return <div>No signup forms.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Start time</th>
|
||||
<th>End time</th>
|
||||
<th>Sign-ups</th>
|
||||
<th>Send email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{signupForms.map((signupForm) => (
|
||||
<tr key={signupForm.id}>
|
||||
<td><Link to={`${URL}/${signupForm.id}`}>{signupForm.title_fi}</Link></td>
|
||||
<td>{formatRelative(new Date(signupForm.start_time), new Date())}</td>
|
||||
<td>{formatRelative(new Date(signupForm.end_time), new Date())}</td>
|
||||
<td><Link to={`${URL}/${signupForm.id}/list`}>View</Link></td>
|
||||
<td><Link to={`${URL}/${signupForm.id}/email`}>Send</Link></td>
|
||||
<td>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(signupForm)}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
<div>
|
||||
<SelectWrapper>
|
||||
Sort by:
|
||||
<StyledSelect name="" onChange={(e) => setSort(e.target.value)}>
|
||||
<option value="start_time">Start time</option>
|
||||
<option value="end_time">End time</option>
|
||||
<option value="id">Creation order</option>
|
||||
</StyledSelect>
|
||||
Order:
|
||||
<StyledSelect name="" onChange={(e) => setOrder(e.target.value)}>
|
||||
<option value="descending">Descending</option>
|
||||
<option value="ascending">Ascending</option>
|
||||
</StyledSelect>
|
||||
Filter:
|
||||
<StyledSelect name="" onChange={(e) => setFilter(e.target.value)}>
|
||||
<option value="all">All</option>
|
||||
<option value="upcoming">Upcoming</option>
|
||||
<option value="past">Past</option>
|
||||
</StyledSelect>
|
||||
</SelectWrapper>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Start time</th>
|
||||
<th>End time</th>
|
||||
<th>Sign-ups</th>
|
||||
<th>Send email</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{signupForms.sort(signupFormSort).filter(dateFilter).map((signupForm) => (
|
||||
<tr key={signupForm.id}>
|
||||
<td><Link to={`${URL}/${signupForm.id}`}>{signupForm.title_fi}</Link></td>
|
||||
<td>{formatRelative(new Date(signupForm.start_time), new Date())}</td>
|
||||
<td>{formatRelative(new Date(signupForm.end_time), new Date())}</td>
|
||||
<td><Link to={`${URL}/${signupForm.id}/list`}>View</Link></td>
|
||||
<td><Link to={`${URL}/${signupForm.id}/email`}>Send</Link></td>
|
||||
<td>
|
||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(signupForm)}>
|
||||
Delete
|
||||
</StyledButton>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AdminSignupPage: NextPage = () => {
|
||||
const [forms, setForms] = useState<SignupForm[]>(null);
|
||||
|
||||
useEffect(() => {
|
||||
SignupApi.getForms(true)
|
||||
.then((res) => setForms(res));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AdminListCommon>
|
||||
<h1>Sign-up forms</h1>
|
||||
<AddLink text="Create signup form" to={`${URL}/create`} data-e2e="create-signup" />
|
||||
{renderData(forms)}
|
||||
</AdminListCommon>
|
||||
);
|
||||
};
|
||||
const AdminSignupPage: NextPage = () => (
|
||||
<AdminListCommon>
|
||||
<h1>Sign-up forms</h1>
|
||||
<AddLink text="Create signup form" to={`${URL}/create`} data-e2e="create-signup" />
|
||||
<Renderer />
|
||||
</AdminListCommon>
|
||||
);
|
||||
|
||||
export default AdminSignupPage;
|
||||
|
||||
@@ -24,6 +24,7 @@ const Fingrid = "https://static.sahkoinsinoorikilta.fi/img/corporate_logos/fingr
|
||||
const Okmetic = "https://static.sahkoinsinoorikilta.fi/img/corporate_logos/okmetic.jpg";
|
||||
const Nokia = "https://static.sahkoinsinoorikilta.fi/img/corporate_logos/nokia.jpg";
|
||||
const Granlund = "https://static.sahkoinsinoorikilta.fi/img/corporate_logos/granlund.jpg";
|
||||
const GE = "https://static.sahkoinsinoorikilta.fi/img/corporate_logos/GE.png";
|
||||
|
||||
interface FrontPageViewProps {
|
||||
events: Event[];
|
||||
@@ -111,6 +112,9 @@ const FrontPageView: React.FC<FrontPageViewProps> = ({ events, feed }) => (
|
||||
<Link to="https://www.granlund.fi/">
|
||||
<Image src={Granlund} alt="Granlund" layout="responsive" width={200} height={100} objectFit="contain" />
|
||||
</Link>
|
||||
<Link to="https://www.gehealthcare.fi/">
|
||||
<Image src={GE} alt="GE" layout="responsive" width={200} height={100} objectFit="contain" />
|
||||
</Link>
|
||||
</div>
|
||||
<Link to="/yritysyhteistyo">Haluatko kuulla lisää yhteistyöstä kanssamme?</Link>
|
||||
</SponsorReel>
|
||||
|
||||
Reference in New Issue
Block a user