177 lines
4.8 KiB
Vue
177 lines
4.8 KiB
Vue
<script setup lang="ts">
|
|
import { Head } from '@inertiajs/vue3';
|
|
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
|
|
|
const props = defineProps<{
|
|
reason?: string;
|
|
ends_at?: string | null;
|
|
}>();
|
|
|
|
const nowTs = ref<number>(Date.now());
|
|
let ticker: number | undefined;
|
|
|
|
onMounted(() => {
|
|
// Tick every second to update countdown
|
|
ticker = window.setInterval(() => { nowTs.value = Date.now(); }, 1000);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
if (ticker) window.clearInterval(ticker);
|
|
});
|
|
|
|
const endsAtDate = computed<Date | null>(() => {
|
|
if (!props.ends_at) return null;
|
|
const d = new Date(props.ends_at);
|
|
return isNaN(d.getTime()) ? null : d;
|
|
});
|
|
|
|
const remainingMs = computed<number>(() => {
|
|
if (!endsAtDate.value) return 0;
|
|
return Math.max(0, endsAtDate.value.getTime() - nowTs.value);
|
|
});
|
|
|
|
function formatCountdown(ms: number): string {
|
|
const totalSec = Math.floor(ms / 1000);
|
|
const days = Math.floor(totalSec / 86400);
|
|
const hours = Math.floor((totalSec % 86400) / 3600);
|
|
const minutes = Math.floor((totalSec % 3600) / 60);
|
|
const seconds = totalSec % 60;
|
|
const pad = (n: number) => n.toString().padStart(2, '0');
|
|
if (days > 0) return `${days}d ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
|
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
|
}
|
|
|
|
const countdownText = computed<string | null>(() => {
|
|
if (!endsAtDate.value || remainingMs.value <= 0) return null;
|
|
return formatCountdown(remainingMs.value);
|
|
});
|
|
|
|
const exactEndText = computed<string | null>(() => {
|
|
if (!endsAtDate.value) return null;
|
|
try {
|
|
return endsAtDate.value.toLocaleString();
|
|
} catch {
|
|
return endsAtDate.value.toISOString();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<Head title="Account Suspended" />
|
|
<div class="banned-screen">
|
|
<div class="overlay"></div>
|
|
<div class="content">
|
|
<div class="stamp">BANNED</div>
|
|
<h1>Account Suspended</h1>
|
|
<p class="reason" v-if="reason">Reason: {{ reason }}</p>
|
|
<p v-if="countdownText && exactEndText" class="sub">Ends in <strong>{{ countdownText }}</strong> ({{ exactEndText }})</p>
|
|
<p v-else class="sub">Your account has been permanently suspended due to a violation of our Terms of Service.</p>
|
|
|
|
<div class="actions">
|
|
<a href="/logout" class="btn-logout" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">Sign Out</a>
|
|
<form id="logout-form" action="/logout" method="POST" style="display: none;">
|
|
<input type="hidden" name="_token" :value="$page.props.csrf_token">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.banned-screen {
|
|
height: 100vh;
|
|
width: 100vw;
|
|
background-size: cover;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
font-family: 'Impact', sans-serif;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.overlay {
|
|
position: absolute; inset: 0;
|
|
background: radial-gradient(circle, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.9) 100%);
|
|
backdrop-filter: blur(3px);
|
|
}
|
|
|
|
.content {
|
|
position: relative; z-index: 10;
|
|
text-align: center;
|
|
color: #fff;
|
|
animation: zoomIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
}
|
|
|
|
.stamp {
|
|
font-size: 80px;
|
|
color: #ff3e3e;
|
|
border: 8px solid #ff3e3e;
|
|
display: inline-block;
|
|
padding: 10px 40px;
|
|
transform: rotate(-10deg);
|
|
margin-bottom: 30px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 5px;
|
|
opacity: 0;
|
|
animation: stamp-fall 0.4s cubic-bezier(0.6, 0.04, 0.98, 0.335) 0.5s forwards;
|
|
mask-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/8399/grunge.png'); /* Grunge texture effect */
|
|
mask-size: 900px;
|
|
mix-blend-mode: multiply;
|
|
}
|
|
|
|
h1 {
|
|
font-family: 'Helvetica Neue', sans-serif;
|
|
font-size: 32px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 2px;
|
|
margin-bottom: 10px;
|
|
text-shadow: 0 2px 10px rgba(0,0,0,0.8);
|
|
}
|
|
|
|
.reason {
|
|
font-size: 18px;
|
|
color: #ffaaaa;
|
|
background: rgba(255, 0, 0, 0.1);
|
|
padding: 10px 20px;
|
|
border-radius: 8px;
|
|
display: inline-block;
|
|
margin-bottom: 20px;
|
|
border: 1px solid rgba(255, 0, 0, 0.3);
|
|
}
|
|
|
|
.sub {
|
|
font-family: sans-serif;
|
|
font-size: 14px;
|
|
color: #ccc;
|
|
max-width: 400px;
|
|
margin: 0 auto 40px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.btn-logout {
|
|
background: #fff;
|
|
color: #000;
|
|
padding: 12px 30px;
|
|
text-decoration: none;
|
|
font-weight: 900;
|
|
text-transform: uppercase;
|
|
border-radius: 4px;
|
|
transition: 0.2s;
|
|
}
|
|
.btn-logout:hover {
|
|
background: #ccc;
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
@keyframes stamp-fall {
|
|
0% { opacity: 0; transform: rotate(-10deg) scale(3); }
|
|
100% { opacity: 1; transform: rotate(-10deg) scale(1); }
|
|
}
|
|
|
|
@keyframes zoomIn {
|
|
from { opacity: 0; transform: scale(0.8); }
|
|
to { opacity: 1; transform: scale(1); }
|
|
}
|
|
</style>
|