Skip to main content

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

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' }
]}
/>
);
}

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

  1. Keep components simple: Each component should have a single responsibility
  2. Use TypeScript: Define prop interfaces for type safety
  3. Document props: Include JSDoc comments for all props
  4. Handle loading states: Always show loading indicators
  5. Error boundaries: Wrap components in error boundaries
  6. Accessibility: Use semantic HTML and ARIA attributes
  7. 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