fix broken profile page #22
This commit is contained in:
@@ -1,31 +1,25 @@
|
|||||||
import { Suspense } from 'react';
|
import { start } from 'repl';
|
||||||
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import ClickToCopy from '@/components/click-to-copy';
|
import ClickToCopy from '@/components/click-to-copy';
|
||||||
import { ListPropertiesIcon } from '@/components/events/list-properties-icon';
|
import { ListPropertiesIcon } from '@/components/events/list-properties-icon';
|
||||||
import { ProfileAvatar } from '@/components/profiles/profile-avatar';
|
import { ProfileAvatar } from '@/components/profiles/profile-avatar';
|
||||||
import { ChartSwitch } from '@/components/report/chart';
|
|
||||||
import { Tooltiper } from '@/components/ui/tooltip';
|
|
||||||
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
|
|
||||||
import {
|
import {
|
||||||
eventQueryFiltersParser,
|
eventQueryFiltersParser,
|
||||||
eventQueryNamesFilter,
|
eventQueryNamesFilter,
|
||||||
} from '@/hooks/useEventQueryFilters';
|
} from '@/hooks/useEventQueryFilters';
|
||||||
import { clipboard } from '@/utils/clipboard';
|
|
||||||
import { getProfileName } from '@/utils/getters';
|
import { getProfileName } from '@/utils/getters';
|
||||||
import { CopyIcon } from 'lucide-react';
|
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { parseAsInteger, parseAsString } from 'nuqs';
|
import { parseAsInteger } from 'nuqs';
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
import type { GetEventListOptions } from '@openpanel/db';
|
import type { GetEventListOptions } from '@openpanel/db';
|
||||||
import { getEventList, getEventsCount, getProfileById } from '@openpanel/db';
|
import { getProfileById } from '@openpanel/db';
|
||||||
import type { IChartInput } from '@openpanel/validation';
|
|
||||||
|
|
||||||
import { EventList } from '../../events/event-list';
|
import EventListServer from '../../events/event-list';
|
||||||
import { StickyBelowHeader } from '../../layout-sticky-below-header';
|
import { StickyBelowHeader } from '../../layout-sticky-below-header';
|
||||||
import MostEventsServer from './most-events';
|
import MostEventsServer from './most-events';
|
||||||
import PopularRoutesServer from './popular-routes';
|
import PopularRoutesServer from './popular-routes';
|
||||||
import ProfileActivityServer from './profile-activity';
|
import ProfileActivityServer from './profile-activity';
|
||||||
|
import ProfileCharts from './profile-charts';
|
||||||
import ProfileMetrics from './profile-metrics';
|
import ProfileMetrics from './profile-metrics';
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
@@ -58,80 +52,8 @@ export default async function Page({
|
|||||||
eventQueryFiltersParser.parseServerSide(searchParams.f ?? '') ??
|
eventQueryFiltersParser.parseServerSide(searchParams.f ?? '') ??
|
||||||
undefined,
|
undefined,
|
||||||
};
|
};
|
||||||
const startDate = parseAsString.parseServerSide(searchParams.startDate);
|
|
||||||
const endDate = parseAsString.parseServerSide(searchParams.endDate);
|
|
||||||
const profile = await getProfileById(profileId, projectId);
|
const profile = await getProfileById(profileId, projectId);
|
||||||
|
|
||||||
const pageViewsChart: IChartInput = {
|
|
||||||
projectId,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
chartType: 'linear',
|
|
||||||
events: [
|
|
||||||
{
|
|
||||||
segment: 'event',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
id: 'profile_id',
|
|
||||||
name: 'profile_id',
|
|
||||||
operator: 'is',
|
|
||||||
value: [profileId],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
id: 'A',
|
|
||||||
name: '*',
|
|
||||||
displayName: 'Events',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
breakdowns: [
|
|
||||||
{
|
|
||||||
id: 'path',
|
|
||||||
name: 'path',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
lineType: 'monotone',
|
|
||||||
interval: 'day',
|
|
||||||
name: 'Events',
|
|
||||||
range: '30d',
|
|
||||||
previous: false,
|
|
||||||
metric: 'sum',
|
|
||||||
};
|
|
||||||
|
|
||||||
const eventsChart: IChartInput = {
|
|
||||||
projectId,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
chartType: 'linear',
|
|
||||||
events: [
|
|
||||||
{
|
|
||||||
segment: 'event',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
id: 'profile_id',
|
|
||||||
name: 'profile_id',
|
|
||||||
operator: 'is',
|
|
||||||
value: [profileId],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
id: 'A',
|
|
||||||
name: '*',
|
|
||||||
displayName: 'Events',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
breakdowns: [
|
|
||||||
{
|
|
||||||
id: 'name',
|
|
||||||
name: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
lineType: 'monotone',
|
|
||||||
interval: 'day',
|
|
||||||
name: 'Events',
|
|
||||||
range: '30d',
|
|
||||||
previous: false,
|
|
||||||
metric: 'sum',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
@@ -169,37 +91,13 @@ export default async function Page({
|
|||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<PopularRoutesServer profileId={profileId} projectId={projectId} />
|
<PopularRoutesServer profileId={profileId} projectId={projectId} />
|
||||||
</div>
|
</div>
|
||||||
<Widget className="col-span-3 w-full">
|
|
||||||
<WidgetHead>
|
<ProfileCharts profileId={profileId} projectId={projectId} />
|
||||||
<span className="title">Page views</span>
|
|
||||||
</WidgetHead>
|
|
||||||
<WidgetBody className="flex gap-2">
|
|
||||||
<ChartSwitch {...pageViewsChart} />
|
|
||||||
</WidgetBody>
|
|
||||||
</Widget>
|
|
||||||
<Widget className="col-span-3 w-full">
|
|
||||||
<WidgetHead>
|
|
||||||
<span className="title">Events per day</span>
|
|
||||||
</WidgetHead>
|
|
||||||
<WidgetBody className="flex gap-2">
|
|
||||||
<ChartSwitch {...eventsChart} />
|
|
||||||
</WidgetBody>
|
|
||||||
</Widget>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<Suspense fallback={<div />}>
|
<EventListServer {...eventListOptions} />
|
||||||
<EventListServer {...eventListOptions} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function EventListServer(props: GetEventListOptions) {
|
|
||||||
const [events, count] = await Promise.all([
|
|
||||||
getEventList(props),
|
|
||||||
getEventsCount(props),
|
|
||||||
]);
|
|
||||||
return <EventList data={events} count={count} />;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { ChartSwitch } from '@/components/report/chart';
|
||||||
|
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
|
||||||
|
|
||||||
|
import type { IChartInput } from '@openpanel/validation';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
profileId: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProfileCharts = ({ profileId, projectId }: Props) => {
|
||||||
|
const pageViewsChart: IChartInput = {
|
||||||
|
projectId,
|
||||||
|
chartType: 'linear',
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
segment: 'event',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
id: 'profile_id',
|
||||||
|
name: 'profile_id',
|
||||||
|
operator: 'is',
|
||||||
|
value: [profileId],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: 'A',
|
||||||
|
name: '*',
|
||||||
|
displayName: 'Events',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
breakdowns: [
|
||||||
|
{
|
||||||
|
id: 'path',
|
||||||
|
name: 'path',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lineType: 'monotone',
|
||||||
|
interval: 'day',
|
||||||
|
name: 'Events',
|
||||||
|
range: '30d',
|
||||||
|
previous: false,
|
||||||
|
metric: 'sum',
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventsChart: IChartInput = {
|
||||||
|
projectId,
|
||||||
|
chartType: 'linear',
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
segment: 'event',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
id: 'profile_id',
|
||||||
|
name: 'profile_id',
|
||||||
|
operator: 'is',
|
||||||
|
value: [profileId],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: 'A',
|
||||||
|
name: '*',
|
||||||
|
displayName: 'Events',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
breakdowns: [
|
||||||
|
{
|
||||||
|
id: 'name',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lineType: 'monotone',
|
||||||
|
interval: 'day',
|
||||||
|
name: 'Events',
|
||||||
|
range: '30d',
|
||||||
|
previous: false,
|
||||||
|
metric: 'sum',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Widget className="col-span-3 w-full">
|
||||||
|
<WidgetHead>
|
||||||
|
<span className="title">Page views</span>
|
||||||
|
</WidgetHead>
|
||||||
|
<WidgetBody className="flex gap-2">
|
||||||
|
<ChartSwitch {...pageViewsChart} />
|
||||||
|
</WidgetBody>
|
||||||
|
</Widget>
|
||||||
|
<Widget className="col-span-3 w-full">
|
||||||
|
<WidgetHead>
|
||||||
|
<span className="title">Events per day</span>
|
||||||
|
</WidgetHead>
|
||||||
|
<WidgetBody className="flex gap-2">
|
||||||
|
<ChartSwitch {...eventsChart} />
|
||||||
|
</WidgetBody>
|
||||||
|
</Widget>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// No clue why I need to check for equality here
|
||||||
|
export default memo(ProfileCharts, (a, b) => {
|
||||||
|
return a.profileId === b.profileId && a.projectId === b.projectId;
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user