posts page filter, sort, pagination

This commit is contained in:
VividTruthKeeper 2022-09-13 13:54:45 +05:00
parent 830459cdee
commit e980a47515
9 changed files with 302 additions and 68 deletions

1
dist/assets/index.0ae2b81c.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View File

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Admin Panel</title>
<script type="module" crossorigin src="/assets/index.ebddc40f.js"></script>
<link rel="stylesheet" href="/assets/index.727c4021.css">
<script type="module" crossorigin src="/assets/index.3929b7af.js"></script>
<link rel="stylesheet" href="/assets/index.0ae2b81c.css">
</head>
<body>
<div id="root"></div>

View File

@ -45,7 +45,7 @@ button {
button:disabled {
background: #d4d4d4 !important;
border: none !important;
cursor: not-allowed;
cursor: not-allowed !important;
}
a {

View File

@ -1,4 +1,50 @@
.posts {
&__pagination {
width: 100%;
display: flex;
gap: 3rem;
align-items: center;
justify-content: flex-end;
input {
width: 5%;
text-align: center;
font-weight: bold;
}
button {
cursor: pointer;
font-size: 1.6rem;
width: 15%;
padding: 1rem 2rem;
border-radius: 0.5rem;
background: #7c69ef;
color: white;
}
}
&__select {
display: flex;
flex-direction: column;
gap: 0.8rem;
label {
font-size: 1.4rem;
}
input {
font-size: 1.6rem;
padding: 1rem 1.2rem;
border: 1px solid #b8b9bb;
border-radius: 0.2rem;
cursor: default;
background: #fff;
}
&__wrapper {
display: flex;
gap: 1rem;
}
}
select {
padding: 1rem 1.2rem;
border: 1px solid #b8b9bb;
@ -22,9 +68,21 @@
}
th {
cursor: pointer;
width: 100%;
height: 100%;
text-align: center;
font-size: 1.6rem;
justify-self: center;
background: transparent;
color: #000;
@include transition-std;
&.active {
background: rgb(98, 98, 98);
color: #fff;
@include transition-std;
}
}
.post-link {
@ -151,3 +209,11 @@
}
}
}
.table {
&__empty {
font-size: 1.6rem;
text-align: center;
}
}

View File

@ -6,9 +6,12 @@ import React from "react";
import { PostType } from "../types/posts";
import { LinksAll } from "../types/links";
export const getPosts = (setPosts: React.Dispatch<PostType[]>) => {
export const getPosts = (
setPosts: React.Dispatch<PostType[]>,
params?: string
) => {
axios
.get("http://95.85.124.41:8080/posts")
.get("http://95.85.124.41:8080/posts" + (params ? params : ""))
.then((res) => {
setPosts(res.data.data);
})

View File

@ -14,34 +14,55 @@ import { Link } from "react-router-dom";
import { parseDate } from "../helpers/parseDate";
import { capitalizeFirstLetter } from "../helpers/stringMethods";
// Data
const headers: string[] = [
"ID",
"Category",
"Title",
"Link",
"Date",
"Summary",
];
// Types
import { paramsType } from "../types/posts";
interface pageType {
perPage: number;
pageNumber: number;
}
const Posts = () => {
const [categories, setCategories] = useState<string[]>(["All"]);
const { posts, setPosts } = useContext<any>(PostContext);
const [category, setCategory] = useState<string>("All");
const [sort, setSort] = useState<string>("id");
const [page, setPage] = useState<pageType>({
perPage: 10,
pageNumber: 1,
});
const [params, setParams] = useState<paramsType>({
id: "asc",
category: "asc",
title: "asc",
link: "asc",
date: "asc",
summary: "asc",
});
const [search, setSearch] = useState<string>("");
useEffect(() => {
getPosts(setPosts);
}, []);
const key = sort as keyof typeof params;
getPosts(
setPosts,
`?sortBy=${sort}.${params[key]}&strLimit=${page.perPage}&strOffset=${page.pageNumber}&filter=${search}`
);
}, [params, sort, page, search]);
useEffect(() => {
const categoriesTemp: string[] = categories;
if (posts[0].id !== -1) {
posts.map((post: PostType) =>
categoriesTemp.push(capitalizeFirstLetter(post.category.toLowerCase()))
);
let categoriesTempUnique = categoriesTemp.filter((element, index) => {
return categoriesTemp.indexOf(element) === index;
});
setCategories(categoriesTempUnique);
if (posts[0]) {
if (posts[0].id !== -1) {
posts.map((post: PostType) =>
categoriesTemp.push(
capitalizeFirstLetter(post.category.toLowerCase())
)
);
let categoriesTempUnique = categoriesTemp.filter((element, index) => {
return categoriesTemp.indexOf(element) === index;
});
setCategories(categoriesTempUnique);
}
}
}, [posts]);
@ -53,40 +74,145 @@ const Posts = () => {
<BsFillFileEarmarkPostFill className="dashboard__img" />
<h1>Posts</h1>
</div>
<div className="posts_select">
<select
value={category}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
setCategory(e.target.value);
}}
>
{categories.map((category) => {
if (category === "All") {
return (
<option key={uuidv4()} value={category} defaultChecked>
{category}
</option>
);
} else {
return (
<option key={uuidv4()} value={category}>
{category}
</option>
);
}
})}
</select>
<div className="posts__select__wrapper">
<div className="posts__select">
<label htmlFor="category">Category</label>
<select
id="category"
value={category}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
setCategory(e.target.value);
}}
>
{categories.map((category) => {
if (category === "All") {
return (
<option key={uuidv4()} value={category} defaultChecked>
{category}
</option>
);
} else {
return (
<option key={uuidv4()} value={category}>
{category}
</option>
);
}
})}
</select>
</div>
<div className="posts__select">
<label htmlFor="pp">Per page</label>
<select
id="pp"
value={page.perPage}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
setPage({ ...page, perPage: parseInt(e.target.value) });
}}
>
<option value="10" defaultChecked>
10
</option>
<option value="15" defaultChecked>
15
</option>
<option value="20" defaultChecked>
20
</option>
<option value="30" defaultChecked>
30
</option>
<option value="40" defaultChecked>
40
</option>
</select>
</div>
<div className="posts__select">
<label htmlFor="filter">Filter</label>
<input
type="text"
id="filter"
value={search}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value);
}}
/>
</div>
</div>
<table className="posts__table">
<tbody>
<tr className="posts__table__head">
{headers.map((header: string) => {
return <th key={uuidv4()}>{header}</th>;
})}
<th
className={sort === "id" ? "active" : ""}
onClick={() => {
setSort("id");
if (params.id === "asc")
setParams({ ...params, id: "asc" });
else setParams({ ...params, id: "desc" });
}}
>
ID
</th>
<th
className={sort === "category" ? "active" : ""}
onClick={() => {
setSort("category");
if (params.id === "asc")
setParams({ ...params, category: "asc" });
else setParams({ ...params, category: "desc" });
}}
>
Category
</th>
<th
className={sort === "title" ? "active" : ""}
onClick={() => {
setSort("title");
if (params.id === "asc")
setParams({ ...params, title: "asc" });
else setParams({ ...params, title: "desc" });
}}
>
Title
</th>
<th
className={sort === "link" ? "active" : ""}
onClick={() => {
setSort("link");
if (params.id === "asc")
setParams({ ...params, link: "asc" });
else setParams({ ...params, link: "desc" });
}}
>
Link
</th>
<th
className={sort === "date" ? "active" : ""}
onClick={() => {
setSort("date");
if (params.id === "asc")
setParams({ ...params, date: "asc" });
else setParams({ ...params, date: "desc" });
}}
>
Date
</th>
<th
className={sort === "summary" ? "active" : ""}
onClick={() => {
setSort("summary");
if (params.id === "asc")
setParams({ ...params, summary: "asc" });
else setParams({ ...params, summary: "desc" });
}}
>
Summary
</th>
</tr>
{posts[0].id !== -1
? category === "All"
? posts.map((post: PostType, index: number) => {
{posts[0] ? (
posts[0].id !== -1 ? (
category === "All" ? (
posts.map((post: PostType, index: number) => {
return (
<Link
className="post-link"
@ -94,7 +220,7 @@ const Posts = () => {
key={uuidv4()}
>
<tr>
<td>{index + 1}</td>
<td>{post.id}</td>
<td>
{capitalizeFirstLetter(
post.category.toLowerCase()
@ -116,7 +242,8 @@ const Posts = () => {
</Link>
);
})
: posts.map((post: PostType, index: number) => {
) : (
posts.map((post: PostType, index: number) => {
if (
capitalizeFirstLetter(post.category.toLowerCase()) ===
category
@ -153,9 +280,38 @@ const Posts = () => {
return "";
}
})
: ""}
)
) : (
""
)
) : (
<td>
<tr className="table__empty">No posts</tr>
</td>
)}
</tbody>
</table>
<div className="posts__pagination">
<button
type="button"
disabled={page.pageNumber === 1}
onClick={() =>
setPage({ ...page, pageNumber: page.pageNumber - 1 })
}
>
Previous
</button>
<input type="text" value={page.pageNumber} readOnly />
<button
disabled={posts[0] ? false : true}
type="button"
onClick={() =>
setPage({ ...page, pageNumber: page.pageNumber + 1 })
}
>
Next
</button>
</div>
</div>
</div>
</main>

View File

@ -8,3 +8,12 @@ export interface PostType {
createdAt: Date;
updatedAt: Date;
}
export interface paramsType {
id: "asc" | "desc";
category: "asc" | "desc";
title: "asc" | "desc";
link: "asc" | "desc";
date: "asc" | "desc";
summary: "asc" | "desc";
}