feat: add top-artists report

This commit is contained in:
Julian Tölle 2020-05-31 23:26:06 +02:00
parent 6a6ba493f6
commit 6fc10c40ca
18 changed files with 345 additions and 30 deletions

View file

@ -25,6 +25,9 @@ export const NavBar: React.FC = () => {
<Link to="/reports/listens">
<NavItem>Listens Report</NavItem>
</Link>
<Link to="/reports/top-artists">
<NavItem>Top Artists</NavItem>
</Link>
</>
)}
</div>

View file

@ -0,0 +1,65 @@
import React from "react";
import { TimePreset } from "../api/entities/time-preset.enum";
import { TopArtistsOptions } from "../api/entities/top-artists-options";
import { DateSelect } from "./inputs/DateSelect";
interface ReportOptionsProps {
reportOptions: TopArtistsOptions;
setReportOptions: (options: TopArtistsOptions) => void;
}
const timePresetOptions = [
{ value: TimePreset.LAST_7_DAYS, description: "Last 7 days" },
{ value: TimePreset.LAST_30_DAYS, description: "Last 30 days" },
{ value: TimePreset.LAST_90_DAYS, description: "Last 90 days" },
{ value: TimePreset.LAST_180_DAYS, description: "Last 180 days" },
{ value: TimePreset.LAST_365_DAYS, description: "Last 365 days" },
{ value: TimePreset.ALL_TIME, description: "All time" },
{ value: TimePreset.CUSTOM, description: "Custom" },
];
export const ReportOptions: React.FC<ReportOptionsProps> = ({
reportOptions,
setReportOptions,
}) => {
return (
<div className="md:flex">
<div className="text-gray-700">
<label className="text-sm">Timeframe</label>
<select
className="block appearance-none min-w-full md:w-1/4 bg-white border border-gray-400 hover:border-gray-500 p-2 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
onChange={(e) =>
setReportOptions({
...reportOptions,
timePreset: e.target.value as TimePreset,
})
}
>
{timePresetOptions.map(({ value, description }) => (
<option value={value} key={value}>
{description}
</option>
))}
</select>
</div>
{reportOptions.timePreset === TimePreset.CUSTOM && (
<div className="md:flex text-gray-700">
<DateSelect
label="Start"
value={reportOptions.customTimeStart}
onChange={(newDate) =>
setReportOptions({ ...reportOptions, customTimeStart: newDate })
}
/>
<DateSelect
label="End"
value={reportOptions.customTimeEnd}
onChange={(newDate) =>
setReportOptions({ ...reportOptions, customTimeEnd: newDate })
}
/>
</div>
)}
</div>
);
};

View file

@ -0,0 +1,62 @@
import React, { useMemo, useState } from "react";
import { Redirect } from "react-router-dom";
import { getTopArtists } from "../api/api";
import { TimePreset } from "../api/entities/time-preset.enum";
import { TopArtistsOptions } from "../api/entities/top-artists-options";
import { useAsync } from "../hooks/use-async";
import { useAuth } from "../hooks/use-auth";
import { ReportOptions } from "./ReportOptions";
export const ReportTopArtists: React.FC = () => {
const { user } = useAuth();
const [reportOptions, setReportOptions] = useState<TopArtistsOptions>({
timePreset: TimePreset.LAST_90_DAYS,
customTimeStart: new Date(0),
customTimeEnd: new Date(),
});
const fetchData = useMemo(() => () => getTopArtists(reportOptions), [
reportOptions,
]);
const { value: report, pending: isLoading } = useAsync(fetchData, []);
const reportHasItems = !isLoading && report.length !== 0;
if (!user) {
return <Redirect to="/" />;
}
return (
<div className="md:flex md:justify-center p-4">
<div className="md:flex-shrink-0 min-w-full xl:min-w-0 xl:w-2/3 max-w-screen-lg">
<div className="flex justify-between">
<p className="text-2xl font-normal text-gray-700">Top Artists</p>
</div>
<div className="shadow-xl bg-gray-100 rounded p-5 m-2">
<ReportOptions
reportOptions={reportOptions}
setReportOptions={setReportOptions}
/>
{isLoading && (
<div>
<div className="loader rounded-full border-8 h-64 w-64"></div>
</div>
)}
{!reportHasItems && (
<div>
<p>Report is emtpy! :(</p>
</div>
)}
{reportHasItems &&
report.map(({ artist, count }) => (
<div>
{count} - {artist.name}
</div>
))}
</div>
</div>
</div>
);
};

View file

@ -0,0 +1,31 @@
import { format, parse } from "date-fns";
import React from "react";
const parseDateFromDateInput = (input: string) =>
parse(input, "yyyy-MM-dd", new Date());
const formatDateForDateInput = (date: Date) => format(date, "yyyy-MM-dd");
interface DateSelectProps {
label: string;
value: Date;
onChange: (date: Date) => void;
}
export const DateSelect: React.FC<DateSelectProps> = ({
label,
value,
onChange,
}) => {
return (
<div>
<label className="text-sm">{label}</label>
<input
className="block appearance-none min-w-full md:win-w-0 md:w-1/4 bg-white border border-gray-400 hover:border-gray-500 p-2 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
type="date"
value={formatDateForDateInput(value)}
onChange={(e) => onChange(parseDateFromDateInput(e.target.value))}
/>
</div>
);
};