cmc-sales/go-app/internal/cmc/handlers/pages.go
2025-06-24 20:32:28 +10:00

603 lines
15 KiB
Go

package handlers
import (
"net/http"
"strconv"
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/db"
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/templates"
"github.com/gorilla/mux"
)
type PageHandler struct {
queries *db.Queries
tmpl *templates.TemplateManager
}
func NewPageHandler(queries *db.Queries, tmpl *templates.TemplateManager) *PageHandler {
return &PageHandler{
queries: queries,
tmpl: tmpl,
}
}
// Home page
func (h *PageHandler) Home(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"Title": "Dashboard",
}
if err := h.tmpl.Render(w, "index.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// Customer pages
func (h *PageHandler) CustomersIndex(w http.ResponseWriter, r *http.Request) {
page := 1
if p := r.URL.Query().Get("page"); p != "" {
if val, err := strconv.Atoi(p); err == nil && val > 0 {
page = val
}
}
limit := 20
offset := (page - 1) * limit
customers, err := h.queries.ListCustomers(r.Context(), db.ListCustomersParams{
Limit: int32(limit + 1), // Get one extra to check if there are more
Offset: int32(offset),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
hasMore := len(customers) > limit
if hasMore {
customers = customers[:limit]
}
data := map[string]interface{}{
"Customers": customers,
"Page": page,
"PrevPage": page - 1,
"NextPage": page + 1,
"HasMore": hasMore,
}
// Check if this is an HTMX request
if r.Header.Get("HX-Request") == "true" {
if err := h.tmpl.Render(w, "customers/table.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
if err := h.tmpl.Render(w, "customers/index.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) CustomersNew(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"Customer": db.Customer{},
}
if err := h.tmpl.Render(w, "customers/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) CustomersEdit(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid customer ID", http.StatusBadRequest)
return
}
customer, err := h.queries.GetCustomer(r.Context(), int32(id))
if err != nil {
http.Error(w, "Customer not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"Customer": customer,
}
if err := h.tmpl.Render(w, "customers/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) CustomersShow(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid customer ID", http.StatusBadRequest)
return
}
customer, err := h.queries.GetCustomer(r.Context(), int32(id))
if err != nil {
http.Error(w, "Customer not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"Customer": customer,
}
if err := h.tmpl.Render(w, "customers/show.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) CustomersSearch(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("search")
page := 1
if p := r.URL.Query().Get("page"); p != "" {
if val, err := strconv.Atoi(p); err == nil && val > 0 {
page = val
}
}
limit := 20
offset := (page - 1) * limit
var customers []db.Customer
var err error
if query == "" {
customers, err = h.queries.ListCustomers(r.Context(), db.ListCustomersParams{
Limit: int32(limit + 1),
Offset: int32(offset),
})
} else {
customers, err = h.queries.SearchCustomersByName(r.Context(), db.SearchCustomersByNameParams{
CONCAT: query,
CONCAT_2: query,
Limit: int32(limit + 1),
Offset: int32(offset),
})
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
hasMore := len(customers) > limit
if hasMore {
customers = customers[:limit]
}
data := map[string]interface{}{
"Customers": customers,
"Page": page,
"PrevPage": page - 1,
"NextPage": page + 1,
"HasMore": hasMore,
}
w.Header().Set("Content-Type", "text/html")
if err := h.tmpl.Render(w, "customers/table.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// Product page handlers
func (h *PageHandler) ProductsIndex(w http.ResponseWriter, r *http.Request) {
// Similar implementation to CustomersIndex but for products
data := map[string]interface{}{
"Products": []db.Product{}, // Placeholder
}
if err := h.tmpl.Render(w, "products/index.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) ProductsNew(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"Product": db.Product{},
}
if err := h.tmpl.Render(w, "products/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) ProductsShow(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid product ID", http.StatusBadRequest)
return
}
product, err := h.queries.GetProduct(r.Context(), int32(id))
if err != nil {
http.Error(w, "Product not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"Product": product,
}
if err := h.tmpl.Render(w, "products/show.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) ProductsEdit(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid product ID", http.StatusBadRequest)
return
}
product, err := h.queries.GetProduct(r.Context(), int32(id))
if err != nil {
http.Error(w, "Product not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"Product": product,
}
if err := h.tmpl.Render(w, "products/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) ProductsSearch(w http.ResponseWriter, r *http.Request) {
// Similar to CustomersSearch but for products
data := map[string]interface{}{
"Products": []db.Product{},
}
w.Header().Set("Content-Type", "text/html")
if err := h.tmpl.Render(w, "products/table.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// Purchase Order page handlers
func (h *PageHandler) PurchaseOrdersIndex(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"PurchaseOrders": []db.PurchaseOrder{},
}
if err := h.tmpl.Render(w, "purchase-orders/index.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) PurchaseOrdersNew(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"PurchaseOrder": db.PurchaseOrder{},
}
if err := h.tmpl.Render(w, "purchase-orders/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) PurchaseOrdersShow(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid purchase order ID", http.StatusBadRequest)
return
}
purchaseOrder, err := h.queries.GetPurchaseOrder(r.Context(), int32(id))
if err != nil {
http.Error(w, "Purchase order not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"PurchaseOrder": purchaseOrder,
}
if err := h.tmpl.Render(w, "purchase-orders/show.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) PurchaseOrdersEdit(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid purchase order ID", http.StatusBadRequest)
return
}
purchaseOrder, err := h.queries.GetPurchaseOrder(r.Context(), int32(id))
if err != nil {
http.Error(w, "Purchase order not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"PurchaseOrder": purchaseOrder,
}
if err := h.tmpl.Render(w, "purchase-orders/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) PurchaseOrdersSearch(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"PurchaseOrders": []db.PurchaseOrder{},
}
w.Header().Set("Content-Type", "text/html")
if err := h.tmpl.Render(w, "purchase-orders/table.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// Enquiry page handlers
func (h *PageHandler) EnquiriesIndex(w http.ResponseWriter, r *http.Request) {
page := 1
if p := r.URL.Query().Get("page"); p != "" {
if val, err := strconv.Atoi(p); err == nil && val > 0 {
page = val
}
}
limit := 150
offset := (page - 1) * limit
var enquiries interface{}
var err error
var hasMore bool
// Check if we want archived enquiries
if r.URL.Query().Get("archived") == "true" {
archivedEnquiries, err := h.queries.ListArchivedEnquiries(r.Context(), db.ListArchivedEnquiriesParams{
Limit: int32(limit + 1),
Offset: int32(offset),
})
if err == nil {
hasMore = len(archivedEnquiries) > limit
if hasMore {
archivedEnquiries = archivedEnquiries[:limit]
}
enquiries = archivedEnquiries
}
} else {
activeEnquiries, err := h.queries.ListEnquiries(r.Context(), db.ListEnquiriesParams{
Limit: int32(limit + 1),
Offset: int32(offset),
})
if err == nil {
hasMore = len(activeEnquiries) > limit
if hasMore {
activeEnquiries = activeEnquiries[:limit]
}
enquiries = activeEnquiries
}
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Get status list for dropdown and CSS classes
statuses, err := h.queries.GetAllStatuses(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Enquiries": enquiries,
"Statuses": statuses,
"Page": page,
"PrevPage": page - 1,
"NextPage": page + 1,
"HasMore": hasMore,
}
// Check if this is an HTMX request
if r.Header.Get("HX-Request") == "true" {
if err := h.tmpl.RenderPartial(w, "enquiries/table.html", "enquiry-table", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
if err := h.tmpl.Render(w, "enquiries/index.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) EnquiriesNew(w http.ResponseWriter, r *http.Request) {
// Get required form data
statuses, err := h.queries.GetAllStatuses(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
principles, err := h.queries.GetAllPrinciples(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
states, err := h.queries.GetAllStates(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
countries, err := h.queries.GetAllCountries(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Enquiry": db.Enquiry{},
"Statuses": statuses,
"Principles": principles,
"States": states,
"Countries": countries,
}
if err := h.tmpl.Render(w, "enquiries/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) EnquiriesShow(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid enquiry ID", http.StatusBadRequest)
return
}
enquiry, err := h.queries.GetEnquiry(r.Context(), int32(id))
if err != nil {
http.Error(w, "Enquiry not found", http.StatusNotFound)
return
}
data := map[string]interface{}{
"Enquiry": enquiry,
}
if err := h.tmpl.Render(w, "enquiries/show.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) EnquiriesEdit(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
http.Error(w, "Invalid enquiry ID", http.StatusBadRequest)
return
}
enquiry, err := h.queries.GetEnquiry(r.Context(), int32(id))
if err != nil {
http.Error(w, "Enquiry not found", http.StatusNotFound)
return
}
// Get required form data
statuses, err := h.queries.GetAllStatuses(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
principles, err := h.queries.GetAllPrinciples(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
states, err := h.queries.GetAllStates(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
countries, err := h.queries.GetAllCountries(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Enquiry": enquiry,
"Statuses": statuses,
"Principles": principles,
"States": states,
"Countries": countries,
}
if err := h.tmpl.Render(w, "enquiries/form.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *PageHandler) EnquiriesSearch(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("search")
page := 1
if p := r.URL.Query().Get("page"); p != "" {
if val, err := strconv.Atoi(p); err == nil && val > 0 {
page = val
}
}
limit := 150
offset := (page - 1) * limit
var enquiries interface{}
var hasMore bool
if query == "" {
// If no search query, return regular list
regularEnquiries, err := h.queries.ListEnquiries(r.Context(), db.ListEnquiriesParams{
Limit: int32(limit + 1),
Offset: int32(offset),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
hasMore = len(regularEnquiries) > limit
if hasMore {
regularEnquiries = regularEnquiries[:limit]
}
enquiries = regularEnquiries
} else {
searchResults, err := h.queries.SearchEnquiries(r.Context(), db.SearchEnquiriesParams{
CONCAT: query,
CONCAT_2: query,
CONCAT_3: query,
Limit: int32(limit + 1),
Offset: int32(offset),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
hasMore = len(searchResults) > limit
if hasMore {
searchResults = searchResults[:limit]
}
enquiries = searchResults
}
data := map[string]interface{}{
"Enquiries": enquiries,
"Page": page,
"PrevPage": page - 1,
"NextPage": page + 1,
"HasMore": hasMore,
}
w.Header().Set("Content-Type", "text/html")
if err := h.tmpl.RenderPartial(w, "enquiries/table.html", "enquiry-table", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}