diff --git a/apps/web/main.go b/apps/web/main.go index e8eccb1..596b14b 100644 --- a/apps/web/main.go +++ b/apps/web/main.go @@ -27,7 +27,11 @@ func main() { protected.HandleFunc("/", web.DashboardHandler).Methods("GET") protected.HandleFunc("/jobs", web.JobsHandler).Methods("GET") protected.HandleFunc("/invoices", web.InvoicesHandler).Methods("GET", "POST") - protected.HandleFunc("/void-invoice/{id}", web.VoidInvoiceHandler).Methods("PUT") + protected.HandleFunc("/ok-invoice/{id}", web.UpdateInvoiceStatusHandler).Methods("PUT") + protected.HandleFunc("/fail-invoice/{id}", web.UpdateInvoiceStatusHandler).Methods("PUT") + protected.HandleFunc("/pending-invoice/{id}", web.UpdateInvoiceStatusHandler).Methods("PUT") + protected.HandleFunc("/processed-invoice/{id}", web.UpdateInvoiceStatusHandler).Methods("PUT") + protected.HandleFunc("/void-invoice/{id}", web.UpdateInvoiceStatusHandler).Methods("PUT") protected.HandleFunc("/admin", web.AdminHandler).Methods("GET") protected.HandleFunc("/assets", web.AssetsHandler).Methods("GET") protected.HandleFunc("/companies", web.CompaniesHandler).Methods("GET") diff --git a/internal/api/invoices.go b/internal/api/invoices.go index 76eb2c5..439bec0 100644 --- a/internal/api/invoices.go +++ b/internal/api/invoices.go @@ -22,7 +22,7 @@ func (s *Session) GetInvoice(identifier string) (map[string]interface{}, error) return nil, fmt.Errorf("error making request: %v", err) } defer resp.Body.Close() - + fmt.Println(resp.Status) body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("error reading response body: %v", err) @@ -43,7 +43,7 @@ func (s *Session) GetInvoice(identifier string) (map[string]interface{}, error) } // Log the entire response for debugging - fmt.Printf("API Response: %+v\n", result) + // fmt.Printf("API Response: %+v\n", result) if isInvoiceNumber { // Handle invoice number case diff --git a/internal/handlers/web/invoices.go b/internal/handlers/web/invoices.go index e63ac46..26902c2 100644 --- a/internal/handlers/web/invoices.go +++ b/internal/handlers/web/invoices.go @@ -49,12 +49,12 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se invoice, err := session.GetInvoice(searchTerm) - log.Printf("GetInvoice result - invoice: %+v, err: %v", invoice, err) + // log.Printf("GetInvoice result - invoice: %+v, err: %v", invoice, err) if err != nil { log.Printf("Error fetching invoice: %v", err) w.WriteHeader(http.StatusOK) - errorMsg := fmt.Sprintf("Error fetching invoice: %v", err) + errorMsg := fmt.Sprintf("No invoice found for: %s", searchTerm) if strings.Contains(err.Error(), "access forbidden") { errorMsg = "You do not have permission to view this invoice." } @@ -83,7 +83,7 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se invoice["id"] = fmt.Sprintf("%.0f", id) } - log.Printf("Invoice found: %+v", invoice) + // log.Printf("Invoice found: %+v", invoice) tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) err = tmpl.ExecuteTemplate(w, "invoice_search_results", invoice) @@ -93,7 +93,7 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se } } -func VoidInvoiceHandler(w http.ResponseWriter, r *http.Request) { +func UpdateInvoiceStatusHandler(w http.ResponseWriter, r *http.Request) { session, ok := r.Context().Value("session").(*api.Session) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) @@ -105,41 +105,56 @@ func VoidInvoiceHandler(w http.ResponseWriter, r *http.Request) { return } - // Extract the invoice ID from the URL + // Extract the invoice ID and status from the URL parts := strings.Split(r.URL.Path, "/") if len(parts) < 3 { http.Error(w, "Invalid request", http.StatusBadRequest) return } invoiceID := parts[len(parts)-1] + status := parts[len(parts)-2] if invoiceID == "" { http.Error(w, "Invalid invoice ID", http.StatusBadRequest) return } + // Validate the status + validStatuses := map[string]bool{ + "fail": true, + "ok": true, + "pending": true, + "processed": true, + "void": true, + } + + if !validStatuses[status] { + http.Error(w, "Invalid status", http.StatusBadRequest) + return + } + // Prepare the request body - requestBody := map[string]string{"status": "void"} + requestBody := map[string]string{"status": status} jsonBody, err := json.Marshal(requestBody) if err != nil { http.Error(w, "Error preparing request", http.StatusInternalServerError) return } - // Send the PUT request to void the invoice + // Send the PUT request to update the invoice status endpoint := fmt.Sprintf("/invoice/%s", invoiceID) resp, err := session.DoRequest("PUT", endpoint, bytes.NewBuffer(jsonBody)) if err != nil { - log.Printf("Error voiding invoice: %v", err) - http.Error(w, fmt.Sprintf("Error voiding invoice: %v", err), http.StatusInternalServerError) + log.Printf("Error updating invoice status: %v", err) + http.Error(w, fmt.Sprintf("Error updating invoice status: %v", err), http.StatusInternalServerError) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) - log.Printf("Failed to void invoice: %s", body) - http.Error(w, fmt.Sprintf("Failed to void invoice: %s", body), resp.StatusCode) + log.Printf("Failed to update invoice status: %s", body) + http.Error(w, fmt.Sprintf("Failed to update invoice status: %s", body), resp.StatusCode) return } @@ -151,7 +166,7 @@ func VoidInvoiceHandler(w http.ResponseWriter, r *http.Request) { return } - log.Printf("Updated invoice after voiding: %+v", invoice) + log.Printf("Updated invoice after status change to %s: %+v", status, invoice) // Render the updated invoice details tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) diff --git a/static/css/styles.css b/static/css/styles.css index a8412d3..5b6893b 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -1,5 +1,6 @@ /* General reset and body setup */ -body, html { +body, +html { margin: 0; height: 100%; font-family: Arial, sans-serif; @@ -272,7 +273,8 @@ body, html { } /* Style for buttons or links within submenu items */ -.submenu-item a, .submenu-item button { +.submenu-item a, +.submenu-item button { display: inline-block; background-color: #4299e1; color: white; @@ -286,17 +288,23 @@ body, html { cursor: pointer; } -.submenu-item a:hover, .submenu-item button:hover { +.submenu-item a:hover, +.submenu-item button:hover { background-color: #2b6cb0; } /* Style for error and not-found message display */ @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + to { + opacity: 1; + } } -.error, .not-found { +.error, +.not-found { text-align: center; padding: 0.75rem; border-radius: 0.25rem; @@ -317,22 +325,27 @@ body, html { color: #92400e; } -.error::before, .not-found::before { +.error::before, +.not-found::before { margin-right: 0.5rem; } .error::before { - content: '⚠️'; + content: "⚠️"; } .not-found::before { - content: '🔍'; + content: "🔍"; } /* Loading indicator */ @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } .htmx-indicator { @@ -367,16 +380,20 @@ body, html { /* Highlight effect for changed content */ @keyframes highlightBackground { - 0% { background-color: #fef3c7; } - 100% { background-color: transparent; } + 0% { + background-color: #fef3c7; + } + 100% { + background-color: transparent; + } } .highlight-change { animation: highlightBackground 2s ease-in-out; } -/* Void button styles */ -.void-button { +/* You better be sure you wanna do this button styles */ +.warning-button { background-color: #ef4444; color: white; border: none; @@ -388,11 +405,55 @@ body, html { margin-bottom: 1rem; } -.void-button:hover { +.warning-button:hover { background-color: #dc2626; } -.void-button:disabled { +.warning-button:disabled { + background-color: #9ca3af; + cursor: not-allowed; +} + +/* Pretty sure you wanna do this but lets think about it again button styles */ +.caution-button { + background-color: #eab308; + color: #1f2937; + border: none; + padding: 0.5rem 1rem; + border-radius: 0.25rem; + font-size: 0.875rem; + cursor: pointer; + transition: background-color 0.2s; + margin-bottom: 1rem; +} + +.caution-button:hover { + background-color: #ca8a04; +} + +.caution-button:disabled { background-color: #9ca3af; cursor: not-allowed; -} \ No newline at end of file +} + +/* This button is harmless button styles */ +.success-button { + background-color: #22c55e; + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 0.25rem; + font-size: 0.875rem; + cursor: pointer; + transition: background-color 0.2s; + margin-bottom: 1rem; +} + +.success-button:hover { + background-color: #16a34a; +} + +.success-button:disabled { + background-color: #9ca3af; + cursor: not-allowed; +} diff --git a/templates/partials/invoice_search_results.html b/templates/partials/invoice_search_results.html index 4683b78..70fee26 100644 --- a/templates/partials/invoice_search_results.html +++ b/templates/partials/invoice_search_results.html @@ -1,5 +1,5 @@ {{define "invoice_search_results"}} {{if .Error}} -
{{.ErrorMsg}}
Search term: "{{.SearchTerm}}"