mirror of https://github.com/morpheus65535/bazarr
150 lines
4.3 KiB
TypeScript
150 lines
4.3 KiB
TypeScript
import { merge } from "lodash";
|
|
import React, {
|
|
FunctionComponent,
|
|
useCallback,
|
|
useEffect,
|
|
useState,
|
|
} from "react";
|
|
import { Col, Container } from "react-bootstrap";
|
|
import { Helmet } from "react-helmet";
|
|
import {
|
|
Bar,
|
|
BarChart,
|
|
CartesianGrid,
|
|
Legend,
|
|
ResponsiveContainer,
|
|
Tooltip,
|
|
XAxis,
|
|
YAxis,
|
|
} from "recharts";
|
|
import { useDidMount } from "rooks";
|
|
import {
|
|
HistoryApi,
|
|
ProvidersApi,
|
|
SystemApi,
|
|
useAsyncRequest,
|
|
} from "../../apis";
|
|
import {
|
|
AsyncOverlay,
|
|
AsyncSelector,
|
|
ContentHeader,
|
|
LanguageSelector,
|
|
Selector,
|
|
} from "../../components";
|
|
import { actionOptions, timeframeOptions } from "./options";
|
|
|
|
function converter(item: History.Stat) {
|
|
const movies = item.movies.map((v) => ({
|
|
date: v.date,
|
|
movies: v.count,
|
|
}));
|
|
const series = item.series.map((v) => ({
|
|
date: v.date,
|
|
series: v.count,
|
|
}));
|
|
const result = merge(movies, series);
|
|
return result;
|
|
}
|
|
|
|
const providerLabel = (item: System.Provider) => item.name;
|
|
|
|
const SelectorContainer: FunctionComponent = ({ children }) => (
|
|
<Col xs={6} lg={3} className="p-1">
|
|
{children}
|
|
</Col>
|
|
);
|
|
|
|
const HistoryStats: FunctionComponent = () => {
|
|
const [languages, updateLanguages] = useAsyncRequest(
|
|
SystemApi.languages.bind(SystemApi)
|
|
);
|
|
const [providerList, updateProviderParam] = useAsyncRequest(
|
|
ProvidersApi.providers.bind(ProvidersApi)
|
|
);
|
|
|
|
const updateProvider = useCallback(
|
|
() => updateProviderParam(true),
|
|
[updateProviderParam]
|
|
);
|
|
|
|
useDidMount(() => {
|
|
updateLanguages(true);
|
|
});
|
|
|
|
const [timeframe, setTimeframe] = useState<History.TimeframeOptions>("month");
|
|
const [action, setAction] = useState<Nullable<History.ActionOptions>>(null);
|
|
const [lang, setLanguage] = useState<Nullable<Language.Info>>(null);
|
|
const [provider, setProvider] = useState<Nullable<System.Provider>>(null);
|
|
|
|
const [stats, update] = useAsyncRequest(HistoryApi.stats.bind(HistoryApi));
|
|
|
|
useEffect(() => {
|
|
update(timeframe, action ?? undefined, provider?.name, lang?.code2);
|
|
}, [timeframe, action, provider?.name, lang?.code2, update]);
|
|
|
|
return (
|
|
// TODO: Responsive
|
|
<Container fluid className="vh-75">
|
|
<Helmet>
|
|
<title>History Statistics - Bazarr</title>
|
|
</Helmet>
|
|
<AsyncOverlay ctx={stats}>
|
|
{({ content }) => (
|
|
<React.Fragment>
|
|
<ContentHeader scroll={false}>
|
|
<SelectorContainer>
|
|
<Selector
|
|
placeholder="Time..."
|
|
options={timeframeOptions}
|
|
value={timeframe}
|
|
onChange={(v) => setTimeframe(v ?? "month")}
|
|
></Selector>
|
|
</SelectorContainer>
|
|
<SelectorContainer>
|
|
<Selector
|
|
placeholder="Action..."
|
|
clearable
|
|
options={actionOptions}
|
|
value={action}
|
|
onChange={setAction}
|
|
></Selector>
|
|
</SelectorContainer>
|
|
<SelectorContainer>
|
|
<AsyncSelector
|
|
placeholder="Provider..."
|
|
clearable
|
|
state={providerList}
|
|
label={providerLabel}
|
|
update={updateProvider}
|
|
onChange={setProvider}
|
|
></AsyncSelector>
|
|
</SelectorContainer>
|
|
<SelectorContainer>
|
|
<LanguageSelector
|
|
clearable
|
|
options={languages.content ?? []}
|
|
value={lang}
|
|
onChange={setLanguage}
|
|
></LanguageSelector>
|
|
</SelectorContainer>
|
|
</ContentHeader>
|
|
<ResponsiveContainer height="100%">
|
|
<BarChart data={content ? converter(content) : []}>
|
|
<CartesianGrid strokeDasharray="4 2"></CartesianGrid>
|
|
<XAxis dataKey="date"></XAxis>
|
|
<YAxis allowDecimals={false}></YAxis>
|
|
<Tooltip></Tooltip>
|
|
<Legend verticalAlign="top"></Legend>
|
|
<Bar name="Series" dataKey="series" fill="#2493B6"></Bar>
|
|
<Bar name="Movies" dataKey="movies" fill="#FFC22F"></Bar>
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</React.Fragment>
|
|
)}
|
|
</AsyncOverlay>
|
|
</Container>
|
|
);
|
|
};
|
|
|
|
export default HistoryStats;
|