Compare commits
2 Commits
13555c3fab
...
e7e4d371c2
| Author | SHA1 | Date | |
|---|---|---|---|
| e7e4d371c2 | |||
| b19a00c6c2 |
+59
-63
@@ -2,77 +2,73 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
:root {
|
||||||
:root {
|
--background: 210 40% 98%;
|
||||||
--background: 210 40% 98%;
|
--foreground: 222.2 84% 4.9%;
|
||||||
--foreground: 222.2 84% 4.9%;
|
--card: 0 0% 100%;
|
||||||
--card: 0 0% 100%;
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
--card-foreground: 222.2 84% 4.9%;
|
--popover: 0 0% 100%;
|
||||||
--popover: 0 0% 100%;
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
--popover-foreground: 222.2 84% 4.9%;
|
--primary: 221.2 83.2% 53.3%;
|
||||||
--primary: 221.2 83.2% 53.3%;
|
--primary-foreground: 210 40% 98%;
|
||||||
--primary-foreground: 210 40% 98%;
|
--secondary: 210 40% 96.1%;
|
||||||
--secondary: 210 40% 96.1%;
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
--muted: 210 40% 96.1%;
|
||||||
--muted: 210 40% 96.1%;
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
--accent: 210 40% 96.1%;
|
||||||
--accent: 210 40% 96.1%;
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
--destructive: 0 84.2% 60.2%;
|
||||||
--destructive: 0 84.2% 60.2%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--destructive-foreground: 210 40% 98%;
|
--border: 214.3 31.8% 91.4%;
|
||||||
--border: 214.3 31.8% 91.4%;
|
--input: 214.3 31.8% 91.4%;
|
||||||
--input: 214.3 31.8% 91.4%;
|
--ring: 221.2 83.2% 53.3%;
|
||||||
--ring: 221.2 83.2% 53.3%;
|
--sidebar: 222.2 84% 4.9%;
|
||||||
--sidebar: 222.2 84% 4.9%;
|
--sidebar-foreground: 210 40% 98%;
|
||||||
--sidebar-foreground: 210 40% 98%;
|
--sidebar-primary: 217.2 91.2% 59.8%;
|
||||||
--sidebar-primary: 217.2 91.2% 59.8%;
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
--sidebar-primary-foreground: 0 0% 100%;
|
--sidebar-accent: 217.2 32.6% 17.5%;
|
||||||
--sidebar-accent: 217.2 32.6% 17.5%;
|
--sidebar-accent-foreground: 210 40% 98%;
|
||||||
--sidebar-accent-foreground: 210 40% 98%;
|
--sidebar-border: 217.2 32.6% 17.5%;
|
||||||
--sidebar-border: 217.2 32.6% 17.5%;
|
--sidebar-ring: 224.3 76.3% 48%;
|
||||||
--sidebar-ring: 224.3 76.3% 48%;
|
--radius: 0.5rem;
|
||||||
--radius: 0.5rem;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 222.2 84% 4.9%;
|
--background: 222.2 84% 4.9%;
|
||||||
--foreground: 210 40% 98%;
|
--foreground: 210 40% 98%;
|
||||||
--card: 222.2 84% 4.9%;
|
--card: 222.2 84% 4.9%;
|
||||||
--card-foreground: 210 40% 98%;
|
--card-foreground: 210 40% 98%;
|
||||||
--popover: 222.2 84% 4.9%;
|
--popover: 222.2 84% 4.9%;
|
||||||
--popover-foreground: 210 40% 98%;
|
--popover-foreground: 210 40% 98%;
|
||||||
--primary: 217.2 91.2% 59.8%;
|
--primary: 217.2 91.2% 59.8%;
|
||||||
--primary-foreground: 222.2 47.4% 11.2%;
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
--secondary: 217.2 32.6% 17.5%;
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
--secondary-foreground: 210 40% 98%;
|
--secondary-foreground: 210 40% 98%;
|
||||||
--muted: 217.2 32.6% 17.5%;
|
--muted: 217.2 32.6% 17.5%;
|
||||||
--muted-foreground: 215 20.2% 65.1%;
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
--accent: 217.2 32.6% 17.5%;
|
--accent: 217.2 32.6% 17.5%;
|
||||||
--accent-foreground: 210 40% 98%;
|
--accent-foreground: 210 40% 98%;
|
||||||
--destructive: 0 62.8% 30.6%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--border: 217.2 32.6% 17.5%;
|
--border: 217.2 32.6% 17.5%;
|
||||||
--input: 217.2 32.6% 17.5%;
|
--input: 217.2 32.6% 17.5%;
|
||||||
--ring: 224.3 76.3% 48%;
|
--ring: 224.3 76.3% 48%;
|
||||||
--sidebar: 222.2 84% 4.9%;
|
--sidebar: 222.2 84% 4.9%;
|
||||||
--sidebar-foreground: 210 40% 98%;
|
--sidebar-foreground: 210 40% 98%;
|
||||||
--sidebar-primary: 217.2 91.2% 59.8%;
|
--sidebar-primary: 217.2 91.2% 59.8%;
|
||||||
--sidebar-primary-foreground: 0 0% 100%;
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
--sidebar-accent: 217.2 32.6% 17.5%;
|
--sidebar-accent: 217.2 32.6% 17.5%;
|
||||||
--sidebar-accent-foreground: 210 40% 98%;
|
--sidebar-accent-foreground: 210 40% 98%;
|
||||||
--sidebar-border: 217.2 32.6% 17.5%;
|
--sidebar-border: 217.2 32.6% 17.5%;
|
||||||
--sidebar-ring: 224.3 76.3% 48%;
|
--sidebar-ring: 224.3 76.3% 48%;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
|
font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={`${inter.variable} min-h-screen antialiased`}>
|
<body className={`${inter.variable} min-h-screen antialiased`}>
|
||||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
|
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
|
||||||
{children}
|
{children}
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
+392
-173
@@ -1,13 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Suspense, useState } from "react"
|
import { useState } from "react"
|
||||||
import { useRouter, useSearchParams } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { motion } from "framer-motion"
|
import { motion } from "framer-motion"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import { COMPANY_NAME } from "@/lib/constants"
|
import { COMPANY_NAME } from "@/lib/constants"
|
||||||
import { Eye, EyeOff, Loader2, AlertCircle } from "lucide-react"
|
import { Eye, EyeOff, Loader2 } from "lucide-react"
|
||||||
|
|
||||||
function LoginForm() {
|
function LoginForm() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -16,191 +17,409 @@ function LoginForm() {
|
|||||||
const [password, setPassword] = useState("")
|
const [password, setPassword] = useState("")
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState("")
|
const [remember, setRemember] = useState(false)
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setError("")
|
setError("")
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
try {
|
await new Promise((resolve) => setTimeout(resolve, 1200))
|
||||||
const res = await fetch("/api/auth/login", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ email, password }),
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = await res.json()
|
router.push("/dashboard")
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
setError(data.error || "Authentication failed.")
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const redirect = searchParams.get("redirect") || "/dashboard"
|
|
||||||
router.push(redirect)
|
|
||||||
} catch {
|
|
||||||
setError("Unable to connect to authentication service. Please ensure the database is running.")
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{error && (
|
<style>{`
|
||||||
<div className="flex items-start gap-2 rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive">
|
.left-panel {
|
||||||
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
|
background: #0a0a0f;
|
||||||
<span>{error}</span>
|
position: relative;
|
||||||
</div>
|
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>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
<div className="flex min-h-screen bg-[#0a0a0f]">
|
||||||
<div className="space-y-2">
|
<div className="left-panel flex flex-1 flex-col p-12">
|
||||||
<Label htmlFor="email">Email</Label>
|
<div className="relative z-10">
|
||||||
<Input
|
<img
|
||||||
id="email"
|
src="/logo/CompanyLogo.png"
|
||||||
type="email"
|
alt={COMPANY_NAME}
|
||||||
placeholder="name@company.com"
|
className="h-44 w-auto object-contain"
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
required
|
|
||||||
autoComplete="email"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="password">Password</Label>
|
|
||||||
<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"
|
|
||||||
/>
|
/>
|
||||||
<button
|
</div>
|
||||||
type="button"
|
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
<div className="relative z-10 flex-1 flex items-center justify-center">
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
<div className="text-center">
|
||||||
>
|
<h1 className="text-[34px] font-extrabold text-[#e8e8ef] leading-tight tracking-[-0.6px]">
|
||||||
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
Your Agency's{" "}
|
||||||
</button>
|
<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>
|
||||||
|
|
||||||
|
<div className="relative z-10 testimonial">
|
||||||
|
<p className="text-[12px] italic" style={{ color: "#0a0a0f" }}>
|
||||||
|
“This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.”
|
||||||
|
</p>
|
||||||
|
<p className="text-[11px] mt-1" style={{ color: "#0a0a0f" }}>
|
||||||
|
Marcus Johnson, Sales Lead at Coast IT
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<canvas ref={canvasRef} className="wave-canvas" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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 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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
<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
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</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>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LoginPage() {
|
|
||||||
|
|
||||||
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" />
|
|
||||||
|
|
||||||
<div className="relative z-10">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<img
|
|
||||||
src="/logo/CompanyLogo.png"
|
|
||||||
alt={COMPANY_NAME}
|
|
||||||
className="h-10 w-10 rounded-xl 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'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>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
“This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.”
|
|
||||||
</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">
|
|
||||||
© 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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Suspense fallback={<div className="text-center text-muted-foreground py-8">Loading...</div>}>
|
|
||||||
<LoginForm />
|
|
||||||
</Suspense>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user