import { ReactNode } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Progress } from '@/components/ui/progress' import { StatCard } from '@/components/atoms' import * as Icons from '@phosphor-icons/react' import { cn } from '@/lib/utils' export interface PageComponentConfig { id: string type: string [key: string]: any } export interface PageLayoutConfig { type: string spacing?: string sections?: PageSectionConfig[] [key: string]: any } export interface PageSectionConfig { type: string [key: string]: any } export interface LegacyPageSchema { id: string layout: PageLayoutConfig dashboardCards?: any[] statCards?: any[] [key: string]: any } export interface ComponentRendererProps { config?: Record schema?: LegacyPageSchema data?: Record functions?: Record any> } function resolveBinding(binding: string, data: Record): any { try { const func = new Function(...Object.keys(data), `return ${binding}`) return func(...Object.values(data)) } catch { return binding } } function getIcon(iconName: string, props?: any) { const IconComponent = (Icons as any)[iconName] if (!IconComponent) return null return } export function JSONPageRenderer({ config, schema, data = {}, functions = {} }: ComponentRendererProps) { const pageSchema = config || schema if (!pageSchema) { return
No schema provided
} const renderSection = (section: PageSectionConfig, index: number): ReactNode => { switch (section.type) { case 'header': return (

{section.title}

{section.description && (

{section.description}

)}
) case 'cards': { const cards = pageSchema[section.items as string] || [] return (
{cards.map((card: any) => renderCard(card))}
) } case 'grid': { const gridItems = pageSchema[section.items as string] || [] const { sm = 1, md = 2, lg = 3 } = section.columns || {} return (
{gridItems.map((item: any) => renderStatCard(item))}
) } default: return null } } const renderCard = (card: any): ReactNode => { const icon = card.icon ? getIcon(card.icon) : null if (card.type === 'gradient-card') { const computeFn = functions[card.dataSource?.compute] const computedData = computeFn ? computeFn(data) : {} return ( {icon && {icon}} {card.title} {card.description} {card.components?.map((comp: any, idx: number) => renderSubComponent(comp, computedData, idx) )} ) } return ( {card.title} {card.component && renderCustomComponent(card.component, card.props || {})} ) } const renderSubComponent = (comp: any, dataContext: any, key: number): ReactNode => { const value = dataContext[comp.binding] switch (comp.type) { case 'metric': return (
{comp.format === 'percentage' ? `${value}%` : value}
) case 'badge': { const variant = value === 'ready' ? comp.variants?.ready : comp.variants?.inProgress return ( {variant?.label} ) } case 'progress': return case 'text': return (

{value}

) default: return null } } const renderStatCard = (stat: any): ReactNode => { const icon = stat.icon ? getIcon(stat.icon) : undefined const value = resolveBinding(stat.dataBinding, data) const description = `${value} ${stat.description}` return ( ) } const renderCustomComponent = (componentName: string, props: any): ReactNode => { return
Custom component: {componentName}
} return (
{pageSchema.layout.sections?.map((section, index) => renderSection(section, index))}
) }