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); }