refactor(dashboard): the chart component is now cleaned up and easier to extend
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||
import { FullPageEmptyState } from '@/components/full-page-empty-state';
|
||||
import { useOverviewOptions } from '@/components/overview/useOverviewOptions';
|
||||
import { LazyChart } from '@/components/report/chart/LazyChart';
|
||||
import { ReportChart } from '@/components/report-chart';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -133,17 +132,22 @@ export function ListReports({ reports, dashboard }: ListReportsProps) {
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
<div className={cn('p-4')}>
|
||||
<LazyChart
|
||||
<div
|
||||
className={cn('p-4', report.chartType === 'metric' && 'p-0')}
|
||||
>
|
||||
<ReportChart
|
||||
{...report}
|
||||
range={range ?? report.range}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
interval={
|
||||
getDefaultIntervalByDates(startDate, endDate) ||
|
||||
(range ? getDefaultIntervalByRange(range) : report.interval)
|
||||
}
|
||||
editMode={false}
|
||||
report={{
|
||||
...report,
|
||||
range: range ?? report.range,
|
||||
startDate: startDate ?? report.startDate,
|
||||
endDate: endDate ?? report.endDate,
|
||||
interval:
|
||||
getDefaultIntervalByDates(startDate, endDate) ||
|
||||
(range
|
||||
? getDefaultIntervalByRange(range)
|
||||
: report.interval),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
||||
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
||||
import { ChartRootShortcut } from '@/components/report/chart';
|
||||
import { ReportChartShortcut } from '@/components/report-chart/shortcut';
|
||||
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
|
||||
import {
|
||||
useEventQueryFilters,
|
||||
@@ -44,7 +44,7 @@ function Charts({ projectId }: Props) {
|
||||
<span className="title">Events per day</span>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<ChartRootShortcut
|
||||
<ReportChartShortcut
|
||||
projectId={projectId}
|
||||
range="30d"
|
||||
chartType="histogram"
|
||||
@@ -67,7 +67,7 @@ function Charts({ projectId }: Props) {
|
||||
<span className="title">Event distribution</span>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<ChartRootShortcut
|
||||
<ReportChartShortcut
|
||||
projectId={projectId}
|
||||
range="30d"
|
||||
chartType="pie"
|
||||
@@ -104,7 +104,7 @@ function Charts({ projectId }: Props) {
|
||||
<span className="title">Event distribution</span>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<ChartRootShortcut
|
||||
<ReportChartShortcut
|
||||
projectId={projectId}
|
||||
range="30d"
|
||||
chartType="bar"
|
||||
@@ -141,7 +141,7 @@ function Charts({ projectId }: Props) {
|
||||
<span className="title">Event distribution</span>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<ChartRootShortcut
|
||||
<ReportChartShortcut
|
||||
projectId={projectId}
|
||||
range="30d"
|
||||
chartType="linear"
|
||||
|
||||
@@ -12,8 +12,10 @@ export default function LayoutContent({
|
||||
const segments = useSelectedLayoutSegments();
|
||||
|
||||
if (segments[0] && NOT_MIGRATED_PAGES.includes(segments[0])) {
|
||||
return <div className="transition-all lg:pl-72">{children}</div>;
|
||||
return <div className="pb-20 transition-all lg:pl-72">{children}</div>;
|
||||
}
|
||||
|
||||
return <div className="transition-all max-lg:mt-12 lg:pl-72">{children}</div>;
|
||||
return (
|
||||
<div className="pb-20 transition-all max-lg:mt-12 lg:pl-72">{children}</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { memo } from 'react';
|
||||
import { LazyChart } from '@/components/report/chart/LazyChart';
|
||||
import { ReportChart } from '@/components/report-chart';
|
||||
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||
import { cn } from '@/utils/cn';
|
||||
import isEqual from 'lodash.isequal';
|
||||
@@ -71,41 +71,46 @@ export const PagesTable = memo(
|
||||
index === data.length - 1 && 'rounded-br-md'
|
||||
)}
|
||||
>
|
||||
<LazyChart
|
||||
hideYAxis
|
||||
hideXAxis
|
||||
className="w-full"
|
||||
lineType="linear"
|
||||
breakdowns={[]}
|
||||
name="screen_view"
|
||||
metric="sum"
|
||||
range="30d"
|
||||
interval="day"
|
||||
previous
|
||||
aspectRatio={0.15}
|
||||
chartType="linear"
|
||||
projectId={item.project_id}
|
||||
events={[
|
||||
{
|
||||
id: 'A',
|
||||
name: 'screen_view',
|
||||
segment: 'event',
|
||||
filters: [
|
||||
{
|
||||
id: 'path',
|
||||
name: 'path',
|
||||
value: [item.path],
|
||||
operator: 'is',
|
||||
},
|
||||
{
|
||||
id: 'origin',
|
||||
name: 'origin',
|
||||
value: [item.origin],
|
||||
operator: 'is',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
<ReportChart
|
||||
options={{
|
||||
hideID: true,
|
||||
hideXAxis: true,
|
||||
hideYAxis: true,
|
||||
aspectRatio: 0.15,
|
||||
}}
|
||||
report={{
|
||||
lineType: 'linear',
|
||||
breakdowns: [],
|
||||
name: 'screen_view',
|
||||
metric: 'sum',
|
||||
range: '30d',
|
||||
interval: 'day',
|
||||
previous: true,
|
||||
|
||||
chartType: 'linear',
|
||||
projectId: item.project_id,
|
||||
events: [
|
||||
{
|
||||
id: 'A',
|
||||
name: 'screen_view',
|
||||
segment: 'event',
|
||||
filters: [
|
||||
{
|
||||
id: 'path',
|
||||
name: 'path',
|
||||
value: [item.path],
|
||||
operator: 'is',
|
||||
},
|
||||
{
|
||||
id: 'origin',
|
||||
name: 'origin',
|
||||
value: [item.origin],
|
||||
operator: 'is',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { memo } from 'react';
|
||||
import { ChartRoot } from '@/components/report/chart';
|
||||
import { ReportChart } from '@/components/report-chart';
|
||||
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
|
||||
|
||||
import type { IChartProps } from '@openpanel/validation';
|
||||
@@ -85,7 +85,7 @@ const ProfileCharts = ({ profileId, projectId }: Props) => {
|
||||
<span className="title">Page views</span>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<ChartRoot {...pageViewsChart} />
|
||||
<ReportChart report={pageViewsChart} />
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
<Widget className="col-span-6 md:col-span-3">
|
||||
@@ -93,7 +93,7 @@ const ProfileCharts = ({ profileId, projectId }: Props) => {
|
||||
<span className="title">Events per day</span>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<ChartRoot {...eventsChart} />
|
||||
<ReportChart report={eventsChart} />
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
</>
|
||||
|
||||
@@ -18,7 +18,7 @@ function Card({ title, value }: { title: string; value: string }) {
|
||||
return (
|
||||
<div className="col gap-2 p-4 ring-[0.5px] ring-border">
|
||||
<div className="text-muted-foreground">{title}</div>
|
||||
<div className="font-mono truncate text-2xl font-bold">{value}</div>
|
||||
<div className="truncate font-mono text-2xl font-bold">{value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ function Info({ title, value }: { title: string; value: string }) {
|
||||
return (
|
||||
<div className="col gap-2">
|
||||
<div className="capitalize text-muted-foreground">{title}</div>
|
||||
<div className="font-mono truncate">{value || '-'}</div>
|
||||
<div className="truncate font-mono">{value || '-'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ const ProfileMetrics = ({ data, profile }: Props) => {
|
||||
const number = useNumber();
|
||||
return (
|
||||
<div className="@container">
|
||||
<div className="grid grid-cols-2 overflow-hidden whitespace-nowrap rounded-md border bg-background @xl:grid-cols-3 @4xl:grid-cols-6">
|
||||
<div className="grid grid-cols-2 overflow-hidden whitespace-nowrap rounded-md border bg-background @xl:grid-cols-3 @4xl:grid-cols-6">
|
||||
<div className="col-span-2 @xl:col-span-3 @4xl:col-span-6">
|
||||
<div className="row border-b">
|
||||
<button
|
||||
|
||||
@@ -4,9 +4,8 @@ import {
|
||||
FullscreenClose,
|
||||
FullscreenOpen,
|
||||
} from '@/components/fullscreen-toggle';
|
||||
import { LazyChart } from '@/components/report/chart/LazyChart';
|
||||
import { ReportChart } from '@/components/report-chart';
|
||||
|
||||
import PageLayout from '../page-layout';
|
||||
import RealtimeMap from './map';
|
||||
import RealtimeLiveEventsServer from './realtime-live-events';
|
||||
import { RealtimeLiveHistogram } from './realtime-live-histogram';
|
||||
@@ -42,9 +41,11 @@ export default function Page({ params: { projectId } }: Props) {
|
||||
<div className="mb-6">
|
||||
<div className="font-bold">Pages</div>
|
||||
</div>
|
||||
<LazyChart
|
||||
hideID
|
||||
{...{
|
||||
<ReportChart
|
||||
options={{
|
||||
hideID: true,
|
||||
}}
|
||||
report={{
|
||||
projectId,
|
||||
events: [
|
||||
{
|
||||
@@ -74,9 +75,11 @@ export default function Page({ params: { projectId } }: Props) {
|
||||
<div className="mb-6">
|
||||
<div className="font-bold">Cities</div>
|
||||
</div>
|
||||
<LazyChart
|
||||
hideID
|
||||
{...{
|
||||
<ReportChart
|
||||
options={{
|
||||
hideID: true,
|
||||
}}
|
||||
report={{
|
||||
projectId,
|
||||
events: [
|
||||
{
|
||||
@@ -106,9 +109,11 @@ export default function Page({ params: { projectId } }: Props) {
|
||||
<div className="mb-6">
|
||||
<div className="font-bold">Referrers</div>
|
||||
</div>
|
||||
<LazyChart
|
||||
hideID
|
||||
{...{
|
||||
<ReportChart
|
||||
options={{
|
||||
hideID: true,
|
||||
}}
|
||||
report={{
|
||||
projectId,
|
||||
events: [
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||
import { ChartRoot } from '@/components/report/chart';
|
||||
import { ReportChart } from '@/components/report-chart';
|
||||
import { ReportChartType } from '@/components/report/ReportChartType';
|
||||
import { ReportInterval } from '@/components/report/ReportInterval';
|
||||
import { ReportLineType } from '@/components/report/ReportLineType';
|
||||
@@ -99,7 +99,7 @@ export default function ReportEditor({
|
||||
</StickyBelowHeader>
|
||||
<div className="flex flex-col gap-4 p-4" id="report-editor">
|
||||
{report.ready && (
|
||||
<ChartRoot {...report} projectId={projectId} editMode />
|
||||
<ReportChart report={{ ...report, projectId }} isEditMode />
|
||||
)}
|
||||
</div>
|
||||
<SheetContent className="!max-w-lg" side="left">
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { getYAxisWidth } from '@/components/report/chart/chart-utils';
|
||||
import { ResponsiveContainer } from '@/components/report/chart/ResponsiveContainer';
|
||||
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||
import {
|
||||
useXAxisProps,
|
||||
useYAxisProps,
|
||||
} from '@/components/report-chart/common/axis';
|
||||
import { getChartColor } from '@/utils/theme';
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
Tooltip as RechartTooltip,
|
||||
ResponsiveContainer,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
@@ -39,69 +41,62 @@ function Tooltip(props: any) {
|
||||
}
|
||||
|
||||
const Chart = ({ data }: Props) => {
|
||||
const max = Math.max(...data.map((d) => d.users));
|
||||
const number = useNumber();
|
||||
const xAxisProps = useXAxisProps();
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: data.map((d) => d.users),
|
||||
});
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="aspect-video max-h-[300px] w-full p-4">
|
||||
<ResponsiveContainer>
|
||||
{({ width, height }) => (
|
||||
<AreaChart data={data} width={width} height={height}>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<AreaChart data={data}>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<RechartTooltip content={<Tooltip />} />
|
||||
<RechartTooltip content={<Tooltip />} />
|
||||
|
||||
<Area
|
||||
dataKey="users"
|
||||
stroke={getChartColor(0)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#bg)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="days"
|
||||
axisLine={false}
|
||||
fontSize={12}
|
||||
// type="number"
|
||||
tickLine={false}
|
||||
label={{
|
||||
value: 'DAYS',
|
||||
position: 'insideBottom',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
label={{
|
||||
value: 'USERS',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
dataKey="users"
|
||||
fontSize={12}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
width={getYAxisWidth(max)}
|
||||
allowDecimals={false}
|
||||
domain={[0, max]}
|
||||
tickFormatter={number.short}
|
||||
/>
|
||||
</AreaChart>
|
||||
)}
|
||||
<Area
|
||||
dataKey="users"
|
||||
stroke={getChartColor(0)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#bg)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<XAxis
|
||||
{...xAxisProps}
|
||||
dataKey="days"
|
||||
scale="auto"
|
||||
type="category"
|
||||
label={{
|
||||
value: 'DAYS',
|
||||
position: 'insideBottom',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
{...yAxisProps}
|
||||
label={{
|
||||
value: 'USERS',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
dataKey="users"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -55,7 +55,7 @@ const Retention = ({ params: { projectId } }: Props) => {
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<LastActiveUsersServer projectId={projectId} />
|
||||
<UsersRetentionSeries projectId={projectId} />
|
||||
{/* <UsersRetentionSeries projectId={projectId} /> */}
|
||||
<WeeklyCohortsServer projectId={projectId} />
|
||||
</div>
|
||||
</Padding>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { getYAxisWidth } from '@/components/report/chart/chart-utils';
|
||||
import { ResponsiveContainer } from '@/components/report/chart/ResponsiveContainer';
|
||||
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||
import {
|
||||
useXAxisProps,
|
||||
useYAxisProps,
|
||||
} from '@/components/report-chart/common/axis';
|
||||
import { getChartColor } from '@/utils/theme';
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
Tooltip as RechartTooltip,
|
||||
ResponsiveContainer,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
@@ -50,106 +52,94 @@ function Tooltip(props: any) {
|
||||
}
|
||||
|
||||
const Chart = ({ data }: Props) => {
|
||||
const max = Math.max(...data.monthly.map((d) => d.users));
|
||||
const number = useNumber();
|
||||
const rechartData = data.daily.map((d) => ({
|
||||
date: d.date,
|
||||
date: new Date(d.date).getTime(),
|
||||
dau: d.users,
|
||||
wau: data.weekly.find((w) => w.date === d.date)?.users,
|
||||
mau: data.monthly.find((m) => m.date === d.date)?.users,
|
||||
}));
|
||||
const xAxisProps = useXAxisProps({ interval: 'day' });
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: data.monthly.map((d) => d.users),
|
||||
});
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="aspect-video max-h-[300px] w-full p-4">
|
||||
<ResponsiveContainer>
|
||||
{({ width, height }) => (
|
||||
<AreaChart data={rechartData} width={width} height={height}>
|
||||
<defs>
|
||||
<linearGradient id="dau" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="wau" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(1)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(1)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="mau" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(2)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(2)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<AreaChart data={rechartData}>
|
||||
<defs>
|
||||
<linearGradient id="dau" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="wau" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(1)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(1)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="mau" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(2)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(2)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<RechartTooltip content={<Tooltip />} />
|
||||
<RechartTooltip content={<Tooltip />} />
|
||||
|
||||
<Area
|
||||
dataKey="dau"
|
||||
stroke={getChartColor(0)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#dau)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="wau"
|
||||
stroke={getChartColor(1)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#wau)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="mau"
|
||||
stroke={getChartColor(2)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#mau)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
axisLine={false}
|
||||
fontSize={12}
|
||||
// type="number"
|
||||
tickLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
label={{
|
||||
value: 'UNIQUE USERS',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
fontSize={12}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
width={getYAxisWidth(max)}
|
||||
allowDecimals={false}
|
||||
domain={[0, max]}
|
||||
tickFormatter={number.short}
|
||||
/>
|
||||
</AreaChart>
|
||||
)}
|
||||
<Area
|
||||
dataKey="dau"
|
||||
stroke={getChartColor(0)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#dau)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="wau"
|
||||
stroke={getChartColor(1)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#wau)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="mau"
|
||||
stroke={getChartColor(2)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#mau)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<XAxis {...xAxisProps} dataKey="date" />
|
||||
<YAxis
|
||||
{...yAxisProps}
|
||||
label={{
|
||||
value: 'UNIQUE USERS',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import { getYAxisWidth } from '@/components/report/chart/chart-utils';
|
||||
import { ResponsiveContainer } from '@/components/report/chart/ResponsiveContainer';
|
||||
import {
|
||||
useXAxisProps,
|
||||
useYAxisProps,
|
||||
} from '@/components/report-chart/common/axis';
|
||||
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
||||
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { getChartColor } from '@/utils/theme';
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
Tooltip as RechartTooltip,
|
||||
ResponsiveContainer,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
@@ -54,69 +56,61 @@ function Tooltip({ payload }: any) {
|
||||
}
|
||||
|
||||
const Chart = ({ data }: Props) => {
|
||||
const max = Math.max(...data.map((d) => d.retention));
|
||||
const number = useNumber();
|
||||
const xAxisProps = useXAxisProps();
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: data.map((d) => d.retention),
|
||||
});
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="aspect-video max-h-[300px] w-full p-4">
|
||||
<ResponsiveContainer>
|
||||
{({ width, height }) => (
|
||||
<AreaChart data={data} width={width} height={height}>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<AreaChart data={data}>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.8}
|
||||
></stop>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={getChartColor(0)}
|
||||
stopOpacity={0.1}
|
||||
></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<RechartTooltip content={<Tooltip />} />
|
||||
<RechartTooltip content={<Tooltip />} />
|
||||
|
||||
<Area
|
||||
dataKey="retention"
|
||||
stroke={getChartColor(0)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#bg)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<XAxis
|
||||
axisLine={false}
|
||||
fontSize={12}
|
||||
dataKey="date"
|
||||
tickFormatter={(m: string) => formatDate(new Date(m))}
|
||||
tickLine={false}
|
||||
allowDuplicatedCategory={false}
|
||||
label={{
|
||||
value: 'DATE',
|
||||
position: 'insideBottom',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
label={{
|
||||
value: 'RETENTION (%)',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
fontSize={12}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
width={getYAxisWidth(max)}
|
||||
allowDecimals={false}
|
||||
domain={[0, max]}
|
||||
tickFormatter={number.short}
|
||||
/>
|
||||
</AreaChart>
|
||||
)}
|
||||
<Area
|
||||
dataKey="retention"
|
||||
stroke={getChartColor(0)}
|
||||
strokeWidth={2}
|
||||
fill={`url(#bg)`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<XAxis
|
||||
{...xAxisProps}
|
||||
dataKey="date"
|
||||
tickFormatter={(m: string) => formatDate(new Date(m))}
|
||||
allowDuplicatedCategory={false}
|
||||
label={{
|
||||
value: 'DATE',
|
||||
position: 'insideBottom',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
{...yAxisProps}
|
||||
label={{
|
||||
value: 'RETENTION (%)',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 0,
|
||||
fontSize: 10,
|
||||
}}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user