diff --git a/src/pages/dashboard/invoices/index.astro b/src/pages/dashboard/invoices/index.astro
index ccffc33..077aae0 100644
--- a/src/pages/dashboard/invoices/index.astro
+++ b/src/pages/dashboard/invoices/index.astro
@@ -3,7 +3,7 @@ import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import { db } from '../../../db';
import { invoices, clients, members } from '../../../db/schema';
-import { eq, desc, and } from 'drizzle-orm';
+import { eq, desc, and, gte, lte, sql } from 'drizzle-orm';
const user = Astro.locals.user;
if (!user) return Astro.redirect('/login');
@@ -25,17 +25,71 @@ const userMembership = currentTeamId
const currentTeamIdResolved = userMembership.organizationId;
-// Fetch invoices and quotes
-const allInvoices = await db.select({
+// Get filter parameters
+const currentYear = new Date().getFullYear();
+const selectedYear = Astro.url.searchParams.get('year') ? parseInt(Astro.url.searchParams.get('year')!) : 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))
- .orderBy(desc(invoices.issueDate))
.all();
+// Get unique years from invoices
+const availableYears = [...new Set(allInvoicesRaw.map(i => i.invoice.issueDate.getFullYear()))].sort((a, b) => b - a);
+
+// Filter by year
+const yearStart = new Date(selectedYear, 0, 1);
+const yearEnd = new Date(selectedYear, 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 formatCurrency = (amount: number, currency: string) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
@@ -75,8 +129,8 @@ const getStatusColor = (status: string) => {
+ Showing {allInvoices.length} + {allInvoices.length === 1 ? 'result' : 'results'} + {selectedYear && ` for ${selectedYear}`} +
+