feat: initialize monorepo with full dev team best practices
- Unified monorepo with backend (Express), frontend (Next.js), and devops - Backend: ESLint, Prettier, Jest tests (3 passing), health endpoint, .env.example - Frontend: Fixed build errors, fixed all lint errors (0 remaining), tests passing - DevOps: Docker Compose with PostgreSQL, backend, frontend + healthchecks - CI/CD: 3 GitHub Actions workflows (backend, frontend, docker integration) - DX: Husky pre-commit hooks with smart change detection - Docs: Root README with architecture, CONTRIBUTING.md, PR template Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
"use client";
|
||||
|
||||
import { AuthForm } from "@/components/auth/AuthForm";
|
||||
import { Shield, ArrowLeft } from "lucide-react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { motion } from "framer-motion";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
|
||||
export default function AuthPage() {
|
||||
const searchParams = useSearchParams();
|
||||
const source = searchParams.get("source");
|
||||
const email = searchParams.get("email");
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const router = useRouter();
|
||||
const { user } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
if (user) {
|
||||
router.push("/dashboard");
|
||||
}
|
||||
}, [user, router]);
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen flex items-center justify-center overflow-hidden bg-gradient-to-br from-gray-50 to-white p-4">
|
||||
{/* Animated background elements */}
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className="absolute inset-0 bg-grid-pattern opacity-[0.02]" />
|
||||
|
||||
{mounted && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.5 }}
|
||||
transition={{ duration: 1 }}
|
||||
className="absolute top-0 -left-4 w-[500px] h-[500px] bg-gradient-to-br from-blue-400/20 to-indigo-400/20 rounded-full blur-3xl"
|
||||
style={{ filter: "blur(120px)" }}
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.5 }}
|
||||
transition={{ duration: 1, delay: 0.2 }}
|
||||
className="absolute bottom-0 -right-4 w-[500px] h-[500px] bg-gradient-to-br from-purple-400/20 to-pink-400/20 rounded-full blur-3xl"
|
||||
style={{ filter: "blur(120px)" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Main content container */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
className="relative w-full max-w-lg mx-auto"
|
||||
>
|
||||
{/* Logo and name above the card */}
|
||||
<div className="absolute top-[-40px] left-1/2 transform -translate-x-1/2 flex items-center space-x-2">
|
||||
<Shield className="h-8 w-8 text-blue-600" />
|
||||
<span className="text-2xl font-semibold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-indigo-600">
|
||||
CloudLense
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Glass card effect */}
|
||||
<div className="relative backdrop-blur-xl bg-white/80 rounded-2xl shadow-xl border border-white/20 overflow-hidden">
|
||||
{/* Card header */}
|
||||
<div className="px-8 pt-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<div
|
||||
className="flex items-center space-x-4 cursor-pointer transition-transform transform hover:scale-110"
|
||||
onClick={() => router.push("/")}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<ArrowLeft className="h-4 w-4 text-blue-600" />
|
||||
<span className="text-sm font-medium text-blue-600">
|
||||
Back to Home
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
className="mt-6 mb-4"
|
||||
>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
{source === "hero" ? "Get Started" : "Welcome"}
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
{source === "hero"
|
||||
? "Set up your account in seconds"
|
||||
: "Sign in to continue to your dashboard"}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Form section */}
|
||||
<div className="p-8">
|
||||
<AuthForm initialEmail={email} />
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.4 }}
|
||||
className="px-8 pb-8 text-center"
|
||||
>
|
||||
<p className="text-sm text-gray-500">
|
||||
By continuing, you agree to our{" "}
|
||||
<a href="#" className="text-blue-600 hover:underline">
|
||||
Terms
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a href="#" className="text-blue-600 hover:underline">
|
||||
Privacy Policy
|
||||
</a>
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Decorative elements */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.5 }}
|
||||
className="absolute -z-10 inset-0 border border-blue-100 rounded-2xl -m-2"
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.6 }}
|
||||
className="absolute -z-10 inset-0 border border-blue-50 rounded-2xl -m-4"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user