package templates import ( "encoding/json" "log" "net/http" "time" "rideaware/internal/config" "rideaware/internal/middleware" "rideaware/internal/workout" ) type Handler struct { workoutRepo *workout.Repository } func NewHandler() *Handler { return &Handler{ workoutRepo: workout.NewRepository(), } } // TemplateSummary is a lighter view of a template for listing. type TemplateSummary struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Type string `json:"type"` Duration int `json:"duration"` Difficulty string `json:"difficulty"` Category string `json:"category"` Segments int `json:"segments"` } // ListTemplates GET /api/protected/workout-templates func (h *Handler) ListTemplates(w http.ResponseWriter, r *http.Request) { category := r.URL.Query().Get("category") var source []Template if category != "" { source = GetByCategory(category) } else { source = All } summaries := make([]TemplateSummary, 0, len(source)) for _, t := range source { summaries = append(summaries, TemplateSummary{ ID: t.ID, Name: t.Name, Description: t.Description, Type: t.Type, Duration: t.Duration, Difficulty: t.Difficulty, Category: t.Category, Segments: len(t.Segments), }) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(summaries) } // GetTemplate GET /api/protected/workout-templates/detail?id=X func (h *Handler) GetTemplate(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("id") if id == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "template id is required"}) return } t := GetByID(id) if t == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(map[string]string{"error": "template not found"}) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(t) } // CreateFromTemplate POST /api/protected/workouts/from-template func (h *Handler) CreateFromTemplate(w http.ResponseWriter, r *http.Request) { claims := r.Context().Value(middleware.UserContextKey).(*config.CustomClaims) if claims == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"}) return } var req struct { TemplateID string `json:"template_id"` ScheduledDate string `json:"scheduled_date"` Title string `json:"title"` Notes string `json:"notes"` EquipmentID *uint `json:"equipment_id"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "invalid request body"}) return } if req.TemplateID == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "template_id is required"}) return } tmpl := GetByID(req.TemplateID) if tmpl == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(map[string]string{"error": "template not found"}) return } scheduledDate := time.Now() if req.ScheduledDate != "" { parsed, err := time.Parse("2006-01-02", req.ScheduledDate) if err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "invalid scheduled_date format, use YYYY-MM-DD"}) return } scheduledDate = parsed } title := req.Title if title == "" { title = tmpl.Name } newWorkout := &workout.Workout{ UserID: claims.UserID, Title: title, Description: tmpl.Description, Type: tmpl.Type, Status: "planned", ScheduledDate: scheduledDate, Duration: tmpl.Duration, EquipmentID: req.EquipmentID, Notes: req.Notes, WorkoutData: workout.WorkoutDataJSON{ Name: tmpl.Name, Author: "RideAware", TotalDuration: tmpl.Duration, Segments: tmpl.Segments, }, } if err := h.workoutRepo.CreateWorkout(newWorkout); err != nil { log.Printf("Create from template error: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": "failed to create workout"}) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newWorkout) }