diff --git a/src/app/(main)/boards/[boardId]/BoardComponentSelect.tsx b/src/app/(main)/boards/[boardId]/BoardComponentSelect.tsx
index 4ab7fe0f2..67d0227b6 100644
--- a/src/app/(main)/boards/[boardId]/BoardComponentSelect.tsx
+++ b/src/app/(main)/boards/[boardId]/BoardComponentSelect.tsx
@@ -24,10 +24,9 @@ import {
isOpenBoardType,
} from '@/lib/boards';
import {
- CATEGORIES,
type ComponentDefinition,
type ConfigField,
- getComponentsByCategory,
+ getComponentDefinitions,
} from '../boardComponentRegistry';
import { BoardComponentRenderer } from './BoardComponentRenderer';
@@ -62,7 +61,7 @@ export function BoardComponentSelect({
const [description, setDescription] = useState('');
const allDefinitions = useMemo(
- () => CATEGORIES.flatMap(category => getComponentsByCategory(category.key)),
+ () => getComponentDefinitions().toSorted((a, b) => a.name.localeCompare(b.name)),
[],
);
const activeEntityType = isOpenBoardType(boardType) ? selectedEntityType : boardEntityType;
@@ -189,6 +188,14 @@ export function BoardComponentSelect({
: null;
const canSave = !!selectedDef && isSelectedDefSupported && (!needsWebsite || !!resolvedEntityId);
+ const availableDefinitions = useMemo(
+ () =>
+ allDefinitions.filter(
+ def =>
+ isBoardComponentSupported(def.type, activeEntityType) || def.type === selectedDef?.type,
+ ),
+ [activeEntityType, allDefinitions, selectedDef?.type],
+ );
return (
@@ -310,48 +317,38 @@ export function BoardComponentSelect({
{hasSelectedEntity ? (
- {CATEGORIES.map(category => {
- const components = getComponentsByCategory(category.key).filter(def =>
- isBoardComponentSupported(def.type, activeEntityType) ||
- def.type === selectedDef?.type,
- );
-
- if (!components.length) {
- return null;
- }
+ {availableDefinitions.map(def => {
+ const Icon = def.icon;
return (
-
- {category.name}
- {components.map(def => (
-
- handleSelectComponent(def)}
+
+ handleSelectComponent(def)}
+ >
+
+
+
-
-
- {def.name}
-
-
- {def.description}
-
-
-
-
- ))}
-
+ {def.name}
+
+
+ {def.description}
+
+
+
+
);
})}
diff --git a/src/app/(main)/boards/boardComponentRegistry.tsx b/src/app/(main)/boards/boardComponentRegistry.tsx
index 1d7222c60..9bb39d71f 100644
--- a/src/app/(main)/boards/boardComponentRegistry.tsx
+++ b/src/app/(main)/boards/boardComponentRegistry.tsx
@@ -2,6 +2,14 @@ import type { ComponentType } from 'react';
import { TextBlock } from '@/app/(main)/boards/TextBlock';
import { WebsiteChart } from '@/app/(main)/websites/[websiteId]/WebsiteChart';
import { WebsiteMetricsBar } from '@/app/(main)/websites/[websiteId]/WebsiteMetricsBar';
+import {
+ Activity,
+ Calendar,
+ ChartPie,
+ FileText,
+ Globe,
+ Sheet,
+} from '@/components/icons';
import { EventsChart } from '@/components/metrics/EventsChart';
import { MetricsTable } from '@/components/metrics/MetricsTable';
import { WeeklyTraffic } from '@/components/metrics/WeeklyTraffic';
@@ -20,6 +28,7 @@ export interface ComponentDefinition {
name: string;
description: string;
category: string;
+ icon: ComponentType;
component: ComponentType;
defaultProps?: Record;
configFields?: ConfigField[];
@@ -66,6 +75,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'Metrics bar',
description: 'Key metrics: views, visitors, bounces, time on site',
category: 'overview',
+ icon: ChartPie,
component: WebsiteMetricsBar,
},
{
@@ -73,6 +83,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'Website chart',
description: 'Page views and visitors over time',
category: 'overview',
+ icon: Activity,
component: WebsiteChart,
},
@@ -82,6 +93,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'Metrics table',
description: 'Table of metrics by dimension',
category: 'tables',
+ icon: Sheet,
component: MetricsTable,
defaultProps: { type: 'path', limit: 10 },
configFields: [
@@ -108,6 +120,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'World map',
description: 'Geographic distribution of visitors',
category: 'visualization',
+ icon: Globe,
component: WorldMap,
},
{
@@ -115,6 +128,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'Weekly traffic',
description: 'Traffic heatmap by day and hour',
category: 'visualization',
+ icon: Calendar,
component: WeeklyTraffic,
},
{
@@ -122,6 +136,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'Events chart',
description: 'Custom events over time',
category: 'visualization',
+ icon: ChartPie,
component: EventsChart,
},
@@ -131,6 +146,7 @@ const componentDefinitions: ComponentDefinition[] = [
name: 'Text',
description: 'Free-form text content',
category: 'content',
+ icon: FileText,
component: TextBlock,
requiresWebsite: false,
defaultProps: { text: '' },
@@ -147,6 +163,10 @@ const componentDefinitions: ComponentDefinition[] = [
const definitionMap = new Map(componentDefinitions.map(def => [def.type, def]));
+export function getComponentDefinitions(): ComponentDefinition[] {
+ return componentDefinitions;
+}
+
export function getComponentDefinition(type: string): ComponentDefinition | undefined {
return definitionMap.get(type);
}