All checks were successful
Build Frontend / Build Frontend (push) Successful in 23s
526 lines
12 KiB
Vue
526 lines
12 KiB
Vue
<template>
|
|
<div class="banned-page">
|
|
<div class="banned-container">
|
|
<!-- Icon -->
|
|
<div class="icon-wrapper">
|
|
<ShieldAlert class="banned-icon" />
|
|
</div>
|
|
|
|
<!-- Title -->
|
|
<h1 class="banned-title">Account Suspended</h1>
|
|
|
|
<!-- Ban Details and Appeal Section -->
|
|
<div class="ban-details-container">
|
|
<div v-if="banInfo" class="ban-details">
|
|
<div class="detail-item">
|
|
<span class="detail-label">Reason:</span>
|
|
<span class="detail-value">{{
|
|
banInfo.reason || "Violation of Terms of Service"
|
|
}}</span>
|
|
</div>
|
|
|
|
<div v-if="banInfo.bannedAt" class="detail-item">
|
|
<span class="detail-label">Banned on:</span>
|
|
<span class="detail-value">{{ formatDate(banInfo.bannedAt) }}</span>
|
|
</div>
|
|
|
|
<div
|
|
v-if="banInfo.bannedUntil && !banInfo.isPermanent"
|
|
class="detail-item"
|
|
>
|
|
<span class="detail-label">Ban expires:</span>
|
|
<span class="detail-value">{{
|
|
formatDate(banInfo.bannedUntil)
|
|
}}</span>
|
|
</div>
|
|
|
|
<div v-if="banInfo.isPermanent" class="detail-item permanent-ban">
|
|
<AlertCircle :size="18" />
|
|
<span>This is a permanent ban</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="appeal-section">
|
|
<p class="appeal-text">
|
|
If you believe this ban was made in error, you can submit an appeal.
|
|
</p>
|
|
<a href="/support" class="appeal-btn">
|
|
<Mail :size="20" />
|
|
<span>Contact Support</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logout Button -->
|
|
<button @click="handleLogout" class="logout-btn">
|
|
<LogOut :size="20" />
|
|
<span>Logout</span>
|
|
</button>
|
|
|
|
<!-- Footer Info -->
|
|
<div class="banned-footer">
|
|
<p>For more information, please review our</p>
|
|
<div class="footer-links">
|
|
<a href="/terms" class="footer-link">Terms of Service</a>
|
|
<span class="separator">•</span>
|
|
<a href="/privacy" class="footer-link">Privacy Policy</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Social Links -->
|
|
<div class="social-links">
|
|
<a
|
|
:href="socialLinks.twitter"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="social-link"
|
|
aria-label="X (Twitter)"
|
|
>
|
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
<a
|
|
:href="socialLinks.discord"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="social-link"
|
|
aria-label="Discord"
|
|
>
|
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from "vue";
|
|
import { useRouter } from "vue-router";
|
|
import { useAuthStore } from "@/stores/auth";
|
|
import { ShieldAlert, AlertCircle, Info, Mail, LogOut } from "lucide-vue-next";
|
|
import axios from "@/utils/axios";
|
|
|
|
const router = useRouter();
|
|
const authStore = useAuthStore();
|
|
|
|
const socialLinks = ref({
|
|
twitter: "https://x.com",
|
|
discord: "https://discord.gg",
|
|
});
|
|
|
|
const banInfo = computed(() => {
|
|
if (!authStore.user) return null;
|
|
|
|
return {
|
|
reason: authStore.user.ban?.reason,
|
|
bannedUntil: authStore.user.ban?.expires,
|
|
isPermanent:
|
|
authStore.user.ban?.permanent || !authStore.user.ban?.bannedUntil,
|
|
};
|
|
});
|
|
|
|
const formatDate = (date) => {
|
|
if (!date) return "";
|
|
|
|
const d = new Date(date);
|
|
return d.toLocaleString("en-US", {
|
|
weekday: "long",
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
hour: "numeric",
|
|
minute: "numeric",
|
|
hour12: true,
|
|
});
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
await authStore.logout();
|
|
router.push("/");
|
|
};
|
|
|
|
const fetchSocialLinks = async () => {
|
|
try {
|
|
const response = await axios.get("/api/config/public");
|
|
if (response.data.success && response.data.config.social) {
|
|
const social = response.data.config.social;
|
|
if (social.twitter) {
|
|
socialLinks.value.twitter = social.twitter;
|
|
}
|
|
if (social.discord) {
|
|
socialLinks.value.discord = social.discord;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to fetch social links:", error);
|
|
// Keep default values if fetch fails
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
// If user is not banned, redirect to home
|
|
if (!authStore.isBanned) {
|
|
router.push("/");
|
|
}
|
|
|
|
// Fetch social links
|
|
fetchSocialLinks();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.banned-page {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
|
padding: 2rem;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.banned-page::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: radial-gradient(
|
|
circle at 20% 50%,
|
|
rgba(239, 68, 68, 0.1) 0%,
|
|
transparent 50%
|
|
),
|
|
radial-gradient(
|
|
circle at 80% 80%,
|
|
rgba(220, 38, 38, 0.1) 0%,
|
|
transparent 50%
|
|
);
|
|
animation: pulse 4s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%,
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
.banned-container {
|
|
position: relative;
|
|
z-index: 1;
|
|
max-width: 600px;
|
|
width: 100%;
|
|
text-align: center;
|
|
background: rgba(30, 30, 46, 0.9);
|
|
backdrop-filter: blur(20px);
|
|
border-radius: 1.5rem;
|
|
padding: 3rem 2rem;
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
}
|
|
|
|
.icon-wrapper {
|
|
margin-bottom: 2rem;
|
|
display: inline-block;
|
|
}
|
|
|
|
.banned-icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
color: #ef4444;
|
|
animation: shake 0.5s ease-in-out;
|
|
}
|
|
|
|
@keyframes shake {
|
|
0%,
|
|
100% {
|
|
transform: translateX(0);
|
|
}
|
|
10%,
|
|
30%,
|
|
50%,
|
|
70%,
|
|
90% {
|
|
transform: translateX(-5px);
|
|
}
|
|
20%,
|
|
40%,
|
|
60%,
|
|
80% {
|
|
transform: translateX(5px);
|
|
}
|
|
}
|
|
|
|
.banned-title {
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: #ef4444;
|
|
margin-bottom: 1rem;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.banned-message {
|
|
font-size: 1.125rem;
|
|
color: #d1d5db;
|
|
margin-bottom: 2rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.detail-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.75rem 0;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.detail-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.detail-label {
|
|
font-size: 0.875rem;
|
|
color: #9ca3af;
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.detail-value {
|
|
font-size: 0.9375rem;
|
|
color: #ffffff;
|
|
font-weight: 600;
|
|
text-align: right;
|
|
}
|
|
|
|
.permanent-ban {
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
color: #ef4444;
|
|
font-weight: 600;
|
|
font-size: 0.9375rem;
|
|
}
|
|
|
|
.info-box {
|
|
display: flex;
|
|
gap: 1rem;
|
|
padding: 1.25rem;
|
|
background: rgba(59, 130, 246, 0.1);
|
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
border-radius: 1rem;
|
|
margin-bottom: 2rem;
|
|
text-align: left;
|
|
}
|
|
|
|
.info-icon {
|
|
flex-shrink: 0;
|
|
color: #3b82f6;
|
|
margin-top: 0.125rem;
|
|
}
|
|
|
|
.info-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.info-title {
|
|
font-size: 0.9375rem;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
margin: 0 0 0.5rem 0;
|
|
}
|
|
|
|
.info-text {
|
|
font-size: 0.875rem;
|
|
color: #d1d5db;
|
|
margin: 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.ban-details-container {
|
|
background: rgba(239, 68, 68, 0.1);
|
|
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
border-radius: 1rem;
|
|
padding: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.ban-details {
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 1.5rem;
|
|
border-bottom: 1px solid rgba(239, 68, 68, 0.2);
|
|
text-align: left;
|
|
}
|
|
|
|
.appeal-section {
|
|
margin: 0;
|
|
padding: 0;
|
|
background: none;
|
|
border: none;
|
|
text-align: center;
|
|
}
|
|
|
|
.appeal-text {
|
|
font-size: 0.9375rem;
|
|
color: #d1d5db;
|
|
margin: 0 0 1rem 0;
|
|
text-align: center;
|
|
}
|
|
|
|
.appeal-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.75rem;
|
|
padding: 0.875rem 2rem;
|
|
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
|
color: white;
|
|
text-decoration: none;
|
|
border-radius: 0.5rem;
|
|
font-weight: 600;
|
|
font-size: 1rem;
|
|
transition: all 0.3s;
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
|
border: 1px solid rgba(59, 130, 246, 0.5);
|
|
width: 100%;
|
|
max-width: 300px;
|
|
}
|
|
|
|
.appeal-btn:hover {
|
|
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.5);
|
|
}
|
|
|
|
.appeal-btn:active {
|
|
transform: translateY(0);
|
|
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);
|
|
}
|
|
|
|
.logout-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.75rem 1.5rem;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
color: #d1d5db;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 0.5rem;
|
|
font-weight: 600;
|
|
font-size: 0.9375rem;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: #ffffff;
|
|
border-color: rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.banned-footer {
|
|
margin-top: 2rem;
|
|
padding-top: 2rem;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.banned-footer p {
|
|
color: #9ca3af;
|
|
font-size: 0.875rem;
|
|
margin: 0 0 0.5rem 0;
|
|
}
|
|
|
|
.footer-links {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.footer-link {
|
|
color: #3b82f6;
|
|
text-decoration: none;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.footer-link:hover {
|
|
color: #60a5fa;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.separator {
|
|
color: #6b7280;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.social-links {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.social-link {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 0.75rem;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
color: #9ca3af;
|
|
transition: all 0.2s;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.social-link:hover {
|
|
background: rgba(59, 130, 246, 0.2);
|
|
color: #3b82f6;
|
|
border-color: rgba(59, 130, 246, 0.3);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.banned-container {
|
|
padding: 2rem 1.5rem;
|
|
}
|
|
|
|
.banned-title {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.banned-icon {
|
|
width: 60px;
|
|
height: 60px;
|
|
}
|
|
|
|
.detail-item {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.detail-value {
|
|
text-align: left;
|
|
}
|
|
|
|
.appeal-btn {
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
</style>
|