add: improved frontend and added descriptions to users

This commit is contained in:
Lukian 2025-05-17 00:22:31 +02:00
parent 6342377aa0
commit eca9efc170
14 changed files with 266 additions and 29 deletions

View file

@ -75,10 +75,11 @@ label {
.form-horizontal {
display: flex;
flex-direction: row;
justify-content: space-between;
justify-content: start;
align-items: center;
gap: 5px;
position: relative;
flex-wrap: wrap;
}
.form-vertical {
@ -147,7 +148,7 @@ label {
.forum-button {
border: 1px solid #a678af;
background-color: #847996;
background-color: #7d5a81;
color: black;
}

View file

@ -4,6 +4,8 @@ import { Channels, User } from "../types"
import axios from "axios"
import TopBar from "../components/TopBar"
import "../styles/ChannelsPage.css"
export default function ChannelsPage({socket}: {socket: WebSocket}) {
const [channels, setChannels] = useState<Channels>();
const [search, setSearch] = useState<string>("");
@ -85,18 +87,22 @@ export default function ChannelsPage({socket}: {socket: WebSocket}) {
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<ul>
<div className="forum-channels">
{channels?.sort().filter((channel) => channel.name.toLowerCase().includes(search.toLowerCase())).map((channel) => (
<li key={channel.id}>
<Link to={`/c/${channel.name}`}>{channel.name}</Link>
<div key={channel.id} className="forum-channel">
<div className="forum-channel-left">
<h3><Link to={`/c/${channel.name}`}>{channel.name}</Link></h3>
<p>{channel.description}</p>
<p>Owner : <Link to={`/u/${channel.owner_username}`}>{channel.owner_username}</Link></p>
</div>
{user?.admin == 1 && (
<button onClick={() => deleteChannel(channel.name)} className="forum-button">
Delete
</button>
)}
</li>
</div>
))}
</ul>
</div>
</div>
</div>
)

View file

@ -7,6 +7,7 @@ import TopBar from "../components/TopBar"
export default function EditProfile() {
const [token, setToken] = useState<string>("");
const [user, setUser] = useState<User>();
const [description, setDescription] = useState<string>("");
useEffect(() => {
const localToken = localStorage.getItem("token");
@ -88,6 +89,18 @@ export default function EditProfile() {
});
}
function editDescription(e: React.FormEvent) {
e.preventDefault();
axios
.post("/api/auth/me/setdescription", { token: token, description: description })
.then(() => {
window.location.reload();
})
.catch((err) => {
console.error(err.response.data);
});
}
if (!user) {
return (
<div className="forum-page">
@ -121,6 +134,20 @@ export default function EditProfile() {
<button onClick={editUsername} className="forum-button">Save</button>
</form>
</div>
<div className="forum-section">
<h2>Edit description</h2>
<form className="form-horizontal">
<textarea
name="description"
id="description"
placeholder={user?.description}
className="forum-input"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
<button onClick={editDescription} className="forum-button">Save</button>
</form>
</div>
<div className="forum-section">
<h2>Edit Password</h2>
<form className="form-horizontal">

View file

@ -90,9 +90,9 @@ export default function EmojisPage({socket}: {socket: WebSocket}) {
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<ul>
<div className="forum-emojis">
{emojis?.sort().filter((emoji) => emoji.name.toLowerCase().includes(search.toLowerCase())).map((emoji) => (
<li key={emoji.id}>
<div key={emoji.id} className="forum-emoji">
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emoji-fat" />
<span>:{emoji.name}:</span>
{user?.admin == 1 && (
@ -100,9 +100,9 @@ export default function EmojisPage({socket}: {socket: WebSocket}) {
Delete
</button>
)}
</li>
</div>
))}
</ul>
</div>
</div>
</div>
)

View file

@ -127,6 +127,9 @@ export default function UserPage({socket}: {socket: WebSocket}) {
<img src={`/api/users/${pageUser.username}/pfp`} alt="pfp" className="user-page-pfp" />
<h2>{pageUser.username}</h2>
</div>
<p>
{pageUser.description ? pageUser.description : "No description"}
</p>
{pageUser.admin ? <p>Admin</p> : <p>User</p>}
{pageUser.id === user?.id && (
<Link to="/edit-profile">Edit profile</Link>

View file

@ -4,6 +4,8 @@ import { User, Users } from "../types"
import axios from "axios"
import TopBar from "../components/TopBar"
import "../styles/UsersPage.css"
export default function UsersPage({socket}: {socket: WebSocket}) {
const [users, setUsers] = useState<Users>();
const [search, setSearch] = useState<string>("");
@ -84,18 +86,31 @@ export default function UsersPage({socket}: {socket: WebSocket}) {
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<ul>
<div className="forum-users">
{users?.sort().filter((channel) => channel.username.toLowerCase().includes(search.toLowerCase())).map((user) => (
<li key={user.id}>
<Link to={`/u/${user.username}`}>{user.username}</Link>
<div key={user.id} className="forum-user">
<div className="forum-user-left">
<img src={`/api/users/${user.username}/pfp`} alt="pfp" className="forum-user-pfp" />
<div className="forum-user-info">
<div className="forum-user-title">
<h3><Link to={`/u/${user.username}`}>{user.username}</Link></h3>
{user.admin == 1 && <span className="forum-user-admin">Admin</span>}
</div>
{user.description ? (
<p className="forum-user-description">{user.description}</p>
) : (
<p className="forum-user-description">No description</p>
)}
</div>
</div>
{thisUser?.admin == 1 && (
<button onClick={() => deleteUser(user.username)} className="forum-button">
Delete
</button>
)}
</li>
</div>
))}
</ul>
</div>
</div>
</div>
)

View file

@ -0,0 +1,50 @@
.forum-channels {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 10px;
}
.forum-channel {
width: 97%;
border: 1px solid #270722;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: start;
gap: 5px;
padding: 10px;
}
.forum-channel-left {
display: flex;
flex-direction: column;
align-items: start;
padding: 5px;
gap: 5px;
}
.forum-channel-title {
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
gap: 15px;
}
.forum-channel-left h3 {
margin: 0;
}
.forum-channel-left p {
margin: 0;
}
@media (prefers-color-scheme: dark) {
.forum-channel {
border: 1px solid #a678af;
}
}

View file

@ -1,4 +1,31 @@
.forum-emojis {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: start;
align-items: start;
gap: 10px;
}
.forum-emoji {
width: 150px;
border: 1px solid #270722;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 5px;
padding: 10px;
}
.emoji-fat {
max-width: 2em;
max-height: 2em;
}
max-width: 100px;
max-height: 100px;
}
@media (prefers-color-scheme: dark) {
.forum-emoji {
border: 1px solid #a678af;
}
}

View file

@ -63,10 +63,12 @@
}
.messages {
order: 2;
width: 97%;
}
.channels {
order: 1;
width: 97%;
}
}

View file

@ -0,0 +1,74 @@
.forum-users {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 10px;
}
.forum-user {
width: 97%;
border: 1px solid #270722;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: start;
gap: 5px;
padding: 10px;
}
.forum-user-left {
display: flex;
flex-direction: row;
align-items: start;
padding: 5px;
gap: 15px;
}
.forum-user-pfp {
width: 40px;
height: 40px;
border-radius: 50%;
}
.forum-user-info {
display: flex;
flex-direction: column;
justify-content: start;
align-items: start;
gap: 5px;
}
.forum-user-title {
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
gap: 15px;
}
.forum-user-admin {
background-color: #ebb8e6;
padding: 2px 5px;
}
.forum-user-title h3 {
margin: 0;
}
.forum-user-left p {
margin: 0;
}
@media (prefers-color-scheme: dark) {
.forum-user {
border: 1px solid #a678af;
}
.forum-user-admin {
background-color: #a678af;
padding: 2px 5px;
}
}

View file

@ -54,7 +54,7 @@ export type User = {
id: number
username: string
admin: number
pfp: string
description: string
}
export type Users = User[]