diff --git a/.env b/.env index b1141ba..7c0838b 100644 --- a/.env +++ b/.env @@ -3,6 +3,7 @@ APP_NAME=MyClub APP_ENV=development PORT=8080 DEBUG=true +PREMIUM=true # Database Migrations & Seeding RUN_MIGRATIONS=true diff --git a/DOCS/PREMIUM_ARCHITECTURE.md b/DOCS/PREMIUM_ARCHITECTURE.md new file mode 100644 index 0000000..c5f9735 --- /dev/null +++ b/DOCS/PREMIUM_ARCHITECTURE.md @@ -0,0 +1,630 @@ +# Premium Version - Technical Architecture + +## Overview + +This document describes the architecture for implementing a premium/pro version toggle system that allows switching between: +- **Standard Mode**: Current React + MyUIbrix system +- **Premium Mode**: Professional Elementor-style templates + +## System Design + +### Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────────┐ +│ User Request │ +└────────────────────────┬────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Nginx / Reverse Proxy │ +│ Routes: /premium/* → Static Assets │ +│ /api/* → Backend │ +│ /* → Frontend React App │ +└────────────────────────┬────────────────────────────────────┘ + │ + ┌──────────────┴──────────────┐ + │ │ + ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ +│ Backend (Go) │ │ Frontend (React)│ +│ │ │ │ +│ Middleware: │◄─────────►│ Check Settings: │ +│ - Premium Mode │ API │ premium_mode_ │ +│ - Feature Flags │ │ active │ +│ │ │ │ +│ Routes: │ │ Conditional │ +│ /premium/css/* │ │ Render: │ +│ /premium/js/* │ │ - Standard │ +│ /premium/img/* │ │ - Premium │ +└──────────────────┘ └──────────────────┘ + │ │ + ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ +│ PostgreSQL │ │ Browser │ +│ │ │ │ +│ settings table: │ │ Loads: │ +│ - premium_mode_ │ │ - Premium CSS │ +│ active │ │ - Premium JS │ +│ - premium_ │ │ - Club Theme │ +│ features │ │ │ +└──────────────────┘ └──────────────────┘ +``` + +--- + +## Component Architecture + +### Backend Components + +#### 1. Configuration Layer +**File:** `internal/config/config.go` + +```go +type Config struct { + // Existing fields... + + // Premium Mode Configuration + PremiumMode bool `env:"PREMIUM_MODE" envDefault:"false"` + PremiumHomepage bool `env:"PREMIUM_HOMEPAGE" envDefault:"false"` + PremiumBlog bool `env:"PREMIUM_BLOG" envDefault:"false"` + Premium404 bool `env:"PREMIUM_404" envDefault:"false"` + DisableMyUIbrix bool `env:"PREMIUM_DISABLE_MYUIBRIX" envDefault:"false"` + PremiumAssetsPath string `env:"PREMIUM_ASSETS_PATH" envDefault:"./pro"` +} + +func (c *Config) IsPremiumActive(pageType string) bool { + if !c.PremiumMode { + return false + } + + switch pageType { + case "homepage": + return c.PremiumHomepage + case "blog": + return c.PremiumBlog + case "404": + return c.Premium404 + default: + return false + } +} +``` + +#### 2. Middleware Layer +**File:** `internal/middleware/premium_mode.go` + +```go +func PremiumModeMiddleware(cfg *config.Config) gin.HandlerFunc { + return func(c *gin.Context) { + // Set premium context + c.Set("premium_mode", cfg.PremiumMode) + c.Set("disable_myuibrix", cfg.DisableMyUIbrix) + + // Add premium features to context + premiumFeatures := map[string]bool{ + "homepage": cfg.PremiumHomepage, + "blog": cfg.PremiumBlog, + "404": cfg.Premium404, + } + c.Set("premium_features", premiumFeatures) + + // Log premium mode status + if cfg.PremiumMode { + log.Debug("Premium mode active for request: %s", c.Request.URL.Path) + } + + c.Next() + } +} +``` + +#### 3. Settings Extension +**File:** `internal/models/settings.go` + +```go +type Settings struct { + // Existing fields... + + PremiumModeActive bool `json:"premium_mode_active" gorm:"default:false"` + PremiumFeatures string `json:"premium_features" gorm:"type:text"` // JSON + PremiumThemeVariant string `json:"premium_theme_variant" gorm:"default:'default'"` +} + +type PremiumFeatures struct { + Homepage bool `json:"homepage"` + Blog bool `json:"blog"` + Error404 bool `json:"404"` +} + +func (s *Settings) GetPremiumFeatures() (*PremiumFeatures, error) { + if s.PremiumFeatures == "" { + return &PremiumFeatures{}, nil + } + + var features PremiumFeatures + err := json.Unmarshal([]byte(s.PremiumFeatures), &features) + return &features, err +} +``` + +#### 4. Controller Extension +**File:** `internal/controllers/base_controller.go` + +```go +func (ctrl *BaseController) GetPublicSettings(c *gin.Context) { + settings, err := ctrl.DB.GetSettings() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // Add premium mode info from environment + cfg := config.GetConfig() + settings.PremiumModeActive = cfg.PremiumMode + + if cfg.PremiumMode { + features, _ := settings.GetPremiumFeatures() + c.JSON(http.StatusOK, gin.H{ + "settings": settings, + "premium": gin.H{ + "enabled": true, + "features": features, + "disable_myuibrix": cfg.DisableMyUIbrix, + }, + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "settings": settings, + "premium": gin.H{ + "enabled": false, + }, + }) + } +} +``` + +--- + +### Frontend Components + +#### 1. Premium Layout System +**File:** `frontend/src/layouts/PremiumLayout.tsx` + +```typescript +import React, { useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet'; +import { loadPremiumAssets, cleanupPremiumAssets } from '../utils/premiumAssets'; + +interface PremiumLayoutProps { + children: React.ReactNode; + pageType: 'home' | 'blog' | '404'; + settings?: any; +} + +export const PremiumLayout: React.FC = ({ + children, + pageType, + settings +}) => { + const [assetsLoaded, setAssetsLoaded] = useState(false); + + useEffect(() => { + // Load premium assets + loadPremiumAssets(pageType) + .then(() => setAssetsLoaded(true)) + .catch(err => console.error('Failed to load premium assets:', err)); + + // Cleanup on unmount + return () => { + cleanupPremiumAssets(); + }; + }, [pageType]); + + if (!assetsLoaded) { + return
Loading premium theme...
; + } + + return ( + <> + + + +
+ {children} +
+ + ); +}; +``` + +#### 2. Asset Loader Utility +**File:** `frontend/src/utils/premiumAssets.ts` + +```typescript +interface AssetConfig { + css: string[]; + js: string[]; + fonts: string[]; +} + +const assetConfig: Record = { + home: { + css: [ + '/premium/css/bootstrap.css', + '/premium/css/bizoni.css', + '/premium/css/elementor-frontend.min.css', + '/premium/css/zoom-slider.css', + '/premium/css/swiper.css', + '/premium/css/post-32647.css', + ], + js: [ + '/premium/js/jquery.min.js', + '/premium/js/jquery-migrate.min.js', + '/premium/js/modernizr-2.6.2.min.js', + '/premium/js/swiper.min.js', + '/premium/js/jquery.zoomslider.js', + '/premium/js/parallax-js.js', + '/premium/js/script.js', + '/premium/js/webpack.runtime.min.js', + '/premium/js/frontend-modules.min.js', + '/premium/js/frontend.min.js', + ], + fonts: [ + 'https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,700|Sofia+Sans+Extra+Condensed:800,300i', + 'https://fonts.googleapis.com/icon?family=Material+Icons', + ], + }, + blog: { + css: [ + '/premium/css/bootstrap.css', + '/premium/css/bizoni.css', + '/premium/css/elementor-frontend.min.css', + '/premium/css/post-29393.css', + ], + js: [ + '/premium/js/jquery.min.js', + '/premium/js/scripts.js', + ], + fonts: [], + }, + '404': { + css: [ + '/premium/css/bootstrap.css', + '/premium/css/bizoni.css', + ], + js: [ + '/premium/js/jquery.min.js', + ], + fonts: [], + }, +}; + +const loadedAssets: Set = new Set(); + +export const loadCSS = (href: string): Promise => { + if (loadedAssets.has(href)) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = href; + link.onload = () => { + loadedAssets.add(href); + resolve(); + }; + link.onerror = () => reject(new Error(`Failed to load CSS: ${href}`)); + document.head.appendChild(link); + }); +}; + +export const loadJS = (src: string): Promise => { + if (loadedAssets.has(src)) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = src; + script.async = false; // Maintain load order + script.onload = () => { + loadedAssets.add(src); + resolve(); + }; + script.onerror = () => reject(new Error(`Failed to load JS: ${src}`)); + document.body.appendChild(script); + }); +}; + +export const loadPremiumAssets = async (pageType: string): Promise => { + const config = assetConfig[pageType]; + if (!config) { + throw new Error(`Unknown page type: ${pageType}`); + } + + try { + // Load CSS first + await Promise.all(config.css.map(loadCSS)); + + // Load fonts + await Promise.all(config.fonts.map(loadCSS)); + + // Load JS in order + for (const src of config.js) { + await loadJS(src); + } + } catch (error) { + console.error('Failed to load premium assets:', error); + throw error; + } +}; + +export const cleanupPremiumAssets = (): void => { + // Remove all premium CSS + document.querySelectorAll('link[href^="/premium/"]').forEach(el => el.remove()); + + // Remove all premium JS + document.querySelectorAll('script[src^="/premium/"]').forEach(el => el.remove()); + + // Clear loaded assets cache + loadedAssets.clear(); +}; +``` + +#### 3. Theme Hook +**File:** `frontend/src/hooks/usePremiumTheme.ts` + +```typescript +import { useEffect } from 'react'; +import { useSettings } from './useSettings'; + +export const usePremiumTheme = () => { + const { settings } = useSettings(); + + useEffect(() => { + if (!settings) return; + + const root = document.documentElement; + + // Inject club colors into premium CSS variables + root.style.setProperty('--lte-main-color', settings.primary_color || '#e63946'); + root.style.setProperty('--lte-secondary-color', settings.secondary_color || '#1d3557'); + root.style.setProperty('--lte-text-on-primary', settings.text_on_primary || '#ffffff'); + root.style.setProperty('--lte-text-on-secondary', settings.text_on_secondary || '#f1faee'); + root.style.setProperty('--lte-accent-color', settings.accent_color || '#a8dadc'); + + // Typography + root.style.setProperty('--lte-font-primary', "'Open Sans', sans-serif"); + root.style.setProperty('--lte-font-display', "'Sofia Sans Extra Condensed', sans-serif"); + + // Cleanup + return () => { + root.style.removeProperty('--lte-main-color'); + root.style.removeProperty('--lte-secondary-color'); + // ... other cleanup + }; + }, [settings]); + + return { settings }; +}; +``` + +#### 4. Routing Logic +**File:** `frontend/src/App.tsx` + +```typescript +import { useSettings } from './hooks/useSettings'; +import { PremiumHomePage } from './pages/PremiumHomePage'; +import { PremiumBlogPage } from './pages/PremiumBlogPage'; +import { PremiumNotFoundPage } from './pages/PremiumNotFoundPage'; +import HomePage from './pages/HomePage'; +import BlogPage from './pages/BlogPage'; +import NotFoundPage from './pages/NotFoundPage'; + +const App: React.FC = () => { + const { settings, isLoading } = useSettings(); + + if (isLoading) { + return ; + } + + const isPremiumMode = settings?.premium_mode_active; + + return ( + + + {/* Conditional homepage */} + : } + /> + + {/* Conditional blog */} + : } + /> + + {/* Other routes stay standard */} + } /> + } /> + + {/* Conditional 404 */} + : } + /> + + + ); +}; +``` + +--- + +## Data Flow + +### 1. Standard Mode (PREMIUM_MODE=false) +``` +Request → Backend → Settings API → Frontend + → Render: HomePage + → MyUIbrix: Enabled +``` + +### 2. Premium Mode (PREMIUM_MODE=true) +``` +Request → Backend → Settings API → Frontend + → Check: premium_mode_active=true + → Load: Premium Assets + → Inject: Club Colors + → Render: PremiumHomePage + → MyUIbrix: Disabled +``` + +### 3. Asset Loading Sequence +``` +1. React App Loads +2. Check Settings API +3. If Premium Mode: + a. Load Core CSS (bootstrap, bizoni) + b. Load Component CSS (zoom-slider, swiper) + c. Load Fonts (Google Fonts) + d. Load Core JS (jQuery, modernizr) + e. Load Libraries (swiper, parallax) + f. Load Elementor JS (webpack, frontend) + g. Initialize Premium Components + h. Inject Club Theme +4. Render Premium Page +``` + +--- + +## Performance Optimization + +### Code Splitting Strategy +```typescript +// Lazy load premium components +const PremiumHomePage = React.lazy(() => import('./pages/PremiumHomePage')); +const PremiumBlogPage = React.lazy(() => import('./pages/PremiumBlogPage')); + +// Use Suspense for loading states +}> + + +``` + +### Asset Optimization +1. **CSS Minification**: All premium CSS files minified +2. **JS Bundle Splitting**: Separate bundles for homepage, blog, 404 +3. **Image Lazy Loading**: Premium images load on scroll +4. **Font Subsetting**: Load only used glyphs +5. **Resource Hints**: Preload critical assets + +### Caching Strategy +```nginx +# Nginx configuration +location /premium/ { + expires 1y; + add_header Cache-Control "public, immutable"; +} +``` + +--- + +## Security Considerations + +### 1. Asset Isolation +- Premium assets served from separate directory +- No cross-contamination with standard mode + +### 2. Feature Toggles +- Environment-based (server-side control) +- Cannot be manipulated by client + +### 3. Content Security Policy +```typescript + + + +``` + +--- + +## Rollback Strategy + +### Quick Rollback +```bash +# Set environment variable +PREMIUM_MODE=false + +# Restart backend +docker-compose restart backend + +# Clear cache +redis-cli FLUSHALL +``` + +### Database Rollback +```sql +-- Revert settings +UPDATE settings SET premium_mode_active = FALSE; + +-- Run down migration +migrate -path database/migrations -database "postgres://..." down 1 +``` + +--- + +## Monitoring & Logging + +### Key Metrics +- Premium mode activation rate +- Asset load times +- Error rates (CSS/JS loading failures) +- User engagement (premium vs standard) +- Performance metrics (Lighthouse scores) + +### Logging +```go +log.Info("Premium mode activated", map[string]interface{}{ + "user_id": userID, + "page_type": pageType, + "assets_loaded": len(loadedAssets), + "load_time_ms": loadTime, +}) +``` + +--- + +## Migration Path + +### Phase 1: Add Premium Support (No Breaking Changes) +- Add environment variables +- Extend Settings model +- Create premium components +- Keep standard mode as default + +### Phase 2: Test Premium Mode (Opt-In) +- Enable for specific users/teams +- Collect feedback +- Fix bugs +- Optimize performance + +### Phase 3: Production Rollout +- Enable premium mode globally +- Monitor metrics +- Gradual migration +- Keep rollback option + +### Phase 4: Sunset Standard Mode (Optional) +- After 3-6 months of stable premium operation +- Remove MyUIbrix dependencies +- Simplify codebase diff --git a/DOCS/PREMIUM_FEATURE_COMPARISON.md b/DOCS/PREMIUM_FEATURE_COMPARISON.md new file mode 100644 index 0000000..47b1ab7 --- /dev/null +++ b/DOCS/PREMIUM_FEATURE_COMPARISON.md @@ -0,0 +1,514 @@ +# Premium vs Standard Feature Comparison + +## Executive Summary + +| Aspect | Standard Mode | Premium Mode | +|--------|---------------|--------------| +| **Visual Editor** | MyUIbrix (Elementor-style) | Disabled | +| **Design System** | Chakra UI + Custom CSS | Atleticos Theme + Elementor | +| **Hero Section** | Static/Swiper variants | Zoom Slider with Parallax | +| **Animations** | Basic transitions | Advanced parallax effects | +| **Typography** | Chakra fonts | Open Sans, Sofia Sans, Marcellus, Tangerine | +| **Grid System** | Chakra responsive | Bootstrap 4 grid | +| **Components** | React Chakra UI | Elementor widgets | +| **Customization** | Live editor (MyUIbrix) | Code/Admin panel | +| **Performance** | 1.5-2s load time | 1.8-2.5s load time | +| **Maintenance** | React updates needed | Static template updates | + +--- + +## Detailed Feature Comparison + +### 1. Homepage + +#### Standard Mode +**Features:** +- ✅ MyUIbrix drag-and-drop editor +- ✅ Live style editing +- ✅ Column layouts configurable +- ✅ 17+ section types +- ✅ Inline text editing +- ✅ CSS editor +- ✅ Variant switcher per section +- ❌ No zoom slider +- ❌ Basic parallax effects + +**Tech Stack:** +- React 18 +- Chakra UI components +- Custom hooks +- React Query +- MyUIbrix editor + +**Use Cases:** +- Clubs wanting full control +- Frequent content updates +- Non-technical admins +- Custom branding needs + +#### Premium Mode +**Features:** +- ✅ Professional zoom slider hero +- ✅ Advanced parallax animations +- ✅ Elementor-style layout +- ✅ Premium typography +- ✅ Smooth transitions +- ✅ Magazine-style sections +- ✅ Optimized for visual impact +- ❌ No live editor +- ❌ Requires code changes for structure + +**Tech Stack:** +- React + Premium templates +- Bootstrap grid +- jQuery (for animations) +- Swiper slider +- Zoom slider plugin +- Elementor CSS framework + +**Use Cases:** +- Professional club presentation +- Marketing-focused sites +- High visual impact needed +- Less frequent updates + +--- + +### 2. Blog/Articles + +#### Standard Mode +**HTML Structure:** +```typescript + + + + + + + + + + + + + +