mirror of
https://github.com/apricote/Listory.git
synced 2026-01-13 13:11:02 +00:00
fix(frontend): add selectable timeframe and styling to listens report
This commit is contained in:
parent
cab349c7d0
commit
cc7f4b354d
1 changed files with 133 additions and 37 deletions
|
|
@ -9,6 +9,7 @@ import {
|
|||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
TooltipProps,
|
||||
} from "recharts";
|
||||
import { getListensReport } from "../api/api";
|
||||
import { ListenReportItem } from "../api/entities/listen-report-item";
|
||||
|
|
@ -51,7 +52,30 @@ export const ReportListens: React.FC = () => {
|
|||
<div className="flex justify-between">
|
||||
<p className="text-2xl font-normal text-gray-700">Listen Report</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="shadow-xl bg-gray-100 rounded p-5 m-2">
|
||||
<div className="md:flex">
|
||||
<div className="text-gray-700">
|
||||
<label className="text-sm">Timeframe</label>
|
||||
<select
|
||||
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"
|
||||
onChange={(e) =>
|
||||
setReportOptions({
|
||||
...reportOptions,
|
||||
timeFrame: e.target.value as
|
||||
| "day"
|
||||
| "week"
|
||||
| "month"
|
||||
| "year",
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="day">Daily</option>
|
||||
<option value="week">Weekly</option>
|
||||
<option value="month">Monthly</option>
|
||||
<option value="year">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{isLoading && (
|
||||
<div>
|
||||
<span>Loading Listens</span>
|
||||
|
|
@ -63,7 +87,7 @@ export const ReportListens: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
{report.length > 0 && (
|
||||
<div className="table-auto my-2 w-full text-gray-700">
|
||||
<div className="w-full text-gray-700 mt-5">
|
||||
<ReportGraph options={reportOptions} data={report} />
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -82,41 +106,113 @@ const ReportGraph: React.FC<{
|
|||
date: getTime(date),
|
||||
}));
|
||||
|
||||
const ReportTooltip: React.FC<TooltipProps> = ({
|
||||
active,
|
||||
payload,
|
||||
label,
|
||||
}) => {
|
||||
if (!active || payload === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [{ value: listens }] = payload;
|
||||
const date = format(
|
||||
label as number,
|
||||
dateFormatFromTimeFrame(options.timeFrame)
|
||||
);
|
||||
|
||||
return (
|
||||
<ResponsiveContainer width="90%" height={400}>
|
||||
<div className="bg-gray-100 shadow-xl p-2 rounded text-sm font-light">
|
||||
<p>{date}</p>
|
||||
<p>
|
||||
Listens: <span className="font-bold">{listens}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<AreaChart
|
||||
data={dataLocal}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 20,
|
||||
bottom: 5,
|
||||
left: -20,
|
||||
}}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="colorPv" x1="0" y1="0" x2="0" y2="1">
|
||||
<linearGradient id="colorCount" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#48bb78" stopOpacity={0.8} />
|
||||
<stop offset="90%" stopColor="#48bb78" stopOpacity={0.1} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid vertical={false} />
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
scale="time"
|
||||
type="number"
|
||||
domain={["auto", "auto"]}
|
||||
dataKey="date"
|
||||
tickFormatter={(date) => format(date, "P")}
|
||||
tickFormatter={(date) =>
|
||||
format(date, shortDateFormatFromTimeFrame(options.timeFrame))
|
||||
}
|
||||
/>
|
||||
<YAxis />
|
||||
<Tooltip separator=": " formatter={(value) => [value, "Listens"]} />
|
||||
<Tooltip content={ReportTooltip} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="count"
|
||||
stroke="#48bb78"
|
||||
fillOpacity={1}
|
||||
fill="url(#colorPv)"
|
||||
fill="url(#colorCount)"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const shortDateFormatFromTimeFrame = (
|
||||
timeFrame: "day" | "week" | "month" | "year"
|
||||
): string => {
|
||||
const FORMAT_DAY = "P";
|
||||
const FORMAT_WEEK = "'Week' w yyyy";
|
||||
const FORMAT_MONTH = "LLL yyyy";
|
||||
const FORMAT_YEAR = "yyyy";
|
||||
const FORMAT_DEFAULT = FORMAT_DAY;
|
||||
|
||||
switch (timeFrame) {
|
||||
case "day":
|
||||
return FORMAT_DAY;
|
||||
case "week":
|
||||
return FORMAT_WEEK;
|
||||
case "month":
|
||||
return FORMAT_MONTH;
|
||||
case "year":
|
||||
return FORMAT_YEAR;
|
||||
default:
|
||||
return FORMAT_DEFAULT;
|
||||
}
|
||||
};
|
||||
|
||||
const dateFormatFromTimeFrame = (
|
||||
timeFrame: "day" | "week" | "month" | "year"
|
||||
): string => {
|
||||
const FORMAT_DAY = "PPPP";
|
||||
const FORMAT_WEEK = "'Week starting on' PPPP";
|
||||
const FORMAT_MONTH = "LLLL yyyy";
|
||||
const FORMAT_YEAR = "yyyy";
|
||||
const FORMAT_DEFAULT = FORMAT_DAY;
|
||||
|
||||
switch (timeFrame) {
|
||||
case "day":
|
||||
return FORMAT_DAY;
|
||||
case "week":
|
||||
return FORMAT_WEEK;
|
||||
case "month":
|
||||
return FORMAT_MONTH;
|
||||
case "year":
|
||||
return FORMAT_YEAR;
|
||||
default:
|
||||
return FORMAT_DEFAULT;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue