--- import DashboardLayout from '../../../layouts/DashboardLayout.astro'; import { Icon } from 'astro-icon/components'; import StatCard from '../../../components/StatCard.astro'; import { db } from '../../../db'; import { invoices, clients } from '../../../db/schema'; import { eq, desc, and, gte, lte, sql } from 'drizzle-orm'; import { getCurrentTeam } from '../../../lib/getCurrentTeam'; import { formatCurrency } from '../../../lib/formatTime'; const user = Astro.locals.user; if (!user) return Astro.redirect('/login'); const userMembership = await getCurrentTeam(user, Astro.cookies.get('currentTeamId')?.value); if (!userMembership) return Astro.redirect('/dashboard'); const currentTeamIdResolved = userMembership.organizationId; // Get filter parameters const currentYear = new Date().getFullYear(); const yearParam = Astro.url.searchParams.get('year'); const selectedYear: string | number = yearParam === 'current' || !yearParam ? 'current' : parseInt(yearParam); const yearNum = typeof selectedYear === 'number' ? selectedYear : currentYear; const selectedType = Astro.url.searchParams.get('type') || 'all'; const selectedStatus = Astro.url.searchParams.get('status') || 'all'; const sortBy = Astro.url.searchParams.get('sort') || 'date-desc'; // Fetch all invoices for the organization (for year dropdown) const allInvoicesRaw = await db.select({ invoice: invoices, client: clients, }) .from(invoices) .leftJoin(clients, eq(invoices.clientId, clients.id)) .where(eq(invoices.organizationId, currentTeamIdResolved)) .all(); // Get unique years from invoices const availableYears = [...new Set(allInvoicesRaw.map(i => i.invoice.issueDate.getFullYear()))].sort((a, b) => b - a); // Ensure current year is in the list if (!availableYears.includes(currentYear)) { availableYears.unshift(currentYear); } // Filter by year const yearStart = new Date(yearNum, 0, 1); const yearEnd = selectedYear === 'current' ? new Date() : new Date(yearNum, 11, 31, 23, 59, 59); let filteredInvoices = allInvoicesRaw.filter(i => { const issueDate = i.invoice.issueDate; return issueDate >= yearStart && issueDate <= yearEnd; }); // Filter by type if (selectedType !== 'all') { filteredInvoices = filteredInvoices.filter(i => i.invoice.type === selectedType); } // Filter by status if (selectedStatus !== 'all') { filteredInvoices = filteredInvoices.filter(i => i.invoice.status === selectedStatus); } // Sort invoices const allInvoices = filteredInvoices.sort((a, b) => { switch (sortBy) { case 'date-desc': return b.invoice.issueDate.getTime() - a.invoice.issueDate.getTime(); case 'date-asc': return a.invoice.issueDate.getTime() - b.invoice.issueDate.getTime(); case 'amount-desc': return b.invoice.total - a.invoice.total; case 'amount-asc': return a.invoice.total - b.invoice.total; case 'number-desc': return b.invoice.number.localeCompare(a.invoice.number); case 'number-asc': return a.invoice.number.localeCompare(b.invoice.number); default: return b.invoice.issueDate.getTime() - a.invoice.issueDate.getTime(); } }); // Calculate stats for the selected year const yearInvoices = allInvoicesRaw.filter(i => { const issueDate = i.invoice.issueDate; return issueDate >= yearStart && issueDate <= yearEnd; }); const getStatusColor = (status: string) => { switch (status) { case 'paid': return 'badge-success'; case 'accepted': return 'badge-success'; case 'sent': return 'badge-info'; case 'draft': return 'badge-ghost'; case 'void': return 'badge-error'; case 'declined': return 'badge-error'; default: return 'badge-ghost'; } }; ---

Invoices & Quotes

Manage your billing and estimates

Create New
i.invoice.type === 'invoice').length)} description={selectedYear === 'current' ? `${currentYear} (YTD)` : String(selectedYear)} icon="heroicons:document-text" color="text-primary" /> i.invoice.type === 'quote' && i.invoice.status === 'sent').length)} description="Waiting for approval" icon="heroicons:clipboard-document-list" color="text-secondary" /> i.invoice.type === 'invoice' && i.invoice.status === 'paid') .reduce((acc, curr) => acc + curr.invoice.total, 0), 'USD')} description={`Paid invoices (${selectedYear === 'current' ? `${currentYear} YTD` : selectedYear})`} icon="heroicons:currency-dollar" color="text-success" />
Year
Type
Status
Sort By
{(selectedYear !== 'current' || selectedType !== 'all' || selectedStatus !== 'all' || sortBy !== 'date-desc') && ( )}

Showing {allInvoices.length} {allInvoices.length === 1 ? 'result' : 'results'} {selectedYear === 'current' ? ` for ${currentYear} (year to date)` : ` for ${selectedYear}`}

{allInvoices.length === 0 ? ( ) : ( allInvoices.map(({ invoice, client }) => ( )) )}
Number Client Date Due Date Amount Status Type
No invoices or quotes found. Create one to get started.
{invoice.number} {client ? (
{client.name}
) : ( Deleted Client )}
{invoice.issueDate.toLocaleDateString()} {invoice.dueDate.toLocaleDateString()} {formatCurrency(invoice.total, invoice.currency)}
{invoice.status}
{invoice.type}