+
-
{label}
+
+ {label}
+
+ {stale && (
+
+ cached
+
+ )}
+
- {value}
+
+ {value ?? 0}
+
{unit}
+
{status && (
{status}
@@ -33,8 +67,7 @@ function MetricCard({ icon: Icon, label, value, unit, status, statusColor }) {
);
}
-// Graph Card Component
-function GraphCard({ data, dataKey, label, icon: Icon, unit }) {
+function GraphCard({ data, dataKey, label, icon: Icon, unit, refLines }) {
return (
@@ -43,364 +76,313 @@ function GraphCard({ data, dataKey, label, icon: Icon, unit }) {
{label}
+ {data.length > 0 && (
+
{data.length} pts
+ )}
-
-
-
-
-
-
-
-
-
-
-
-
- new Date(v).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
- }
- stroke="#9ca3af"
- style={{ fontSize: "11px" }}
- />
-
-
-
- new Date(v).toLocaleString()}
- formatter={(value) => [`${value} ${unit}`, label]}
- />
-
-
-
-
+ {data.length === 0 ? (
+
+ No data yet — waiting for sensor readings…
+
+ ) : (
+
+
+
+
+ new Date(v + "Z").toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ })
+ }
+ stroke="#9ca3af"
+ style={{ fontSize: "11px" }}
+ />
+
+ new Date(v + "Z").toLocaleString()}
+ formatter={(value) => [`${value} ${unit}`, label]}
+ />
+ {refLines?.map((r) => (
+
+ ))}
+
+
+
+ )}
);
}
-function RealtimeGraphs() {
+export default function RealtimeGraphs({ patientId: propPatientId }) {
const [history, setHistory] = useState([]);
- const [currentData, setCurrentData] = useState(null);
+ const [current, setCurrent] = useState(null);
const [loading, setLoading] = useState(true);
+ const [isStale, setIsStale] = useState(false);
+ const [lastUpdate, setLastUpdate] = useState(null);
+ const [error, setError] = useState(null);
+ const pollerRef = useRef(null);
+
+ const patientId = propPatientId || localStorage.getItem("patientId");
+
+ const fetchData = useCallback(async () => {
+ if (!patientId) {
+ setError("No patient ID found");
+ setLoading(false);
+ return;
+ }
- const fetchHistory = useCallback(async () => {
try {
- const res = await fetch(`${BACKEND_URL}/api/sensordata/latest/50`);
- if (res.ok) {
- const data = await res.json();
- const reversed = Array.isArray(data) ? [...data].reverse() : [];
- setHistory(reversed);
- if (reversed.length > 0) {
- setCurrentData(reversed[reversed.length - 1]);
- }
+ const url = `${BACKEND_URL}/api/sensordata/patient/${patientId}/latest/50`;
+ console.log("Fetching IoT data from:", url);
+
+ const res = await fetch(url, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}`);
}
- } catch (error) {
- console.error("Error fetching data:", error);
- }
- }, []);
- useEffect(() => {
- let active = true;
+ const data = await res.json();
- const loadInitial = async () => {
- await fetchHistory();
- if (active) setLoading(false);
- };
+ if (Array.isArray(data) && data.length > 0) {
+ const latest = data[0];
+ const chartData = [...data].reverse();
- loadInitial();
- const interval = setInterval(fetchHistory, 5000);
+ setHistory(chartData);
+ setCurrent(latest);
- return () => {
- active = false;
- clearInterval(interval);
- };
- }, [fetchHistory]);
+ const receivedAtUTC = new Date(latest.receivedAt + "Z");
+ setLastUpdate(receivedAtUTC);
- if (loading) {
- return (
-
-
-
-
Loading patient monitoring...
-
-
- );
- }
+ const ageSeconds = (Date.now() - receivedAtUTC.getTime()) / 1000;
+ setIsStale(ageSeconds > 30);
+ } else {
+ setHistory([]);
+ setCurrent(null);
+ setLastUpdate(null);
+ setIsStale(false);
+ }
- const getHeartRateStatus = (bpm) => {
- if (!bpm || bpm === 0) return { text: "No Data", color: "bg-gray-100 text-gray-600" };
- if (bpm < 60) return { text: "Low", color: "bg-yellow-100 text-yellow-700" };
- if (bpm > 100) return { text: "High", color: "bg-red-100 text-red-700" };
+ setError(null);
+ } catch (e) {
+ console.error("Fetch error:", e);
+ setError("Cannot reach IoT service — showing last known patient data");
+ } finally {
+ setLoading(false);
+ }
+ }, [patientId]);
+
+ useEffect(() => {
+ fetchData();
+ pollerRef.current = setInterval(fetchData, 5000);
+ return () => clearInterval(pollerRef.current);
+ }, [fetchData]);
+
+ const hrStatus = (v) => {
+ if (!v || v === 0) return { text: "No Data", color: "bg-gray-100 text-gray-600" };
+ if (v < 60) return { text: "Low", color: "bg-yellow-100 text-yellow-700" };
+ if (v > 100) return { text: "High", color: "bg-red-100 text-red-700" };
return { text: "Normal", color: "bg-teal-100 text-teal-700" };
};
- const getSpo2Status = (spo2) => {
- if (!spo2 || spo2 === 0) return { text: "No Data", color: "bg-gray-100 text-gray-600" };
- if (spo2 < 95) return { text: "Low", color: "bg-orange-100 text-orange-700" };
+ const spo2Status = (v) => {
+ if (!v || v === 0) return { text: "No Data", color: "bg-gray-100 text-gray-600" };
+ if (v < 95) return { text: "Low", color: "bg-orange-100 text-orange-700" };
return { text: "Normal", color: "bg-teal-100 text-teal-700" };
};
- const getBodyTempStatus = (temp) => {
- if (!temp || temp === 0) return { text: "No Data", color: "bg-gray-100 text-gray-600" };
- if (temp < 36.1) return { text: "Low", color: "bg-blue-100 text-blue-700" };
- if (temp > 37.2) return { text: "High", color: "bg-red-100 text-red-700" };
+ const tempStatus = (v) => {
+ if (!v || v === 0) return { text: "No Data", color: "bg-gray-100 text-gray-600" };
+ if (v < 36.1) return { text: "Low", color: "bg-blue-100 text-blue-700" };
+ if (v > 37.2) return { text: "High", color: "bg-red-100 text-red-700" };
return { text: "Normal", color: "bg-teal-100 text-teal-700" };
};
- const hrStatus = currentData ? getHeartRateStatus(currentData.avgBpm) : { text: "No Data", color: "bg-gray-100 text-gray-600" };
- const spo2Status = currentData ? getSpo2Status(currentData.spo2) : { text: "No Data", color: "bg-gray-100 text-gray-600" };
- const bodyTempStatus = currentData ? getBodyTempStatus(currentData.waterTempC) : { text: "No Data", color: "bg-gray-100 text-gray-600" };
+ const roomTempStatus = (v) => {
+ if (v === null || v === undefined || v === 0) {
+ return { text: "No Data", color: "bg-gray-100 text-gray-600" };
+ }
+ if (v < 20) return { text: "Cold", color: "bg-blue-100 text-blue-700" };
+ if (v > 32) return { text: "Hot", color: "bg-red-100 text-red-700" };
+ return { text: "Comfort", color: "bg-teal-100 text-teal-700" };
+ };
- return (
-
+ const humidityStatus = (v) => {
+ if (v === null || v === undefined || v === 0) {
+ return { text: "No Data", color: "bg-gray-100 text-gray-600" };
+ }
+ if (v < 30) return { text: "Dry", color: "bg-yellow-100 text-yellow-700" };
+ if (v > 70) return { text: "Humid", color: "bg-blue-100 text-blue-700" };
+ return { text: "Normal", color: "bg-teal-100 text-teal-700" };
+ };
+
+ if (loading) {
+ return
Loading patient monitoring…
;
+ }
-
+ const hr = hrStatus(current?.avgBpm);
+ const sp = spo2Status(current?.spo2);
+ const bt = tempStatus(current?.waterTempC);
+ const rt = roomTempStatus(current?.roomTemp);
+ const hm = humidityStatus(current?.humidity);
- {/* Patient Vital Signs */}
-
-
-
-
Patient Vital Signs
+ return (
+
+
+ {isStale && !error && (
+
+
+ Sensor not sending live data — showing last recorded readings from database
+ {lastUpdate && (
+
+ Last: {lastUpdate.toLocaleTimeString()}
+
+ )}
+
+ )}
+
+ {error && (
+
+
+ {error}
+ {history.length > 0 && (
+ Showing {history.length} cached readings
+ )}
+ )}
-
+ {!loading && history.length === 0 && (
+
+
+
No sensor data found for this patient
+
Make sure the device is assigned and sending data
+
+ )}
+
-
- {/* Environmental Conditions */}
-
-
-
-
-
-
Environmental Conditions
-
-
-
-
-
-
-
-
- {/* Real-Time Monitoring Graphs */}
-
-
-
-
Real-Time Monitoring
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
- {/* Health Reference Ranges */}
-
-
-
- Health Reference Ranges
-
-
-
-
-
-
- Heart Rate (BPM)
-
-
-
-
- Normal: 60-100
-
-
-
- Low: <60
-
-
-
- High: >100
-
-
-
-
-
-
-
- SpO2 (%)
-
-
-
-
- Normal: 95-100%
-
-
-
- Low: 90-94%
-
-
-
- Critical: <90%
-
-
-
-
-
-
-
- Body Temperature (°C)
-
-
-
-
- Normal: 36.1-37.2
-
-
-
- Low: <36.1
-
-
-
- High: >37.2
-
-
-
-
-
-
-
- Room Humidity (%)
-
-
-
-
- Optimal: 30-60%
-
-
-
- Dry: <30%
-
-
-
- Humid: >60%
-
-
-
-
-
-
-
- {/* Status Footer */}
-
-
-
-
-
24/7 Monitoring Active
-
- {currentData && (
-
- Last update: {new Date(currentData.receivedAt).toLocaleTimeString()}
-
- )}
-
-
-
);
-}
-
-export default RealtimeGraphs;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/pages/AdminPages/IotDevices.jsx b/src/pages/AdminPages/IotDevices.jsx
index 8710554..f18902c 100644
--- a/src/pages/AdminPages/IotDevices.jsx
+++ b/src/pages/AdminPages/IotDevices.jsx
@@ -1,14 +1,192 @@
-import React from "react";
+import React, { useEffect, useMemo, useState } from "react";
+import { API_BASE_URL } from "../../api";
function IotDevices() {
+ const [devices, setDevices] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [search, setSearch] = useState("");
+ const [error, setError] = useState("");
+
+ const fetchDevices = async () => {
+ try {
+ setLoading(true);
+ setError("");
+
+ const token = localStorage.getItem("token");
+
+ const response = await fetch(`${API_BASE_URL}/api/sensordata/devices`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ const text = await response.text();
+ let data = [];
+
+ try {
+ data = text ? JSON.parse(text) : [];
+ } catch {
+ data = [];
+ }
+
+ if (!response.ok) {
+ throw new Error(data.message || "Failed to fetch devices");
+ }
+
+ setDevices(Array.isArray(data) ? data : []);
+ } catch (err) {
+ console.error(err);
+ setError(err.message || "Unable to load IoT devices");
+ setDevices([]);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchDevices();
+ }, []);
+
+ const filteredDevices = useMemo(() => {
+ return devices.filter((device) => {
+ const q = search.toLowerCase();
+
+ return (
+ String(device.deviceId || "").toLowerCase().includes(q) ||
+ String(device.status || "").toLowerCase().includes(q) ||
+ String(device.userId || "").toLowerCase().includes(q)
+ );
+ });
+ }, [devices, search]);
+
+ const getStatusStyle = (status) => {
+ switch ((status || "").toUpperCase()) {
+ case "ACTIVE":
+ return "bg-green-100 text-green-700";
+ case "INACTIVE":
+ return "bg-red-100 text-red-700";
+ case "UNASSIGNED":
+ return "bg-yellow-100 text-yellow-700";
+ default:
+ return "bg-gray-100 text-gray-700";
+ }
+ };
+
+ const maskToken = (token) => {
+ if (!token) return "-";
+ if (token.length <= 6) return token;
+ return `${token.slice(0, 3)}***${token.slice(-3)}`;
+ };
+
+ const totalDevices = devices.length;
+ const activeDevices = devices.filter(
+ (d) => String(d.status || "").toUpperCase() === "ACTIVE"
+ ).length;
+ const assignedDevices = devices.filter((d) => d.userId !== null && d.userId !== undefined).length;
+
return (
-
IoT Devices
-
- IoT device list and live status will appear here.
-
+
+
+
IoT Devices
+
+ Monitor device assignment and status
+
+
+
+
+ Refresh
+
+
+
+
+
+
Total Devices
+
{totalDevices}
+
+
+
+
Active Devices
+
{activeDevices}
+
+
+
+
Assigned Devices
+
{assignedDevices}
+
+
+
+
setSearch(e.target.value)}
+ className="w-full mb-4 px-4 py-2 bg-gray-100 rounded-full text-sm outline-none"
+ />
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {loading &&
Loading devices...
}
+
+ {!loading && filteredDevices.length === 0 && (
+
No IoT devices found
+ )}
+
+ {!loading && filteredDevices.length > 0 && (
+
+ {filteredDevices.map((device) => (
+
+
+
+ {device.deviceId || "-"}
+
+
+ Token: {maskToken(device.deviceToken)}
+
+
+
+
+
+
Patient ID
+
+ {device.userId ?? "Unassigned"}
+
+
+
+
+
Status
+
+ {device.status || "UNKNOWN"}
+
+
+
+
+
Device Row ID
+
{device.id}
+
+
+
+ ))}
+
+ )}
);
}
-export default IotDevices
\ No newline at end of file
+export default IotDevices;
\ No newline at end of file
diff --git a/src/pages/AdminPages/PatientManagement.jsx b/src/pages/AdminPages/PatientManagement.jsx
index bc62df2..04b784d 100644
--- a/src/pages/AdminPages/PatientManagement.jsx
+++ b/src/pages/AdminPages/PatientManagement.jsx
@@ -1,109 +1,91 @@
import React, { useEffect, useState } from "react";
-import { fetchPatient, deletePatient, updatePatient } from "./service/patientconnecter";
-import { API_BASE_URL } from "../../api";
+import {
+ fetchPatient,
+ deletePatient,
+ updatePatient,
+ createPatient,
+} from "./service/patientconnecter";
function PatientManagement() {
const [patients, setPatient] = useState([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState("");
+
const [showModal, setShowModal] = useState(false);
- const [formData, setFormData] = useState({
- name: "",
- dateOfBirth: "",
- address: "",
- email: "",
- nicNo: "",
- gender: "",
- contactNo: "",
- guardiansName: "",
- guardiansContactNo: "",
- username: "",
- password: "",
- bloodType: "",
- city: "",
- district: "",
- dispostalCodet: "",
- guardianRelationship: "",
- guardianEmail: "",
- medicalConditions: "",
- allergies: "",
- currentMedications: "",
- pastSurgeries: "",
- emergencyNotes: "",
- });
- const [error, setError] = useState("");
- const [submitting, setSubmitting] = useState(false);
-
- const [showEditModal, setShowEditModal] = useState(false);
- const [editPatientId, setEditPatientId] = useState(null);
- const [editFormData, setEditFormData] = useState({
- name: "",
- dateOfBirth: "",
- address: "",
- email: "",
- nicNo: "",
- gender: "",
- contactNo: "",
- guardiansName: "",
- guardiansContactNo: "",
- username: "",
- password: "",
- bloodType: "",
- city: "",
- district: "",
- dispostalCodet: "",
- guardianRelationship: "",
- guardianEmail: "",
- medicalConditions: "",
- allergies: "",
- currentMedications: "",
- pastSurgeries: "",
- emergencyNotes: "",
- });
-//new
- const [showViewModal, setShowViewModal] = useState(false);
- const [selectedPatient, setSelectedPatient] = useState(null);
+ const [showEditModal, setShowEditModal] = useState(false);
+ const [showViewModal, setShowViewModal] = useState(false);
+
+ const [selectedPatient, setSelectedPatient] = useState(null);
+ const [editPatientId, setEditPatientId] = useState(null);
+
+ const [error, setError] = useState("");
+ const [submitting, setSubmitting] = useState(false);
+
+ const initialFormData = {
+ name: "",
+ dateOfBirth: "",
+ address: "",
+ email: "",
+ nicNo: "",
+ gender: "",
+ contactNo: "",
+ guardiansName: "",
+ guardiansContactNo: "",
+ username: "",
+ password: "",
+ bloodType: "",
+ city: "",
+ district: "",
+ postalCode: "",
+ guardianRelationship: "",
+ guardianEmail: "",
+ medicalConditions: "",
+ allergies: "",
+ currentMedications: "",
+ pastSurgeries: "",
+ emergencyNotes: "",
+ deviceId: "",
+ };
+
+ const [formData, setFormData] = useState(initialFormData);
+ const [editFormData, setEditFormData] = useState(initialFormData);
useEffect(() => {
- async function load() {
- try {
- const data = await fetchPatient();
- setPatient(data || []);
- } catch (err) {
- console.error(err);
- } finally {
- setLoading(false);
- }
- }
- load();
+ loadPatients();
}, []);
- // search bar
- const filteredPatients = patients.filter((pt) =>
- pt.name?.toLowerCase().includes(search.toLowerCase())
- );
+ async function loadPatients() {
+ try {
+ setLoading(true);
+ const data = await fetchPatient();
+ setPatient(data || []);
+ } catch (err) {
+ console.error(err);
+ setPatient([]);
+ } finally {
+ setLoading(false);
+ }
+ }
- const handleDelete = async (PatientId) => {
- if (!window.confirm("Reject this Patient?")) return;
-
- try {
- await deletePatient(PatientId);
-
- setPatient((prev) =>
- prev.filter((pt) => pt.Id !== PatientId)
- );
-
- alert("Patient rejected successfully");
- } catch (error) {
- console.error(error);
- alert("Failed to reject Patient")}
- };
+ const filteredPatients = patients.filter((patient) =>
+ patient.name?.toLowerCase().includes(search.toLowerCase())
+ );
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
+ const handleEditInputChange = (e) => {
+ const { name, value } = e.target;
+ setEditFormData((prev) => ({ ...prev, [name]: value }));
+ };
+
+ const handleView = (patient) => {
+ setSelectedPatient(patient);
+ setShowViewModal(true);
+ };
+
const handleEdit = (patient) => {
setEditPatientId(patient.id || patient.Id);
@@ -115,132 +97,99 @@ function PatientManagement() {
nicNo: patient.nicNo || "",
gender: patient.gender || "",
contactNo: patient.contactNo || "",
+ guardiansName: patient.guardiansName || "",
+ guardiansContactNo: patient.guardiansContactNo || "",
+ username: patient.username || "",
+ password: "",
+ bloodType: patient.bloodType || "",
+ city: patient.city || "",
+ district: patient.district || "",
+ postalCode: patient.postalCode || "",
+ guardianRelationship: patient.guardianRelationship || "",
+ guardianEmail: patient.guardianEmail || "",
+ medicalConditions: patient.medicalConditions || "",
+ allergies: patient.allergies || "",
+ currentMedications: patient.currentMedications || "",
+ pastSurgeries: patient.pastSurgeries || "",
+ emergencyNotes: patient.emergencyNotes || "",
+ deviceId: patient.deviceId || "",
});
setShowEditModal(true);
- };
- //new
- const handleView = (patient) => {
- setSelectedPatient(patient);
- setShowViewModal(true);
};
- const handleEditInputChange = (e) => {
- const { name, value } = e.target;
- setEditFormData((prev) => ({ ...prev, [name]: value }));
+ const handleDelete = async (patientId) => {
+ if (!window.confirm("Delete this patient?")) return;
+
+ try {
+ await deletePatient(patientId);
+ setPatient((prev) => prev.filter((p) => (p.id || p.Id) !== patientId));
+ alert("Patient deleted successfully");
+ } catch (error) {
+ console.error(error);
+ alert(error.message || "Failed to delete patient");
+ }
};
const handleCreatePatient = async (e) => {
- e.preventDefault();
- setError("");
-
- if (
- !formData.name ||
- !formData.email ||
- !formData.username ||
- !formData.password ||
- !formData.address ||
- !formData.dateOfBirth ||
- !formData.allergies ||
- !formData.nicNo ||
- !formData.gender ||
- !formData.contactNo ||
- !formData.bloodType ||
- !formData.city ||
- !formData.currentMedications ||
- !formData.dispostalCodet ||
- !formData.district ||
- !formData.guardianEmail ||
- !formData.guardianRelationship ||
- !formData.guardiansContactNo ||
- !formData.guardiansName ||
- !formData.medicalConditions
- ) {
- setError("Please fill in all required fields");
- return;
- }
-
+ e.preventDefault();
+ setError("");
+
+ if (
+ !formData.name ||
+ !formData.email ||
+ !formData.username ||
+ !formData.password ||
+ !formData.dateOfBirth ||
+ !formData.address ||
+ !formData.nicNo ||
+ !formData.gender ||
+ !formData.contactNo
+ ) {
+ setError("Please fill in all required fields");
+ return;
+ }
+
+ try {
setSubmitting(true);
- try {
- const token = localStorage.getItem("token");
- const response = await fetch(`${API_BASE_URL}/api/admin/patient/create`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${token}`,
- },
- body: JSON.stringify(formData),
- });
-
- if (!response.ok) {
- const text = await response.text();
- setError(text || "Failed to create Patient");
- setSubmitting(false);
- return;
- }
-
- // Reset form and close modal
- setFormData({
- name: "",
- dateOfBirth: "",
- address: "",
- email: "",
- nicNo: "",
- gender: "",
- contactNo: "",
- guardiansName: "",
- guardiansContactNo: "",
- username: "",
- password: "",
- bloodType: "",
- city: "",
- district: "",
- dispostalCodet: "",
- guardianRelationship: "",
- guardianEmail: "",
- medicalConditions: "",
- allergies: "",
- currentMedications: "",
- pastSurgeries: "",
- emergencyNotes: "",
- createdAt: "",
- updatedAt: "",
- });
- setShowModal(false);
- setError("");
-
- const data = await fetchPatient();
- setPatient(response.data || []);
- } catch (e) {
- setError("Unable to connect to server. Please try again.");
- } finally {
- setSubmitting(false);
-
- }
- };
- const handleUpdatePatient = async (e) => {
- e.preventDefault();
-
- try {
- await updatePatient(editPatientId, editFormData);
-
- const data = await fetchPatient();
- setPatient(data || []);
- setShowEditModal(false);
-
- alert("Patient updated successfully");
- } catch (err) {
- console.error(err);
- alert(err.message || "Failed to update Patient");
- }
- };
+ await createPatient(formData);
+ setShowModal(false);
+ setFormData(initialFormData);
+ await loadPatients();
+ alert("Patient created successfully");
+ } catch (err) {
+ console.error(err);
+ setError(err.message || "Failed to create patient");
+ } finally {
+ setSubmitting(false);
+ }
+ };
+ const handleUpdatePatient = async (e) => {
+ e.preventDefault();
+
+ try {
+ await updatePatient(editPatientId, editFormData);
+ setShowEditModal(false);
+ await loadPatients();
+ alert("Patient updated successfully");
+ } catch (err) {
+ console.error(err);
+ alert(err.message || "Failed to update patient");
+ }
+ };
+
+ const resetCreateModal = () => {
+ setShowModal(false);
+ setError("");
+ setFormData(initialFormData);
+ };
return (
Patients
-
setShowModal(true)}
className="bg-gradient-to-r from-blue-500 to-purple-600 text-white text-sm px-4 py-1.5 rounded-full hover:shadow-lg transition"
>
@@ -250,42 +199,42 @@ function PatientManagement() {
setSearch(e.target.value)}
className="w-full mb-4 px-4 py-2 bg-gray-100 rounded-full text-sm outline-none"
/>
- {loading && (
-
Loading...
- )}
+ {loading &&
Loading...
}
{!loading && filteredPatients.length === 0 && (
-
No Patient found
+
No patients found
)}
{filteredPatients.map((patient) => (
-
- {/* Leftside-Patient Info */}
-
+
+
+
{patient.name}
+
+ {patient.email || "No email"}
+
+
+ Device: {patient.deviceId || "Not assigned"}
+
+
- {/* RIGHT SIDE – ACTION BUTTONS */}
-
+
handleView(patient)}
className="text-xs px-3 py-1 rounded-full bg-blue-100 text-blue-700 hover:bg-blue-200"
>
View
+
handleEdit(patient)}
className="text-xs px-3 py-1 rounded-full bg-yellow-100 text-yellow-700 hover:bg-yellow-200"
@@ -294,112 +243,82 @@ function PatientManagement() {
handleDelete(patient.id)}
- className="text-xs px-3 py-1 rounded-full bg-red-100 text-red-700 hover:bg-red-200"
+ onClick={() => handleDelete(patient.id || patient.Id)}
+ className="text-xs px-3 py-1 rounded-full bg-red-100 text-red-700 hover:bg-red-200"
>
- Delete
+ Delete
))}
- {/* View Patient Modal */}
- {showViewModal && selectedPatient && (
-
-
-
-
- {/* Header */}
-
-
- Patient Profile
-
- setShowViewModal(false)}
- className="w-9 h-9 flex items-center justify-center rounded-full bg-gray-200 hover:bg-red-500 hover:text-white transition"
- >
- ×
-
-
-
- {/* Scrollable Body */}
-
-
-
-
-
-
-
-
+ {showViewModal && selectedPatient && (
+
+
+
+
Patient Profile
+ setShowViewModal(false)}
+ className="w-9 h-9 flex items-center justify-center rounded-full bg-gray-200 hover:bg-red-500 hover:text-white transition"
+ >
+ ×
+
+
- {/* Right column */}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
- {/* Guardian Details */}
-
-
-
-
+
+
+
+
+
+
+
+
+
+
- {/* Footer */}
-
- setShowViewModal(false)}
- className="px-6 py-2 rounded-lg bg-blue-600 text-white font-medium hover:bg-blue-700 transition"
- >
- Close
-
-
-
+
+ setShowViewModal(false)}
+ className="px-6 py-2 rounded-lg bg-blue-600 text-white font-medium hover:bg-blue-700 transition"
+ >
+ Close
+
- )}
+
+ )}
- {/* Create Patient Modal */}
{showModal && (
-
+
Create New Patient
{
- setShowModal(false);
- setError("");
- setFormData({
- name: "",
- dateOfBirth: "",
- address: "",
- email: "",
- nicNo: "",
- gender: "",
- contactNo: "",
- guardiansName: "",
- guardiansContactNo: "",
- username: "",
- password: "",
- bloodType: "",
- city: "",
- district: "",
- dispostalCodet: "",
- guardianRelationship: "",
- guardianEmail: "",
- medicalConditions: "",
- allergies: "",
- currentMedications: "",
- pastSurgeries: "",
- emergencyNotes: "",
- });
- }}
+ onClick={resetCreateModal}
className="text-gray-500 hover:text-gray-700 text-2xl"
>
×
@@ -415,80 +334,28 @@ function PatientManagement() {
)}
-
-
- Name *
-
-
-
+
+
+
-
-
- Date of Birth *
-
-
-
+
+
+
-
-
- Address *
-
-
-
+
+
+
-
-
- Email *
-
-
-
+
+
+
-
-
- NIC No *
-
-
-
+
+
+
-
- Gender *
-
+
Gender *
Male
@@ -509,7 +375,6 @@ function PatientManagement() {
value="FEMALE"
checked={formData.gender === "FEMALE"}
onChange={handleInputChange}
- required
className="accent-blue-500"
/>
Female
@@ -517,235 +382,92 @@ function PatientManagement() {
-
-
- Contact No *
-
-
-
+
+
+
-
-
- Guardians Name *
-
-
-
+
+
+
-
-
- Guardians ContactNo *
-
-
-
+
+
+
-
-
- Username *
-
-
-
+
+
+
-
-
- Password *
-
-
-
-
-
-
- Blood Type *
-
-
-
-
-
-
- City *
-
-
-
+
+
+
-
-
- District *
-
-
-
+
+
+
-
-
- District Postal Code *
-
-
-
+
+
+
-
-
- Guardian Relationship *
-
-
-
+
+
+
-
-
- Guardian Email *
-
-
-
+
+
+
-
-
- Medical Conditions *
-
-
-
+
+
+
-
-
- Allergies *
-
-
-
+
+
+
-
{
- setShowModal(false);
- setError("");
- setFormData({
- name: "",
- dateOfBirth: "",
- address: "",
- email: "",
- nicNo: "",
- gender: "",
- contactNo: "",
- guardiansName: "",
- guardiansContactNo: "",
- username: "",
- password: "",
- bloodType: "",
- city: "",
- district: "",
- dispostalCodet: "",
- guardianRelationship: "",
- guardianEmail: "",
- medicalConditions: "",
- allergies: "",
- currentMedications: "",
- pastSurgeries: "",
- emergencyNotes: "",
- });
- }}
+ onClick={resetCreateModal}
className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
>
Cancel
@@ -765,7 +487,7 @@ function PatientManagement() {
{showEditModal && (
-
+
Edit Patient
@@ -991,19 +558,36 @@ function PatientManagement() {
)}
+
);
}
+const Field = ({ label, children }) => (
+
+ {label}
+ {children}
+
+);
+
const InfoItem = ({ label, value }) => (
-
- {label}
-
-
- {value || "-"}
-
+
{label}
+
{value || "-"}
);
-export default PatientManagement;
+export default PatientManagement;
\ No newline at end of file
diff --git a/src/pages/AdminPages/service/patientconnecter.jsx b/src/pages/AdminPages/service/patientconnecter.jsx
index 8ae82a1..0e595e7 100644
--- a/src/pages/AdminPages/service/patientconnecter.jsx
+++ b/src/pages/AdminPages/service/patientconnecter.jsx
@@ -1,44 +1,66 @@
-const BASE_URL = import.meta.env.VITE_API_URL + "/api/patient";
+import { API_BASE_URL } from "../../../api";
-async function handleResponse(res) {
- if (!res.ok) {
- const text = await res.text();
- throw new Error(text || `Request failed with status ${res.status}`);
+const getAuthHeaders = () => {
+ const token = localStorage.getItem("token");
+ return {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ };
+};
+
+export async function fetchPatient() {
+ const response = await fetch(`${API_BASE_URL}/api/patient/get`, {
+ method: "GET",
+ headers: getAuthHeaders(),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch patients");
}
- if (res.status === 204) return null;
+ return response.json();
+}
+
+export async function createPatient(payload) {
+ const response = await fetch(`${API_BASE_URL}/api/admin/patient/create`, {
+ method: "POST",
+ headers: getAuthHeaders(),
+ body: JSON.stringify(payload),
+ });
- const contentType = res.headers.get("content-type") || "";
- if (contentType.includes("application/json")) {
- return res.json();
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(text || "Failed to create patient");
}
- return null;
+ return response.json();
}
-export async function fetchPatient() {
- const res = await fetch(`${BASE_URL}/get`);
- return handleResponse(res);
+export async function updatePatient(patientId, payload) {
+ const response = await fetch(`${API_BASE_URL}/api/admin/patient/update/${patientId}`, {
+ method: "PUT",
+ headers: getAuthHeaders(),
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(text || "Failed to update patient");
+ }
+
+ return response.json();
}
export async function deletePatient(patientId) {
- const res = await fetch(`${BASE_URL}/delete/${patientId}`, {
+ const response = await fetch(`${API_BASE_URL}/api/patient/delete/${patientId}`, {
method: "DELETE",
+ headers: getAuthHeaders(),
});
- return handleResponse(res);
-}
-export async function updatePatient(PatientId, data) {
- const token = localStorage.getItem("token");
-
- const res = await fetch(`${BASE_URL}/update/${PatientId}`, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${token}`,
- },
- body: JSON.stringify(data),
- });
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(text || "Failed to delete patient");
+ }
- return handleResponse(res);
+ return response.text();
}
\ No newline at end of file