feat(ai): add ai chat to dashboard

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-04-15 14:30:21 +02:00
parent 804a9c8056
commit 34769a5d58
46 changed files with 2624 additions and 1449 deletions

View File

@@ -14,7 +14,7 @@ import {
} from 'lucide-react';
import { chartTypes } from '@openpanel/constants';
import { objectToZodEnums } from '@openpanel/validation';
import { type IChartType, objectToZodEnums } from '@openpanel/validation';
import {
DropdownMenu,
@@ -32,10 +32,14 @@ import { changeChartType } from './reportSlice';
interface ReportChartTypeProps {
className?: string;
value: IChartType;
onChange: (type: IChartType) => void;
}
export function ReportChartType({ className }: ReportChartTypeProps) {
const dispatch = useDispatch();
const type = useSelector((state) => state.report.chartType);
export function ReportChartType({
className,
value,
onChange,
}: ReportChartTypeProps) {
const items = objectToZodEnums(chartTypes).map((key) => ({
label: chartTypes[key],
value: key,
@@ -61,10 +65,10 @@ export function ReportChartType({ className }: ReportChartTypeProps) {
<DropdownMenuTrigger asChild>
<Button
variant="outline"
icon={Icons[type]}
icon={Icons[value]}
className={cn('justify-start', className)}
>
{items.find((item) => item.value === type)?.label}
{items.find((item) => item.value === value)?.label}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
@@ -77,7 +81,7 @@ export function ReportChartType({ className }: ReportChartTypeProps) {
return (
<DropdownMenuItem
key={item.value}
onClick={() => dispatch(changeChartType(item.value))}
onClick={() => onChange(item.value)}
className="group"
>
{item.label}

View File

@@ -6,17 +6,36 @@ import {
isMinuteIntervalEnabledByRange,
} from '@openpanel/constants';
import { Combobox } from '../ui/combobox';
import { cn } from '@/utils/cn';
import type { IChartRange, IChartType, IInterval } from '@openpanel/validation';
import { Button } from '../ui/button';
import { CommandShortcut } from '../ui/command';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from '../ui/dropdown-menu';
import { changeInterval } from './reportSlice';
interface ReportIntervalProps {
className?: string;
interval: IInterval;
onChange: (range: IInterval) => void;
chartType: IChartType;
range: IChartRange;
}
export function ReportInterval({ className }: ReportIntervalProps) {
const dispatch = useDispatch();
const interval = useSelector((state) => state.report.interval);
const range = useSelector((state) => state.report.range);
const chartType = useSelector((state) => state.report.chartType);
export function ReportInterval({
className,
interval,
onChange,
chartType,
range,
}: ReportIntervalProps) {
if (
chartType !== 'linear' &&
chartType !== 'histogram' &&
@@ -28,37 +47,66 @@ export function ReportInterval({ className }: ReportIntervalProps) {
return null;
}
const items = [
{
value: 'minute',
label: 'Minute',
disabled: !isMinuteIntervalEnabledByRange(range),
},
{
value: 'hour',
label: 'Hour',
disabled: !isHourIntervalEnabledByRange(range),
},
{
value: 'day',
label: 'Day',
},
{
value: 'month',
label: 'Month',
disabled: range === 'today' || range === 'lastHour' || range === '30min',
},
];
const selectedItem = items.find((item) => item.value === interval);
return (
<Combobox
icon={ClockIcon}
className={className}
placeholder="Interval"
onChange={(value) => {
dispatch(changeInterval(value));
}}
value={interval}
items={[
{
value: 'minute',
label: 'Minute',
disabled: !isMinuteIntervalEnabledByRange(range),
},
{
value: 'hour',
label: 'Hour',
disabled: !isHourIntervalEnabledByRange(range),
},
{
value: 'day',
label: 'Day',
},
{
value: 'month',
label: 'Month',
disabled:
range === 'today' || range === 'lastHour' || range === '30min',
},
]}
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
icon={ClockIcon}
className={cn('justify-start', className)}
>
{items.find((item) => item.value === interval)?.label || 'Interval'}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuLabel className="row items-center justify-between">
Select interval
{!!selectedItem && (
<CommandShortcut>{selectedItem?.label}</CommandShortcut>
)}
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
{items.map((item) => (
<DropdownMenuItem
key={item.value}
onClick={() => onChange(item.value as IInterval)}
disabled={item.disabled}
>
{item.label}
{item.value === interval && (
<DropdownMenuShortcut>
<ClockIcon className="size-4" />
</DropdownMenuShortcut>
)}
</DropdownMenuItem>
))}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}