sozsoft-platform/ui/src/routes/dynamicRouteLoader.tsx

156 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { lazy } from 'react'
import type { RouteDto } from '@/proxy/routes/models'
import type { CustomComponent } from '@/proxy/developerKit/models'
// Tüm view bileşenlerini import et (vite özel)
// shared klasörü hariç, çünkü bu bileşenler genellikle başka yerlerde statik import ediliyor
const modules = import.meta.glob(['../views/**/*.tsx', '!../views/shared/**/*.tsx'])
// Only physical modules are safe to cache. Runtime components depend on the
// latest ComponentContext render function and must not retain an old closure.
const physicalComponentCache = new Map<
string,
React.LazyExoticComponent<React.ComponentType<any>>
>()
// Fiziksel komponent yükleme (mevcut mantık)
function loadPhysicalComponent(componentPath: string) {
const cleanedPath = componentPath.replace(/^@\//, '')
const fullPath = `../${cleanedPath}.tsx`
if (physicalComponentCache.has(fullPath)) {
return physicalComponentCache.get(fullPath)!
}
const loader = modules[fullPath]
if (!loader) {
console.error(`Physical component not found for path: ${fullPath}`)
throw new Error(`Physical component not found for path: ${fullPath}`)
}
const LazyComponent = lazy(loader as () => Promise<{ default: React.ComponentType<any> }>)
physicalComponentCache.set(fullPath, LazyComponent)
return LazyComponent
}
// Dinamik komponent yükleme (yeni mantık)
function loadDynamicComponent(
componentPath: string,
registeredComponents: Record<string, React.ComponentType<unknown>>,
renderComponent?: (name: string, props?: any) => React.ReactNode,
isComponentRegistered?: (name: string) => boolean,
) {
// Önce manuel registered komponentleri kontrol et
let DynamicComponent = registeredComponents[componentPath]
// Eğer manuel registered'da yoksa, database compiled komponentleri kontrol et
if (
!DynamicComponent &&
isComponentRegistered &&
renderComponent &&
isComponentRegistered(componentPath)
) {
DynamicComponent = (props: any) => renderComponent(componentPath, props) as React.ReactElement
}
if (!DynamicComponent) {
if (isComponentRegistered) {
console.log('Database component registry available - checking...')
}
throw new Error(`Dynamic component not found: ${componentPath}`)
}
// Do not put this wrapper in a module-level cache. renderComponent changes
// after the asynchronous database bundle compilation finishes. Caching this
// function would keep the pre-compilation closure and render an empty page.
return DynamicComponent as React.ComponentType<any>
}
export function loadComponent(
componentType: string,
componentPath: string,
registeredComponents?: Record<string, React.ComponentType<unknown>>,
renderComponent?: (name: string, props?: any) => React.ReactNode,
isComponentRegistered?: (name: string) => boolean,
) {
if (componentType === 'normal') {
return loadPhysicalComponent(componentPath)
} else if (componentType === 'dynamic') {
if (!registeredComponents) {
throw new Error('Registered components required for dynamic component loading')
}
return loadDynamicComponent(
componentPath,
registeredComponents,
renderComponent,
isComponentRegistered,
)
} else {
// Backward compatibility: varsayılan olarak fiziksel komponent kabul et
return loadPhysicalComponent(componentPath)
}
}
// React Router için uygun bir route tipi
export interface DynamicReactRoute {
key: string
path: string
getComponent: (
registeredComponents?: Record<string, React.ComponentType<unknown>>,
renderComponent?: (name: string, props?: any) => React.ReactNode,
isComponentRegistered?: (name: string) => boolean,
) => React.ComponentType<any>
routeType: string
authority?: string[]
componentType: string
componentPath: string
/** Browser tab title; only runtime components carry a human readable label. */
title?: string
}
// API'den gelen route objesini, React Router için uygun hale getirir
export function mapDynamicRoutes(routes: RouteDto[]): DynamicReactRoute[] {
return routes.map((route) => ({
key: route.path,
path: route.path,
getComponent: (registeredComponents, renderComponent, isComponentRegistered) =>
loadComponent(
route.componentType,
route.componentPath,
registeredComponents,
renderComponent,
isComponentRegistered,
),
routeType: route.routeType,
authority: route.authority,
componentType: route.componentType,
componentPath: route.componentPath,
}))
}
// Custom components are the single source of truth for runtime routes.
export function mapCustomComponentRoutes(
components: Pick<CustomComponent, 'id' | 'name' | 'routePath' | 'isActive' | 'description'>[],
): DynamicReactRoute[] {
return components
.filter((component) => component.isActive && component.routePath?.trim())
.map((component) => ({
key: `custom-component-${component.id}`,
path: component.routePath.startsWith('/') ? component.routePath : `/${component.routePath}`,
getComponent: (registeredComponents, renderComponent, isComponentRegistered) =>
loadComponent(
'dynamic',
component.name,
registeredComponents,
renderComponent,
isComponentRegistered,
),
routeType: component.routePath.startsWith('/admin/') ? 'protected' : 'public',
authority: [],
componentType: 'dynamic',
componentPath: component.name,
// A runtime component has no module of its own to set the page title, so
// the description entered in the designer names the tab.
title: component.description?.trim() || component.name,
}))
}