diff --git a/README.md b/README.md index d071b86..44b76ef 100644 --- a/README.md +++ b/README.md @@ -8,23 +8,29 @@ A modern, professional invoicing application built for freelancers and small bus ## ✨ Features -- **🔐 Secure Authentication** - Email/password registration and sign-in with NextAuth.js +- **🔐 Secure Authentication** - Email/password registration and sign-in with better-auth, plus SSO via Authentik OIDC - **👥 Client Management** - Create, edit, and manage client information +- **🏢 Business Profiles** - Manage your business details, logo, and email settings - **📄 Professional Invoices** - Generate detailed invoices with line items +- **📅 Timesheet View** - Calendar-based time entry with month and week views +- **📧 Email Delivery** - Send invoices via email using Resend +- **📥 PDF Export** - Download invoices as professional PDFs +- **📊 CSV Import** - Bulk import invoice data from CSV files - **💰 Flexible Pricing** - Set custom rates and calculate totals automatically - **📱 Responsive Design** - Works seamlessly on desktop, tablet, and mobile - **🎨 Modern UI** - Clean, professional interface built with shadcn/ui - **⚡ Type-Safe** - Full TypeScript support with tRPC for API calls -- **💾 Local Database** - SQLite database with Drizzle ORM +- **💾 PostgreSQL Database** - Robust relational database with Drizzle ORM ## 🚀 Tech Stack -- **Frontend**: Next.js 15 with App Router +- **Frontend**: Next.js 16 with App Router - **Backend**: tRPC for type-safe API calls -- **Database**: Drizzle ORM with LibSQL (SQLite) -- **Authentication**: NextAuth.js with email/password -- **UI Components**: shadcn/ui with Tailwind CSS -- **Styling**: Geist font family +- **Database**: Drizzle ORM with PostgreSQL +- **Authentication**: better-auth with email/password and Authentik OIDC SSO +- **UI Components**: shadcn/ui with Tailwind CSS v4 +- **Email**: Resend for transactional email delivery +- **PDF**: @react-pdf/renderer for invoice PDF generation - **Package Manager**: Bun ## 📦 Installation @@ -32,6 +38,7 @@ A modern, professional invoicing application built for freelancers and small bus ### Prerequisites - Node.js 18+ or Bun +- Docker & Docker Compose (for local PostgreSQL) - Git ### Quick Start @@ -43,7 +50,6 @@ A modern, professional invoicing application built for freelancers and small bus ``` 2. **Install dependencies** - ```bash ```bash bun install ``` @@ -55,22 +61,39 @@ A modern, professional invoicing application built for freelancers and small bus Edit `.env.local` and add your configuration: ```env - DATABASE_URL="file:./db.sqlite" - NEXTAUTH_SECRET="your-secret-key-here" - NEXTAUTH_URL="http://localhost:3000" + # Database + DATABASE_URL="postgresql://postgres:password@localhost:5432/beenvoice" + DB_DISABLE_SSL="true" + + # Authentication + AUTH_SECRET="your-secret-key-here" + BETTER_AUTH_URL="http://localhost:3000" + + # Application + NEXT_PUBLIC_APP_URL="http://localhost:3000" + NODE_ENV="development" + + # Email (optional for local dev) + RESEND_API_KEY="your-resend-api-key" + RESEND_DOMAIN="yourdomain.com" ``` -4. **Initialize the database** +4. **Start the database** + ```bash + docker-compose up -d + ``` + +5. **Push the database schema** ```bash bun run db:push ``` -5. **Start the development server** +6. **Start the development server** ```bash bun run dev ``` -6. **Open your browser** +7. **Open your browser** Navigate to [http://localhost:3000](http://localhost:3000) ## 🏗️ Project Structure @@ -79,21 +102,28 @@ A modern, professional invoicing application built for freelancers and small bus beenvoice/ ├── src/ │ ├── app/ # Next.js App Router pages -│ │ ├── api/ # API routes (NextAuth, tRPC) +│ │ ├── api/ # API routes (better-auth, tRPC) │ │ ├── auth/ # Authentication pages -│ │ ├── clients/ # Client management pages -│ │ ├── invoices/ # Invoice management pages +│ │ ├── dashboard/ # Main app pages +│ │ │ ├── clients/ # Client management pages +│ │ │ ├── invoices/ # Invoice management pages +│ │ │ └── businesses/ # Business profile pages │ │ └── _components/ # Page-specific components │ ├── components/ # Shared UI components +│ │ ├── ui/ # shadcn/ui components +│ │ ├── data/ # Data display components +│ │ ├── forms/ # Form components +│ │ └── layout/ # Layout components │ ├── server/ # Server-side code │ │ ├── api/ # tRPC routers -│ │ ├── auth/ # NextAuth configuration │ │ └── db/ # Database schema and connection +│ ├── lib/ # Utilities (auth, pdf export, etc.) │ ├── styles/ # Global styles │ └── trpc/ # tRPC client configuration ├── drizzle/ # Database migrations ├── public/ # Static assets -└── docs/ # Documentation +├── docs/ # Documentation +└── docker-compose.yml # Local PostgreSQL setup ``` ## 🎯 Usage @@ -103,41 +133,53 @@ beenvoice/ 1. **Register an Account** - Visit the sign-up page - Enter your name, email, and password - - Verify your email (if configured) -2. **Add Your First Client** +2. **Set Up Your Business** + - Navigate to Business Settings + - Add your business name, contact info, and logo + - Configure email settings for invoice delivery (Resend API key + domain) + +3. **Add Your First Client** - Navigate to the Clients page - Click "Add New Client" - Fill in client details (name, email, phone, address) -3. **Create an Invoice** +4. **Create an Invoice** - Go to the Invoices page - Click "Create New Invoice" - - Select a client + - Select a client and optionally a business profile - Add line items with descriptions, dates, hours, and rates - - Save and generate your invoice + - Use the Timesheet tab for calendar-based time entry + - Save and send or download as PDF ### Features Overview #### Client Management - Create and edit client profiles - Store contact information and addresses +- Set default hourly rates per client - Search and filter client list -- View client history #### Invoice Creation -- Select from existing clients -- Add multiple line items +- Select from existing clients and business profiles +- Add multiple line items with drag-and-drop reordering - Set custom rates per item -- Automatic total calculations +- Automatic total calculations with configurable tax rate +- Timesheet calendar view for date-based time tracking - Professional invoice formatting +#### Invoice Delivery +- Send invoices via email directly from the app +- Rich text email composer with preview +- Resend and re-deliver sent invoices +- Track invoice status: Draft → Sent → Paid (+ Overdue) + #### User Interface - Clean, modern design -- Responsive layout -- Intuitive navigation +- Fully responsive — desktop, tablet, and mobile +- Intuitive navigation with breadcrumbs - Toast notifications for feedback -- Modal dialogs for forms +- Dark mode support ## 🔧 Development @@ -145,44 +187,53 @@ beenvoice/ ```bash # Development -bun run dev # Start development server +bun run dev # Start development server (Turbo) bun run build # Build for production bun run start # Start production server # Database bun run db:push # Push schema changes to database +bun run db:migrate # Run migrations bun run db:studio # Open Drizzle Studio bun run db:generate # Generate new migration +# Docker +bun run docker:up # Start local PostgreSQL via Docker +bun run docker:down # Stop Docker services + # Code Quality bun run lint # Run ESLint -bun run format # Format code with Prettier -bun run type-check # Run TypeScript type checking +bun run lint:fix # Fix ESLint issues +bun run format:write # Format code with Prettier +bun run typecheck # Run TypeScript type checking ``` ### Database Schema -The application uses four main tables: +The application uses the following core tables: -- **users**: User accounts and authentication -- **clients**: Client information and contact details -- **invoices**: Invoice headers with client relationships -- **invoice_items**: Individual line items with pricing +- **users** - User accounts and authentication +- **sessions** - Active user sessions +- **clients** - Client information and contact details +- **businesses** - Business profiles with email/logo settings +- **invoices** - Invoice headers with client and business relationships +- **invoice_items** - Individual line items with pricing and position ordering ### API Development All API endpoints are built with tRPC for type safety: -- **Authentication**: NextAuth.js integration +- **Authentication**: better-auth integration (email/password + OIDC) - **Clients**: CRUD operations for client management -- **Invoices**: Invoice creation and management +- **Businesses**: Business profile management +- **Invoices**: Invoice creation, management, and status tracking - **Validation**: Zod schemas for input validation ## 🎨 Customization ### Styling -The app uses Tailwind CSS with a custom design system: +The app uses Tailwind CSS v4 with a custom design system: - **Primary Color**: Green (#16a34a) - **Font**: Geist for professional typography @@ -198,38 +249,54 @@ Update the logo and colors in: ## 🚀 Deployment -### Deployment +You can deploy this application to any platform that supports Next.js and PostgreSQL (Docker, Coolify, Railway, etc.). -You can deploy this application to any platform that supports Next.js (Docker, Coolify, Railway, etc.). +1. **Build the application:** + ```bash + bun run build + ``` -1. Build the application: -```bash -bun run build -``` +2. **Set up production environment variables** (see `.env.local` example above, adjusting URLs and secrets for production) -2. Start the server: -```bash -bun start -``` +3. **Run database migrations:** + ```bash + bun run db:push + ``` -### Other Platforms - -The app can be deployed to any platform that supports Next.js: - -- **Netlify**: Use the Next.js build command -- **Railway**: Connect your GitHub repository -- **DigitalOcean App Platform**: Deploy with automatic scaling +4. **Start the server:** + ```bash + bun start + ``` ### Environment Variables Required for production: ```env -DATABASE_URL="your-database-url" -NEXTAUTH_SECRET="your-secret-key" -NEXTAUTH_URL="https://your-domain.com" +DATABASE_URL="postgresql://user:password@host:5432/dbname" +AUTH_SECRET="your-long-random-secret" +BETTER_AUTH_URL="https://your-domain.com" +NEXT_PUBLIC_APP_URL="https://your-domain.com" +NODE_ENV="production" + +# Email (required for invoice sending) +RESEND_API_KEY="re_xxxxxxxxxxxx" +RESEND_DOMAIN="yourdomain.com" + +# Optional: Authentik SSO +AUTHENTIK_ISSUER="https://your-authentik-instance/application/o/beenvoice/" +AUTHENTIK_CLIENT_ID="your-client-id" +AUTHENTIK_CLIENT_SECRET="your-client-secret" ``` +### Other Platforms + +The app can be deployed to any platform that supports Next.js: + +- **Coolify**: Deploy with Docker Compose support +- **Railway**: Connect your GitHub repository (includes managed PostgreSQL) +- **DigitalOcean App Platform**: Deploy with automatic scaling + ## 🤝 Contributing 1. Fork the repository @@ -243,8 +310,7 @@ NEXTAUTH_URL="https://your-domain.com" - Follow TypeScript best practices - Use shadcn/ui components for consistency - Implement proper error handling -- Add tests for new features -- Follow the existing code style +- Follow the existing code style (Prettier + ESLint configs provided) ## 📄 License @@ -254,14 +320,14 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file - [T3 Stack](https://create.t3.gg/) for the excellent development stack - [shadcn/ui](https://ui.shadcn.com/) for beautiful UI components -- [NextAuth.js](https://next-auth.js.org/) for authentication +- [better-auth](https://www.better-auth.com/) for modern authentication - [Drizzle ORM](https://orm.drizzle.team/) for database management +- [Resend](https://resend.com/) for reliable email delivery ## 📞 Support - **Issues**: [GitHub Issues](https://github.com/yourusername/beenvoice/issues) - **Discussions**: [GitHub Discussions](https://github.com/yourusername/beenvoice/discussions) -- **Email**: support@beenvoice.com --- diff --git a/src/app/dashboard/invoices/[id]/_components/invoice-items-table.tsx b/src/app/dashboard/invoices/[id]/_components/invoice-items-table.tsx index 646733f..d456954 100644 --- a/src/app/dashboard/invoices/[id]/_components/invoice-items-table.tsx +++ b/src/app/dashboard/invoices/[id]/_components/invoice-items-table.tsx @@ -40,13 +40,32 @@ const columns: ColumnDef[] = [ accessorKey: "date", header: "Date", cell: ({ row }) => formatDate(row.getValue("date")), + meta: { + headerClassName: "hidden sm:table-cell", + cellClassName: "hidden sm:table-cell", + }, }, { accessorKey: "description", header: "Description", - cell: ({ row }) => ( -
{row.getValue("description")}
- ), + cell: ({ row }) => { + const item = row.original; + return ( + <> + {/* Desktop: plain description */} +
+ {item.description} +
+ {/* Mobile: description + date + hours @ rate stacked */} +
+

{item.description}

+

+ {formatDate(item.date)} · {item.hours}h @ {formatCurrency(item.rate)}/hr +

+
+ + ); + }, }, { accessorKey: "hours", @@ -54,6 +73,10 @@ const columns: ColumnDef[] = [ cell: ({ row }) => (
{row.getValue("hours")}
), + meta: { + headerClassName: "hidden sm:table-cell", + cellClassName: "hidden sm:table-cell", + }, }, { accessorKey: "rate", @@ -61,6 +84,10 @@ const columns: ColumnDef[] = [ cell: ({ row }) => (
{formatCurrency(row.getValue("rate"))}
), + meta: { + headerClassName: "hidden sm:table-cell", + cellClassName: "hidden sm:table-cell", + }, }, { accessorKey: "amount", diff --git a/src/app/dashboard/invoices/_components/invoices-data-table.tsx b/src/app/dashboard/invoices/_components/invoices-data-table.tsx index fdbb7a8..84a1f6a 100644 --- a/src/app/dashboard/invoices/_components/invoices-data-table.tsx +++ b/src/app/dashboard/invoices/_components/invoices-data-table.tsx @@ -134,13 +134,23 @@ export function InvoicesDataTable({ invoices }: InvoicesDataTableProps) {
-
+

{invoice.client?.name ?? "—"}

{invoice.invoiceNumber}

+ {/* Show status + amount inline on mobile only */} +
+ + + {formatCurrency(invoice.totalAmount)} + +
); diff --git a/src/components/data/invoice-view.tsx b/src/components/data/invoice-view.tsx index 8fd4f22..c9abaf5 100644 --- a/src/components/data/invoice-view.tsx +++ b/src/components/data/invoice-view.tsx @@ -185,14 +185,14 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) { {/* Invoice Header Card */} -
-
+
+
-
+
-
-

+
+

{invoice.invoiceNumber}

@@ -217,21 +217,23 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {

-
- - - -
- {formatCurrency(invoice.totalAmount)} +
+
+ + + +
+ {formatCurrency(invoice.totalAmount)} +