From 31a1f9dc57f6b124b849d1fd622d029badc80a7e Mon Sep 17 00:00:00 2001 From: ColinJakob <colin.jakob@student.reutlingen-university.de> Date: Thu, 19 Jun 2025 19:32:26 +0200 Subject: [PATCH] add controllers for displaying and handling requirement --- src/app/project/web/web.go | 141 +++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/app/project/web/web.go b/src/app/project/web/web.go index 5ee8347..c3b46f9 100644 --- a/src/app/project/web/web.go +++ b/src/app/project/web/web.go @@ -36,6 +36,15 @@ type ProjectDetailData struct { RequirementStatuses []string // Available status options for requirements } +// RequirementCloneFormData is passed to the requirement clone modal +type RequirementCloneFormData struct { + Name string `hvalidate:"required"` + ProjectID string + Requirement *project.RequirementSummary + Projects []*project.Project + Cloned bool +} + // RegisterController sets up all HTTP routes and handlers for project management // This function is called during application startup to configure the web routing func RegisterController(appCtx *hctx.AppCtx, webCtx *web.Ctx, mongoManager *project.MongoManager) { @@ -87,6 +96,12 @@ func RegisterController(appCtx *hctx.AppCtx, webCtx *web.Ctx, mongoManager *proj // POST /project/search - Handle project search functionality router.Post("/project/search", projectSearchController(appCtx, webCtx, projectService).ServeHTTP) + // GET /requirement/{id}/clone/modal - Show requirement clone modal + router.Get("/requirement/{id}/clone/modal", requirementCloneModalController(appCtx, webCtx, projectService).ServeHTTP) + + // POST /requirement/{id}/clone - Process requirement cloning + router.Post("/requirement/{id}/clone", requirementCloneController(appCtx, webCtx, projectService).ServeHTTP) + log.Println("INFO: Project routes registered successfully") } @@ -531,3 +546,129 @@ func renderProjectEditForm(io web.IO, toUpdate *project.ProjectToUpdate, success return err } + +// requirementCloneModalController displays the modal for cloning a requirement +func requirementCloneModalController(appCtx *hctx.AppCtx, webCtx *web.Ctx, service *project.ProjectService) http.Handler { + return web.NewController(appCtx, webCtx, func(io web.IO) error { + ctx := io.Context() + usr := user.MustCtxUser(ctx) + + // Parse requirement ID from URL + reqID, err := parseObjectID(io.Request(), "id") + if err != nil { + return io.InlineError(web.ErrInternal, err) + } + + // Find the requirement by searching through all projects + var targetReq *project.RequirementSummary + projects, err := service.GetProjectsByUser(ctx, usr.ID) + if err != nil { + return io.InlineError(web.ErrInternal, err) + } + + for _, proj := range projects { + projWithReqs, err := service.GetProjectWithRequirements(ctx, proj.ID) + if err != nil { + continue + } + for _, req := range projWithReqs.Requirements { + if req.ID == reqID { + targetReq = req + break + } + } + if targetReq != nil { + break + } + } + + if targetReq == nil { + return io.InlineError(web.ErrInternal, errors.New("requirement not found")) + } + + return io.Render(web.NewFormData(RequirementCloneFormData{ + Requirement: targetReq, + Projects: projects, + }, nil), "requirement.clone.modal", "project/_modal-clone-requirement.go.html") + }) +} + +// requirementCloneController processes the requirement cloning form submission +func requirementCloneController(appCtx *hctx.AppCtx, webCtx *web.Ctx, service *project.ProjectService) http.Handler { + return web.NewController(appCtx, webCtx, func(io web.IO) error { + ctx := io.Context() + usr := user.MustCtxUser(ctx) + + log.Printf("INFO: Starting requirement clone process for user %s", usr.ID) + + // Parse requirement ID from URL + reqID, err := parseObjectID(io.Request(), "id") + if err != nil { + log.Printf("ERROR: Failed to parse requirement ID: %v", err) + return io.InlineError(web.ErrInternal, err) + } + + log.Printf("INFO: Cloning requirement with ID %s", reqID.Hex()) + + // Get projects for form data + projects, err := service.GetProjectsByUser(ctx, usr.ID) + if err != nil { + return io.InlineError(web.ErrInternal, err) + } + + // Find the source requirement + var sourceReq *project.RequirementSummary + for _, proj := range projects { + projWithReqs, err := service.GetProjectWithRequirements(ctx, proj.ID) + if err != nil { + continue + } + for _, req := range projWithReqs.Requirements { + if req.ID == reqID { + sourceReq = req + break + } + } + if sourceReq != nil { + break + } + } + + if sourceReq == nil { + log.Printf("ERROR: Source requirement %s not found", reqID.Hex()) + return io.InlineError(web.ErrInternal, errors.New("source requirement not found")) + } + + log.Printf("INFO: Found source requirement %s", sourceReq.RequirementID) + + formData := &RequirementCloneFormData{Requirement: sourceReq, Projects: projects} + err, validationErrs := web.ReadForm(io.Request(), formData, appCtx.Validator) + if err != nil { + log.Printf("ERROR: Failed to read form: %v", err) + return io.InlineError(web.ErrInternal, err) + } + + log.Printf("INFO: Form data - ProjectID: %s, Name: %s", formData.ProjectID, formData.Name) + + if validationErrs != nil { + log.Printf("WARN: Validation errors: %v", validationErrs) + return io.Render(web.NewFormData(formData, nil, validationErrs...), "requirement.clone.modal", "project/_modal-clone-requirement.go.html") + } + + // Clone the requirement + log.Printf("INFO: Calling service.CloneRequirement with ProjectID: %s, Name: %s", formData.ProjectID, formData.Name) + err = service.CloneRequirement(ctx, reqID, formData.ProjectID, formData.Name) + if err != nil { + log.Printf("ERROR: Clone failed: %v", err) + return io.InlineError(web.ErrInternal, err) + } + + log.Printf("INFO: Requirement cloned successfully") + + // Return a simple success response for API calls + io.Response().Header().Set("Content-Type", "text/plain") + io.Response().WriteHeader(http.StatusOK) + _, err = io.Response().Write([]byte("Anforderung erfolgreich geklont")) + return err + }) +} -- GitLab