Component Registry
The Vertical Template includes a registry of pre-built components that can be used in configuration files. This reference documents the built-in components and how to register custom components.
Built-in Components
Header
Application header with logo, title, and navigation.
interface HeaderProps {
logo?: string;
title?: string;
subtitle?: string;
actions?: ActionItem[];
userMenu?: boolean;
className?: string;
}
interface ActionItem {
label: string;
path?: string;
onClick?: () => void;
icon?: string;
}
Example Configuration
{
"component": "Header",
"props": {
"logo": "/logo.png",
"title": "CRM Platform",
"subtitle": "Enterprise Edition",
"actions": [
{ "label": "Settings", "path": "/settings", "icon": "cog" },
{ "label": "Help", "path": "/help", "icon": "question" }
],
"userMenu": true
}
}
Usage in JSX
import { Header } from '@enterpriseaigroup/core/components';
function App() {
return (
<Header
logo="/logo.png"
title="My App"
actions={[
{ label: 'Dashboard', path: '/' },
{ label: 'Settings', path: '/settings' }
]}
/>
);
}
Sidebar
Collapsible sidebar navigation with menu items.
interface SidebarProps {
items: SidebarItem[];
collapsed?: boolean;
onCollapse?: (collapsed: boolean) => void;
footer?: React.ReactNode;
className?: string;
}
interface SidebarItem {
label: string;
path: string;
icon?: string;
badge?: string | number;
children?: SidebarItem[];
}
Example Configuration
{
"component": "Sidebar",
"props": {
"collapsed": false,
"items": [
{
"label": "Dashboard",
"path": "/",
"icon": "dashboard"
},
{
"label": "Projects",
"path": "/projects",
"icon": "folder",
"badge": 5
},
{
"label": "Settings",
"path": "/settings",
"icon": "cog",
"children": [
{ "label": "Profile", "path": "/settings/profile" },
{ "label": "Team", "path": "/settings/team" }
]
}
]
}
}
SlotRenderer
Renders child components within a layout slot.
interface SlotRendererProps {
children?: ComponentConfig[];
className?: string;
style?: React.CSSProperties;
}
Example Configuration
{
"component": "SlotRenderer",
"children": [
{
"component": "ResourceList",
"props": { "title": "Projects" }
},
{
"component": "ChatPanel",
"props": { "title": "AI Assistant" }
}
]
}
ResourceList
Displays a list or grid of resources with filtering and sorting.
interface ResourceListProps {
title?: string;
type: string;
variant?: 'list' | 'grid' | 'table';
columns?: number;
filter?: Record<string, any>;
sort?: SortOptions;
itemRenderer?: (item: any) => React.ReactNode;
emptyState?: React.ReactNode;
onItemClick?: (item: any) => void;
}
Example Configuration
{
"component": "ResourceList",
"props": {
"title": "Active Projects",
"type": "project",
"variant": "grid",
"columns": 3
},
"store": {
"binding": "resources.projects",
"filter": { "status": "active" },
"sort": { "field": "updatedAt", "order": "desc" }
}
}
ResourceDetail
Displays detailed view of a single resource.
interface ResourceDetailProps {
type: string;
id: string;
sections?: DetailSection[];
actions?: ActionItem[];
onEdit?: () => void;
onDelete?: () => void;
}
interface DetailSection {
title: string;
fields: DetailField[];
}
interface DetailField {
label: string;
key: string;
format?: 'text' | 'date' | 'currency' | 'badge';
}
Example Configuration
{
"component": "ResourceDetail",
"props": {
"type": "project",
"id": "proj_123",
"sections": [
{
"title": "Overview",
"fields": [
{ "label": "Name", "key": "name" },
{ "label": "Status", "key": "status", "format": "badge" },
{ "label": "Budget", "key": "budget", "format": "currency" }
]
}
]
}
}
EAIConfigProvider
Provider component that loads and applies configuration.
interface EAIConfigProviderProps {
config: EAIConfig;
children: React.ReactNode;
}
Usage
import { EAIConfigProvider } from '@enterpriseaigroup/core';
import config from './config.json';
function App() {
return (
<EAIConfigProvider config={config}>
{/* Your app components */}
</EAIConfigProvider>
);
}
DataTable
Advanced table component with sorting, filtering, and pagination.
interface DataTableProps {
data: any[];
columns: TableColumn[];
sortable?: boolean;
filterable?: boolean;
paginated?: boolean;
pageSize?: number;
onRowClick?: (row: any) => void;
}
interface TableColumn {
key: string;
label: string;
sortable?: boolean;
filterable?: boolean;
format?: (value: any) => string;
width?: string;
}
Example Configuration
{
"component": "DataTable",
"props": {
"columns": [
{ "key": "name", "label": "Project Name", "sortable": true },
{ "key": "status", "label": "Status", "sortable": true },
{ "key": "budget", "label": "Budget", "sortable": true },
{ "key": "owner", "label": "Owner" }
],
"sortable": true,
"filterable": true,
"paginated": true,
"pageSize": 20
},
"store": {
"binding": "resources.projects"
}
}
Registering Custom Components
Component Registration
import { registerComponent } from '@enterpriseaigroup/core';
// Define your component
function CustomWidget({ title, data }: CustomWidgetProps) {
return (
<div className="custom-widget">
<h2>{title}</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
// Register component
registerComponent('CustomWidget', CustomWidget);
Using in Configuration
{
"component": "CustomWidget",
"props": {
"title": "My Custom Widget",
"data": { "foo": "bar" }
}
}
Component Registration API
function registerComponent(
name: string,
component: React.ComponentType<any>
): void;
function getComponent(name: string): React.ComponentType<any> | undefined;
function listComponents(): string[];
Example: Custom Dashboard Card
import { registerComponent } from '@enterpriseaigroup/core';
import { useResources } from '@enterpriseaigroup/core';
interface DashboardCardProps {
title: string;
metric: string;
resourceType: string;
filter?: Record<string, any>;
}
function DashboardCard({ title, metric, resourceType, filter }: DashboardCardProps) {
const { data, loading } = useResources({
type: resourceType,
filter
});
const value = data?.length || 0;
return (
<div className="dashboard-card">
<h3>{title}</h3>
{loading ? (
<div>Loading...</div>
) : (
<div className="metric">
<span className="value">{value}</span>
<span className="label">{metric}</span>
</div>
)}
</div>
);
}
registerComponent('DashboardCard', DashboardCard);
Configuration
{
"component": "DashboardCard",
"props": {
"title": "Active Projects",
"metric": "Projects",
"resourceType": "project"
},
"store": {
"binding": "resources.projects",
"filter": { "status": "active" }
}
}
Component Props with Store Binding
Components can receive data from the store via the store binding:
interface ComponentWithStoreProps {
// Props from config
title: string;
// Data from store binding
data?: any[];
loading?: boolean;
error?: Error | null;
}
function ComponentWithStore({ title, data, loading, error }: ComponentWithStoreProps) {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>{title}</h2>
{data?.map(item => <div key={item.id}>{item.name}</div>)}
</div>
);
}
Conditional Rendering
Use the conditional property to conditionally render components:
{
"component": "AdminPanel",
"props": {
"title": "Admin Settings"
},
"conditional": {
"when": "user.role",
"operator": "eq",
"value": "admin",
"fallback": {
"component": "AccessDenied",
"props": {
"message": "Admin access required"
}
}
}
}
Component Styling
CSS Classes
Apply custom CSS classes via configuration:
{
"component": "ResourceList",
"props": {
"className": "custom-list fancy-borders"
}
}
Inline Styles
Apply inline styles via configuration:
{
"component": "ResourceList",
"props": {
"style": {
"padding": "20px",
"backgroundColor": "#f5f5f5",
"borderRadius": "8px"
}
}
}
Best Practices
- Keep components simple: Each component should have a single responsibility
- Use TypeScript: Define prop interfaces for type safety
- Document props: Include JSDoc comments for all props
- Handle loading states: Always show loading indicators
- Error boundaries: Wrap components in error boundaries
- Accessibility: Use semantic HTML and ARIA attributes
- Performance: Memoize expensive computations with
useMemo
Component Development Example
Complete example of a custom component:
import { registerComponent } from '@enterpriseaigroup/core';
import { useResources } from '@enterpriseaigroup/core';
import { useMemo } from 'react';
interface ProjectStatsProps {
title?: string;
showChart?: boolean;
className?: string;
}
/**
* Displays project statistics with optional chart visualization
*/
function ProjectStats({ title = 'Project Statistics', showChart = true, className }: ProjectStatsProps) {
const { data: projects, loading, error } = useResources({
type: 'project'
});
const stats = useMemo(() => {
if (!projects) return null;
return {
total: projects.length,
active: projects.filter(p => p.status === 'active').length,
completed: projects.filter(p => p.status === 'completed').length,
totalBudget: projects.reduce((sum, p) => sum + (p.budget || 0), 0)
};
}, [projects]);
if (loading) return <div className="loading">Loading statistics...</div>;
if (error) return <div className="error">Failed to load statistics</div>;
if (!stats) return null;
return (
<div className={`project-stats ${className || ''}`}>
<h2>{title}</h2>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-value">{stats.total}</div>
<div className="stat-label">Total Projects</div>
</div>
<div className="stat-card">
<div className="stat-value">{stats.active}</div>
<div className="stat-label">Active</div>
</div>
<div className="stat-card">
<div className="stat-value">{stats.completed}</div>
<div className="stat-label">Completed</div>
</div>
<div className="stat-card">
<div className="stat-value">${(stats.totalBudget / 1000000).toFixed(1)}M</div>
<div className="stat-label">Total Budget</div>
</div>
</div>
{showChart && <div className="chart">{/* Chart implementation */}</div>}
</div>
);
}
// Register component
registerComponent('ProjectStats', ProjectStats);
Next Steps
- Explore Interactive Tools
- Learn about Configuration Schema
- Read Component development guide
- See Styling guide