@@ -136,7 +121,6 @@ export default function VpsDetailPage() {
);
}
- // If we have no VPS data at all, show a loading message
if (!vps) {
return (
diff --git a/src/app/forgot-password/page.tsx b/src/app/forgot-password/page.tsx
index be226bb..4814cb3 100644
--- a/src/app/forgot-password/page.tsx
+++ b/src/app/forgot-password/page.tsx
@@ -14,13 +14,11 @@ export default function ForgotPasswordPage() {
e.preventDefault();
setError(null);
- // Validação básica do e-mail
if (!email.trim()) {
setError("Por favor, digite seu endereço de e-mail");
return;
}
- // Validação de formato de e-mail usando regex simples
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
setError("Por favor, digite um endereço de e-mail válido");
@@ -30,13 +28,11 @@ export default function ForgotPasswordPage() {
setIsLoading(true);
try {
- // Obter URL da API do ambiente
const apiUrl =
process.env.NEXT_PUBLIC_API_URL ||
process.env.API_URL ||
"http://localhost:4000";
- // Enviar solicitação de recuperação de senha
const response = await fetch(`${apiUrl}/v1/public/user/reset-password`, {
method: "POST",
headers: {
diff --git a/src/app/globals.css b/src/app/globals.css
index a5e8b1b..9ad8559 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -14,9 +14,9 @@
}
}
+/* Animações */
@keyframes float {
- 0%,
- 100% {
+ 0%, 100% {
transform: translateY(0) rotate(0deg);
}
25% {
@@ -31,8 +31,7 @@
}
@keyframes pulse-glow {
- 0%,
- 100% {
+ 0%, 100% {
opacity: 0.3;
filter: blur(80px);
}
@@ -67,8 +66,7 @@
}
@keyframes morph {
- 0%,
- 100% {
+ 0%, 100% {
border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
}
25% {
@@ -83,8 +81,7 @@
}
@keyframes rotate3d-slow {
- 0%,
- 100% {
+ 0%, 100% {
transform: perspective(1000px) rotateX(15deg) rotateY(-10deg);
}
50% {
@@ -93,8 +90,7 @@
}
@keyframes rotate3d-slow-reverse {
- 0%,
- 100% {
+ 0%, 100% {
transform: perspective(1000px) rotateX(-15deg) rotateY(10deg);
}
50% {
@@ -104,12 +100,43 @@
@keyframes grid-move {
0% {
- transform: perspective(1000px) rotateX(60deg) translateZ(-100px)
- translateY(0) scale(2);
+ transform: perspective(1000px) rotateX(60deg) translateZ(-100px) translateY(0) scale(2);
}
100% {
- transform: perspective(1000px) rotateX(60deg) translateZ(-100px)
- translateY(-50px) scale(2);
+ transform: perspective(1000px) rotateX(60deg) translateZ(-100px) translateY(-50px) scale(2);
+ }
+}
+
+@keyframes bounce-button {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-4px);
+ }
+}
+
+@keyframes shake {
+ 0%, 100% {
+ transform: translateX(0);
+ }
+ 25% {
+ transform: translateX(-4px);
+ }
+ 75% {
+ transform: translateX(4px);
+ }
+}
+
+@keyframes rotate-color {
+ 0% {
+ filter: hue-rotate(0deg);
+ }
+ 50% {
+ filter: hue-rotate(15deg);
+ }
+ 100% {
+ filter: hue-rotate(0deg);
}
}
@@ -147,16 +174,58 @@
animation: grid-move 15s linear infinite alternate;
}
+ /* Elementos 3D */
+ .card-3d {
+ @apply relative overflow-hidden rounded-xl transition-all duration-300;
+ transform-style: preserve-3d;
+ perspective: 1000px;
+ box-shadow: 0 10px 30px -15px rgba(255, 0, 0, 0.25);
+ background: linear-gradient(135deg, rgba(255, 60, 56, 0.1), rgba(20, 20, 20, 0.8));
+ border: 1px solid rgba(255, 60, 56, 0.2);
+ backdrop-filter: blur(5px);
+ }
+
+ .card-3d:hover {
+ transform: translateY(-5px) rotateX(5deg);
+ box-shadow: 0 15px 35px -15px rgba(255, 60, 56, 0.4);
+ border: 1px solid rgba(255, 60, 56, 0.4);
+ }
+
+ .btn-3d {
+ @apply relative overflow-hidden font-bold text-white rounded-lg transition-all duration-300 px-6 py-3;
+ background: linear-gradient(135deg, #ff3c38, #be1e2d);
+ box-shadow:
+ 0 4px 0 0 #be1e2d,
+ 0 10px 15px -3px rgba(190, 30, 45, 0.3);
+ transform-style: preserve-3d;
+ transform: translateZ(0);
+ }
+
+ .btn-3d:hover {
+ transform: translateY(-2px);
+ box-shadow:
+ 0 6px 0 0 #be1e2d,
+ 0 15px 20px -3px rgba(190, 30, 45, 0.4);
+ animation: rotate-color 3s infinite;
+ }
+
+ .btn-3d:active {
+ transform: translateY(2px);
+ box-shadow:
+ 0 2px 0 0 #be1e2d,
+ 0 8px 10px -5px rgba(190, 30, 45, 0.3);
+ }
+
.btn-primary {
- @apply bg-primary text-white px-6 py-3 rounded-lg font-semibold hover:bg-primary/90 transition-all duration-300 transform hover:scale-105 active:scale-95;
+ @apply bg-gradient-to-r from-red-600 to-red-700 text-white px-6 py-3 rounded-lg font-semibold transition-all duration-300 transform hover:scale-105 active:scale-95 hover:shadow-lg hover:shadow-red-600/30;
}
.btn-secondary {
- @apply bg-transparent border-2 border-support text-support px-6 py-3 rounded-lg font-semibold hover:bg-support hover:text-secondary transition-all duration-300;
+ @apply bg-transparent border-2 border-red-500 text-red-500 px-6 py-3 rounded-lg font-semibold hover:bg-red-500 hover:text-white transition-all duration-300;
}
.card {
- @apply bg-secondary/50 border border-detail rounded-xl p-6 backdrop-blur-sm hover:border-support/50 transition-all duration-300;
+ @apply bg-slate-900/80 border border-red-500/30 rounded-xl p-6 backdrop-blur-sm hover:border-red-500/50 transition-all duration-300 hover:shadow-lg hover:shadow-red-600/20;
}
.section-padding {
@@ -168,7 +237,7 @@
}
.text-gradient {
- @apply text-[#ff3c38];
+ @apply text-transparent bg-clip-text bg-gradient-to-r from-red-500 to-red-700;
}
.glow-effect {
@@ -176,12 +245,20 @@
}
.tech-border {
- background: linear-gradient(
- 45deg,
- transparent,
- rgba(0, 191, 255, 0.1),
- transparent
- );
- border: 1px solid rgba(141, 153, 174, 0.2);
+ background: linear-gradient(45deg, transparent, rgba(255, 60, 56, 0.1), transparent);
+ border: 1px solid rgba(255, 60, 56, 0.2);
+ }
+
+ .cartoon-bg {
+ background-image: url('/images/bg-cartoon.svg');
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ }
+
+ .animated-gradient-red {
+ background: linear-gradient(135deg, #ff3c38, #be1e2d, #ff3c38);
+ background-size: 200% 200%;
+ animation: move-gradient 10s ease infinite;
}
}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 86ec5d6..06f153a 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,5 +1,5 @@
import type { Metadata, Viewport } from "next";
-import { Inter } from "next/font/google";
+import { Rubik, Exo_2, Orbitron } from "next/font/google";
import "./globals.css";
import { AuthProvider } from "@/contexts/AuthContext";
import ConsoleWarning from "@/components/layout/ConsoleWarning";
@@ -9,7 +9,9 @@ import UtmTracker from "@/components/tracking/UtmTracker";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { Analytics } from "@vercel/analytics/react";
-const inter = Inter({ subsets: ["latin"] });
+const rubik = Rubik({ subsets: ["latin"], variable: "--font-rubik" });
+const exo2 = Exo_2({ subsets: ["latin"], variable: "--font-exo2" });
+const orbitron = Orbitron({ subsets: ["latin"], variable: "--font-orbitron" });
export const viewport: Viewport = {
width: "device-width",
@@ -52,7 +54,7 @@ export default function RootLayout({
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index bbddf9f..503bc6e 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -28,7 +28,6 @@ function LoginContent() {
const [activeProvider, setActiveProvider] = useState<"google" | "discord" | null>(null);
const [error, setError] = useState("");
- // Lê erro da query string para exibir mensagem amigável
useEffect(() => {
if (typeof window !== "undefined") {
const params = new URLSearchParams(window.location.search);
@@ -55,11 +54,9 @@ function LoginContent() {
const token = params.get("token");
if (method === "magiclink" && token) {
- // Remover os parâmetros da URL para evitar problemas de segurança
const cleanUrl = window.location.pathname;
window.history.replaceState({}, document.title, cleanUrl);
- // Tentar fazer login com o token magic link
setIsLoading(true);
setError("");
@@ -71,7 +68,6 @@ function LoginContent() {
setError(result.message || "Link mágico inválido ou expirado");
} else {
setSuccessMessage("Login realizado com sucesso!");
- // O redirecionamento é feito pelo useEffect que monitora isAuthenticated
}
} catch (err) {
console.error("Magic link login error:", err);
@@ -138,7 +134,6 @@ function LoginContent() {
}
};
- // Social OAuth login handler
const handleSocialLogin = async (provider: "google" | "discord") => {
setError("");
setIsLoading(true);
@@ -149,7 +144,6 @@ function LoginContent() {
console.error(`${provider} login error:`, error);
setError(`Erro ao conectar com ${provider === "google" ? "Google" : "Discord"}`);
} finally {
- // Não definimos setIsLoading(false) aqui porque o usuário será redirecionado
}
};
@@ -271,7 +265,7 @@ function LoginContent() {