Add vaccine tabs: Completed/Upcoming/Overdue with days overdue display
- Calculate schedule from child's DOB - Add tab navigation for vaccines - Show days overdue for missed vaccines - Visual indicators (opacity for completed, red border for overdue) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
bc08828c18
commit
3e54efaf66
1 changed files with 56 additions and 4 deletions
|
|
@ -75,6 +75,7 @@ export default function MedicalPage() {
|
|||
const [loading, setLoading] = useState(true);
|
||||
const { childId: sessionChildId, child, loading: loadingChild } = useFamily();
|
||||
const [tab, setTab] = useState<"vaccinations" | "medicine" | "allergies" | "visits" | "illness">("vaccinations");
|
||||
const [vaccineTab, setVaccineTab] = useState<"upcoming" | "completed" | "overdue">("upcoming");
|
||||
const [showAddDate, setShowAddDate] = useState<string | null>(null);
|
||||
const [givenDate, setGivenDate] = useState("");
|
||||
|
||||
|
|
@ -407,6 +408,33 @@ const SUPPLEMENTS = [
|
|||
const getGivenDate = (name: string) => vaccinations.find((v) => v.vaccine_name === name && v.status === "given")?.given_date;
|
||||
const isPending = (name: string) => !isGiven(name);
|
||||
|
||||
// Get vaccines by status
|
||||
const getVaccinesByStatus = (status: "upcoming" | "completed" | "overdue") => {
|
||||
const today = new Date();
|
||||
const todayStr = today.toISOString().split("T")[0];
|
||||
|
||||
return IAP_SCHEDULE.filter((v) => {
|
||||
const dueDate = calculateDueDate(birthDate, v.weeks);
|
||||
const given = isGiven(v.name);
|
||||
|
||||
if (status === "completed") return given;
|
||||
if (status === "overdue") return !given && dueDate < todayStr;
|
||||
if (status === "upcoming") return !given && dueDate >= todayStr;
|
||||
return false;
|
||||
}).sort((a, b) => a.weeks - b.weeks);
|
||||
};
|
||||
|
||||
const getVaccineStatus = (name: string) => {
|
||||
const dueDate = calculateDueDate(birthDate, IAP_SCHEDULE.find((v) => v.name === name)?.weeks || 0);
|
||||
const today = new Date();
|
||||
const todayStr = today.toISOString().split("T")[0];
|
||||
const given = isGiven(name);
|
||||
|
||||
if (given) return "completed";
|
||||
if (dueDate < todayStr) return "overdue";
|
||||
return "upcoming";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-rose-50 to-amber-50">
|
||||
<div className="p-4">
|
||||
|
|
@ -450,21 +478,42 @@ const SUPPLEMENTS = [
|
|||
|
||||
{tab === "vaccinations" && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex gap-2 mb-4 overflow-x-auto">
|
||||
<button
|
||||
onClick={() => setVaccineTab("upcoming")}
|
||||
className={`px-4 py-2 rounded-xl whitespace-nowrap ${vaccineTab === "upcoming" ? "bg-rose-400 text-white" : "bg-white"}`}
|
||||
>
|
||||
Upcoming ({getVaccinesByStatus("upcoming").length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setVaccineTab("completed")}
|
||||
className={`px-4 py-2 rounded-xl whitespace-nowrap ${vaccineTab === "completed" ? "bg-rose-400 text-white" : "bg-white"}`}
|
||||
>
|
||||
Completed ({getVaccinesByStatus("completed").length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setVaccineTab("overdue")}
|
||||
className={`px-4 py-2 rounded-xl whitespace-nowrap ${vaccineTab === "overdue" ? "bg-red-500 text-white" : "bg-white"}`}
|
||||
>
|
||||
Overdue ({getVaccinesByStatus("overdue").length})
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2 className="font-semibold mb-3">IAP Schedule</h2>
|
||||
{loading ? (
|
||||
<p className="text-gray-500">Loading...</p>
|
||||
) : (
|
||||
IAP_SCHEDULE.filter((v) => isPending(v.name) || isGiven(v.name))
|
||||
.sort((a, b) => a.weeks - b.weeks)
|
||||
.map((vaccine) => {
|
||||
getVaccinesByStatus(vaccineTab).map((vaccine) => {
|
||||
const given = isGiven(vaccine.name);
|
||||
const actualDate = getGivenDate(vaccine.name);
|
||||
const dueDate = calculateDueDate(birthDate, vaccine.weeks);
|
||||
const status = getVaccineStatus(vaccine.name);
|
||||
const daysOverdue = status === "overdue" ? Math.floor((new Date().getTime() - new Date(dueDate).getTime()) / (1000 * 60 * 60 * 24)) : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={vaccine.name}
|
||||
className={`p-4 bg-white rounded-xl ${given ? "opacity-60" : ""}`}
|
||||
className={`p-4 bg-white rounded-xl ${status === "completed" ? "opacity-60" : ""} ${status === "overdue" ? "border-l-4 border-red-500" : ""}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
|
|
@ -473,6 +522,9 @@ const SUPPLEMENTS = [
|
|||
Due: {new Date(dueDate).toLocaleDateString()}
|
||||
{actualDate && ` · Given: ${new Date(actualDate).toLocaleDateString()}`}
|
||||
</div>
|
||||
{status === "overdue" && (
|
||||
<div className="text-red-500 text-sm font-medium">{daysOverdue} days overdue</div>
|
||||
)}
|
||||
</div>
|
||||
{given ? (
|
||||
<span className="text-green-500">✓</span>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue