add details button on overview

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-05-20 09:45:30 +02:00
parent 4a7f21663f
commit 4350670bbc
13 changed files with 79 additions and 9 deletions

View File

@@ -0,0 +1,25 @@
import { pushModal } from '@/modals';
import { ScanEyeIcon } from 'lucide-react';
import type { IChartInput } from '@openpanel/validation';
type Props = {
chart: IChartInput;
};
const OverviewDetailsButton = ({ chart }: Props) => {
return (
<button
className="-mb-2 mt-5 flex w-full items-center justify-center gap-2 text-sm font-semibold"
onClick={() => {
pushModal('OverviewChartDetails', {
chart: chart,
});
}}
>
<ScanEyeIcon size={18} /> Details
</button>
);
};
export default OverviewDetailsButton;

View File

@@ -5,6 +5,7 @@ import { useEventQueryFilters } from '@/hooks/useEventQueryFilters';
import { cn } from '@/utils/cn'; import { cn } from '@/utils/cn';
import { Widget, WidgetBody } from '../../widget'; import { Widget, WidgetBody } from '../../widget';
import OverviewDetailsButton from '../overview-details-button';
import { WidgetButtons, WidgetHead } from '../overview-widget'; import { WidgetButtons, WidgetHead } from '../overview-widget';
import { useOverviewOptions } from '../useOverviewOptions'; import { useOverviewOptions } from '../useOverviewOptions';
import { useOverviewWidget } from '../useOverviewWidget'; import { useOverviewWidget } from '../useOverviewWidget';
@@ -121,6 +122,7 @@ export default function OverviewLatestEvents({
</WidgetHead> </WidgetHead>
<WidgetBody> <WidgetBody>
<LazyChart hideID {...widget.chart} previous={false} /> <LazyChart hideID {...widget.chart} previous={false} />
<OverviewDetailsButton chart={widget.chart} />
</WidgetBody> </WidgetBody>
</Widget> </Widget>
</> </>

View File

@@ -9,6 +9,7 @@ import type { IChartType } from '@openpanel/validation';
import { LazyChart } from '../report/chart/LazyChart'; import { LazyChart } from '../report/chart/LazyChart';
import { Widget, WidgetBody } from '../widget'; import { Widget, WidgetBody } from '../widget';
import { OverviewChartToggle } from './overview-chart-toggle'; import { OverviewChartToggle } from './overview-chart-toggle';
import OverviewDetailsButton from './overview-details-button';
import { WidgetButtons, WidgetHead } from './overview-widget'; import { WidgetButtons, WidgetHead } from './overview-widget';
import { useOverviewOptions } from './useOverviewOptions'; import { useOverviewOptions } from './useOverviewOptions';
import { useOverviewWidget } from './useOverviewWidget'; import { useOverviewWidget } from './useOverviewWidget';
@@ -222,6 +223,7 @@ export default function OverviewTopDevices({
} }
}} }}
/> />
<OverviewDetailsButton chart={widget.chart} />
</WidgetBody> </WidgetBody>
</Widget> </Widget>
</> </>

View File

@@ -1,17 +1,15 @@
'use client'; 'use client';
import { useState } from 'react'; import { useState } from 'react';
import { ChartSwitch } from '@/components/report/chart';
import { LazyChart } from '@/components/report/chart/LazyChart'; import { LazyChart } from '@/components/report/chart/LazyChart';
import { Button } from '@/components/ui/button';
import { useEventQueryFilters } from '@/hooks/useEventQueryFilters'; import { useEventQueryFilters } from '@/hooks/useEventQueryFilters';
import { cn } from '@/utils/cn'; import { cn } from '@/utils/cn';
import { BarChartIcon, LineChart, LineChartIcon } from 'lucide-react';
import type { IChartType } from '@openpanel/validation'; import type { IChartType } from '@openpanel/validation';
import { Widget, WidgetBody } from '../../widget'; import { Widget, WidgetBody } from '../../widget';
import { OverviewChartToggle } from '../overview-chart-toggle'; import { OverviewChartToggle } from '../overview-chart-toggle';
import OverviewDetailsButton from '../overview-details-button';
import { WidgetButtons, WidgetHead } from '../overview-widget'; import { WidgetButtons, WidgetHead } from '../overview-widget';
import { useOverviewOptions } from '../useOverviewOptions'; import { useOverviewOptions } from '../useOverviewOptions';
import { useOverviewWidget } from '../useOverviewWidget'; import { useOverviewWidget } from '../useOverviewWidget';
@@ -132,6 +130,7 @@ export default function OverviewTopEvents({
</WidgetHead> </WidgetHead>
<WidgetBody> <WidgetBody>
<LazyChart hideID {...widget.chart} previous={false} /> <LazyChart hideID {...widget.chart} previous={false} />
<OverviewDetailsButton chart={widget.chart} />
</WidgetBody> </WidgetBody>
</Widget> </Widget>
</> </>

View File

@@ -10,6 +10,7 @@ import type { IChartType } from '@openpanel/validation';
import { LazyChart } from '../report/chart/LazyChart'; import { LazyChart } from '../report/chart/LazyChart';
import { Widget, WidgetBody } from '../widget'; import { Widget, WidgetBody } from '../widget';
import { OverviewChartToggle } from './overview-chart-toggle'; import { OverviewChartToggle } from './overview-chart-toggle';
import OverviewDetailsButton from './overview-details-button';
import { WidgetButtons, WidgetHead } from './overview-widget'; import { WidgetButtons, WidgetHead } from './overview-widget';
import { useOverviewOptions } from './useOverviewOptions'; import { useOverviewOptions } from './useOverviewOptions';
import { useOverviewWidget } from './useOverviewWidget'; import { useOverviewWidget } from './useOverviewWidget';
@@ -157,6 +158,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
} }
}} }}
/> />
<OverviewDetailsButton chart={widget.chart} />
</WidgetBody> </WidgetBody>
</Widget> </Widget>
<Widget className="col-span-6 md:col-span-3"> <Widget className="col-span-6 md:col-span-3">

View File

@@ -9,6 +9,7 @@ import type { IChartType } from '@openpanel/validation';
import { LazyChart } from '../report/chart/LazyChart'; import { LazyChart } from '../report/chart/LazyChart';
import { Widget, WidgetBody } from '../widget'; import { Widget, WidgetBody } from '../widget';
import { OverviewChartToggle } from './overview-chart-toggle'; import { OverviewChartToggle } from './overview-chart-toggle';
import OverviewDetailsButton from './overview-details-button';
import OverviewTopBots from './overview-top-bots'; import OverviewTopBots from './overview-top-bots';
import { WidgetButtons, WidgetHead } from './overview-widget'; import { WidgetButtons, WidgetHead } from './overview-widget';
import { useOverviewOptions } from './useOverviewOptions'; import { useOverviewOptions } from './useOverviewOptions';
@@ -154,6 +155,7 @@ export default function OverviewTopPages({ projectId }: OverviewTopPagesProps) {
}} }}
/> />
)} )}
<OverviewDetailsButton chart={widget.chart} />
</WidgetBody> </WidgetBody>
</Widget> </Widget>
</> </>

View File

@@ -2,13 +2,16 @@
import { useState } from 'react'; import { useState } from 'react';
import { useEventQueryFilters } from '@/hooks/useEventQueryFilters'; import { useEventQueryFilters } from '@/hooks/useEventQueryFilters';
import { pushModal } from '@/modals';
import { cn } from '@/utils/cn'; import { cn } from '@/utils/cn';
import { ScanEyeIcon } from 'lucide-react';
import type { IChartType } from '@openpanel/validation'; import type { IChartType } from '@openpanel/validation';
import { LazyChart } from '../report/chart/LazyChart'; import { LazyChart } from '../report/chart/LazyChart';
import { Widget, WidgetBody } from '../widget'; import { Widget, WidgetBody } from '../widget';
import { OverviewChartToggle } from './overview-chart-toggle'; import { OverviewChartToggle } from './overview-chart-toggle';
import OverviewDetailsButton from './overview-details-button';
import { WidgetButtons, WidgetHead } from './overview-widget'; import { WidgetButtons, WidgetHead } from './overview-widget';
import { useOverviewOptions } from './useOverviewOptions'; import { useOverviewOptions } from './useOverviewOptions';
import { useOverviewWidget } from './useOverviewWidget'; import { useOverviewWidget } from './useOverviewWidget';
@@ -324,6 +327,7 @@ export default function OverviewTopSources({
} }
}} }}
/> />
<OverviewDetailsButton chart={widget.chart} />
</WidgetBody> </WidgetBody>
</Widget> </Widget>
</> </>

View File

@@ -20,7 +20,7 @@ export function WidgetHead({ className, ...props }: WidgetHeadProps) {
return ( return (
<WidgetHeadBase <WidgetHeadBase
className={cn( className={cn(
'flex flex-col rounded-t-xl p-0 [&_.title]:flex [&_.title]:items-center [&_.title]:justify-between [&_.title]:p-4', 'flex flex-col rounded-t-xl p-0 [&_.title]:flex [&_.title]:items-center [&_.title]:justify-between [&_.title]:p-4 [&_.title]:font-semibold',
className className
)} )}
{...props} {...props}

View File

@@ -20,6 +20,7 @@ export interface ChartContextType extends IChartInput {
editMode?: boolean; editMode?: boolean;
hideID?: boolean; hideID?: boolean;
onClick?: (item: IChartSerie) => void; onClick?: (item: IChartSerie) => void;
limit?: number;
} }
type ChartProviderProps = { type ChartProviderProps = {
@@ -37,6 +38,7 @@ const ChartContext = createContext<ChartContextType | null>({
metric: 'sum', metric: 'sum',
previous: false, previous: false,
projectId: '', projectId: '',
limit: undefined,
}); });
export function ChartProvider({ export function ChartProvider({
@@ -44,6 +46,7 @@ export function ChartProvider({
editMode, editMode,
previous, previous,
hideID, hideID,
limit,
...props ...props
}: ChartProviderProps) { }: ChartProviderProps) {
return ( return (
@@ -54,8 +57,9 @@ export function ChartProvider({
editMode: editMode ?? false, editMode: editMode ?? false,
previous: previous ?? false, previous: previous ?? false,
hideID: hideID ?? false, hideID: hideID ?? false,
limit,
}), }),
[editMode, previous, hideID, props] [editMode, previous, hideID, limit, props]
)} )}
> >
{children} {children}

View File

@@ -17,11 +17,11 @@ interface ReportBarChartProps {
} }
export function ReportBarChart({ data }: ReportBarChartProps) { export function ReportBarChart({ data }: ReportBarChartProps) {
const { editMode, metric, onClick } = useChartContext(); const { editMode, metric, onClick, limit } = useChartContext();
const number = useNumber(); const number = useNumber();
const series = useMemo( const series = useMemo(
() => (editMode ? data.series : data.series.slice(0, 10)), () => (editMode ? data.series : data.series.slice(0, limit || 10)),
[data, editMode] [data, editMode, limit]
); );
const maxCount = Math.max(...series.map((serie) => serie.metrics[metric])); const maxCount = Math.max(...series.map((serie) => serie.metrics[metric]));

View File

@@ -0,0 +1,25 @@
import { ChartSwitch } from '@/components/report/chart';
import { ScrollArea } from '@/components/ui/scroll-area';
import type { IChartInput } from '@openpanel/validation';
import { ModalContent, ModalHeader } from './Modal/Container';
type Props = {
chart: IChartInput;
};
const OverviewChartDetails = (props: Props) => {
return (
<ModalContent>
<ModalHeader title={props.chart.name} />
<ScrollArea className="-m-6 max-h-[calc(100vh-200px)]">
<div className="p-6">
<ChartSwitch {...props.chart} limit={999} chartType="bar" />
</div>
</ScrollArea>
</ModalContent>
);
};
export default OverviewChartDetails;

View File

@@ -62,6 +62,9 @@ const modals = {
DateRangerPicker: dynamic(() => import('./DateRangerPicker'), { DateRangerPicker: dynamic(() => import('./DateRangerPicker'), {
loading: Loading, loading: Loading,
}), }),
OverviewChartDetails: dynamic(() => import('./OverviewChartDetails'), {
loading: Loading,
}),
}; };
export const { export const {

View File

@@ -209,7 +209,9 @@ export const chartRouter = createTRPCRouter({
const final: FinalChart = { const final: FinalChart = {
events: input.events, events: input.events,
series: series.map((serie, index) => { series: series.map((serie, index) => {
const previousSerie = previousSeries?.[index]; const previousSerie = previousSeries?.find(
(item) => item.name === serie.name
);
const metrics = { const metrics = {
sum: sum(serie.data.map((item) => item.count)), sum: sum(serie.data.map((item) => item.count)),
average: round(average(serie.data.map((item) => item.count)), 2), average: round(average(serie.data.map((item) => item.count)), 2),