From 1cd690d7a30bf58a7ae628af1597f06610ef34f1 Mon Sep 17 00:00:00 2001 From: Joe Wee Date: Thu, 21 May 2026 12:02:13 +0100 Subject: [PATCH] Initial: saas template via tAI --- .claude/settings.json | 13 ++ CLAUDE.md | 31 ++++ README.md | 3 + index.html | 12 ++ src/App.tsx | 38 ++++ src/components/DashboardHeader.tsx | 52 ++++++ src/components/Footer.tsx | 85 +++++++++ src/components/Header.tsx | 103 +++++++++++ src/components/Sidebar.tsx | 72 ++++++++ src/index.css | 3 + src/main.tsx | 10 ++ src/pages/About.tsx | 148 +++++++++++++++ src/pages/Analytics.tsx | 127 +++++++++++++ src/pages/Blog.tsx | 142 +++++++++++++++ src/pages/Contact.tsx | 154 ++++++++++++++++ src/pages/Customers.tsx | 120 +++++++++++++ src/pages/Dashboard.tsx | 172 ++++++++++++++++++ src/pages/Features.tsx | 146 +++++++++++++++ src/pages/ForgotPassword.tsx | 107 +++++++++++ src/pages/Index.tsx | 278 +++++++++++++++++++++++++++++ src/pages/Login.tsx | 94 ++++++++++ src/pages/Pricing.tsx | 198 ++++++++++++++++++++ src/pages/Settings.tsx | 163 +++++++++++++++++ src/pages/Signup.tsx | 130 ++++++++++++++ 24 files changed, 2401 insertions(+) create mode 100644 .claude/settings.json create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 index.html create mode 100644 src/App.tsx create mode 100644 src/components/DashboardHeader.tsx create mode 100644 src/components/Footer.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/Sidebar.tsx create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/pages/About.tsx create mode 100644 src/pages/Analytics.tsx create mode 100644 src/pages/Blog.tsx create mode 100644 src/pages/Contact.tsx create mode 100644 src/pages/Customers.tsx create mode 100644 src/pages/Dashboard.tsx create mode 100644 src/pages/Features.tsx create mode 100644 src/pages/ForgotPassword.tsx create mode 100644 src/pages/Index.tsx create mode 100644 src/pages/Login.tsx create mode 100644 src/pages/Pricing.tsx create mode 100644 src/pages/Settings.tsx create mode 100644 src/pages/Signup.tsx diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..dba91da --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Read", + "Write", + "Edit", + "Bash", + "Glob", + "Grep" + ] + }, + "model": "claude-sonnet-4-20250514" +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8f517f3 --- /dev/null +++ b/CLAUDE.md @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..b44238d --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# template-saas + +Template: saas \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..cba9189 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + Saas + + +
+ + + diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..52d1f3b --- /dev/null +++ b/src/App.tsx @@ -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 ( + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); +} + +export default App; diff --git a/src/components/DashboardHeader.tsx b/src/components/DashboardHeader.tsx new file mode 100644 index 0000000..410bb36 --- /dev/null +++ b/src/components/DashboardHeader.tsx @@ -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 ( +
+
+ {/* Left */} +
+ +
+ + +
+
+ + {/* Right */} +
+ + + + + + + + Profile + Settings + + Logout + + +
+
+
+ ); +} diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000..dbe61df --- /dev/null +++ b/src/components/Footer.tsx @@ -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 ( + + ); +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..7d2dd65 --- /dev/null +++ b/src/components/Header.tsx @@ -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 ( +
+
+ {/* Logo */} + +
+ +
+ + Nexus + + + + {/* Desktop Nav */} + + + {/* Right Actions */} +
+ + + +
+ + {/* Mobile Menu */} + + + + + +
+ {navLinks.map((l) => ( + 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} + + ))} +
+ 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 + + +
+
+ + +
+
+ ); +} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx new file mode 100644 index 0000000..576d477 --- /dev/null +++ b/src/components/Sidebar.tsx @@ -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 ( +
+
+
+ +
+ Nexus +
+ +
+ ); +} + +export default function Sidebar({ activePath }: { activePath?: string }) { + const location = useLocation(); + const path = activePath || location.pathname; + const [open, setOpen] = useState(false); + + return ( + <> + {/* Desktop sidebar */} + + + {/* Mobile sidebar via Sheet */} + + + + + + + + + + ); +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..9b67590 --- /dev/null +++ b/src/main.tsx @@ -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( + + + +); diff --git a/src/pages/About.tsx b/src/pages/About.tsx new file mode 100644 index 0000000..2a734f5 --- /dev/null +++ b/src/pages/About.tsx @@ -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 ( +
+
+ + {/* ── HERO ── */} +
+
+ About Us +

We are building the future of work

+

+ 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. +

+
+
+ + {/* ── MISSION ── */} +
+
+
+
+ Our Mission +

Empower every team to do their best work

+

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.

+ +
+
+
+
+
+ Mission Image +
+
+
+
+ + {/* ── TEAM ── */} +
+
+
+ Our Team +

Meet the people behind Nexus

+
+
+ {team.map((member) => ( + + + + {member.initials} + +

{member.name}

+

{member.role}

+ + LinkedIn + +
+
+ ))} +
+
+
+ + {/* ── VALUES ── */} +
+
+
+ Our Values +

What drives us every day

+
+
+ {values.map((v) => ( + + +
+ +
+

{v.title}

+

{v.desc}

+
+
+ ))} +
+
+
+ + {/* ── TIMELINE ── */} +
+
+
+ Our Journey +

Company milestones

+
+
+
+
+ {milestones.map((m) => ( +
+
+ {m.year.slice(2)} +
+
+ {m.year} +

{m.event}

+
+
+ ))} +
+
+
+
+ +
+
+ ); +} diff --git a/src/pages/Analytics.tsx b/src/pages/Analytics.tsx new file mode 100644 index 0000000..5220b30 --- /dev/null +++ b/src/pages/Analytics.tsx @@ -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 ( +
+ +
+ +
+ {/* User Growth Chart */} + + + User Growth + + +
+ + + + + + + + + +
+
+
+ + {/* Conversion Funnel */} + + + Conversion Funnel + + + {funnel.map((step) => ( +
+
+ {step.label} + {step.value.toLocaleString()} ({step.pct}%) +
+
+
+
+
+ ))} + + + + {/* Engagement Grid */} +
+ {engagement.map((e) => ( + + +

{e.label}

+

{e.value}

+
+
+ ))} +
+ + {/* Top Pages Table */} + + + Top Pages + + +
+ + + + + + + + + + + {topPages.map((p) => ( + + + + + + + ))} + +
PageViewsUnique VisitorsBounce Rate
{p.page}{p.views.toLocaleString()}{p.unique.toLocaleString()}{p.bounce}
+
+
+
+
+
+
+ ); +} diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx new file mode 100644 index 0000000..2e86008 --- /dev/null +++ b/src/pages/Blog.tsx @@ -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 ( +
+
+ + {/* ── HERO ── */} +
+
+ Blog +

Insights & Updates

+

+ Product news, engineering deep-dives, and guides from the Nexus team. +

+
+
+ + {/* ── CONTENT ── */} +
+
+
+ {categories.map((cat) => ( + + ))} +
+ +
+ {filtered.map((article) => ( + +
+
+
+
+ Article Image +
+ + {article.category} +

{article.title}

+

{article.excerpt}

+
+ + {article.initials} + +
+
{article.author}
+
{article.date}
+
+
+
+ + ))} +
+ +
+ +
+
+
+ +
+
+ ); +} diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx new file mode 100644 index 0000000..333d5e9 --- /dev/null +++ b/src/pages/Contact.tsx @@ -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 ( +
+
+ + {/* ── HERO ── */} +
+
+ Contact +

Get in touch

+

+ Have questions? We would love to hear from you. Our team typically responds within 24 hours. +

+
+
+ + {/* ── CONTACT SPLIT ── */} +
+
+
+ {/* Left — Info */} +
+
+

Contact Information

+

Reach out through any of these channels and we will get back to you as soon as possible.

+
+
+
+
+ +
+
+

Email

+

hello@nexus.dev

+
+
+
+
+ +
+
+

Phone

+

+1 (415) 555-0132

+
+
+
+
+ +
+
+

Office

+

100 Market St, Suite 400, San Francisco, CA 94105

+
+
+
+
+ + {/* Right — Form */} +
+ + + {success && ( +
+ Message sent successfully! We will get back to you within 24 hours. +
+ )} +
{ e.preventDefault(); setSuccess(true); toast.success("Message sent! Our team will reach out shortly."); }}> +
+
+ + setName(e.target.value)} className="mt-1.5" /> +
+
+ + setEmail(e.target.value)} className="mt-1.5" /> +
+
+
+ + setCompany(e.target.value)} className="mt-1.5" /> +
+
+ + +
+
+ +