Add src/app/signin/page.tsx

This commit is contained in:
2026-03-06 18:54:27 +00:00
parent e563eeb9bd
commit 1ddfc0e1f6

270
src/app/signin/page.tsx Normal file
View 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>
);
}