first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View 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,
});
}

View 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);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}