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