Popup pour état connexion sur avatar + Refonte views Dashboard + Ajout types depuis DiscordJS

This commit is contained in:
Angels-dev
2024-09-03 01:24:13 +02:00
parent 9fec536395
commit 69b648d773
25 changed files with 867 additions and 586 deletions

View File

@@ -1,15 +1,20 @@
<script setup lang="ts">
import { RouterView, RouterLink } from 'vue-router'
import { ref, provide, onMounted } from 'vue'
import { useLoading } from 'vue-loading-overlay'
import { ModalsContainer, useModal } from 'vue-final-modal'
import { checkUser, checkBots, checkUserGuilds } from '@/utils/discord'
import AvatarModal from './components/AvatarModal.vue'
import type { Ref } from 'vue'
import type { User, Guild } from '@/utils/discord'
import type { APIUser, APIGuild } from 'discord-api-types/v10'
// État de l'utilisateur, des bots et de leur avatar
const user: Ref<User | null> = ref(null)
const bots: Ref<User[] | null> = ref([])
const guilds: Ref<Guild[] | null> = ref([])
const avatar = ref('/question-square.svg')
const user: Ref<APIUser | null> = ref(null)
const bots: Ref<APIUser[] | null> = ref([])
const guilds: Ref<APIGuild[] | null> = ref([])
const avatar = ref('/question-square-48.png')
const ready = ref(false)
provide('user', user) // Fournit l'utilisateur aux composants enfants
@@ -17,14 +22,25 @@ provide('bots', bots) // Fournit les bots aux composants enfants
provide('guilds', guilds) // Fournit les guilds aux composants enfants
provide('ready', ready) // Fournit l'état de préparation aux composants enfants
// Affiche un écran de chargement
const $loading = useLoading({ color: '#eee', opacity: 0 })
// Modèle du popup de l'avatar
const { open } = useModal({ component: AvatarModal })
// Cycle de vie
onMounted(async () => {
const loader = $loading.show()
user.value = await checkUser() // Vérifie l'authentification lors du montage du composant
if (user.value) {
avatar.value = `https://cdn.discordapp.com/avatars/${user.value.id}/${user.value.avatar}?size=48`
bots.value = await checkBots() // Vérifie les bots
guilds.value = await checkUserGuilds() // Vérifie les guilds de l'utilisateur
ready.value = true
}
ready.value = true
loader.hide()
})
</script>
@@ -37,8 +53,8 @@ onMounted(async () => {
<nav class="vc">
<router-link to="/">Index</router-link>
<p>|</p>
<router-link to="/discord">Discord</router-link>
<img class="rounded" :src=avatar>
<router-link to="/discord/dashboard">Discord</router-link>
<img class="rounded" @click="() => open()" :src=avatar>
</nav>
</header>
@@ -47,6 +63,8 @@ onMounted(async () => {
<component :is="Component" />
</transition>
</router-view>
<ModalsContainer />
</template>
<style scoped>

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import { inject } from 'vue'
import { VueFinalModal } from 'vue-final-modal'
import { login, logout } from '@/utils/discord'
import type { Ref } from 'vue'
import type { APIUser } from 'discord-api-types/v10'
// État de l'utilisateur
const user: Ref<APIUser | null> | undefined = inject('user')
// Variable titre depuis le parent
defineProps<{
title?: string
}>()
// Événement de mise à jour du modal
const emit = defineEmits<{
(e: 'update:modelValue', modelValue: boolean): void
}>()
</script>
<template>
<VueFinalModal @update:model-value="val => emit('update:modelValue', val)" class="confirm-modal"
content-transition="vfm-fade" overlay-transition="vfm-fade" content-class="confirm-modal-content">
<!--<h1>{{ title }}</h1>
<slot />-->
<div v-if="user">
<p>Vous êtes connecté en tant que</p>
{{ user.username }}
{{ user.id }}
<p>avec Discord.</p>
<button @click="emit('update:modelValue', false); logout()">Se déconnecter </button>
</div>
<div v-else>
<p>Vous n'êtes pas connecté.</p>
<p>Cliquez sur le bouton ci-dessous, vous serez redirigé sur une page de connexion Discord !</p>
<button @click="emit('update:modelValue', false); login()">Se connecter avec Discord</button>
</div>
</VueFinalModal>
</template>
<style>
.confirm-modal {
display: flex;
justify-content: center;
align-items: center;
}
.confirm-modal-content {
display: flex;
flex-direction: column;
padding: 1rem;
background: #126;
border-radius: 0.5rem;
}
.confirm-modal-content>*+* {
margin: 0.5rem 0;
}
.confirm-modal-content h1 {
font-size: 1.375rem;
}
.confirm-modal-content button {
margin: 0.25rem 0 0 auto;
padding: 0 8px;
border: 1px solid;
border-radius: 0.5rem;
}
</style>

View File

@@ -3,20 +3,22 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import { fab } from '@fortawesome/free-brands-svg-icons'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createI18n } from 'vue-i18n'
import cookies from 'vue-cookies'
import { createVfm } from 'vue-final-modal'
import { LoadingPlugin } from 'vue-loading-overlay'
import App from './App.vue'
import router from './router'
import './assets/main.css'
import 'vue-final-modal/style.css'
import 'vue-loading-overlay/dist/css/index.css'
library.add(fas, fab)
createApp(App)
.use(createPinia())
.use(createI18n({}))
.use(createVfm())
.use(LoadingPlugin)
.use(router)
.use(cookies)
.component('font-awesome-icon', FontAwesomeIcon)
.mount('#app')

View File

@@ -28,9 +28,28 @@ const router = createRouter({
meta: { title: 'MainFrame | Discord', discordAuth: false },
children: [
{
path: '',
path: 'dashboard',
name: 'dashboard',
component: () => import('../views/Discord/DashboardView.vue')
component: () => import('../views/Discord/DashboardView.vue'),
children: [
{
path: '',
name: 'dashboardMain',
component: () => import('../views/Discord/Dashboard/MainView.vue')
},
{
path: 'bot/:botId(\\d+)',
name: 'dashboardBot',
component: () => import('../views/Discord/Dashboard/BotView.vue'),
props: true
},
{
path: 'bot/:botId(\\d+)/guild/:guildId(\\d+)',
name: 'dashboardGuild',
component: () => import('../views/Discord/Dashboard/GuildView.vue'),
props: true
}
]
},
{
path: 'config',
@@ -49,7 +68,7 @@ const router = createRouter({
path: '/:pathMatch(.*)*',
name: '404',
component: () => import('../views/404View.vue'),
meta: { title: 'MainFrame | 404' },
meta: { title: 'MainFrame | 404' }
}
]
})

View File

@@ -2,248 +2,77 @@ import axios from 'axios'
// Fonction pour rediriger vers Discord pour l'authentification
export function login() {
window.location.href = import.meta.env.VITE_BACKEND_URL + '/discord/auth/redirect';
window.location.href = import.meta.env.VITE_BACKEND_URL + '/discord/auth/redirect'
}
// Fonction pour se déconnecter
export async function logout() {
try {
console.log('1111')
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/auth/logout', { withCredentials: true })
console.log('1112')
console.log(response.data)
console.log('1113')
window.location.href = '/discord/logout';
} catch (err) { return null }
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/auth/logout', {
withCredentials: true
})
console.log(response.data)
window.location.href = '/discord/logout'
} catch (err) {
return null
}
}
// Fonction pour récupérer l'état des bots
export async function checkBots() {
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/bots', { withCredentials: true })
return response.data
} catch (err) { return null }
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/bots', {
withCredentials: true
})
return response.data
} catch (err) {
return null
}
}
// Fonction pour récupérer les informations de l'utilisateur
export async function checkUser() {
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/user', { withCredentials: true })
return response.data
} catch (err) { return null }
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/user', {
withCredentials: true
})
return response.data
} catch (err) {
return null
}
}
// Fonction pour récupérer les guilds de l'utilisateur
export async function checkUserGuilds() {
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/user/guilds', { withCredentials: true })
return response.data
} catch (err) { return null }
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/user/guilds', {
withCredentials: true
})
return response.data
} catch (err) {
return null
}
}
// Types de données Discord
export type snowflake = string // A unique identifier for an object within Discord
export type ISO8601 = string // Date and hour timestamp "2017-07-11T17:27:07.299000+00:00"
export async function getUser(userId: String) {
try {
const response = await axios.get(import.meta.env.VITE_BACKEND_URL + '/discord/user/' + userId, {
withCredentials: true
})
return response.data
} catch (err) {
return null
}
}
export interface User {
id: snowflake // the user's id
username: string // the user's username, not unique across the platform
discriminator: string // the user's Discord-tag
global_name?: string // the user's display name, if it is set. For bots, this is the application name
avatar?: string // the user's avatar hash
bot?: boolean // whether the user belongs to an OAuth2 application
system?: boolean // whether the user is an Official Discord System user (part of the urgent message system)
mfa_enabled?: boolean // whether the user has two factor enabled on their account
banner?: string // the user's banner hash
accent_color?: number // the user's banner color encoded as an integer representation of hexadecimal color code
locale?: string // the user's chosen language option
verified?: boolean // whether the email on this account has been verified
email?: string // the user's email
flags?: number // the flags on a user's account
premium_type?: number // the type of Nitro subscription on a user's account
public_flags?: number // the public flags on a user's account
avatar_decoration_data?: AvatarDecoration // data for the user's avatar decoration
export async function getGuild(guildId: String) {
try {
const response = await axios.get(
import.meta.env.VITE_BACKEND_URL + '/discord/guild/' + guildId,
{ withCredentials: true }
)
return response.data
} catch (err) {
return null
}
}
export interface AvatarDecoration {
asset: string // the avatar decoration hash
sku_id: snowflake // id of the avatar decoration's SKU
}
export interface Guild {
id: snowflake // guild id
name: string // guild name (2-100 characters, excluding trailing and leading whitespace)
icon?: string // icon hash
icon_hash?: string // icon hash, returned when in the template object
splash?: string // splash hash
discovery_splash?: string // discovery splash hash; only present for guilds with the "DISCOVERABLE" feature
owner?: boolean // true if the user is the owner of the guild
owner_id: snowflake // id of owner
permissions?: string // total permissions for the user in the guild (excludes overwrites and implicit permissions)
region?: string // voice region id for the guild (deprecated)
afk_channel_id?: snowflake // id of afk channel
afk_timeout: number // afk timeout in seconds
widget_enabled?: boolean // true if the server widget is enabled
widget_channel_id?: snowflake // the channel id that the widget will generate an invite to, or null if set to no invite
verification_level: number // verification level required for the guild
default_message_notifications: number // default message notifications level
explicit_content_filter: number // explicit content filter level
roles: Role[] // roles in the guild
emojis: Emoji[] // custom guild emojis
features: string[] // enabled guild features
mfa_level: number // required MFA level for the guild
application_id?: snowflake // application id of the guild creator if it is bot-created
system_channel_id?: snowflake // the id of the channel where guild notices such as welcome messages and boost events are posted
system_channel_flags: number // system channel flags
rules_channel_id?: snowflake // the id of the channel where Community guilds can display rules and/or guidelines
max_presences?: number // the maximum number of presences for the guild (null is always returned, apart from the largest of guilds)
max_members?: number // the maximum number of members for the guild
vanity_url_code?: string // the vanity url code for the guild
description?: string // the description of a guild
banner?: string // banner hash
premium_tier: number // premium tier (Server Boost level)
premium_subscription_count?: number // the number of boosts this guild currently has
preferred_locale: string // the preferred locale of a Community guild; used in server discovery and notices from Discord, and sent in interactions; defaults to "en-US"
public_updates_channel_id?: snowflake // the id of the channel where admins and moderators of Community guilds receive notices from Discord
max_video_channel_users?: number // the maximum amount of users in a video channel
max_stage_video_channel_users?: number // the maximum amount of users in a stage video channel
approximate_member_count?: number // approximate number of members in this guild, returned from the GET /guilds/<id> and /users/@me/guilds endpoints when with_counts is true
approximate_presence_count?: number // approximate number of non-offline members in this guild, returned from the GET /guilds/<id> and /users/@me/guilds endpoints when with_counts is true
welcome_screen?: WelcomeScreen // the welcome screen of a Community guild, shown to new members, returned in an Invite's guild object
nsfw_level?: number // guild NSFW level
stickers?: Sticker[] // custom guild stickers
premium_progress_bar_enabled: boolean // whether the guild has the boost progress bar enabled
safety_alerts_channel_id?: snowflake // the id of the channel where admins and moderators of Community guilds receive safety alerts from Discord
}
export interface GuildMember {
user?: User // the user this guild member represents
nick?: string // this user's guild nickname
avatar?: string // the member's guild avatar hash
roles: snowflake[] // array of role object ids
joined_at: ISO8601 // when the user joined the guild
premium_since?: ISO8601 // when the user started boosting the guild
deaf: boolean // whether the user is deafened in voice channels
mute: boolean // whether the user is muted in voice channels
flags: number // guild member flags represented as a bit set, defaults to 0
pending?: boolean // whether the user has not yet passed the guild's Membership Screening requirements
permissions?: string // total permissions of the member in the channel, including overwrites, returned when in the interaction object
communication_disabled_until?: ISO8601 // when the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out
avatar_decoration_data?: AvatarDecoration // data for the member's guild avatar decoration
}
export interface Role {
id: snowflake // role id
name: string // role name
color: number // integer representation of hexadecimal color code
hoist: boolean // if this role is pinned in the user listing
icon?: string // role icon hash
unicode_emoji?: string // role unicode emoji
position: number // position of this role (roles with the same position are sorted by id)
permissions: string // permission bit set
managed: boolean // whether this role is managed by an integration
mentionable: boolean // whether this role is mentionable
tags?: RoleTags // the tags this role has
flags: number // role flags combined as a bitfield
}
export interface RoleTags {
bot_id?: snowflake // the id of the bot this role belongs to
integration_id?: snowflake // the id of the integration this role belongs to
premium_subscriber?: null // whether this is the guild's Booster role
subscription_listing_id?: snowflake // the id of this role's subscription sku and listing
available_for_purchase?: null // whether this role is available for purchase
guild_connections?: null // whether this role is a guild's linked role
}
export interface Emoji {
id?: snowflake // emoji id
name?: string // (can be null only in reaction emoji objects) // emoji name
roles?: snowflake[] // roles allowed to use this emoji
user?: User // user that created this emoji
require_colons?: boolean // whether this emoji must be wrapped in colons
managed?: boolean // whether this emoji is managed
animated?: boolean // whether this emoji is animated
available?: boolean // whether this emoji can be used, may be false due to loss of Server Boosts
}
export interface Sticker {
id: snowflake // id of the sticker
pack_id?: snowflake // for standard stickers, id of the pack the sticker is from
name: string // name of the sticker
description?: string // description of the sticker
tags: string // autocomplete/suggestion tags for the sticker (max 200 characters)
asset?: string // Deprecated previously the sticker asset hash, now an empty string
type: number // type of sticker
format_type: number // type of sticker format
available?: boolean // whether this guild sticker can be used, may be false due to loss of Server Boosts
guild_id?: snowflake // id of the guild that owns this sticker
user?: User // the user that uploaded the guild sticker
sort_value?: number // the standard sticker's sort order within its pack
}
export interface WelcomeScreen {
description?: string // the server description shown in the welcome screen
welcome_channels: WelcomeChannel[] // the channels shown in the welcome screen, up to 5
}
export interface WelcomeChannel {
channel_id: snowflake // the channel's id
description: string // the description shown for the channel
emoji_id?: snowflake // the emoji id, if the emoji is custom
emoji_name?: string // the emoji name if custom, the unicode character if standard, or null if no emoji is set
}
export interface Channel {
id: snowflake // the id of this channel
type: number // the type of channel
guild_id?: snowflake // the id of the guild (may be missing for some channel objects received over gateway guild dispatches)
position?: number // sorting position of the channel (channels with the same position are sorted by id)
permission_overwrites?: [] // array of overwrite objects // explicit permission overwrites for members and roles
name?: string // the name of the channel (1-100 characters)
topic?: string // the channel topic (0-4096 characters for GUILD_FORUM and GUILD_MEDIA channels, 0-1024 characters for all others)
nsfw?: boolean // whether the channel is nsfw
last_message_id?: snowflake // the id of the last message sent in this channel (or thread for GUILD_FORUM or GUILD_MEDIA channels) (may not point to an existing or valid message or thread)
bitrate?: number // the bitrate (in bits) of the voice channel
user_limit?: number // the user limit of the voice channel
rate_limit_per_user?: number // amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected
recipients?: User[] // the recipients of the DM
icon?: string // icon hash of the group DM
owner_id?: snowflake // id of the creator of the group DM or thread
application_id?: snowflake // application id of the group DM creator if it is bot-created
managed?: boolean // for group DM channels: whether the channel is managed by an application via the gdm.join OAuth2 scope
parent_id?: snowflake // for guild channels: id of the parent category for a channel (each parent category can contain up to 50 channels), for threads: id of the text channel this thread was created
last_pin_timestamp?: ISO8601 // when the last pinned message was pinned. This may be null in events such as GUILD_CREATE when a message is not pinned.
rtc_region?: string // voice region id for the voice channel, automatic when set to null
video_quality_mode?: number // the camera video quality mode of the voice channel, 1 when not present
message_count?: number // number of messages (not including the initial message or deleted messages) in a thread.
member_count?: number // an approximate count of users in a thread, stops counting at 50
thread_metadata?: ThreadMetadata // thread-specific fields not needed by other channels
member?: ThreadMember // thread member object for the current user, if they have joined the thread, only included on certain API endpoints
default_auto_archive_duration?: number // default duration, copied onto newly created threads, in minutes, threads will stop showing in the channel list after the specified period of inactivity, can be set to: 60, 1440, 4320, 10080
permissions?: string // computed permissions for the invoking user in the channel, including overwrites, only included when part of the resolved data received on a slash command interaction. This does not include implicit permissions, which may need to be checked separately
flags?: number // channel flags combined as a bitfield
total_message_sent?: number // number of messages ever sent in a thread, it's similar to message_count on message creation, but will not decrement the number when a message is deleted
available_tags?: Tag[] // the set of tags that can be used in a GUILD_FORUM or a GUILD_MEDIA channel
applied_tags?: snowflake[] // the IDs of the set of tags that have been applied to a thread in a GUILD_FORUM or a GUILD_MEDIA channel
default_reaction_emoji?: DefaultReaction // the emoji to show in the add reaction button on a thread in a GUILD_FORUM or a GUILD_MEDIA channel
default_thread_rate_limit_per_user?: number // the initial rate_limit_per_user to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update.
default_sort_order?: number // the default sort order type used to order posts in GUILD_FORUM and GUILD_MEDIA channels. Defaults to null, which indicates a preferred sort order hasn't been set by a channel admin
default_forum_layout?: number // the default forum layout view used to display posts in GUILD_FORUM channels. Defaults to 0, which indicates a layout view has not been set by a channel admin
}
export interface Tag {
id: snowflake // the id of the tag
name: string // the name of the tag (0-20 characters)
moderated: boolean // whether this tag can only be added to or removed from threads by a member with the MANAGE_THREADS permission
emoji_id?: snowflake // the id of a guild's custom emoji
emoji_name?: string // the unicode character of the emoji
}
export interface ThreadMetadata {
archived: boolean // whether the thread is archived
auto_archive_duration: number // the thread will stop showing in the channel list after auto_archive_duration minutes of inactivity, can be set to: 60, 1440, 4320, 10080
archive_timestamp: ISO8601 // timestamp when the thread's archive status was last changed, used for calculating recent activity
locked: boolean // whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it
invitable?: boolean // whether non-moderators can add other non-moderators to a thread; only available on private threads
create_timestamp?: ISO8601 // timestamp when the thread was created; only populated for threads created after 2022-01-09
}
export interface ThreadMember {
id?: snowflake // ID of the thread
user_id?: snowflake // ID of the user
join_timestamp: ISO8601 // Time the user last joined the thread
flags: number // Any user-thread settings, currently only used for notifications
member?: GuildMember // Additional information about the user
}
export interface DefaultReaction {
emoji_id?: snowflake // the id of a guild's custom emoji
emoji_name?: string // the unicode character of the emoji
}

View File

@@ -1,9 +1,12 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router'
import { inject } from 'vue'
import type { Ref } from 'vue'
const ready: Ref<boolean> | undefined = inject('ready')
</script>
<template>
<div class="main">
<div class="main" v-if="ready">
<h1>Oula ! (Erreur 404)</h1>
<h3>
Je sais pas ce que t'as fais mais tu es tombé sur une page inexistante.

View File

@@ -0,0 +1,52 @@
<script setup lang="ts">
import { ref, provide, inject, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { useLoading } from 'vue-loading-overlay'
import { getUser } from '@/utils/discord'
import type { Ref } from 'vue'
import type { APIUser, APIGuild } from 'discord-api-types/v10'
// Récupérer le bot
defineProps({ botId: String })
const route = useRoute()
const bot = ref({} as APIUser)
provide('bot', bot) // Fournit le bot aux composants enfants
// État des guilds
const guilds = inject('guilds') as Ref<APIGuild[] | null>
const guildsAdmin = ref([] as APIGuild[] | undefined)
guildsAdmin.value = guilds?.value?.filter(guild => guild.permissions === '2251799813685247')
function guildIcon(guild: APIGuild) { return guild.icon ? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png?size=128` : '/question-square-128-white.png' }
const $loading = useLoading({ color: '#eee', opacity: 0 })
onMounted(async () => {
const loader = $loading.show()
bot.value = await getUser(route.params.botId as string)
if (bot.value) loader.hide()
})
</script>
<template>
<div class="main">
<div class="title">
<h1>Infos du bot {{ bot.username }}</h1>
<div v-if="bot">
<p>ID : {{ bot.id }}.</p>
</div>
<p v-else>...</p>
</div>
<br>
<div v-if="guilds">
<p>Vous avez {{ guilds.length }} guilds.</p>
<p>Voici les guilds vous avez les permissions d'administrateur :</p>
<div v-for="guild in guildsAdmin" :key="guild.id">
<p class="guild-title">{{ guild.name }}</p>
<router-link :to="`${botId}/guild/${guild.id}`"><img class="rounded"
:src="guildIcon(guild)"></router-link>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
import { ref, provide, inject } from 'vue'
import { useRoute } from 'vue-router'
import type { Ref } from 'vue'
import type { APIGuild } from 'discord-api-types/v10'
// Récupérer le bot
defineProps({ guildId: String })
const route = useRoute()
const guilds = inject('guilds') as Ref<APIGuild[] | null>
const guild = ref({} as APIGuild | undefined)
guild.value = guilds.value?.find(guild => guild.id === route.params.guildId)
provide('guild', guild) // Fournit la guild aux composants enfants
</script>
<template>
<div class="main">
<div class="title">
<h1>Infos de la guild {{ guild?.name }}</h1>
<div v-if="guild">
<p>ID : {{ guild.id }}.</p>
</div>
<p v-else>...</p>
</div>
</div>
</template>

View File

@@ -0,0 +1,126 @@
<script setup lang="ts">
import { ref, inject } from 'vue'
import type { Ref } from 'vue'
import type { APIUser, APIGuild } from 'discord-api-types/v10'
// État de l'utilisateur, des bots et des guilds
const user: Ref<APIUser | null> | undefined = inject('user')
const bots: Ref<APIUser[] | null> | undefined = inject('bots')
const guilds = inject('guilds') as Ref<APIGuild[] | null>
function avatar(bot: APIUser) { return bot ? `https://cdn.discordapp.com/avatars/${bot.id}/${bot.avatar}?size=256` : '/question-square-256-white.png' }
const botTamiseur = ref({} as APIUser)
const botGroove = ref({} as APIUser)
const botFunky = ref({} as APIUser)
const botJujul = ref({} as APIUser)
const botChantier = ref({} as APIUser)
if (bots?.value) {
botTamiseur.value = bots.value[0]
botGroove.value = bots.value[1]
botFunky.value = bots.value[2]
botJujul.value = bots.value[3]
botChantier.value = bots.value[4]
}
const jujul = ref(false)
const owner = ref(false)
jujul.value = guilds?.value?.some(guild => guild.id === '796327643783626782' && guild.permissions === '2251799813685247') as boolean
owner.value = user?.value?.id === '223831938346123275'
</script>
<template>
<div class="main">
<div class="title">
<h1>Liste de mes bots</h1>
<div v-if="user">
<p>Vous êtes connecté en tant que {{ user.global_name }}.</p>
<p>Vous avez l'ID {{ user.id }}.</p>
</div>
<div v-else>
<p>Vous n'êtes pas connecté.</p>
<p>Connectez-vous avec le bouton en haut à droite !</p>
</div>
</div>
<div v-if="user" class="bots">
<div>
<p class="bot-title">{{ botGroove.username }}</p>
<router-link :to="`dashboard/bot/${botGroove.id}`"><img class="rounded"
:src="avatar(botGroove)"></router-link>
<p class="bot-desc">Petit chouchou du tamiseur</p>
</div>
<div>
<p style="font-size: 26px">{{ botTamiseur.username }}</p>
<router-link :to="`dashboard/bot/${botTamiseur.id}`"><img class="rounded" style="max-width: 80%"
:src="avatar(botTamiseur)"></router-link>
<p style="font-size: 22px">Le big boss</p>
</div>
<div>
<p class="bot-title">{{ botFunky.username }}</p>
<router-link :to="`dashboard/bot/${botFunky.id}`"><img class="rounded"
:src="avatar(botFunky)"></router-link>
<p class="bot-desc">L'autre , qui le connait ?</p>
</div>
</div>
<div v-if="user" class="bots">
<div>
<p class="bot-title">{{ botJujul.username }}</p>
<router-link v-if="jujul" :to="`dashboard/bot/${botJujul.id}`"><img class="rounded"
:src="avatar(botJujul)"></router-link>
<img v-else style="filter: grayscale(1)" class="rounded" :src="avatar(botJujul)">
<p class="bot-desc">(Réservé pour la streameuse Jujul / Laytho)</p>
</div>
<div>
<p class="bot-title">{{ botChantier.username }}</p>
<router-link v-if="owner" :to="`dashboard/bot/${botChantier.id}`"><img class="rounded"
:src="avatar(botChantier)"></router-link>
<img v-else style="filter: grayscale(1)" class="rounded" :src="avatar(botChantier)">
<p class="bot-desc">(Réservé pour les débug temporaires)</p>
</div>
</div>
</div>
</template>
<style scoped>
.main {
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
align-content: stretch;
justify-content: center;
}
.title {
flex: 0.6;
margin-right: auto;
}
.bots {
flex: 1.2;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
text-align: center;
gap: 30px;
div {
flex: 1 1 0;
}
}
.bot-title {
font-size: 20px;
}
.bot-desc {
font-size: 16px;
}
img {
max-width: 60%;
height: auto;
}
</style>

View File

@@ -1,106 +1,5 @@
<script setup lang="ts">
import { ref, inject } from 'vue'
import type { Ref } from 'vue'
import type { User, Guild } from '@/utils/discord'
// État de l'utilisateur et des bots
const user: Ref<User | null> | undefined = inject('user')
const bots: Ref<User[] | null> | undefined = inject('bots')
const guilds: Ref<Guild[] | null> | undefined = inject('guilds')
console.log(guilds)
function avatar(bot: User) { return bot?`https://cdn.discordapp.com/avatars/${bot.id}/${bot.avatar}?size=256` : '' }
const bot1 = ref({} as User)
const bot2 = ref({} as User)
const bot3 = ref({} as User)
const bot4 = ref({} as User)
const bot5 = ref({} as User)
if (bots?.value) {
bot1.value = bots.value[0]
bot2.value = bots.value[1]
bot3.value = bots.value[2]
bot4.value = bots.value[3]
bot5.value = bots.value[4]
}
</script>
<template>
<div class="main">
<div class="title">
<h1>Liste de mes bots</h1>
<div v-if="user">
<p>Vous êtes connecté en tant que {{ user.global_name }}.</p>
<p>Vous avez l'ID {{ user.id }}.</p>
</div>
<p v-else>Vous n'êtes pas connecté.</p>
</div>
<div class="bots">
<div>
<p class="bot-title">{{ bot2.username }}</p>
<img class="rounded" :src="avatar(bot2)">
<p class="bot-desc">Petit chouchou du tamiseur</p>
</div>
<div>
<p style="font-size: 26px">{{ bot1.username }}</p>
<img style="max-width: 80%" class="rounded" :src="avatar(bot1)">
<p style="font-size: 22px">Le big boss</p>
</div>
<div>
<p class="bot-title">{{ bot3.username }}</p>
<img class="rounded" :src="avatar(bot3)">
<p class="bot-desc">L'autre , qui le connait ?</p>
</div>
</div>
<div class="bots">
<div>
<p class="bot-title">{{ bot4.username }}</p>
<img class="rounded" :src="avatar(bot4)">
<p class="bot-desc">(Réservé pour la streameuse Jujul / Laytho)</p>
</div>
<div>
<p class="bot-title">{{ bot5.username }}</p>
<img class="rounded" :src="avatar(bot5)">
<p class="bot-desc">(Réservé pour les débug temporaires)</p>
</div>
</div>
</div>
</template>
<style scoped>
.main {
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
align-content: stretch;
justify-content: center;
}
.title {
flex: 0.6;
margin-right: auto;
}
.bots {
flex: 1.2;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
text-align: center;
gap: 30px;
div {
flex: 1 1 0;
}
}
.bot-title {
font-size: 20px;
}
.bot-desc {
font-size: 16px;
}
img {
max-width: 60%;
height: auto;
}
</style>
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</template>

View File

@@ -1,12 +1,12 @@
<script setup lang="ts">
import { inject } from 'vue'
import type { Ref } from 'vue'
import type { User, Guild } from '@/utils/discord'
import type { APIUser, APIGuild } from 'discord-api-types/v10'
// État de l'utilisateur et des bots
const user: Ref<User | null> | undefined = inject('user')
const bots: Ref<User[] | null> | undefined = inject('bots')
const guilds: Ref<Guild[] | null> | undefined = inject('guilds')
const user: Ref<APIUser | null> | undefined = inject('user')
const bots: Ref<APIUser[] | null> | undefined = inject('bots')
const guilds: Ref<APIGuild[] | null> | undefined = inject('guilds')
if (user) user.value = null
if (bots) bots.value = null
if (guilds) guilds.value = null

View File

@@ -1,22 +1,27 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { login, logout } from '@/utils/discord'
import { inject } from 'vue'
import { inject, ref } from 'vue'
import { useRoute } from 'vue-router'
import type { Ref } from 'vue'
import type { User } from '@/utils/discord'
// État de l'utilisateur et des bots
const user: Ref<User | null> | undefined = inject('user')
// État de l'utilisateur
const ready: Ref<boolean> | undefined = inject('ready')
// Récupérer l'ID du bot et de la guild
const route = useRoute()
const botId = ref('')
const guildId = ref('')
botId.value = route.params.botId as string
guildId.value = route.params.guildId as string
</script>
<template>
<div>
<div class="sidenav">
<router-link to="/discord">Dashboard</router-link>
<router-link to="/discord/dashboard">Dashboard</router-link>
<router-link v-show="botId" :to="`/discord/dashboard/bot/${botId}`">Bot</router-link>
<router-link v-show="guildId" :to="`/discord/dashboard/bot/${botId}/guild/${guildId}`">Guild</router-link>
<div class="hl"></div>
<router-link to="/discord/config">Config</router-link>
<button v-if="!user" @click="login">Se connecter</button>
<button v-if="user" @click="logout">Se déconnecter</button>
</div>
<router-view v-slot="{ Component }">
@@ -28,9 +33,9 @@ const ready: Ref<boolean> | undefined = inject('ready')
</template>
<style scoped>
.sidenav button {
position: absolute;
bottom: 60px;
.hl {
height: 2px;
background-color: rgb(0, 255, 140);
margin: 10px 0;
}
</style>

View File

@@ -1,5 +1,8 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { inject } from 'vue'
import type { Ref } from 'vue'
const ready: Ref<boolean> | undefined = inject('ready')
</script>
<template>
@@ -7,6 +10,7 @@ import { RouterLink, RouterView } from 'vue-router'
<div class="sidenav">
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<div class="hl"></div>
<a href="https://github.com/angels-dev" target="_blank">
<font-awesome-icon :icon="['fab', 'github']" /> GitHub
</a>
@@ -14,8 +18,16 @@ import { RouterLink, RouterView } from 'vue-router'
<router-view v-slot="{ Component }">
<transition name="slide-fade" mode="out-in">
<component :is="Component" />
<component v-if="ready" :is="Component" />
</transition>
</router-view>
</div>
</template>
</template>
<style scoped>
.hl {
height: 2px;
background-color: rgb(0, 255, 140);
margin: 10px 0;
}
</style>