add filter and sort functionality to admin pages
This commit is contained in:
@@ -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 };
|
||||||
@@ -10,6 +10,7 @@ import AddLink from "@components/AddLink";
|
|||||||
import Event from "@models/Event";
|
import Event from "@models/Event";
|
||||||
import EventApi from "@api/eventApi";
|
import EventApi from "@api/eventApi";
|
||||||
import { fetcher, APIPath, API } from "@api/backend";
|
import { fetcher, APIPath, API } from "@api/backend";
|
||||||
|
import { StyledSelect, SelectWrapper } from "@components/Select";
|
||||||
|
|
||||||
const URL = "/admin/events";
|
const URL = "/admin/events";
|
||||||
|
|
||||||
@@ -21,15 +22,6 @@ const StyledButton = styled(Button) <{ $colorOverride: "red" }>`
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledSelect = styled.select`
|
|
||||||
padding: 0.25rem;
|
|
||||||
margin: 0.5rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SelectWrapper = styled.div`
|
|
||||||
padding: 0.5rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const confirmDelete = async (event: Event) => {
|
const confirmDelete = async (event: Event) => {
|
||||||
if (window.confirm(`Delete: ${event.id}: ${event.title_fi}/${event.title_en}; Are you sure?`) === true) {
|
if (window.confirm(`Delete: ${event.id}: ${event.title_fi}/${event.title_en}; Are you sure?`) === true) {
|
||||||
try {
|
try {
|
||||||
@@ -50,41 +42,36 @@ const Renderer: React.FC = () => {
|
|||||||
const [order, setOrder] = useState<string>("descending");
|
const [order, setOrder] = useState<string>("descending");
|
||||||
const [filter, setFilter] = useState<string>("all");
|
const [filter, setFilter] = useState<string>("all");
|
||||||
|
|
||||||
const [processedEvents, setProcessedEvents] = useState<Event[]>([]);
|
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(() => {
|
useEffect(() => {
|
||||||
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 checkDate = (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;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (events) {
|
|
||||||
setProcessedEvents(events.sort(eventSort).filter(checkDate));
|
|
||||||
}
|
|
||||||
}, [sort, order, filter, events]);
|
}, [sort, order, filter, events]);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -130,19 +117,18 @@ const Renderer: React.FC = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{processedEvents
|
{events.sort(eventSort).filter(dateFilter).map((event) => (
|
||||||
.map((event) => (
|
<tr key={event.id}>
|
||||||
<tr key={event.id}>
|
<td><Link to={`${URL}/${event.id}`}>{event.title_fi}</Link></td>
|
||||||
<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.start_time), new Date())}</td>
|
<td>{formatRelative(new Date(event.end_time), new Date())}</td>
|
||||||
<td>{formatRelative(new Date(event.end_time), new Date())}</td>
|
<td>
|
||||||
<td>
|
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(event)}>
|
||||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(event)}>
|
Delete
|
||||||
Delete
|
</StyledButton>
|
||||||
</StyledButton>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
))}
|
||||||
))}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { formatRelative } from "date-fns";
|
import { formatRelative } from "date-fns";
|
||||||
@@ -10,6 +10,7 @@ import AddLink from "@components/AddLink";
|
|||||||
import Post from "@models/Feed";
|
import Post from "@models/Feed";
|
||||||
import PostApi from "@api/feedApi";
|
import PostApi from "@api/feedApi";
|
||||||
import { fetcher, APIPath, API } from "@api/backend";
|
import { fetcher, APIPath, API } from "@api/backend";
|
||||||
|
import { SelectWrapper, StyledSelect } from "@components/Select";
|
||||||
|
|
||||||
const URL = "/admin/feed";
|
const URL = "/admin/feed";
|
||||||
|
|
||||||
@@ -37,6 +38,21 @@ const Renderer: React.FC = () => {
|
|||||||
const api: API = { path: APIPath.FEED, authenticated: true };
|
const api: API = { path: APIPath.FEED, authenticated: true };
|
||||||
const { data: feed, error } = useSWR<Post[]>(api, fetcher);
|
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) {
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return (
|
return (
|
||||||
@@ -52,29 +68,38 @@ const Renderer: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table>
|
<div>
|
||||||
<thead>
|
<SelectWrapper>
|
||||||
<tr>
|
Order:
|
||||||
<th>Title</th>
|
<StyledSelect name="" onChange={(e) => setOrder(e.target.value)}>
|
||||||
<th>Description</th>
|
<option value="descending">Descending</option>
|
||||||
<th>Publish time</th>
|
<option value="ascending">Ascending</option>
|
||||||
</tr>
|
</StyledSelect>
|
||||||
</thead>
|
</SelectWrapper>
|
||||||
<tbody>
|
<table>
|
||||||
{feed.map((post) => (
|
<thead>
|
||||||
<tr key={post.id}>
|
<tr>
|
||||||
<td><Link to={`${URL}/${post.id}`}>{post.title_fi}</Link></td>
|
<th>Title</th>
|
||||||
<td>{post.description_fi}</td>
|
<th>Description</th>
|
||||||
<td>{formatRelative(new Date(post.publish_time), new Date())}</td>
|
<th>Publish time</th>
|
||||||
<td>
|
|
||||||
<StyledButton $colorOverride="red" buttonStyle="filled" onClick={() => confirmDelete(post)}>
|
|
||||||
Delete
|
|
||||||
</StyledButton>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{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,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { formatRelative } from "date-fns";
|
import { formatRelative } from "date-fns";
|
||||||
@@ -10,6 +10,7 @@ import AddLink from "@components/AddLink";
|
|||||||
import { SignupForm } from "@models/Signup";
|
import { SignupForm } from "@models/Signup";
|
||||||
import SignupApi from "@api/signupApi";
|
import SignupApi from "@api/signupApi";
|
||||||
import { fetcher, APIPath, API } from "@api/backend";
|
import { fetcher, APIPath, API } from "@api/backend";
|
||||||
|
import { SelectWrapper, StyledSelect } from "@components/Select";
|
||||||
|
|
||||||
const URL = "/admin/signups";
|
const URL = "/admin/signups";
|
||||||
|
|
||||||
@@ -37,6 +38,42 @@ const Renderer: React.FC = () => {
|
|||||||
const api: API = { path: APIPath.SIGNUP_FORMS, authenticated: true };
|
const api: API = { path: APIPath.SIGNUP_FORMS, authenticated: true };
|
||||||
const { data: signupForms, error } = useSWR<SignupForm[]>(api, fetcher);
|
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) {
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return (
|
return (
|
||||||
@@ -52,6 +89,25 @@ const Renderer: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<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>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -63,7 +119,7 @@ const Renderer: React.FC = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{signupForms.map((signupForm) => (
|
{signupForms.sort(signupFormSort).filter(dateFilter).map((signupForm) => (
|
||||||
<tr key={signupForm.id}>
|
<tr key={signupForm.id}>
|
||||||
<td><Link to={`${URL}/${signupForm.id}`}>{signupForm.title_fi}</Link></td>
|
<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.start_time), new Date())}</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user