Begin dark mode!
This commit is contained in:
@@ -49,23 +49,25 @@ function RegisterForm() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4">
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4 dark:from-gray-900 dark:to-gray-800">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
{/* Logo and Welcome */}
|
||||
<div className="space-y-4 text-center">
|
||||
<Logo size="lg" className="mx-auto" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Join beenvoice</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Join beenvoice
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-300">
|
||||
Create your account to get started
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Registration Form */}
|
||||
<Card className="border-0 shadow-xl">
|
||||
<Card className="border-0 shadow-xl dark:bg-gray-800">
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-center text-xl">
|
||||
<CardTitle className="text-center text-xl dark:text-white">
|
||||
Create Account
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -75,7 +77,7 @@ function RegisterForm() {
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="firstName">First Name</Label>
|
||||
<div className="relative">
|
||||
<User className="absolute top-3 left-3 h-4 w-4 text-gray-400" />
|
||||
<User className="absolute top-3 left-3 h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
<Input
|
||||
id="firstName"
|
||||
type="text"
|
||||
@@ -91,7 +93,7 @@ function RegisterForm() {
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="lastName">Last Name</Label>
|
||||
<div className="relative">
|
||||
<User className="absolute top-3 left-3 h-4 w-4 text-gray-400" />
|
||||
<User className="absolute top-3 left-3 h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
<Input
|
||||
id="lastName"
|
||||
type="text"
|
||||
@@ -107,7 +109,7 @@ function RegisterForm() {
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute top-3 left-3 h-4 w-4 text-gray-400" />
|
||||
<Mail className="absolute top-3 left-3 h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
@@ -122,7 +124,7 @@ function RegisterForm() {
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute top-3 left-3 h-4 w-4 text-gray-400" />
|
||||
<Lock className="absolute top-3 left-3 h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
@@ -134,7 +136,7 @@ function RegisterForm() {
|
||||
placeholder="Create a password"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Must be at least 6 characters
|
||||
</p>
|
||||
</div>
|
||||
@@ -150,10 +152,12 @@ function RegisterForm() {
|
||||
</Button>
|
||||
</form>
|
||||
<div className="mt-6 text-center text-sm">
|
||||
<span className="text-gray-600">Already have an account? </span>
|
||||
<span className="text-gray-600 dark:text-gray-300">
|
||||
Already have an account?{" "}
|
||||
</span>
|
||||
<Link
|
||||
href="/auth/signin"
|
||||
className="font-medium text-green-600 hover:text-green-700"
|
||||
className="font-medium text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300"
|
||||
>
|
||||
Sign in here
|
||||
</Link>
|
||||
@@ -163,8 +167,10 @@ function RegisterForm() {
|
||||
|
||||
{/* Features */}
|
||||
<div className="space-y-4 text-center">
|
||||
<p className="text-sm text-gray-500">Start invoicing like a pro</p>
|
||||
<div className="flex justify-center space-x-6 text-xs text-gray-400">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Start invoicing like a pro
|
||||
</p>
|
||||
<div className="flex justify-center space-x-6 text-xs text-gray-400 dark:text-gray-500">
|
||||
<span>✓ Free to start</span>
|
||||
<span>✓ No credit card</span>
|
||||
<span>✓ Cancel anytime</span>
|
||||
@@ -179,15 +185,17 @@ export default function RegisterPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4">
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4 dark:from-gray-900 dark:to-gray-800">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
<div className="space-y-4 text-center">
|
||||
<Logo size="lg" className="mx-auto" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Join beenvoice
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">Loading...</p>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-300">
|
||||
Loading...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,30 +42,34 @@ function SignInForm() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4">
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4 dark:from-gray-900 dark:to-gray-800">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
{/* Logo and Welcome */}
|
||||
<div className="space-y-4 text-center">
|
||||
<Logo size="lg" className="mx-auto" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Welcome back</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Welcome back
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-300">
|
||||
Sign in to your beenvoice account
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sign In Form */}
|
||||
<Card className="border-0 shadow-xl">
|
||||
<Card className="border-0 shadow-xl dark:bg-gray-800">
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-center text-xl">Sign In</CardTitle>
|
||||
<CardTitle className="text-center text-xl dark:text-white">
|
||||
Sign In
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSignIn} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute top-3 left-3 h-4 w-4 text-gray-400" />
|
||||
<Mail className="absolute top-3 left-3 h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
@@ -81,7 +85,7 @@ function SignInForm() {
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute top-3 left-3 h-4 w-4 text-gray-400" />
|
||||
<Lock className="absolute top-3 left-3 h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
@@ -105,12 +109,12 @@ function SignInForm() {
|
||||
</Button>
|
||||
</form>
|
||||
<div className="mt-6 text-center text-sm">
|
||||
<span className="text-gray-600">
|
||||
<span className="text-gray-600 dark:text-gray-300">
|
||||
Don't have an account?{" "}
|
||||
</span>
|
||||
<Link
|
||||
href="/auth/register"
|
||||
className="font-medium text-green-600 hover:text-green-700"
|
||||
className="font-medium text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300"
|
||||
>
|
||||
Create one now
|
||||
</Link>
|
||||
@@ -120,10 +124,10 @@ function SignInForm() {
|
||||
|
||||
{/* Features */}
|
||||
<div className="space-y-4 text-center">
|
||||
<p className="text-sm text-gray-500">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Simple invoicing for freelancers and small businesses
|
||||
</p>
|
||||
<div className="flex justify-center space-x-6 text-xs text-gray-400">
|
||||
<div className="flex justify-center space-x-6 text-xs text-gray-400 dark:text-gray-500">
|
||||
<span>✓ Easy client management</span>
|
||||
<span>✓ Professional invoices</span>
|
||||
<span>✓ Payment tracking</span>
|
||||
@@ -138,15 +142,17 @@ export default function SignInPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4">
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 p-4 dark:from-gray-900 dark:to-gray-800">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
<div className="space-y-4 text-center">
|
||||
<Logo size="lg" className="mx-auto" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Welcome back
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">Loading...</p>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-300">
|
||||
Loading...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,21 +3,26 @@
|
||||
import { api } from "~/trpc/react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import {
|
||||
Users,
|
||||
FileText,
|
||||
TrendingUp,
|
||||
import {
|
||||
Users,
|
||||
FileText,
|
||||
TrendingUp,
|
||||
Calendar,
|
||||
Plus,
|
||||
ArrowRight
|
||||
ArrowRight,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { DashboardStatsSkeleton, DashboardActivitySkeleton } from "~/components/ui/skeleton";
|
||||
import {
|
||||
DashboardStatsSkeleton,
|
||||
DashboardActivitySkeleton,
|
||||
} from "~/components/ui/skeleton";
|
||||
|
||||
// Client component for dashboard stats
|
||||
export function DashboardStats() {
|
||||
const { data: clients, isLoading: clientsLoading } = api.clients.getAll.useQuery();
|
||||
const { data: invoices, isLoading: invoicesLoading } = api.invoices.getAll.useQuery();
|
||||
const { data: clients, isLoading: clientsLoading } =
|
||||
api.clients.getAll.useQuery();
|
||||
const { data: invoices, isLoading: invoicesLoading } =
|
||||
api.invoices.getAll.useQuery();
|
||||
|
||||
if (clientsLoading || invoicesLoading) {
|
||||
return <DashboardStatsSkeleton />;
|
||||
@@ -25,8 +30,12 @@ export function DashboardStats() {
|
||||
|
||||
const totalClients = clients?.length ?? 0;
|
||||
const totalInvoices = invoices?.length ?? 0;
|
||||
const totalRevenue = invoices?.reduce((sum, invoice) => sum + invoice.totalAmount, 0) ?? 0;
|
||||
const pendingInvoices = invoices?.filter(invoice => invoice.status === "sent" || invoice.status === "draft").length ?? 0;
|
||||
const totalRevenue =
|
||||
invoices?.reduce((sum, invoice) => sum + invoice.totalAmount, 0) ?? 0;
|
||||
const pendingInvoices =
|
||||
invoices?.filter(
|
||||
(invoice) => invoice.status === "sent" || invoice.status === "draft",
|
||||
).length ?? 0;
|
||||
|
||||
// Calculate month-over-month changes (simplified)
|
||||
const lastMonthClients = 0; // This would need historical data
|
||||
@@ -34,62 +43,85 @@ export function DashboardStats() {
|
||||
const lastMonthRevenue = 0;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm hover:shadow-2xl transition-all duration-300">
|
||||
<div className="mb-8 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm transition-all duration-300 hover:shadow-2xl dark:bg-gray-800/80">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-700">Total Clients</CardTitle>
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<Users className="h-4 w-4 text-emerald-600" />
|
||||
<CardTitle className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Total Clients
|
||||
</CardTitle>
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<Users className="h-4 w-4 text-emerald-600 dark:text-emerald-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-emerald-600">{totalClients}</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
{totalClients > lastMonthClients ? "+" : ""}{totalClients - lastMonthClients} from last month
|
||||
<div className="text-3xl font-bold text-emerald-600 dark:text-emerald-400">
|
||||
{totalClients}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{totalClients > lastMonthClients ? "+" : ""}
|
||||
{totalClients - lastMonthClients} from last month
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm hover:shadow-2xl transition-all duration-300">
|
||||
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm transition-all duration-300 hover:shadow-2xl dark:bg-gray-800/80">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-700">Total Invoices</CardTitle>
|
||||
<div className="p-2 bg-blue-100 rounded-lg">
|
||||
<FileText className="h-4 w-4 text-blue-600" />
|
||||
<CardTitle className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Total Invoices
|
||||
</CardTitle>
|
||||
<div className="rounded-lg bg-blue-100 p-2 dark:bg-blue-900/30">
|
||||
<FileText className="h-4 w-4 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-blue-600">{totalInvoices}</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
{totalInvoices > lastMonthInvoices ? "+" : ""}{totalInvoices - lastMonthInvoices} from last month
|
||||
<div className="text-3xl font-bold text-blue-600 dark:text-blue-400">
|
||||
{totalInvoices}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{totalInvoices > lastMonthInvoices ? "+" : ""}
|
||||
{totalInvoices - lastMonthInvoices} from last month
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm hover:shadow-2xl transition-all duration-300">
|
||||
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm transition-all duration-300 hover:shadow-2xl dark:bg-gray-800/80">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-700">Revenue</CardTitle>
|
||||
<div className="p-2 bg-teal-100 rounded-lg">
|
||||
<TrendingUp className="h-4 w-4 text-teal-600" />
|
||||
<CardTitle className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Revenue
|
||||
</CardTitle>
|
||||
<div className="rounded-lg bg-teal-100 p-2 dark:bg-teal-900/30">
|
||||
<TrendingUp className="h-4 w-4 text-teal-600 dark:text-teal-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-teal-600">${totalRevenue.toFixed(2)}</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
{totalRevenue > lastMonthRevenue ? "+" : ""}{((totalRevenue - lastMonthRevenue) / (lastMonthRevenue || 1) * 100).toFixed(1)}% from last month
|
||||
<div className="text-3xl font-bold text-teal-600 dark:text-teal-400">
|
||||
${totalRevenue.toFixed(2)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{totalRevenue > lastMonthRevenue ? "+" : ""}
|
||||
{(
|
||||
((totalRevenue - lastMonthRevenue) / (lastMonthRevenue || 1)) *
|
||||
100
|
||||
).toFixed(1)}
|
||||
% from last month
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm hover:shadow-2xl transition-all duration-300">
|
||||
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm transition-all duration-300 hover:shadow-2xl dark:bg-gray-800/80">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-700">Pending Invoices</CardTitle>
|
||||
<div className="p-2 bg-orange-100 rounded-lg">
|
||||
<Calendar className="h-4 w-4 text-orange-600" />
|
||||
<CardTitle className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Pending Invoices
|
||||
</CardTitle>
|
||||
<div className="rounded-lg bg-orange-100 p-2 dark:bg-orange-900/30">
|
||||
<Calendar className="h-4 w-4 text-orange-600 dark:text-orange-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-orange-600">{pendingInvoices}</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
<div className="text-3xl font-bold text-orange-600 dark:text-orange-400">
|
||||
{pendingInvoices}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Due this month
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -101,34 +133,34 @@ export function DashboardStats() {
|
||||
// Client component for dashboard cards
|
||||
export function DashboardCards() {
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm hover:shadow-2xl transition-all duration-300">
|
||||
<div className="mb-8 grid grid-cols-1 gap-8 lg:grid-cols-2">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm transition-all duration-300 hover:shadow-2xl dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700 dark:text-emerald-400">
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<Users className="h-5 w-5" />
|
||||
</div>
|
||||
Manage Clients
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Add new clients and manage your existing client relationships.
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
<Button
|
||||
asChild
|
||||
className="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700 text-white font-medium shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
className="bg-gradient-to-r from-emerald-600 to-teal-600 font-medium text-white shadow-lg transition-all duration-200 hover:from-emerald-700 hover:to-teal-700 hover:shadow-xl"
|
||||
>
|
||||
<Link href="/dashboard/clients/new">
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
Add Client
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
asChild
|
||||
className="border-gray-300 text-gray-700 hover:bg-gray-50 font-medium"
|
||||
className="border-gray-300 font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
>
|
||||
<Link href="/dashboard/clients">
|
||||
View All Clients
|
||||
@@ -139,33 +171,33 @@ export function DashboardCards() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm hover:shadow-2xl transition-all duration-300">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm transition-all duration-300 hover:shadow-2xl dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700 dark:text-emerald-400">
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<FileText className="h-5 w-5" />
|
||||
</div>
|
||||
Create Invoices
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Generate professional invoices and track payments.
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
<Button
|
||||
asChild
|
||||
className="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700 text-white font-medium shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
className="bg-gradient-to-r from-emerald-600 to-teal-600 font-medium text-white shadow-lg transition-all duration-200 hover:from-emerald-700 hover:to-teal-700 hover:shadow-xl"
|
||||
>
|
||||
<Link href="/dashboard/invoices/new">
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
New Invoice
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
asChild
|
||||
className="border-gray-300 text-gray-700 hover:bg-gray-50 font-medium"
|
||||
className="border-gray-300 font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
>
|
||||
<Link href="/dashboard/invoices">
|
||||
View All Invoices
|
||||
@@ -190,42 +222,60 @@ export function DashboardActivity() {
|
||||
const recentInvoices = invoices?.slice(0, 5) ?? [];
|
||||
|
||||
return (
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700">Recent Activity</CardTitle>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Recent Activity
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{recentInvoices.length === 0 ? (
|
||||
<div className="text-center py-12 text-gray-500">
|
||||
<div className="p-4 bg-gray-100 rounded-full w-20 h-20 mx-auto mb-4 flex items-center justify-center">
|
||||
<FileText className="h-8 w-8 text-gray-400" />
|
||||
<div className="py-12 text-center text-gray-500 dark:text-gray-400">
|
||||
<div className="mx-auto mb-4 flex h-20 w-20 items-center justify-center rounded-full bg-gray-100 p-4 dark:bg-gray-700">
|
||||
<FileText className="h-8 w-8 text-gray-400 dark:text-gray-500" />
|
||||
</div>
|
||||
<p className="text-lg font-medium mb-2">No recent activity</p>
|
||||
<p className="text-sm">Start by adding your first client or creating an invoice</p>
|
||||
<p className="mb-2 text-lg font-medium dark:text-gray-300">
|
||||
No recent activity
|
||||
</p>
|
||||
<p className="text-sm dark:text-gray-400">
|
||||
Start by adding your first client or creating an invoice
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{recentInvoices.map((invoice) => (
|
||||
<div key={invoice.id} className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
||||
<div
|
||||
key={invoice.id}
|
||||
className="flex items-center justify-between rounded-lg bg-gray-50 p-4 dark:bg-gray-700"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<FileText className="h-4 w-4 text-emerald-600" />
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<FileText className="h-4 w-4 text-emerald-600 dark:text-emerald-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">Invoice #{invoice.invoiceNumber}</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
{invoice.client?.name ?? "Unknown Client"} • ${invoice.totalAmount.toFixed(2)}
|
||||
<p className="font-medium text-gray-900 dark:text-white">
|
||||
Invoice #{invoice.invoiceNumber}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{invoice.client?.name ?? "Unknown Client"} • $
|
||||
{invoice.totalAmount.toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`px-2 py-1 text-xs font-medium rounded-full ${
|
||||
invoice.status === "paid" ? "bg-green-100 text-green-800" :
|
||||
invoice.status === "sent" ? "bg-blue-100 text-blue-800" :
|
||||
invoice.status === "overdue" ? "bg-red-100 text-red-800" :
|
||||
"bg-gray-100 text-gray-800"
|
||||
}`}>
|
||||
{invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1)}
|
||||
<span
|
||||
className={`rounded-full px-2 py-1 text-xs font-medium ${
|
||||
invoice.status === "paid"
|
||||
? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400"
|
||||
: invoice.status === "sent"
|
||||
? "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400"
|
||||
: invoice.status === "overdue"
|
||||
? "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400"
|
||||
: "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300"
|
||||
}`}
|
||||
>
|
||||
{invoice.status.charAt(0).toUpperCase() +
|
||||
invoice.status.slice(1)}
|
||||
</span>
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
<Link href={`/dashboard/invoices/${invoice.id}`}>
|
||||
@@ -240,4 +290,4 @@ export function DashboardActivity() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,27 @@ import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import Link from "next/link";
|
||||
import { Edit, Mail, Phone, MapPin, Building, Calendar, DollarSign } from "lucide-react";
|
||||
import {
|
||||
Edit,
|
||||
Mail,
|
||||
Phone,
|
||||
MapPin,
|
||||
Building,
|
||||
Calendar,
|
||||
DollarSign,
|
||||
} from "lucide-react";
|
||||
|
||||
interface ClientDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function ClientDetailPage({ params }: ClientDetailPageProps) {
|
||||
export default async function ClientDetailPage({
|
||||
params,
|
||||
}: ClientDetailPageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
|
||||
const client = await api.clients.getById({ id });
|
||||
|
||||
|
||||
if (!client) {
|
||||
notFound();
|
||||
}
|
||||
@@ -34,20 +44,26 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
const totalInvoiced = client.invoices?.reduce((sum, invoice) => sum + invoice.totalAmount, 0) || 0;
|
||||
const paidInvoices = client.invoices?.filter(invoice => invoice.status === "paid").length || 0;
|
||||
const pendingInvoices = client.invoices?.filter(invoice => invoice.status === "sent").length || 0;
|
||||
const totalInvoiced =
|
||||
client.invoices?.reduce((sum, invoice) => sum + invoice.totalAmount, 0) ||
|
||||
0;
|
||||
const paidInvoices =
|
||||
client.invoices?.filter((invoice) => invoice.status === "paid").length || 0;
|
||||
const pendingInvoices =
|
||||
client.invoices?.filter((invoice) => invoice.status === "sent").length || 0;
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 md:ml-72 md:mr-4">
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
<div className="p-4 md:mr-4 md:ml-72 md:p-6">
|
||||
<div className="mx-auto max-w-4xl space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
||||
<h1 className="bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-3xl font-bold text-transparent">
|
||||
{client.name}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Client Details</p>
|
||||
<p className="text-muted-foreground dark:text-gray-300">
|
||||
Client Details
|
||||
</p>
|
||||
</div>
|
||||
<Link href={`/clients/${client.id}/edit`}>
|
||||
<Button className="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700">
|
||||
@@ -57,39 +73,47 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||
{/* Client Information Card */}
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center space-x-2 text-emerald-700">
|
||||
<CardTitle className="flex items-center space-x-2 text-emerald-700 dark:text-emerald-400">
|
||||
<Building className="h-5 w-5" />
|
||||
<span>Contact Information</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Basic Info */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
{client.email && (
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<Mail className="h-4 w-4 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Email</p>
|
||||
<p className="text-sm">{client.email}</p>
|
||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||
Email
|
||||
</p>
|
||||
<p className="text-sm dark:text-gray-300">
|
||||
{client.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{client.phone && (
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<Phone className="h-4 w-4 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Phone</p>
|
||||
<p className="text-sm">{client.phone}</p>
|
||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||
Phone
|
||||
</p>
|
||||
<p className="text-sm dark:text-gray-300">
|
||||
{client.phone}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -99,19 +123,23 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
{(client.addressLine1 ?? client.city ?? client.state) && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<MapPin className="h-4 w-4 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Address</p>
|
||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||
Address
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-11 space-y-1 text-sm">
|
||||
<div className="ml-11 space-y-1 text-sm dark:text-gray-300">
|
||||
{client.addressLine1 && <p>{client.addressLine1}</p>}
|
||||
{client.addressLine2 && <p>{client.addressLine2}</p>}
|
||||
{(client.city ?? client.state ?? client.postalCode) && (
|
||||
<p>
|
||||
{[client.city, client.state, client.postalCode].filter(Boolean).join(", ")}
|
||||
{[client.city, client.state, client.postalCode]
|
||||
.filter(Boolean)
|
||||
.join(", ")}
|
||||
</p>
|
||||
)}
|
||||
{client.country && <p>{client.country}</p>}
|
||||
@@ -121,12 +149,16 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
|
||||
{/* Client Since */}
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-emerald-100 rounded-lg">
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<Calendar className="h-4 w-4 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Client Since</p>
|
||||
<p className="text-sm">{formatDate(client.createdAt)}</p>
|
||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||
Client Since
|
||||
</p>
|
||||
<p className="text-sm dark:text-gray-300">
|
||||
{formatDate(client.createdAt)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -135,9 +167,9 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
|
||||
{/* Stats Card */}
|
||||
<div className="space-y-6">
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center space-x-2 text-emerald-700">
|
||||
<CardTitle className="flex items-center space-x-2 text-emerald-700 dark:text-emerald-400">
|
||||
<DollarSign className="h-5 w-5" />
|
||||
<span>Invoice Summary</span>
|
||||
</CardTitle>
|
||||
@@ -147,17 +179,27 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
<p className="text-2xl font-bold text-emerald-600">
|
||||
{formatCurrency(totalInvoiced)}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">Total Invoiced</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Total Invoiced
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-semibold text-green-600">{paidInvoices}</p>
|
||||
<p className="text-xs text-gray-500">Paid</p>
|
||||
<p className="text-lg font-semibold text-green-600">
|
||||
{paidInvoices}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Paid
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-semibold text-orange-600">{pendingInvoices}</p>
|
||||
<p className="text-xs text-gray-500">Pending</p>
|
||||
<p className="text-lg font-semibold text-orange-600">
|
||||
{pendingInvoices}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Pending
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -165,24 +207,38 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
|
||||
{/* Recent Invoices */}
|
||||
{client.invoices && client.invoices.length > 0 && (
|
||||
<Card className="shadow-xl border-0 bg-white/80 backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Recent Invoices</CardTitle>
|
||||
<CardTitle className="text-lg dark:text-white">
|
||||
Recent Invoices
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{client.invoices.slice(0, 3).map((invoice) => (
|
||||
<div key={invoice.id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div
|
||||
key={invoice.id}
|
||||
className="flex items-center justify-between rounded-lg bg-gray-50 p-3 dark:bg-gray-700"
|
||||
>
|
||||
<div>
|
||||
<p className="font-medium text-sm">{invoice.invoiceNumber}</p>
|
||||
<p className="text-xs text-gray-500">{formatDate(invoice.issueDate)}</p>
|
||||
<p className="text-sm font-medium dark:text-white">
|
||||
{invoice.invoiceNumber}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{formatDate(invoice.issueDate)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-medium text-sm">{formatCurrency(invoice.totalAmount)}</p>
|
||||
<Badge
|
||||
<p className="text-sm font-medium dark:text-white">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</p>
|
||||
<Badge
|
||||
variant={
|
||||
invoice.status === "paid" ? "default" :
|
||||
invoice.status === "sent" ? "secondary" : "outline"
|
||||
invoice.status === "paid"
|
||||
? "default"
|
||||
: invoice.status === "sent"
|
||||
? "secondary"
|
||||
: "outline"
|
||||
}
|
||||
className="text-xs"
|
||||
>
|
||||
@@ -200,4 +256,4 @@ export default async function ClientDetailPage({ params }: ClientDetailPageProps
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ export default async function InvoicePage({
|
||||
<h1 className="bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-3xl font-bold text-transparent">
|
||||
Invoice Details
|
||||
</h1>
|
||||
<p className="mt-1 text-lg text-gray-600">
|
||||
<p className="mt-1 text-lg text-gray-600 dark:text-gray-300">
|
||||
View and manage invoice information.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative flex rounded-lg border border-gray-200 bg-gray-100 p-1">
|
||||
<div className="relative flex rounded-lg border border-gray-200 bg-gray-100 p-1 dark:border-gray-700 dark:bg-gray-800">
|
||||
<div
|
||||
className={`absolute top-1 bottom-1 rounded-md bg-white shadow-sm transition-all duration-300 ease-in-out ${
|
||||
className={`absolute top-1 bottom-1 rounded-md bg-white shadow-sm transition-all duration-300 ease-in-out dark:bg-gray-700 ${
|
||||
mode === "view" ? "left-1 w-10" : "left-11 w-10"
|
||||
}`}
|
||||
/>
|
||||
@@ -43,7 +43,7 @@ export default async function InvoicePage({
|
||||
className={`relative z-10 rounded-md px-3 py-2 transition-all duration-200 ${
|
||||
mode === "view"
|
||||
? "text-emerald-600"
|
||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-800"
|
||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300"
|
||||
}`}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
@@ -53,7 +53,7 @@ export default async function InvoicePage({
|
||||
className={`relative z-10 rounded-md px-3 py-2 transition-all duration-200 ${
|
||||
mode === "edit"
|
||||
? "text-emerald-600"
|
||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-800"
|
||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300"
|
||||
}`}
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
|
||||
@@ -16,7 +16,7 @@ export default async function DashboardPage() {
|
||||
<h1 className="bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-4xl font-bold text-transparent">
|
||||
Welcome back, {session?.user?.name?.split(" ")[0] ?? "User"}!
|
||||
</h1>
|
||||
<p className="mt-2 text-lg text-gray-600">
|
||||
<p className="mt-2 text-lg text-gray-600 dark:text-gray-300">
|
||||
Here's what's happening with your invoicing business
|
||||
</p>
|
||||
</div>
|
||||
|
||||
+4
-3
@@ -8,7 +8,8 @@ import { Toaster } from "~/components/ui/toaster";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "beenvoice - Invoicing Made Simple",
|
||||
description: "Simple and efficient invoicing for freelancers and small businesses",
|
||||
description:
|
||||
"Simple and efficient invoicing for freelancers and small businesses",
|
||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||
};
|
||||
|
||||
@@ -21,8 +22,8 @@ export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<html lang="en" className={`${geist.variable}`}>
|
||||
<body className="relative min-h-screen font-sans antialiased overflow-x-hidden bg-gradient-to-br from-emerald-100 via-white via-60% to-teal-100 before:content-[''] before:fixed before:inset-0 before:z-0 before:pointer-events-none before:bg-[radial-gradient(ellipse_at_80%_0%,rgba(16,185,129,0.10)_0%,transparent_60%)]">
|
||||
<html lang="en" className={`${geist.variable}`}>
|
||||
<body className="relative min-h-screen overflow-x-hidden bg-gradient-to-br from-emerald-100 via-white via-60% to-teal-100 font-sans antialiased before:pointer-events-none before:fixed before:inset-0 before:z-0 before:bg-[radial-gradient(ellipse_at_80%_0%,rgba(16,185,129,0.10)_0%,transparent_60%)] before:content-[''] dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 dark:before:bg-[radial-gradient(ellipse_at_80%_0%,rgba(34,197,94,0.15)_0%,transparent_60%)]">
|
||||
<TRPCReactProvider>{children}</TRPCReactProvider>
|
||||
<Toaster />
|
||||
</body>
|
||||
|
||||
+87
-50
@@ -23,19 +23,26 @@ import {
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-100">
|
||||
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-100 dark:from-gray-900 dark:to-gray-800">
|
||||
<AuthRedirect />
|
||||
{/* Header */}
|
||||
<header className="border-b border-green-200 bg-white/80 backdrop-blur-sm">
|
||||
<header className="border-b border-green-200 bg-white/80 backdrop-blur-sm dark:border-gray-700 dark:bg-gray-900/80">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Logo />
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link href="/auth/signin">
|
||||
<Button variant="ghost">Sign In</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/auth/register">
|
||||
<Button>Get Started</Button>
|
||||
<Button className="dark:bg-green-600 dark:hover:bg-green-700">
|
||||
Get Started
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,23 +52,33 @@ export default function HomePage() {
|
||||
{/* Hero Section */}
|
||||
<section className="px-4 py-20">
|
||||
<div className="container mx-auto max-w-4xl text-center">
|
||||
<h1 className="mb-6 text-5xl font-bold text-gray-900 md:text-6xl">
|
||||
<h1 className="mb-6 text-5xl font-bold text-gray-900 md:text-6xl dark:text-white">
|
||||
Simple Invoicing for
|
||||
<span className="text-green-600"> Freelancers</span>
|
||||
<span className="text-green-600 dark:text-green-400">
|
||||
{" "}
|
||||
Freelancers
|
||||
</span>
|
||||
</h1>
|
||||
<p className="mx-auto mb-8 max-w-2xl text-xl text-gray-600">
|
||||
<p className="mx-auto mb-8 max-w-2xl text-xl text-gray-600 dark:text-gray-300">
|
||||
Create professional invoices, manage clients, and get paid faster
|
||||
with beenvoice. The invoicing app that works as hard as you do.
|
||||
</p>
|
||||
<div className="flex flex-col justify-center gap-4 sm:flex-row">
|
||||
<Link href="/auth/register">
|
||||
<Button size="lg" className="px-8 py-6 text-lg">
|
||||
<Button
|
||||
size="lg"
|
||||
className="px-8 py-6 text-lg dark:bg-green-600 dark:hover:bg-green-700"
|
||||
>
|
||||
Start Free Trial
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="#features">
|
||||
<Button variant="outline" size="lg" className="px-8 py-6 text-lg">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="px-8 py-6 text-lg dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
>
|
||||
See How It Works
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -70,28 +87,30 @@ export default function HomePage() {
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section id="features" className="bg-white px-4 py-20">
|
||||
<section id="features" className="bg-white px-4 py-20 dark:bg-gray-800">
|
||||
<div className="container mx-auto max-w-6xl">
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="mb-4 text-4xl font-bold text-gray-900">
|
||||
<h2 className="mb-4 text-4xl font-bold text-gray-900 dark:text-white">
|
||||
Everything you need to invoice like a pro
|
||||
</h2>
|
||||
<p className="mx-auto max-w-2xl text-xl text-gray-600">
|
||||
<p className="mx-auto max-w-2xl text-xl text-gray-600 dark:text-gray-300">
|
||||
Powerful features designed for freelancers and small businesses
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card className="border-0 shadow-lg">
|
||||
<Card className="border-0 shadow-lg dark:bg-gray-700">
|
||||
<CardHeader>
|
||||
<Users className="mb-4 h-12 w-12 text-green-600" />
|
||||
<CardTitle>Client Management</CardTitle>
|
||||
<CardDescription>
|
||||
<Users className="mb-4 h-12 w-12 text-green-600 dark:text-green-400" />
|
||||
<CardTitle className="dark:text-white">
|
||||
Client Management
|
||||
</CardTitle>
|
||||
<CardDescription className="dark:text-gray-300">
|
||||
Keep all your client information organized in one place
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm text-gray-600">
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
||||
<li className="flex items-center">
|
||||
<CheckCircle className="mr-2 h-4 w-4 text-green-500" />
|
||||
Store contact details and addresses
|
||||
@@ -108,16 +127,18 @@ export default function HomePage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border-0 shadow-lg">
|
||||
<Card className="border-0 shadow-lg dark:bg-gray-700">
|
||||
<CardHeader>
|
||||
<FileText className="mb-4 h-12 w-12 text-green-600" />
|
||||
<CardTitle>Professional Invoices</CardTitle>
|
||||
<CardDescription>
|
||||
<FileText className="mb-4 h-12 w-12 text-green-600 dark:text-green-400" />
|
||||
<CardTitle className="dark:text-white">
|
||||
Professional Invoices
|
||||
</CardTitle>
|
||||
<CardDescription className="dark:text-gray-300">
|
||||
Create beautiful, detailed invoices with line items
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm text-gray-600">
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
||||
<li className="flex items-center">
|
||||
<CheckCircle className="mr-2 h-4 w-4 text-green-500" />
|
||||
Add multiple line items with dates
|
||||
@@ -134,16 +155,18 @@ export default function HomePage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border-0 shadow-lg">
|
||||
<Card className="border-0 shadow-lg dark:bg-gray-700">
|
||||
<CardHeader>
|
||||
<DollarSign className="mb-4 h-12 w-12 text-green-600" />
|
||||
<CardTitle>Payment Tracking</CardTitle>
|
||||
<CardDescription>
|
||||
<DollarSign className="mb-4 h-12 w-12 text-green-600 dark:text-green-400" />
|
||||
<CardTitle className="dark:text-white">
|
||||
Payment Tracking
|
||||
</CardTitle>
|
||||
<CardDescription className="dark:text-gray-300">
|
||||
Monitor invoice status and track payments
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm text-gray-600">
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
||||
<li className="flex items-center">
|
||||
<CheckCircle className="mr-2 h-4 w-4 text-green-500" />
|
||||
Track draft, sent, paid, and overdue status
|
||||
@@ -164,19 +187,21 @@ export default function HomePage() {
|
||||
</section>
|
||||
|
||||
{/* Benefits Section */}
|
||||
<section className="bg-gray-50 px-4 py-20">
|
||||
<section className="bg-gray-50 px-4 py-20 dark:bg-gray-900">
|
||||
<div className="container mx-auto max-w-4xl text-center">
|
||||
<h2 className="mb-16 text-4xl font-bold text-gray-900">
|
||||
<h2 className="mb-16 text-4xl font-bold text-gray-900 dark:text-white">
|
||||
Why choose beenvoice?
|
||||
</h2>
|
||||
|
||||
<div className="grid gap-12 md:grid-cols-2">
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-start space-x-4">
|
||||
<Zap className="mt-1 h-8 w-8 text-green-600" />
|
||||
<Zap className="mt-1 h-8 w-8 text-green-600 dark:text-green-400" />
|
||||
<div className="text-left">
|
||||
<h3 className="mb-2 text-xl font-semibold">Lightning Fast</h3>
|
||||
<p className="text-gray-600">
|
||||
<h3 className="mb-2 text-xl font-semibold dark:text-white">
|
||||
Lightning Fast
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Create invoices in seconds, not minutes. Our streamlined
|
||||
interface gets you back to work faster.
|
||||
</p>
|
||||
@@ -184,12 +209,12 @@ export default function HomePage() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<Shield className="mt-1 h-8 w-8 text-green-600" />
|
||||
<Shield className="mt-1 h-8 w-8 text-green-600 dark:text-green-400" />
|
||||
<div className="text-left">
|
||||
<h3 className="mb-2 text-xl font-semibold">
|
||||
<h3 className="mb-2 text-xl font-semibold dark:text-white">
|
||||
Secure & Private
|
||||
</h3>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Your data is encrypted and secure. We never share your
|
||||
information with third parties.
|
||||
</p>
|
||||
@@ -199,12 +224,12 @@ export default function HomePage() {
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-start space-x-4">
|
||||
<Star className="mt-1 h-8 w-8 text-green-600" />
|
||||
<Star className="mt-1 h-8 w-8 text-green-600 dark:text-green-400" />
|
||||
<div className="text-left">
|
||||
<h3 className="mb-2 text-xl font-semibold">
|
||||
<h3 className="mb-2 text-xl font-semibold dark:text-white">
|
||||
Professional Quality
|
||||
</h3>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Generate invoices that look professional and build trust
|
||||
with your clients.
|
||||
</p>
|
||||
@@ -212,10 +237,12 @@ export default function HomePage() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<Clock className="mt-1 h-8 w-8 text-green-600" />
|
||||
<Clock className="mt-1 h-8 w-8 text-green-600 dark:text-green-400" />
|
||||
<div className="text-left">
|
||||
<h3 className="mb-2 text-xl font-semibold">Save Time</h3>
|
||||
<p className="text-gray-600">
|
||||
<h3 className="mb-2 text-xl font-semibold dark:text-white">
|
||||
Save Time
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Automated calculations, templates, and client management
|
||||
save you hours every month.
|
||||
</p>
|
||||
@@ -227,39 +254,49 @@ export default function HomePage() {
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="bg-green-600 px-4 py-20">
|
||||
<section className="bg-green-600 px-4 py-20 dark:bg-green-700">
|
||||
<div className="container mx-auto max-w-2xl text-center">
|
||||
<h2 className="mb-4 text-4xl font-bold text-white">
|
||||
Ready to get started?
|
||||
</h2>
|
||||
<p className="mb-8 text-xl text-green-100">
|
||||
<p className="mb-8 text-xl text-green-100 dark:text-green-200">
|
||||
Join thousands of freelancers who trust beenvoice for their
|
||||
invoicing needs.
|
||||
</p>
|
||||
<Link href="/auth/register">
|
||||
<Button size="lg" variant="secondary" className="px-8 py-6 text-lg">
|
||||
<Button
|
||||
size="lg"
|
||||
variant="secondary"
|
||||
className="px-8 py-6 text-lg dark:bg-white dark:text-green-700 dark:hover:bg-gray-100"
|
||||
>
|
||||
Start Your Free Trial
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<p className="mt-4 text-sm text-green-200">
|
||||
<p className="mt-4 text-sm text-green-200 dark:text-green-300">
|
||||
No credit card required • Cancel anytime
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-900 px-4 py-12 text-white">
|
||||
<footer className="bg-gray-900 px-4 py-12 text-white dark:bg-black">
|
||||
<div className="container mx-auto text-center">
|
||||
<Logo className="mx-auto mb-4" />
|
||||
<p className="mb-4 text-gray-400">
|
||||
<p className="mb-4 text-gray-400 dark:text-gray-500">
|
||||
Simple invoicing for freelancers and small businesses
|
||||
</p>
|
||||
<div className="flex justify-center space-x-6 text-sm text-gray-400">
|
||||
<Link href="/auth/signin" className="hover:text-white">
|
||||
<div className="flex justify-center space-x-6 text-sm text-gray-400 dark:text-gray-500">
|
||||
<Link
|
||||
href="/auth/signin"
|
||||
className="hover:text-white dark:hover:text-gray-300"
|
||||
>
|
||||
Sign In
|
||||
</Link>
|
||||
<Link href="/auth/register" className="hover:text-white">
|
||||
<Link
|
||||
href="/auth/register"
|
||||
className="hover:text-white dark:hover:text-gray-300"
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user