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

157 lines
5.6 KiB
TypeScript
Raw Normal View History

2026-07-06 09:44:26 +00:00
import React, { lazy } from 'react'
2026-07-13 08:39:13 +00:00
import type { RouteDto } from '@/proxy/routes/models'
import type { CustomComponent } from '@/proxy/developerKit/models'
2026-02-24 20:44:16 +00:00
// 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'])
2026-07-13 08:39:13 +00:00
// 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>>
>()
2026-02-24 20:44:16 +00:00
// Fiziksel komponent yükleme (mevcut mantık)
function loadPhysicalComponent(componentPath: string) {
const cleanedPath = componentPath.replace(/^@\//, '')
const fullPath = `../${cleanedPath}.tsx`
2026-07-13 08:39:13 +00:00
if (physicalComponentCache.has(fullPath)) {
return physicalComponentCache.get(fullPath)!
2026-02-24 20:44:16 +00:00
}
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> }>)
2026-07-13 08:39:13 +00:00
physicalComponentCache.set(fullPath, LazyComponent)
2026-02-24 20:44:16 +00:00
return LazyComponent
}
// Dinamik komponent yükleme (yeni mantık)
function loadDynamicComponent(
componentPath: string,
2026-02-24 20:44:16 +00:00
registeredComponents: Record<string, React.ComponentType<unknown>>,
renderComponent?: (name: string, props?: any) => React.ReactNode,
isComponentRegistered?: (name: string) => boolean,
2026-02-24 20:44:16 +00:00
) {
// Önce manuel registered komponentleri kontrol et
let DynamicComponent = registeredComponents[componentPath]
2026-02-24 20:44:16 +00:00
// 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
2026-02-24 20:44:16 +00:00
}
if (!DynamicComponent) {
if (isComponentRegistered) {
console.log('Database component registry available - checking...')
}
throw new Error(`Dynamic component not found: ${componentPath}`)
2026-02-24 20:44:16 +00:00
}
2026-07-13 08:39:13 +00:00
// 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>
2026-02-24 20:44:16 +00:00
}
export function loadComponent(
componentType: string,
componentPath: string,
2026-02-24 20:44:16 +00:00
registeredComponents?: Record<string, React.ComponentType<unknown>>,
renderComponent?: (name: string, props?: any) => React.ReactNode,
isComponentRegistered?: (name: string) => boolean,
2026-02-24 20:44:16 +00:00
) {
if (componentType === 'normal') {
2026-02-24 20:44:16 +00:00
return loadPhysicalComponent(componentPath)
} else if (componentType === 'dynamic') {
2026-02-24 20:44:16 +00:00
if (!registeredComponents) {
throw new Error('Registered components required for dynamic component loading')
}
return loadDynamicComponent(
componentPath,
registeredComponents,
renderComponent,
isComponentRegistered,
)
2026-02-24 20:44:16 +00:00
} 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,
2026-07-13 08:39:13 +00:00
) => React.ComponentType<any>
2026-02-24 20:44:16 +00:00
routeType: string
authority?: string[]
componentType: string
2026-02-24 20:44:16 +00:00
componentPath: string
/** Browser tab title; only runtime components carry a human readable label. */
title?: string
2026-02-24 20:44:16 +00:00
}
// 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,
),
2026-02-24 20:44:16 +00:00
routeType: route.routeType,
authority: route.authority,
componentType: route.componentType,
2026-02-24 20:44:16 +00:00
componentPath: route.componentPath,
}))
}
2026-07-11 20:35:32 +00:00
// Custom components are the single source of truth for runtime routes.
export function mapCustomComponentRoutes(
components: Pick<CustomComponent, 'id' | 'name' | 'routePath' | 'isActive' | 'description'>[],
2026-07-11 20:35:32 +00:00
): 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,
2026-07-11 20:35:32 +00:00
}))
}