feat: add administration page and account role management

- Implemented `AdministrationContent` component for managing account roles.
- Created `AdministrationPage` to serve as the main entry point for administration tasks.
- Added PDF preview functionality with `PdfPreviewFrame` component for invoice generation.
- Introduced `InputColor` component for advanced color selection with various formats.
- Established color conversion utilities in `color-converter.ts` for handling color formats.
- Defined appearance-related schemas and types in `appearance.ts` for consistent theme management.
This commit is contained in:
2026-04-30 10:50:50 -04:00
parent ddc2b42672
commit 0e46fdafb2
87 changed files with 4562 additions and 2421 deletions
+73 -259
View File
@@ -1,294 +1,108 @@
import Link from "next/link";
import { Button } from "~/components/ui/button";
import { ArrowRight, FileText, UserRound } from "lucide-react";
import { AuthRedirect } from "~/components/AuthRedirect";
import { Card, CardContent } from "~/components/ui/card";
import { Badge } from "~/components/ui/badge";
import { Logo } from "~/components/branding/logo";
import {
ArrowRight,
Check,
Zap,
Shield,
BarChart3,
Rocket,
} from "lucide-react";
import { brand } from "~/lib/branding";
import { Button } from "~/components/ui/button";
import { env } from "~/env";
import { brand } from "~/lib/branding";
export default function HomePage() {
const allowRegistration = env.DISABLE_SIGNUPS !== true;
return (
<div className="relative min-h-screen overflow-x-hidden">
<main className="bg-background text-foreground min-h-screen">
<AuthRedirect />
{/* Blob Background for Homepage */}
<div className="pointer-events-none fixed inset-0 -z-10 flex items-center justify-center overflow-hidden">
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]"></div>
<div className="animate-blob h-[800px] w-[800px] rounded-full bg-neutral-400/30 blur-3xl dark:bg-neutral-500/20"></div>
</div>
<div className="mx-auto flex min-h-screen w-full max-w-5xl flex-col px-5 py-5 sm:px-6 lg:px-8">
<header className="flex items-center justify-between gap-4 border-b py-4">
<Logo animated={false} />
<nav className="flex items-center gap-2">
<Link href="/auth/signin">
<Button variant="ghost" size="sm">
Sign in
</Button>
</Link>
{allowRegistration && (
<Link href="/auth/register">
<Button size="sm">Create account</Button>
</Link>
)}
</nav>
</header>
{/* Navigation */}
<nav className="border-border/60 bg-background/80 fixed top-4 right-4 left-4 z-50 m-4 rounded-2xl border backdrop-blur-md">
<div className="mx-auto px-6">
<div className="flex h-16 items-center justify-between">
<Logo />
<div className="hidden items-center space-x-8 md:flex">
<a
href="#features"
className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors"
>
Features
</a>
<a
href="#pricing"
className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors"
>
Pricing
</a>
<section className="grid flex-1 items-center gap-10 py-14 md:grid-cols-[1fr_320px] md:py-20">
<div className="max-w-2xl space-y-7">
<div className="space-y-4">
<p className="text-muted-foreground text-sm font-medium">
Personal invoicing
</p>
<h1 className="font-heading text-4xl leading-tight font-bold tracking-normal sm:text-5xl">
{brand.name} is a place to make and track invoices.
</h1>
<p className="text-muted-foreground max-w-xl text-base leading-7 sm:text-lg">
Built for one person managing real clients, real work, and the
small admin loop around getting paid.
</p>
</div>
<div className="flex items-center space-x-4">
<div className="flex flex-col gap-3 sm:flex-row">
<Link href="/auth/signin">
<Button
variant="ghost"
size="sm"
className="text-muted-foreground hover:text-foreground"
>
Sign In
<Button size="lg" className="h-11 px-5">
Open workspace
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</Link>
{allowRegistration && (
<Link href="/auth/register">
<Button
size="sm"
variant="default"
className="rounded-xl px-6"
>
Get Started
<Button variant="outline" size="lg" className="h-11 px-5">
Create account
</Button>
</Link>
)}
</div>
</div>
</div>
</nav>
{/* Hero Section */}
<section className="relative pt-48 pb-32">
<div className="container mx-auto px-4 text-center">
<div className="mx-auto max-w-4xl">
<Badge className="bg-primary/10 text-primary border-primary/20 mb-8 rounded-full border px-4 py-1 text-sm">
<Zap className="mr-2 h-3.5 w-3.5" />
Completely Free for Everyone
</Badge>
<h1 className="text-foreground font-heading mb-8 text-6xl leading-tight font-bold tracking-tight sm:text-7xl lg:text-8xl">
{brand.name} <br />
<span className="text-primary italic">Beautifully Simple.</span>
</h1>
<p className="text-muted-foreground mx-auto mb-12 max-w-2xl font-sans text-xl leading-relaxed">
{brand.tagline}
</p>
<div className="flex flex-col items-center gap-6 sm:flex-row sm:justify-center">
{allowRegistration && (
<Link href="/auth/register">
<Button
size="lg"
className="shadow-primary/20 hover:shadow-primary/30 h-14 rounded-2xl px-10 text-lg shadow-xl transition-all duration-300 hover:shadow-2xl"
>
Start For Free
<ArrowRight className="ml-2 h-5 w-5" />
</Button>
</Link>
)}
<a href="#features">
<Button
variant="outline"
size="lg"
className="border-border/50 bg-background/50 hover:bg-background/80 h-14 rounded-2xl px-10 text-lg backdrop-blur-sm"
>
Learn More
</Button>
</a>
</div>
<div className="text-muted-foreground/80 mt-16 flex flex-col items-center justify-center gap-2 text-sm sm:flex-row sm:gap-8">
<div className="flex items-center gap-2">
<Check className="text-primary h-4 w-4" />
<span>No credit card required</span>
</div>
<div className="flex items-center gap-2">
<Check className="text-primary h-4 w-4" />
<span>Setup in 2 minutes</span>
</div>
<div className="flex items-center gap-2">
<Check className="text-primary h-4 w-4" />
<span>Free forever</span>
</div>
</div>
</div>
</div>
</section>
{/* Features Section */}
<section id="features" className="relative py-24">
<div className="relative z-10 container mx-auto px-4">
<div className="mb-20 text-center">
<h2 className="text-foreground font-heading mb-6 text-4xl font-bold sm:text-5xl">
Everything you need to{" "}
<span className="text-primary italic">thrive</span>
</h2>
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
Powerful features wrapped in a calm, focused interface.
</p>
</div>
<div className="grid gap-8 md:grid-cols-3">
{[
{
icon: Rocket,
title: "Quick Setup",
description:
"Start creating invoices immediately. No complicated setup required.",
items: [
"Simple client management",
"Professional templates",
"Easy invoice sending",
],
},
{
icon: BarChart3,
title: "Payment Tracking",
description:
"Keep track of invoice status and monitor your payments effortlessly.",
items: [
"Invoice status tracking",
"Payment history",
"Overdue notifications",
],
},
{
icon: Shield,
title: "Professional Features",
description:
"Tools that make you look professional and get you paid faster.",
items: [
"PDF generation",
"Custom tax rates",
"Professional numbering",
],
},
].map((feature, i) => (
<Card
key={i}
className="group border-border/40 bg-background/60 backdrop-blur-xl transition-transform duration-500 hover:-translate-y-2"
>
<CardContent className="p-8">
<div className="bg-primary/10 text-primary mb-6 inline-flex rounded-2xl p-4">
<feature.icon className="h-8 w-8" />
</div>
<h3 className="text-foreground font-heading mb-4 text-2xl font-bold">
{feature.title}
</h3>
<p className="text-muted-foreground mb-6 leading-relaxed">
{feature.description}
<div className="border-border bg-card text-card-foreground rounded-xl border p-5 shadow-sm">
<div className="space-y-5">
<div className="flex items-start gap-3">
<div className="bg-primary/10 text-primary rounded-md p-2">
<UserRound className="h-4 w-4" />
</div>
<div>
<h2 className="text-sm font-semibold">Clients</h2>
<p className="text-muted-foreground mt-1 text-sm leading-6">
Keep the people and businesses you invoice in one place.
</p>
<ul className="space-y-3">
{feature.items.map((item, j) => (
<li
key={j}
className="text-foreground/80 flex items-center gap-3 text-sm"
>
<div className="bg-primary h-1.5 w-1.5 rounded-full" />
{item}
</li>
))}
</ul>
</CardContent>
</Card>
))}
</div>
</div>
</section>
{/* Pricing Section */}
<section id="pricing" className="relative overflow-hidden py-24">
<div className="relative z-10 container mx-auto px-4">
<div className="mx-auto mb-16 max-w-4xl text-center">
<h2 className="font-heading mb-6 text-5xl font-bold">
Simple Pricing
</h2>
<p className="text-muted-foreground text-xl">
Focus on your work, not on fees.
</p>
</div>
<div className="mx-auto max-w-md">
<Card className="border-primary/50 shadow-primary/5 bg-background/80 relative overflow-visible shadow-2xl backdrop-blur-xl">
<div className="bg-primary text-primary-foreground absolute -top-4 left-1/2 -translate-x-1/2 rounded-full px-6 py-1.5 text-sm font-medium shadow-lg">
Forever Free
</div>
</div>
<CardContent className="p-10 text-center">
<div className="font-heading mb-2 text-6xl font-bold">$0</div>
<div className="text-muted-foreground mb-8">
No credit card required.
<div className="flex items-start gap-3 border-t pt-5">
<div className="bg-primary/10 text-primary rounded-md p-2">
<FileText className="h-4 w-4" />
</div>
<div className="mb-10 space-y-4 pl-8 text-left">
{[
"Unlimited Invoices",
"Unlimited Clients",
"PDF Downloads",
"Payment Tracking",
"Email Support",
].map((item, i) => (
<div key={i} className="flex items-center gap-3">
<Check className="text-primary h-5 w-5 shrink-0" />
<span className="text-foreground/90">{item}</span>
</div>
))}
<div>
<h2 className="text-sm font-semibold">Invoices</h2>
<p className="text-muted-foreground mt-1 text-sm leading-6">
Draft, send, mark paid, and export the PDF when you need it.
</p>
</div>
{allowRegistration && (
<Link href="/auth/register" className="block">
<Button
size="lg"
className="h-12 w-full rounded-xl text-lg"
>
Get Started
</Button>
</Link>
)}
</CardContent>
</Card>
</div>
</div>
</div>
</div>
</section>
</section>
{/* Footer */}
<footer className="border-border/40 bg-background/50 mt-12 border-t py-12 backdrop-blur-sm">
<div className="container mx-auto flex flex-col items-center justify-between gap-6 px-6 md:flex-row">
<div className="flex items-center gap-3">
<Logo size="sm" />
<span className="text-muted-foreground text-sm">
© 2024 beenvoice
</span>
</div>
<div className="text-muted-foreground flex gap-8 text-sm">
<a href="#" className="hover:text-foreground transition-colors">
<footer className="text-muted-foreground flex flex-col gap-3 border-t py-5 text-sm sm:flex-row sm:items-center sm:justify-between">
<span>© 2026 {brand.name}</span>
<div className="flex gap-5">
<Link href="/privacy" className="hover:text-foreground">
Privacy
</a>
<a href="#" className="hover:text-foreground transition-colors">
</Link>
<Link href="/terms" className="hover:text-foreground">
Terms
</a>
<a href="#" className="hover:text-foreground transition-colors">
Contact
</a>
</Link>
</div>
</div>
</footer>
</div>
</footer>
</div>
</main>
);
}