diff --git a/apps/web/main.go b/apps/web/main.go index b83466b..e8eccb1 100644 --- a/apps/web/main.go +++ b/apps/web/main.go @@ -27,6 +27,7 @@ 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("/admin", web.AdminHandler).Methods("GET") protected.HandleFunc("/assets", web.AssetsHandler).Methods("GET") protected.HandleFunc("/companies", web.CompaniesHandler).Methods("GET") diff --git a/internal/api/api.go b/internal/api/api.go index 0722531..65815ce 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -48,6 +48,7 @@ func (s *Session) ValidateSession() error { func (s *Session) DoRequest(method, endpoint string, body io.Reader) (*http.Response, error) { url := fmt.Sprintf("%s%s", BaseURL, endpoint) + fmt.Println(url) req, err := http.NewRequest(method, url, body) if err != nil { return nil, fmt.Errorf("error creating request: %v", err) diff --git a/internal/handlers/web/invoices.go b/internal/handlers/web/invoices.go index 5c105bf..980970c 100644 --- a/internal/handlers/web/invoices.go +++ b/internal/handlers/web/invoices.go @@ -1,8 +1,11 @@ package web import ( + "bytes" + "encoding/json" "fmt" "html/template" + "io/ioutil" "log" "marmic/servicetrade-toolbox/internal/api" "net/http" @@ -76,6 +79,10 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se return } + if id, ok := invoice["id"].(float64); ok { + invoice["id"] = fmt.Sprintf("%.0f", id) + } + log.Printf("Invoice found: %+v", invoice) tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) @@ -85,3 +92,73 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } + +func VoidInvoiceHandler(w http.ResponseWriter, r *http.Request) { + session, ok := r.Context().Value("session").(*api.Session) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + if r.Method != http.MethodPut { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Extract the invoice ID 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] + + if invoiceID == "" { + http.Error(w, "Invalid invoice ID", http.StatusBadRequest) + return + } + + // Prepare the request body + requestBody := map[string]string{"status": "void"} + 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 + 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) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := ioutil.ReadAll(resp.Body) + log.Printf("Failed to void invoice: %s", body) + http.Error(w, fmt.Sprintf("Failed to void invoice: %s", body), resp.StatusCode) + return + } + + // If successful, fetch the updated invoice details + invoice, err := session.GetInvoice(invoiceID) + if err != nil { + log.Printf("Error fetching updated invoice: %v", err) + http.Error(w, fmt.Sprintf("Error fetching updated invoice: %v", err), http.StatusInternalServerError) + return + } + + log.Printf("Updated invoice after voiding: %+v", invoice) + + // Render the updated invoice details + tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) + err = tmpl.ExecuteTemplate(w, "invoice_search_results", invoice) + if err != nil { + log.Printf("Error executing template: %v", err) + http.Error(w, "Error rendering template", http.StatusInternalServerError) + return + } +} diff --git a/static/css/styles.css b/static/css/styles.css index cbbeea1..a8412d3 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -373,4 +373,26 @@ body, html { .highlight-change { animation: highlightBackground 2s ease-in-out; +} + +/* Void button styles */ +.void-button { + background-color: #ef4444; + 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; +} + +.void-button:hover { + background-color: #dc2626; +} + +.void-button:disabled { + background-color: #9ca3af; + cursor: not-allowed; } \ No newline at end of file diff --git a/templates/partials/invoice_search_results.html b/templates/partials/invoice_search_results.html index f0be7dd..bade5bc 100644 --- a/templates/partials/invoice_search_results.html +++ b/templates/partials/invoice_search_results.html @@ -1,15 +1,22 @@ {{define "invoice_search_results"}} {{if .Error}}
{{.ErrorMsg}}
-Invoice ID: "{{.SearchTerm}}"
+Search term: "{{.SearchTerm}}"
{{.ErrorMsg}}
-Invoice Number: "{{.SearchTerm}}"
+Search term: "{{.SearchTerm}}"
Invoice Number: {{.invoiceNumber}}
Total Price: ${{.totalPrice}}
Status: {{.status}}