package api import ( "encoding/json" "net/http" "github.com/google/uuid" "github.com/thepeterstone/claudomator/internal/storage" "github.com/thepeterstone/claudomator/internal/story" ) func (s *Server) handleListEpics(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") epics, err := s.store.ListEpics(status) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } if epics == nil { epics = []*story.Epic{} } writeJSON(w, http.StatusOK, epics) } func (s *Server) handleCreateEpic(w http.ResponseWriter, r *http.Request) { var e story.Epic if err := json.NewDecoder(r.Body).Decode(&e); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()}) return } if e.Name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name is required"}) return } if e.ID == "" { e.ID = uuid.New().String() } if err := s.store.CreateEpic(&e); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusCreated, e) } func (s *Server) handleGetEpic(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") e, err := s.store.GetEpic(id) if err != nil { writeJSON(w, http.StatusNotFound, map[string]string{"error": "epic not found"}) return } writeJSON(w, http.StatusOK, e) } func (s *Server) handleUpdateEpic(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") existing, err := s.store.GetEpic(id) if err != nil { writeJSON(w, http.StatusNotFound, map[string]string{"error": "epic not found"}) return } createdAt := existing.CreatedAt if err := json.NewDecoder(r.Body).Decode(existing); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()}) return } existing.ID = id // ensure ID cannot be changed via body existing.CreatedAt = createdAt if err := s.store.UpdateEpic(existing); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, existing) } // handleListEpicStories lists stories whose epic_id matches the path {id}. // Mirrors handleListSubtasks: no existence check on the epic itself, just // returns an empty list if there are no matching stories. func (s *Server) handleListEpicStories(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") stories, err := s.store.ListStories(storage.StoryFilter{EpicID: id}) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } if stories == nil { stories = []*story.Story{} } writeJSON(w, http.StatusOK, stories) }