agent-ecosystem/landing/error.vue

270 lines
6.1 KiB
Vue

<script setup lang="ts">
import { mdiHome } from '@mdi/js'
import type { NuxtError } from "#app";
const props = defineProps<{
error: NuxtError;
}>();
const { t } = useI18n();
const config = useRuntimeConfig();
const siteUrl = ((config.public.siteUrl as string) || "https://777genius.github.io/agent-teams-ai").replace(/\/+$/, "");
const ogImage = `${siteUrl}/og-image-agent-teams-v6.png`;
const statusCode = computed(() => props.error?.statusCode || 404);
const isNotFound = computed(() => statusCode.value === 404);
const errorTitle = computed(() => (isNotFound.value ? t("error.notFoundTitle") : t("error.genericTitle")));
const errorDescription = computed(() => (isNotFound.value ? t("error.notFoundDescription") : t("error.genericDescription")));
useSeoMeta({
title: errorTitle,
description: errorDescription,
robots: "noindex, nofollow",
ogTitle: errorTitle,
ogDescription: errorDescription,
ogType: "website",
ogSiteName: "Agent Teams",
ogImage,
ogImageType: "image/png",
ogImageWidth: "1200",
ogImageHeight: "630",
ogImageAlt: "Agent Teams - AI agent orchestration",
twitterCard: "summary_large_image",
twitterTitle: errorTitle,
twitterDescription: errorDescription,
twitterImage: ogImage,
twitterImageAlt: "Agent Teams - AI agent orchestration"
});
const handleGoHome = () => clearError({ redirect: "/" });
</script>
<template>
<v-app>
<div class="error-page">
<!-- Фоновые орбы -->
<div class="error-page__bg">
<div class="error-page__orb error-page__orb--1" />
<div class="error-page__orb error-page__orb--2" />
<div class="error-page__grid-pattern" />
</div>
<v-container class="error-page__container">
<!-- Код ошибки -->
<span class="error-page__code">{{ statusCode }}</span>
<!-- Заголовок -->
<h1 class="error-page__title">
{{ isNotFound ? t("error.notFoundTitle") : t("error.genericTitle") }}
</h1>
<!-- Описание -->
<p class="error-page__description">
{{ isNotFound ? t("error.notFoundDescription") : t("error.genericDescription") }}
</p>
<!-- Кнопка -->
<v-btn
size="large"
color="primary"
class="error-page__btn"
@click="handleGoHome"
>
<v-icon start :icon="mdiHome" />
{{ t("error.goHome") }}
</v-btn>
</v-container>
</div>
</v-app>
</template>
<style scoped>
.error-page {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
/* ─── Background ─── */
.error-page__bg {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
}
.error-page__orb {
position: absolute;
border-radius: 50%;
filter: blur(120px);
opacity: 0.1;
}
.error-page__orb--1 {
width: 600px;
height: 600px;
background: #6366f1;
top: -200px;
right: -100px;
animation: orbDrift1 20s ease-in-out infinite;
}
.error-page__orb--2 {
width: 450px;
height: 450px;
background: #ec4899;
bottom: -150px;
left: -80px;
animation: orbDrift2 25s ease-in-out infinite;
}
.error-page__grid-pattern {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(99, 102, 241, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(99, 102, 241, 0.03) 1px, transparent 1px);
background-size: 48px 48px;
mask-image: radial-gradient(ellipse 80% 70% at 50% 50%, black, transparent);
}
@keyframes orbDrift1 {
0%, 100% { transform: translate(0, 0); }
33% { transform: translate(25px, 15px); }
66% { transform: translate(-15px, 10px); }
}
@keyframes orbDrift2 {
0%, 100% { transform: translate(0, 0); }
33% { transform: translate(-20px, -10px); }
66% { transform: translate(10px, -20px); }
}
/* ─── Content ─── */
.error-page__container {
position: relative;
z-index: 1;
text-align: center;
max-width: 600px;
}
.error-page__code {
display: block;
font-size: 8rem;
font-weight: 800;
letter-spacing: -0.04em;
line-height: 1;
margin-bottom: 16px;
background: linear-gradient(135deg, #6366f1 0%, #ec4899 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: fadeIn 0.6s ease both;
}
.error-page__title {
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 12px;
animation: fadeIn 0.6s ease both;
animation-delay: 0.1s;
}
.error-page__description {
font-size: 1.1rem;
line-height: 1.6;
opacity: 0.6;
margin-bottom: 36px;
animation: fadeIn 0.6s ease both;
animation-delay: 0.2s;
}
.error-page__btn {
font-weight: 600 !important;
animation: fadeIn 0.6s ease both;
animation-delay: 0.3s;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ─── Dark ─── */
.v-theme--dark .error-page__orb {
opacity: 0.14;
}
.v-theme--dark .error-page__orb--1 {
background: #818cf8;
}
.v-theme--dark .error-page__orb--2 {
background: #f472b6;
}
.v-theme--dark .error-page__code {
background: linear-gradient(135deg, #a5b4fc 0%, #f9a8d4 100%);
-webkit-background-clip: text;
background-clip: text;
}
.v-theme--dark .error-page__title {
color: #e2e8f0;
}
.v-theme--dark .error-page__description {
color: #94a3b8;
opacity: 0.8;
}
.v-theme--dark .error-page__grid-pattern {
background-image:
linear-gradient(rgba(129, 140, 248, 0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(129, 140, 248, 0.04) 1px, transparent 1px);
}
/* ─── Light ─── */
.v-theme--light .error-page__orb {
opacity: 0.06;
}
.v-theme--light .error-page__code {
background: linear-gradient(135deg, #4f46e5 0%, #db2777 100%);
-webkit-background-clip: text;
background-clip: text;
}
.v-theme--light .error-page__title {
color: #1e293b;
}
.v-theme--light .error-page__description {
color: #475569;
}
/* ─── Responsive ─── */
@media (max-width: 600px) {
.error-page__code {
font-size: 5rem;
}
.error-page__title {
font-size: 1.5rem;
}
.error-page__description {
font-size: 0.95rem;
}
}
</style>