diff --git a/discord-invite/index.html b/discord-invite/index.html
new file mode 100644
index 00000000..8a803912
--- /dev/null
+++ b/discord-invite/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+ Discord Invite
+
+
+
+
+
+
+
Loading...
+
+
+
+
+
+
+
+
diff --git a/discord-invite/script.js b/discord-invite/script.js
new file mode 100644
index 00000000..834ad365
--- /dev/null
+++ b/discord-invite/script.js
@@ -0,0 +1,52 @@
+// Script file for discord-invite feature.
+import {
+ createElement,
+ getIsSuperUser,
+ generateDiscordInviteLink,
+} from './utils.js';
+
+const discordInviteDescription = document.querySelector(
+ '#discord-invite-link-description',
+);
+const createInviteButton = document.querySelector('#create-discord-invite');
+const loader = document.querySelector('.loader');
+const mainContainer = document.querySelector('.container');
+const subContainer = document.querySelector('.wrapper');
+const showDataWrapper = document.querySelector('.show-data-wrapper');
+const invitePurpose = document.querySelector('.invite-purpose');
+const discordInviteLink = document.querySelector('.discord-invite-link');
+
+function changeLoaderVisibility({ hide }) {
+ if (hide) loader.classList.add('hidden');
+ else loader.classList.remove('hidden');
+}
+
+(async function renderCardsInitial() {
+ changeLoaderVisibility({ hide: false });
+
+ const isSuperUser = await getIsSuperUser();
+
+ if (!isSuperUser) {
+ const unAuthorizedText = createElement({
+ type: 'h2',
+ attributes: { class: 'unauthorized-text' },
+ innerText: 'You are not authorized to view this page.',
+ });
+ mainContainer.append(unAuthorizedText);
+ subContainer.classList.add('hidden');
+ changeLoaderVisibility({ hide: true });
+ return;
+ }
+
+ subContainer.classList.remove('hidden');
+
+ changeLoaderVisibility({ hide: true });
+})();
+
+createInviteButton.addEventListener('click', async () => {
+ const data = await generateDiscordInviteLink(discordInviteDescription.value);
+ subContainer.classList.add('hidden');
+ showDataWrapper.classList.remove('hidden');
+ invitePurpose.innerHTML = data.purpose;
+ discordInviteLink.innerHTML = data.inviteLink;
+});
diff --git a/discord-invite/style.css b/discord-invite/style.css
new file mode 100644
index 00000000..9eeab8e0
--- /dev/null
+++ b/discord-invite/style.css
@@ -0,0 +1,69 @@
+:root {
+ font-family: 'Inter', sans-serif;
+ --color-primary: #1d1283;
+ --color-primary-hover: #11085c;
+ --color-button-hover: #2c1bc6;
+ --white: #fff;
+ --color-gray: #666;
+ --black-color: black;
+ --black-transparent: #000000a8;
+ --light-gray-color: lightgray;
+ --red-color: red;
+ --elevation-1: 0 1px 3px 1px rgba(0, 0, 0, 0.1),
+ 0 1px 2px 0 rgba(0, 0, 0, 0.1);
+ --elevation-3: 0px 1px 3px 0px rgba(0, 0, 0, 0.3),
+ 0px 4px 8px 3px rgba(0, 0, 0, 0.15);
+ --color-green: green;
+ --color-red-variant1: #f43030;
+}
+
+body {
+ font-family: 'Roboto', sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+.header {
+ text-align: center;
+}
+
+.container {
+ text-align: center;
+}
+
+.wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.create-discord-invite {
+ margin: 2rem;
+ padding: 10px;
+}
+
+.hidden {
+ visibility: hidden;
+}
+
+#toast {
+ position: fixed;
+ top: 20px;
+ right: -300px;
+ color: #fff;
+ padding: 15px;
+ border-radius: 5px;
+}
+
+.success {
+ background: var(--color-green);
+}
+
+.failure {
+ background: var(--color-red-variant1);
+}
+
+.animated_toast {
+ animation: slideIn 0.5s ease-in-out forwards,
+ slideOut 0.5s ease-in-out 2.5s forwards;
+}
diff --git a/discord-invite/utils.js b/discord-invite/utils.js
new file mode 100644
index 00000000..17d815ef
--- /dev/null
+++ b/discord-invite/utils.js
@@ -0,0 +1,77 @@
+const BASE_URL =
+ window.location.hostname === 'localhost'
+ ? 'https://staging-api.realdevsquad.com'
+ : window.API_BASE_URL;
+
+const toast = document.getElementById('toast');
+
+function createElement({ type, attributes = {}, innerText }) {
+ const element = document.createElement(type);
+ Object.keys(attributes).forEach((item) => {
+ element.setAttribute(item, attributes[item]);
+ });
+ element.textContent = innerText;
+ return element;
+}
+
+async function getIsSuperUser() {
+ try {
+ const res = await fetch(`${BASE_URL}/users/self`, {
+ method: 'GET',
+ credentials: 'include',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ });
+ const self_user = await res.json();
+ return self_user?.roles['super_user'];
+ } catch (error) {
+ console.error(error);
+ const errorMessage = error?.message || 'Something went wrong!';
+ showToast({ message: errorMessage, type: 'error' });
+ }
+}
+
+async function generateDiscordInviteLink(purpose) {
+ const body = {
+ purpose,
+ };
+ try {
+ const res = await fetch(`${BASE_URL}/discord-actions/invite?dev=true`, {
+ method: 'POST',
+ credentials: 'include',
+ body: JSON.stringify(body),
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ });
+ const data = await res.json();
+ return data;
+ } catch (error) {
+ console.error(error);
+ const errorMessage = error?.message || 'Something went wrong!';
+ showToast({ message: errorMessage, type: 'error' });
+ }
+}
+
+function showToast({ message, type }) {
+ toast.innerText = message;
+
+ if (type === 'success') {
+ toast.classList.add('success');
+ toast.classList.remove('failure');
+ } else {
+ toast.classList.add('failure');
+ toast.classList.remove('success');
+ }
+
+ toast.classList.remove('hidden');
+ toast.classList.add('animated_toast');
+
+ setTimeout(() => {
+ toast.classList.add('hidden');
+ toast.classList.remove('animated_toast');
+ }, 3000);
+}
+
+export { createElement, getIsSuperUser, showToast, generateDiscordInviteLink };
diff --git a/index.html b/index.html
index b473a0bf..270d9e6d 100644
--- a/index.html
+++ b/index.html
@@ -146,6 +146,13 @@
Applications
+
+ Create Discord Invite
+