53
resources/client/admin/reports/insights/insights-async-chart.tsx
Executable file
53
resources/client/admin/reports/insights/insights-async-chart.tsx
Executable file
@@ -0,0 +1,53 @@
|
||||
import {cloneElement, ReactElement, useCallback, useRef, useState} from 'react';
|
||||
import {BaseChartProps} from '@common/charts/base-chart';
|
||||
import {UseQueryResult} from '@tanstack/react-query';
|
||||
import {
|
||||
FetchInsightsReportResponse,
|
||||
InsightsReportMetric,
|
||||
useInsightsReport,
|
||||
} from '@app/admin/reports/requests/use-insights-report';
|
||||
import {useInsightsChartContext} from '@app/admin/reports/insights/insights-charts-context';
|
||||
|
||||
interface Props {
|
||||
children:
|
||||
| ReactElement<BaseChartProps>
|
||||
| ((
|
||||
query: UseQueryResult<FetchInsightsReportResponse>
|
||||
) => ReactElement<BaseChartProps>);
|
||||
metric: InsightsReportMetric;
|
||||
}
|
||||
export function InsightsAsyncChart({children, metric}: Props) {
|
||||
const [isEnabled, setIsEnabled] = useState(false);
|
||||
const {dateRange, model} = useInsightsChartContext();
|
||||
const query = useInsightsReport(
|
||||
{metrics: [metric], model, dateRange},
|
||||
{isEnabled}
|
||||
);
|
||||
const chart = typeof children === 'function' ? children(query) : children;
|
||||
const observerRef = useRef<IntersectionObserver>();
|
||||
|
||||
const contentRef = useCallback((el: HTMLDivElement | null) => {
|
||||
if (el) {
|
||||
const observer = new IntersectionObserver(
|
||||
([e]) => {
|
||||
if (e.isIntersecting) {
|
||||
setIsEnabled(true);
|
||||
observerRef.current?.disconnect();
|
||||
observerRef.current = undefined;
|
||||
}
|
||||
},
|
||||
{threshold: 0.1} // if only header is visible, don't load
|
||||
);
|
||||
observerRef.current = observer;
|
||||
observer.observe(el);
|
||||
} else if (observerRef.current) {
|
||||
observerRef.current?.disconnect();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return cloneElement<BaseChartProps>(chart, {
|
||||
data: query.data?.report?.[metric],
|
||||
isLoading: query.isLoading,
|
||||
contentRef,
|
||||
});
|
||||
}
|
||||
14
resources/client/admin/reports/insights/insights-charts-context.ts
Executable file
14
resources/client/admin/reports/insights/insights-charts-context.ts
Executable file
@@ -0,0 +1,14 @@
|
||||
import React, {useContext} from 'react';
|
||||
import {DateRangeValue} from '@common/ui/forms/input-field/date/date-range-picker/date-range-value';
|
||||
|
||||
export interface InsightsChartsContextValue {
|
||||
dateRange: DateRangeValue;
|
||||
model: string;
|
||||
}
|
||||
|
||||
export const InsightsChartsContext =
|
||||
React.createContext<InsightsChartsContextValue>(null!);
|
||||
|
||||
export function useInsightsChartContext() {
|
||||
return useContext(InsightsChartsContext);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-devices-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-devices-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {PolarAreaChart} from '@common/charts/polar-area-chart';
|
||||
|
||||
export function InsightsDevicesChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="devices">
|
||||
<PolarAreaChart title={<Trans message="Top devices" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-episodes-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-episodes-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {TopModelsChartLayout} from '@app/admin/reports/top-models-chart-layout';
|
||||
|
||||
export function InsightsEpisodesChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="episodes">
|
||||
<TopModelsChartLayout title={<Trans message="Most played episodes" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
11
resources/client/admin/reports/insights/insights-locations-chart.tsx
Executable file
11
resources/client/admin/reports/insights/insights-locations-chart.tsx
Executable file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {GeoChart} from '@common/admin/analytics/geo-chart/geo-chart';
|
||||
|
||||
export function InsightsLocationsChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="locations">
|
||||
<GeoChart className="flex-auto w-1/2 lg:max-w-[740px]" />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-movies-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-movies-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {TopModelsChartLayout} from '@app/admin/reports/top-models-chart-layout';
|
||||
|
||||
export function InsightsMoviesChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="movies">
|
||||
<TopModelsChartLayout title={<Trans message="Most played movies" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
15
resources/client/admin/reports/insights/insights-platforms-chart.tsx
Executable file
15
resources/client/admin/reports/insights/insights-platforms-chart.tsx
Executable file
@@ -0,0 +1,15 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {PolarAreaChart} from '@common/charts/polar-area-chart';
|
||||
|
||||
export function InsightsPlatformsChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="platforms">
|
||||
<PolarAreaChart
|
||||
className="max-w-500"
|
||||
title={<Trans message="Top platforms" />}
|
||||
/>
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
29
resources/client/admin/reports/insights/insights-plays-chart.tsx
Executable file
29
resources/client/admin/reports/insights/insights-plays-chart.tsx
Executable file
@@ -0,0 +1,29 @@
|
||||
import {LineChart} from '@common/charts/line-chart';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {FormattedNumber} from '@common/i18n/formatted-number';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
|
||||
export function InsightsPlaysChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="plays">
|
||||
{({data}) => (
|
||||
<LineChart
|
||||
className="flex-auto"
|
||||
title={<Trans message="Plays" />}
|
||||
hideLegend
|
||||
description={
|
||||
<Trans
|
||||
message=":count total plays"
|
||||
values={{
|
||||
count: (
|
||||
<FormattedNumber value={data?.report.plays.total || 0} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-report-row.tsx
Executable file
12
resources/client/admin/reports/insights/insights-report-row.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {ReactNode} from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
export function InsightsReportRow({children}: Props) {
|
||||
return (
|
||||
<div className="mb-12 flex flex-col gap-12 overflow-x-auto md:mb-18 md:gap-18 lg:flex-row lg:items-center">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-seasons-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-seasons-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {TopModelsChartLayout} from '@app/admin/reports/top-models-chart-layout';
|
||||
|
||||
export function InsightsSeasonsChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="seasons">
|
||||
<TopModelsChartLayout title={<Trans message="Most played seasons" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-series-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-series-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {TopModelsChartLayout} from '@app/admin/reports/top-models-chart-layout';
|
||||
|
||||
export function InsightsSeriesChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="series">
|
||||
<TopModelsChartLayout title={<Trans message="Most played series" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-users-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-users-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {TopModelsChartLayout} from '@app/admin/reports/top-models-chart-layout';
|
||||
|
||||
export function InsightsUsersChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="users">
|
||||
<TopModelsChartLayout title={<Trans message="Top users" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
12
resources/client/admin/reports/insights/insights-videos-chart.tsx
Executable file
12
resources/client/admin/reports/insights/insights-videos-chart.tsx
Executable file
@@ -0,0 +1,12 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React from 'react';
|
||||
import {InsightsAsyncChart} from '@app/admin/reports/insights/insights-async-chart';
|
||||
import {TopModelsChartLayout} from '@app/admin/reports/top-models-chart-layout';
|
||||
|
||||
export function InsightsVideosChart() {
|
||||
return (
|
||||
<InsightsAsyncChart metric="videos">
|
||||
<TopModelsChartLayout title={<Trans message="Most played videos" />} />
|
||||
</InsightsAsyncChart>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user