fix: improvements for frontend
This commit is contained in:
@@ -23,6 +23,7 @@ import EditReport from './edit-report';
|
||||
import EventDetails from './event-details';
|
||||
import OnboardingTroubleshoot from './onboarding-troubleshoot';
|
||||
import OverviewChartDetails from './overview-chart-details';
|
||||
import OverviewFilters from './overview-filters';
|
||||
import RequestPasswordReset from './request-reset-password';
|
||||
import SaveReport from './save-report';
|
||||
import ShareOverviewModal from './share-overview-modal';
|
||||
@@ -52,6 +53,7 @@ const modals = {
|
||||
OverviewChartDetails: OverviewChartDetails,
|
||||
AddIntegration: AddIntegration,
|
||||
AddNotificationRule: AddNotificationRule,
|
||||
OverviewFilters: OverviewFilters,
|
||||
CreateInvite: CreateInvite,
|
||||
};
|
||||
|
||||
|
||||
157
apps/start/src/modals/overview-filters.tsx
Normal file
157
apps/start/src/modals/overview-filters.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import { PureFilterItem } from '@/components/report/sidebar/filters/FilterItem';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { SheetContent } from '@/components/ui/sheet';
|
||||
import { useEventNames } from '@/hooks/use-event-names';
|
||||
import {
|
||||
useEventQueryFilters,
|
||||
useEventQueryNamesFilter,
|
||||
} from '@/hooks/use-event-query-filters';
|
||||
import { useProfileValues } from '@/hooks/use-profile-values';
|
||||
import { FilterIcon, XIcon } from 'lucide-react';
|
||||
import type { Options as NuqsOptions } from 'nuqs';
|
||||
|
||||
import type {
|
||||
IChartEventFilter,
|
||||
IChartEventFilterOperator,
|
||||
IChartEventFilterValue,
|
||||
} from '@openpanel/validation';
|
||||
|
||||
import { OriginFilter } from '@/components/overview/filters/origin-filter';
|
||||
import { PropertiesCombobox } from '@/components/report/sidebar/PropertiesCombobox';
|
||||
import { ComboboxEvents } from '@/components/ui/combobox-events';
|
||||
import { useAppParams } from '@/hooks/use-app-params';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { ModalHeader } from './Modal/Container';
|
||||
|
||||
export interface OverviewFiltersProps {
|
||||
nuqsOptions?: NuqsOptions;
|
||||
enableEventsFilter?: boolean;
|
||||
mode?: 'events' | 'profile';
|
||||
}
|
||||
|
||||
export default function OverviewFilters({
|
||||
nuqsOptions,
|
||||
enableEventsFilter,
|
||||
mode,
|
||||
}: OverviewFiltersProps) {
|
||||
const { projectId } = useAppParams();
|
||||
const [filters, setFilter] = useEventQueryFilters(nuqsOptions);
|
||||
const [event, setEvent] = useEventQueryNamesFilter(nuqsOptions);
|
||||
const eventNames = useEventNames({ projectId });
|
||||
const selectedFilters = filters.filter((filter) => filter.value[0] !== null);
|
||||
return (
|
||||
<SheetContent className="[&>button.absolute]:hidden">
|
||||
<ModalHeader title="Filters" />
|
||||
<div className="flex flex-col gap-4">
|
||||
<OriginFilter />
|
||||
{enableEventsFilter && (
|
||||
<ComboboxEvents
|
||||
size="lg"
|
||||
className="w-full"
|
||||
value={event}
|
||||
onChange={setEvent}
|
||||
multiple
|
||||
items={eventNames}
|
||||
placeholder="Select event"
|
||||
maxDisplayItems={2}
|
||||
searchable
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div
|
||||
className={cn(
|
||||
'bg-def-200 rounded-lg border',
|
||||
selectedFilters.length === 0 && 'hidden',
|
||||
)}
|
||||
>
|
||||
{selectedFilters.map((filter) => {
|
||||
return (
|
||||
<PureFilterItem
|
||||
className="border-t p-4 first:border-0"
|
||||
eventName="screen_view"
|
||||
key={filter.name}
|
||||
filter={filter}
|
||||
onRemove={() => {
|
||||
setFilter(filter.name, [], filter.operator);
|
||||
}}
|
||||
onChangeValue={(value) => {
|
||||
setFilter(filter.name, value, filter.operator);
|
||||
}}
|
||||
onChangeOperator={(operator) => {
|
||||
setFilter(filter.name, filter.value, operator);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<PropertiesCombobox
|
||||
mode={mode}
|
||||
exclude={[
|
||||
'properties.*',
|
||||
'name',
|
||||
'duration',
|
||||
'created_at',
|
||||
'has_profile',
|
||||
]}
|
||||
onSelect={(action) => {
|
||||
setFilter(action.value, [], 'is');
|
||||
}}
|
||||
>
|
||||
{(setOpen) => (
|
||||
<Button
|
||||
onClick={() => setOpen((p) => !p)}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="w-full"
|
||||
icon={FilterIcon}
|
||||
>
|
||||
Add filter
|
||||
</Button>
|
||||
)}
|
||||
</PropertiesCombobox>
|
||||
</div>
|
||||
</SheetContent>
|
||||
);
|
||||
}
|
||||
|
||||
export function FilterOptionProfile({
|
||||
setFilter,
|
||||
projectId,
|
||||
...filter
|
||||
}: IChartEventFilter & {
|
||||
projectId: string;
|
||||
setFilter: (
|
||||
name: string,
|
||||
value: IChartEventFilterValue,
|
||||
operator: IChartEventFilterOperator,
|
||||
) => void;
|
||||
}) {
|
||||
const values = useProfileValues(projectId, filter.name);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<div>{filter.name}</div>
|
||||
<Combobox
|
||||
className="flex-1"
|
||||
onChange={(value) => setFilter(filter.name, value, filter.operator)}
|
||||
placeholder={'Select a value'}
|
||||
items={values.map((value) => ({
|
||||
value,
|
||||
label: value,
|
||||
}))}
|
||||
value={String(filter.value[0] ?? '')}
|
||||
/>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
setFilter(filter.name, filter.value[0] ?? '', filter.operator)
|
||||
}
|
||||
>
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { useAppParams } from '@/hooks/use-app-params';
|
||||
import { handleError } from '@/integrations/trpc/react';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useRouter } from '@tanstack/react-router';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
import type { z } from 'zod';
|
||||
@@ -22,6 +22,7 @@ type IForm = z.infer<typeof validator>;
|
||||
|
||||
export default function ShareOverviewModal() {
|
||||
const { projectId, organizationId } = useAppParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { register, handleSubmit } = useForm<IForm>({
|
||||
resolver: zodResolver(validator),
|
||||
@@ -44,6 +45,16 @@ export default function ShareOverviewModal() {
|
||||
description: `Your overview is now ${
|
||||
res.public ? 'public' : 'private'
|
||||
}`,
|
||||
action: {
|
||||
label: 'View',
|
||||
onClick: () =>
|
||||
navigate({
|
||||
to: '/share/overview/$shareId',
|
||||
params: {
|
||||
shareId: res.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
popModal();
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user