Browse Source

added more buttons and updated void func to be a more generic update status func

cli-archive
nic 2 years ago
parent
commit
dd1ce16efb
  1. 6
      apps/web/main.go
  2. 4
      internal/api/invoices.go
  3. 39
      internal/handlers/web/invoices.go
  4. 95
      static/css/styles.css
  5. 44
      templates/partials/invoice_search_results.html

6
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")

4
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

39
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"))

95
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;
}
/* 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;
}

44
templates/partials/invoice_search_results.html

@ -1,5 +1,5 @@
{{define "invoice_search_results"}} {{if .Error}}
<div class="error">
<div class="not-found">
<p>{{.ErrorMsg}}</p>
<p>Search term: "{{.SearchTerm}}"</p>
</div>
@ -10,12 +10,50 @@
</div>
{{else if .invoiceNumber}}
<h3>Invoice Details</h3>
{{if ne .status "void"}}
{{if and (or (ne .status "void") (ne .status "processed")) (or (eq .status
"draft") (eq .status "ok") (eq .status "pending_accounting") (eq .status
"failed") )}}
<button
hx-put="/draft-invoice/{{.id}}"
hx-confirm="Are you sure you want to void this invoice?"
hx-target="#invoice-search-results"
class="success-button">
Draft Invoice
</button>
<button
hx-put="/ok-invoice/{{.id}}"
hx-confirm="Are you sure you want to void this invoice?"
hx-target="#invoice-search-results"
class="success-button">
Ok Invoice
</button>
<button
hx-put="/fail-invoice/{{.id}}"
hx-confirm="Are you sure you want to void this invoice?"
hx-target="#invoice-search-results"
class="caution-button">
Fail Invoice
</button>
<button
hx-put="/pending-invoice/{{.id}}"
hx-confirm="Are you sure you want to void this invoice?"
hx-target="#invoice-search-results"
class="caution-button">
Pending Invoice
</button>
{{end}} {{if and (ne .status "void") (ne .status "processed")}}
<button
hx-put="/process-invoice/{{.id}}"
hx-confirm="Are you sure you want to void this invoice?"
hx-target="#invoice-search-results"
class="warning-button">
Process Invoice
</button>
<button
hx-put="/void-invoice/{{.id}}"
hx-confirm="Are you sure you want to void this invoice?"
hx-target="#invoice-search-results"
class="void-button">
class="warning-button">
Void Invoice
</button>
{{end}}

Loading…
Cancel
Save