mirror of
https://github.com/apricote/Listory.git
synced 2026-01-13 21:21: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,
|
Tooltip,
|
||||||
XAxis,
|
XAxis,
|
||||||
YAxis,
|
YAxis,
|
||||||
|
TooltipProps,
|
||||||
} from "recharts";
|
} from "recharts";
|
||||||
import { getListensReport } from "../api/api";
|
import { getListensReport } from "../api/api";
|
||||||
import { ListenReportItem } from "../api/entities/listen-report-item";
|
import { ListenReportItem } from "../api/entities/listen-report-item";
|
||||||
|
|
@ -51,7 +52,30 @@ export const ReportListens: React.FC = () => {
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<p className="text-2xl font-normal text-gray-700">Listen Report</p>
|
<p className="text-2xl font-normal text-gray-700">Listen Report</p>
|
||||||
</div>
|
</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 && (
|
{isLoading && (
|
||||||
<div>
|
<div>
|
||||||
<span>Loading Listens</span>
|
<span>Loading Listens</span>
|
||||||
|
|
@ -63,7 +87,7 @@ export const ReportListens: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{report.length > 0 && (
|
{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} />
|
<ReportGraph options={reportOptions} data={report} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -82,41 +106,113 @@ const ReportGraph: React.FC<{
|
||||||
date: getTime(date),
|
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 (
|
||||||
|
<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 (
|
return (
|
||||||
<ResponsiveContainer width="90%" height={400}>
|
<div className="w-full">
|
||||||
<AreaChart
|
<ResponsiveContainer width="100%" height={400}>
|
||||||
data={dataLocal}
|
<AreaChart
|
||||||
margin={{
|
data={dataLocal}
|
||||||
top: 5,
|
margin={{
|
||||||
right: 30,
|
left: -20,
|
||||||
left: 20,
|
}}
|
||||||
bottom: 5,
|
>
|
||||||
}}
|
<defs>
|
||||||
>
|
<linearGradient id="colorCount" x1="0" y1="0" x2="0" y2="1">
|
||||||
<defs>
|
<stop offset="5%" stopColor="#48bb78" stopOpacity={0.8} />
|
||||||
<linearGradient id="colorPv" x1="0" y1="0" x2="0" y2="1">
|
<stop offset="90%" stopColor="#48bb78" stopOpacity={0.1} />
|
||||||
<stop offset="5%" stopColor="#48bb78" stopOpacity={0.8} />
|
</linearGradient>
|
||||||
<stop offset="90%" stopColor="#48bb78" stopOpacity={0.1} />
|
</defs>
|
||||||
</linearGradient>
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
</defs>
|
<XAxis
|
||||||
<CartesianGrid vertical={false} />
|
scale="time"
|
||||||
<XAxis
|
type="number"
|
||||||
scale="time"
|
domain={["auto", "auto"]}
|
||||||
type="number"
|
dataKey="date"
|
||||||
domain={["auto", "auto"]}
|
tickFormatter={(date) =>
|
||||||
dataKey="date"
|
format(date, shortDateFormatFromTimeFrame(options.timeFrame))
|
||||||
tickFormatter={(date) => format(date, "P")}
|
}
|
||||||
/>
|
/>
|
||||||
<YAxis />
|
<YAxis />
|
||||||
<Tooltip separator=": " formatter={(value) => [value, "Listens"]} />
|
<Tooltip content={ReportTooltip} />
|
||||||
<Area
|
<Area
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="count"
|
dataKey="count"
|
||||||
stroke="#48bb78"
|
stroke="#48bb78"
|
||||||
fillOpacity={1}
|
fillOpacity={1}
|
||||||
fill="url(#colorPv)"
|
fill="url(#colorCount)"
|
||||||
/>
|
/>
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
</ResponsiveContainer>
|
</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