mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
mock pricing page
This commit is contained in:
@@ -13,6 +13,7 @@ import Issues from "@/pages/Issues";
|
|||||||
import Landing from "@/pages/Landing";
|
import Landing from "@/pages/Landing";
|
||||||
import Login from "@/pages/Login";
|
import Login from "@/pages/Login";
|
||||||
import NotFound from "@/pages/NotFound";
|
import NotFound from "@/pages/NotFound";
|
||||||
|
import Pricing from "@/pages/Pricing";
|
||||||
import Test from "@/pages/Test";
|
import Test from "@/pages/Test";
|
||||||
import Timeline from "@/pages/Timeline";
|
import Timeline from "@/pages/Timeline";
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
|||||||
{/* public routes */}
|
{/* public routes */}
|
||||||
<Route path="/" element={<Landing />} />
|
<Route path="/" element={<Landing />} />
|
||||||
<Route path="/font" element={<Font />} />
|
<Route path="/font" element={<Font />} />
|
||||||
|
<Route path="/pricing" element={<Pricing />} />
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
|
|
||||||
{/* authed routes */}
|
{/* authed routes */}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Icon } from "@iconify/react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useSession } from "@/components/session-provider";
|
import { useSession } from "@/components/session-provider";
|
||||||
import ThemeToggle from "@/components/theme-toggle";
|
import ThemeToggle from "@/components/theme-toggle";
|
||||||
@@ -12,16 +11,14 @@ export default function Landing() {
|
|||||||
<header className="relative flex items-center justify-center p-2 border-b">
|
<header className="relative flex items-center justify-center p-2 border-b">
|
||||||
<div className="text-3xl font-basteleur font-700">Sprint</div>
|
<div className="text-3xl font-basteleur font-700">Sprint</div>
|
||||||
<nav className="absolute right-2 flex items-center gap-4">
|
<nav className="absolute right-2 flex items-center gap-4">
|
||||||
|
<Link to="/pricing" className="text-sm hover:text-personality transition-colors">
|
||||||
|
Pricing
|
||||||
|
</Link>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
{!isLoading && user ? (
|
{!isLoading && user ? (
|
||||||
<>
|
<Button asChild variant="outline" size="sm">
|
||||||
{user && (
|
<Link to="/issues">Open app</Link>
|
||||||
<h1 className="text-xl font-basteleur font-400">Welcome back {user.name.split(" ")[0]}!</h1>
|
</Button>
|
||||||
)}
|
|
||||||
<Button asChild variant="outline" size="sm">
|
|
||||||
<Link to="/issues">Open app</Link>
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<Button asChild variant="outline" size="sm">
|
<Button asChild variant="outline" size="sm">
|
||||||
<Link to="/login">Sign in</Link>
|
<Link to="/login">Sign in</Link>
|
||||||
@@ -51,22 +48,6 @@ export default function Landing() {
|
|||||||
<Link to="/login">Get started</Link>
|
<Link to="/login">Get started</Link>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<div className="inline-flex gap-2 items-center">
|
|
||||||
<span className="relative">
|
|
||||||
<a
|
|
||||||
href="https://github.com/hex248/issue"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="inline-flex gap-2 text-muted-foreground hover:text-personality"
|
|
||||||
>
|
|
||||||
<Icon icon="mdi:github" className="h-7 w-7" />
|
|
||||||
<span className="font-goudy font-700 text-2xl">GitHub</span>
|
|
||||||
</a>
|
|
||||||
<span className="text-violet-400/90 absolute left-full top-[45%] ml-4 -translate-y-1/2 whitespace-nowrap select-none text-muted-foreground">
|
|
||||||
{"<-- you can self-host me!"}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
144
packages/frontend/src/pages/Pricing.tsx
Normal file
144
packages/frontend/src/pages/Pricing.tsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { Icon } from "@iconify/react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { useSession } from "@/components/session-provider";
|
||||||
|
import ThemeToggle from "@/components/theme-toggle";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const pricingTiers = [
|
||||||
|
{
|
||||||
|
name: "Indie",
|
||||||
|
price: "£0",
|
||||||
|
period: "Free forever",
|
||||||
|
description: "Perfect for individual developers and small teams",
|
||||||
|
features: ["1 organisation (owned or joined)", "1 project", "100 issues", "Up to 5 users"],
|
||||||
|
cta: "Get started",
|
||||||
|
ctaLink: "/login",
|
||||||
|
highlighted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Pro",
|
||||||
|
price: "£12",
|
||||||
|
period: "per user/month",
|
||||||
|
description: "For growing teams and professional developers",
|
||||||
|
features: [
|
||||||
|
"Unlimited issues",
|
||||||
|
"Unlimited projects",
|
||||||
|
"Unlimited organisations",
|
||||||
|
"Time tracking & reports",
|
||||||
|
],
|
||||||
|
cta: "Start free trial",
|
||||||
|
ctaLink: "/login",
|
||||||
|
highlighted: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Pricing() {
|
||||||
|
const { user, isLoading } = useSession();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col">
|
||||||
|
<header className="relative flex items-center justify-center p-2 border-b">
|
||||||
|
<div className="text-3xl font-basteleur font-700">Sprint</div>
|
||||||
|
<nav className="absolute right-2 flex items-center gap-4">
|
||||||
|
<Link to="/" className="text-sm hover:text-personality transition-colors">
|
||||||
|
Home
|
||||||
|
</Link>
|
||||||
|
<ThemeToggle />
|
||||||
|
{!isLoading && user ? (
|
||||||
|
<Button asChild variant="outline" size="sm">
|
||||||
|
<Link to="/issues">Open app</Link>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button asChild variant="outline" size="sm">
|
||||||
|
<Link to="/login">Sign in</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="flex-1 flex flex-col items-center py-16 px-4">
|
||||||
|
<div className="max-w-4xl w-full space-y-16">
|
||||||
|
<div className="text-center space-y-8">
|
||||||
|
<h1 className="text-5xl font-basteleur font-700">Simple, transparent pricing</h1>
|
||||||
|
<p className="text-2xl font-goudy text-muted-foreground">Choose the plan that fits your team</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
|
{pricingTiers.map((tier) => (
|
||||||
|
<div
|
||||||
|
key={tier.name}
|
||||||
|
className={`flex flex-col border p-8 ${
|
||||||
|
tier.highlighted ? "border-2 border-personality shadow-lg relative" : "border-border"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tier.highlighted && (
|
||||||
|
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-personality text-background px-4 py-1 text-sm font-600">
|
||||||
|
Most popular
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-4 mb-8">
|
||||||
|
<h2 className="text-4xl font-basteleur font-700">{tier.name}</h2>
|
||||||
|
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-baseline gap-2">
|
||||||
|
<span className="text-4xl font-600">{tier.price}</span>
|
||||||
|
<span className="text-md text-muted-foreground">{tier.period}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xl font-goudy text-muted-foreground">{tier.description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="space-y-3 mb-8 flex-1">
|
||||||
|
{tier.features.map((feature) => (
|
||||||
|
<li key={feature} className="flex items-start gap-2">
|
||||||
|
<Icon icon="lucide:check" className="size-5 text-personality shrink-0 mt-0.5" />
|
||||||
|
<span className="text-sm">{feature}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
variant={tier.highlighted ? "default" : "outline"}
|
||||||
|
className={cn("font-700", tier.highlighted ? "bg-personality hover:bg-personality/90" : "")}
|
||||||
|
>
|
||||||
|
<Link to={tier.ctaLink}>{tier.cta}</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center flex flex-col items-center justify-center gap-2">
|
||||||
|
<p className="text-xl font-goudy text-muted-foreground">Need something custom? </p>
|
||||||
|
<a
|
||||||
|
href="mailto:ob248@proton.me"
|
||||||
|
className="text-personality hover:underline font-goudy text-lg font-700"
|
||||||
|
>
|
||||||
|
Get in touch
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer className="flex justify-center gap-2 items-center py-1 border-t">
|
||||||
|
<span className="font-300 text-lg text-muted-foreground font-goudy">
|
||||||
|
Built by{" "}
|
||||||
|
<a
|
||||||
|
href="https://ob248.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="hover:text-personality font-goudy font-700"
|
||||||
|
>
|
||||||
|
Oliver Bryan
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<a href="https://ob248.com" target="_blank" rel="noopener noreferrer">
|
||||||
|
<img src="oliver-bryan.svg" alt="Oliver Bryan" className="w-4 h-4" />
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user