feat: Variantenmanagement — Sub-Projekte mit GAP-Analyse
Backend: - parent_project_id auf iace_projects (DB + Go Struct) - POST/GET /variants + GET /variant-gap Endpoints - GAP-Analyse: Differenz Hazards/Massnahmen/Kategorien Frontend: - VariantPanel auf Projekt-Uebersicht - Variante erstellen Dialog - Sidebar-Anzeige (Variantenanzahl / Basis-Link) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -308,3 +308,88 @@ func (h *IACEHandler) InitFromProfile(c *gin.Context) {
|
||||
"regulations_triggered": triggeredRegulations,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Variant Management
|
||||
// ============================================================================
|
||||
|
||||
// CreateVariant handles POST /projects/:id/variants
|
||||
// Creates a new variant sub-project linked to the given base project.
|
||||
func (h *IACEHandler) CreateVariant(c *gin.Context) {
|
||||
parentID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
// Verify parent project exists
|
||||
parent, err := h.store.GetProject(c.Request.Context(), parentID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if parent == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "parent project not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req iace.CreateProjectRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Force the parent link
|
||||
req.ParentProjectID = &parentID
|
||||
|
||||
project, err := h.store.CreateProject(c.Request.Context(), parent.TenantID, req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"project": project})
|
||||
}
|
||||
|
||||
// ListVariants handles GET /projects/:id/variants
|
||||
// Returns all variant sub-projects for a given base project.
|
||||
func (h *IACEHandler) ListVariants(c *gin.Context) {
|
||||
parentID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
variants, err := h.store.ListVariants(c.Request.Context(), parentID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if variants == nil {
|
||||
variants = []iace.Project{}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, iace.ProjectListResponse{
|
||||
Projects: variants,
|
||||
Total: len(variants),
|
||||
})
|
||||
}
|
||||
|
||||
// GetVariantGap handles GET /projects/:id/variant-gap
|
||||
// Returns a gap analysis comparing a variant against its base project.
|
||||
func (h *IACEHandler) GetVariantGap(c *gin.Context) {
|
||||
variantID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
gap, err := h.store.GetVariantGap(c.Request.Context(), variantID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gap)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user