Files
castration_tracker/app/components/castration-tracker.tsx
2026-04-16 22:00:50 +02:00

134 lines
4.6 KiB
TypeScript

import { useState, useEffect } from "react";
import type { castrations } from "~/database/schema";
interface CastrationTrackerProps {
castrations: (typeof castrations.$inferSelect)[];
totalCount: number;
}
export function CastrationTracker({
castrations: initialCastrations,
totalCount: initialCount,
}: CastrationTrackerProps) {
const [castrations, setCastrations] = useState(initialCastrations);
const [totalCount, setTotalCount] = useState(initialCount);
const [loading, setLoading] = useState(false);
const handleCastration = async (gender: "male" | "female") => {
setLoading(true);
try {
const response = await fetch("/api/castration", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ gender }),
});
if (response.ok) {
const result = await response.json();
// Add new castration to the list
const newCastration = {
id: castrations.length + 1,
gender,
timestamp: new Date(),
};
setCastrations([newCastration, ...castrations]);
setTotalCount(totalCount + 1);
}
} catch (error) {
console.error("Failed to record castration:", error);
} finally {
setLoading(false);
}
};
const formatTime = (date: Date | string) => {
const d = typeof date === "string" ? new Date(date) : date;
return d.toLocaleString();
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-8">
<div className="max-w-2xl mx-auto">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-800 mb-2">
Castration Tracker
</h1>
<div className="bg-white rounded-lg shadow-md p-6">
<p className="text-3xl font-bold text-indigo-600">{totalCount}</p>
<p className="text-gray-600 text-lg">Total Castrations</p>
</div>
</div>
{/* Buttons */}
<div className="grid grid-cols-2 gap-6 mb-12">
<button
onClick={() => handleCastration("male")}
disabled={loading}
className="bg-blue-500 hover:bg-blue-600 disabled:bg-blue-300 text-white font-bold py-12 px-6 rounded-lg shadow-lg transition duration-200 transform hover:scale-105 active:scale-95"
>
<div className="text-6xl mb-4"></div>
<div className="text-xl">Male</div>
</button>
<button
onClick={() => handleCastration("female")}
disabled={loading}
className="bg-pink-500 hover:bg-pink-600 disabled:bg-pink-300 text-white font-bold py-12 px-6 rounded-lg shadow-lg transition duration-200 transform hover:scale-105 active:scale-95"
>
<div className="text-6xl mb-4"></div>
<div className="text-xl">Female</div>
</button>
</div>
{/* Castration List */}
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="bg-indigo-600 text-white p-4">
<h2 className="text-2xl font-bold">Recent Castrations</h2>
</div>
{castrations.length > 0 ? (
<div className="divide-y">
{castrations.map((castration) => (
<div
key={castration.id}
className="p-4 hover:bg-gray-50 transition flex items-center justify-between"
>
<div className="flex items-center gap-4">
<div
className={`text-4xl ${
castration.gender === "male"
? "text-blue-500"
: "text-pink-500"
}`}
>
{castration.gender === "male" ? "♂" : "♀"}
</div>
<div>
<p className="font-semibold text-gray-800 capitalize">
{castration.gender}
</p>
<p className="text-gray-600 text-sm">
{formatTime(castration.timestamp)}
</p>
</div>
</div>
<div className="text-gray-400 text-sm">#{castration.id}</div>
</div>
))}
</div>
) : (
<div className="p-8 text-center text-gray-500">
<p>No castrations recorded yet. Start by clicking a button above!</p>
</div>
)}
</div>
</div>
</div>
);
}