Files
MyClub/internal/controllers/myuibrix_controller.go
T
Tomas Dvorak b9cea0cd77 dev day #79
2025-11-02 01:04:02 +01:00

330 lines
8.1 KiB
Go

package controllers
import (
"encoding/json"
"net/http"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type MyUIbrixController struct {
DB *gorm.DB
}
// PageElementConfigRequest represents element configuration
type PageElementConfigRequest struct {
PageType string `json:"page_type" binding:"required"`
ElementName string `json:"element_name" binding:"required"`
Variant string `json:"variant"`
Visible bool `json:"visible"`
DisplayOrder int `json:"display_order"`
CustomStyles map[string]interface{} `json:"custom_styles"`
}
// ValidateElementConfig validates and optimizes element configuration
func (ctrl *MyUIbrixController) ValidateElementConfig(c *gin.Context) {
var req PageElementConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Validate element name (alphanumeric, underscore, hyphen only)
if !isValidElementName(req.ElementName) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid element name"})
return
}
// Optimize custom styles by removing redundant properties
optimizedStyles := optimizeStyles(req.CustomStyles)
c.JSON(http.StatusOK, gin.H{
"valid": true,
"optimized_styles": optimizedStyles,
"suggestions": generateStyleSuggestions(req.ElementName, optimizedStyles),
})
}
// GetElementPreview generates server-side preview data
func (ctrl *MyUIbrixController) GetElementPreview(c *gin.Context) {
elementName := c.Query("element")
variant := c.Query("variant")
viewport := c.Query("viewport") // desktop, tablet, mobile
if elementName == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "element required"})
return
}
// Generate preview metadata
preview := generatePreviewMetadata(elementName, variant, viewport)
c.JSON(http.StatusOK, preview)
}
// BatchValidateConfigs validates multiple configs at once
func (ctrl *MyUIbrixController) BatchValidateConfigs(c *gin.Context) {
var configs []PageElementConfigRequest
if err := c.ShouldBindJSON(&configs); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
results := make([]map[string]interface{}, 0, len(configs))
for _, config := range configs {
if !isValidElementName(config.ElementName) {
results = append(results, map[string]interface{}{
"element_name": config.ElementName,
"valid": false,
"error": "Invalid element name",
})
continue
}
optimizedStyles := optimizeStyles(config.CustomStyles)
results = append(results, map[string]interface{}{
"element_name": config.ElementName,
"valid": true,
"optimized_styles": optimizedStyles,
"display_order": config.DisplayOrder,
})
}
c.JSON(http.StatusOK, gin.H{
"results": results,
"timestamp": time.Now().Unix(),
})
}
// OptimizePageLayout analyzes and optimizes page element layout
func (ctrl *MyUIbrixController) OptimizePageLayout(c *gin.Context) {
pageType := c.Query("page_type")
if pageType == "" {
pageType = "homepage"
}
// Load current configurations from database
var configs []map[string]interface{}
query := `
SELECT element_name, variant, visible, display_order, settings
FROM page_element_configs
WHERE page_type = ?
ORDER BY display_order ASC
`
rows, err := ctrl.DB.Raw(query, pageType).Rows()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load configs"})
return
}
defer rows.Close()
for rows.Next() {
var elementName, variant string
var visible bool
var displayOrder int
var settingsJSON []byte
if err := rows.Scan(&elementName, &variant, &visible, &displayOrder, &settingsJSON); err != nil {
continue
}
// settings is a JSON object; we expect optional nested key "styles"
var settings map[string]interface{}
if len(settingsJSON) > 0 {
_ = json.Unmarshal(settingsJSON, &settings)
}
var customStyles map[string]interface{}
if settings != nil {
if st, ok := settings["styles"]; ok {
if m, ok2 := st.(map[string]interface{}); ok2 {
customStyles = m
}
}
}
configs = append(configs, map[string]interface{}{
"element_name": elementName,
"variant": variant,
"visible": visible,
"display_order": displayOrder,
// Keep API field name as custom_styles for compatibility
"custom_styles": customStyles,
})
}
// Generate optimization suggestions
suggestions := generateLayoutSuggestions(configs)
c.JSON(http.StatusOK, gin.H{
"current_layout": configs,
"suggestions": suggestions,
"performance_score": calculatePerformanceScore(configs),
})
}
// Helper functions
func isValidElementName(name string) bool {
if name == "" || len(name) > 100 {
return false
}
for _, r := range name {
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
(r >= '0' && r <= '9') || r == '_' || r == '-') {
return false
}
}
return true
}
func optimizeStyles(styles map[string]interface{}) map[string]interface{} {
if styles == nil {
return make(map[string]interface{})
}
optimized := make(map[string]interface{})
// Remove redundant or default values
for key, value := range styles {
// Skip empty values
if value == nil || value == "" || value == 0 {
continue
}
// Skip defaults
if isDefaultValue(key, value) {
continue
}
optimized[key] = value
}
return optimized
}
func isDefaultValue(key string, value interface{}) bool {
defaults := map[string]interface{}{
"margin": "0",
"padding": "0",
"border": "none",
"background": "transparent",
"opacity": 1,
"display": "block",
}
if defaultVal, exists := defaults[key]; exists {
return value == defaultVal
}
return false
}
func generateStyleSuggestions(elementName string, styles map[string]interface{}) []string {
suggestions := []string{}
// Check for performance-heavy properties
if _, hasBoxShadow := styles["box-shadow"]; hasBoxShadow {
if _, hasFilter := styles["filter"]; hasFilter {
suggestions = append(suggestions, "Multiple shadows and filters may impact performance")
}
}
// Check for conflicting properties
if position, ok := styles["position"].(string); ok {
if position == "fixed" || position == "sticky" {
suggestions = append(suggestions, "Fixed/sticky positioning may cause layout issues on mobile")
}
}
return suggestions
}
func generatePreviewMetadata(elementName, variant, viewport string) map[string]interface{} {
// Generate viewport-specific metadata
dimensions := map[string]map[string]int{
"desktop": {"width": 1920, "height": 1080},
"tablet": {"width": 768, "height": 1024},
"mobile": {"width": 375, "height": 667},
}
dim := dimensions["desktop"]
if d, ok := dimensions[viewport]; ok {
dim = d
}
return map[string]interface{}{
"element_name": elementName,
"variant": variant,
"viewport": viewport,
"dimensions": dim,
"render_hints": map[string]interface{}{
"use_gpu_acceleration": true,
"optimize_images": true,
"lazy_load": viewport == "mobile",
},
}
}
func generateLayoutSuggestions(configs []map[string]interface{}) []string {
suggestions := []string{}
visibleCount := 0
for _, config := range configs {
if visible, ok := config["visible"].(bool); ok && visible {
visibleCount++
}
}
if visibleCount > 15 {
suggestions = append(suggestions, "Consider hiding some elements to improve page load time")
}
if visibleCount < 3 {
suggestions = append(suggestions, "Page may look empty - consider adding more visible elements")
}
return suggestions
}
func calculatePerformanceScore(configs []map[string]interface{}) int {
score := 100
// Deduct for too many visible elements
visibleCount := 0
for _, config := range configs {
if visible, ok := config["visible"].(bool); ok && visible {
visibleCount++
}
}
if visibleCount > 15 {
score -= (visibleCount - 15) * 2
}
// Deduct for heavy custom styles
heavyStyleCount := 0
for _, config := range configs {
if styles, ok := config["custom_styles"].(map[string]interface{}); ok {
if len(styles) > 10 {
heavyStyleCount++
}
}
}
score -= heavyStyleCount * 5
if score < 0 {
score = 0
}
if score > 100 {
score = 100
}
return score
}