Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 120 additions & 16 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BackgroundWrapper } from "@/components/background-wrapper";
import { DashboardNavigation } from "@/components/dashboard-navigation";
import { Footer } from "@/components/footer";
import { Header } from "@/components/header";
import { Button } from "@/components/ui/button";
import { getCurrentSession } from "@/server/auth";
import { PlusCircle } from "lucide-react";
import Link from "next/link";
Expand All @@ -24,13 +25,12 @@ export default async function DashboardLayout({
<main className="flex-grow flex flex-col w-full max-w-sm sm:max-w-2xl md:max-w-4xl lg:max-w-6xl xl:max-w-[72rem] mx-auto px-4 sm:px-6 lg:px-8 py-8 z-10">
<div className="flex justify-between items-center mb-8">
<h1 className="mb-2 text-4xl font-bold tracking-tight">Dashboard</h1>
<Link
href="/invoices/create"
className="bg-black hover:bg-zinc-800 text-white transition-colors px-4 py-2 rounded-md flex items-center"
>
<PlusCircle className="mr-2 h-4 w-4" />
Create Invoice
</Link>
<Button asChild variant="default" className="flex items-center gap-2">
<Link href="/invoices/create">
<PlusCircle className="mr-2 h-4 w-4" />
Create Invoice
</Link>
</Button>
</div>
<DashboardNavigation />
{children}
Expand Down
2 changes: 1 addition & 1 deletion src/app/i/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default async function InvoiceMePage({
<div className="flex items-center mb-8">
<Link
href="/"
className="text-zinc-600 hover:text-black transition-colors mr-4"
className="text-muted-foreground hover:text-foreground transition-colors mr-4"
>
<ArrowLeft className="h-6 w-6" />
</Link>
Expand Down
54 changes: 30 additions & 24 deletions src/app/invoices/[ID]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,35 +52,37 @@ export default async function PaymentPage({
<PaymentSection serverInvoice={invoice} />

{/* Invoice Preview */}
<Card className="w-full bg-white shadow-sm border-0">
<Card className="w-full shadow-sm border-0">
<CardContent className="p-8">
{/* Header Section */}
<div className="mb-12">
<div className="grid grid-cols-12 gap-4">
<div className="col-span-4">
<div className="text-xs text-neutral-500 mb-1">
<div className="text-xs text-muted-foreground mb-1">
INVOICE NO
</div>
<div className="text-sm font-medium">
{invoice.invoiceNumber}
</div>
</div>
<div className="col-span-4">
<div className="text-xs text-neutral-500 mb-1">ISSUED</div>
<div className="text-xs text-muted-foreground mb-1">
ISSUED
</div>
<div className="text-sm">
{formatDate(invoice.issuedDate)}
</div>
</div>
<div className="col-span-4">
<div className="text-xs text-neutral-500 mb-1">
<div className="text-xs text-muted-foreground mb-1">
DUE DATE
</div>
<div className="text-sm">{formatDate(invoice.dueDate)}</div>
</div>
</div>
{invoice.recurrence && (
<div className="mt-4">
<div className="text-xs text-neutral-500 mb-1">
<div className="text-xs text-muted-foreground mb-1">
RECURRING
</div>
<div className="text-sm flex items-center gap-1">
Expand Down Expand Up @@ -108,23 +110,23 @@ export default async function PaymentPage({
{/* From/To Section */}
<div className="grid grid-cols-2 gap-16 mb-12">
<div>
<div className="text-xs text-neutral-500 mb-3">FROM</div>
<div className="text-xs text-muted-foreground mb-3">FROM</div>
<div className="space-y-1">
<div className="text-sm">{invoice.creatorName}</div>
<div className="text-sm text-neutral-600">
<div className="text-sm text-muted-foreground">
{invoice.creatorEmail}
</div>
<div className="text-sm mt-4">PAYABLE TO:</div>
<div className="text-sm text-neutral-600 font-mono break-all">
<div className="text-sm text-muted-foreground font-mono break-all">
{invoice.payee}
</div>
</div>
</div>
<div>
<div className="text-xs text-neutral-500 mb-3">TO</div>
<div className="text-xs text-muted-foreground mb-3">TO</div>
<div className="space-y-1">
<div className="text-sm">{invoice.clientName}</div>
<div className="text-sm text-neutral-600">
<div className="text-sm text-muted-foreground">
{invoice.clientEmail}
</div>
</div>
Expand All @@ -136,21 +138,21 @@ export default async function PaymentPage({
<table className="w-full">
<thead>
<tr>
<th className="text-xs text-neutral-500 text-left pb-3">
<th className="text-xs text-muted-foreground text-left pb-3">
DESCRIPTION
</th>
<th className="text-xs text-neutral-500 text-right pb-3">
<th className="text-xs text-muted-foreground text-right pb-3">
QTY
</th>
<th className="text-xs text-neutral-500 text-right pb-3">
<th className="text-xs text-muted-foreground text-right pb-3">
PRICE
</th>
<th className="text-xs text-neutral-500 text-right pb-3">
<th className="text-xs text-muted-foreground text-right pb-3">
AMOUNT
</th>
</tr>
</thead>
<tbody className="border-y border-neutral-200">
<tbody className="border-y border-border">
{(invoice.items as InvoiceItem[]).map((item, index) => (
<tr key={`invoice-item-${item.description}-${index}`}>
<td className="py-3">
Expand All @@ -174,18 +176,20 @@ export default async function PaymentPage({
<div className="flex justify-end mt-6">
<div className="w-48">
<div className="flex justify-between py-2">
<span className="text-sm text-neutral-600">Subtotal</span>
<span className="text-sm text-muted-foreground">
Subtotal
</span>
<span className="text-sm">
{Number(invoice.amount).toString()}
</span>
</div>
<div className="flex justify-between py-2 border-t border-neutral-200">
<div className="flex justify-between py-2 border-t border-border">
<span className="text-sm font-medium">Total</span>
<div>
<div className="text-sm text-right font-medium">
{Number(invoice.amount).toString()}
</div>
<div className="text-xs text-neutral-500">
<div className="text-xs text-muted-foreground">
{formatCurrencyLabel(invoice.invoiceCurrency)}
</div>
</div>
Expand All @@ -197,7 +201,7 @@ export default async function PaymentPage({
{/* Payment Details and Notes Section */}
<div className="grid grid-cols-2 gap-16">
<div>
<div className="text-xs text-neutral-500 mb-1">
<div className="text-xs text-muted-foreground mb-1">
PAYABLE IN
</div>
<div className="text-sm">
Expand All @@ -206,7 +210,7 @@ export default async function PaymentPage({
</div>
{paymentDetailsData?.paymentDetails ? (
<div>
<div className="text-xs text-neutral-500 mb-1">
<div className="text-xs text-muted-foreground mb-1">
BANK ACCOUNT DETAILS
</div>
{paymentDetailsData.paymentDetails.accountName && (
Expand Down Expand Up @@ -245,10 +249,10 @@ export default async function PaymentPage({
</div>
) : (
<div>
<div className="text-xs text-neutral-500 mb-1">
<div className="text-xs text-muted-foreground mb-1">
BANK ACCOUNT DETAILS
</div>
<div className="text-sm text-neutral-500">
<div className="text-sm text-muted-foreground">
{!invoice.paymentDetailsId
? "No payment details available"
: "Unable to load payment details"}
Expand All @@ -257,8 +261,10 @@ export default async function PaymentPage({
)}
{invoice.notes && (
<div>
<div className="text-xs text-neutral-500 mb-1">NOTES</div>
<div className="text-sm text-neutral-600 whitespace-pre-wrap break-words max-w-[300px]">
<div className="text-xs text-muted-foreground mb-1">
NOTES
</div>
<div className="text-sm text-muted-foreground whitespace-pre-wrap break-words max-w-[300px]">
{invoice.notes}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/app/invoices/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default async function CreateInvoicePage() {
<div className="flex items-center mb-8">
<Link
href="/dashboard"
className="text-zinc-600 hover:text-black transition-colors mr-4"
className="text-muted-foreground hover:text-foreground transition-colors mr-4"
>
<ArrowLeft className="h-6 w-6" />
</Link>
Expand Down
31 changes: 20 additions & 11 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { Metadata } from "next";
import localFont from "next/font/local";
import { cookies } from "next/headers";
import "./globals.css";
import { ThemeProvider } from "next-themes";

const geistSans = localFont({
src: "./fonts/GeistVF.woff",
Expand All @@ -32,20 +33,28 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<GoogleTagManager gtmId={process.env.NEXT_PUBLIC_GTM_ID as string} />
<html lang="en" suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<AppKit>
<TooltipProvider>
<TRPCReactProvider cookies={cookies().toString()}>
<BackgroundWrapper>{children}</BackgroundWrapper>
</TRPCReactProvider>
<Toaster />
</TooltipProvider>
</AppKit>
<VersionDisplay githubRelease="https://github.com/RequestNetwork/easy-invoice/releases" />
<GoogleTagManager gtmId={process.env.NEXT_PUBLIC_GTM_ID as string} />
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
enableColorScheme
disableTransitionOnChange
>
<AppKit>
<TooltipProvider>
<TRPCReactProvider cookies={cookies().toString()}>
<BackgroundWrapper>{children}</BackgroundWrapper>
</TRPCReactProvider>
<Toaster />
</TooltipProvider>
</AppKit>
<VersionDisplay githubRelease="https://github.com/RequestNetwork/easy-invoice/releases" />
</ThemeProvider>
</body>
</html>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/subscription-plans/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default async function SubscriptionPlansPage() {
<div className="flex items-center mb-8">
<Link
href="/dashboard"
className="text-zinc-600 hover:text-black transition-colors mr-4"
className="text-muted-foreground hover:text-foreground transition-colors mr-4"
>
<ArrowLeft className="h-6 w-6" />
</Link>
Expand Down
35 changes: 30 additions & 5 deletions src/components/background-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"use client";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import type { ReactNode } from "react";

interface BackgroundWrapperProps {
Expand All @@ -23,6 +26,13 @@ export function BackgroundWrapper({
to: "zinc-200",
},
}: BackgroundWrapperProps) {
const { resolvedTheme } = useTheme();
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
setIsMounted(true);
}, []);

// Convert Tailwind color names to CSS variables or hex values
const getTailwindColor = (colorName: string): string => {
const colors: Record<string, string> = {
Expand All @@ -42,24 +52,39 @@ export function BackgroundWrapper({
"zinc-100": "#f4f4f5",
"zinc-200": "#e4e4e7",

// Dark mode colors
"zinc-800": "#27272a",
"zinc-900": "#18181b",
"slate-800": "#1e293b",
"slate-900": "#0f172a",

// Add any other colors you need here
};

return colors[colorName] || "#f4f4f5"; // Default to zinc-100 if color not found
};

// Only trust theme after mount to keep SSR/CSR output consistent
const isDark = isMounted && resolvedTheme === "dark";

return (
<div className="min-h-screen relative overflow-hidden bg-[#FAFAFA]">
{/* Decorative elements */}
<div className="absolute top-0 right-0 w-[600px] h-[600px] -translate-y-1/2 translate-x-1/2">
<div className="min-h-screen relative overflow-hidden bg-background">
{/* Decorative elements: keep DOM shape stable; toggle visibility */}
<div
className="absolute top-0 right-0 w-[600px] h-[600px] -translate-y-1/2 translate-x-1/2"
style={{ display: isMounted && !isDark ? "block" : "none" }}
>
<div
className="w-full h-full rounded-full opacity-30 blur-3xl"
style={{
background: `linear-gradient(to bottom right, ${getTailwindColor(topGradient.from)}, ${getTailwindColor(topGradient.to)})`,
}}
/>
</div>
<div className="absolute bottom-0 left-0 w-[600px] h-[600px] translate-y-1/2 -translate-x-1/2">
<div
className="absolute bottom-0 left-0 w-[600px] h-[600px] translate-y-1/2 -translate-x-1/2"
style={{ display: isMounted && !isDark ? "block" : "none" }}
>
<div
className="w-full h-full rounded-full opacity-30 blur-3xl"
style={{
Expand All @@ -73,7 +98,7 @@ export function BackgroundWrapper({
className="absolute inset-0"
style={{
backgroundImage:
"radial-gradient(circle at 1px 1px, #e5e5e5 1px, transparent 0)",
"radial-gradient(circle at 1px 1px, hsl(var(--muted)) 1px, transparent 0)",
backgroundSize: "40px 40px",
}}
/>
Expand Down
Loading