├── .env ├── .env.development ├── .env.production ├── .env.staging ├── .eslintrc.js ├── .github ├── FUNDING.yml └── workflows │ ├── deploy.yml │ └── test-build.yml ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── Dockerfile ├── LICENSE ├── README.md ├── apis └── api-helper.ts ├── axios-config.ts ├── changelogs.tsx ├── components ├── animated-div-list.tsx ├── bans-list-panel.tsx ├── basic-panel.tsx ├── block-button.tsx ├── block-input.tsx ├── block-text-area.tsx ├── blog-comment.tsx ├── blog-create-comment.tsx ├── blog-h1.tsx ├── blog-h2.tsx ├── blog-markdown.tsx ├── blog-post-showcase-panel.tsx ├── boost-panel.tsx ├── box-panel.tsx ├── button-cluster.tsx ├── chart-panel.tsx ├── checkbox-content-panel.tsx ├── debug-menu.tsx ├── desktop │ ├── desktop-nav-bar.tsx │ ├── tab-bar.tsx │ └── tab-button.tsx ├── dropdown-panel.tsx ├── empty-panel.tsx ├── export-checklist-item.tsx ├── fallback-image.tsx ├── guide-showcase-panel.tsx ├── guild-list.tsx ├── head-set.tsx ├── header-content │ ├── header-blog-list.tsx │ ├── header-blog-post.tsx │ ├── header-changelogs.tsx │ ├── header-edit-blog-post.tsx │ ├── header-home.tsx │ ├── header-index.tsx │ ├── header-premium.tsx │ ├── header-server-leaderboard.tsx │ ├── header-server-list.tsx │ ├── header-server.tsx │ ├── header-status.tsx │ └── header-user-profile.tsx ├── header-navigation-animator.tsx ├── header.tsx ├── ignore-panel.tsx ├── inbox-item.tsx ├── inbox-popout.tsx ├── incident-panel.tsx ├── index-emp-bg.tsx ├── index-panel.tsx ├── input-error-label.tsx ├── input-label.tsx ├── leaderboard-panel.tsx ├── leaderboard-podium-panel.tsx ├── levelrole-panel.tsx ├── loading.tsx ├── log-panel.tsx ├── mobile │ └── mobile-nav-bar.tsx ├── modal.tsx ├── module-panel.tsx ├── multiline-input.tsx ├── page-navigation-animator.tsx ├── page-title.tsx ├── panel-input.tsx ├── ranking-card.tsx ├── recently-panel.tsx ├── select.tsx ├── svg │ ├── arrows.tsx │ ├── logos.tsx │ └── waves.tsx ├── toast-item.tsx ├── tooltip.tsx ├── value-panel.tsx └── wave-page.tsx ├── config.json ├── context ├── blog-comments-section.tsx ├── guild-details-context.tsx ├── layout-context.tsx ├── socket-manager.tsx ├── toast-context.tsx ├── user-context.tsx └── user-details-context.tsx ├── custom-tailwind └── tailwind-ios-selector.js ├── default-models ├── xp-modules.ts └── xp-values.ts ├── hooks ├── use-access-restriction.tsx ├── use-breakpoints.ts ├── use-local-storage.tsx ├── use-media-query.tsx ├── use-window-size.ts └── useNavigationLock.ts ├── index.d.ts ├── models ├── api-models.ts ├── backend │ ├── blog-models.ts │ ├── discord-models.ts │ ├── ilum-models.ts │ ├── inbox-interfaces.ts │ ├── incident-models.ts │ ├── socket-models.ts │ └── xp-models.ts └── page.ts ├── next.config.js ├── package-lock.json ├── package.json ├── page-tabs ├── server-tabs │ ├── ignore-parts │ │ ├── ignored-category.tsx │ │ ├── ignored-roles.tsx │ │ ├── ignored-text.tsx │ │ └── ignored-voice.tsx │ ├── server-announcements.tsx │ ├── server-boosts.tsx │ ├── server-ignores.tsx │ ├── server-loggers.tsx │ ├── server-logs.tsx │ ├── server-modules.tsx │ ├── server-page-layout.tsx │ ├── server-roles.tsx │ ├── server-routes.tsx │ ├── server-settings.tsx │ ├── server-template.tsx │ └── server-values.tsx └── user-tabs │ ├── user-bans.tsx │ ├── user-incidents.tsx │ ├── user-manage-premium.tsx │ ├── user-page-layout.tsx │ ├── user-ranking-card.tsx │ ├── user-routes.tsx │ ├── user-settings.tsx │ └── user-test-messaging.tsx ├── pages ├── 404.tsx ├── 503.tsx ├── _app.tsx ├── _document.tsx ├── blog │ ├── [blogID].tsx │ ├── editor │ │ └── [[...postID]].tsx │ └── index.tsx ├── changes.tsx ├── home.tsx ├── index.tsx ├── lb │ └── [serverid].tsx ├── legal │ └── privacy.tsx ├── me │ └── [[...tab]].tsx ├── premium.tsx ├── servers │ ├── [serverid] │ │ └── [[...tab]].tsx │ └── index.tsx └── status.tsx ├── postcss.config.js ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon-114x114.png ├── apple-touch-icon-120x120.png ├── apple-touch-icon-144x144.png ├── apple-touch-icon-152x152.png ├── apple-touch-icon-180x180.png ├── apple-touch-icon-57x57.png ├── apple-touch-icon-60x60.png ├── apple-touch-icon-72x72.png ├── apple-touch-icon-76x76.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── fonts │ ├── Montserrat.ttf │ ├── Roboto-Bold.ttf │ ├── Roboto-Light.ttf │ ├── Roboto.ttf │ └── Whitney-Semibold.ttf ├── images │ ├── discord-mark-white.svg │ ├── fallback.gif │ ├── fallback.png │ ├── header.jpg │ └── wallpaperflare.com_wallpaper.jpg ├── mstile-150x150.png ├── safari-pinned-tab.svg ├── scripts │ └── ga.js └── site.webmanifest ├── styles ├── checkmark.scss ├── globals.scss ├── markdown.scss └── xp-loading.scss ├── tailwind.config.js ├── tsconfig.json └── utils ├── animation-utils.ts ├── default-background.ts ├── discord-utils.ts ├── download-blob.ts ├── flags ├── client.ts ├── config.ts ├── edge.ts └── server.ts ├── image-utils.ts ├── is-id-developer.ts ├── object-utils.ts ├── remove-urls.ts ├── text-utils.ts ├── theme-utils.ts ├── time-utils.ts ├── url-to-html-a.ts ├── url-utils.ts └── xp-variable-images-utils.ts /.env: -------------------------------------------------------------------------------- 1 | ILUM_DOMAIN=https://ilumv2.xp-bot.net 2 | DEVELOPERS=265849018662387712,524860237979582464 -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # NEXT_PUBLIC_BACKEND_DOMAIN=https://api.xp-bot.net 2 | NEXT_PUBLIC_BACKEND_DOMAIN=http://localhost:3300 3 | PUBLIC_HAPPYKIT_API=flags_pub_development_349193885386277458 -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_BACKEND_DOMAIN=https://api.xp-bot.net 2 | PUBLIC_HAPPYKIT_API=flags_pub_349193885386277458 -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | # NEXT_PUBLIC_BACKEND_DOMAIN=https://beta.xp-bot.net 2 | NEXT_PUBLIC_BACKEND_DOMAIN=https://api.beta.xp-bot.net 3 | PUBLIC_HAPPYKIT_API=flags_pub_development_349193885386277458 -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ignorePatterns: ["*.config.*", "*.d.ts", "*.js"], 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | plugins: [ 9 | "@typescript-eslint", 10 | "simple-import-sort", 11 | "unused-imports", 12 | "lodash", 13 | "check-file", 14 | ], 15 | extends: [ 16 | "eslint:recommended", 17 | "next", 18 | "next/core-web-vitals", 19 | "plugin:@typescript-eslint/recommended", 20 | "prettier", 21 | "plugin:lodash/recommended", 22 | ], 23 | rules: { 24 | "@typescript-eslint/no-empty-interface": "off", 25 | "react-hooks/exhaustive-deps": "off", 26 | "lodash/chaining": "off", 27 | "import/no-anonymous-default-export": "off", 28 | "check-file/filename-naming-convention": [ 29 | "error", 30 | { 31 | "*": "KEBAB_CASE", 32 | }, 33 | ], 34 | "check-file/folder-naming-convention": [ 35 | "error", 36 | { 37 | "*": "KEBAB_CASE", 38 | }, 39 | ], 40 | "lodash/import-scope": ["error", "member"], 41 | "lodash/matches-prop-shorthand": "off", 42 | "lodash/matches-shorthand": "off", 43 | "lodash/chain-style": [2, "explicit"], 44 | "max-depth": ["error", 3], 45 | "no-unused-vars": "off", 46 | "no-console": "warn", 47 | "@typescript-eslint/explicit-module-boundary-types": "off", 48 | "react/no-unescaped-entities": "off", 49 | 50 | "react/display-name": "off", 51 | "react/jsx-curly-brace-presence": [ 52 | "warn", 53 | { props: "never", children: "never" }, 54 | ], 55 | 56 | //#region //*=========== Unused Import =========== 57 | "@typescript-eslint/no-unused-vars": "off", 58 | "unused-imports/no-unused-imports": "warn", 59 | "unused-imports/no-unused-vars": [ 60 | "warn", 61 | { 62 | vars: "all", 63 | varsIgnorePattern: "^_", 64 | args: "after-used", 65 | argsIgnorePattern: "^_", 66 | }, 67 | ], 68 | //#endregion //*======== Unused Import =========== 69 | 70 | //#region //*=========== Import Sort =========== 71 | "simple-import-sort/exports": "warn", 72 | "simple-import-sort/imports": [ 73 | "warn", 74 | { 75 | groups: [ 76 | // ext library & side effect imports 77 | ["^@?\\w", "^\\u0000"], 78 | // {s}css files 79 | ["^.+\\.s?css$"], 80 | // Lib and hooks 81 | ["^@/lib", "^@/hooks"], 82 | // static data 83 | ["^@/data"], 84 | // components 85 | ["^@/components", "^@/container"], 86 | // zustand store 87 | ["^@/store"], 88 | // Other imports 89 | ["^@/"], 90 | // relative paths up until 3 level 91 | [ 92 | "^\\./?$", 93 | "^\\.(?!/?$)", 94 | "^\\.\\./?$", 95 | "^\\.\\.(?!/?$)", 96 | "^\\.\\./\\.\\./?$", 97 | "^\\.\\./\\.\\.(?!/?$)", 98 | "^\\.\\./\\.\\./\\.\\./?$", 99 | "^\\.\\./\\.\\./\\.\\.(?!/?$)", 100 | ], 101 | ["^@/types"], 102 | // other that didnt fit in 103 | ["^"], 104 | ], 105 | }, 106 | ], 107 | //#endregion //*======== Import Sort =========== 108 | }, 109 | globals: { 110 | React: true, 111 | JSX: true, 112 | }, 113 | }; 114 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: xpbot 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Live Docker Image 2 | 3 | on: 4 | push: 5 | branches: 6 | - production 7 | workflow_dispatch: 8 | 9 | jobs: 10 | deploy: 11 | name: Deployment 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Bumping version 18 | uses: jpb06/bump-package@latest 19 | with: 20 | major-keywords: BREAKING CHANGE 21 | minor-keywords: major 22 | patch-keywords: feat,fix,qol,refactor,perf,docs,style,ci,chore,test 23 | 24 | - name: get-npm-version 25 | id: package-version 26 | uses: martinbeentjes/npm-get-version-action@v1.3.1 27 | 28 | - uses: actions/setup-node@v3 29 | with: 30 | node-version: 18 31 | 32 | - name: Build and push the Docker image 33 | uses: mr-smithers-excellent/docker-build-push@v5 34 | with: 35 | image: xp-dashboard 36 | tags: v${{ steps.package-version.outputs.current-version}}, latest 37 | # tags: latest 38 | registry: ghcr.io 39 | username: ${{ github.actor }} 40 | password: ${{ secrets.GITHUB_TOKEN }} 41 | -------------------------------------------------------------------------------- /.github/workflows/test-build.yml: -------------------------------------------------------------------------------- 1 | name: Test Build on PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - development 7 | - production 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.x, 18.x, 20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm ci 24 | - run: npm run build 25 | -------------------------------------------------------------------------------- /.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 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | *.code-workspace 39 | /.vscode/launch.json 40 | 41 | .idea 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | // "source.organizeImports": true, 4 | "source.fixAll.eslint": true, 5 | "source.addMissingImports": true, 6 | }, 7 | "editor.formatOnSave": true, 8 | "eslint.validate": [ 9 | "javascript" 10 | ] 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "dev", 7 | "problemMatcher": [], 8 | "label": "npm: dev", 9 | "detail": "next dev" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20.5.0 2 | LABEL org.opencontainers.image.description The official docker image of the XP website and dashboard. Built with TypeScript, NextJS and TailwindCSS. 3 | 4 | WORKDIR /app 5 | COPY . . 6 | 7 | RUN npm ci 8 | RUN npm run build 9 | 10 | CMD ["npm", "run", "start"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 namespace.media 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XP-Dashboard 2 | The official XP website and dashboard. 3 | Built with TypeScript, NextJS and TailwindCSS. 4 | 5 | ### Why contribute? 6 | Every contributor earns a brand new badge called the Contributor Badge, which will be displayed on your Ranking Card and on our official Discord community. 7 | Additionally, third-party contributions help us maintain this project as we work on XP in our freetime without any commercial profit. 8 | We're also looking to publish more projects as open-source in the future. 9 | Thank you for contributing to XP! 10 | 11 | # Contribute 12 | For testing, you only need to set up this codebase. By default, all requests will go directly to our official backend server, so you can test your changes without any hassle and everything would work precisely as it would on a production level. 13 | 14 | 1. [Fork](https://github.com/xp-bot/dashboard/fork) the **develop** branch. 15 | 2. Push your changes as commits on your fork. 16 | 3. [Create a Pull Request](https://github.com/xp-bot/dashboard/compare). Make sure to use a descriptive title and detailed description. 17 | > Please avoid submitting large pull requests with multiple changes as they will be categorically rejected. Always create one pull request per change. 18 | 19 | ## How to Set Up the Repository 20 | 21 | To set up the repository, follow these steps: 22 | 23 | 1. Install Node.js and NPM: 24 | Make sure you have Node.js 16+ and NPM 8+ installed on your machine. You can download and install them from the official Node.js website. 25 | 26 | 2. Pull the Project from GitHub: 27 | Clone or download the project from the GitHub repository to your local machine. 28 | 29 | 3. Install Required Packages: 30 | Open your terminal or command prompt and navigate to the project directory. Run the command `npm ci` to install all required packages for the project. 31 | 32 | 4. Start the Dashboard: 33 | After the packages are installed, you can start the dashboard by running the appropriate command in your terminal: 34 | 35 | - To boot up the development environment, run the command `npm run dev`. 36 | - To boot up the production environment, run the command `npm run prod`. 37 | 38 | That's it! 39 | You can now access the dashboard locally by opening it in a web browser. For the development environment, it runs on port 3000, and for the production environment, it runs on port 3838. 40 | 41 | 42 | # License 43 | This project is covered by the MIT License. [Read more](./LICENSE) 44 | -------------------------------------------------------------------------------- /axios-config.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const baseURL = `${process.env.BACKEND_DOMAIN}/`; 4 | const baseIlumURL = `${process.env.ILUM_DOMAIN}/`; 5 | 6 | const axiosApp = axios.create({ 7 | baseURL, 8 | withCredentials: true, 9 | }); 10 | const axiosIlumApp = axios.create({ 11 | baseURL: baseIlumURL, 12 | }); 13 | 14 | export default axiosApp; 15 | 16 | export { axiosIlumApp }; 17 | -------------------------------------------------------------------------------- /components/animated-div-list.tsx: -------------------------------------------------------------------------------- 1 | import { AnimatePresence, motion } from "framer-motion"; 2 | import { filter, isEqual, map, size } from "lodash"; 3 | import { FC, ReactNode } from "react"; 4 | import { MotionListProps } from "utils/animation-utils"; 5 | 6 | import EmptyPanel from "./empty-panel"; 7 | 8 | interface IAnimatedDivList { 9 | children: { element: ReactNode | JSX.Element; key: string }[]; 10 | emptyMessage: string; 11 | disableSeparators?: boolean; 12 | } 13 | 14 | const AnimatedDivList: FC = ({ 15 | children, 16 | emptyMessage, 17 | disableSeparators, 18 | }) => { 19 | const list = filter(children, (child) => !isEqual(child.key, ``)); 20 | return ( 21 | 22 | {size(list) > 0 ? ( 23 | map(list, (element, idx) => { 24 | return ( 25 | 31 | {element.element} 32 | {!isEqual(idx, size(list) - 1) && !disableSeparators && ( 33 |
34 | )} 35 |
36 | ); 37 | }) 38 | ) : ( 39 | 46 | 47 | 48 | )} 49 |
50 | ); 51 | }; 52 | 53 | export default AnimatedDivList; 54 | -------------------------------------------------------------------------------- /components/bans-list-panel.tsx: -------------------------------------------------------------------------------- 1 | import { apiRoutes } from "apis/api-helper"; 2 | import { IDiscordUserLookup } from "models/backend/discord-models"; 3 | import { IXPDBUserBan } from "models/backend/xp-models"; 4 | import { FC, useEffect, useState } from "react"; 5 | import { avatarToURL } from "utils/discord-utils"; 6 | 7 | import BasicPanel from "./basic-panel"; 8 | import BlockButton, { BlockButtonVariant } from "./block-button"; 9 | import ButtonCluster from "./button-cluster"; 10 | import FallBackImage from "./fallback-image"; 11 | 12 | interface BansListPanelProps { 13 | ban: IXPDBUserBan; 14 | requestModify?: () => void; 15 | } 16 | 17 | const BansListPanel: FC = ({ ban, requestModify }) => { 18 | const [user, setUser] = useState(); 19 | useEffect(() => { 20 | const lookupUser = async () => { 21 | const res = await apiRoutes.discord.lookupUser(ban.userID); 22 | if (!res.success) return; 23 | setUser(res.body); 24 | }; 25 | lookupUser(); 26 | }, []); 27 | return ( 28 | 29 |
30 |
31 |
32 | 33 |
34 |

35 | {user ? ( 36 | <> 37 | {user?.username} •{" "} 38 | {user?.id} 39 | 40 | ) : ( 41 | <> 42 | Deleted Discord Account •{" "} 43 | {ban.userID} 44 | 45 | )} 46 |

47 |
48 |
49 |
50 |

Information

51 |

52 | {ban.content.notes || `None provided`} 53 |

54 |
55 |
56 | 60 | Modify 61 | 62 |
63 |
64 | 68 |
69 |
70 |
71 | ); 72 | }; 73 | 74 | export default BansListPanel; 75 | -------------------------------------------------------------------------------- /components/basic-panel.tsx: -------------------------------------------------------------------------------- 1 | import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import parse from 'html-react-parser'; 4 | import { CSSProperties, FC, ReactNode } from 'react'; 5 | import { fixDiscordMarkdownFormat } from 'utils/text-utils'; 6 | 7 | import Tooltip from './tooltip'; 8 | 9 | interface BasicPanelProps { 10 | disabled?: boolean; 11 | title?: string; 12 | description?: string; 13 | children?: ReactNode; 14 | md?: boolean; 15 | variant?: BasicPanelVariant; 16 | borderColor?: string; 17 | borderTop?: boolean; 18 | className?: string; 19 | style?: CSSProperties; 20 | tooltipText?: string; 21 | } 22 | 23 | export enum BasicPanelVariant { 24 | inPanel, 25 | } 26 | 27 | const variantToStye = (variant?: BasicPanelVariant) => { 28 | const className = `text-darkText border border-1 dark:text-darkText-darkMode font-semibold dark:bg-input-darkMode border-input-border bg-input dark:border-input-border-darkMode`; 29 | 30 | switch (variant) { 31 | case BasicPanelVariant.inPanel: 32 | return className; 33 | 34 | default: 35 | return ` bg-panelBack dark:bg-panelBack-darkMode`; 36 | } 37 | }; 38 | 39 | const BasicPanel: FC = (props) => { 40 | return ( 41 |
66 | {(props.title || props.description) && ( 67 |
68 | {props.title && ( 69 |
70 |

71 | {props.title} 72 | {props.tooltipText && ( 73 | 74 | 78 | 79 | )} 80 |

81 |
82 | )} 83 | {props.description && ( 84 | 85 |

86 | {props.md 87 | ? parse(fixDiscordMarkdownFormat(props.description)) 88 | : props.description}{' '} 89 |

90 |
91 | )} 92 |
93 | )} 94 | 95 | {props.children} 96 |
97 | ); 98 | }; 99 | 100 | export default BasicPanel; 101 | -------------------------------------------------------------------------------- /components/block-button.tsx: -------------------------------------------------------------------------------- 1 | import { IconDefinition } from '@fortawesome/free-solid-svg-icons'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react'; 4 | 5 | interface IBlockButtonProps 6 | extends DetailedHTMLProps< 7 | ButtonHTMLAttributes, 8 | HTMLButtonElement 9 | > { 10 | variant?: BlockButtonVariant; 11 | icon?: IconDefinition; 12 | } 13 | 14 | export enum BlockButtonVariant { 15 | inPanel, 16 | danger, 17 | blog, 18 | save, 19 | } 20 | 21 | const variantToStye = (variant?: BlockButtonVariant) => { 22 | const className = `relative flex h-fit w-full cursor-pointer flex-row items-center whitespace-nowrap rounded-md border px-4 py-2 text-left transition ease-in-out shadow-md hover:-translate-y-1 active:translate-y-0 active:shadow-sm active:outline disabled:pointer-events-none disabled:opacity-75 disabled:grayscale text-darkText dark:text-darkText-darkMode outline-white dark:outline-panelBack-darkMode`; 23 | 24 | switch (variant) { 25 | case BlockButtonVariant.blog: 26 | return `border-2 px-2 py-1 transition text-darkText dark:text-darkText-darkMode ease-in-out dark:md:hover:bg-white/10 dark:md:active:bg-white/20 md:hover:bg-black/10 md:active:bg-black/20`; 27 | case BlockButtonVariant.inPanel: 28 | return `${className} border-1 dark:text-darkText-darkMode font-semibold active:bg-button-state-pressed dark:bg-input-darkMode active:dark:bg-button-state-pressed-darkMode border-input-border bg-input dark:border-input-border-darkMode`; 29 | 30 | case BlockButtonVariant.save: 31 | return `${className} border-1 dark:text-darkText-darkMode font-semibold active:bg-button-state-pressed dark:bg-input-darkMode active:dark:bg-button-state-pressed-darkMode border-input-border bg-input justify-center dark:border-input-border-darkMode border-y-2 border-y-green-500`; 32 | 33 | case BlockButtonVariant.danger: 34 | return `${className} border-1 text-darkText-darkMode font-semibold dark:active:bg-red-500/50 active:bg-red-500/75 dark:bg-red-500/75 bg-red-500 border-input-border bg-input dark:border-input-border-darkMode`; 35 | 36 | default: 37 | return `${className} bg-panelBack dark:text-darkText-darkMode active:bg-button-state-pressed dark:bg-panelBack-darkMode active:dark:bg-button-state-pressed-darkMode dark:border-panelBack-darkMode`; 38 | } 39 | }; 40 | 41 | const BlockButton: FC = (props) => ( 42 | 53 | ); 54 | 55 | export default BlockButton; 56 | -------------------------------------------------------------------------------- /components/block-input.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeEventHandler, 3 | DetailedHTMLProps, 4 | FC, 5 | InputHTMLAttributes, 6 | } from 'react'; 7 | import { FieldError, UseFormRegisterReturn } from 'react-hook-form'; 8 | 9 | import ErrorLabel from './input-error-label'; 10 | import Label from './input-label'; 11 | 12 | interface InputProps { 13 | inputProps?: DetailedHTMLProps< 14 | InputHTMLAttributes, 15 | HTMLInputElement 16 | >; 17 | registerForm?: UseFormRegisterReturn; 18 | disabled?: boolean; 19 | value?: string | number; 20 | label?: string; 21 | placeholder?: string; 22 | onChange?: ChangeEventHandler; 23 | formError?: FieldError; 24 | } 25 | 26 | const BlockInput: FC = (props) => { 27 | return ( 28 |
29 | {props.label &&
45 | ); 46 | }; 47 | 48 | export default BlockInput; 49 | -------------------------------------------------------------------------------- /components/block-text-area.tsx: -------------------------------------------------------------------------------- 1 | import { isUndefined } from "lodash"; 2 | import { 3 | ChangeEventHandler, 4 | DetailedHTMLProps, 5 | FC, 6 | TextareaHTMLAttributes, 7 | } from "react"; 8 | import { FieldError, UseFormRegisterReturn } from "react-hook-form"; 9 | 10 | import ErrorLabel from "./input-error-label"; 11 | import Label from "./input-label"; 12 | 13 | interface IBlockTextAreaProps { 14 | inputProps?: DetailedHTMLProps< 15 | TextareaHTMLAttributes, 16 | HTMLTextAreaElement 17 | >; 18 | registerForm?: UseFormRegisterReturn; 19 | disabled?: boolean; 20 | value?: string | number; 21 | label?: string; 22 | placeholder?: string; 23 | onChange?: ChangeEventHandler; 24 | formError?: FieldError; 25 | } 26 | 27 | const BlockTextArea: FC = (props) => { 28 | return ( 29 |
30 | {props.label &&