├── src ├── react-app-env.d.ts ├── assets │ ├── nestjs.png │ ├── reactiflux.png │ ├── typescript.png │ └── default_avatar.png ├── components │ ├── Spinner.tsx │ ├── GuildMenuItem.tsx │ └── AppBar.tsx ├── utils │ ├── helpers.ts │ ├── contexts │ │ └── GuildContext.tsx │ ├── hooks │ │ ├── useFetchUser.tsx │ │ ├── useFetchGuilds.tsx │ │ ├── useFetchGuildBans.tsx │ │ ├── useFetchGuildConfig.tsx │ │ └── useWelcomePage.tsx │ ├── types.ts │ ├── api.ts │ └── styles │ │ └── index.tsx ├── index.css ├── setupTests.ts ├── __mocks__ │ └── guilds.ts ├── reportWebVitals.ts ├── index.tsx ├── pages │ ├── LoginPage.tsx │ ├── MenuPage.tsx │ ├── CategoryPage.tsx │ ├── GuildPrefixPage.tsx │ ├── WelcomeMessagePage.tsx │ ├── GuildBansPage.tsx │ └── GuildAnalyticsPage.tsx └── App.tsx ├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/assets/nestjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/src/assets/nestjs.png -------------------------------------------------------------------------------- /src/assets/reactiflux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/src/assets/reactiflux.png -------------------------------------------------------------------------------- /src/assets/typescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/src/assets/typescript.png -------------------------------------------------------------------------------- /src/assets/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/discord-dashboard-react/HEAD/src/assets/default_avatar.png -------------------------------------------------------------------------------- /src/components/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { Overlay } from '../utils/styles'; 3 | 4 | export const Spinner: FC = ({ children }) => {children}; 5 | -------------------------------------------------------------------------------- /src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { PartialGuild } from './types'; 2 | 3 | export const getIconURL = (guild: PartialGuild) => 4 | `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`; 5 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | background-color: #292929; 4 | color: #fff; 5 | font-family: 'DM Sans'; 6 | height: 100%; 7 | } 8 | 9 | html, 10 | #root { 11 | height: 100%; 12 | } 13 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/utils/contexts/GuildContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | import { PartialGuild } from '../types'; 3 | 4 | type GuildContextType = { 5 | guild?: PartialGuild; 6 | updateGuild: (guild: PartialGuild) => void; 7 | }; 8 | 9 | export const GuildContext = createContext({ 10 | updateGuild: () => {}, 11 | }); 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/__mocks__/guilds.ts: -------------------------------------------------------------------------------- 1 | import NestJSIcon from '../assets/nestjs.png'; 2 | import ReactIcon from '../assets/reactiflux.png'; 3 | import TypescriptIcon from '../assets/typescript.png'; 4 | 5 | export const mockGuilds = [ 6 | { 7 | id: '123', 8 | name: 'NestJS', 9 | icon: NestJSIcon, 10 | }, 11 | { 12 | id: '124', 13 | name: 'React', 14 | icon: ReactIcon, 15 | }, 16 | { 17 | id: '125', 18 | name: 'Typescript', 19 | icon: TypescriptIcon, 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/components/GuildMenuItem.tsx: -------------------------------------------------------------------------------- 1 | import { getIconURL } from '../utils/helpers'; 2 | import { GuildMenuItemStyle } from '../utils/styles'; 3 | import { PartialGuild } from '../utils/types'; 4 | 5 | type Props = { 6 | guild: PartialGuild; 7 | }; 8 | 9 | export const GuildMenuItem = ({ guild }: Props) => ( 10 | 11 | {guild.name} 18 |

{guild.name}

19 |
20 | ); 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import { BrowserRouter as Router } from 'react-router-dom'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | 17 | // If you want to start measuring performance in your app, pass a function 18 | // to log results (for example: reportWebVitals(console.log)) 19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 20 | reportWebVitals(); 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/hooks/useFetchUser.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { getAuthStatus } from '../api'; 3 | import { User } from '../types'; 4 | 5 | export function useFetchUser() { 6 | const [user, setUser] = useState(); 7 | const [error, setError] = useState(); 8 | const [loading, setLoading] = useState(false); 9 | 10 | useEffect(() => { 11 | setLoading(true); 12 | getAuthStatus() 13 | .then(({ data }) => { 14 | console.log(data); 15 | setUser(data); 16 | }) 17 | .catch((err) => { 18 | console.log(err); 19 | setError(err); 20 | }) 21 | .finally(() => setTimeout(() => setLoading(false), 1000)); 22 | }, []); 23 | 24 | return { user, error, loading }; 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/hooks/useFetchGuilds.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { getMutualGuilds } from '../api'; 3 | import { PartialGuild } from '../types'; 4 | 5 | export function useFetchGuilds() { 6 | const [guilds, setGuilds] = useState(); 7 | const [loading, setLoading] = useState(false); 8 | const [error, setError] = useState(); 9 | 10 | useEffect(() => { 11 | setLoading(true); 12 | getMutualGuilds() 13 | .then(({ data }) => { 14 | setGuilds(data); 15 | }) 16 | .catch((err) => { 17 | console.log(err); 18 | setError(err); 19 | }) 20 | .finally(() => { 21 | setLoading(false); 22 | }); 23 | }, []); 24 | 25 | return { guilds, loading, error }; 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/hooks/useFetchGuildBans.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { getGuildBans } from '../api'; 3 | import { GuildBanType } from '../types'; 4 | 5 | export function useFetchGuildBans(guildId: string) { 6 | const [bans, setBans] = useState([]); 7 | const [loading, setLoading] = useState(false); 8 | const [error, setError] = useState(); 9 | const [updating, setUpdating] = useState(false); 10 | 11 | useEffect(() => { 12 | setLoading(true); 13 | getGuildBans(guildId) 14 | .then(({ data }) => { 15 | setBans(data); 16 | }) 17 | .catch((err) => { 18 | console.log(err); 19 | setError(err); 20 | }) 21 | .finally(() => setLoading(false)); 22 | }, [updating]); 23 | 24 | return { bans, loading, error, updating, setUpdating }; 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/hooks/useFetchGuildConfig.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { getGuildConfig } from '../api'; 3 | import { GuildConfigType } from '../types'; 4 | 5 | export function useGuildConfig(guildId: string) { 6 | const [config, setConfig] = useState(); 7 | const [prefix, setPrefix] = useState(''); 8 | const [loading, setLoading] = useState(false); 9 | const [error, setError] = useState(); 10 | 11 | useEffect(() => { 12 | setLoading(true); 13 | getGuildConfig(guildId) 14 | .then(({ data }) => { 15 | setConfig(data); 16 | setPrefix(data.prefix); 17 | }) 18 | .catch((err) => { 19 | console.log(err); 20 | setError(err); 21 | }) 22 | .finally(() => setTimeout(() => setLoading(false), 1000)); 23 | }, []); 24 | 25 | return { config, prefix, setPrefix, loading, error }; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/AppBar.tsx: -------------------------------------------------------------------------------- 1 | import { AppBarStyle } from '../utils/styles'; 2 | import NestJSIcon from '../assets/nestjs.png'; 3 | import { useContext } from 'react'; 4 | import { GuildContext } from '../utils/contexts/GuildContext'; 5 | import { Navigate } from 'react-router-dom'; 6 | import { getIconURL } from '../utils/helpers'; 7 | 8 | export const AppBar = () => { 9 | const { guild } = useContext(GuildContext); 10 | console.log(guild); 11 | 12 | return guild ? ( 13 | 14 |

15 | Configuring {guild.name} 16 |

17 | logo 26 |
27 | ) : ( 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /src/utils/hooks/useWelcomePage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { getGuildChannels, getGuildConfig } from '../api'; 3 | import { GuildConfigType, PartialGuildChannel } from '../types'; 4 | 5 | export function useWelcomePage(guildId: string) { 6 | const [config, setConfig] = useState(); 7 | const [channels, setChannels] = useState(); 8 | const [selectedChannel, setSelectedChannel] = useState(); 9 | const [loading, setLoading] = useState(false); 10 | useEffect(() => { 11 | setLoading(true); 12 | getGuildConfig(guildId) 13 | .then(({ data }) => { 14 | setConfig(data); 15 | setSelectedChannel(data.welcomeChannelId); 16 | return getGuildChannels(guildId); 17 | }) 18 | .then(({ data }) => setChannels(data)) 19 | .catch((err) => console.log(err)) 20 | .finally(() => setLoading(false)); 21 | }, []); 22 | 23 | return { 24 | config, 25 | channels, 26 | loading, 27 | selectedChannel, 28 | setSelectedChannel, 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/pages/LoginPage.tsx: -------------------------------------------------------------------------------- 1 | import { FaDiscord, FaQuestionCircle } from 'react-icons/fa'; 2 | import { HomePageStyle, MainButton } from '../utils/styles'; 3 | 4 | export const LoginPage = () => { 5 | const redirect = () => { 6 | window.location.href = 'http://localhost:3001/api/auth/login'; 7 | }; 8 | return ( 9 | 10 |
11 |
12 | 13 | 14 |

Login with Discord

15 |
16 | 17 | 18 |

Support Server

19 |
20 |
21 |
28 | Privacy Policy 29 | Terms of Service 30 | Contact Us 31 |
32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-dashboard-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^12.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "@types/jest": "^27.0.1", 10 | "@types/node": "^16.7.13", 11 | "@types/react": "^17.0.20", 12 | "@types/react-dom": "^17.0.9", 13 | "axios": "^0.24.0", 14 | "chart.js": "^3.7.0", 15 | "react": "^17.0.2", 16 | "react-chartjs-2": "^4.0.0", 17 | "react-dom": "^17.0.2", 18 | "react-icons": "^4.3.1", 19 | "react-router-dom": "^6.2.1", 20 | "react-scripts": "5.0.0", 21 | "react-spinners": "^0.11.0", 22 | "styled-components": "^5.3.3", 23 | "typescript": "^4.4.2", 24 | "web-vitals": "^2.1.0" 25 | }, 26 | "scripts": { 27 | "start:dev": "react-scripts start", 28 | "build": "react-scripts build", 29 | "test": "react-scripts test", 30 | "eject": "react-scripts eject" 31 | }, 32 | "eslintConfig": { 33 | "extends": [ 34 | "react-app", 35 | "react-app/jest" 36 | ] 37 | }, 38 | "browserslist": { 39 | "production": [ 40 | ">0.2%", 41 | "not dead", 42 | "not op_mini all" 43 | ], 44 | "development": [ 45 | "last 1 chrome version", 46 | "last 1 firefox version", 47 | "last 1 safari version" 48 | ] 49 | }, 50 | "devDependencies": { 51 | "@types/axios": "^0.14.0", 52 | "@types/styled-components": "^5.1.19" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/pages/MenuPage.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | import { MoonLoader } from 'react-spinners'; 4 | import { GuildMenuItem } from '../components/GuildMenuItem'; 5 | import { GuildContext } from '../utils/contexts/GuildContext'; 6 | import { useFetchGuilds } from '../utils/hooks/useFetchGuilds'; 7 | import { Container, Flex, Page } from '../utils/styles'; 8 | import { PartialGuild } from '../utils/types'; 9 | import { mockGuilds } from '../__mocks__/guilds'; 10 | 11 | export const MenuPage = () => { 12 | const navigate = useNavigate(); 13 | const { updateGuild } = useContext(GuildContext); 14 | const { guilds, loading, error } = useFetchGuilds(); 15 | 16 | const handleClick = (guild: PartialGuild) => { 17 | updateGuild(guild); 18 | navigate('/dashboard/categories'); 19 | }; 20 | 21 | return ( 22 | 23 | 24 |

Select a Server

25 |
26 | {loading ? ( 27 | 28 | 29 | 30 | ) : ( 31 |
32 | {guilds && 33 | guilds.map((guild) => ( 34 |
handleClick(guild)}> 35 | 36 |
37 | ))} 38 |
39 | )} 40 |
41 |
42 |
43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type User = { 2 | id: string; 3 | discordId: string; 4 | }; 5 | 6 | export type PartialGuild = { 7 | id: string; 8 | name: string; 9 | icon: string; 10 | owner: boolean; 11 | permissions: string; 12 | features: string[]; 13 | }; 14 | 15 | export type GuildConfigType = { 16 | id: number; 17 | guildId: string; 18 | prefix: string; 19 | welcomeChannelId: string; 20 | }; 21 | 22 | export type PartialGuildChannel = { 23 | id: string; 24 | last_message_id: string; 25 | type: number; 26 | name: string; 27 | position: number; 28 | parent_id?: string; 29 | topic?: string; 30 | guild_id: string; 31 | permission_overwrites: string[]; 32 | nsfw: boolean; 33 | rate_limit_per_user: string; 34 | banner?: string; 35 | }; 36 | 37 | export type ModerationActionType = 'ban' | 'kick' | 'timeout'; 38 | 39 | export type GuildModLogType = { 40 | id: number; 41 | guildId: string; 42 | memberId: string; 43 | issuedBy: string; 44 | issuedOn: Date; 45 | reason?: string; 46 | type: ModerationActionType; 47 | }; 48 | 49 | export type DiscordUserType = { 50 | id: string; 51 | username: string; 52 | discriminator: string; 53 | avatar?: string; 54 | bot?: boolean; 55 | system?: boolean; 56 | mfa_enabled?: boolean; 57 | banner?: string; 58 | accent_color?: number; 59 | locale?: string; 60 | verified?: boolean; 61 | email?: string; 62 | flags?: number; 63 | premium_type?: number; 64 | public_flags?: number; 65 | }; 66 | 67 | export type GuildBanType = { 68 | reason?: string; 69 | user: DiscordUserType; 70 | }; 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord Bot Dashboard (2022) 2 | 3 | This is a Discord Bot Dashboard written in React. You might also want to take a look at the API and Discord Bot, the repositories can be found here: 4 | 5 | - [Dashboard NestJS API](https://github.com/stuyy/discord-dashboard-api) 6 | - [Discord Bot](https://github.com/stuyy/discord-dashboard-bot) 7 | 8 | Please do note all code provided in these projects are NOT for production use. They are only for eductional purposes only. These repositories are only a supplement to the new Discord Bot Dashboard Tutorial Series on my YouTube channel, which you can find [here](https://youtube.com/ansonthedeveloper). You can find the playlist with all episodes [here](https://www.youtube.com/playlist?list=PL_cUvD4qzbkyX4Wp8TAfjpttjUldDWJnp). 9 | 10 | For any further questions, you may raise an issue or contact me on Discord here: https://discord.gg/3S68xJnqZt 11 | 12 | # Instructions 13 | 14 | To setup this project, all you need to do is clone the repository. 15 | 16 | 1. Clone using SSH or HTTPS 17 | 18 | `git clone git@github.com:stuyy/discord-dashboard-react.git` 19 | 20 | The package manager I used for this project, and use generally, is `yarn`. You may use `npm` if you wish however if you run into any issues with installing dependencies or building, consider switching to `yarn`. 21 | 22 | 2. `cd` into the cloned project, and then install dependencies using `npm install` or `yarn install`. 23 | 24 | 3. Run `npm start` or `yarn start`, this should start the project on `http://localhost:3000`. 25 | 26 | In order to have the app fully working, you must have the NestJS API setup locally as well. Please visit the [Dashboard NestJS API](https://github.com/stuyy/discord-dashboard-api) project repository and go through the README for instructions on how to setup the Nest API for the dashboard. -------------------------------------------------------------------------------- /src/utils/api.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig } from 'axios'; 2 | import { 3 | GuildModLogType, 4 | GuildConfigType, 5 | PartialGuild, 6 | User, 7 | GuildBanType, 8 | } from './types'; 9 | 10 | const CONFIG: AxiosRequestConfig = { withCredentials: true }; 11 | const API_URL = 'http://localhost:3001/api'; 12 | 13 | export const getAuthStatus = () => 14 | axios.get(`${API_URL}/auth/status`, CONFIG); 15 | 16 | export const getMutualGuilds = () => 17 | axios.get(`${API_URL}/discord/guilds`, CONFIG); 18 | 19 | export const getGuildConfig = (guildId: string) => 20 | axios.get(`${API_URL}/guilds/config/${guildId}`, CONFIG); 21 | 22 | export const updateGuildPrefix = (guildId: string, prefix: string) => 23 | axios.post( 24 | `${API_URL}/guilds/${guildId}/config/prefix`, 25 | { 26 | prefix, 27 | }, 28 | CONFIG 29 | ); 30 | 31 | export const getGuildChannels = (guildId: string) => 32 | axios.get(`${API_URL}/discord/guilds/${guildId}/channels`, CONFIG); 33 | 34 | export const updateWelcomeChannelId = (guildId: string, channelId: string) => 35 | axios.post( 36 | `${API_URL}/guilds/${guildId}/config/welcome`, 37 | { 38 | channelId, 39 | }, 40 | CONFIG 41 | ); 42 | 43 | export const getGuildBanLogs = (guildId: string, fromDate: string) => 44 | axios.get( 45 | `${API_URL}/guilds/${guildId}/bans?fromDate=${fromDate}`, 46 | CONFIG 47 | ); 48 | 49 | export const getGuildModLogs = (guildId: string, fromDate: string) => 50 | axios.get( 51 | `${API_URL}/guilds/${guildId}/logs?fromDate=${fromDate}`, 52 | CONFIG 53 | ); 54 | 55 | export const getGuildBans = (guildId: string) => 56 | axios.get( 57 | `${API_URL}/discord/guilds/${guildId}/bans`, 58 | CONFIG 59 | ); 60 | 61 | export const deleteGuildBan = (guildId: string, userId: string) => 62 | axios.delete(`${API_URL}/discord/guilds/${guildId}/bans/${userId}`, CONFIG); 63 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | 28 | 29 | 33 | React App 34 | 35 | 36 | 37 |
38 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/pages/CategoryPage.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { GuildContext } from '../utils/contexts/GuildContext'; 3 | import { 4 | Container, 5 | Flex, 6 | Grid, 7 | Page, 8 | TextButton, 9 | Title, 10 | } from '../utils/styles'; 11 | import { 12 | IoSettingsOutline, 13 | IoNewspaperOutline, 14 | IoInformationCircleOutline, 15 | } from 'react-icons/io5'; 16 | import { useNavigate } from 'react-router-dom'; 17 | 18 | export const CategoryPage = () => { 19 | const { guild } = useContext(GuildContext); 20 | const navigate = useNavigate(); 21 | 22 | return ( 23 | 24 | 25 |
26 | 27 | Guild Information 28 | 29 | 30 | 31 | navigate('/dashboard/analytics')}> 32 | Analytics 33 | 34 | navigate('/dashboard/bans')}> 35 | Guild Bans 36 | 37 | 38 |
39 |
40 | 41 | Basic Configurations 42 | 43 | 44 | 45 | navigate('/dashboard/prefix')}> 46 | Command Prefix 47 | 48 | navigate('/dashboard/message')}> 49 | Welcome Message 50 | 51 | 52 |
53 |
54 | 55 | Channel Logs 56 | 57 | 58 | 59 | Moderation Logs 60 | Bot Logs 61 | 62 |
63 |
64 |
65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /src/pages/GuildPrefixPage.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { MoonLoader } from 'react-spinners'; 3 | import { updateGuildPrefix } from '../utils/api'; 4 | import { GuildContext } from '../utils/contexts/GuildContext'; 5 | import { useGuildConfig } from '../utils/hooks/useFetchGuildConfig'; 6 | import { 7 | Button, 8 | Container, 9 | Flex, 10 | InputField, 11 | Page, 12 | Title, 13 | } from '../utils/styles'; 14 | 15 | export const GuildPrefixPage = () => { 16 | const { guild } = useContext(GuildContext); 17 | const guildId = (guild && guild.id) || ''; 18 | const { config, loading, error, prefix, setPrefix } = useGuildConfig(guildId); 19 | 20 | const savePrefix = async ( 21 | e: React.MouseEvent 22 | ) => { 23 | e.preventDefault(); 24 | console.log(prefix); 25 | try { 26 | const res = await updateGuildPrefix(guildId, prefix); 27 | console.log(res); 28 | } catch (err) { 29 | console.log(err); 30 | } 31 | }; 32 | 33 | return ( 34 | 35 | 36 | {!loading && config ? ( 37 | <> 38 | Update Command Prefix 39 |
40 |
41 | 42 |
43 | setPrefix(e.target.value)} 48 | /> 49 | 50 | 59 | 62 | 63 | 64 | 65 | ) : ( 66 | 67 | 68 | 69 | )} 70 |
71 |
72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Routes, Route } from 'react-router-dom'; 3 | import { BarLoader } from 'react-spinners'; 4 | import { AppBar } from './components/AppBar'; 5 | import { Spinner } from './components/Spinner'; 6 | import { CategoryPage } from './pages/CategoryPage'; 7 | import { GuildAnalyticsPage } from './pages/GuildAnalyticsPage'; 8 | import { GuildBansPage } from './pages/GuildBansPage'; 9 | import { GuildPrefixPage } from './pages/GuildPrefixPage'; 10 | import { LoginPage } from './pages/LoginPage'; 11 | import { MenuPage } from './pages/MenuPage'; 12 | import { WelcomeMessagePage } from './pages/WelcomeMessagePage'; 13 | import { GuildContext } from './utils/contexts/GuildContext'; 14 | import { useFetchUser } from './utils/hooks/useFetchUser'; 15 | import { PartialGuild } from './utils/types'; 16 | 17 | function App() { 18 | const [guild, setGuild] = useState(); 19 | const { user, loading, error } = useFetchUser(); 20 | 21 | const updateGuild = (guild: PartialGuild) => setGuild(guild); 22 | 23 | if (loading) return } />; 24 | 25 | return ( 26 | 27 | {user && !error ? ( 28 | <> 29 | 30 | } /> 31 | 32 | 33 | } /> 34 | } /> 35 | {/* } /> */} 36 | } /> 37 | } /> 38 | } /> 39 | } 42 | /> 43 | } /> 44 | 45 | 46 | ) : ( 47 | 48 | } /> 49 | Not Found} /> 50 | 51 | )} 52 | 53 | ); 54 | } 55 | 56 | export default App; 57 | -------------------------------------------------------------------------------- /src/pages/WelcomeMessagePage.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { MoonLoader } from 'react-spinners'; 3 | import { updateWelcomeChannelId } from '../utils/api'; 4 | import { GuildContext } from '../utils/contexts/GuildContext'; 5 | import { useWelcomePage } from '../utils/hooks/useWelcomePage'; 6 | import { 7 | Button, 8 | Container, 9 | Flex, 10 | Page, 11 | Select, 12 | TextArea, 13 | Title, 14 | } from '../utils/styles'; 15 | 16 | export const WelcomeMessagePage = () => { 17 | const { guild } = useContext(GuildContext); 18 | const guildId = (guild && guild.id) || ''; 19 | const { config, channels, selectedChannel, setSelectedChannel, loading } = 20 | useWelcomePage(guildId); 21 | 22 | const updateWelcomeChannel = async () => { 23 | console.log(selectedChannel); 24 | try { 25 | await updateWelcomeChannelId(guildId, selectedChannel || ''); 26 | } catch (err) { 27 | console.log(err); 28 | } 29 | }; 30 | 31 | return ( 32 | 33 | 34 | Update Welcome Message 35 | {channels && config && !loading ? ( 36 |
37 |
38 |
39 | 40 |
41 | 55 |
56 |
57 |
58 | 59 |
60 |