diff --git a/internal/handlers/customers.go b/internal/handlers/customers.go index a6abe20..15ccf50 100644 --- a/internal/handlers/customers.go +++ b/internal/handlers/customers.go @@ -9,18 +9,32 @@ import ( func (h *Handler) CustomerList(w http.ResponseWriter, r *http.Request) { search := r.URL.Query().Get("search") - customers, err := models.CustomerGetAll(h.DB, search) + page, _ := strconv.Atoi(r.URL.Query().Get("page")) + if page < 1 { + page = 1 + } + limit := 10 + + customers, total, err := models.CustomerGetPaginated(h.DB, search, page, limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + totalPages := (total + limit - 1) / limit + data := map[string]interface{}{ "Title": "Customers", "Username": h.getUsername(r), "ActivePage": "customers", "Customers": customers, "Search": search, + "Page": page, + "TotalPages": totalPages, + "HasPrev": page > 1, + "HasNext": page < totalPages, + "PrevPage": page - 1, + "NextPage": page + 1, } // HTMX partial for search diff --git a/internal/handlers/invoices.go b/internal/handlers/invoices.go index a601857..f61a5ac 100644 --- a/internal/handlers/invoices.go +++ b/internal/handlers/invoices.go @@ -10,18 +10,32 @@ import ( func (h *Handler) InvoiceList(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") - invoices, err := models.InvoiceGetAll(h.DB, status) + page, _ := strconv.Atoi(r.URL.Query().Get("page")) + if page < 1 { + page = 1 + } + limit := 10 + + invoices, total, err := models.InvoiceGetPaginated(h.DB, status, page, limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + totalPages := (total + limit - 1) / limit + data := map[string]interface{}{ "Title": "Invoices", "Username": h.getUsername(r), "ActivePage": "invoices", "Invoices": invoices, "FilterStatus": status, + "Page": page, + "TotalPages": totalPages, + "HasPrev": page > 1, + "HasNext": page < totalPages, + "PrevPage": page - 1, + "NextPage": page + 1, } if r.Header.Get("HX-Request") == "true" && r.URL.Query().Get("partial") == "true" { diff --git a/internal/handlers/ledger.go b/internal/handlers/ledger.go index d462a14..d3a1561 100644 --- a/internal/handlers/ledger.go +++ b/internal/handlers/ledger.go @@ -25,17 +25,31 @@ func (h *Handler) ChartOfAccounts(w http.ResponseWriter, r *http.Request) { } func (h *Handler) JournalEntries(w http.ResponseWriter, r *http.Request) { - entries, err := models.JournalEntryGetAll(h.DB) + page, _ := strconv.Atoi(r.URL.Query().Get("page")) + if page < 1 { + page = 1 + } + limit := 20 + + entries, total, err := models.JournalEntryGetPaginated(h.DB, page, limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + totalPages := (total + limit - 1) / limit + data := map[string]interface{}{ "Title": "Journal Entries", "Username": h.getUsername(r), "ActivePage": "ledger", "Entries": entries, + "Page": page, + "TotalPages": totalPages, + "HasPrev": page > 1, + "HasNext": page < totalPages, + "PrevPage": page - 1, + "NextPage": page + 1, } h.render(w, []string{"layout.html", "ledger/journal_entries.html"}, data) } diff --git a/internal/handlers/orders.go b/internal/handlers/orders.go index 63a83c6..9ecef78 100644 --- a/internal/handlers/orders.go +++ b/internal/handlers/orders.go @@ -11,18 +11,32 @@ import ( func (h *Handler) OrderList(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") - orders, err := models.OrderGetAll(h.DB, status) + page, _ := strconv.Atoi(r.URL.Query().Get("page")) + if page < 1 { + page = 1 + } + limit := 10 + + orders, total, err := models.OrderGetPaginated(h.DB, status, page, limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + totalPages := (total + limit - 1) / limit + data := map[string]interface{}{ "Title": "Orders", "Username": h.getUsername(r), "ActivePage": "orders", "Orders": orders, "FilterStatus": status, + "Page": page, + "TotalPages": totalPages, + "HasPrev": page > 1, + "HasNext": page < totalPages, + "PrevPage": page - 1, + "NextPage": page + 1, } if r.Header.Get("HX-Request") == "true" && r.URL.Query().Get("partial") == "true" { diff --git a/internal/models/customer.go b/internal/models/customer.go index b984f1e..c9a2741 100644 --- a/internal/models/customer.go +++ b/internal/models/customer.go @@ -84,3 +84,46 @@ func CustomerCount(db *sql.DB) int { db.QueryRow("SELECT COUNT(*) FROM customers").Scan(&count) return count } + +func CustomerGetPaginated(db *sql.DB, search string, page, limit int) ([]Customer, int, error) { + offset := (page - 1) * limit + + // Base queries + query := "SELECT id, name, email, phone, address, created_at, updated_at FROM customers" + countQuery := "SELECT COUNT(*) FROM customers" + + args := []interface{}{} + if search != "" { + where := " WHERE name LIKE ? OR email LIKE ?" + query += where + countQuery += where + s := "%" + search + "%" + args = append(args, s, s) + } + + // Get total count first + var total int + if err := db.QueryRow(countQuery, args...).Scan(&total); err != nil { + return nil, 0, err + } + + // Add limit and offset to query + query += " ORDER BY name LIMIT ? OFFSET ?" + args = append(args, limit, offset) + + rows, err := db.Query(query, args...) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var customers []Customer + for rows.Next() { + var c Customer + if err := rows.Scan(&c.ID, &c.Name, &c.Email, &c.Phone, &c.Address, &c.CreatedAt, &c.UpdatedAt); err != nil { + return nil, 0, err + } + customers = append(customers, c) + } + return customers, total, nil +} diff --git a/internal/models/invoice.go b/internal/models/invoice.go index 4fc977d..d21fa7c 100644 --- a/internal/models/invoice.go +++ b/internal/models/invoice.go @@ -46,6 +46,48 @@ func InvoiceGetAll(db *sql.DB, status string) ([]Invoice, error) { return invoices, nil } +func InvoiceGetPaginated(db *sql.DB, status string, page, limit int) ([]Invoice, int, error) { + offset := (page - 1) * limit + + // Base queries + query := `SELECT i.id, i.order_id, i.customer_id, c.name, i.invoice_number, i.status, i.amount, i.due_date, i.paid_date, i.created_at + FROM invoices i JOIN customers c ON i.customer_id = c.id` + countQuery := "SELECT COUNT(*) FROM invoices i" + + args := []interface{}{} + if status != "" { + where := " WHERE i.status = ?" + query += where + countQuery += where + args = append(args, status) + } + + // Get total count + var total int + if err := db.QueryRow(countQuery, args...).Scan(&total); err != nil { + return nil, 0, err + } + + query += " ORDER BY i.created_at DESC LIMIT ? OFFSET ?" + args = append(args, limit, offset) + + rows, err := db.Query(query, args...) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var invoices []Invoice + for rows.Next() { + var inv Invoice + if err := rows.Scan(&inv.ID, &inv.OrderID, &inv.CustomerID, &inv.CustomerName, &inv.InvoiceNumber, &inv.Status, &inv.Amount, &inv.DueDate, &inv.PaidDate, &inv.CreatedAt); err != nil { + return nil, 0, err + } + invoices = append(invoices, inv) + } + return invoices, total, nil +} + func InvoiceGetByID(db *sql.DB, id int) (*Invoice, error) { inv := &Invoice{} err := db.QueryRow( diff --git a/internal/models/ledger.go b/internal/models/ledger.go index fe57b32..6ee7a67 100644 --- a/internal/models/ledger.go +++ b/internal/models/ledger.go @@ -103,6 +103,47 @@ func JournalEntryGetAll(db *sql.DB) ([]JournalEntry, error) { return entries, nil } +func JournalEntryGetPaginated(db *sql.DB, page, limit int) ([]JournalEntry, int, error) { + offset := (page - 1) * limit + + var total int + if err := db.QueryRow("SELECT COUNT(*) FROM journal_entries").Scan(&total); err != nil { + return nil, 0, err + } + + query := ` + SELECT + je.id, + je.entry_date, + je.description, + je.reference, + je.created_at, + COALESCE(SUM(jl.debit), 0), + COALESCE(SUM(jl.credit), 0) + FROM journal_entries je + LEFT JOIN journal_lines jl ON je.id = jl.journal_entry_id + GROUP BY je.id + ORDER BY je.created_at DESC + LIMIT ? OFFSET ? + ` + + rows, err := db.Query(query, limit, offset) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var entries []JournalEntry + for rows.Next() { + var je JournalEntry + if err := rows.Scan(&je.ID, &je.EntryDate, &je.Description, &je.Reference, &je.CreatedAt, &je.TotalDebit, &je.TotalCredit); err != nil { + return nil, 0, err + } + entries = append(entries, je) + } + return entries, total, nil +} + func JournalEntryGetByID(db *sql.DB, id int) (*JournalEntry, error) { je := &JournalEntry{} err := db.QueryRow("SELECT id, entry_date, description, reference, created_at FROM journal_entries WHERE id = ?", id). diff --git a/internal/models/order.go b/internal/models/order.go index 59d38cc..08ea00c 100644 --- a/internal/models/order.go +++ b/internal/models/order.go @@ -55,6 +55,48 @@ func OrderGetAll(db *sql.DB, status string) ([]Order, error) { return orders, nil } +func OrderGetPaginated(db *sql.DB, status string, page, limit int) ([]Order, int, error) { + offset := (page - 1) * limit + + // Base queries + query := `SELECT o.id, o.customer_id, c.name, o.status, o.order_date, o.total_amount, o.notes, o.created_at, o.updated_at + FROM orders o JOIN customers c ON o.customer_id = c.id` + countQuery := "SELECT COUNT(*) FROM orders o" + + args := []interface{}{} + if status != "" { + where := " WHERE o.status = ?" + query += where + countQuery += where + args = append(args, status) + } + + // Get total count + var total int + if err := db.QueryRow(countQuery, args...).Scan(&total); err != nil { + return nil, 0, err + } + + query += " ORDER BY o.created_at DESC LIMIT ? OFFSET ?" + args = append(args, limit, offset) + + rows, err := db.Query(query, args...) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var orders []Order + for rows.Next() { + var o Order + if err := rows.Scan(&o.ID, &o.CustomerID, &o.CustomerName, &o.Status, &o.OrderDate, &o.TotalAmount, &o.Notes, &o.CreatedAt, &o.UpdatedAt); err != nil { + return nil, 0, err + } + orders = append(orders, o) + } + return orders, total, nil +} + func OrderGetByID(db *sql.DB, id int) (*Order, error) { o := &Order{} err := db.QueryRow( diff --git a/templates/customers/list.html b/templates/customers/list.html index 7771e2a..13268d1 100644 --- a/templates/customers/list.html +++ b/templates/customers/list.html @@ -60,4 +60,40 @@ + +
+ +
{{end}} diff --git a/templates/invoices/list.html b/templates/invoices/list.html index 7f5273d..b34599d 100644 --- a/templates/invoices/list.html +++ b/templates/invoices/list.html @@ -64,4 +64,40 @@ + +
+ +
{{end}} diff --git a/templates/ledger/journal_entries.html b/templates/ledger/journal_entries.html index 72b411d..00ba8f5 100644 --- a/templates/ledger/journal_entries.html +++ b/templates/ledger/journal_entries.html @@ -45,5 +45,37 @@ + +
+ +
{{end}} diff --git a/templates/orders/list.html b/templates/orders/list.html index 1382f19..9df9b82 100644 --- a/templates/orders/list.html +++ b/templates/orders/list.html @@ -66,4 +66,40 @@ + +
+ +
{{end}}