42 changed files with 367 additions and 493 deletions
@ -1,72 +0,0 @@ |
|||
package api |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"marmic/servicetrade-toolbox/internal/auth" |
|||
"strings" |
|||
) |
|||
|
|||
func GetAttachmentsForJob(session *auth.Session, jobID string) (map[string]interface{}, error) { |
|||
url := fmt.Sprintf("%s/job/%s/paperwork", BaseURL, jobID) |
|||
req, err := AuthenticatedRequest(session, "GET", url, nil) |
|||
if err != nil { |
|||
return nil, fmt.Errorf("error creating request: %v", err) |
|||
} |
|||
|
|||
resp, err := DoAuthenticatedRequest(session, req) |
|||
if err != nil { |
|||
return nil, fmt.Errorf("error sending request: %v", err) |
|||
} |
|||
defer resp.Body.Close() |
|||
|
|||
var result map[string]interface{} |
|||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { |
|||
return nil, fmt.Errorf("error decoding response: %v", err) |
|||
} |
|||
|
|||
return result, nil |
|||
} |
|||
|
|||
func GenerateDeleteEndpoints(data map[string]interface{}, filenames []string) []string { |
|||
var endpoints []string |
|||
filenamesToDeleteMap := make(map[string]struct{}) |
|||
for _, name := range filenames { |
|||
filenamesToDeleteMap[strings.ToLower(strings.TrimSpace(name))] = struct{}{} |
|||
} |
|||
|
|||
if dataMap, ok := data["data"].(map[string]interface{}); ok { |
|||
if attachments, ok := dataMap["attachments"].([]interface{}); ok { |
|||
for _, item := range attachments { |
|||
attachment := item.(map[string]interface{}) |
|||
if filename, ok := attachment["fileName"].(string); ok { |
|||
trimmedFilename := strings.ToLower(strings.TrimSpace(filename)) |
|||
if _, exists := filenamesToDeleteMap[trimmedFilename]; exists { |
|||
endpoints = append(endpoints, fmt.Sprintf("%s/attachment/%d", BaseURL, int64(attachment["id"].(float64)))) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return endpoints |
|||
} |
|||
|
|||
func DeleteAttachment(session *auth.Session, endpoint string) error { |
|||
req, err := AuthenticatedRequest(session, "DELETE", endpoint, nil) |
|||
if err != nil { |
|||
return fmt.Errorf("failed to create DELETE request: %v", err) |
|||
} |
|||
|
|||
resp, err := DoAuthenticatedRequest(session, req) |
|||
if err != nil { |
|||
return fmt.Errorf("failed to send DELETE request: %v", err) |
|||
} |
|||
defer resp.Body.Close() |
|||
|
|||
if resp.StatusCode != 200 && resp.StatusCode != 204 { |
|||
return fmt.Errorf("failed to delete attachment: %s", resp.Status) |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
package api |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
) |
|||
|
|||
const BaseURL = "https://api.servicetrade.com/api" |
|||
|
|||
// DecodeJSONResponse decodes a JSON response into the provided interface
|
|||
func DecodeJSONResponse(resp *http.Response, v interface{}) error { |
|||
defer resp.Body.Close() |
|||
|
|||
if resp.StatusCode != http.StatusOK { |
|||
return fmt.Errorf("API request failed with status code: %d", resp.StatusCode) |
|||
} |
|||
|
|||
return json.NewDecoder(resp.Body).Decode(v) |
|||
} |
|||
@ -1,46 +0,0 @@ |
|||
package api |
|||
|
|||
import ( |
|||
"sync" |
|||
"time" |
|||
) |
|||
|
|||
type SessionStore struct { |
|||
sessions map[string]*Session |
|||
mu sync.RWMutex |
|||
} |
|||
|
|||
func NewSessionStore() *SessionStore { |
|||
return &SessionStore{ |
|||
sessions: make(map[string]*Session), |
|||
} |
|||
} |
|||
|
|||
func (store *SessionStore) Set(sessionID string, session *Session) { |
|||
store.mu.Lock() |
|||
defer store.mu.Unlock() |
|||
store.sessions[sessionID] = session |
|||
} |
|||
|
|||
func (store *SessionStore) Get(sessionID string) (*Session, bool) { |
|||
store.mu.RLock() |
|||
defer store.mu.RUnlock() |
|||
session, ok := store.sessions[sessionID] |
|||
return session, ok |
|||
} |
|||
|
|||
func (store *SessionStore) Delete(sessionID string) { |
|||
store.mu.Lock() |
|||
defer store.mu.Unlock() |
|||
delete(store.sessions, sessionID) |
|||
} |
|||
|
|||
func (store *SessionStore) CleanupSessions() { |
|||
store.mu.Lock() |
|||
defer store.mu.Unlock() |
|||
for id, session := range store.sessions { |
|||
if time.Since(session.LastAccessed) > 24*time.Hour { |
|||
delete(store.sessions, id) |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func AdminHandler(w http.ResponseWriter, r *http.Request) { |
|||
if r.Header.Get("HX-Request") == "true" { |
|||
// This is an HTMX request, return only the jobs partial
|
|||
tmpl := template.Must(template.ParseFiles("templates/partials/jobs.html")) |
|||
jobs := r.Cookies() // Replace with actual data fetching
|
|||
tmpl.Execute(w, jobs) |
|||
} else { |
|||
// This is a full page request
|
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/admin.html")) |
|||
jobs := []string{"Job 1", "Job 2", "Job 3"} // Replace with actual data fetching
|
|||
|
|||
tmpl.Execute(w, map[string]interface{}{ |
|||
"Title": "Jobs", |
|||
"Jobs": jobs, |
|||
}) |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func AssetsHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/assets.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func CompaniesHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/companies.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func ContactsHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/contacts.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func ContractsHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/contracts.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"log" |
|||
"net/http" |
|||
) |
|||
|
|||
func DashboardHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl, err := template.ParseFiles( |
|||
"templates/layout.html", |
|||
"templates/dashboard.html", |
|||
"templates/partials/invoice_search.html", |
|||
"templates/partials/invoice_search_results.html", |
|||
) |
|||
if err != nil { |
|||
log.Printf("Template parsing error: %v", err) |
|||
http.Error(w, "Internal Server Error", http.StatusInternalServerError) |
|||
return |
|||
} |
|||
|
|||
data := struct{}{} // Empty struct as data
|
|||
|
|||
if r.Header.Get("HX-Request") == "true" { |
|||
err = tmpl.ExecuteTemplate(w, "content", data) |
|||
} else { |
|||
err = tmpl.ExecuteTemplate(w, "layout.html", data) |
|||
} |
|||
if err != nil { |
|||
log.Printf("Template execution error: %v", err) |
|||
http.Error(w, "Internal Server Error", http.StatusInternalServerError) |
|||
return |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func GenericHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/generic.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"log" |
|||
"marmic/servicetrade-toolbox/internal/api" |
|||
"net/http" |
|||
"strings" |
|||
) |
|||
|
|||
func InvoicesHandler(w http.ResponseWriter, r *http.Request) { |
|||
session, ok := r.Context().Value("session").(*api.Session) |
|||
if !ok { |
|||
http.Error(w, "Unauthorized", http.StatusUnauthorized) |
|||
return |
|||
} |
|||
|
|||
// Handle the search request
|
|||
if r.Method == "GET" && r.URL.Query().Get("search") != "" { |
|||
handleInvoiceSearch(w, r, session) |
|||
return |
|||
} |
|||
|
|||
// Handle the initial page load
|
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/invoices.html")) |
|||
err := tmpl.Execute(w, nil) |
|||
if err != nil { |
|||
log.Printf("Error executing template: %v", err) |
|||
http.Error(w, "Internal Server Error", http.StatusInternalServerError) |
|||
} |
|||
} |
|||
|
|||
func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Session) { |
|||
searchTerm := strings.TrimSpace(r.URL.Query().Get("search")) |
|||
|
|||
if searchTerm == "" { |
|||
log.Println("Empty search term, returning empty response") |
|||
w.WriteHeader(http.StatusOK) |
|||
return |
|||
} |
|||
|
|||
invoice, err := session.GetInvoice(searchTerm) |
|||
if err != nil { |
|||
log.Printf("Error fetching invoice: %v", err) |
|||
w.WriteHeader(http.StatusInternalServerError) |
|||
tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) |
|||
tmpl.ExecuteTemplate(w, "invoice_search_results", map[string]interface{}{"Error": err.Error()}) |
|||
return |
|||
} |
|||
|
|||
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, "Internal Server Error", http.StatusInternalServerError) |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func JobsHandler(w http.ResponseWriter, r *http.Request) { |
|||
jobs := []string{"Job 1", "Job 2", "Job 3"} // Replace with actual data fetching
|
|||
|
|||
if r.Header.Get("HX-Request") == "true" { |
|||
// This is an HTMX request, return the jobs content wrapped in the content template
|
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/jobs.html")) |
|||
tmpl.ExecuteTemplate(w, "content", map[string]interface{}{ |
|||
"Title": "Jobs", |
|||
"Jobs": jobs, |
|||
}) |
|||
} else { |
|||
// This is a full page request
|
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/jobs.html")) |
|||
tmpl.Execute(w, map[string]interface{}{ |
|||
"Title": "Jobs", |
|||
"Jobs": jobs, |
|||
}) |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func LocationsHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/locations.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"log" |
|||
"marmic/servicetrade-toolbox/internal/api" |
|||
"marmic/servicetrade-toolbox/internal/middleware" |
|||
"net/http" |
|||
"strings" |
|||
) |
|||
|
|||
func LoginHandler(w http.ResponseWriter, r *http.Request) { |
|||
if r.Method == "GET" { |
|||
tmpl := template.Must(template.ParseFiles("templates/login.html")) |
|||
tmpl.Execute(w, nil) |
|||
return |
|||
} |
|||
|
|||
if r.Method == "POST" { |
|||
email := r.FormValue("email") |
|||
password := r.FormValue("password") |
|||
|
|||
session := api.NewSession() |
|||
err := session.Login(email, password) |
|||
if err != nil { |
|||
if r.Header.Get("HX-Request") == "true" { |
|||
w.Write([]byte("<div class='error'>Login failed: " + err.Error() + "</div>")) |
|||
} else { |
|||
http.Error(w, "Login failed", http.StatusUnauthorized) |
|||
} |
|||
return |
|||
} |
|||
|
|||
cookieParts := strings.Split(session.Cookie, ";") |
|||
sessionID := strings.TrimPrefix(cookieParts[0], "PHPSESSID=") |
|||
|
|||
middleware.SessionStore.Set(sessionID, session) |
|||
|
|||
http.SetCookie(w, &http.Cookie{ |
|||
Name: "PHPSESSID", |
|||
Value: sessionID, |
|||
Path: "/", |
|||
HttpOnly: true, |
|||
Secure: r.TLS != nil, |
|||
SameSite: http.SameSiteLaxMode, |
|||
}) |
|||
|
|||
if r.Header.Get("HX-Request") == "true" { |
|||
w.Header().Set("HX-Redirect", "/") |
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte("Login successful")) |
|||
} else { |
|||
http.Redirect(w, r, "/", http.StatusSeeOther) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func LogoutHandler(w http.ResponseWriter, r *http.Request) { |
|||
cookie, err := r.Cookie("PHPSESSID") |
|||
if err != nil { |
|||
log.Printf("No session cookie found: %v", err) |
|||
redirectToLogin(w, r) |
|||
return |
|||
} |
|||
|
|||
sessionID := cookie.Value |
|||
session, exists := middleware.SessionStore.Get(sessionID) |
|||
if !exists { |
|||
log.Println("No session found in store") |
|||
redirectToLogin(w, r) |
|||
return |
|||
} |
|||
|
|||
err = session.Logout() |
|||
if err != nil { |
|||
log.Printf("Logout failed: %v", err) |
|||
http.Error(w, "Logout failed", http.StatusInternalServerError) |
|||
return |
|||
} |
|||
|
|||
middleware.SessionStore.Delete(sessionID) |
|||
|
|||
http.SetCookie(w, &http.Cookie{ |
|||
Name: "PHPSESSID", |
|||
Value: "", |
|||
Path: "/", |
|||
MaxAge: -1, |
|||
HttpOnly: true, |
|||
Secure: r.TLS != nil, |
|||
SameSite: http.SameSiteLaxMode, |
|||
}) |
|||
|
|||
log.Println("Logout successful, redirecting to login page") |
|||
redirectToLogin(w, r) |
|||
} |
|||
|
|||
func redirectToLogin(w http.ResponseWriter, r *http.Request) { |
|||
if r.Header.Get("HX-Request") != "" { |
|||
w.Header().Set("HX-Redirect", "/login") |
|||
w.WriteHeader(http.StatusOK) |
|||
} else { |
|||
http.Redirect(w, r, "/login", http.StatusSeeOther) |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func NotificationsHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/notifications.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func QuotesHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/quotes.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func ServicesHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/services.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func TagsHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/tags.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package handlers |
|||
|
|||
import ( |
|||
"html/template" |
|||
"net/http" |
|||
) |
|||
|
|||
func UsersHandler(w http.ResponseWriter, r *http.Request) { |
|||
tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/users.html")) |
|||
tmpl.Execute(w, nil) |
|||
} |
|||
@ -1,189 +0,0 @@ |
|||
package menu |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"marmic/servicetrade-toolbox/internal/api" |
|||
"marmic/servicetrade-toolbox/internal/ui" |
|||
) |
|||
|
|||
func HandleJobs(session *api.Session) { |
|||
jobsMenu := getJobsMenu() |
|||
for { |
|||
choice := DisplayMenuAndGetChoice(jobsMenu, "Jobs Menu") |
|||
if choice == len(jobsMenu)+1 { |
|||
return // Go back to main menu
|
|||
} |
|||
jobsMenu[choice-1].Handler(session) |
|||
} |
|||
} |
|||
|
|||
func getJobsMenu() []MenuItem { |
|||
return []MenuItem{ |
|||
{"Search Job by ID", searchJobByID, nil}, |
|||
{"List Recent Jobs", listRecentJobs, nil}, |
|||
{"Create New Job", createNewJob, nil}, |
|||
{"Manage Job Attachments", manageJobAttachments, nil}, |
|||
{"View Deficiencies", viewDeficiencyById, nil}, |
|||
} |
|||
} |
|||
|
|||
func searchJobByID(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("We will search a job ID here.") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func listRecentJobs(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Listing recent jobs...") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func createNewJob(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Creating a new job...") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func manageJobAttachments(session *api.Session) { |
|||
ui.ClearScreen() |
|||
jobID := ui.PromptForInput("Enter Job ID: ") |
|||
|
|||
attachments, err := session.GetAttachmentsForJob(jobID) |
|||
if err != nil { |
|||
ui.DisplayError("Failed to retrieve attachments:", err) |
|||
return |
|||
} |
|||
|
|||
// Display attachments
|
|||
ui.DisplayMessage("Attachments for Job " + jobID + ":") |
|||
if dataMap, ok := attachments["data"].(map[string]interface{}); ok { |
|||
if attachmentsList, ok := dataMap["attachments"].([]interface{}); ok { |
|||
for i, attachment := range attachmentsList { |
|||
if att, ok := attachment.(map[string]interface{}); ok { |
|||
fmt.Printf("%d. %s\n", i+1, att["fileName"]) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Prompt for attachments to delete
|
|||
toDelete := ui.PromptForInput("Enter the numbers of attachments to delete (comma-separated), or 'all' for all: ") |
|||
|
|||
var filesToDelete []string |
|||
if toDelete == "all" { |
|||
if dataMap, ok := attachments["data"].(map[string]interface{}); ok { |
|||
if attachmentsList, ok := dataMap["attachments"].([]interface{}); ok { |
|||
for _, attachment := range attachmentsList { |
|||
if att, ok := attachment.(map[string]interface{}); ok { |
|||
filesToDelete = append(filesToDelete, att["fileName"].(string)) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
// Parse the input and get the corresponding filenames
|
|||
// This part needs to be implemented
|
|||
} |
|||
|
|||
// Generate delete endpoints
|
|||
endpoints := generateDeleteEndpoints(attachments, filesToDelete) |
|||
|
|||
// Confirm deletion
|
|||
ui.DisplayMessage(fmt.Sprintf("You are about to delete %d attachments. Are you sure? (y/n)", len(endpoints))) |
|||
confirm := ui.PromptForInput("") |
|||
if confirm != "y" { |
|||
ui.DisplayMessage("Deletion cancelled.") |
|||
return |
|||
} |
|||
|
|||
// Perform deletion
|
|||
for _, endpoint := range endpoints { |
|||
err := session.DeleteAttachment(endpoint) |
|||
if err != nil { |
|||
ui.DisplayError(fmt.Sprintf("Failed to delete attachment %s:", endpoint), err) |
|||
} else { |
|||
ui.DisplayMessage(fmt.Sprintf("Successfully deleted attachment: %s", endpoint)) |
|||
} |
|||
} |
|||
|
|||
ui.DisplayMessage("Attachment management completed.") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func viewDeficiencyById(session *api.Session) { |
|||
ui.ClearScreen() |
|||
deficiencyId := ui.PromptForInput("Enter Deficiency ID: ") |
|||
|
|||
ui.DisplayMessage(fmt.Sprintf("Fetching information for Deficiency %s...", deficiencyId)) |
|||
|
|||
result, err := session.GetDeficiencyById(deficiencyId) |
|||
if err != nil { |
|||
ui.DisplayError("Failed to retrieve deficiency information:", err) |
|||
ui.PressEnterToContinue() |
|||
return |
|||
} |
|||
|
|||
ui.ClearScreen() |
|||
ui.DisplayMessage(fmt.Sprintf("Information for Deficiency %s:", deficiencyId)) |
|||
|
|||
if data, ok := result["data"].(map[string]interface{}); ok { |
|||
if len(data) == 0 { |
|||
ui.DisplayMessage("No information found for this deficiency.") |
|||
} else { |
|||
fmt.Println("Deficiency Details:") |
|||
for key, value := range data { |
|||
fmt.Printf("- %s:\n", key) |
|||
if details, ok := value.(map[string]interface{}); ok { |
|||
for detailKey, detailValue := range details { |
|||
if detailValue != nil { |
|||
// Handle potential json.Number values
|
|||
if num, ok := detailValue.(json.Number); ok { |
|||
fmt.Printf(" %s: %s\n", detailKey, num.String()) |
|||
} else { |
|||
fmt.Printf(" %s: %v\n", detailKey, detailValue) |
|||
} |
|||
} |
|||
} |
|||
} else if num, ok := value.(json.Number); ok { |
|||
fmt.Printf(" %s\n", num.String()) |
|||
} else { |
|||
fmt.Printf(" %v\n", value) |
|||
} |
|||
fmt.Println() |
|||
} |
|||
} |
|||
} else { |
|||
ui.DisplayMessage("Unexpected data structure in the API response.") |
|||
fmt.Printf("Response structure: %+v\n", result) |
|||
} |
|||
|
|||
ui.DisplayMessage("\nDeficiency information viewing completed.") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
// Helper function to generate delete endpoints
|
|||
func generateDeleteEndpoints(data map[string]interface{}, filenames []string) []string { |
|||
var endpoints []string |
|||
|
|||
filenamesToDeleteMap := make(map[string]struct{}) |
|||
for _, name := range filenames { |
|||
filenamesToDeleteMap[name] = struct{}{} |
|||
} |
|||
|
|||
if dataMap, ok := data["data"].(map[string]interface{}); ok { |
|||
if attachments, ok := dataMap["attachments"].([]interface{}); ok { |
|||
for _, item := range attachments { |
|||
attachment := item.(map[string]interface{}) |
|||
if filename, ok := attachment["fileName"].(string); ok { |
|||
if _, exists := filenamesToDeleteMap[filename]; exists { |
|||
endpoints = append(endpoints, fmt.Sprintf("https://api.servicetrade.com/api/attachment/%d", int64(attachment["id"].(float64)))) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return endpoints |
|||
} |
|||
@ -1,129 +0,0 @@ |
|||
package menu |
|||
|
|||
import ( |
|||
"marmic/servicetrade-toolbox/internal/api" |
|||
"marmic/servicetrade-toolbox/internal/ui" |
|||
) |
|||
|
|||
type MenuItem struct { |
|||
Name string |
|||
Handler func(*api.Session) |
|||
Submenu []MenuItem |
|||
} |
|||
|
|||
func GetMainMenu() []MenuItem { |
|||
return []MenuItem{ |
|||
{"Administration", HandleAdministration, nil}, |
|||
{"Assets", HandleAssets, nil}, |
|||
{"Companies", HandleCompanies, nil}, |
|||
{"Contacts", HandleContacts, nil}, |
|||
{"Contracts", HandleContracts, nil}, |
|||
{"Generic Tools", HandleGenericTools, nil}, |
|||
{"Invoices", HandleInvoices, nil}, |
|||
{"Jobs", HandleJobs, nil}, |
|||
{"Locations", HandleLocations, nil}, |
|||
{"Notifications", HandleNotifications, nil}, |
|||
{"Quotes", HandleQuotes, nil}, |
|||
{"Services", HandleServices, nil}, |
|||
{"Tags", HandleTags, nil}, |
|||
{"Users", HandleUsers, nil}, |
|||
} |
|||
} |
|||
|
|||
func DisplayMenuAndGetChoice(menuItems []MenuItem, title string) int { |
|||
for { |
|||
displayMenuItems := make([]string, len(menuItems)+1) |
|||
for i, item := range menuItems { |
|||
displayMenuItems[i] = item.Name |
|||
} |
|||
displayMenuItems[len(menuItems)] = "Back/Exit" |
|||
|
|||
ui.DisplayMenu(displayMenuItems, title) |
|||
|
|||
choice, err := ui.GetUserChoice(len(displayMenuItems)) |
|||
if err == nil { |
|||
return choice |
|||
} |
|||
ui.DisplayMessage("Invalid input. Please try again.") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
} |
|||
|
|||
// Placeholder functions for other main menu items
|
|||
func HandleAdministration(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Administration menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleAssets(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Assets menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleCompanies(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Companies menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleContacts(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Contacts menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleContracts(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Contracts menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleGenericTools(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Generic Tools menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleInvoices(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Invoices menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleLocations(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Locations menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleNotifications(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Notifications menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleQuotes(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Quotes menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleServices(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Services menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleTags(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Tags menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
|
|||
func HandleUsers(session *api.Session) { |
|||
ui.ClearScreen() |
|||
ui.DisplayMessage("Users menu placeholder") |
|||
ui.PressEnterToContinue() |
|||
} |
|||
@ -1,36 +0,0 @@ |
|||
package middleware |
|||
|
|||
import ( |
|||
"context" |
|||
"marmic/servicetrade-toolbox/internal/api" |
|||
"net/http" |
|||
) |
|||
|
|||
var SessionStore = api.NewSessionStore() |
|||
|
|||
func AuthMiddleware(next http.Handler) http.Handler { |
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|||
cookie, err := r.Cookie("PHPSESSID") |
|||
if err != nil { |
|||
http.Redirect(w, r, "/login", http.StatusSeeOther) |
|||
return |
|||
} |
|||
|
|||
sessionID := cookie.Value |
|||
session, exists := SessionStore.Get(sessionID) |
|||
if !exists { |
|||
session = api.NewSession() |
|||
session.Cookie = "PHPSESSID=" + sessionID |
|||
|
|||
if err := session.ValidateSession(); err != nil { |
|||
http.Redirect(w, r, "/login", http.StatusSeeOther) |
|||
return |
|||
} |
|||
|
|||
SessionStore.Set(sessionID, session) |
|||
} |
|||
|
|||
ctx := context.WithValue(r.Context(), "session", session) |
|||
next.ServeHTTP(w, r.WithContext(ctx)) |
|||
}) |
|||
} |
|||
Loading…
Reference in new issue