# Generic Page System - Reducing Hardcoded TSX Dependency ## Overview MetaBuilder now features a **Generic Page System** that allows Level 1-3 (Homepage, User Area, Admin Panel) to be defined declaratively using JSON configuration and Lua scripts, dramatically reducing dependence on hardcoded TSX files. ## Key Improvements ### Before (Iteration 1-23) - ❌ Level1.tsx, Level2.tsx, Level3.tsx were **hardcoded TSX files** - ❌ Each level had fixed UI structure - ❌ Changes required code modification - ❌ IRC was the only declarative component ### After (Iteration 24+) - ✅ **PageDefinition** system allows declarative page configuration - ✅ **GenericPage** component renders any page from JSON - ✅ **PageRenderer** manages page loading, permissions, and Lua execution - ✅ **PageDefinitionBuilder** provides default pages - ✅ Levels 1-3 can be fully customized from Level 4/5 without code changes ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ PAGE DEFINITION (JSON) │ │ - Components tree │ │ - Layout configuration │ │ - Permission rules │ │ - Lua script hooks │ │ - Metadata (header, sidebar, footer) │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ PAGE RENDERER │ │ - Loads page from database │ │ - Checks permissions │ │ - Executes onLoad/onUnload Lua scripts │ │ - Provides context to components │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ GENERIC PAGE (React) │ │ - Renders layout (default, sidebar, dashboard, blank) │ │ - Renders header/footer based on metadata │ │ - Renders component tree using RenderComponent │ │ - Handles navigation and logout │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ RENDER COMPONENT │ │ - Recursively renders component tree │ │ - Supports shadcn components │ │ - Supports declarative components (IRC, Forum, etc.) │ │ - Executes Lua handlers for interactions │ └─────────────────────────────────────────────────────────────┘ ``` ## Core Files ### 1. `/src/lib/page-renderer.ts` **PageRenderer** class that manages pages: - `registerPage(page)` - Register a page definition - `loadPages()` - Load pages from database - `getPage(id)` - Get page by ID - `getPagesByLevel(level)` - Get all pages for a level - `checkPermissions(page, user)` - Verify user can access page - `executeLuaScript(scriptId, context)` - Run Lua script - `onPageLoad(page, context)` - Lifecycle hook - `onPageUnload(page, context)` - Lifecycle hook ### 2. `/src/lib/page-definition-builder.ts` **PageDefinitionBuilder** creates default pages: - `initializeDefaultPages()` - Creates Level 1-3 default pages - `buildLevel1Homepage()` - Public homepage with hero + features - `buildLevel2UserDashboard()` - User dashboard with profile + comments - `buildLevel3AdminPanel()` - Admin panel with user/content management - `getPages()` - Returns all built pages ### 3. `/src/components/GenericPage.tsx` **GenericPage** React component that renders any page: - Loads page definition from PageRenderer - Checks permissions - Renders appropriate layout - Executes Lua lifecycle hooks - Handles navigation and user actions ## PageDefinition Structure ```typescript interface PageDefinition { id: string // Unique page identifier level: 1 | 2 | 3 | 4 | 5 // Application level title: string // Page title description?: string // Optional description layout: 'default' | 'sidebar' | 'dashboard' | 'blank' // Layout type components: ComponentInstance[] // Component tree luaScripts?: { onLoad?: string // Script ID to run on page load onUnload?: string // Script ID to run on page unload handlers?: Record // Event handlers (Lua script IDs) } permissions?: { requiresAuth: boolean // Requires authentication? requiredRole?: string // Minimum role required customCheck?: string // Custom Lua permission check } metadata?: { showHeader?: boolean // Show header? showFooter?: boolean // Show footer? headerTitle?: string // Header title headerActions?: ComponentInstance[] // Header buttons/actions sidebarItems?: Array<{ // Sidebar navigation items id: string label: string icon: string action: 'navigate' | 'lua' | 'external' target: string }> } } ``` ## Layout Types ### 1. Default Layout Standard page with header, content area, and footer: ``` ┌────────────────────────────────────┐ │ Header │ ├────────────────────────────────────┤ │ │ │ Content │ │ │ ├────────────────────────────────────┤ │ Footer │ └────────────────────────────────────┘ ``` ### 2. Sidebar Layout Page with persistent sidebar navigation: ``` ┌────────────────────────────────────┐ │ Header │ ├────────┬───────────────────────────┤ │ │ │ │ Sidebar│ Content │ │ │ │ └────────┴───────────────────────────┘ ``` ### 3. Dashboard Layout Full application dashboard with sidebar + header: ``` ┌────────────────────────────────────┐ │ Header │ ├────────┬───────────────────────────┤ │ │ │ │ Sidebar│ Dashboard Content │ │ │ (usually cards/widgets) │ └────────┴───────────────────────────┘ ``` ### 4. Blank Layout No header/footer, just content (for custom layouts): ``` ┌────────────────────────────────────┐ │ │ │ Full Content │ │ │ └────────────────────────────────────┘ ``` ## Usage Examples ### Example 1: Create a Custom Homepage ```typescript import { getPageRenderer } from '@/lib/page-renderer' const customHomepage: PageDefinition = { id: 'page_custom_home', level: 1, title: 'My Custom Homepage', layout: 'default', components: [ { id: 'hero', type: 'Container', props: { className: 'py-20 text-center bg-gradient-to-br from-blue-500 to-purple-600' }, children: [ { id: 'hero_title', type: 'Heading', props: { level: 1, children: 'Welcome to Our Platform', className: 'text-5xl font-bold text-white mb-4' }, children: [] }, { id: 'hero_cta', type: 'Button', props: { children: 'Get Started', variant: 'default', size: 'lg' }, children: [] } ] } ], permissions: { requiresAuth: false }, metadata: { showHeader: true, showFooter: true } } const renderer = getPageRenderer() await renderer.registerPage(customHomepage) ``` ### Example 2: User Dashboard with Sidebar ```typescript const userDashboard: PageDefinition = { id: 'page_user_dash', level: 2, title: 'Dashboard', layout: 'dashboard', components: [ { id: 'stats_card', type: 'Card', props: { className: 'p-6' }, children: [ { id: 'stats_title', type: 'Heading', props: { level: 2, children: 'Your Stats' }, children: [] } ] } ], permissions: { requiresAuth: true, requiredRole: 'user' }, metadata: { showHeader: true, headerTitle: 'Dashboard', sidebarItems: [ { id: 'nav_home', label: 'Home', icon: '🏠', action: 'navigate', target: '1' }, { id: 'nav_profile', label: 'Profile', icon: '👤', action: 'navigate', target: '2' } ] } } ``` ### Example 3: Page with Lua Lifecycle Hooks ```typescript // First, create Lua scripts in database const onLoadScript: LuaScript = { id: 'lua_page_analytics', name: 'Track Page View', code: ` function trackPageView(userId, pageId) log("User " .. userId .. " viewed page " .. pageId) -- Could save to database, call API, etc. return true end return trackPageView `, parameters: [ { name: 'userId', type: 'string' }, { name: 'pageId', type: 'string' } ], returnType: 'boolean' } await Database.addLuaScript(onLoadScript) // Then reference it in page definition const trackedPage: PageDefinition = { id: 'page_tracked', level: 2, title: 'Tracked Page', layout: 'default', components: [], luaScripts: { onLoad: 'lua_page_analytics', // Runs when page loads onUnload: 'lua_page_cleanup' // Runs when page unloads }, permissions: { requiresAuth: true } } ``` ## Rendering a Generic Page In `App.tsx` or any level component: ```typescript import { GenericPage } from '@/components/GenericPage' // Instead of: // // Use: ``` ## Extending the System ### Add Custom Component Types Register new component types in `component-catalog.ts`: ```typescript { type: 'VideoPlayer', label: 'Video Player', icon: 'Play', category: 'Media', allowsChildren: false, defaultProps: { src: '', controls: true }, propSchema: [ { name: 'src', label: 'Video URL', type: 'string' }, { name: 'controls', label: 'Show Controls', type: 'boolean' } ] } ``` Then add rendering logic in `RenderComponent.tsx`: ```typescript case 'VideoPlayer': return (