Add full CRUD for Medicine, Allergies, Doctor Visits, Illness

- Renamed Visits to Doctor Visit
- Medicine: add edit delete with name, dose, notes, reminder time
- Allergies: add edit delete with severity (mild/moderate/severe)
- Doctor Visit: add edit delete with doctor, reason, date, notes
- Illness: add edit delete with start/end dates
- All data persists to localStorage

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-10 18:07:06 +05:30
parent e1bd89e664
commit a1b436710f

View file

@ -2,6 +2,37 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
interface Medicine {
id: string;
name: string;
dose: string;
notes: string;
reminderTime?: string;
}
interface Allergy {
id: string;
name: string;
severity: string;
notes: string;
}
interface Visit {
id: string;
doctorName: string;
reason: string;
date: string;
notes: string;
}
interface Illness {
id: string;
name: string;
startDate: string;
endDate?: string;
notes: string;
}
// IAP Vaccination Schedule (India) // IAP Vaccination Schedule (India)
const IAP_SCHEDULE = [ const IAP_SCHEDULE = [
{ name: "BCG", weeks: 0 }, { name: "BCG", weeks: 0 },
@ -48,6 +79,218 @@ export default function MedicalPage() {
const [showReminder, setShowReminder] = useState<string | null>(null); const [showReminder, setShowReminder] = useState<string | null>(null);
const [reminderTime, setReminderTime] = useState("08:00"); const [reminderTime, setReminderTime] = useState("08:00");
// CRUD state for medicine, allergies, visits, illness
const [medicines, setMedicines] = useState<Medicine[]>([]);
const [allergies, setAllergies] = useState<Allergy[]>([]);
const [visits, setVisits] = useState<Visit[]>([]);
const [illnesses, setIllnesses] = useState<Illness[]>([]);
// Add/Edit mode
const [editingMed, setEditingMed] = useState<Medicine | null>(null);
const [editingAllergy, setEditingAllergy] = useState<Allergy | null>(null);
const [editingVisit, setEditingVisit] = useState<Visit | null>(null);
const [editingIllness, setEditingIllness] = useState<Illness | null>(null);
const [showAddMed, setShowAddMed] = useState(false);
const [showAddAllergy, setShowAddAllergy] = useState(false);
const [showAddVisit, setShowAddVisit] = useState(false);
const [showAddIllness, setShowAddIllness] = useState(false);
// Form state for new items
const [newMedName, setNewMedName] = useState("");
const [newMedDose, setNewMedDose] = useState("");
const [newMedNotes, setNewMedNotes] = useState("");
const [newAllergyName, setNewAllergyName] = useState("");
const [newAllergySeverity, setNewAllergySeverity] = useState("mild");
const [newAllergyNotes, setNewAllergyNotes] = useState("");
const [newVisitDoctor, setNewVisitDoctor] = useState("");
const [newVisitReason, setNewVisitReason] = useState("");
const [newVisitDate, setNewVisitDate] = useState("");
const [newVisitNotes, setNewVisitNotes] = useState("");
const [newIllnessName, setNewIllnessName] = useState("");
const [newIllnessStart, setNewIllnessStart] = useState("");
const [newIllnessEnd, setNewIllnessEnd] = useState("");
const [newIllnessNotes, setNewIllnessNotes] = useState("");
// Load data from localStorage on mount
useEffect(() => {
// Load medicines
const savedMeds = localStorage.getItem("tia_medicines");
if (savedMeds) setMedicines(JSON.parse(savedMeds));
const savedAllergies = localStorage.getItem("tia_allergies");
if (savedAllergies) setAllergies(JSON.parse(savedAllergies));
const savedVisits = localStorage.getItem("tia_visits");
if (savedVisits) setVisits(JSON.parse(savedVisits));
const savedIllnesses = localStorage.getItem("tia_illnesses");
if (savedIllnesses) setIllnesses(JSON.parse(savedIllnesses));
}, []);
// Medicine CRUD
const saveMedicine = () => {
if (!newMedName) return;
const newItem: Medicine = {
id: editingMed?.id || Date.now().toString(),
name: newMedName,
dose: newMedDose,
notes: newMedNotes,
reminderTime: reminderTime,
};
const updated = editingMed
? medicines.map((m) => (m.id === editingMed.id ? newItem : m))
: [...medicines, newItem];
setMedicines(updated);
localStorage.setItem("tia_medicines", JSON.stringify(updated));
resetMedForm();
};
const deleteMedicine = (id: string) => {
const updated = medicines.filter((m) => m.id !== id);
setMedicines(updated);
localStorage.setItem("tia_medicines", JSON.stringify(updated));
};
const editMedicine = (med: Medicine) => {
setEditingMed(med);
setNewMedName(med.name);
setNewMedDose(med.dose);
setNewMedNotes(med.notes);
setReminderTime(med.reminderTime || "08:00");
setShowAddMed(true);
};
const resetMedForm = () => {
setEditingMed(null);
setNewMedName("");
setNewMedDose("");
setNewMedNotes("");
setShowAddMed(false);
};
// Allergy CRUD
const saveAllergy = () => {
if (!newAllergyName) return;
const newItem: Allergy = {
id: editingAllergy?.id || Date.now().toString(),
name: newAllergyName,
severity: newAllergySeverity,
notes: newAllergyNotes,
};
const updated = editingAllergy
? allergies.map((a) => (a.id === editingAllergy.id ? newItem : a))
: [...allergies, newItem];
setAllergies(updated);
localStorage.setItem("tia_allergies", JSON.stringify(updated));
resetAllergyForm();
};
const deleteAllergy = (id: string) => {
const updated = allergies.filter((a) => a.id !== id);
setAllergies(updated);
localStorage.setItem("tia_allergies", JSON.stringify(updated));
};
const editAllergy = (allergy: Allergy) => {
setEditingAllergy(allergy);
setNewAllergyName(allergy.name);
setNewAllergySeverity(allergy.severity);
setNewAllergyNotes(allergy.notes);
setShowAddAllergy(true);
};
const resetAllergyForm = () => {
setEditingAllergy(null);
setNewAllergyName("");
setNewAllergySeverity("mild");
setNewAllergyNotes("");
setShowAddAllergy(false);
};
// Visit CRUD
const saveVisit = () => {
if (!newVisitDoctor) return;
const newItem: Visit = {
id: editingVisit?.id || Date.now().toString(),
doctorName: newVisitDoctor,
reason: newVisitReason,
date: newVisitDate || new Date().toISOString().split("T")[0],
notes: newVisitNotes,
};
const updated = editingVisit
? visits.map((v) => (v.id === editingVisit.id ? newItem : v))
: [...visits, newItem];
setVisits(updated);
localStorage.setItem("tia_visits", JSON.stringify(updated));
resetVisitForm();
};
const deleteVisit = (id: string) => {
const updated = visits.filter((v) => v.id !== id);
setVisits(updated);
localStorage.setItem("tia_visits", JSON.stringify(updated));
};
const editVisit = (visit: Visit) => {
setEditingVisit(visit);
setNewVisitDoctor(visit.doctorName);
setNewVisitReason(visit.reason);
setNewVisitDate(visit.date);
setNewVisitNotes(visit.notes);
setShowAddVisit(true);
};
const resetVisitForm = () => {
setEditingVisit(null);
setNewVisitDoctor("");
setNewVisitReason("");
setNewVisitDate("");
setNewVisitNotes("");
setShowAddVisit(false);
};
// Illness CRUD
const saveIllness = () => {
if (!newIllnessName) return;
const newItem: Illness = {
id: editingIllness?.id || Date.now().toString(),
name: newIllnessName,
startDate: newIllnessStart || new Date().toISOString().split("T")[0],
endDate: newIllnessEnd || undefined,
notes: newIllnessNotes,
};
const updated = editingIllness
? illnesses.map((i) => (i.id === editingIllness.id ? newItem : i))
: [...illnesses, newItem];
setIllnesses(updated);
localStorage.setItem("tia_illnesses", JSON.stringify(updated));
resetIllnessForm();
};
const deleteIllness = (id: string) => {
const updated = illnesses.filter((i) => i.id !== id);
setIllnesses(updated);
localStorage.setItem("tia_illnesses", JSON.stringify(updated));
};
const editIllness = (illness: Illness) => {
setEditingIllness(illness);
setNewIllnessName(illness.name);
setNewIllnessStart(illness.startDate);
setNewIllnessEnd(illness.endDate || "");
setNewIllnessNotes(illness.notes);
setShowAddIllness(true);
};
const resetIllnessForm = () => {
setEditingIllness(null);
setNewIllnessName("");
setNewIllnessStart("");
setNewIllnessEnd("");
setNewIllnessNotes("");
setShowAddIllness(false);
};
// Reminder save handler // Reminder save handler
const handleSetReminder = (medName: string) => { const handleSetReminder = (medName: string) => {
// In production, save to database with reminder time // In production, save to database with reminder time
@ -134,7 +377,7 @@ const SUPPLEMENTS = [
onClick={() => setTab("visits")} onClick={() => setTab("visits")}
className={`px-4 p-3 rounded-xl ${tab === "visits" ? "bg-rose-400 text-white" : "bg-white"}`} className={`px-4 p-3 rounded-xl ${tab === "visits" ? "bg-rose-400 text-white" : "bg-white"}`}
> >
Visits Doctor Visit
</button> </button>
<button <button
onClick={() => setTab("illness")} onClick={() => setTab("illness")}
@ -212,86 +455,303 @@ const SUPPLEMENTS = [
{tab === "medicine" && ( {tab === "medicine" && (
<div className="space-y-2"> <div className="space-y-2">
<h2 className="font-semibold mb-3">Supplements & Medicine</h2> <h2 className="font-semibold mb-3">Medicine & Supplements</h2>
{SUPPLEMENTS.map((supp) => (
<div key={supp.name} className="p-4 bg-white rounded-xl"> {/* Add Form */}
{showAddMed && (
<div className="p-4 bg-white rounded-xl space-y-3">
<input
type="text"
value={newMedName}
onChange={(e) => setNewMedName(e.target.value)}
placeholder="Medicine name"
className="w-full p-2 border rounded-lg"
/>
<input
type="text"
value={newMedDose}
onChange={(e) => setNewMedDose(e.target.value)}
placeholder="Dose (e.g., 5ml, 1 tablet)"
className="w-full p-2 border rounded-lg"
/>
<input
type="text"
value={newMedNotes}
onChange={(e) => setNewMedNotes(e.target.value)}
placeholder="Notes"
className="w-full p-2 border rounded-lg"
/>
<div className="flex gap-2">
<button onClick={saveMedicine} className="flex-1 py-2 bg-rose-400 text-white rounded-lg">
{editingMed ? "Update" : "Add"}
</button>
<button onClick={resetMedForm} className="flex-1 py-2 bg-gray-200 rounded-lg">
Cancel
</button>
</div>
</div>
)}
{medicines.length === 0 && !showAddMed ? (
<div className="p-4 bg-white rounded-xl">
<p className="text-gray-500">No medicines added</p>
</div>
) : (
medicines.map((med) => (
<div key={med.id} className="p-4 bg-white rounded-xl">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex-1"> <div className="flex-1">
<div className="font-medium">{supp.name}</div> <div className="font-medium">{med.name}</div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
{supp.dose} · {supp.notes} {med.dose} {med.notes && `· ${med.notes}`}
{med.reminderTime && ` · ⏰ ${med.reminderTime}`}
</div> </div>
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
{showReminder === supp.name ? ( <button onClick={() => editMedicine(med)} className="p-2 text-gray-400"></button>
<div className="flex items-center gap-2"> <button onClick={() => deleteMedicine(med.id)} className="p-2 text-red-400">🗑</button>
<input
type="time"
value={reminderTime}
onChange={(e) => setReminderTime(e.target.value)}
className="p-1 text-sm border rounded"
/>
<button
onClick={() => handleSetReminder(supp.name)}
className="px-2 py-1 bg-rose-400 text-white rounded text-sm"
>
</button>
<button
onClick={() => setShowReminder(null)}
className="text-gray-400"
>
</button>
</div> </div>
) : ( </div>
<button </div>
onClick={() => setShowReminder(supp.name)} ))
className="px-3 py-1 bg-gray-100 rounded-lg text-sm" )}
>
{!showAddMed && (
<button onClick={() => setShowAddMed(true)} className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500">
+ Add Medicine
</button> </button>
)} )}
</div> </div>
</div>
</div>
))}
</div>
)} )}
{tab === "allergies" && ( {tab === "allergies" && (
<div className="space-y-2"> <div className="space-y-2">
<h2 className="font-semibold mb-3">Known Allergies</h2> <h2 className="font-semibold mb-3">Known Allergies</h2>
{showAddAllergy && (
<div className="p-4 bg-white rounded-xl space-y-3">
<input
type="text"
value={newAllergyName}
onChange={(e) => setNewAllergyName(e.target.value)}
placeholder="Allergy name (e.g., Peanut, Milk)"
className="w-full p-2 border rounded-lg"
/>
<select
value={newAllergySeverity}
onChange={(e) => setNewAllergySeverity(e.target.value)}
className="w-full p-2 border rounded-lg"
>
<option value="mild">Mild</option>
<option value="moderate">Moderate</option>
<option value="severe">Severe</option>
</select>
<input
type="text"
value={newAllergyNotes}
onChange={(e) => setNewAllergyNotes(e.target.value)}
placeholder="Notes"
className="w-full p-2 border rounded-lg"
/>
<div className="flex gap-2">
<button onClick={saveAllergy} className="flex-1 py-2 bg-rose-400 text-white rounded-lg">
{editingAllergy ? "Update" : "Add"}
</button>
<button onClick={resetAllergyForm} className="flex-1 py-2 bg-gray-200 rounded-lg">
Cancel
</button>
</div>
</div>
)}
{allergies.length === 0 && !showAddAllergy ? (
<div className="p-4 bg-white rounded-xl"> <div className="p-4 bg-white rounded-xl">
<p className="text-gray-500">No allergies recorded</p> <p className="text-gray-500">No allergies recorded</p>
</div> </div>
<button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500"> ) : (
allergies.map((allergy) => (
<div key={allergy.id} className="p-4 bg-white rounded-xl">
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="font-medium">{allergy.name}</div>
<div className="text-sm text-gray-500">
<span className={allergy.severity === "severe" ? "text-red-500" : "text-orange-500"}>
{allergy.severity.toUpperCase()}
</span>
{allergy.notes && ` · ${allergy.notes}`}
</div>
</div>
<div className="flex gap-2">
<button onClick={() => editAllergy(allergy)} className="p-2 text-gray-400"></button>
<button onClick={() => deleteAllergy(allergy.id)} className="p-2 text-red-400">🗑</button>
</div>
</div>
</div>
))
)}
{!showAddAllergy && (
<button onClick={() => setShowAddAllergy(true)} className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500">
+ Add Allergy + Add Allergy
</button> </button>
)}
</div> </div>
)} )}
{tab === "visits" && ( {tab === "visits" && (
<div className="space-y-2"> <div className="space-y-2">
<h2 className="font-semibold mb-3">Doctor Visits</h2> <h2 className="font-semibold mb-3">Doctor Visits</h2>
{showAddVisit && (
<div className="p-4 bg-white rounded-xl space-y-3">
<input
type="text"
value={newVisitDoctor}
onChange={(e) => setNewVisitDoctor(e.target.value)}
placeholder="Doctor name"
className="w-full p-2 border rounded-lg"
/>
<input
type="text"
value={newVisitReason}
onChange={(e) => setNewVisitReason(e.target.value)}
placeholder="Reason (e.g., Checkup, Fever)"
className="w-full p-2 border rounded-lg"
/>
<input
type="date"
value={newVisitDate}
onChange={(e) => setNewVisitDate(e.target.value)}
className="w-full p-2 border rounded-lg"
/>
<input
type="text"
value={newVisitNotes}
onChange={(e) => setNewVisitNotes(e.target.value)}
placeholder="Notes"
className="w-full p-2 border rounded-lg"
/>
<div className="flex gap-2">
<button onClick={saveVisit} className="flex-1 py-2 bg-rose-400 text-white rounded-lg">
{editingVisit ? "Update" : "Add"}
</button>
<button onClick={resetVisitForm} className="flex-1 py-2 bg-gray-200 rounded-lg">
Cancel
</button>
</div>
</div>
)}
{visits.length === 0 && !showAddVisit ? (
<div className="p-4 bg-white rounded-xl"> <div className="p-4 bg-white rounded-xl">
<p className="text-gray-500">No visits recorded</p> <p className="text-gray-500">No visits recorded</p>
</div> </div>
<button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500"> ) : (
visits.map((visit) => (
<div key={visit.id} className="p-4 bg-white rounded-xl">
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="font-medium">{visit.doctorName}</div>
<div className="text-sm text-gray-500">
{new Date(visit.date).toLocaleDateString()}
{visit.reason && ` · ${visit.reason}`}
{visit.notes && ` · ${visit.notes}`}
</div>
</div>
<div className="flex gap-2">
<button onClick={() => editVisit(visit)} className="p-2 text-gray-400"></button>
<button onClick={() => deleteVisit(visit.id)} className="p-2 text-red-400">🗑</button>
</div>
</div>
</div>
))
)}
{!showAddVisit && (
<button onClick={() => setShowAddVisit(true)} className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500">
+ Add Visit + Add Visit
</button> </button>
)}
</div> </div>
)} )}
{tab === "illness" && ( {tab === "illness" && (
<div className="space-y-2"> <div className="space-y-2">
<h2 className="font-semibold mb-3">Illness Log</h2> <h2 className="font-semibold mb-3">Illness Log</h2>
{showAddIllness && (
<div className="p-4 bg-white rounded-xl space-y-3">
<input
type="text"
value={newIllnessName}
onChange={(e) => setNewIllnessName(e.target.value)}
placeholder="Illness (e.g., Cold, Fever, Flu)"
className="w-full p-2 border rounded-lg"
/>
<div className="grid grid-cols-2 gap-2">
<input
type="date"
value={newIllnessStart}
onChange={(e) => setNewIllnessStart(e.target.value)}
placeholder="Start date"
className="w-full p-2 border rounded-lg"
/>
<input
type="date"
value={newIllnessEnd}
onChange={(e) => setNewIllnessEnd(e.target.value)}
placeholder="End date"
className="w-full p-2 border rounded-lg"
/>
</div>
<input
type="text"
value={newIllnessNotes}
onChange={(e) => setNewIllnessNotes(e.target.value)}
placeholder="Notes"
className="w-full p-2 border rounded-lg"
/>
<div className="flex gap-2">
<button onClick={saveIllness} className="flex-1 py-2 bg-rose-400 text-white rounded-lg">
{editingIllness ? "Update" : "Add"}
</button>
<button onClick={resetIllnessForm} className="flex-1 py-2 bg-gray-200 rounded-lg">
Cancel
</button>
</div>
</div>
)}
{illnesses.length === 0 && !showAddIllness ? (
<div className="p-4 bg-white rounded-xl"> <div className="p-4 bg-white rounded-xl">
<p className="text-gray-500">No illnesses recorded</p> <p className="text-gray-500">No illnesses recorded</p>
</div> </div>
<button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500"> ) : (
illnesses.map((illness) => (
<div key={illness.id} className="p-4 bg-white rounded-xl">
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="font-medium">{illness.name}</div>
<div className="text-sm text-gray-500">
{new Date(illness.startDate).toLocaleDateString()}
{illness.endDate && ` - ${new Date(illness.endDate).toLocaleDateString()}`}
{illness.notes && ` · ${illness.notes}`}
</div>
</div>
<div className="flex gap-2">
<button onClick={() => editIllness(illness)} className="p-2 text-gray-400"></button>
<button onClick={() => deleteIllness(illness.id)} className="p-2 text-red-400">🗑</button>
</div>
</div>
</div>
))
)}
{!showAddIllness && (
<button onClick={() => setShowAddIllness(true)} className="w-full p-4 border-2 border-dashed border-gray-300 rounded-xl text-gray-500">
+ Log Illness + Log Illness
</button> </button>
)}
</div> </div>
)} )}
</div> </div>