You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
8.1 KiB
TypeScript

import fs from "node:fs";
import { HourChart } from "./(charts)/hour";
import { DayChart } from "./(charts)/day";
import { MinuteChart } from "./(charts)/minute";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Card,
CardContent,
CardFooter,
CardHeader,
} from "@/components/ui/card";
export const dynamic = "force-dynamic";
export default async function Home() {
const file = await fs.promises.readFile("./output.txt");
const lines = file.toString().split("\n");
const data = lines.map((line) => {
const [timestamp, , value] = line.split(",");
return {
timestamp: Math.round(parseFloat(timestamp) * 1000),
foldtime: parseFloat(value),
};
});
const thisMonthData = convertDataToThisMonthPerDay(data);
const thisHourData = convertDataToThisHourPerMinute(data);
const todayData = convertDataToTodayPerHour(data);
return (
<main className="flex-grow w-full flex flex-col justify-center gap-4 items-center p-4">
<div className="p-4 w-full bg-accent max-w-[1200px] rounded-xl flex justify-start gap-4">
<div className="flex flex-col gap-2 items-start p-4">
<span className="text-muted-foreground">This Month</span>
<h2 className="text-2xl font-bold">
{thisMonthData.reduce((acc) => {
return acc + 1;
}, 0)}
{" Towel(s)"}
</h2>
<span className="text-muted-foreground">
Avg Fold Time (s):{" "}
{thisMonthData.reduce((acc, curr) => {
return acc + curr.foldtime;
}, 0) / thisMonthData.length || 0}
</span>
</div>
<div className="flex flex-col gap-2 items-start p-4">
<span className="text-muted-foreground">Today</span>
<h2 className="text-2xl font-bold">
{todayData.reduce((acc) => {
return acc + 1;
}, 0)}
{" Towel(s)"}
</h2>
<span className="text-muted-foreground">
Avg Fold Time (s):{" "}
{todayData.reduce((acc, curr) => {
return acc + curr.foldtime;
}, 0) / todayData.length || 0}
</span>
</div>
<div className="flex flex-col gap-2 items-start p-4">
<span className="text-muted-foreground">This Month</span>
<h2 className="text-2xl font-bold">
{thisMonthData.reduce((acc) => {
return acc + 1;
}, 0)}
{" Towel(s)"}
</h2>
<span className="text-muted-foreground">
Avg Fold Time (s):{" "}
{thisHourData.reduce((acc, curr) => {
return acc + curr.foldtime;
}, 0) / thisHourData.length || 0}
</span>
</div>
</div>
<Card className="w-full max-w-[1200px]">
<Tabs defaultValue="month" className="w-full">
<CardHeader>
<TabsList className="w-full">
<TabsTrigger className="w-full" value="month">
Month
</TabsTrigger>
<TabsTrigger className="w-full" value="day">
Day
</TabsTrigger>
<TabsTrigger className="w-full" value="hour">
Hour
</TabsTrigger>
</TabsList>
</CardHeader>
<CardContent>
<TabsContent value="month">
<DayChart
chartData={convertDataToThisMonthPerDayChartData(thisMonthData)}
/>
</TabsContent>
<TabsContent value="day">
<HourChart
chartData={convertDataToTodayPerHourChartData(todayData)}
/>
</TabsContent>
<TabsContent value="hour">
<MinuteChart
chartData={convertDataToThisHourPerMinuteChartData(
thisHourData
)}
/>
</TabsContent>
</CardContent>
<CardFooter></CardFooter>
</Tabs>
</Card>
</main>
);
}
const convertDataToThisMonthPerDay = (
data: { timestamp: number; foldtime: number }[]
) => {
const thisMonthTimestamp = new Date();
thisMonthTimestamp.setHours(0, 0, 0, 0);
thisMonthTimestamp.setDate(1);
const nextMonthTimestamp = new Date();
nextMonthTimestamp.setHours(0, 0, 0, 0);
nextMonthTimestamp.setDate(1);
nextMonthTimestamp.setMonth(nextMonthTimestamp.getMonth() + 1);
const thisMonthData = data.filter(
(data) =>
data.timestamp >= thisMonthTimestamp.getTime() &&
data.timestamp < nextMonthTimestamp.getTime()
);
return thisMonthData;
};
const convertDataToThisMonthPerDayChartData = (
data: { timestamp: number; foldtime: number }[]
) => {
const todayDataPerHour = data.reduce((acc, data) => {
const day = new Date(data.timestamp).getDate();
if (!acc[day]) {
acc[day] = {
count: 0,
foldTime: 0,
};
}
acc[day].count += 1;
acc[day].foldTime += data.foldtime;
return acc;
}, {} as Record<number, { count: number; foldTime: number }>);
const thisMonthDays = new Date();
thisMonthDays.setMonth(thisMonthDays.getMonth() + 1);
thisMonthDays.setDate(1);
thisMonthDays.setDate(thisMonthDays.getDate() - 1);
const chartData = new Array(thisMonthDays.getDate()).fill(0).map((_, i) => {
const day = i + 1;
if (!todayDataPerHour[day])
return { day: "Day " + day, value: 0, foldtime: 0 };
return {
day: "Day " + day,
value: todayDataPerHour[day].count,
foldtime:
todayDataPerHour[day].foldTime / todayDataPerHour[day].count || 0,
};
});
return chartData;
};
const convertDataToThisHourPerMinute = (
data: { timestamp: number; foldtime: number }[]
) => {
const thisHourTimestamp = new Date();
thisHourTimestamp.setMinutes(0, 0, 0);
const nextHourTimestamp = new Date();
nextHourTimestamp.setHours(nextHourTimestamp.getHours() + 1);
nextHourTimestamp.setMinutes(0, 0, 0);
const todayData = data.filter(
(data) =>
data.timestamp >= thisHourTimestamp.getTime() &&
data.timestamp < nextHourTimestamp.getTime()
);
return todayData;
};
const convertDataToThisHourPerMinuteChartData = (
data: { timestamp: number; foldtime: number }[]
) => {
const todayDataPerHour = data.reduce((acc, data) => {
const hour = new Date(data.timestamp).getMinutes();
if (!acc[hour]) {
acc[hour] = {
count: 0,
foldTime: 0,
};
}
acc[hour].count += 1;
acc[hour].foldTime += data.foldtime;
return acc;
}, {} as Record<number, { count: number; foldTime: number }>);
const chartData = new Array(60).fill(0).map((_, i) => {
if (!todayDataPerHour[i])
return { minute: "Minute " + i, value: 0, foldtime: 0 };
return {
minute: "Minute " + i,
value: todayDataPerHour[i].count,
foldtime: todayDataPerHour[i].foldTime / todayDataPerHour[i].count || 0,
};
});
return chartData;
};
const convertDataToTodayPerHour = (
data: { timestamp: number; foldtime: number }[]
) => {
const todayTimestamp = new Date();
todayTimestamp.setHours(0, 0, 0, 0);
const tomorrowTimestamp = new Date();
tomorrowTimestamp.setHours(0, 0, 0, 0);
tomorrowTimestamp.setDate(tomorrowTimestamp.getDate() + 1);
const todayData = data.filter(
(data) =>
data.timestamp >= todayTimestamp.getTime() &&
data.timestamp < tomorrowTimestamp.getTime()
);
return todayData;
};
const convertDataToTodayPerHourChartData = (
data: { timestamp: number; foldtime: number }[]
) => {
const todayDataPerHour = data.reduce((acc, data) => {
const hour = new Date(data.timestamp).getHours();
if (!acc[hour]) {
acc[hour] = {
count: 0,
foldTime: 0,
};
}
acc[hour].count += 1;
acc[hour].foldTime += data.foldtime;
return acc;
}, {} as Record<number, { count: number; foldTime: number }>);
const chartData = new Array(24).fill(0).map((_, i) => {
if (!todayDataPerHour[i])
return { hour: i + 1 + ":00", value: 0, foldtime: 0 };
return {
hour: i + 1 + ":00",
value: todayDataPerHour[i].count,
foldtime: todayDataPerHour[i].foldTime / todayDataPerHour[i].count || 0,
};
});
return chartData;
};