feat(dashboard): add quick filter for origin
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useAppParams } from '@/hooks/useAppParams';
|
||||
import { useEventQueryFilters } from '@/hooks/useEventQueryFilters';
|
||||
import { api } from '@/trpc/client';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { GlobeIcon } from 'lucide-react';
|
||||
|
||||
export function OriginFilter() {
|
||||
const { projectId } = useAppParams();
|
||||
const [filters, setFilter] = useEventQueryFilters();
|
||||
const originFilter = filters.find((item) => item.name === 'origin');
|
||||
|
||||
const { data } = api.event.origin.useQuery(
|
||||
{
|
||||
projectId: projectId,
|
||||
},
|
||||
{
|
||||
staleTime: 1000 * 60 * 60,
|
||||
}
|
||||
);
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{data?.map((item) => {
|
||||
return (
|
||||
<Button
|
||||
key={item.origin}
|
||||
variant="outline"
|
||||
icon={GlobeIcon}
|
||||
className={cn(
|
||||
originFilter?.value.includes(item.origin) && 'border-foreground'
|
||||
)}
|
||||
onClick={() => setFilter('origin', [item.origin], 'is')}
|
||||
>
|
||||
{item.origin}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { useProfileProperties } from '@/hooks/useProfileProperties';
|
||||
import { useProfileValues } from '@/hooks/useProfileValues';
|
||||
import { usePropertyValues } from '@/hooks/usePropertyValues';
|
||||
import { XIcon } from 'lucide-react';
|
||||
import { GlobeIcon, XIcon } from 'lucide-react';
|
||||
import type { Options as NuqsOptions } from 'nuqs';
|
||||
|
||||
import type {
|
||||
@@ -22,6 +22,7 @@ import type {
|
||||
} from '@openpanel/validation';
|
||||
|
||||
import { useOverviewOptions } from '../useOverviewOptions';
|
||||
import { OriginFilter } from './origin-filter';
|
||||
|
||||
export interface OverviewFiltersDrawerContentProps {
|
||||
projectId: string;
|
||||
@@ -52,6 +53,7 @@ export function OverviewFiltersDrawerContent({
|
||||
|
||||
<div className="mt-8 flex flex-col rounded-md border bg-def-100">
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<OriginFilter />
|
||||
{enableEventsFilter && (
|
||||
<ComboboxAdvanced
|
||||
className="w-full"
|
||||
@@ -106,10 +108,8 @@ export function OverviewFiltersDrawerContent({
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
) : (
|
||||
/* TODO: Implement profile filters */
|
||||
null
|
||||
);
|
||||
) : /* TODO: Implement profile filters */
|
||||
null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { api } from '@/trpc/client';
|
||||
import debounce from 'lodash.debounce';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
import type { IChartProps } from '@openpanel/validation';
|
||||
|
||||
@@ -18,94 +19,51 @@ import { ReportPieChart } from './ReportPieChart';
|
||||
|
||||
export type ReportChartProps = IChartProps;
|
||||
|
||||
const pluckChartContext = (context: IChartProps) => ({
|
||||
chartType: context.chartType,
|
||||
interval: context.interval,
|
||||
breakdowns: context.breakdowns,
|
||||
range: context.range,
|
||||
previous: context.previous,
|
||||
formula: context.formula,
|
||||
metric: context.metric,
|
||||
projectId: context.projectId,
|
||||
startDate: context.startDate,
|
||||
endDate: context.endDate,
|
||||
limit: context.limit,
|
||||
offset: context.offset,
|
||||
events: context.events.map((event) => ({
|
||||
...event,
|
||||
filters: event.filters?.filter((filter) => filter.value.length > 0),
|
||||
})),
|
||||
});
|
||||
|
||||
// TODO: Quick hack to avoid re-fetching
|
||||
// Will refactor the entire chart component soon anyway...
|
||||
function useChartData() {
|
||||
const {
|
||||
interval,
|
||||
events,
|
||||
breakdowns,
|
||||
chartType,
|
||||
range,
|
||||
previous,
|
||||
formula,
|
||||
metric,
|
||||
projectId,
|
||||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
offset,
|
||||
} = useChartContext();
|
||||
|
||||
const [debouncedParams, setDebouncedParams] = useState({
|
||||
interval,
|
||||
events,
|
||||
breakdowns,
|
||||
chartType,
|
||||
range,
|
||||
previous,
|
||||
formula,
|
||||
metric,
|
||||
projectId,
|
||||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
|
||||
const debouncedSetParams = useMemo(
|
||||
() => debounce(setDebouncedParams, 500),
|
||||
[]
|
||||
);
|
||||
const context = useChartContext();
|
||||
const [params, setParams] = useState(() => pluckChartContext(context));
|
||||
const debouncedSetParams = useMemo(() => debounce(setParams, 500), []);
|
||||
|
||||
useEffect(() => {
|
||||
debouncedSetParams({
|
||||
interval,
|
||||
events: events.map((event) => ({
|
||||
...event,
|
||||
filters: event.filters?.filter((filter) => filter.value.length > 0),
|
||||
})),
|
||||
breakdowns,
|
||||
chartType,
|
||||
range,
|
||||
previous,
|
||||
formula,
|
||||
metric,
|
||||
projectId,
|
||||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
const newParams = pluckChartContext(context);
|
||||
if (!isEqual(newParams, params)) {
|
||||
debouncedSetParams(newParams);
|
||||
}
|
||||
return () => {
|
||||
debouncedSetParams.cancel();
|
||||
};
|
||||
}, [
|
||||
interval,
|
||||
events,
|
||||
breakdowns,
|
||||
chartType,
|
||||
range,
|
||||
previous,
|
||||
formula,
|
||||
metric,
|
||||
projectId,
|
||||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
offset,
|
||||
debouncedSetParams,
|
||||
]);
|
||||
}, [context, params, debouncedSetParams]);
|
||||
|
||||
const [data] = api.chart.chart.useSuspenseQuery(debouncedParams, {
|
||||
return api.chart.chart.useSuspenseQuery(params, {
|
||||
keepPreviousData: true,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export function Chart() {
|
||||
const { chartType } = useChartContext();
|
||||
const data = useChartData();
|
||||
const [data] = useChartData();
|
||||
|
||||
if (data.series.length === 0) {
|
||||
return <ChartEmpty />;
|
||||
|
||||
@@ -169,7 +169,10 @@ const DropdownMenuShortcut = ({
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn('ml-auto text-sm tracking-widest opacity-60', className)}
|
||||
className={cn(
|
||||
'ml-auto font-mono text-sm tracking-widest opacity-60',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user