AnalyticsView

This commit is contained in:
Ryan Pandya 2025-02-01 16:17:44 -08:00
parent 4222c910eb
commit 10b1ed944d
3 changed files with 113 additions and 51 deletions

View File

@ -8,9 +8,10 @@ import { useSearchParams } from "next/navigation";
import { parseISO, format as fmt } from "date-fns";
import { DatePickerInput } from '@mantine/dates';
import { Calendar1, MenuIcon } from "lucide-react";
import { Anchor, Button, Menu } from "@mantine/core";
import { Anchor, Button, ButtonGroup, Menu } from "@mantine/core";
import { useTooltip, useTooltipInPortal, defaultStyles } from '@visx/tooltip';
import PieChart from "./PieChart";
// import PieChart from "./PieChart";
import { Cell, LabelList, Pie, PieChart, Tooltip } from "recharts";
import { useTimezone } from "@/lib/userLocalSettings/client";
import TimeseriesChart from "./Timeseries";
@ -56,6 +57,7 @@ export default function AnalyticsView() {
const [dateRange, setDateRange] = useState(initialDateRange);
const [datePickerRange, setDatePickerRange] = useState(initialDateRange);
const [hideSleep, setHideSleep] = useState(true);
useEffect(() => {
if (datePickerRef.current?.getAttribute("aria-expanded") === "false") {
@ -71,19 +73,34 @@ export default function AnalyticsView() {
scroll: true,
});
const categoryFrequencies = api.hours.categoryFrequencies.useQuery({
const rawCategoryFrequencies = api.hours.categoryFrequencies.useQuery({
dateRange,
timezone: useTimezone(),
}).data ?? [];
}).data?.filter(
(category) => category.categoryCode != undefined
) ?? [];
const categoryFrequencies = hideSleep ? rawCategoryFrequencies.filter((category) => category.categoryCode > 1) : rawCategoryFrequencies;
const maybeExtendWindow = (dateRange: Date[]) => {
// If distance between dates is less than 2 days, extend the first date back by 14 days
if (spacetime(dateRange[0]).diff(dateRange[1], "day") < 2) {
return [
spacetime(dateRange[0]).subtract(14, "day").toNativeDate(),
dateRange[1]
];
}
return [dateRange[0], dateRange[1]];
}
const weightData = api.measurements.getTimeseries.useQuery({
dateRange,
dateRange: maybeExtendWindow(dateRange) as [Date, Date],
timezone: useTimezone(),
metricName: "weight"
}).data ?? [];
return (
<div className="px-4 flex flex-col gap-y-4">
<div className="px-4 flex flex-col">
<div className="flex justify-between">
<h1>Analytics for&nbsp;
{dateRange[0].getUTCDate() == dateRange[1].getUTCDate()
@ -147,52 +164,82 @@ export default function AnalyticsView() {
</Menu></div>
</div>
<div className="flex hidden">
<PieChart width={500} height={500}
data={categoryFrequencies}
/>
<div className="grow">
<div className="grid font-bold border-b border-gray-200"
style={{
gridTemplateColumns: "1fr 1fr"
}}
>
<div className="whitespace-nowrap ">
Total
</div>
<div>
{categoryFrequencies.reduce((acc, category) => acc + category.count, 0)} hours
</div></div>
{
categoryFrequencies
.sort((a, b) => b.count - a.count)
.map((category, i) => (
<div className="mt-8 pt-4 flex gap-4">
<h2>Time</h2>
{categoryFrequencies.length === 0 ? <LoadingSpinner /> :
<div className="w-full flex flex-col lg:flex-row gap-4">
<div key={i} className="grid"
style={{
gridTemplateColumns: "1fr 1fr"
}}
<PieChart width={500} height={500} startAngle={180} endAngle={0} >
<Pie data={categoryFrequencies} dataKey="percentage" nameKey="categoryName" label={false}
paddingAngle={0} stroke={(c) => console.log(c)}>
{categoryFrequencies.sort(
(a, b) => b.count - a.count
).map((c, _i) => (
<Cell key={`pie-category-${c.id}`} fill={c.background}
style={{ outline: 'none !important' }}
/>
))}
</Pie>
<Tooltip />
</PieChart>
<div className="grow">
<div className="flex flex-col justify-evenly h-full">
<div>
<div className="grid font-bold border-b border-gray-200 pl-4"
style={{
gridTemplateColumns: "1fr 1fr"
}}
>
<div className="whitespace-nowrap ">
Total
</div>
<div>
{categoryFrequencies.reduce((acc, category) => acc + category.count, 0)} hours
</div></div>
{
categoryFrequencies
.sort((a, b) => b.count - a.count)
.map((category, i) => (
<div key={i} className="grid p-1 pl-4"
style={{
gridTemplateColumns: "1fr 1fr",
color: category.inverse,
backgroundColor: category.color
}}
>
<div className="whitespace-nowrap">
{category.categoryName ?? "[Unallocated]"}
</div>
<div>
{category.count} hours
</div></div>
))
}</div>
</div>
</div>
<div className="mt-4 bg-accent w-fit p-2 border border-primary rounded-md cursor-pointer flex flex-col items-center">
Sleep
<ButtonGroup className="mt-2">
<Button
onClick={() => setHideSleep(true)}
color={hideSleep ? "gray" : "primary"}
>
<div className="whitespace-nowrap">
{category.categoryName ?? "[Unallocated]"}
</div>
<div>
{category.count} hours
</div></div>
))
}
</div>
Hide
</Button>
<Button
onClick={() => setHideSleep(false)}
color={hideSleep ? "primary" : "gray"}
>
Show
</Button>
</ButtonGroup>
</div>
</div>
}
</div>
<div className="flex gap-4">
<h2>Weight</h2>
<div>
{
!weightData ? <LoadingSpinner /> :
<TimeseriesChart data={weightData} />
}
</div>
</div>
<div className="flex gap-4">
<div className="mt-8 pt-4 flex gap-4">
<h2 className="">Drugs</h2>
<div>
{
@ -206,7 +253,19 @@ export default function AnalyticsView() {
</ul>
}
</div>
</div>
<div className="mt-8 pt-4 flex gap-4">
<h2>Weight</h2>
{
weightData.length === 0 ? <LoadingSpinner /> :
<div>
<TimeseriesChart data={weightData} />
</div>
}
</div>
</div >
);
}

View File

@ -29,7 +29,6 @@ export default function TimeseriesChart({ data }) {
smoothed: polyResult.predict(i)[1],
};
}));
console.log(polyResult);
return (
<div className='flex flex-col items-center'>

View File

@ -229,6 +229,8 @@ export const hoursAppRouter = router({
{
count: z.number(),
percentage: z.number(),
color: z.string(),
inverse: z.string(),
...zHourSchema.shape
}
)))
@ -257,6 +259,8 @@ export const hoursAppRouter = router({
if (!categoriesList[h.categoryCode]) {
categoriesList[h.categoryCode] = {
count: 0,
color: h.background,
inverse: h.foreground,
...h
};
}
@ -269,7 +273,7 @@ export const hoursAppRouter = router({
const percentage = (count / totalHours);
return {
...categoriesList[categoryCode],
percentage: percentage
percentage: percentage,
};
});
return categoryPercentages;