somewhat working hater management

This commit is contained in:
Stepan Usatiuk
2023-12-28 18:20:18 +01:00
parent 43d1f90549
commit a0b46ce650
11 changed files with 288 additions and 11 deletions

View File

@@ -37,6 +37,7 @@ import { Chats } from "./Chats";
import { ChatCreate } from "./ChatCreate";
import { Chat } from "./Chat";
import { ChatEdit } from "./ChatEdit";
import { Haters } from "./Haters";
const router = createBrowserRouter([
{
@@ -98,6 +99,10 @@ const router = createBrowserRouter([
// action: profileSelfAction,
element: <Profile self={false} />,
},
{
path: "haters",
element: <Haters />,
},
],
},
{

21
client/src/Haters.scss Normal file
View File

@@ -0,0 +1,21 @@
@import "./common";
.hatersManagement {
min-width: 100%;
display: flex;
flex-direction: column;
padding: 2.5rem 2rem 0;
.tools {
display: flex;
flex-direction: column;
max-width: 10rem;
}
.found {
margin-top: 1rem;
}
.posts {
}
}

178
client/src/Haters.tsx Normal file
View File

@@ -0,0 +1,178 @@
import { useEffect, useState } from "react";
import { deleteUser, getAllPerson } from "./api/Person";
import { getAllMessage } from "./api/Message";
import { getAllPost } from "./api/Post";
import { isError, TMessageTo, TPersonTo, TPostTo } from "./api/dto";
import "./Haters.scss";
import "./Post.scss";
import { getTokenUserUuid } from "./api/utils";
export function Haters() {
const [data, setData] = useState<
| null
| {
error: false;
persons: TPersonTo[];
messages: TMessageTo[];
posts: TPostTo[];
}
| { error: true; what: string }
>(null);
const [search, setSearch] = useState<string>("");
const [deleted, setDeleted] = useState<string[]>([]);
useEffect(() => {
let ignore = false;
const fetchall = async () => {
return {
error: false,
persons: await getAllPerson(),
messages: await getAllMessage(),
posts: await getAllPost(),
};
};
fetchall()
.then((result) => {
if (isError(result.persons))
setData({
error: true,
what: result.persons.errors.join(" "),
});
else if (isError(result.messages))
setData({
error: true,
what: result.messages.errors.join(" "),
});
else if (isError(result.posts))
setData({
error: true,
what: result.posts.errors.join(" "),
});
else if (!ignore) {
setData(
result as {
error: false;
persons: TPersonTo[];
messages: TMessageTo[];
posts: TPostTo[];
},
);
}
})
.catch((e) => setData(e));
return () => {
ignore = true;
};
}, []);
if (!data || data.error) {
return (
<div className={"hatersManagement"}>
{data ? data.what : "error"}
</div>
);
}
const foundM = data.messages.filter(
(m) =>
m.contents.includes(search) && m.authorUuid != getTokenUserUuid(),
);
const foundP = data.posts.filter(
(p) => p.text.includes(search) && p.authorUuid != getTokenUserUuid(),
);
const foundU = new Set<string>();
const allfound: {
id: number;
type: string;
contents: string;
author: string;
}[] = [];
foundM.forEach((m) => {
foundU.add(m.authorUuid);
allfound.push({
id: m.id,
type: "message",
contents: m.contents,
author: m.authorUsername,
});
});
foundP.forEach((p) => {
foundU.add(p.authorUuid);
allfound.push({
id: p.id,
type: "post",
contents: p.text,
author: p.authorUsername,
});
});
console.log(foundU);
const foundUArr = Array.from(foundU);
return (
<div className={"hatersManagement"}>
<div className={"tools"}>
<label htmlFor="whattosearch">Search for:</label>
<input
type="text"
name="whattosearch"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<button
onClick={(e) => {
e.preventDefault();
foundUArr.forEach((u) =>
deleteUser(u)
.then((e) => {
if (isError(e)) {
setDeleted((old) => [
...old,
`error deleting user with uuid ${u}: ` +
e.errors.join(" "),
]);
} else {
setDeleted((old) => [
...old,
"deleted user with id: " + u,
]);
}
})
.catch((e) => {
setDeleted((old) => [
...old,
`error deleting user with uuid ${u}`,
]);
}),
);
}}
>
delete all users
</button>
<div>
{deleted.map((d) => (
<span>{d}</span>
))}
</div>
</div>
<div className={"found"}>
{allfound.map((m) => (
<div key={m.type + +m.id} className={"post"}>
<span className={"text"}>{m.contents}</span>
<div className={"footer"}>
<div className={"info"}>
{m.type} by {m.author}
</div>
</div>
</div>
))}
</div>
</div>
);
}

View File

@@ -53,6 +53,14 @@ export function Home() {
<NavLink to={"profile"} className={activePendingClassName}>
Profile
</NavLink>
{loaderData.isAdmin && (
<NavLink
to={"haters"}
className={activePendingClassName}
>
Haters
</NavLink>
)}
</div>
</div>
<div id="HomeContent">

View File

@@ -76,17 +76,25 @@ export function Profile({ self }: IProfileProps) {
<>
<span className={"fullName"}>{user.fullName}</span>
<span className={"username"}>{user.username}</span>
{<button onClick={() => setEditing(true)}>edit</button>}
<Form className={"postForm"} method="post">
<button
name="intent"
value="deleteSelf"
type="submit"
disabled={busy}
>
delete account
</button>
</Form>
{self && (
<>
{
<button onClick={() => setEditing(true)}>
edit
</button>
}
<Form className={"postForm"} method="post">
<button
name="intent"
value="deleteSelf"
type="submit"
disabled={busy}
>
delete account
</button>
</Form>
</>
)}
</>
)}
</div>

View File

@@ -46,3 +46,7 @@ export async function deleteMessage(
NoContentToResp,
);
}
export async function getAllMessage(): Promise<TMessagesToResp> {
return fetchJSONAuth("/message", "GET", MessagesToResp);
}

View File

@@ -36,6 +36,10 @@ export async function deleteSelf(): Promise<TNoContentToResp> {
return fetchJSONAuth("/person/self", "DELETE", NoContentToResp);
}
export async function deleteUser(uuid: string): Promise<TNoContentToResp> {
return fetchJSONAuth("/person/by-uuid/" + uuid, "DELETE", NoContentToResp);
}
export async function getPersonByUuid(uuid: string): Promise<TPersonToResp> {
return fetchJSONAuth("/person/by-uuid/" + uuid, "GET", PersonToResp);
}

View File

@@ -50,3 +50,7 @@ export async function getPostsByAuthorUsername(
PostToArrResp,
);
}
export async function getAllPost(): Promise<TPostToArrResp> {
return fetchJSONAuth("/post", "GET", PostToArrResp);
}