diff --git a/web/src/App.tsx b/web/src/App.tsx
index 2180b24..449373d 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -5,7 +5,7 @@ import '@revoltchat/ui/src/styles/dark.css';
import '@revoltchat/ui/src/styles/common.css';
import RequireAuth from './components/RequireAuth';
import DashboardHome from './pages/DashboardHome';
-import ServerDashboard from './pages/ServerDashboard';
+import ServerDashboard from './pages/ServerDashboard/ServerDashboard';
const API_URL = import.meta.env.VITE_API_URL?.toString()
|| 'http://localhost:9000';
diff --git a/web/src/components/CategorySelector.tsx b/web/src/components/CategorySelector.tsx
new file mode 100644
index 0000000..c180fbc
--- /dev/null
+++ b/web/src/components/CategorySelector.tsx
@@ -0,0 +1,20 @@
+import { FunctionComponent, useState } from "react";
+import './styles/CategorySelector.css';
+
+const CategorySelector: FunctionComponent<{ keys: { id: string, name: string }[], selected: string, onChange: (key: string) => void }> = (props) => {
+ return (
+
+ {props.keys.map((k) => (
+
props.onChange(k.id)}
+ >
+ {k.name}
+
+ ))}
+
+ );
+}
+
+export default CategorySelector;
diff --git a/web/src/components/styles/CategorySelector.css b/web/src/components/styles/CategorySelector.css
new file mode 100644
index 0000000..bde659f
--- /dev/null
+++ b/web/src/components/styles/CategorySelector.css
@@ -0,0 +1,40 @@
+.category-selector-outer {
+ width: calc(100% - 20px);
+ margin: 8px 10px;
+ height: 32px;
+ background-color: var(--secondary-background);
+ display: flex;
+ border-radius: 6px;
+ justify-content: space-evenly;
+}
+
+.category-selector-inner {
+ background-color: var(--tertiary-background);
+ height: 24px;
+ margin: 4px;
+ width: 100vw;
+ user-select: none;
+ text-align: center;
+ overflow: hidden;
+ border-radius: 4px;
+ transition: filter .2s, background-color .3s;
+}
+
+.category-selector-inner:hover {
+ filter: brightness(1.1);
+}
+
+.category-selector-inner:active {
+ filter: brightness(1.2);
+}
+
+.category-selector-inner.selected {
+ background-color: var(--accent);
+}
+
+.category-selector-inner span {
+ color: var(--secondary-foreground);
+}
+.category-selector-inner.selected span {
+ color: var(--foreground);
+}
diff --git a/web/src/pages/ServerDashboard.tsx b/web/src/pages/ServerDashboard/ServerDashboard.tsx
similarity index 69%
rename from web/src/pages/ServerDashboard.tsx
rename to web/src/pages/ServerDashboard/ServerDashboard.tsx
index 3ac1019..38b63cb 100644
--- a/web/src/pages/ServerDashboard.tsx
+++ b/web/src/pages/ServerDashboard/ServerDashboard.tsx
@@ -10,10 +10,11 @@ import { H3 } from '@revoltchat/ui/lib/components/atoms/heading/H3';
import { H4 } from '@revoltchat/ui/lib/components/atoms/heading/H4';
import { Icon } from '@mdi/react';
import { mdiChevronLeft, mdiCloseBox } from '@mdi/js';
-import { API_URL } from "../App";
-import { getAuthHeaders } from "../utils";
+import { API_URL } from "../../App";
+import { getAuthHeaders } from "../../utils";
import { Link, useParams } from "react-router-dom";
-import defaultChannelIcon from '../assets/channel-default-icon.svg';
+import defaultChannelIcon from '../../assets/channel-default-icon.svg';
+import CategorySelector from '../../components/CategorySelector';
type User = { id: string, username?: string, avatarURL?: string }
type Channel = { id: string, name: string, icon?: string, type: 'VOICE'|'TEXT', nsfw: boolean }
@@ -40,6 +41,8 @@ type AntispamRule = {
}
const ServerDashboard: FunctionComponent = () => {
+ const [category, setCategory] = useState('home');
+
const [serverInfo, setServerInfo] = useState({} as Server);
const [status, setStatus] = useState('');
@@ -77,7 +80,7 @@ const ServerDashboard: FunctionComponent = () => {
try {
const res = await axios.get(`${API_URL}/dash/server/${serverid}`, { headers: await getAuthHeaders() });
console.log(res.data);
-
+
const server: Server = res.data.server;
setServerInfo(server);
@@ -108,131 +111,179 @@ const ServerDashboard: FunctionComponent = () => {
return (
<>
-
-
-
- Back
-
-
- {serverInfo?.name ?? 'Loading...'}
- {status.length ? {status} : }
+ {status.length ? {status} : <>>}
+
+
+
+
+ Back
+
+
+
+ {serverInfo?.name ?? 'Loading...'}
+
+
+ •
+
+
+ {serverInfo.description || No server description set }
+
+
+
+
+
-
{serverInfo.description ?? No server description set }
-
- <>
-
Prefix
-
{
- setPrefix(e.currentTarget.value);
- setChanged({ ...changed, prefix: true });
- }}
- />
- {
- setPrefixAllowSpace(!prefixAllowSpace);
- setChanged({ ...changed, prefixAllowSpace: true });
- }}
- title="Allow space after prefix"
- description={'Whether the bot recognizes a command if the prefix is followed by a space. Enable if your prefix is a word.'}
- />
- Save
- >
-
+ {category == 'home' && (
+ <>
+ <>
+ Prefix
+ {
+ setPrefix(e.currentTarget.value);
+ setChanged({ ...changed, prefix: true });
+ }}
+ />
+ {
+ setPrefixAllowSpace(!prefixAllowSpace);
+ setChanged({ ...changed, prefixAllowSpace: true });
+ }}
+ title="Allow space after prefix"
+ description={'Whether the bot recognizes a command if the prefix is followed by a space. Enable if your prefix is a word.'}
+ />
+ Save
+ >
- <>
- Bot Managers
-
- Only users with "Manage Server" permission are allowed to add/remove other
- bot managers and are automatically considered bot manager.
-
-
-
- {botManagers.map((uid: string) => {
- const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
- return (
-
- )})}
-
-
-
+
- Moderators
-
- Only bot managers are allowed to add/remove moderators.
- All bot managers are also moderators.
-
-
-
- {moderators.map((uid: string) => {
- const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
- return (
-
- )})}
-
-
-
- >
+ <>
+ Bot Managers
+
+ Only users with "Manage Server" permission are allowed to add/remove other
+ bot managers and are automatically considered bot manager.
+
+
+
+ {botManagers.map((uid: string) => {
+ const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
+ return (
+
+ )})}
+
+
+
-
+ Moderators
+
+ Only bot managers are allowed to add/remove moderators.
+ All bot managers are also moderators.
+
+
+
+ {moderators.map((uid: string) => {
+ const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
+ return (
+
+ )})}
+
+
+
+ >
+ >
+ )}
- <>
- Antispam Rules
- {serverInfo.perms != null && automodSettings && (
- serverInfo.perms > 0
- ? (
- <>
- {automodSettings.antispam.map(r => )}
- {
- const newRule: AntispamRule = {
- action: 0,
- max_msg: 5,
- timeframe: 3,
- message: null,
- id: '',
- channels: [],
- }
+ {category == 'automod' && (
+ <>
+ Antispam Rules
+ {serverInfo.perms != null && automodSettings && (
+ serverInfo.perms > 0
+ ? (
+ <>
+ {automodSettings.antispam.map((r, i) => (
+ <>
+
+ {i < automodSettings.antispam.length - 1 && }
+ >
+ ))}
+ {
+ const newRule: AntispamRule = {
+ action: 0,
+ max_msg: 5,
+ timeframe: 3,
+ message: null,
+ id: '',
+ channels: [],
+ }
- const res = await axios.post(
- `${API_URL}/dash/server/${serverid}/automod`,
- {
- action: newRule.action,
- max_msg: newRule.max_msg,
- timeframe: newRule.timeframe,
- },
- { headers: await getAuthHeaders() }
- );
+ const res = await axios.post(
+ `${API_URL}/dash/server/${serverid}/automod`,
+ {
+ action: newRule.action,
+ max_msg: newRule.max_msg,
+ timeframe: newRule.timeframe,
+ },
+ { headers: await getAuthHeaders() }
+ );
- newRule.id = res.data.id;
+ newRule.id = res.data.id;
- setAutomodSettings({ antispam: [ ...(automodSettings.antispam), newRule ] });
- }}>
- Create Rule
-
- >
+ setAutomodSettings({ antispam: [ ...(automodSettings.antispam), newRule ] });
+ }}>
+ Create Rule
+
+ >
+ )
+ : (
+
+
+ You do not have access to this.
+
+
+ )
)
- : (
-
-
- You do not have access to this.
-
-
- )
- )
- }
- >
+ }
+ >
+ )}
>
@@ -296,7 +347,7 @@ const ServerDashboard: FunctionComponent = () => {
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}/${props.user.id}`,
{ headers: await getAuthHeaders() }
);
-
+
if (props.type == 'MANAGER') {
setBotManagers(res.data.managers);
}
@@ -308,7 +359,7 @@ const ServerDashboard: FunctionComponent = () => {
);
}
-
+
function UserListContainer(props: { disabled: boolean, children: any }) {
return (
{
.find(c => c.name == content)
|| serverInfo.channels // Prefer channel with same capitalization,
.find(c => c.name.toLowerCase() == content.toLowerCase()); // otherwise search case insensitive
-
+
if (channel && channel.type == 'TEXT') {
props.onInput(channel);
setContent('');
@@ -613,7 +664,6 @@ const ServerDashboard: FunctionComponent = () => {
{props.rule.id}
-
)