diff --git a/internal/api/jobs.go b/internal/api/jobs.go index b64c681..c0cc6dd 100644 --- a/internal/api/jobs.go +++ b/internal/api/jobs.go @@ -4,8 +4,154 @@ import ( "encoding/json" "fmt" "io" + "log" + "net/url" ) +type JobsResponse struct { + Data struct { + Jobs []Job `json:"jobs"` + } `json:"data"` +} + +type Job struct { + ID int64 `json:"id"` + Name string `json:"name"` + CustomName *string `json:"customName"` + Type string `json:"type"` + JobTypeWeight int `json:"jobTypeWeight"` + Status string `json:"status"` + Visibility []string `json:"visibility"` + Number int `json:"number"` + RefNumber string `json:"refNumber"` + Description *string `json:"description"` + ScheduledDate *int64 `json:"scheduledDate"` + CompletedOn *int64 `json:"completedOn"` + ServiceLine string `json:"serviceLine"` + EstimatedPrice *float64 `json:"estimatedPrice"` + Vendor Vendor `json:"vendor"` + Customer Customer `json:"customer"` + Location Location `json:"location"` + Owner Owner `json:"owner"` + Tags []Tag `json:"tags"` + Appointments []Appointment `json:"appointments"` + CurrentAppointment Appointment `json:"currentAppointment"` + AssignedOffice Location `json:"assignedOffice"` + Offices []Location `json:"offices"` + Terms *Term `json:"terms"` + Contract *Contract `json:"contract"` + PrimaryContact *PrimaryContact `json:"primaryContact"` +} + +type Vendor struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Name string `json:"name"` + Status string `json:"status"` +} + +type Customer struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Name string `json:"name"` + Status string `json:"status"` +} + +type Location struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Name string `json:"name"` + RefNumber string `json:"refNumber"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + Address Address `json:"address"` +} + +type Address struct { + Street string `json:"street"` + City string `json:"city"` + State string `json:"state"` + PostalCode string `json:"postalCode"` +} + +func (a Address) String() string { + return fmt.Sprintf("%s, %s, %s %s", a.Street, a.City, a.State, a.PostalCode) +} + +type Owner struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Name string `json:"name"` + Status string `json:"status"` + Email string `json:"email"` +} + +type Tag struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Name string `json:"name"` +} + +type Appointment struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Name string `json:"name"` + Status string `json:"status"` + WindowStart *int64 `json:"windowStart"` + WindowEnd *int64 `json:"windowEnd"` + Techs []Tech `json:"techs"` + Released bool `json:"released"` +} + +type Tech struct { + ID int64 `json:"id"` + Name string `json:"name"` + Email string `json:"email"` +} + +type Term struct { + ID int64 `json:"id"` + Name string `json:"name"` +} + +type Contract struct { + ID int64 `json:"id"` + Name string `json:"name"` +} + +type PrimaryContact struct { + ID int64 `json:"id"` + URI string `json:"uri"` + Email string `json:"email"` +} + +func (s *Session) SearchJobs(filters url.Values) ([]Job, error) { + endpoint := "/job?" + query := filters.Encode() + url := endpoint + query + + resp, err := s.DoRequest("GET", url, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + log.Printf("Raw API Response: %s", string(body)) + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to search jobs: %s, response: %s", resp.Status, string(body)) + } + + var result JobsResponse + if err := json.Unmarshal(body, &result); err != nil { + return nil, fmt.Errorf("error unmarshalling response: %v", err) + } + + log.Printf("Parsed Data: %+v", result.Data) + return result.Data.Jobs, nil +} + func (s *Session) GetAttachmentsForJob(jobID string) (map[string]interface{}, error) { resp, err := s.DoRequest("GET", fmt.Sprintf("/job/%s/paperwork", jobID), nil) if err != nil { diff --git a/internal/handlers/web/jobs.go b/internal/handlers/web/jobs.go index 641dfe4..6003a5d 100644 --- a/internal/handlers/web/jobs.go +++ b/internal/handlers/web/jobs.go @@ -1,26 +1,82 @@ package web import ( - "html/template" + "fmt" + "log" + root "marmic/servicetrade-toolbox" + "marmic/servicetrade-toolbox/internal/api" "net/http" + "net/url" ) 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, - }) + session, ok := r.Context().Value("session").(*api.Session) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return } + + tmpl := root.WebTemplates + data := map[string]interface{}{ + "Title": "Jobs", + } + + switch r.URL.Path { + case "/jobs": + // Fetch and display search results only + jobs, err := handleJobSearch(r, session) + if err != nil { + log.Printf("Error in job search: %v", err) + data["Error"] = true + data["ErrorMsg"] = "Failed to fetch jobs. Please try again later." + } else { + data["Jobs"] = jobs + } + err = tmpl.ExecuteTemplate(w, "job_search_results", data) + if err != nil { + log.Printf("Template execution error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + } + + case "/jobs/documents": + // Fetch jobs and uploaded documents + jobs, err := handleJobSearch(r, session) + if err != nil { + log.Printf("Error in job search: %v", err) + data["Error"] = true + data["ErrorMsg"] = "Failed to fetch jobs. Please try again later." + } else { + data["Jobs"] = jobs + } + + err = tmpl.ExecuteTemplate(w, "job_document_management", data) + if err != nil { + log.Printf("Template execution error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + } + + default: + http.Error(w, "Not Found", http.StatusNotFound) + } +} + +func handleJobSearch(r *http.Request, session *api.Session) ([]api.Job, error) { + queryParams := r.URL.Query() + cleanedParams := url.Values{} + log.Printf("Searching jobs with filters: %v", queryParams) + + for key, values := range queryParams { + for _, value := range values { + if value != "" { + cleanedParams.Add(key, value) + } + } + } + + jobs, err := session.SearchJobs(cleanedParams) + if err != nil { + return nil, fmt.Errorf("error fetching jobs: %w", err) + } + + return jobs, nil }