Initial: saas template via tAI
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Read",
|
||||||
|
"Write",
|
||||||
|
"Edit",
|
||||||
|
"Bash",
|
||||||
|
"Glob",
|
||||||
|
"Grep"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"model": "claude-sonnet-4-20250514"
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Nexus
|
||||||
|
SaaS product landing with pricing, features, about, blog, auth, and contact pages
|
||||||
|
Stack: React 18 / TypeScript / Vite / Tailwind CSS / shadcn/ui
|
||||||
|
Pages: Index, Pricing, Features, About, Blog, Login, Signup, Contact
|
||||||
|
Palette: indigo/purple/pink gradients
|
||||||
|
|
||||||
|
## Files
|
||||||
|
- src/pages/Index.tsx (18KB)
|
||||||
|
- src/pages/Pricing.tsx (11KB)
|
||||||
|
- src/pages/Features.tsx (7KB)
|
||||||
|
- src/pages/About.tsx (8KB)
|
||||||
|
- src/pages/Blog.tsx (6KB)
|
||||||
|
- src/pages/Login.tsx (5KB)
|
||||||
|
- src/pages/Signup.tsx (7KB)
|
||||||
|
- src/pages/Contact.tsx (8KB)
|
||||||
|
- src/components/Header.tsx (4KB)
|
||||||
|
- src/components/Footer.tsx (5KB)
|
||||||
|
- src/App.tsx (1KB)
|
||||||
|
|
||||||
|
## Imports
|
||||||
|
UI: @/components/ui/{button,card,badge,input,textarea,tabs,accordion,dialog,sheet}
|
||||||
|
Icons: lucide-react
|
||||||
|
Toast: import { toast } from "sonner"
|
||||||
|
Theme: ThemeToggle in headers (next-themes)
|
||||||
|
Router: react-router-dom BrowserRouter
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
- MODIFY existing files — never rebuild from scratch
|
||||||
|
- Pages import Header + Footer from @/components/
|
||||||
|
- Tailwind only — no inline styles
|
||||||
|
- Use existing shadcn components — don't create custom ones
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Saas</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||||
|
import IndexPage from "@/pages/Index";
|
||||||
|
import PricingPage from "@/pages/Pricing";
|
||||||
|
import FeaturesPage from "@/pages/Features";
|
||||||
|
import AboutPage from "@/pages/About";
|
||||||
|
import BlogPage from "@/pages/Blog";
|
||||||
|
import LoginPage from "@/pages/Login";
|
||||||
|
import SignupPage from "@/pages/Signup";
|
||||||
|
import ContactPage from "@/pages/Contact";
|
||||||
|
import DashboardPage from "@/pages/Dashboard";
|
||||||
|
import AnalyticsPage from "@/pages/Analytics";
|
||||||
|
import CustomersPage from "@/pages/Customers";
|
||||||
|
import SettingsPage from "@/pages/Settings";
|
||||||
|
import ForgotPasswordPage from "@/pages/ForgotPassword";
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<IndexPage />} />
|
||||||
|
<Route path="/pricing" element={<PricingPage />} />
|
||||||
|
<Route path="/features" element={<FeaturesPage />} />
|
||||||
|
<Route path="/about" element={<AboutPage />} />
|
||||||
|
<Route path="/blog" element={<BlogPage />} />
|
||||||
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
<Route path="/signup" element={<SignupPage />} />
|
||||||
|
<Route path="/contact" element={<ContactPage />} />
|
||||||
|
<Route path="/dashboard" element={<DashboardPage />} />
|
||||||
|
<Route path="/analytics" element={<AnalyticsPage />} />
|
||||||
|
<Route path="/customers" element={<CustomersPage />} />
|
||||||
|
<Route path="/settings" element={<SettingsPage />} />
|
||||||
|
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,
|
||||||
|
DropdownMenuItem, DropdownMenuSeparator
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Search, Bell, Menu, LogOut, User, Settings } from "lucide-react";
|
||||||
|
|
||||||
|
export default function DashboardHeader() {
|
||||||
|
return (
|
||||||
|
<header className="sticky top-0 z-30 bg-white/80 backdrop-blur-lg border-b border-slate-200">
|
||||||
|
<div className="flex items-center justify-between px-6 py-3">
|
||||||
|
{/* Left */}
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Button variant="ghost" size="icon" className="lg:hidden text-slate-600">
|
||||||
|
<Menu className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
<div className="relative hidden sm:block">
|
||||||
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" />
|
||||||
|
<Input placeholder="Search..." className="pl-9 w-64 bg-slate-50 border-slate-200" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Button variant="ghost" size="icon" className="relative text-slate-600">
|
||||||
|
<Bell className="h-5 w-5" />
|
||||||
|
<Badge className="absolute -top-1 -right-1 h-5 w-5 flex items-center justify-center p-0 text-[10px] bg-red-500 text-white border-2 border-white">3</Badge>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" className="relative h-9 w-9 rounded-full">
|
||||||
|
<Avatar className="h-9 w-9">
|
||||||
|
<AvatarFallback className="bg-indigo-100 text-indigo-600 font-semibold text-sm">JD</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-48">
|
||||||
|
<DropdownMenuItem><User className="mr-2 h-4 w-4" /> Profile</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem><Settings className="mr-2 h-4 w-4" /> Settings</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem className="text-red-600"><LogOut className="mr-2 h-4 w-4" /> Logout</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Layers } from "lucide-react";
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "Product",
|
||||||
|
links: [
|
||||||
|
{ label: "Features", to: "/features" },
|
||||||
|
{ label: "Pricing", to: "/pricing" },
|
||||||
|
{ label: "Integrations", to: "/features" },
|
||||||
|
{ label: "Changelog", to: "/blog" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Company",
|
||||||
|
links: [
|
||||||
|
{ label: "About", to: "/about" },
|
||||||
|
{ label: "Blog", to: "/blog" },
|
||||||
|
{ label: "Careers", to: "/about" },
|
||||||
|
{ label: "Contact", to: "/contact" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Legal",
|
||||||
|
links: [
|
||||||
|
{ label: "Privacy", to: "#" },
|
||||||
|
{ label: "Terms", to: "#" },
|
||||||
|
{ label: "Security", to: "#" },
|
||||||
|
{ label: "GDPR", to: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="bg-slate-950 text-slate-400 pt-16 pb-8">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 pb-12 border-b border-slate-800">
|
||||||
|
{/* Brand */}
|
||||||
|
<div className="col-span-2 md:col-span-1">
|
||||||
|
<Link to="/" className="flex items-center gap-2 mb-4">
|
||||||
|
<div className="h-8 w-8 rounded-lg bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center">
|
||||||
|
<Layers className="h-4 w-4 text-white" />
|
||||||
|
</div>
|
||||||
|
<span className="text-lg font-bold text-white tracking-tight">Nexus</span>
|
||||||
|
</Link>
|
||||||
|
<p className="text-sm leading-relaxed text-slate-500">
|
||||||
|
Streamline your workflow with intelligent automation, real-time collaboration, and predictive analytics that keep your team ahead of every deadline.
|
||||||
|
</p>
|
||||||
|
{/* Social Icons */}
|
||||||
|
<div className="flex gap-4 mt-6">
|
||||||
|
<a href="#" className="text-slate-500 hover:text-indigo-400 transition-colors" aria-label="Twitter">
|
||||||
|
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a href="#" className="text-slate-500 hover:text-indigo-400 transition-colors" aria-label="GitHub">
|
||||||
|
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path fillRule="evenodd" clipRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a href="#" className="text-slate-500 hover:text-indigo-400 transition-colors" aria-label="LinkedIn">
|
||||||
|
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Link Columns */}
|
||||||
|
{columns.map((col) => (
|
||||||
|
<div key={col.title}>
|
||||||
|
<h3 className="text-sm font-semibold text-white mb-4">{col.title}</h3>
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{col.links.map((link) => (
|
||||||
|
<li key={link.label}>
|
||||||
|
<Link to={link.to} className="text-sm hover:text-indigo-400 transition-colors">{link.label}</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 text-center text-xs text-slate-600">
|
||||||
|
© {new Date().getFullYear()} Nexus. All rights reserved.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||||
|
import { Menu, Layers } from "lucide-react";
|
||||||
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ label: "Features", to: "/features" },
|
||||||
|
{ label: "Pricing", to: "/pricing" },
|
||||||
|
{ label: "About", to: "/about" },
|
||||||
|
{ label: "Blog", to: "/blog" },
|
||||||
|
{ label: "Contact", to: "/contact" },
|
||||||
|
{ label: "Dashboard", to: "/dashboard" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
const [mobileOpen, setMobileOpen] = useState(false);
|
||||||
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onScroll = () => setScrolled(window.scrollY > 20);
|
||||||
|
window.addEventListener("scroll", onScroll);
|
||||||
|
return () => window.removeEventListener("scroll", onScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className={`fixed top-0 inset-x-0 z-50 transition-all duration-300 \${
|
||||||
|
scrolled
|
||||||
|
? "bg-white/95 backdrop-blur-lg border-b border-slate-100 shadow-sm"
|
||||||
|
: "bg-transparent"
|
||||||
|
}`}>
|
||||||
|
<div className="mx-auto max-w-7xl flex items-center justify-between px-6 py-4">
|
||||||
|
{/* Logo */}
|
||||||
|
<Link to="/" className="flex items-center gap-2">
|
||||||
|
<div className="h-8 w-8 rounded-lg bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center">
|
||||||
|
<Layers className="h-4 w-4 text-white" />
|
||||||
|
</div>
|
||||||
|
<span className={`text-lg font-bold tracking-tight \${scrolled ? "text-slate-900" : "text-white"}`}>
|
||||||
|
Nexus
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Desktop Nav */}
|
||||||
|
<nav className="hidden md:flex items-center gap-8">
|
||||||
|
{navLinks.map((l) => (
|
||||||
|
<Link
|
||||||
|
key={l.to}
|
||||||
|
to={l.to}
|
||||||
|
className={`text-sm font-medium transition-colors \${
|
||||||
|
scrolled ? "text-slate-600 hover:text-slate-900" : "text-slate-300 hover:text-white"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{l.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Right Actions */}
|
||||||
|
<div className="hidden md:flex items-center gap-3">
|
||||||
|
<ThemeToggle />
|
||||||
|
<Button asChild variant="ghost" size="sm" className={`rounded-full \${scrolled ? "text-slate-600 hover:text-slate-900" : "text-slate-300 hover:text-white hover:bg-white/10"}`}>
|
||||||
|
<Link to="/login">Sign In</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild size="sm" className="active:scale-[0.98] rounded-full bg-indigo-600 hover:bg-indigo-500 text-white shadow-lg shadow-indigo-500/20">
|
||||||
|
<Link to="/signup">Get Started</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
|
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
|
||||||
|
<SheetTrigger asChild className="md:hidden">
|
||||||
|
<Button variant="ghost" size="icon" className={`\${scrolled ? "text-slate-900" : "text-white"}`}>
|
||||||
|
<Menu className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent side="right" className="w-72 bg-white">
|
||||||
|
<div className="flex flex-col gap-1 mt-8">
|
||||||
|
{navLinks.map((l) => (
|
||||||
|
<Link
|
||||||
|
key={l.to}
|
||||||
|
to={l.to}
|
||||||
|
onClick={() => setMobileOpen(false)}
|
||||||
|
className="text-sm font-medium text-slate-600 hover:text-slate-900 hover:bg-slate-50 rounded-lg px-4 py-3 transition-colors"
|
||||||
|
>
|
||||||
|
{l.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<div className="border-t border-slate-100 my-4" />
|
||||||
|
<Link to="/login" onClick={() => setMobileOpen(false)} className="text-sm font-medium text-slate-600 hover:text-slate-900 hover:bg-slate-50 rounded-lg px-4 py-3">
|
||||||
|
Sign In
|
||||||
|
</Link>
|
||||||
|
<Button asChild className="active:scale-[0.98] mx-4 mt-2 rounded-full bg-indigo-600 hover:bg-indigo-500 text-white">
|
||||||
|
<Link to="/signup" onClick={() => setMobileOpen(false)}>Get Started</Link>
|
||||||
|
</Button>
|
||||||
|
<div className="px-4 mt-2"><ThemeToggle /></div>
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Link, useLocation } from "react-router-dom";
|
||||||
|
import { LayoutDashboard, BarChart3, Users, Settings, Layers, Menu } from "lucide-react";
|
||||||
|
import { Sheet, SheetTrigger, SheetContent } from "@/components/ui/sheet";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ label: "Dashboard", to: "/dashboard", icon: LayoutDashboard },
|
||||||
|
{ label: "Analytics", to: "/analytics", icon: BarChart3 },
|
||||||
|
{ label: "Customers", to: "/customers", icon: Users },
|
||||||
|
{ label: "Settings", to: "/settings", icon: Settings },
|
||||||
|
];
|
||||||
|
|
||||||
|
function SidebarContent({ activePath }: { activePath: string }) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex items-center gap-2 px-6 py-6">
|
||||||
|
<div className="h-8 w-8 rounded-lg bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center">
|
||||||
|
<Layers className="h-4 w-4 text-white" />
|
||||||
|
</div>
|
||||||
|
<span className="text-lg font-bold text-white tracking-tight">Nexus</span>
|
||||||
|
</div>
|
||||||
|
<nav className="flex-1 px-3 space-y-1 mt-4">
|
||||||
|
{navItems.map((item) => {
|
||||||
|
const Icon = item.icon;
|
||||||
|
const isActive = activePath === item.to;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.to}
|
||||||
|
to={item.to}
|
||||||
|
className={`flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors \${
|
||||||
|
isActive
|
||||||
|
? "bg-indigo-500/15 text-indigo-400 border-l-2 border-indigo-400"
|
||||||
|
: "text-slate-400 hover:text-white hover:bg-white/5"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Icon className="h-4 w-4" />
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Sidebar({ activePath }: { activePath?: string }) {
|
||||||
|
const location = useLocation();
|
||||||
|
const path = activePath || location.pathname;
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Desktop sidebar */}
|
||||||
|
<aside className="hidden lg:flex fixed inset-y-0 left-0 z-40 w-64 flex-col bg-slate-900/95 backdrop-blur-xl border-r border-slate-800">
|
||||||
|
<SidebarContent activePath={path} />
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* Mobile sidebar via Sheet */}
|
||||||
|
<Sheet open={open} onOpenChange={setOpen}>
|
||||||
|
<SheetTrigger asChild className="lg:hidden fixed top-4 left-4 z-50">
|
||||||
|
<Button variant="ghost" size="icon" className="text-slate-600">
|
||||||
|
<Menu className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent side="left" className="w-64 p-0 bg-slate-900/95 backdrop-blur-xl border-r border-slate-800">
|
||||||
|
<SidebarContent activePath={path} />
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "./App";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { ArrowRight, Linkedin, Heart, Target, Lightbulb, Users } from "lucide-react";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
const team = [
|
||||||
|
{ name: "Alex Kim", role: "Co-Founder & CEO", initials: "AK" },
|
||||||
|
{ name: "Sophia Patel", role: "Co-Founder & CTO", initials: "SP" },
|
||||||
|
{ name: "James Moreno", role: "VP of Engineering", initials: "JM" },
|
||||||
|
{ name: "Lena Chen", role: "Head of Design", initials: "LC" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const values = [
|
||||||
|
{ icon: Target, title: "Customer Obsessed", desc: "Every decision starts with what is best for our users." },
|
||||||
|
{ icon: Lightbulb, title: "Relentlessly Innovative", desc: "We push the boundaries of what project management can be." },
|
||||||
|
{ icon: Heart, title: "People First", desc: "We build for humans, and we treat our team as family." },
|
||||||
|
{ icon: Users, title: "Radically Transparent", desc: "Open roadmaps, public changelogs, and honest communication." },
|
||||||
|
];
|
||||||
|
|
||||||
|
const milestones = [
|
||||||
|
{ year: "2020", event: "Founded in San Francisco by two ex-Google engineers" },
|
||||||
|
{ year: "2021", event: "Launched beta with 500 early adopter teams" },
|
||||||
|
{ year: "2022", event: "Series A: $18M raised, 5,000 teams onboarded" },
|
||||||
|
{ year: "2023", event: "AI Copilot launched, crossed 10,000 teams" },
|
||||||
|
{ year: "2024", event: "Series B: $45M raised, expanded to 150+ countries" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white text-slate-900 antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-50 to-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">About Us</Badge>
|
||||||
|
<h1 className="animate-fade-up text-4xl font-bold tracking-tight sm:text-5xl">We are building the future of work</h1>
|
||||||
|
<p className="mt-4 text-lg text-slate-500 max-w-2xl mx-auto">
|
||||||
|
Nexus was born from a simple belief: teams deserve tools that work as hard as they do. We are a team of engineers, designers, and dreamers on a mission to make work feel effortless.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── MISSION ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||||
|
<div>
|
||||||
|
<Badge className="mb-4 bg-cyan-50 text-cyan-600 border-0">Our Mission</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight">Empower every team to do their best work</h2>
|
||||||
|
<p className="mt-6 text-slate-500 leading-relaxed text-lg">We believe that great software should amplify human potential, not replace it. Nexus combines the best of AI with intuitive design so teams can focus on what matters most: building great products.</p>
|
||||||
|
<Button asChild className="active:scale-[0.98] mt-8 rounded-full bg-indigo-600 hover:bg-indigo-500">
|
||||||
|
<Link to="/features">See What We Built <ArrowRight className="ml-2 h-4 w-4" /></Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="animate-scale-in aspect-[4/3] rounded-2xl bg-gradient-to-br from-indigo-100 via-cyan-50 to-slate-100 flex items-center justify-center relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-60" />
|
||||||
|
<div className="absolute -top-10 -right-10 w-40 h-40 rounded-full bg-indigo-300/30 blur-3xl" />
|
||||||
|
<div className="absolute -bottom-10 -left-10 w-32 h-32 rounded-full bg-cyan-300/30 blur-3xl" />
|
||||||
|
<span className="relative text-slate-400 text-sm">Mission Image</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── TEAM ── */}
|
||||||
|
<section className="py-24 bg-slate-50">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Our Team</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight">Meet the people behind Nexus</h2>
|
||||||
|
</div>
|
||||||
|
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-8 stagger">
|
||||||
|
{team.map((member) => (
|
||||||
|
<Card key={member.name} className="hover-card-lift border-slate-200 text-center hover:shadow-lg transition-all duration-300">
|
||||||
|
<CardContent className="p-8">
|
||||||
|
<Avatar className="h-20 w-20 mx-auto mb-4">
|
||||||
|
<AvatarFallback className="bg-gradient-to-br from-indigo-500 to-cyan-500 text-white text-xl font-bold">{member.initials}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<h3 className="font-semibold text-lg">{member.name}</h3>
|
||||||
|
<p className="text-sm text-slate-500 mt-1">{member.role}</p>
|
||||||
|
<a href="#" className="inline-flex items-center gap-1 mt-4 text-indigo-600 hover:text-indigo-700 text-sm">
|
||||||
|
<Linkedin className="h-4 w-4" /> LinkedIn
|
||||||
|
</a>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── VALUES ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<Badge className="mb-4 bg-amber-50 text-amber-600 border-0">Our Values</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight">What drives us every day</h2>
|
||||||
|
</div>
|
||||||
|
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-6 stagger">
|
||||||
|
{values.map((v) => (
|
||||||
|
<Card key={v.title} className="hover-card-lift border-slate-200 hover:border-indigo-200 hover:shadow-lg transition-all duration-300">
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<div className="h-12 w-12 rounded-xl bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center mb-4">
|
||||||
|
<v.icon className="h-6 w-6 text-white" />
|
||||||
|
</div>
|
||||||
|
<h3 className="font-semibold mb-2">{v.title}</h3>
|
||||||
|
<p className="text-sm text-slate-500">{v.desc}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── TIMELINE ── */}
|
||||||
|
<section className="py-24 bg-slate-50">
|
||||||
|
<div className="mx-auto max-w-3xl px-6">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Our Journey</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight">Company milestones</h2>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute left-4 top-0 bottom-0 w-px bg-gradient-to-b from-indigo-500 to-cyan-500" />
|
||||||
|
<div className="space-y-10">
|
||||||
|
{milestones.map((m) => (
|
||||||
|
<div key={m.year} className="flex gap-6 items-start">
|
||||||
|
<div className="relative flex-shrink-0 h-9 w-9 rounded-full bg-indigo-600 flex items-center justify-center text-white text-xs font-bold ring-4 ring-white">
|
||||||
|
{m.year.slice(2)}
|
||||||
|
</div>
|
||||||
|
<div className="pt-1">
|
||||||
|
<span className="text-sm font-bold text-indigo-600">{m.year}</span>
|
||||||
|
<p className="text-slate-600 mt-1">{m.event}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
import DashboardHeader from "@/components/DashboardHeader";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts";
|
||||||
|
|
||||||
|
const growthData = [
|
||||||
|
{ month: "Jul", users: 1200 }, { month: "Aug", users: 1800 },
|
||||||
|
{ month: "Sep", users: 2400 }, { month: "Oct", users: 3100 },
|
||||||
|
{ month: "Nov", users: 3900 }, { month: "Dec", users: 4800 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const funnel = [
|
||||||
|
{ label: "Visitors", value: 12450, pct: 100 },
|
||||||
|
{ label: "Signups", value: 3200, pct: 25.7 },
|
||||||
|
{ label: "Trials", value: 1840, pct: 14.8 },
|
||||||
|
{ label: "Paid", value: 920, pct: 7.4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const engagement = [
|
||||||
|
{ label: "DAU", value: "1,247" },
|
||||||
|
{ label: "WAU", value: "4,832" },
|
||||||
|
{ label: "MAU", value: "12,450" },
|
||||||
|
{ label: "Avg Session", value: "4m 32s" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const topPages = [
|
||||||
|
{ page: "Dashboard", views: 8420, unique: 3210, bounce: "24%" },
|
||||||
|
{ page: "Analytics", views: 5340, unique: 2180, bounce: "31%" },
|
||||||
|
{ page: "Settings", views: 3120, unique: 1540, bounce: "18%" },
|
||||||
|
{ page: "API Docs", views: 2890, unique: 1320, bounce: "42%" },
|
||||||
|
{ page: "Billing", views: 2140, unique: 980, bounce: "15%" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function AnalyticsPage() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen bg-slate-50">
|
||||||
|
<Sidebar activePath="/analytics" />
|
||||||
|
<div className="flex-1 lg:ml-64">
|
||||||
|
<DashboardHeader />
|
||||||
|
<main className="p-6 space-y-6">
|
||||||
|
{/* User Growth Chart */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>User Growth</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="h-72">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<AreaChart data={growthData}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
|
||||||
|
<XAxis dataKey="month" stroke="#94a3b8" fontSize={12} />
|
||||||
|
<YAxis stroke="#94a3b8" fontSize={12} />
|
||||||
|
<Tooltip />
|
||||||
|
<Area type="monotone" dataKey="users" stroke="#6366f1" fill="#6366f1" fillOpacity={0.15} />
|
||||||
|
</AreaChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Conversion Funnel */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Conversion Funnel</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{funnel.map((step) => (
|
||||||
|
<div key={step.label} className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="font-medium">{step.label}</span>
|
||||||
|
<span className="text-slate-500">{step.value.toLocaleString()} ({step.pct}%)</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-3 bg-slate-100 rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-indigo-500 rounded-full transition-all" style={{ width: `\${step.pct}%` }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Engagement Grid */}
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
{engagement.map((e) => (
|
||||||
|
<Card key={e.label}>
|
||||||
|
<CardContent className="p-6 text-center">
|
||||||
|
<p className="text-sm text-slate-500">{e.label}</p>
|
||||||
|
<p className="text-2xl font-bold mt-1">{e.value}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Top Pages Table */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Top Pages</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-slate-200">
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-slate-500">Page</th>
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-slate-500">Views</th>
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-slate-500">Unique Visitors</th>
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-slate-500">Bounce Rate</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{topPages.map((p) => (
|
||||||
|
<tr key={p.page} className="border-b border-slate-100 last:border-0">
|
||||||
|
<td className="py-3 px-4 font-medium">{p.page}</td>
|
||||||
|
<td className="py-3 px-4 text-slate-600">{p.views.toLocaleString()}</td>
|
||||||
|
<td className="py-3 px-4 text-slate-600">{p.unique.toLocaleString()}</td>
|
||||||
|
<td className="py-3 px-4 text-slate-600">{p.bounce}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
const categories = ["All", "Product", "Engineering", "Company", "Guides"];
|
||||||
|
|
||||||
|
const articles = [
|
||||||
|
{
|
||||||
|
title: "How AI is Reshaping Project Management in 2025",
|
||||||
|
excerpt: "Discover how machine learning is automating the mundane and freeing teams to focus on creative work.",
|
||||||
|
category: "Product",
|
||||||
|
author: "Alex Kim",
|
||||||
|
initials: "AK",
|
||||||
|
date: "Mar 15, 2025",
|
||||||
|
gradient: "from-indigo-500 to-purple-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Building a Culture of Asynchronous Collaboration",
|
||||||
|
excerpt: "Why the best distributed teams invest in async-first workflows and how you can too.",
|
||||||
|
category: "Guides",
|
||||||
|
author: "Sophia Patel",
|
||||||
|
initials: "SP",
|
||||||
|
date: "Mar 8, 2025",
|
||||||
|
gradient: "from-cyan-500 to-blue-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Our Journey to SOC 2 Type II Compliance",
|
||||||
|
excerpt: "A behind-the-scenes look at how we achieved enterprise-grade security certification.",
|
||||||
|
category: "Engineering",
|
||||||
|
author: "James Moreno",
|
||||||
|
initials: "JM",
|
||||||
|
date: "Feb 28, 2025",
|
||||||
|
gradient: "from-emerald-500 to-teal-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Introducing Custom Workflow Automations",
|
||||||
|
excerpt: "Build powerful no-code automations that save your team hours every week.",
|
||||||
|
category: "Product",
|
||||||
|
author: "Lena Chen",
|
||||||
|
initials: "LC",
|
||||||
|
date: "Feb 20, 2025",
|
||||||
|
gradient: "from-amber-500 to-orange-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Scaling from 10 to 10,000 Teams: Lessons Learned",
|
||||||
|
excerpt: "The architectural decisions and trade-offs we made during our hyper-growth phase.",
|
||||||
|
category: "Engineering",
|
||||||
|
author: "Sophia Patel",
|
||||||
|
initials: "SP",
|
||||||
|
date: "Feb 12, 2025",
|
||||||
|
gradient: "from-rose-500 to-pink-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "The Future of Work is Hybrid: Our Playbook",
|
||||||
|
excerpt: "How we keep our own team productive across 12 time zones and 4 continents.",
|
||||||
|
category: "Company",
|
||||||
|
author: "Alex Kim",
|
||||||
|
initials: "AK",
|
||||||
|
date: "Feb 5, 2025",
|
||||||
|
gradient: "from-violet-500 to-indigo-600",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function BlogPage() {
|
||||||
|
const [activeCategory, setActiveCategory] = useState("All");
|
||||||
|
|
||||||
|
const filtered = activeCategory === "All" ? articles : articles.filter((a) => a.category === activeCategory);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white text-slate-900 antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-50 to-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Blog</Badge>
|
||||||
|
<h1 className="animate-fade-up text-4xl font-bold tracking-tight sm:text-5xl">Insights & Updates</h1>
|
||||||
|
<p className="mt-4 text-lg text-slate-500 max-w-2xl mx-auto">
|
||||||
|
Product news, engineering deep-dives, and guides from the Nexus team.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── CONTENT ── */}
|
||||||
|
<section className="py-24">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="flex flex-wrap gap-2 mb-12 justify-center">
|
||||||
|
{categories.map((cat) => (
|
||||||
|
<Button
|
||||||
|
key={cat}
|
||||||
|
variant={activeCategory === cat ? "default" : "outline"}
|
||||||
|
size="sm"
|
||||||
|
className={`rounded-full \${activeCategory === cat ? "bg-indigo-600 hover:bg-indigo-500" : ""}`}
|
||||||
|
onClick={() => setActiveCategory(cat)}
|
||||||
|
>
|
||||||
|
{cat}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 stagger">
|
||||||
|
{filtered.map((article) => (
|
||||||
|
<Card key={article.title} className="overflow-hidden border-slate-200 hover:-translate-y-1 hover:shadow-xl hover:border-indigo-200 transition-all duration-300 group cursor-pointer">
|
||||||
|
<div className={`aspect-[16/9] bg-gradient-to-br \${article.gradient} flex items-center justify-center relative overflow-hidden`}>
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-60" />
|
||||||
|
<div className="absolute -top-8 -right-8 w-32 h-32 rounded-full bg-white/10 blur-3xl" />
|
||||||
|
<div className="absolute -bottom-8 -left-8 w-24 h-24 rounded-full bg-white/10 blur-3xl" />
|
||||||
|
<span className="relative text-white/40 text-sm">Article Image</span>
|
||||||
|
</div>
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<Badge className="mb-3 bg-slate-100 text-slate-600 border-0 text-xs">{article.category}</Badge>
|
||||||
|
<h3 className="font-semibold text-lg leading-tight group-hover:text-indigo-600 transition-colors">{article.title}</h3>
|
||||||
|
<p className="mt-2 text-sm text-slate-500 line-clamp-2">{article.excerpt}</p>
|
||||||
|
<div className="mt-4 flex items-center gap-3">
|
||||||
|
<Avatar className="h-8 w-8">
|
||||||
|
<AvatarFallback className="bg-gradient-to-br from-indigo-400 to-cyan-400 text-white text-xs">{article.initials}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium">{article.author}</div>
|
||||||
|
<div className="text-xs text-slate-400">{article.date}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 text-center">
|
||||||
|
<Button variant="outline" className="active:scale-[0.98] rounded-full px-8">Load More Articles</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Mail, Phone, MapPin, Send, CheckCircle2 } from "lucide-react";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [company, setCompany] = useState("");
|
||||||
|
const [subject, setSubject] = useState("");
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
const [success, setSuccess] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white text-slate-900 antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-50 to-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Contact</Badge>
|
||||||
|
<h1 className="animate-fade-up text-4xl font-bold tracking-tight sm:text-5xl">Get in touch</h1>
|
||||||
|
<p className="mt-4 text-lg text-slate-500 max-w-2xl mx-auto">
|
||||||
|
Have questions? We would love to hear from you. Our team typically responds within 24 hours.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── CONTACT SPLIT ── */}
|
||||||
|
<section className="py-24">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="grid lg:grid-cols-5 gap-12">
|
||||||
|
{/* Left — Info */}
|
||||||
|
<div className="lg:col-span-2 space-y-8">
|
||||||
|
<div>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold mb-4 tracking-tight">Contact Information</h2>
|
||||||
|
<p className="text-slate-500">Reach out through any of these channels and we will get back to you as soon as possible.</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="h-10 w-10 rounded-xl bg-indigo-50 flex items-center justify-center flex-shrink-0">
|
||||||
|
<Mail className="h-5 w-5 text-indigo-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-sm">Email</h3>
|
||||||
|
<p className="text-slate-500 text-sm mt-0.5">hello@nexus.dev</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="h-10 w-10 rounded-xl bg-indigo-50 flex items-center justify-center flex-shrink-0">
|
||||||
|
<Phone className="h-5 w-5 text-indigo-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-sm">Phone</h3>
|
||||||
|
<p className="text-slate-500 text-sm mt-0.5">+1 (415) 555-0132</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="h-10 w-10 rounded-xl bg-indigo-50 flex items-center justify-center flex-shrink-0">
|
||||||
|
<MapPin className="h-5 w-5 text-indigo-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-sm">Office</h3>
|
||||||
|
<p className="text-slate-500 text-sm mt-0.5">100 Market St, Suite 400, San Francisco, CA 94105</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right — Form */}
|
||||||
|
<div className="lg:col-span-3">
|
||||||
|
<Card className="border-slate-200 shadow-lg">
|
||||||
|
<CardContent className="p-8">
|
||||||
|
{success && (
|
||||||
|
<div className="mb-6 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-3 text-sm text-green-700">
|
||||||
|
<CheckCircle2 className="h-4 w-4 flex-shrink-0" /> Message sent successfully! We will get back to you within 24 hours.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<form onSubmit={(e) => { e.preventDefault(); setSuccess(true); toast.success("Message sent! Our team will reach out shortly."); }}>
|
||||||
|
<div className="grid sm:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="contact-name" className="text-sm">Name</Label>
|
||||||
|
<Input id="contact-name" required placeholder="Your name" value={name} onChange={(e) => setName(e.target.value)} className="mt-1.5" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="contact-email" className="text-sm">Email</Label>
|
||||||
|
<Input id="contact-email" type="email" required placeholder="you@company.com" value={email} onChange={(e) => setEmail(e.target.value)} className="mt-1.5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Label htmlFor="contact-company" className="text-sm">Company</Label>
|
||||||
|
<Input id="contact-company" placeholder="Your company" value={company} onChange={(e) => setCompany(e.target.value)} className="mt-1.5" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Label htmlFor="contact-subject" className="text-sm">Subject</Label>
|
||||||
|
<Select value={subject} onValueChange={setSubject}>
|
||||||
|
<SelectTrigger className="mt-1.5">
|
||||||
|
<SelectValue placeholder="Select a subject" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="general">General Inquiry</SelectItem>
|
||||||
|
<SelectItem value="sales">Sales</SelectItem>
|
||||||
|
<SelectItem value="support">Technical Support</SelectItem>
|
||||||
|
<SelectItem value="partnership">Partnership</SelectItem>
|
||||||
|
<SelectItem value="press">Press & Media</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Label htmlFor="contact-message" className="text-sm">Message</Label>
|
||||||
|
<Textarea
|
||||||
|
id="contact-message"
|
||||||
|
required
|
||||||
|
placeholder="Tell us how we can help..."
|
||||||
|
value={message}
|
||||||
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
|
className="mt-1.5 min-h-[120px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button type="submit" className="active:scale-[0.98] mt-6 w-full bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg font-semibold">
|
||||||
|
<Send className="mr-2 h-4 w-4" /> Send Message
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── MAP PLACEHOLDER ── */}
|
||||||
|
<section className="pb-24">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="animate-scale-in h-64 rounded-2xl bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-60" />
|
||||||
|
<div className="absolute -top-10 -right-10 w-40 h-40 rounded-full bg-indigo-200/30 blur-3xl" />
|
||||||
|
<div className="absolute -bottom-10 -left-10 w-32 h-32 rounded-full bg-cyan-200/30 blur-3xl" />
|
||||||
|
<span className="relative text-slate-400 text-sm">Map Placeholder</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
import DashboardHeader from "@/components/DashboardHeader";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
|
import { Search } from "lucide-react";
|
||||||
|
|
||||||
|
const filters = ["All", "Active", "Trial", "Churned"];
|
||||||
|
|
||||||
|
const customers = [
|
||||||
|
{ name: "Sarah Chen", email: "sarah@acme.co", plan: "Enterprise", mrr: "\$299", joined: "Jan 12, 2024", status: "active", initials: "SC" },
|
||||||
|
{ name: "Marcus Johnson", email: "marcus@startup.io", plan: "Pro", mrr: "\$49", joined: "Feb 8, 2024", status: "active", initials: "MJ" },
|
||||||
|
{ name: "Emily Rivera", email: "emily@design.co", plan: "Pro", mrr: "\$49", joined: "Mar 15, 2024", status: "trial", initials: "ER" },
|
||||||
|
{ name: "David Kim", email: "david@tech.dev", plan: "Enterprise", mrr: "\$299", joined: "Apr 3, 2024", status: "active", initials: "DK" },
|
||||||
|
{ name: "Anna Petrov", email: "anna@agency.com", plan: "Free", mrr: "\$0", joined: "May 20, 2024", status: "churned", initials: "AP" },
|
||||||
|
{ name: "James Wright", email: "james@corp.biz", plan: "Pro", mrr: "\$49", joined: "Jun 1, 2024", status: "active", initials: "JW" },
|
||||||
|
{ name: "Lisa Nguyen", email: "lisa@creative.co", plan: "Enterprise", mrr: "\$299", joined: "Jul 14, 2024", status: "active", initials: "LN" },
|
||||||
|
{ name: "Tom Bradley", email: "tom@labs.io", plan: "Free", mrr: "\$0", joined: "Aug 22, 2024", status: "trial", initials: "TB" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function CustomersPage() {
|
||||||
|
const [activeFilter, setActiveFilter] = useState("All");
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const filtered = customers.filter((c) => {
|
||||||
|
if (activeFilter !== "All" && c.status !== activeFilter.toLowerCase()) return false;
|
||||||
|
if (search && !c.name.toLowerCase().includes(search.toLowerCase()) && !c.email.toLowerCase().includes(search.toLowerCase())) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen bg-slate-50">
|
||||||
|
<Sidebar activePath="/customers" />
|
||||||
|
<div className="flex-1 lg:ml-64">
|
||||||
|
<DashboardHeader />
|
||||||
|
<main className="p-6 space-y-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Customers</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{/* Search & Filters */}
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||||
|
<div className="relative flex-1">
|
||||||
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" />
|
||||||
|
<Input
|
||||||
|
placeholder="Search customers..."
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
className="pl-9"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{filters.map((f) => (
|
||||||
|
<Button
|
||||||
|
key={f}
|
||||||
|
variant={activeFilter === f ? "default" : "outline"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setActiveFilter(f)}
|
||||||
|
>
|
||||||
|
{f}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Table */}
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Name</TableHead>
|
||||||
|
<TableHead>Plan</TableHead>
|
||||||
|
<TableHead>MRR</TableHead>
|
||||||
|
<TableHead>Joined</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{filtered.map((c, i) => (
|
||||||
|
<TableRow key={i}>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Avatar className="h-8 w-8">
|
||||||
|
<AvatarFallback className="bg-indigo-100 text-indigo-600 text-xs">{c.initials}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">{c.name}</p>
|
||||||
|
<p className="text-xs text-slate-400">{c.email}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge variant={c.plan === "Enterprise" ? "default" : c.plan === "Pro" ? "secondary" : "outline"}>
|
||||||
|
{c.plan}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="font-medium">{c.mrr}</TableCell>
|
||||||
|
<TableCell className="text-slate-500">{c.joined}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge variant={c.status === "active" ? "default" : c.status === "trial" ? "secondary" : "destructive"}>
|
||||||
|
{c.status}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<p className="text-sm text-slate-400 mt-4">Showing {filtered.length} of {customers.length} customers</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
import DashboardHeader from "@/components/DashboardHeader";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
|
import { DollarSign, Users, TrendingDown, ThumbsUp, ArrowUpRight, ArrowDownRight, Plus, UserPlus, Download } from "lucide-react";
|
||||||
|
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts";
|
||||||
|
|
||||||
|
const kpis = [
|
||||||
|
{ label: "MRR", value: "\$48,250", trend: "+12.5%", up: true, icon: DollarSign, color: "bg-sky-100 text-sky-600" },
|
||||||
|
{ label: "Active Users", value: "2,847", trend: "+8.2%", up: true, icon: Users, color: "bg-emerald-100 text-emerald-600" },
|
||||||
|
{ label: "Churn Rate", value: "2.4%", trend: "-0.3%", up: false, icon: TrendingDown, color: "bg-orange-100 text-orange-600" },
|
||||||
|
{ label: "NPS", value: "72", trend: "+4", up: true, icon: ThumbsUp, color: "bg-violet-100 text-violet-600" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const revenueData = [
|
||||||
|
{ month: "Jan", revenue: 32000 }, { month: "Feb", revenue: 34500 },
|
||||||
|
{ month: "Mar", revenue: 36200 }, { month: "Apr", revenue: 38100 },
|
||||||
|
{ month: "May", revenue: 37800 }, { month: "Jun", revenue: 39500 },
|
||||||
|
{ month: "Jul", revenue: 41200 }, { month: "Aug", revenue: 43000 },
|
||||||
|
{ month: "Sep", revenue: 44800 }, { month: "Oct", revenue: 45200 },
|
||||||
|
{ month: "Nov", revenue: 46900 }, { month: "Dec", revenue: 48250 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const activities = [
|
||||||
|
{ name: "Sarah Chen", action: "Upgraded to Pro", date: "2 hours ago", status: "completed", initials: "SC" },
|
||||||
|
{ name: "Marcus Johnson", action: "Created new project", date: "4 hours ago", status: "completed", initials: "MJ" },
|
||||||
|
{ name: "Emily Rivera", action: "Submitted support ticket", date: "5 hours ago", status: "pending", initials: "ER" },
|
||||||
|
{ name: "David Kim", action: "Payment processed", date: "6 hours ago", status: "completed", initials: "DK" },
|
||||||
|
{ name: "Anna Petrov", action: "Account deactivated", date: "8 hours ago", status: "failed", initials: "AP" },
|
||||||
|
{ name: "James Wright", action: "Invited team member", date: "10 hours ago", status: "completed", initials: "JW" },
|
||||||
|
{ name: "Lisa Nguyen", action: "Exported data", date: "12 hours ago", status: "completed", initials: "LN" },
|
||||||
|
{ name: "Tom Bradley", action: "Updated billing info", date: "1 day ago", status: "pending", initials: "TB" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function DashboardPage() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen bg-slate-50">
|
||||||
|
<Sidebar activePath="/dashboard" />
|
||||||
|
<div className="flex-1 lg:ml-64">
|
||||||
|
<DashboardHeader />
|
||||||
|
<main className="p-6 space-y-6">
|
||||||
|
{/* KPI Cards */}
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
{kpis.map((kpi) => {
|
||||||
|
const Icon = kpi.icon;
|
||||||
|
return (
|
||||||
|
<Card key={kpi.label}>
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-slate-500">{kpi.label}</p>
|
||||||
|
<p className="text-2xl font-bold mt-1">{kpi.value}</p>
|
||||||
|
</div>
|
||||||
|
<div className={`h-10 w-10 rounded-full flex items-center justify-center \${kpi.color}`}>
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1 mt-2 text-sm">
|
||||||
|
{kpi.up ? <ArrowUpRight className="h-4 w-4 text-emerald-500" /> : <ArrowDownRight className="h-4 w-4 text-emerald-500" />}
|
||||||
|
<span className="text-emerald-600 font-medium">{kpi.trend}</span>
|
||||||
|
<span className="text-slate-400">vs last month</span>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Revenue Chart */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Revenue Overview</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="h-80">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<BarChart data={revenueData}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
|
||||||
|
<XAxis dataKey="month" stroke="#94a3b8" fontSize={12} />
|
||||||
|
<YAxis stroke="#94a3b8" fontSize={12} />
|
||||||
|
<Tooltip />
|
||||||
|
<Bar dataKey="revenue" fill="#6366f1" radius={[4, 4, 0, 0]} />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Recent Activity */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Recent Activity</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>User</TableHead>
|
||||||
|
<TableHead>Action</TableHead>
|
||||||
|
<TableHead>Date</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{activities.map((a, i) => (
|
||||||
|
<TableRow key={i}>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Avatar className="h-8 w-8">
|
||||||
|
<AvatarFallback className="bg-indigo-100 text-indigo-600 text-xs">{a.initials}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<span className="font-medium">{a.name}</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-slate-500">{a.action}</TableCell>
|
||||||
|
<TableCell className="text-slate-400">{a.date}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge variant={a.status === "completed" ? "default" : a.status === "pending" ? "secondary" : "destructive"}>
|
||||||
|
{a.status}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Quick Actions */}
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
|
<Card className="cursor-pointer hover:shadow-md transition-shadow">
|
||||||
|
<CardContent className="p-6 flex items-center gap-4">
|
||||||
|
<div className="h-10 w-10 rounded-full bg-indigo-100 flex items-center justify-center">
|
||||||
|
<Plus className="h-5 w-5 text-indigo-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-semibold">New Project</p>
|
||||||
|
<p className="text-sm text-slate-500">Create a new project</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card className="cursor-pointer hover:shadow-md transition-shadow">
|
||||||
|
<CardContent className="p-6 flex items-center gap-4">
|
||||||
|
<div className="h-10 w-10 rounded-full bg-emerald-100 flex items-center justify-center">
|
||||||
|
<UserPlus className="h-5 w-5 text-emerald-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-semibold">Invite Team</p>
|
||||||
|
<p className="text-sm text-slate-500">Add team members</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card className="cursor-pointer hover:shadow-md transition-shadow">
|
||||||
|
<CardContent className="p-6 flex items-center gap-4">
|
||||||
|
<div className="h-10 w-10 rounded-full bg-violet-100 flex items-center justify-center">
|
||||||
|
<Download className="h-5 w-5 text-violet-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-semibold">Export Report</p>
|
||||||
|
<p className="text-sm text-slate-500">Download analytics</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
ArrowRight, CheckCircle2, Zap, Shield, BarChart3, Users,
|
||||||
|
Layers, Globe, Workflow, BrainCircuit, GitBranch, Blocks
|
||||||
|
} from "lucide-react";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
const featureSections = [
|
||||||
|
{
|
||||||
|
badge: "Automation",
|
||||||
|
title: "Intelligent Automation That Learns",
|
||||||
|
desc: "Our AI engine observes how your team works and continuously optimizes task routing, resource allocation, and sprint planning.",
|
||||||
|
bullets: [
|
||||||
|
"AI-powered task assignment based on team capacity and expertise",
|
||||||
|
"Automated status updates and progress tracking",
|
||||||
|
"Smart deadline predictions using historical sprint data",
|
||||||
|
],
|
||||||
|
icon: BrainCircuit,
|
||||||
|
gradient: "from-indigo-500 to-purple-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
badge: "Collaboration",
|
||||||
|
title: "Collaboration Without Boundaries",
|
||||||
|
desc: "Break down silos with real-time collaboration tools that keep remote, hybrid, and in-office teams perfectly in sync.",
|
||||||
|
bullets: [
|
||||||
|
"Real-time document editing with inline comments",
|
||||||
|
"Video huddles and screen sharing built right in",
|
||||||
|
"Cross-team visibility with shared project boards",
|
||||||
|
],
|
||||||
|
icon: Users,
|
||||||
|
gradient: "from-cyan-500 to-blue-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
badge: "Analytics",
|
||||||
|
title: "Analytics That Drive Decisions",
|
||||||
|
desc: "Go beyond vanity metrics. Get actionable insights with AI-powered predictions and automated reporting.",
|
||||||
|
bullets: [
|
||||||
|
"Customizable dashboards with drag-and-drop widgets",
|
||||||
|
"Predictive burn-down charts and velocity tracking",
|
||||||
|
"Automated weekly reports delivered to your inbox",
|
||||||
|
],
|
||||||
|
icon: BarChart3,
|
||||||
|
gradient: "from-emerald-500 to-teal-600",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const integrations = [
|
||||||
|
"Slack", "GitHub", "Figma", "Jira", "Google Drive",
|
||||||
|
"Notion", "Zoom", "Linear", "Salesforce", "HubSpot", "Stripe", "Zapier",
|
||||||
|
];
|
||||||
|
|
||||||
|
const stats = [
|
||||||
|
{ value: "10M+", label: "Tasks completed" },
|
||||||
|
{ value: "200+", label: "Integrations" },
|
||||||
|
{ value: "150+", label: "Countries" },
|
||||||
|
{ value: "60%", label: "Faster delivery" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function FeaturesPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white text-slate-900 antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-950 via-indigo-950 to-slate-900">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-indigo-500/10 text-indigo-300 border border-indigo-500/20">Platform</Badge>
|
||||||
|
<h1 className="animate-fade-up text-4xl font-bold tracking-tight text-white sm:text-5xl">Platform Features</h1>
|
||||||
|
<p className="mt-4 text-lg text-slate-400 max-w-2xl mx-auto">
|
||||||
|
Discover every tool your team needs to plan, build, and ship world-class products.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── FEATURE SECTIONS (Alternating) ── */}
|
||||||
|
{featureSections.map((section, i) => (
|
||||||
|
<section key={section.title} className={`py-24 \${i % 2 === 1 ? "bg-slate-50" : "bg-white"}`}>
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||||
|
<div className={i % 2 === 1 ? "lg:order-2" : ""}>
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">{section.badge}</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight">{section.title}</h2>
|
||||||
|
<p className="mt-4 text-lg text-slate-500 leading-relaxed">{section.desc}</p>
|
||||||
|
<ul className="mt-8 space-y-4">
|
||||||
|
{section.bullets.map((b) => (
|
||||||
|
<li key={b} className="flex items-start gap-3">
|
||||||
|
<CheckCircle2 className="h-5 w-5 text-indigo-600 mt-0.5 flex-shrink-0" />
|
||||||
|
<span className="text-slate-600">{b}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<Button asChild className="active:scale-[0.98] mt-8 rounded-full bg-indigo-600 hover:bg-indigo-500">
|
||||||
|
<Link to="/pricing">Learn More <ArrowRight className="ml-2 h-4 w-4" /></Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className={i % 2 === 1 ? "lg:order-1" : ""}>
|
||||||
|
<div className={`animate-scale-in aspect-[4/3] rounded-2xl bg-gradient-to-br \${section.gradient} flex items-center justify-center shadow-2xl shadow-indigo-500/10 relative overflow-hidden`}>
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-60" />
|
||||||
|
<div className="absolute -top-10 -right-10 w-40 h-40 rounded-full bg-white/10 blur-3xl" />
|
||||||
|
<div className="absolute -bottom-10 -left-10 w-32 h-32 rounded-full bg-white/10 blur-3xl" />
|
||||||
|
<section.icon className="relative h-24 w-24 text-white/30" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* ── INTEGRATIONS ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-cyan-50 text-cyan-600 border-0">Integrations</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight sm:text-4xl">Works with your favorite tools</h2>
|
||||||
|
<p className="mt-4 text-slate-500 max-w-2xl mx-auto">Connect Nexus to the tools you already use. 200+ integrations and growing.</p>
|
||||||
|
<div className="mt-12 grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-4 max-w-4xl mx-auto stagger">
|
||||||
|
{integrations.map((name) => (
|
||||||
|
<div key={name} className="flex items-center justify-center h-20 rounded-xl border border-slate-200 hover:border-indigo-200 hover:shadow-md hover:scale-110 transition-all duration-300 bg-white">
|
||||||
|
<span className="text-sm font-medium text-slate-600">{name}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── STATS ── */}
|
||||||
|
<section className="py-20 bg-gradient-to-br from-indigo-600 to-slate-900">
|
||||||
|
<div className="mx-auto max-w-5xl px-6">
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
|
||||||
|
{stats.map((s) => (
|
||||||
|
<div key={s.label}>
|
||||||
|
<div className="text-4xl font-bold text-white">{s.value}</div>
|
||||||
|
<div className="mt-2 text-sm text-indigo-200">{s.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Layers, ArrowLeft, Mail, CheckCircle2, KeyRound } from "lucide-react";
|
||||||
|
|
||||||
|
export default function ForgotPasswordPage() {
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [sent, setSent] = useState(false);
|
||||||
|
const [code, setCode] = useState("");
|
||||||
|
const [newPassword, setNewPassword] = useState("");
|
||||||
|
const [step, setStep] = useState<"email" | "code" | "done">("email");
|
||||||
|
|
||||||
|
const handleSendCode = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setSent(true);
|
||||||
|
toast.success("Reset code sent to " + email);
|
||||||
|
setStep("code");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetPassword = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
toast.success("Password reset successfully!");
|
||||||
|
setStep("done");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-indigo-950 to-slate-900 flex items-center justify-center p-6">
|
||||||
|
<div className="w-full max-w-md">
|
||||||
|
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
||||||
|
<div className="h-10 w-10 rounded-xl bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center">
|
||||||
|
<Layers className="h-5 w-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<span className="text-2xl font-bold text-white tracking-tight">Nexus</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Card className="border-slate-800 bg-slate-900/50 backdrop-blur-xl shadow-2xl">
|
||||||
|
<CardContent className="p-8">
|
||||||
|
{step === "email" && (
|
||||||
|
<>
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<div className="mx-auto w-12 h-12 rounded-full bg-indigo-500/10 flex items-center justify-center mb-4">
|
||||||
|
<Mail className="h-6 w-6 text-indigo-400" />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-white">Forgot password?</h1>
|
||||||
|
<p className="text-slate-400 mt-1 text-sm">No worries, we'll send you a reset code</p>
|
||||||
|
</div>
|
||||||
|
<form onSubmit={handleSendCode} className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="reset-email" className="text-slate-300 text-sm">Email</Label>
|
||||||
|
<Input id="reset-email" type="email" required placeholder="you@company.com" value={email} onChange={(e) => setEmail(e.target.value)} className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500" />
|
||||||
|
</div>
|
||||||
|
<Button type="submit" className="w-full bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg font-semibold">Send Reset Code</Button>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === "code" && (
|
||||||
|
<>
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<div className="mx-auto w-12 h-12 rounded-full bg-emerald-500/10 flex items-center justify-center mb-4">
|
||||||
|
<KeyRound className="h-6 w-6 text-emerald-400" />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-white">Enter reset code</h1>
|
||||||
|
<p className="text-slate-400 mt-1 text-sm">Check your email for a 6-digit code</p>
|
||||||
|
</div>
|
||||||
|
<form onSubmit={handleResetPassword} className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="code" className="text-slate-300 text-sm">Reset Code</Label>
|
||||||
|
<Input id="code" type="text" required placeholder="000000" maxLength={6} value={code} onChange={(e) => setCode(e.target.value)} className="mt-1.5 bg-slate-800/50 border-slate-700 text-white text-center text-2xl tracking-[0.5em] placeholder:text-slate-500 placeholder:tracking-[0.5em] focus:border-indigo-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="new-password" className="text-slate-300 text-sm">New Password</Label>
|
||||||
|
<Input id="new-password" type="password" required placeholder="Enter new password" minLength={8} value={newPassword} onChange={(e) => setNewPassword(e.target.value)} className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500" />
|
||||||
|
</div>
|
||||||
|
<Button type="submit" className="w-full bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg font-semibold">Reset Password</Button>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === "done" && (
|
||||||
|
<div className="text-center py-4">
|
||||||
|
<div className="mx-auto w-12 h-12 rounded-full bg-emerald-500/10 flex items-center justify-center mb-4">
|
||||||
|
<CheckCircle2 className="h-6 w-6 text-emerald-400" />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-white">Password reset!</h1>
|
||||||
|
<p className="text-slate-400 mt-2 text-sm">Your password has been reset successfully</p>
|
||||||
|
<Link to="/login">
|
||||||
|
<Button className="mt-6 bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg font-semibold">Back to Sign In</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mt-6 text-center">
|
||||||
|
<Link to="/login" className="text-sm text-slate-400 hover:text-white inline-flex items-center gap-1">
|
||||||
|
<ArrowLeft className="h-3 w-3" /> Back to sign in
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
ArrowRight, Zap, Shield, BarChart3, Users, Layers, Globe,
|
||||||
|
Star, CheckCircle2, Sparkles, ChevronRight, Play, Quote, Lock
|
||||||
|
} from "lucide-react";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
const metrics = [
|
||||||
|
{ value: "10,000+", label: "Teams worldwide" },
|
||||||
|
{ value: "99.9%", label: "Uptime SLA" },
|
||||||
|
{ value: "4.9/5", label: "User rating" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const logos = [
|
||||||
|
"Acme Corp", "TechFlow", "Quantum",
|
||||||
|
"Synapse", "NovaBuild", "Orbitix",
|
||||||
|
];
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{ icon: Zap, title: "AI Task Automation", desc: "Automatically prioritize, assign, and schedule tasks using machine learning trained on your team habits." },
|
||||||
|
{ icon: Shield, title: "Enterprise Security", desc: "SOC 2 Type II compliant with end-to-end encryption, SSO, and role-based access controls." },
|
||||||
|
{ icon: BarChart3, title: "Advanced Analytics", desc: "Real-time dashboards with predictive insights to spot bottlenecks before they happen." },
|
||||||
|
{ icon: Users, title: "Team Collaboration", desc: "Real-time editing, threaded comments, and @mentions keep everyone aligned." },
|
||||||
|
{ icon: Layers, title: "Custom Workflows", desc: "Build automation workflows with a drag-and-drop builder. No code required." },
|
||||||
|
{ icon: Globe, title: "Global Scale", desc: "Multi-region deployment with 99.99% uptime guarantee and <100ms response times." },
|
||||||
|
];
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
{ num: "01", title: "Create your workspace", desc: "Sign up in seconds and invite your team. Import existing projects from Jira, Asana, or Trello." },
|
||||||
|
{ num: "02", title: "Set up your workflows", desc: "Choose from 50+ templates or build custom workflows. Our AI suggests the best setup for your team." },
|
||||||
|
{ num: "03", title: "Ship with confidence", desc: "Track progress with real-time dashboards. Get AI-powered predictions on delivery timelines." },
|
||||||
|
];
|
||||||
|
|
||||||
|
const testimonials = [
|
||||||
|
{ name: "Sarah Chen", role: "VP Engineering, TechFlow", quote: "Nexus cut our sprint planning time by 60%. The AI suggestions are scarily accurate.", stars: 5 },
|
||||||
|
{ name: "Marcus Rivera", role: "CTO, BuildScale", quote: "We migrated from three different tools to Nexus. Best decision we made all year.", stars: 5 },
|
||||||
|
{ name: "Emily Watson", role: "Product Lead, Orbitix", quote: "The predictive analytics alone are worth the price. We ship 2x faster now.", stars: 5 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function IndexPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white text-slate-900 antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section className="relative overflow-hidden bg-gradient-to-br from-slate-950 via-indigo-950 to-slate-900 pt-32 pb-24 sm:pt-40 sm:pb-32">
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-indigo-900/20 via-transparent to-transparent" />
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-40" />
|
||||||
|
{/* Floating gradient blur circles */}
|
||||||
|
<div className="absolute top-20 left-1/4 w-72 h-72 rounded-full bg-indigo-600/15 blur-[100px] pointer-events-none" />
|
||||||
|
<div className="absolute top-40 right-1/4 w-96 h-96 rounded-full bg-cyan-500/15 blur-[100px] pointer-events-none" />
|
||||||
|
<div className="absolute bottom-10 left-1/3 w-80 h-80 rounded-full bg-violet-500/20 blur-[100px] pointer-events-none" />
|
||||||
|
<div className="absolute -top-10 right-1/3 w-64 h-64 rounded-full bg-blue-500/15 blur-[100px] pointer-events-none" />
|
||||||
|
<div className="relative mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="animate-fade-up mb-6 bg-primary/10 text-primary border-primary/20 px-4 py-1.5 text-sm font-medium inline-flex items-center">
|
||||||
|
<Sparkles className="mr-1.5 h-3.5 w-3.5" /> Now with AI Copilot
|
||||||
|
</Badge>
|
||||||
|
<h1 className="mx-auto max-w-4xl text-4xl font-bold tracking-tight text-white sm:text-5xl lg:text-7xl">
|
||||||
|
<span className="bg-gradient-to-r from-white via-indigo-200 to-cyan-200 bg-clip-text text-transparent">
|
||||||
|
Manage Projects with the Power of AI
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<p className="mx-auto mt-6 max-w-2xl text-lg text-slate-400 leading-relaxed">
|
||||||
|
Streamline your workflow with intelligent automation, real-time collaboration, and predictive analytics that keep your team ahead of every deadline.
|
||||||
|
</p>
|
||||||
|
<div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||||
|
<Button asChild size="lg" className="active:scale-[0.98] bg-indigo-600 hover:bg-indigo-500 text-white px-8 rounded-full shadow-lg shadow-indigo-500/25 text-base">
|
||||||
|
<Link to="/signup">Start Free Trial <ArrowRight className="ml-2 h-4 w-4" /></Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild variant="outline" size="lg" className="active:scale-[0.98] rounded-full border-slate-700 text-slate-300 hover:bg-slate-800 hover:text-white text-base">
|
||||||
|
<Link to="/features"><Play className="mr-2 h-4 w-4" /> Watch Demo</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Metrics */}
|
||||||
|
<div className="mt-16 flex flex-wrap justify-center gap-6">
|
||||||
|
{metrics.map((m) => (
|
||||||
|
<div key={m.label} className="text-center px-8 py-4 rounded-2xl bg-white/5 backdrop-blur-sm border border-white/10">
|
||||||
|
<div className="text-3xl font-bold text-white">{m.value}</div>
|
||||||
|
<div className="mt-1 text-sm text-slate-400">{m.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Trust / Security Badges */}
|
||||||
|
<div className="flex flex-wrap justify-center gap-4 mt-8">
|
||||||
|
<Badge variant="outline" className="text-xs gap-1.5 text-slate-400 border-slate-700 px-3 py-1.5">
|
||||||
|
<Shield className="h-3 w-3" /> SOC 2 Certified
|
||||||
|
</Badge>
|
||||||
|
<Badge variant="outline" className="text-xs gap-1.5 text-slate-400 border-slate-700 px-3 py-1.5">
|
||||||
|
<Lock className="h-3 w-3" /> GDPR Compliant
|
||||||
|
</Badge>
|
||||||
|
<Badge variant="outline" className="text-xs gap-1.5 text-slate-400 border-slate-700 px-3 py-1.5">
|
||||||
|
<CheckCircle2 className="h-3 w-3" /> 99.9% Uptime SLA
|
||||||
|
</Badge>
|
||||||
|
<Badge variant="outline" className="text-xs gap-1.5 text-slate-400 border-slate-700 px-3 py-1.5">
|
||||||
|
<Lock className="h-3 w-3" /> 256-bit Encryption
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── LOGO CLOUD ── */}
|
||||||
|
<section className="py-14 bg-slate-50 border-b border-slate-100">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<p className="text-center text-sm font-medium text-slate-400 mb-8">Trusted by industry-leading teams worldwide</p>
|
||||||
|
<div className="flex flex-wrap items-center justify-center gap-x-12 gap-y-6">
|
||||||
|
{logos.map((logo) => (
|
||||||
|
<div key={logo} className="text-lg font-bold text-slate-300 tracking-wider uppercase">{logo}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── VIDEO DEMO ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="aspect-video max-w-4xl mx-auto rounded-2xl bg-gradient-to-br from-indigo-900 via-slate-900 to-cyan-900 relative overflow-hidden group cursor-pointer shadow-2xl shadow-indigo-500/10">
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-indigo-500/10 via-transparent to-transparent" />
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-40" />
|
||||||
|
<div className="absolute inset-0 flex flex-col items-center justify-center gap-4">
|
||||||
|
<div className="h-20 w-20 rounded-full bg-white/10 backdrop-blur-sm flex items-center justify-center group-hover:scale-110 group-hover:bg-white/20 transition-all duration-300 ring-1 ring-white/20 animate-pulse">
|
||||||
|
<Play className="h-8 w-8 text-white ml-1" />
|
||||||
|
</div>
|
||||||
|
<span className="text-white/80 text-sm font-medium backdrop-blur-sm bg-white/10 px-4 py-1.5 rounded-full border border-white/10">See it in action</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-center mt-6 text-slate-500 text-sm">Watch how teams ship 3x faster with Nexus</p>
|
||||||
|
<p className="text-center mt-2 text-xs text-slate-400">Trusted by teams at Google, Stripe, Vercel</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── FEATURES GRID ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Features</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight sm:text-4xl">Everything you need to ship faster</h2>
|
||||||
|
<p className="mt-4 text-lg text-slate-500 max-w-2xl mx-auto">Powerful tools designed to help your team collaborate, automate, and deliver results.</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 stagger">
|
||||||
|
{features.map((f) => (
|
||||||
|
<Card key={f.title} className="hover-card-lift border-slate-200 hover:border-indigo-200 hover:shadow-lg hover:shadow-indigo-50 hover:bg-gradient-to-b hover:from-primary/5 hover:to-transparent transition-all duration-300 group">
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<div className="h-12 w-12 rounded-xl bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform shadow-lg shadow-primary/10">
|
||||||
|
<f.icon className="h-6 w-6 text-white" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold mb-2">{f.title}</h3>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed">{f.desc}</p>
|
||||||
|
<span className="inline-flex items-center gap-1 mt-4 text-sm font-medium text-indigo-600 group-hover:translate-x-1 transition-transform cursor-pointer">
|
||||||
|
Learn more <ArrowRight className="h-3.5 w-3.5" />
|
||||||
|
</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-12 text-center">
|
||||||
|
<Button asChild variant="outline" className="active:scale-[0.98] rounded-full">
|
||||||
|
<Link to="/features">Explore All Features <ChevronRight className="ml-1 h-4 w-4" /></Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── HOW IT WORKS ── */}
|
||||||
|
<section className="py-24 bg-slate-50">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<Badge className="mb-4 bg-cyan-50 text-cyan-600 border-0">How It Works</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight sm:text-4xl">Get started in minutes</h2>
|
||||||
|
</div>
|
||||||
|
<div className="relative max-w-3xl mx-auto">
|
||||||
|
{/* Connecting line */}
|
||||||
|
<div className="absolute left-8 top-12 bottom-12 w-px bg-gradient-to-b from-indigo-500 via-cyan-500 to-indigo-500 hidden md:block" />
|
||||||
|
<div className="space-y-12">
|
||||||
|
{steps.map((step) => (
|
||||||
|
<div key={step.num} className="flex gap-6 items-start">
|
||||||
|
<div className="relative flex-shrink-0 h-16 w-16 rounded-2xl bg-gradient-to-br from-indigo-600 to-cyan-500 flex items-center justify-center text-white font-bold text-lg shadow-lg shadow-indigo-500/20">
|
||||||
|
{step.num}
|
||||||
|
</div>
|
||||||
|
<div className="pt-2">
|
||||||
|
<h3 className="text-xl font-semibold mb-2">{step.title}</h3>
|
||||||
|
<p className="text-slate-500 leading-relaxed">{step.desc}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── TESTIMONIALS ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<Badge className="mb-4 bg-amber-50 text-amber-600 border-0">Testimonials</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight sm:text-4xl">Loved by teams everywhere</h2>
|
||||||
|
</div>
|
||||||
|
<div className="grid md:grid-cols-3 gap-6 stagger">
|
||||||
|
{testimonials.map((t) => (
|
||||||
|
<Card key={t.name} className="backdrop-blur-sm bg-card/80 border-border/50 hover-card-lift transition-all duration-300 group">
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<Quote className="h-5 w-5 text-indigo-300/50 mb-3" />
|
||||||
|
<div className="flex gap-0.5 mb-4">
|
||||||
|
{Array.from({ length: t.stars }, (_, i) => (
|
||||||
|
<Star key={i} className="h-4 w-4 fill-amber-400 text-amber-400" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="text-slate-600 leading-relaxed mb-6">"{t.quote}"</p>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-indigo-400 to-cyan-400 flex items-center justify-center text-white font-semibold text-sm">
|
||||||
|
{t.name.split(" ").map((n) => n[0]).join("")}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="font-semibold text-sm">{t.name}</div>
|
||||||
|
<div className="text-xs text-slate-400">{t.role}</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-8 w-8 rounded-full bg-gradient-to-br from-slate-200 to-slate-300 flex items-center justify-center text-slate-500 font-bold text-[10px]">
|
||||||
|
{t.role.split(" at ").pop()?.slice(0, 2).toUpperCase() || "Co"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="inline-flex items-center gap-1 mt-4 text-xs font-medium text-indigo-600 group-hover:translate-x-1 transition-transform cursor-pointer">
|
||||||
|
Read case study <ArrowRight className="h-3 w-3" />
|
||||||
|
</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── INTEGRATIONS ── */}
|
||||||
|
<section className="py-24 bg-slate-50">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-cyan-50 text-cyan-600 border-0">Integrations</Badge>
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold tracking-tight sm:text-4xl">Works with tools you already love</h2>
|
||||||
|
<p className="mt-4 text-slate-500 max-w-2xl mx-auto">Connect with 200+ tools your team relies on every day.</p>
|
||||||
|
<div className="mt-12 grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-4 max-w-4xl mx-auto stagger">
|
||||||
|
{logos.map((logo) => (
|
||||||
|
<div key={logo} className="flex items-center justify-center h-24 rounded-xl border border-slate-200 bg-white grayscale hover:grayscale-0 hover:border-indigo-200 hover:shadow-lg hover:scale-110 transition-all duration-300 cursor-pointer group" title={logo}>
|
||||||
|
<span className="text-base font-semibold text-slate-500 group-hover:text-indigo-600 transition-colors">{logo}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Badge className="mt-8 bg-indigo-50 text-indigo-600 border-0 text-sm px-4 py-1.5">50+ integrations</Badge>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── CTA ── */}
|
||||||
|
<section className="py-24 bg-gradient-to-br from-indigo-600 via-indigo-700 to-slate-900">
|
||||||
|
<div className="mx-auto max-w-3xl px-6 text-center">
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold text-white sm:text-4xl">Ready to transform your workflow?</h2>
|
||||||
|
<p className="mt-4 text-lg text-indigo-200">Join 10,000+ teams already using Nexus to ship faster and smarter.</p>
|
||||||
|
<div className="mt-10 flex flex-col sm:flex-row justify-center gap-4">
|
||||||
|
<Button asChild size="lg" className="active:scale-[0.98] bg-white text-indigo-700 hover:bg-indigo-50 rounded-full px-8 shadow-lg text-base font-semibold">
|
||||||
|
<Link to="/signup">Start Free Trial <ArrowRight className="ml-2 h-4 w-4" /></Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild variant="outline" size="lg" className="active:scale-[0.98] rounded-full border-white/30 text-white hover:bg-white/10 text-base">
|
||||||
|
<Link to="/contact">Talk to Sales</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
|
||||||
|
<Badge className="bg-white/10 text-white/80 border border-white/20 text-xs px-3 py-1">SOC 2</Badge>
|
||||||
|
<Badge className="bg-white/10 text-white/80 border border-white/20 text-xs px-3 py-1">GDPR</Badge>
|
||||||
|
<Badge className="bg-white/10 text-white/80 border border-white/20 text-xs px-3 py-1">99.9% Uptime</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="mt-4 text-sm text-indigo-300">No credit card required. 14-day free trial.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { Layers, CheckCircle2 } from "lucide-react";
|
||||||
|
|
||||||
|
export default function LoginPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [success, setSuccess] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-indigo-950 to-slate-900 flex items-center justify-center p-6">
|
||||||
|
<div className="w-full max-w-md">
|
||||||
|
{/* Logo */}
|
||||||
|
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
||||||
|
<div className="h-10 w-10 rounded-xl bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center">
|
||||||
|
<Layers className="h-5 w-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<span className="text-2xl font-bold text-white tracking-tight">Nexus</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Card className="border-slate-800 bg-slate-900/50 backdrop-blur-xl shadow-2xl">
|
||||||
|
<CardContent className="p-8">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h1 className="text-2xl font-bold text-white">Welcome back</h1>
|
||||||
|
<p className="text-slate-400 mt-1 text-sm">Sign in to your account to continue</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{success && (
|
||||||
|
<div className="mb-6 flex items-center gap-2 rounded-lg bg-green-500/10 border border-green-500/20 px-4 py-3 text-sm text-green-400">
|
||||||
|
<CheckCircle2 className="h-4 w-4 flex-shrink-0" /> Sign in successful! Redirecting...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={(e) => { e.preventDefault(); setSuccess(true); toast.success("Welcome back!"); setTimeout(() => navigate("/dashboard"), 1500); }} className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="email" className="text-slate-300 text-sm">Email</Label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
placeholder="you@company.com"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500 focus:ring-indigo-500/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Label htmlFor="password" className="text-slate-300 text-sm">Password</Label>
|
||||||
|
<Link to="/forgot-password" className="text-xs text-indigo-400 hover:text-indigo-300">Forgot password?</Link>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
placeholder="Enter your password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500 focus:ring-indigo-500/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" className="active:scale-[0.98] w-full bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg mt-2 font-semibold">
|
||||||
|
Sign In
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="relative my-6">
|
||||||
|
<Separator className="bg-slate-700" />
|
||||||
|
<span className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-slate-900 px-3 text-xs text-slate-500">or</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button variant="outline" className="active:scale-[0.98] w-full border-slate-700 text-slate-300 hover:bg-slate-800 hover:text-white rounded-lg">
|
||||||
|
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
||||||
|
Sign in with Google
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<p className="mt-6 text-center text-sm text-slate-400">
|
||||||
|
Don't have an account?{" "}
|
||||||
|
<Link to="/signup" className="text-indigo-400 hover:text-indigo-300 font-medium">Sign up</Link>
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
||||||
|
import { Check, X, ArrowRight, Sparkles, Shield } from "lucide-react";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
const tiers = [
|
||||||
|
{
|
||||||
|
name: "Starter",
|
||||||
|
monthlyPrice: 29,
|
||||||
|
annualPrice: 24,
|
||||||
|
description: "Perfect for small teams getting started.",
|
||||||
|
features: ["Up to 10 team members", "5 projects", "Basic analytics", "Email support", "1 GB storage"],
|
||||||
|
cta: "Start Free Trial",
|
||||||
|
popular: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Professional",
|
||||||
|
monthlyPrice: 79,
|
||||||
|
annualPrice: 64,
|
||||||
|
description: "For growing teams that need more power.",
|
||||||
|
features: ["Up to 50 team members", "Unlimited projects", "Advanced analytics", "Priority support", "50 GB storage", "Custom workflows", "API access"],
|
||||||
|
cta: "Start Free Trial",
|
||||||
|
popular: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Enterprise",
|
||||||
|
monthlyPrice: 199,
|
||||||
|
annualPrice: 159,
|
||||||
|
description: "For organizations with advanced needs.",
|
||||||
|
features: ["Unlimited team members", "Unlimited projects", "AI-powered analytics", "24/7 dedicated support", "Unlimited storage", "Custom workflows", "API access", "SSO & SAML", "Custom SLA"],
|
||||||
|
cta: "Contact Sales",
|
||||||
|
popular: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const comparisonFeatures = [
|
||||||
|
{ name: "Team members", starter: "10", pro: "50", enterprise: "Unlimited" },
|
||||||
|
{ name: "Projects", starter: "5", pro: "Unlimited", enterprise: "Unlimited" },
|
||||||
|
{ name: "Storage", starter: "1 GB", pro: "50 GB", enterprise: "Unlimited" },
|
||||||
|
{ name: "Unlimited projects", starter: false, pro: true, enterprise: true },
|
||||||
|
{ name: "API access", starter: false, pro: true, enterprise: true },
|
||||||
|
{ name: "Priority support", starter: false, pro: true, enterprise: true },
|
||||||
|
{ name: "Custom integrations", starter: false, pro: true, enterprise: true },
|
||||||
|
{ name: "SSO/SAML", starter: false, pro: false, enterprise: true },
|
||||||
|
{ name: "Dedicated manager", starter: false, pro: false, enterprise: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const faqs = [
|
||||||
|
{ q: "Can I try Nexus for free?", a: "Yes! Every plan comes with a 14-day free trial. No credit card required to get started." },
|
||||||
|
{ q: "Can I switch plans later?", a: "Absolutely. You can upgrade or downgrade your plan at any time. Changes take effect immediately." },
|
||||||
|
{ q: "What payment methods do you accept?", a: "We accept all major credit cards, ACH bank transfers, and wire transfers for Enterprise plans." },
|
||||||
|
{ q: "Is there a discount for nonprofits?", a: "Yes, we offer a 50% discount for registered nonprofits and educational institutions. Contact sales for details." },
|
||||||
|
{ q: "How does the AI task automation work?", a: "Our AI learns from your team patterns and automatically suggests task assignments, priorities, and deadlines based on historical data." },
|
||||||
|
{ q: "What happens when my trial ends?", a: "Your data is saved for 30 days after trial expiry. Choose a plan to continue, or export your data at any time." },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function PricingPage() {
|
||||||
|
const [billing, setBilling] = useState("monthly");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white text-slate-900 antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-50 to-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 text-center">
|
||||||
|
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Pricing</Badge>
|
||||||
|
<h1 className="animate-fade-up text-4xl font-bold tracking-tight sm:text-5xl">Simple, transparent pricing</h1>
|
||||||
|
<p className="mt-4 text-lg text-slate-500 max-w-2xl mx-auto">No hidden fees. No surprise charges. Choose the plan that fits your team.</p>
|
||||||
|
<div className="mt-8 flex justify-center">
|
||||||
|
<Tabs value={billing} onValueChange={setBilling} className="bg-slate-100 rounded-full p-1">
|
||||||
|
<TabsList className="bg-transparent">
|
||||||
|
<TabsTrigger value="monthly" className="rounded-full px-6 data-[state=active]:bg-white data-[state=active]:shadow-sm">Monthly</TabsTrigger>
|
||||||
|
<TabsTrigger value="annual" className="rounded-full px-6 data-[state=active]:bg-white data-[state=active]:shadow-sm">
|
||||||
|
Annual <Badge className="ml-2 bg-green-100 text-green-700 border-0 text-xs">Save 20%</Badge>
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── PRICING CARDS ── */}
|
||||||
|
<section className="pb-24">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
<div className="grid md:grid-cols-3 gap-6 max-w-5xl mx-auto stagger">
|
||||||
|
{tiers.map((tier) => (
|
||||||
|
<Card key={tier.name} className={`relative flex flex-col hover:border-primary/50 hover:shadow-xl transition-all duration-300 \${tier.popular ? "ring-2 ring-indigo-600 shadow-xl shadow-indigo-100" : "border-slate-200"}`}>
|
||||||
|
{tier.popular && (
|
||||||
|
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-primary text-primary-foreground px-4 py-1 rounded-full text-sm font-medium shadow-lg flex items-center gap-1.5">
|
||||||
|
<Sparkles className="h-3.5 w-3.5" /> Most Popular
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<CardHeader className="pb-4 pt-8">
|
||||||
|
<CardTitle className="text-xl">{tier.name}</CardTitle>
|
||||||
|
<p className="text-sm text-slate-500 mt-1">{tier.description}</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<span className="text-4xl font-bold">\${billing === "monthly" ? tier.monthlyPrice : tier.annualPrice}</span>
|
||||||
|
<span className="text-slate-500 ml-1">/mo</span>
|
||||||
|
</div>
|
||||||
|
{billing === "annual" && (
|
||||||
|
<p className="text-sm text-green-600 mt-1">Billed annually (\${tier.annualPrice * 12}/yr)</p>
|
||||||
|
)}
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex-1 flex flex-col">
|
||||||
|
<ul className="space-y-3 flex-1">
|
||||||
|
{tier.features.map((f) => (
|
||||||
|
<li key={f} className="flex items-center gap-2 text-sm">
|
||||||
|
<Check className="h-4 w-4 text-indigo-600 flex-shrink-0" />
|
||||||
|
<span>{f}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<Button asChild className={`mt-8 w-full rounded-full active:scale-[0.98] \${tier.popular ? "bg-indigo-600 hover:bg-indigo-500 text-white" : ""}`} variant={tier.popular ? "default" : "outline"}>
|
||||||
|
<Link to={tier.name === "Enterprise" ? "/contact" : "/signup"}>{tier.cta}</Link>
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="text-center mt-8 text-sm text-slate-500 flex items-center justify-center gap-1.5">
|
||||||
|
<Shield className="h-4 w-4 text-indigo-500" /> 30-day money-back guarantee. No questions asked.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── COMPARISON TABLE ── */}
|
||||||
|
<section className="py-24 bg-slate-50">
|
||||||
|
<div className="mx-auto max-w-5xl px-6">
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold text-center mb-12 tracking-tight sm:text-4xl">Compare Plans</h2>
|
||||||
|
<Card className="overflow-hidden border-slate-200">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-slate-50">
|
||||||
|
<TableHead className="w-1/3 font-semibold">Feature</TableHead>
|
||||||
|
<TableHead className="text-center font-semibold">Free</TableHead>
|
||||||
|
<TableHead className="text-center font-semibold text-indigo-600">Pro</TableHead>
|
||||||
|
<TableHead className="text-center font-semibold">Enterprise</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{comparisonFeatures.map((f) => (
|
||||||
|
<TableRow key={f.name}>
|
||||||
|
<TableCell className="font-medium">{f.name}</TableCell>
|
||||||
|
<TableCell className="text-center">
|
||||||
|
{typeof f.starter === "boolean" ? (f.starter ? <Check className="h-4 w-4 text-green-500 mx-auto" /> : <X className="h-4 w-4 text-slate-300 mx-auto" />) : f.starter}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-center bg-indigo-50/50">
|
||||||
|
{typeof f.pro === "boolean" ? (f.pro ? <Check className="h-4 w-4 text-green-500 mx-auto" /> : <X className="h-4 w-4 text-slate-300 mx-auto" />) : f.pro}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-center">
|
||||||
|
{typeof f.enterprise === "boolean" ? (f.enterprise ? <Check className="h-4 w-4 text-green-500 mx-auto" /> : <X className="h-4 w-4 text-slate-300 mx-auto" />) : f.enterprise}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── FAQ ── */}
|
||||||
|
<section className="py-24 bg-white">
|
||||||
|
<div className="mx-auto max-w-3xl px-6">
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold text-center mb-12 tracking-tight sm:text-4xl">Frequently Asked Questions</h2>
|
||||||
|
<Accordion type="single" collapsible className="space-y-3 stagger">
|
||||||
|
{faqs.map((faq, i) => (
|
||||||
|
<AccordionItem key={i} value={`faq-\${i}`} className="border border-slate-200 rounded-xl px-6 data-[state=open]:shadow-md transition-shadow">
|
||||||
|
<AccordionTrigger className="text-left font-medium hover:no-underline py-4">{faq.q}</AccordionTrigger>
|
||||||
|
<AccordionContent className="text-slate-500 pb-4">{faq.a}</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── CTA ── */}
|
||||||
|
<section className="py-24 bg-gradient-to-br from-indigo-600 to-slate-900">
|
||||||
|
<div className="mx-auto max-w-3xl px-6 text-center">
|
||||||
|
<h2 className="animate-fade-up text-3xl font-bold text-white sm:text-4xl">Start your free trial today</h2>
|
||||||
|
<p className="mt-4 text-indigo-200">14 days free. No credit card required. Cancel anytime.</p>
|
||||||
|
<Button asChild size="lg" className="active:scale-[0.98] mt-8 bg-white text-indigo-700 hover:bg-indigo-50 rounded-full px-8 shadow-lg font-semibold">
|
||||||
|
<Link to="/signup">Start Free Trial <ArrowRight className="ml-2 h-4 w-4" /></Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
import DashboardHeader from "@/components/DashboardHeader";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { Copy, UserPlus } from "lucide-react";
|
||||||
|
|
||||||
|
const notifications = [
|
||||||
|
{ label: "Email notifications", desc: "Receive email for important updates", defaultChecked: true },
|
||||||
|
{ label: "Push notifications", desc: "Browser push notifications", defaultChecked: true },
|
||||||
|
{ label: "Weekly digest", desc: "Summary of activity every Monday", defaultChecked: false },
|
||||||
|
{ label: "Marketing emails", desc: "Product news and offers", defaultChecked: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const apiKeys = [
|
||||||
|
{ name: "Production", key: "sk-prod-****-****-****-7f3a", created: "Jan 5, 2024" },
|
||||||
|
{ name: "Development", key: "sk-dev-****-****-****-2b8c", created: "Mar 12, 2024" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const teamMembers = [
|
||||||
|
{ name: "John Doe", email: "john@company.com", role: "Owner", initials: "JD" },
|
||||||
|
{ name: "Sarah Chen", email: "sarah@company.com", role: "Admin", initials: "SC" },
|
||||||
|
{ name: "Marcus Johnson", email: "marcus@company.com", role: "Member", initials: "MJ" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function SettingsPage() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen bg-slate-50">
|
||||||
|
<Sidebar activePath="/settings" />
|
||||||
|
<div className="flex-1 lg:ml-64">
|
||||||
|
<DashboardHeader />
|
||||||
|
<main className="p-6 space-y-6 max-w-4xl">
|
||||||
|
{/* Profile */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Profile</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<Avatar className="h-16 w-16">
|
||||||
|
<AvatarFallback className="bg-indigo-100 text-indigo-600 text-xl font-semibold">JD</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div>
|
||||||
|
<p className="font-semibold">John Doe</p>
|
||||||
|
<p className="text-sm text-slate-500">john@company.com</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<Label className="text-sm">Name</Label>
|
||||||
|
<Input defaultValue="John Doe" className="mt-1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label className="text-sm">Email</Label>
|
||||||
|
<Input defaultValue="john@company.com" className="mt-1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button className="bg-indigo-600 hover:bg-indigo-500 text-white">Save Changes</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Billing */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Billing</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-semibold">Current Plan:</span>
|
||||||
|
<Badge className="bg-indigo-100 text-indigo-700">Pro</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-slate-500 mt-1">\$29/mo billed monthly</p>
|
||||||
|
</div>
|
||||||
|
<Button variant="outline">Upgrade</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between text-sm mb-1">
|
||||||
|
<span className="text-slate-500">Usage this period</span>
|
||||||
|
<span className="font-medium">75%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-slate-100 rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-indigo-500 rounded-full" style={{ width: "75%" }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Notifications */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Notifications</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{notifications.map((n) => (
|
||||||
|
<div key={n.label} className="flex items-start gap-3">
|
||||||
|
<Checkbox defaultChecked={n.defaultChecked} className="mt-1" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">{n.label}</p>
|
||||||
|
<p className="text-xs text-slate-400">{n.desc}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* API Keys */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>API Keys</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
{apiKeys.map((k) => (
|
||||||
|
<div key={k.name} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">{k.name}</p>
|
||||||
|
<p className="text-xs text-slate-400 font-mono mt-0.5">{k.key}</p>
|
||||||
|
</div>
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<Copy className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Team */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between">
|
||||||
|
<CardTitle>Team</CardTitle>
|
||||||
|
<Button size="sm" className="bg-indigo-600 hover:bg-indigo-500 text-white">
|
||||||
|
<UserPlus className="h-4 w-4 mr-2" /> Invite
|
||||||
|
</Button>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
{teamMembers.map((m) => (
|
||||||
|
<div key={m.email} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Avatar className="h-8 w-8">
|
||||||
|
<AvatarFallback className="bg-indigo-100 text-indigo-600 text-xs">{m.initials}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">{m.name}</p>
|
||||||
|
<p className="text-xs text-slate-400">{m.email}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Badge variant={m.role === "Owner" ? "default" : m.role === "Admin" ? "secondary" : "outline"}>
|
||||||
|
{m.role}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { Layers, CheckCircle2 } from "lucide-react";
|
||||||
|
|
||||||
|
export default function SignupPage() {
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
|
const [terms, setTerms] = useState(false);
|
||||||
|
const [success, setSuccess] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-indigo-950 to-slate-900 flex items-center justify-center p-6">
|
||||||
|
<div className="w-full max-w-md">
|
||||||
|
{/* Logo */}
|
||||||
|
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
||||||
|
<div className="h-10 w-10 rounded-xl bg-gradient-to-br from-indigo-500 to-cyan-500 flex items-center justify-center">
|
||||||
|
<Layers className="h-5 w-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<span className="text-2xl font-bold text-white tracking-tight">Nexus</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Card className="border-slate-800 bg-slate-900/50 backdrop-blur-xl shadow-2xl">
|
||||||
|
<CardContent className="p-8">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h1 className="text-2xl font-bold text-white">Create your account</h1>
|
||||||
|
<p className="text-slate-400 mt-1 text-sm">Start your 14-day free trial</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{success && (
|
||||||
|
<div className="mb-6 flex items-center gap-2 rounded-lg bg-green-500/10 border border-green-500/20 px-4 py-3 text-sm text-green-400">
|
||||||
|
<CheckCircle2 className="h-4 w-4 flex-shrink-0" /> Account created! Redirecting to dashboard...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={(e) => { e.preventDefault(); setSuccess(true); toast.success("Account created! Check your email."); }} className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="name" className="text-slate-300 text-sm">Full name</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
required
|
||||||
|
placeholder="John Doe"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500 focus:ring-indigo-500/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="signup-email" className="text-slate-300 text-sm">Email</Label>
|
||||||
|
<Input
|
||||||
|
id="signup-email"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
placeholder="you@company.com"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500 focus:ring-indigo-500/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="signup-password" className="text-slate-300 text-sm">Password</Label>
|
||||||
|
<Input
|
||||||
|
id="signup-password"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
placeholder="Create a password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500 focus:ring-indigo-500/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="confirm-password" className="text-slate-300 text-sm">Confirm password</Label>
|
||||||
|
<Input
|
||||||
|
id="confirm-password"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
placeholder="Confirm your password"
|
||||||
|
value={confirmPassword}
|
||||||
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
|
className="mt-1.5 bg-slate-800/50 border-slate-700 text-white placeholder:text-slate-500 focus:border-indigo-500 focus:ring-indigo-500/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-2 pt-1">
|
||||||
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
required
|
||||||
|
checked={terms}
|
||||||
|
onCheckedChange={(checked) => setTerms(checked === true)}
|
||||||
|
className="border-slate-600 data-[state=checked]:bg-indigo-600 data-[state=checked]:border-indigo-600 mt-0.5"
|
||||||
|
/>
|
||||||
|
<Label htmlFor="terms" className="text-xs text-slate-400 leading-relaxed">
|
||||||
|
I agree to the <a href="#" className="text-indigo-400 hover:text-indigo-300">Terms of Service</a> and <a href="#" className="text-indigo-400 hover:text-indigo-300">Privacy Policy</a>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" className="active:scale-[0.98] w-full bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg mt-2 font-semibold">
|
||||||
|
Create Account
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="relative my-6">
|
||||||
|
<Separator className="bg-slate-700" />
|
||||||
|
<span className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-slate-900 px-3 text-xs text-slate-500">or</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button variant="outline" className="active:scale-[0.98] w-full border-slate-700 text-slate-300 hover:bg-slate-800 hover:text-white rounded-lg">
|
||||||
|
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
||||||
|
Sign up with Google
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<p className="mt-6 text-center text-sm text-slate-400">
|
||||||
|
Already have an account?{" "}
|
||||||
|
<Link to="/login" className="text-indigo-400 hover:text-indigo-300 font-medium">Sign in</Link>
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user