Updated the Login

This commit is contained in:
2026-06-18 16:10:02 +02:00
parent 7a42e3c41c
commit b19a00c6c2
4 changed files with 623 additions and 220 deletions
+4 -2
View File
@@ -2,6 +2,7 @@
import Link from "next/link"
import { motion } from "framer-motion"
import { use } from "react"
import { PageHeader } from "@/components/shared/page-header"
import { LeadDetailsCard } from "@/components/leads/lead-details-card"
import { LeadStatusBadge } from "@/components/leads/lead-status-badge"
@@ -19,8 +20,9 @@ import { getLeadById } from "@/data/leads"
import { getNotesByLeadId } from "@/data/notes"
import { ArrowLeft, Edit, ExternalLink } from "lucide-react"
export default function LeadDetailsPage({ params }: { params: { id: string } }) {
const lead = getLeadById(params.id)
export default function LeadDetailsPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = use(params)
const lead = getLeadById(id)
const leadNotes = lead ? getNotesByLeadId(lead.id) : []
if (!lead) {
+68 -70
View File
@@ -2,74 +2,72 @@
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 210 40% 98%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--sidebar: 222.2 84% 4.9%;
--sidebar-foreground: 210 40% 98%;
--sidebar-primary: 217.2 91.2% 59.8%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 217.2 32.6% 17.5%;
--sidebar-accent-foreground: 210 40% 98%;
--sidebar-border: 217.2 32.6% 17.5%;
--sidebar-ring: 224.3 76.3% 48%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
--sidebar: 222.2 84% 4.9%;
--sidebar-foreground: 210 40% 98%;
--sidebar-primary: 217.2 91.2% 59.8%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 217.2 32.6% 17.5%;
--sidebar-accent-foreground: 210 40% 98%;
--sidebar-border: 217.2 32.6% 17.5%;
--sidebar-ring: 224.3 76.3% 48%;
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
}
:root {
--background: 210 40% 98%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--sidebar: 222.2 84% 4.9%;
--sidebar-foreground: 210 40% 98%;
--sidebar-primary: 217.2 91.2% 59.8%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 217.2 32.6% 17.5%;
--sidebar-accent-foreground: 210 40% 98%;
--sidebar-border: 217.2 32.6% 17.5%;
--sidebar-ring: 224.3 76.3% 48%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
--sidebar: 222.2 84% 4.9%;
--sidebar-foreground: 210 40% 98%;
--sidebar-primary: 217.2 91.2% 59.8%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 217.2 32.6% 17.5%;
--sidebar-accent-foreground: 210 40% 98%;
--sidebar-border: 217.2 32.6% 17.5%;
--sidebar-ring: 224.3 76.3% 48%;
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
}
+1 -1
View File
@@ -25,7 +25,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<body className={`${inter.variable} min-h-screen antialiased`}>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
{children}
<Toaster />
</ThemeProvider>
+550 -147
View File
@@ -1,15 +1,18 @@
"use client"
import { useState } from "react"
import { useState, useEffect, useRef } from "react"
import { useRouter } from "next/navigation"
import { motion } from "framer-motion"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Checkbox } from "@/components/ui/checkbox"
import { COMPANY_NAME } from "@/lib/constants"
import { Eye, EyeOff, Loader2 } from "lucide-react"
const waves = [
{ a: 16, f: 0.011, s: 0.018, y: 0.38, fill: "rgba(27,176,206,0.18)" },
{ a: 20, f: 0.008, s: 0.013, y: 0.52, fill: "rgba(27,176,206,0.25)" },
{ a: 12, f: 0.015, s: 0.025, y: 0.28, fill: "rgba(180,192,210,0.06)" },
{ a: 26, f: 0.006, s: 0.010, y: 0.65, fill: "rgba(180,192,210,0.15)" },
{ a: 10, f: 0.019, s: 0.030, y: 0.20, fill: "rgba(27,176,206,0.10)" },
]
export default function LoginPage() {
const router = useRouter()
const [email, setEmail] = useState("admin@coastalit.com")
@@ -17,175 +20,575 @@ export default function LoginPage() {
const [showPassword, setShowPassword] = useState(false)
const [loading, setLoading] = useState(false)
const [remember, setRemember] = useState(false)
const [counts, setCounts] = useState({ leads: 0, conversion: 0, users: 0 })
const counterStarted = useRef(false)
const canvasRef = useRef<HTMLCanvasElement>(null)
const starsCanvasRightRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext("2d")
if (!ctx) return
let animId: number
let time = 0
const resize = () => {
const parent = canvas.parentElement!
const rect = parent.getBoundingClientRect()
const dpr = window.devicePixelRatio || 1
canvas.width = rect.width * dpr
canvas.height = 200 * dpr
canvas.style.width = rect.width + "px"
canvas.style.height = "200px"
ctx!.setTransform(dpr, 0, 0, dpr, 0, 0)
}
resize()
window.addEventListener("resize", resize)
const draw = () => {
const w = canvas.width / (window.devicePixelRatio || 1)
const h = 200
ctx!.clearRect(0, 0, w, h)
for (const wave of waves) {
ctx!.beginPath()
ctx!.moveTo(0, h)
for (let x = 0; x <= w; x++) {
const y = wave.y * h + Math.sin(x * wave.f + time * wave.s) * wave.a
ctx!.lineTo(x, y)
}
ctx!.lineTo(w, h)
ctx!.closePath()
ctx!.fillStyle = wave.fill
ctx!.fill()
}
time += 1
animId = requestAnimationFrame(draw)
}
animId = requestAnimationFrame(draw)
return () => {
cancelAnimationFrame(animId)
window.removeEventListener("resize", resize)
}
}, [])
useEffect(() => {
const canvases = [starsCanvasRightRef.current].filter(Boolean) as HTMLCanvasElement[]
if (canvases.length === 0) return
const stars = Array.from({ length: 312 }, () => ({
x: Math.random(),
y: Math.random(),
r: 0.2 + Math.random() * 0.6,
baseOpacity: 0.12 + Math.random() * 0.43,
speed: 0.003 + Math.random() * 0.015,
phase: Math.random() * Math.PI * 2,
driftX: (Math.random() - 0.5) * 0.00003,
driftY: (Math.random() - 0.5) * 0.00003,
}))
let animId: number
let time = 0
const resize = () => {
for (const c of canvases) {
const dpr = window.devicePixelRatio || 1
const rect = c.getBoundingClientRect()
c.width = rect.width * dpr
c.height = rect.height * dpr
const ctx = c.getContext("2d")
if (ctx) ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
}
}
resize()
const ros = canvases.map((c) => {
const ro = new ResizeObserver(() => resize())
ro.observe(c.parentElement!)
return ro
})
window.addEventListener("resize", resize)
const draw = () => {
time += 1
for (const c of canvases) {
const dpr = window.devicePixelRatio || 1
const w = c.width / dpr
const h = c.height / dpr
const ctx = c.getContext("2d")
if (!ctx) continue
ctx.clearRect(0, 0, w, h)
for (const star of stars) {
const x = ((star.x + time * star.driftX) % 1) * w
const y = ((star.y + time * star.driftY) % 1) * h
const opacity = star.baseOpacity * (0.5 + 0.5 * Math.sin(time * star.speed + star.phase))
ctx.beginPath()
ctx.arc(x, y, star.r, 0, Math.PI * 2)
ctx.fillStyle = `rgba(255,255,255,${opacity})`
ctx.fill()
if (star.r > 0.5) {
ctx.beginPath()
ctx.arc(x, y, star.r * 1.8, 0, Math.PI * 2)
ctx.fillStyle = `rgba(255,255,255,${opacity * 0.06})`
ctx.fill()
}
}
}
animId = requestAnimationFrame(draw)
}
animId = requestAnimationFrame(draw)
return () => {
cancelAnimationFrame(animId)
ros.forEach((ro) => ro.disconnect())
window.removeEventListener("resize", resize)
}
}, [])
useEffect(() => {
if (counterStarted.current) return
counterStarted.current = true
const targets = { leads: 1247, conversion: 40, users: 83 }
const steps = 150
const delayTimer = setTimeout(() => {
let step = 0
const interval = setInterval(() => {
step++
if (step >= steps) {
clearInterval(interval)
setCounts(targets)
return
}
setCounts({
leads: Math.min(Math.floor((targets.leads / steps) * step), targets.leads),
conversion: Math.min(Math.floor((targets.conversion / steps) * step), targets.conversion),
users: Math.min(Math.floor((targets.users / steps) * step), targets.users),
})
}, 15)
}, 400)
return () => clearTimeout(delayTimer)
}, [])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
await new Promise((resolve) => setTimeout(resolve, 1200))
router.push("/dashboard")
}
return (
<div className="flex min-h-screen">
{/* Left - Brand panel */}
<div className="relative hidden flex-1 flex-col justify-between bg-gradient-to-br from-[#1e3a8a] via-[#2563eb] to-[#3b82f6] p-12 text-white lg:flex">
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wNSI+PHBhdGggZD0iTTM2IDM0djItSDI0di0yaDEyek0zNiAyNHYySDI0di0yaDEyeiIvPjwvZz48L2c+PC9zdmc+')] opacity-30" />
<>
<style>{`
.left-panel {
background: #0a0a0f;
position: relative;
overflow: hidden;
}
.right-panel {
background: #111118;
width: 420px;
flex: none;
position: relative;
}
.panel-divider {
width: 1px;
background: rgba(180, 192, 210, 0.1);
flex: none;
}
.growth-word {
position: relative;
color: #1BB0CE;
}
.growth-word::after {
content: "";
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background: #1BB0CE;
animation: pulseUnderline 2.5s ease-in-out infinite;
}
@keyframes pulseUnderline {
0%, 100% { background-color: #1BB0CE; }
50% { background-color: rgba(180, 192, 210, 0.6); }
}
.stats-row {
display: flex;
justify-content: center;
margin-top: 32px;
gap: 0;
}
.stat {
flex: 1;
max-width: 120px;
text-align: center;
position: relative;
padding-top: 12px;
}
.stat::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg, #1BB0CE, rgba(180,192,210,1), #1BB0CE);
background-size: 200% 100%;
animation: statSweep 2.5s ease-in-out infinite;
}
.stat:nth-child(2)::before {
animation-delay: 0.6s;
}
.stat:nth-child(3)::before {
animation-delay: 1.2s;
}
@keyframes statSweep {
0% { background-position: 0% 0; }
100% { background-position: -200% 0; }
}
.stat-number {
font-size: 24px;
font-weight: 700;
color: #1BB0CE;
line-height: 1.2;
}
.stat-label {
font-size: 11px;
color: rgba(232,232,239,0.3);
margin-top: 4px;
letter-spacing: 0.3px;
}
.testimonial {
border-left: 2px solid rgba(27,176,206,0.25);
background: rgba(27,176,206,0.06);
padding: 10px 14px;
}
.stars-canvas {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 0;
}
.wave-canvas {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 200px;
z-index: 0;
pointer-events: none;
background: #0a0a0f;
}
.accent-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, #1BB0CE, rgba(232,232,239,0.15), transparent);
animation: pulseAccent 3s ease-in-out infinite;
}
@keyframes pulseAccent {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
.input-sheen {
position: relative;
overflow: hidden;
border-radius: 4px;
}
.input-sheen::before {
content: "";
position: absolute;
top: -10%;
left: -100%;
width: 60%;
height: 120%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent);
transform: rotate(-15deg);
animation: sheenFloat 3.2s ease-in-out infinite;
pointer-events: none;
z-index: 2;
}
.input-sheen-delayed::before {
animation-delay: 1s;
}
.input-sheen:focus-within::before {
animation: none;
opacity: 0;
}
@keyframes sheenFloat {
0% { left: -100%; }
100% { left: 200%; }
}
.login-input {
width: 100%;
height: 44px;
background: linear-gradient(135deg, #0d0d15, #151520, #0d0d15);
background-size: 200% 200%;
animation: bgShift 6s ease-in-out infinite;
border: 1.5px solid rgba(180,192,210,0.15);
border-radius: 4px;
font-size: 13px;
color: #e8e8ef;
padding: 0 12px;
position: relative;
z-index: 1;
transition: border-color 0.2s ease, background 0.2s ease;
box-sizing: border-box;
}
.login-input::placeholder {
color: rgba(232,232,239,0.2);
}
.login-input:focus {
border-color: #1BB0CE;
outline: none;
background: #111118;
animation: none;
}
@keyframes bgShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.password-toggle {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: rgba(232,232,239,0.3);
cursor: pointer;
padding: 4px;
display: flex;
align-items: center;
z-index: 3;
}
.password-toggle:hover {
color: rgba(232,232,239,0.5);
}
.login-checkbox {
accent-color: #1BB0CE;
width: 16px;
height: 16px;
cursor: pointer;
}
.auth-btn {
width: 100%;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background: #1BB0CE;
color: #ffffff;
font-size: 14px;
font-weight: 700;
padding: 13px;
border-radius: 4px;
border: none;
cursor: pointer;
position: relative;
overflow: hidden;
transition: background 0.2s ease;
}
.auth-btn:hover {
background: #17a0bc;
}
.auth-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.auth-btn::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 60%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
animation: btnSweep 2.2s ease-in-out infinite;
pointer-events: none;
}
@keyframes btnSweep {
0% { left: -100%; }
100% { left: 200%; }
}
.login-label {
font-size: 12px;
font-weight: 600;
color: rgba(232,232,239,0.4);
display: block;
margin-bottom: 6px;
}
.body-text { color: rgba(232,232,239,0.5); }
.subheading-text { color: rgba(232,232,239,0.38); }
.checkbox-text { color: rgba(232,232,239,0.38); }
.footer-text { color: rgba(232,232,239,0.2); }
`}</style>
<div className="relative z-10">
<div className="flex items-center gap-3">
<div className="flex min-h-screen bg-[#0a0a0f]">
<div className="left-panel flex flex-1 flex-col p-12">
<div className="relative z-10">
<img
src="/logo/CompanyLogo.png"
alt={COMPANY_NAME}
className="h-10 w-10 rounded-xl object-contain"
className="h-44 w-auto object-contain"
/>
<span className="text-xl font-semibold">{COMPANY_NAME}</span>
</div>
</div>
<div className="relative z-10 max-w-lg">
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl font-bold leading-tight"
>
Your Agency&apos;s Growth Starts Here
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="mt-4 text-lg text-white/80"
>
Manage leads, track conversions, and grow your web development business with our CRM.
</motion.p>
<div className="relative z-10 flex-1 flex items-center justify-center">
<div className="text-center">
<h1 className="text-[34px] font-extrabold text-[#e8e8ef] leading-tight tracking-[-0.6px]">
Your Agency&apos;s{" "}
<span className="growth-word">Growth</span>{" "}
Starts Here
</h1>
<p className="mt-4 text-[13px] leading-[1.7] max-w-[360px] mx-auto body-text">
Manage leads, track conversions, and grow your web development business with our CRM.
</p>
<div className="stats-row">
<div className="stat">
<div className="stat-number">{counts.leads.toLocaleString()}</div>
<div className="stat-label">leads tracked</div>
</div>
<div className="stat">
<div className="stat-number">{counts.conversion}%</div>
<div className="stat-label">conversion lift</div>
</div>
<div className="stat">
<div className="stat-number">{counts.users}</div>
<div className="stat-label">active users</div>
</div>
</div>
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="mt-8 rounded-xl border border-white/10 bg-white/5 p-5 backdrop-blur-sm"
>
<p className="text-sm italic text-white/90">
<div className="relative z-10 testimonial">
<p className="text-[12px] italic" style={{ color: "#0a0a0f" }}>
&ldquo;This CRM transformed how we manage our sales pipeline. We&apos;ve seen a 40% increase in lead conversion.&rdquo;
</p>
<div className="mt-3 flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-white/20" />
<div>
<p className="text-sm font-medium">Marcus Johnson</p>
<p className="text-xs text-white/60">Sales Lead, Coast IT</p>
</div>
</div>
</motion.div>
</div>
<div className="relative z-10 text-sm text-white/60">
&copy; 2026 {COMPANY_NAME}. All rights reserved.
</div>
</div>
{/* Right - Login form */}
<div className="flex flex-1 items-center justify-center p-8">
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4 }}
className="w-full max-w-sm space-y-8"
>
{/* Mobile logo */}
<div className="flex flex-col items-center gap-3 lg:hidden">
<img
src="/logo/CompanyLogo.png"
alt={COMPANY_NAME}
className="h-12 w-12 rounded-xl object-contain"
/>
<span className="text-xl font-semibold">{COMPANY_NAME}</span>
</div>
<div className="space-y-2 text-center lg:text-left">
<h1 className="text-2xl font-bold tracking-tight">Welcome back</h1>
<p className="text-sm text-muted-foreground">
Sign in to your account to continue
<p className="text-[11px] mt-1" style={{ color: "#0a0a0f" }}>
Marcus Johnson, Sales Lead at Coast IT
</p>
</div>
<form onSubmit={handleSubmit} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="name@company.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
autoComplete="email"
/>
</div>
<canvas ref={canvasRef} className="wave-canvas" />
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="password">Password</Label>
<button
type="button"
className="text-xs text-primary hover:underline"
>
Forgot password?
</button>
<div className="panel-divider" />
<div className="right-panel flex items-center justify-center p-10">
<canvas ref={starsCanvasRightRef} className="stars-canvas" />
<div className="accent-line" />
<div className="w-full" style={{ maxWidth: 340 }}>
<h2 className="text-[24px] font-bold text-[#e8e8ef] tracking-[-0.3px] mb-[5px]">
Welcome back
</h2>
<p className="text-[13px] mb-[26px] subheading-text">
Sign in to your account to continue
</p>
<form onSubmit={handleSubmit} className="space-y-5">
<div>
<label htmlFor="email" className="login-label">
Email
</label>
<div className="input-sheen">
<input
id="email"
type="email"
placeholder="name@company.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
autoComplete="email"
className="login-input"
/>
</div>
</div>
<div className="relative">
<Input
id="password"
type={showPassword ? "text" : "password"}
placeholder="Enter your password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
autoComplete="current-password"
<div>
<div className="flex items-center justify-between mb-[6px]">
<label htmlFor="password" className="login-label" style={{ marginBottom: 0 }}>
Password
</label>
<button type="button" className="text-[12px] text-[#1BB0CE] hover:underline">
Forgot password?
</button>
</div>
<div className="input-sheen input-sheen-delayed">
<div className="relative">
<input
id="password"
type={showPassword ? "text" : "password"}
placeholder="Enter your password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
autoComplete="current-password"
className="login-input"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="password-toggle"
>
{showPassword ? (
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</button>
</div>
</div>
</div>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={remember}
onChange={(e) => setRemember(e.target.checked)}
className="login-checkbox"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="remember"
checked={remember}
onCheckedChange={(checked) => setRemember(checked as boolean)}
/>
<label
htmlFor="remember"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Remember me
<span className="text-[13px] checkbox-text">
Remember me
</span>
</label>
</div>
<Button type="submit" className="w-full" disabled={loading}>
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{loading ? "Signing in..." : "Sign in"}
</Button>
</form>
<button type="submit" className="auth-btn" disabled={loading}>
{loading && <Loader2 className="h-4 w-4 animate-spin" />}
{loading ? "Signing in..." : "Sign in"}
</button>
</form>
<p className="text-center text-xs text-muted-foreground">
By signing in, you agree to our{" "}
<button type="button" className="text-primary hover:underline">
Terms of Service
</button>{" "}
and{" "}
<button type="button" className="text-primary hover:underline">
Privacy Policy
</button>
</p>
</motion.div>
<p className="text-center text-[11px] mt-4 footer-text">
By signing in, you agree to our{" "}
<button type="button" className="text-[#1BB0CE] hover:underline">
Terms of Service
</button>{" "}
and{" "}
<button type="button" className="text-[#1BB0CE] hover:underline">
Privacy Policy
</button>
</p>
</div>
</div>
</div>
</div>
</>
)
}