Add board component selector with live preview

Allows users to select and inject analytics components into board cells.
Includes component registry, renderer, selector modal with category
menu, config fields for MetricsTable, and live preview. Also scopes
website select to team websites when editing a team board.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mike Cao 2026-02-09 00:52:46 -08:00
parent 28246b8c52
commit 87bde9da1f
10 changed files with 498 additions and 113 deletions

View file

@ -1,21 +1,43 @@
import { Box, Button, Column, Icon, Tooltip, TooltipTrigger } from '@umami/react-zen';
import type { ReactElement } from 'react';
import { Plus, X } from '@/components/icons';
import {
Box,
Button,
Column,
Dialog,
Icon,
Modal,
Tooltip,
TooltipTrigger,
} from '@umami/react-zen';
import { useState } from 'react';
import { useBoard } from '@/components/hooks';
import { Pencil, Plus, X } from '@/components/icons';
import type { BoardComponentConfig } from '@/lib/types';
import { BoardComponentRenderer } from './BoardComponentRenderer';
import { BoardComponentSelect } from './BoardComponentSelect';
export function BoardColumn({
id,
component,
editing = false,
onRemove,
onSetComponent,
canRemove = true,
}: {
id: string;
component?: ReactElement;
component?: BoardComponentConfig;
editing?: boolean;
onRemove?: (id: string) => void;
onSetComponent?: (id: string, config: BoardComponentConfig | null) => void;
canRemove?: boolean;
}) {
const handleAddComponent = () => {};
const [showSelect, setShowSelect] = useState(false);
const { board } = useBoard();
const websiteId = board?.parameters?.websiteId;
const handleSelect = (config: BoardComponentConfig) => {
onSetComponent?.(id, config);
setShowSelect(false);
};
return (
<Column
@ -40,13 +62,52 @@ export function BoardColumn({
</TooltipTrigger>
</Box>
)}
{editing && (
<Button variant="outline" onPress={handleAddComponent}>
<Icon>
<Plus />
</Icon>
</Button>
{component && websiteId ? (
<>
<Box width="100%" height="100%" overflow="auto">
<BoardComponentRenderer config={component} websiteId={websiteId} />
</Box>
{editing && (
<Box position="absolute" bottom="10px" right="20px" zIndex={100}>
<TooltipTrigger delay={0}>
<Button variant="quiet" onPress={() => setShowSelect(true)}>
<Icon size="sm">
<Pencil />
</Icon>
</Button>
<Tooltip>Change component</Tooltip>
</TooltipTrigger>
</Box>
)}
</>
) : (
editing && (
<Button variant="outline" onPress={() => setShowSelect(true)}>
<Icon>
<Plus />
</Icon>
</Button>
)
)}
<Modal isOpen={showSelect} onOpenChange={setShowSelect}>
<Dialog
title="Add component"
style={{
width: '750px',
maxWidth: 'calc(100vw - 40px)',
maxHeight: 'calc(100dvh - 40px)',
padding: '32px',
}}
>
{() => (
<BoardComponentSelect
websiteId={websiteId}
onSelect={handleSelect}
onClose={() => setShowSelect(false)}
/>
)}
</Dialog>
</Modal>
</Column>
);
}