histpry list, loader
This commit is contained in:
parent
d9f9c8c013
commit
7c21815133
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: transparent; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<circle cx="50" cy="50" fill="none" stroke="#2fad8c" stroke-width="10" r="35" stroke-dasharray="164.93361431346415 56.97787143782138">
|
||||
<animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="1s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform>
|
||||
</circle></svg>
|
||||
|
After Width: | Height: | Size: 597 B |
|
|
@ -0,0 +1,23 @@
|
|||
.loader {
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
|
||||
&.inner {
|
||||
opacity: 1;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 10rem;
|
||||
max-height: 10rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
button {
|
||||
@include transition-std;
|
||||
cursor: pointer;
|
||||
color: $base-gray;
|
||||
font-size: 1.6rem;
|
||||
width: 15%;
|
||||
padding: 1rem 2rem;
|
||||
|
|
@ -35,6 +36,9 @@
|
|||
gap: 0.8rem;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
|
||||
gap: 0.8rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +71,10 @@
|
|||
}
|
||||
|
||||
&__table {
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&__head {
|
||||
min-height: unset !important;
|
||||
background: $light-black;
|
||||
|
|
@ -96,6 +104,10 @@
|
|||
background: $light-blue;
|
||||
td {
|
||||
color: $light-black;
|
||||
|
||||
svg {
|
||||
fill: $base-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,8 +145,10 @@
|
|||
}
|
||||
|
||||
tbody {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
min-height: 20rem;
|
||||
position: relative;
|
||||
// max-height: 70vh;
|
||||
// overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
@ -211,6 +225,80 @@
|
|||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__table {
|
||||
thead {
|
||||
tr {
|
||||
min-height: unset !important;
|
||||
background: $light-black;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-size: 1.6rem;
|
||||
justify-self: center;
|
||||
background: transparent;
|
||||
color: $base-gray;
|
||||
@include transition-std;
|
||||
}
|
||||
|
||||
tr {
|
||||
background: $base-black;
|
||||
&:nth-child(2n) {
|
||||
background: $light-blue;
|
||||
td {
|
||||
color: $light-black;
|
||||
|
||||
svg {
|
||||
fill: $base-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
min-height: 7rem;
|
||||
padding: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr repeat(4, 2fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
td {
|
||||
color: $base-gray;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
justify-self: center;
|
||||
font-size: 1.2rem;
|
||||
align-self: center;
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
min-height: 20rem;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,3 +9,4 @@
|
|||
@import "./details";
|
||||
@import "./source";
|
||||
@import "./edit-source";
|
||||
@import "./loader.scss";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// Icons
|
||||
import loader from "../assets/icons/loader.svg";
|
||||
|
||||
const Loader = () => {
|
||||
return (
|
||||
<div className={"loader"}>
|
||||
<div className="loader inner">
|
||||
<img src={loader} alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loader;
|
||||
|
|
@ -7,15 +7,18 @@ import { PostType } from "../types/posts";
|
|||
import { LinksAll } from "../types/links";
|
||||
|
||||
export const getPosts = (
|
||||
setLoad: React.Dispatch<boolean>,
|
||||
setPosts: React.Dispatch<PostType[]>,
|
||||
params?: string
|
||||
) => {
|
||||
setLoad(true);
|
||||
axios
|
||||
.get("http://95.85.124.41:8080/posts" + (params ? params : ""))
|
||||
.then((res) => {
|
||||
setPosts(res.data.data);
|
||||
})
|
||||
.catch((err) => {});
|
||||
.catch((err) => {})
|
||||
.finally(() => setLoad(false));
|
||||
};
|
||||
|
||||
export const deleteLink = (setSuccess: React.Dispatch<boolean>, id: number) => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Modules
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useContext, useState, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { PostContext } from "../context/PostContext";
|
||||
import { PostType } from "../types/posts";
|
||||
import { HistoryList, PostType } from "../types/posts";
|
||||
import { IconContext } from "react-icons";
|
||||
|
||||
// Icons
|
||||
|
|
@ -18,16 +19,7 @@ import { ContextType } from "../types/context";
|
|||
|
||||
const Post = () => {
|
||||
const date = new Date("0.0.0000");
|
||||
const [postData, setPostData] = useState<PostType>({
|
||||
id: -1,
|
||||
category: "",
|
||||
title: "",
|
||||
link: "",
|
||||
publish_date: date,
|
||||
summary: "",
|
||||
createdAt: date,
|
||||
updatedAt: date,
|
||||
});
|
||||
const [postData, setPostData] = useState<PostType>();
|
||||
const { posts } = useContext<ContextType>(PostContext).postValue;
|
||||
const { id } = useParams();
|
||||
|
||||
|
|
@ -48,18 +40,12 @@ const Post = () => {
|
|||
<IconContext.Provider value={{ color: "#8DD77F" }}>
|
||||
<FaBoxOpen className="dashboard__img" />
|
||||
</IconContext.Provider>
|
||||
<h1 className="post__head">
|
||||
{postData.id !== -1 ? postData.title : ""}
|
||||
</h1>
|
||||
<h1 className="post__head">{postData ? postData.title : ""}</h1>
|
||||
</div>
|
||||
<div className="post__content">
|
||||
<div className="post__content__block">
|
||||
<h4>ID</h4>
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={postData.id !== -1 ? postData.id : ""}
|
||||
/>
|
||||
<input type="text" readOnly value={postData ? postData.id : ""} />
|
||||
</div>
|
||||
<div className="post__content__block">
|
||||
<h4>Category</h4>
|
||||
|
|
@ -67,7 +53,7 @@ const Post = () => {
|
|||
type="text"
|
||||
readOnly
|
||||
value={
|
||||
postData.id !== -1
|
||||
postData
|
||||
? capitalizeFirstLetter(postData.category.toLowerCase())
|
||||
: ""
|
||||
}
|
||||
|
|
@ -78,7 +64,7 @@ const Post = () => {
|
|||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={postData.id !== -1 ? postData.title : ""}
|
||||
value={postData ? postData.title : ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="post__content__block">
|
||||
|
|
@ -86,9 +72,7 @@ const Post = () => {
|
|||
<input
|
||||
type={"text"}
|
||||
readOnly
|
||||
value={
|
||||
postData.id !== -1 ? parseDate(postData.publish_date)[0] : ""
|
||||
}
|
||||
value={postData ? parseDate(postData.publish_date)[0] : ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="post__content__block">
|
||||
|
|
@ -96,12 +80,12 @@ const Post = () => {
|
|||
<textarea
|
||||
readOnly
|
||||
rows={5}
|
||||
value={postData.id !== -1 ? postData.summary : ""}
|
||||
value={postData ? postData.summary : ""}
|
||||
></textarea>
|
||||
</div>
|
||||
<a
|
||||
className="post__content__btn"
|
||||
href={postData.id !== -1 ? postData.link : ""}
|
||||
href={postData ? postData.link : ""}
|
||||
>
|
||||
<IconContext.Provider value={{ color: "#FFFFFF" }}>
|
||||
<BiLinkExternal />
|
||||
|
|
@ -109,6 +93,49 @@ const Post = () => {
|
|||
|
||||
<span>Link</span>
|
||||
</a>
|
||||
{postData ? (
|
||||
postData.HistoryList.length > 0 ? (
|
||||
<div className="post__content__block post__content__table">
|
||||
<h2>History List</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Post ID</th>
|
||||
<th>Old published</th>
|
||||
<th>New published</th>
|
||||
<th>Created</th>
|
||||
<th>Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{postData
|
||||
? postData.HistoryList.length > 0
|
||||
? postData.HistoryList.map((History: HistoryList) => {
|
||||
return (
|
||||
<tr key={uuidv4()}>
|
||||
<td>{History.PostID}</td>
|
||||
<td>
|
||||
{parseDate(History.old_published_at)[0]}
|
||||
</td>
|
||||
<td>
|
||||
{parseDate(History.new_published_at)[0]}
|
||||
</td>
|
||||
<td>{parseDate(History.createdAt)[0]}</td>
|
||||
<td>{parseDate(History.updatedAt)[0]}</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
: ""
|
||||
: ""}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,24 +17,31 @@ import { parseDate } from "../helpers/parseDate";
|
|||
// Types
|
||||
import { paramsType } from "../types/posts";
|
||||
import { ContextType } from "../types/context";
|
||||
|
||||
// Components
|
||||
import Loader from "../components/Loader";
|
||||
|
||||
interface pageType {
|
||||
perPage: number;
|
||||
pageNumber: number;
|
||||
}
|
||||
|
||||
const Posts = () => {
|
||||
const [categories, setCategories] = useState<string[]>(["All"]);
|
||||
const [load, setLoad] = useState<boolean>(false);
|
||||
const contextValue: ContextType = useContext<ContextType>(PostContext);
|
||||
const { posts, setPosts } = contextValue.postValue;
|
||||
const { sources } = contextValue.sourceValue;
|
||||
const [showAll, setShowAll] = useState<boolean>(true);
|
||||
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",
|
||||
source: "asc",
|
||||
title: "asc",
|
||||
link: "asc",
|
||||
date: "asc",
|
||||
|
|
@ -47,29 +54,17 @@ const Posts = () => {
|
|||
useEffect(() => {
|
||||
const key = sort as keyof typeof params;
|
||||
getPosts(
|
||||
setLoad,
|
||||
setPosts,
|
||||
`?sortBy=${sort}.${params[key]}&strLimit=${page.perPage}&strOffset=${page.pageNumber}&filter=${search}`
|
||||
);
|
||||
}, [params, sort, page, search, category]);
|
||||
}, [params, sort, page, search]);
|
||||
|
||||
useEffect(() => {
|
||||
const categoriesTemp: string[] = categories;
|
||||
if (posts[0]) {
|
||||
if (posts[0].id !== -1) {
|
||||
posts.map((post: PostType) => categoriesTemp.push(post.category));
|
||||
let categoriesTempUnique = categoriesTemp.filter((element, index) => {
|
||||
return categoriesTemp.indexOf(element) === index;
|
||||
});
|
||||
setCategories(categoriesTempUnique);
|
||||
}
|
||||
if (!showAll) {
|
||||
setPage({ ...page, perPage: 60 });
|
||||
}
|
||||
}, [posts]);
|
||||
|
||||
useEffect(() => {
|
||||
if (category !== "All") {
|
||||
setPage({ ...page, perPage: 100 });
|
||||
}
|
||||
}, [category, page.perPage]);
|
||||
}, [showAll, page.perPage]);
|
||||
|
||||
return (
|
||||
<main className="posts">
|
||||
|
|
@ -85,34 +80,40 @@ const Posts = () => {
|
|||
<div className="posts__select">
|
||||
<label htmlFor="category">Source</label>
|
||||
<select
|
||||
disabled={showAll}
|
||||
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>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{sources
|
||||
? sources.map((source) => {
|
||||
return (
|
||||
<option key={uuidv4()} value={source.name}>
|
||||
{source.name}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
: ""}
|
||||
</select>
|
||||
<label>
|
||||
<span>Show all</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked
|
||||
value={showAll.toString()}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setShowAll(e.target.checked)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="posts__select">
|
||||
<label htmlFor="pp">Per page</label>
|
||||
<select
|
||||
disabled={!showAll}
|
||||
id="pp"
|
||||
disabled={category !== "All"}
|
||||
value={page.perPage}
|
||||
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setPage({ ...page, perPage: parseInt(e.target.value) });
|
||||
|
|
@ -142,7 +143,7 @@ const Posts = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<table className="posts__table">
|
||||
<table className={load ? "posts__table disabled" : "posts__table"}>
|
||||
<thead>
|
||||
<tr className="posts__table__head">
|
||||
<th
|
||||
|
|
@ -159,10 +160,10 @@ const Posts = () => {
|
|||
<th
|
||||
className={sort === "category" ? "active" : ""}
|
||||
onClick={() => {
|
||||
setSort("category");
|
||||
if (params.category !== "asc")
|
||||
setParams({ ...params, category: "asc" });
|
||||
else setParams({ ...params, category: "desc" });
|
||||
setSort("source");
|
||||
if (params.source !== "asc")
|
||||
setParams({ ...params, source: "asc" });
|
||||
else setParams({ ...params, source: "desc" });
|
||||
}}
|
||||
>
|
||||
Source
|
||||
|
|
@ -225,9 +226,10 @@ const Posts = () => {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{load ? <Loader /> : null}
|
||||
{posts[0] ? (
|
||||
posts[0].id !== -1 ? (
|
||||
category === "All" ? (
|
||||
showAll ? (
|
||||
posts.map((post: PostType) => {
|
||||
return (
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
export interface HistoryList {
|
||||
old_published_at: Date;
|
||||
new_published_at: Date;
|
||||
PostID: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
export interface PostType {
|
||||
id: number;
|
||||
category: string;
|
||||
|
|
@ -7,11 +14,12 @@ export interface PostType {
|
|||
summary: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
HistoryList: HistoryList[];
|
||||
}
|
||||
|
||||
export interface paramsType {
|
||||
id: "asc" | "desc";
|
||||
category: "asc" | "desc";
|
||||
source: "asc" | "desc";
|
||||
title: "asc" | "desc";
|
||||
link: "asc" | "desc";
|
||||
date: "asc" | "desc";
|
||||
|
|
|
|||
Loading…
Reference in New Issue