Add src/app/signin/page.tsx
This commit is contained in:
270
src/app/signin/page.tsx
Normal file
270
src/app/signin/page.tsx
Normal file
@@ -0,0 +1,270 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||
import HeroLogo from "@/components/sections/hero/HeroLogo";
|
||||
import FooterLogoEmphasis from "@/components/sections/footer/FooterLogoEmphasis";
|
||||
import { Mail, Github, Linkedin, Eye, EyeOff } from "lucide-react";
|
||||
|
||||
export default function SignInPage() {
|
||||
const navItems = [
|
||||
{ name: "Dashboard", id: "dashboard" },
|
||||
{ name: "Features", id: "features" },
|
||||
{ name: "Pricing", id: "pricing" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Contact", id: "contact" },
|
||||
{ name: "Sign In", id: "/signin" },
|
||||
{ name: "Sign Up", id: "/signup" },
|
||||
];
|
||||
|
||||
const footerColumns = [
|
||||
{
|
||||
items: [
|
||||
{ label: "Dashboard", href: "/dashboard" },
|
||||
{ label: "Applications", href: "/applications" },
|
||||
{ label: "Interview Prep", href: "/interview-prep" },
|
||||
{ label: "Settings", href: "/settings" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "Pricing", href: "/pricing" },
|
||||
{ label: "Features", href: "#features" },
|
||||
{ label: "FAQ", href: "#faq" },
|
||||
{ label: "Blog", href: "/blog" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "About Us", href: "/about" },
|
||||
{ label: "Contact", href: "/contact" },
|
||||
{ label: "Support", href: "/support" },
|
||||
{ label: "Status", href: "https://status.trakapply.com" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "Privacy Policy", href: "/privacy" },
|
||||
{ label: "Terms of Service", href: "/terms" },
|
||||
{ label: "Cookie Policy", href: "/cookies" },
|
||||
{ label: "Accessibility", href: "/accessibility" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "Twitter", href: "https://twitter.com/trakapply" },
|
||||
{ label: "LinkedIn", href: "https://linkedin.com/company/trakapply" },
|
||||
{ label: "Instagram", href: "https://instagram.com/trakapply" },
|
||||
{ label: "Discord", href: "https://discord.gg/trakapply" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const validateEmail = (value: string) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(value);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const newErrors: { email?: string; password?: string } = {};
|
||||
|
||||
if (!email.trim()) {
|
||||
newErrors.email = "Email is required";
|
||||
} else if (!validateEmail(email)) {
|
||||
newErrors.email = "Please enter a valid email";
|
||||
}
|
||||
|
||||
if (!password.trim()) {
|
||||
newErrors.password = "Password is required";
|
||||
} else if (password.length < 6) {
|
||||
newErrors.password = "Password must be at least 6 characters";
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
|
||||
if (Object.keys(newErrors).length === 0) {
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
console.log("Sign in submitted", { email, password });
|
||||
}, 1500);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="directional-hover"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="soft"
|
||||
contentWidth="mediumLarge"
|
||||
sizing="largeSizeMediumTitles"
|
||||
background="noise"
|
||||
cardStyle="gradient-bordered"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="layered"
|
||||
headingFontWeight="extrabold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
navItems={navItems}
|
||||
button={{ text: "Sign Up Free", href: "/signup" }}
|
||||
brandName="TrakApply"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="hero" data-section="hero">
|
||||
<HeroLogo
|
||||
logoText="Welcome Back"
|
||||
description="Sign in to your TrakApply account to access your job application dashboard and continue your journey to landing your dream job."
|
||||
buttons={[
|
||||
{ text: "Start Free", href: "/signup" },
|
||||
{ text: "Back to Home", href: "/" },
|
||||
]}
|
||||
imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3Aa4DjH4nqtvm1RVk8EeBf92VBo/a-modern-job-application-tracking-dashbo-1772817322817-dd6e3967.png?_wi=1"
|
||||
imageAlt="TrakApply Sign In"
|
||||
showDimOverlay={true}
|
||||
buttonAnimation="slide-up"
|
||||
ariaLabel="TrakApply sign in hero section"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full py-16 md:py-24 lg:py-32">
|
||||
<div className="mx-auto px-4 md:px-6 max-w-md">
|
||||
<div className="bg-white rounded-xl shadow-lg p-8 border border-gray-200">
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-center mb-2 text-gray-900">
|
||||
Sign In
|
||||
</h2>
|
||||
<p className="text-center text-gray-600 mb-8">
|
||||
Access your TrakApply dashboard
|
||||
</p>
|
||||
|
||||
{/* OAuth Options */}
|
||||
<div className="space-y-3 mb-6">
|
||||
<button className="w-full flex items-center justify-center gap-2 py-3 px-4 rounded-lg border border-gray-300 hover:bg-gray-50 transition-colors font-medium text-gray-700">
|
||||
<Mail className="w-5 h-5" />
|
||||
Continue with Google
|
||||
</button>
|
||||
<button className="w-full flex items-center justify-center gap-2 py-3 px-4 rounded-lg border border-gray-300 hover:bg-gray-50 transition-colors font-medium text-gray-700">
|
||||
<Github className="w-5 h-5" />
|
||||
Continue with GitHub
|
||||
</button>
|
||||
<button className="w-full flex items-center justify-center gap-2 py-3 px-4 rounded-lg border border-gray-300 hover:bg-gray-50 transition-colors font-medium text-gray-700">
|
||||
<Linkedin className="w-5 h-5" />
|
||||
Continue with LinkedIn
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="relative mb-6">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-300"></div>
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="px-2 bg-white text-gray-500">Or continue with email</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email/Password Form */}
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Email Address
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
if (errors.email) setErrors({ ...errors, email: undefined });
|
||||
}}
|
||||
placeholder="you@example.com"
|
||||
className={`w-full px-4 py-3 rounded-lg border ${
|
||||
errors.email ? "border-red-500" : "border-gray-300"
|
||||
} focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all`}
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.email}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Password
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
if (errors.password) setErrors({ ...errors, password: undefined });
|
||||
}}
|
||||
placeholder="Enter your password"
|
||||
className={`w-full px-4 py-3 pr-12 rounded-lg border ${
|
||||
errors.password ? "border-red-500" : "border-gray-300"
|
||||
} focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all`}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-3 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="w-5 h-5" />
|
||||
) : (
|
||||
<Eye className="w-5 h-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
{errors.password && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="flex items-center gap-2">
|
||||
<input type="checkbox" className="rounded border-gray-300" />
|
||||
<span className="text-sm text-gray-600">Remember me</span>
|
||||
</label>
|
||||
<Link href="#" className="text-sm text-blue-600 hover:text-blue-700 font-medium">
|
||||
Forgot password?
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full py-3 px-4 rounded-lg bg-gradient-to-r from-[#0e3a29] to-[#35c18b] text-white font-semibold hover:shadow-lg transition-shadow disabled:opacity-70"
|
||||
>
|
||||
{loading ? "Signing in..." : "Sign In"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Sign Up Link */}
|
||||
<p className="text-center text-gray-600 mt-6">
|
||||
Don't have an account?{" "}
|
||||
<Link href="/signup" className="text-blue-600 hover:text-blue-700 font-semibold">
|
||||
Create one
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterLogoEmphasis columns={footerColumns} logoText="TrakApply" />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user