-
Organisation & Assignment
-
- This route is prepared for organisation overview and assignment-related
- admin work. The team can continue implementation here using the shared
- shell layout.
-
+
+
+
+
+
Organisation Overview
+
{selectedOrg.name}
+
+ View organisation information, staff count, current status, and
+ prepare for organisation-related admin workflows.
+
+
+
+
+
+
+
+ Staff Management
+ View and manage staff linked with this organisation
+
+
+
+
+
+
+
+ Dashboard
+ Return to the main administrator workspace
+
+
+
+
+
+
+
+
+ {stats.map((item, index) => {
+ const Icon = item.icon;
+
+ return (
+
+
+
+
{item.title}
+
{item.value}
+
+
+
+
+
+
+
+ {item.description}
+
+ );
+ })}
+
+
+
+
+
+
Organisation Details
+
+ {organizationOptions.length > 1 && (
+
+ setSelectedOrgId(e.target.value)}
+ options={organizationOptions}
+ />
+
+ )}
+
+
+
+
+
Organisation ID
+
+ {selectedOrg._id || "-"}
+
+
+
+
+
Name
+
{selectedOrg.name || "-"}
+
+
+
+
Description
+
+ {selectedOrg.description || "No description available."}
+
+
+
+
+
Active
+
+ {selectedOrg.active ? "Yes" : "No"}
+
+
+
+
+
Created By
+
+ {selectedOrg.createdBy || "-"}
+
+
+
+
+
Created At
+
+ {formatDate(selectedOrg.created_at)}
+
+
+
+
+
Updated At
+
+ {formatDate(selectedOrg.updated_at)}
+
+
+
+
+
Staff Count
+
+ {selectedOrg.staff?.length ?? 0}
+
+
+
+
+
+
+ Overview Summary
+
+
+
+
+ Organisation data is connected to this admin account
+
+
+
+
+ {selectedOrg.staff?.length ?? 0} linked staff member(s)
+
+
+
+
+ Status: {selectedOrg.active ? "Active" : "Inactive"}
+
+
+
+
+ Ready for future organisation workflows and extensions
+
+
+
+
+
+
+
+
);
}
\ No newline at end of file
diff --git a/guardian-admin-dashboard/src/pages/PatientOverviewPage.jsx b/guardian-admin-dashboard/src/pages/PatientOverviewPage.jsx
new file mode 100644
index 000000000..5b49d43a9
--- /dev/null
+++ b/guardian-admin-dashboard/src/pages/PatientOverviewPage.jsx
@@ -0,0 +1,349 @@
+import { useEffect, useMemo, useState } from "react";
+import { getAllPatients, getPatientOverview } from "../services/patientService";
+
+
+
+export default function PatientOverviewPage() {
+ const [patients, setPatients] = useState([]);
+ const [selectedPatientId, setSelectedPatientId] = useState("");
+ const [searchTerm, setSearchTerm] = useState("");
+
+ const [overview, setOverview] = useState(null);
+ const [loadingPatients, setLoadingPatients] = useState(true);
+ const [loadingOverview, setLoadingOverview] = useState(false);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ const fetchPatients = async () => {
+ try {
+ setLoadingPatients(true);
+ setError("");
+
+ const data = await getAllPatients();
+ const patientList = Array.isArray(data) ? data : data?.patients || [];
+ setPatients(patientList);
+ } catch (err) {
+ console.error("Failed to load patients:", err);
+ setError(err?.response?.data?.message || "Failed to load patients");
+ } finally {
+ setLoadingPatients(false);
+ }
+ };
+
+ fetchPatients();
+ }, []);
+
+ const filteredPatients = useMemo(() => {
+ const term = searchTerm.trim().toLowerCase();
+
+ if (!term) return patients;
+
+ return patients.filter((patient) => {
+ const fullname = patient?.fullname?.toLowerCase() || "";
+ const id = patient?._id?.toLowerCase() || "";
+ return fullname.includes(term) || id.includes(term);
+ });
+ }, [patients, searchTerm]);
+
+ const handleSelectPatient = async (patientId) => {
+ try {
+ setSelectedPatientId(patientId);
+ setLoadingOverview(true);
+ setError("");
+
+ const selectedPatient = patients.find(p => p._id === patientId);
+ const orgId = selectedPatient?.organization;
+ const data = await getPatientOverview(patientId, orgId);
+
+ setOverview(data);
+ } catch (err) {
+ console.error("Failed to load patient overview:", err);
+ console.log("Backend error data:", err?.response?.data);
+ setError(
+ err?.response?.data?.message || "Failed to load patient overview"
+ );
+ setOverview(null);
+ setSelectedPatientId("");
+ } finally {
+ setLoadingOverview(false);
+ }
+ };
+
+ const handleChangePatient = () => {
+ setSelectedPatientId("");
+ setOverview(null);
+ setError("");
+ };
+
+ const patient = overview?.patient || {};
+ const records = overview?.records || [];
+ const carePlans = overview?.carePlans || [];
+ const tasks = overview?.tasks || [];
+ const logs = overview?.logs || [];
+
+ return (
+
+
+
Patient Overview
+
+
+
+
+ setSearchTerm(e.target.value)}
+ style={styles.input}
+ />
+
+
+
+ {loadingPatients ? (
+
Loading patients...
+ ) : error && !overview ? (
+
{error}
+ ) : !selectedPatientId ? (
+
+ {filteredPatients.length === 0 ? (
+
No patients found.
+ ) : (
+ filteredPatients.map((item) => (
+
+
+
{item.fullname}
+ {/*
ID: {item._id}
*/}
+
+ Gender: {item.gender || "-"}
+
+
Age: {item.age ?? "-"}
+
+
+
+
+ ))
+ )}
+
+ ) : null}
+
+ {loadingOverview ? (
+
Loading patient overview...
+ ) : overview ? (
+ <>
+
+
+
+
+ {patient.fullname || "Unknown Patient"}
+
+
Gender: {patient.gender || "-"}
+
Date of Birth: {new Date(patient.dateOfBirth).toLocaleDateString() || "-"}
+
Organization: {patient.organization?.name || "Guardian Moniter"}
+
+
Caretaker
+
{patient.caretaker?.fullname || "-"}
+
{patient.caretaker?.email || "-"}
+
+
Assigned Nurses
+ {patient.assignedNurses?.length ? (
+ patient.assignedNurses.map((nurse) => (
+
+
{nurse.fullname}
+
{nurse.email}
+
+ ))
+ ) : (
+
No nurses assigned
+ )}
+
+
Assigned Doctor
+
{patient.assignedDoctor?.fullname || "-"}
+
{patient.assignedDoctor?.email || "-"}
+
+
+
+
+
Records
+
{records.length}
+
+
+
Care Plans
+
{carePlans.length}
+
+
+
Tasks
+
{tasks.length}
+
+
+
+
+
+
+
+
+ >
+ ) : null}
+
+
+ );
+}
+
+function Section({ title, items }) {
+ return (
+
+
{title}
+ {!items?.length ? (
+
No {title.toLowerCase()} available.
+ ) : (
+
+ {items.map((item, index) => (
+
+
+ {JSON.stringify(item, null, 2)}
+
+
+ ))}
+
+ )}
+
+ );
+}
+
+const styles = {
+ page: {
+ padding: "24px",
+ },
+ card: {
+ background: "#f9fbfd",
+ borderRadius: "28px",
+ padding: "28px",
+ border: "1px solid #d9e4ee",
+ },
+ title: {
+ color: "#1f4788",
+ fontSize: "2rem",
+ fontWeight: 700,
+ marginBottom: "24px",
+ },
+ topSection: {
+ display: "flex",
+ gap: "16px",
+ marginBottom: "20px",
+ },
+ searchBlock: {
+ width: "100%",
+ },
+ label: {
+ display: "block",
+ marginBottom: "10px",
+ fontSize: "1.1rem",
+ fontWeight: 600,
+ color: "#1f4788",
+ },
+ input: {
+ width: "100%",
+ maxWidth: "520px",
+ padding: "16px 20px",
+ borderRadius: "22px",
+ border: "1px solid #d9e4ee",
+ fontSize: "1rem",
+ outline: "none",
+ },
+ listWrap: {
+ display: "grid",
+ gap: "14px",
+ marginBottom: "24px",
+ },
+ patientRow: {
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ background: "#ffffff",
+ borderRadius: "18px",
+ padding: "18px",
+ border: "1px solid #d9e4ee",
+ },
+ patientName: {
+ margin: "0 0 8px 0",
+ color: "#1f4788",
+ },
+ patientMeta: {
+ margin: "4px 0",
+ color: "#5b6b84",
+ },
+ button: {
+ background: "linear-gradient(90deg, #6fb2dc, #4f95c5)",
+ color: "#fff",
+ border: "none",
+ borderRadius: "16px",
+ padding: "12px 18px",
+ cursor: "pointer",
+ fontWeight: 600,
+ },
+ secondaryButton: {
+ background: "#ffffff",
+ color: "#1f4788",
+ border: "1px solid #d9e4ee",
+ borderRadius: "14px",
+ padding: "10px 16px",
+ cursor: "pointer",
+ fontWeight: 600,
+ marginTop: "6px",
+ marginBottom: "14px",
+ },
+ overviewCard: {
+ background: "#fff",
+ borderRadius: "20px",
+ padding: "20px",
+ marginTop: "10px",
+ border: "1px solid #d9e4ee",
+ },
+ sectionTitle: {
+ color: "#1f4788",
+ marginBottom: "12px",
+ },
+ subTitle: {
+ color: "#1f4788",
+ marginTop: "16px",
+ marginBottom: "8px",
+ },
+ statsRow: {
+ display: "grid",
+ gridTemplateColumns: "repeat(4, 1fr)",
+ gap: "14px",
+ marginTop: "20px",
+ },
+ statCard: {
+ background: "#fff",
+ borderRadius: "18px",
+ padding: "18px",
+ textAlign: "center",
+ border: "1px solid #d9e4ee",
+ },
+ emptyBox: {
+ background: "#fff",
+ borderRadius: "14px",
+ padding: "16px",
+ border: "1px solid #d9e4ee",
+ },
+ itemBox: {
+ background: "#fff",
+ borderRadius: "14px",
+ padding: "16px",
+ border: "1px solid #d9e4ee",
+ },
+ message: {
+ marginTop: "18px",
+ color: "#2c3e50",
+ },
+};
\ No newline at end of file
diff --git a/guardian-admin-dashboard/src/pages/PatientsPage.jsx b/guardian-admin-dashboard/src/pages/PatientsPage.jsx
index 8a2899390..faa8e3db8 100644
--- a/guardian-admin-dashboard/src/pages/PatientsPage.jsx
+++ b/guardian-admin-dashboard/src/pages/PatientsPage.jsx
@@ -1,8 +1,814 @@
-export default function PatientsPage() {
+import { useEffect, useState } from 'react';
+import {
+ getPatients,
+ createPatient,
+ deactivatePatient,
+} from '../services/patientService';
+
+const initialFormData = {
+ fullname: '',
+ gender: '',
+ dateOfBirth: '',
+ caretakerId: '',
+ nurseId: '',
+ doctorId: '',
+ image: '',
+ dateOfAdmitting: '',
+ description: '',
+};
+
+function formatDate(dateString) {
+ if (!dateString) return 'N/A';
+
+ const date = new Date(dateString);
+ if (Number.isNaN(date.getTime())) return dateString;
+
+ return date.toLocaleDateString();
+}
+
+function PatientsPage() {
+ const [patients, setPatients] = useState([]);
+ const [pagination, setPagination] = useState({
+ total: 0,
+ page: 1,
+ pages: 1,
+ limit: 10,
+ });
+
+ const [loading, setLoading] = useState(true);
+ const [submitting, setSubmitting] = useState(false);
+ const [deactivatingId, setDeactivatingId] = useState('');
+ const [error, setError] = useState('');
+ const [successMessage, setSuccessMessage] = useState('');
+ const [showForm, setShowForm] = useState(false);
+ const [formData, setFormData] = useState(initialFormData);
+
+ const loadPatients = async (page = 1) => {
+ try {
+ setLoading(true);
+ setError('');
+
+ const data = await getPatients({ page, limit: 10 });
+
+ setPatients(data?.patients || []);
+ setPagination(
+ data?.pagination || {
+ total: 0,
+ page: 1,
+ pages: 1,
+ limit: 10,
+ }
+ );
+ } catch (err) {
+ console.error('Load patients error:', err);
+ setError(
+ err?.response?.data?.message ||
+ err?.message ||
+ 'Failed to load patients.'
+ );
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ loadPatients();
+ }, []);
+
+ const handleInputChange = (e) => {
+ const { name, value } = e.target;
+
+ setFormData((prev) => ({
+ ...prev,
+ [name]: value,
+ }));
+ };
+
+ const resetForm = () => {
+ setFormData(initialFormData);
+ };
+
+ const handleToggleForm = () => {
+ setShowForm((prev) => !prev);
+ setError('');
+ setSuccessMessage('');
+
+ if (showForm) {
+ resetForm();
+ }
+ };
+
+ const handleAddPatient = async (e) => {
+ e.preventDefault();
+
+ if (
+ !formData.fullname ||
+ !formData.gender ||
+ !formData.dateOfBirth ||
+ !formData.caretakerId ||
+ !formData.nurseId ||
+ !formData.doctorId ||
+ !formData.dateOfAdmitting
+ ) {
+ setError(
+ 'Full name, gender, date of birth, caretaker ID, nurse ID, doctor ID, and date of admitting are required.'
+ );
+ setSuccessMessage('');
+ return;
+ }
+
+ try {
+ setSubmitting(true);
+ setError('');
+ setSuccessMessage('');
+
+ const payload = {
+ fullname: formData.fullname,
+ gender: formData.gender,
+ dateOfBirth: formData.dateOfBirth,
+ caretakerId: formData.caretakerId,
+ nurseId: formData.nurseId,
+ doctorId: formData.doctorId,
+ image: formData.image,
+ dateOfAdmitting: formData.dateOfAdmitting,
+ description: formData.description,
+ };
+
+ const response = await createPatient(payload);
+
+ setSuccessMessage(response?.message || 'Patient created.');
+ resetForm();
+ setShowForm(false);
+
+ await loadPatients(pagination.page || 1);
+ } catch (err) {
+ console.error('Add patient error:', err);
+ setError(
+ err?.response?.data?.message ||
+ err?.message ||
+ 'Failed to add patient.'
+ );
+ setSuccessMessage('');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ const handleDeactivate = async (id) => {
+ const confirmed = window.confirm(
+ 'Are you sure you want to deactivate this patient?'
+ );
+
+ if (!confirmed) return;
+
+ try {
+ setDeactivatingId(id);
+ setError('');
+ setSuccessMessage('');
+
+ const response = await deactivatePatient(id);
+
+ setSuccessMessage(response?.message || 'Patient deactivated successfully.');
+ await loadPatients(pagination.page || 1);
+ } catch (err) {
+ console.error('Deactivate patient error:', err);
+ setError(
+ err?.response?.data?.message ||
+ err?.message ||
+ 'Failed to deactivate patient.'
+ );
+ setSuccessMessage('');
+ } finally {
+ setDeactivatingId('');
+ }
+ };
+
+ const handlePreviousPage = () => {
+ if (pagination.page > 1) {
+ loadPatients(pagination.page - 1);
+ }
+ };
+
+ const handleNextPage = () => {
+ if (pagination.page < pagination.pages) {
+ loadPatients(pagination.page + 1);
+ }
+ };
+
return (
-
-
Patients
-
This placeholder route is ready for future patient-related admin work.
+
+
+
+
+
Patients
+
+ Manage patients under your organisation.
+
+
+
+
+
+
+ {successMessage && (
+
+ {successMessage}
+
+ )}
+
+ {showForm && (
+
+ )}
+
+ {loading ? (
+
+ Loading patients...
+
+ ) : error ? (
+
+ {error}
+
+ ) : patients.length === 0 ? (
+
+ No patients found.
+
+ ) : (
+ <>
+
+
+
+
+ |
+ Full Name
+ |
+
+ Gender
+ |
+
+ Date of Birth
+ |
+
+ Age
+ |
+
+ Caretaker
+ |
+
+ Doctor
+ |
+
+ Nurses
+ |
+
+ Actions
+ |
+
+
+
+
+ {patients.map((patient) => (
+
+ |
+ {patient.fullname || 'N/A'}
+ |
+
+ {patient.gender || 'N/A'}
+ |
+
+ {formatDate(patient.dateOfBirth)}
+ |
+
+ {patient.age ?? 'N/A'}
+ |
+
+ {patient.caretaker?.fullname || 'N/A'}
+ |
+
+ {patient.assignedDoctor?.fullname || 'N/A'}
+ |
+
+ {patient.assignedNurses?.length
+ ? patient.assignedNurses
+ .map((nurse) => nurse.fullname)
+ .join(', ')
+ : 'N/A'}
+ |
+
+
+ |
+
+ ))}
+
+
+
+
+
+
+ Total Patients: {pagination.total || 0} | Page{' '}
+ {pagination.page || 1} of {pagination.pages || 1}
+
+
+
+
+
+
+
+
+ >
+ )}
+
);
-}
\ No newline at end of file
+}
+
+export default PatientsPage;
\ No newline at end of file
diff --git a/guardian-admin-dashboard/src/pages/StaffManagementPage.jsx b/guardian-admin-dashboard/src/pages/StaffManagementPage.jsx
index 03fd595a5..0bd6a6fdf 100644
--- a/guardian-admin-dashboard/src/pages/StaffManagementPage.jsx
+++ b/guardian-admin-dashboard/src/pages/StaffManagementPage.jsx
@@ -1,11 +1,299 @@
+import { useState, useEffect, useCallback } from 'react';
+import { UserRoundPlus } from 'lucide-react';
+import DataTable from '../components/common/DataTable';
+import Modal from '../components/common/Modal';
+import Toast from '../components/common/Toast';
+import Button from '../components/common/Button';
+import InputField from '../components/common/InputField';
+import Dropdown from '../components/common/Dropdown';
+import { getMyOrganizations } from '../services/orgService';
+import {
+ getStaff,
+ createStaff,
+ deactivateStaff,
+} from '../services/staffService';
+import { ROLE_OPTIONS } from '../utils/constants';
+
+const emptyForm = { userId: '' };
+const emptyErrors = { userId: '' };
+
+const ROLE_FILTER_OPTIONS = [
+ { value: '', label: 'All Roles' },
+ ...ROLE_OPTIONS,
+];
+
+function formatStaff(raw) {
+ return raw.map((s) => ({
+ id: s._id,
+ fullName: s.fullname,
+ email: s.email,
+ role:
+ ROLE_OPTIONS.find((r) => r.value === s.role?.name?.toLowerCase())
+ ?.label ?? '-',
+ organization: s.organization?.name ?? '-',
+ }));
+}
+
export default function StaffManagementPage() {
+ const [staff, setStaff] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [page, setPage] = useState(1);
+ const [totalRows, setTotalRows] = useState(0);
+ const [modalOpen, setModalOpen] = useState(false);
+ const [form, setForm] = useState(emptyForm);
+ const [errors, setErrors] = useState(emptyErrors);
+ const [orgOptions, setOrgOptions] = useState([]);
+ const [orgLoading, setOrgLoading] = useState(true);
+ const [orgFilter, setOrgFilter] = useState('');
+ const [roleFilter, setRoleFilter] = useState('');
+ const [search, setSearch] = useState('');
+ const [confirmId, setConfirmId] = useState(null);
+ const [successOpen, setSuccessOpen] = useState(false);
+
+ const fetchStaff = useCallback(async () => {
+ setLoading(true);
+ try {
+ const data = await getStaff({
+ page,
+ limit: 10,
+ role: roleFilter,
+ orgId: orgFilter,
+ search,
+ });
+ const normalized = formatStaff(data.staff ?? []);
+ const filtered = normalized.filter((s) =>
+ ROLE_OPTIONS.some((r) => r.value === s.role.toLowerCase()),
+ );
+ setStaff(filtered);
+ setTotalRows(data.pagination?.total ?? 0);
+ } catch (err) {
+ console.error('Failed to load staff:', err);
+ } finally {
+ setLoading(false);
+ }
+ }, [page, roleFilter, orgFilter, search]);
+
+ useEffect(() => {
+ fetchStaff();
+ }, [fetchStaff]);
+
+ useEffect(() => {
+ const fetchOrgs = async () => {
+ setOrgLoading(true);
+ try {
+ const orgs = await getMyOrganizations();
+ setOrgOptions(orgs.map((o) => ({ value: o._id, label: o.name })));
+ } catch (err) {
+ console.error('Failed to load organizations:', err);
+ } finally {
+ setOrgLoading(false);
+ }
+ };
+ fetchOrgs();
+ }, []);
+
+ function validate(fields) {
+ const errs = { ...emptyErrors };
+ if (!fields.userId.trim()) {
+ errs.userId = 'User ID is required.';
+ }
+ return errs;
+ }
+
+ function handleChange(e) {
+ const { name, value } = e.target;
+ setForm((prev) => ({ ...prev, [name]: value }));
+ setErrors((prev) => ({ ...prev, [name]: '' }));
+ }
+
+ function handleClose() {
+ setModalOpen(false);
+ setForm(emptyForm);
+ setErrors(emptyErrors);
+ }
+
+ async function handleSave() {
+ const errs = validate(form);
+ if (Object.values(errs).some(Boolean)) {
+ setErrors(errs);
+ return;
+ }
+ try {
+ await createStaff(form.userId);
+ handleClose();
+ setSuccessOpen(true);
+ fetchStaff();
+ } catch (err) {
+ console.error('Failed to add staff:', err);
+ }
+ }
+
+ async function handleConfirmDeactivate() {
+ try {
+ await deactivateStaff(confirmId);
+ setStaff((prev) => prev.filter((s) => s.id !== confirmId));
+ } catch (err) {
+ console.error('Failed to deactivate staff:', err);
+ } finally {
+ setConfirmId(null);
+ }
+ }
+
+ const columns = [
+ {
+ name: 'ID',
+ selector: (row) => row.id,
+ sortable: true,
+ },
+ {
+ name: 'Full Name',
+ selector: (row) => row.fullName,
+ sortable: true,
+ },
+ {
+ name: 'Email',
+ selector: (row) => row.email,
+ },
+ {
+ name: 'Role',
+ selector: (row) => row.role,
+ sortable: true,
+ },
+ {
+ name: 'Organization',
+ selector: (row) => row.organization,
+ sortable: true,
+ },
+ {
+ name: 'Actions',
+ cell: (row) => (
+
+ ),
+ },
+ ];
+
return (
-
-
Staff Management
-
- This route is prepared for admin-facing staff management work. The team
- can continue implementation here using the shared shell layout.
-
+
+
+
Staff Management
+
+
+
+
+
+ {
+ setSearch(e.target.value);
+ setPage(1);
+ }}
+ placeholder='Search by name or email...'
+ />
+
+
+
+ {
+ setRoleFilter(e.target.value);
+ setPage(1);
+ }}
+ options={ROLE_FILTER_OPTIONS}
+ />
+
+
+ {
+ setOrgFilter(e.target.value);
+ setPage(1);
+ }}
+ options={[
+ { value: '', label: 'All Organizations' },
+ ...orgOptions,
+ ]}
+ placeholder={orgLoading ? 'Loading...' : 'All Organizations'}
+ disabled={orgLoading}
+ />
+
+
+
+
+
setPage(newPage)}
+ />
+
+ setConfirmId(null)}
+ />
+
+
+
+
+
+
+
+
+
+ setSuccessOpen(false)}
+ />
);
-}
\ No newline at end of file
+}
diff --git a/guardian-admin-dashboard/src/services/api.js b/guardian-admin-dashboard/src/services/api.js
index 7d56f1bb6..61eb4864c 100644
--- a/guardian-admin-dashboard/src/services/api.js
+++ b/guardian-admin-dashboard/src/services/api.js
@@ -4,7 +4,7 @@ import { getAuthToken } from "../utils/storage";
const api = axios.create({
baseURL:
import.meta.env.VITE_API_BASE_URL ||
- "https://guardian-backend-ashen.vercel.app/",
+ "https://guardian-backend-git-fix-cors-patelrudra2306-5873s-projects.vercel.app/api/v1",
headers: {
"Content-Type": "application/json",
},
@@ -12,6 +12,7 @@ const api = axios.create({
api.interceptors.request.use((config) => {
const token = getAuthToken();
+ console.log('token:', getAuthToken());
if (token) {
config.headers.Authorization = `Bearer ${token}`;
diff --git a/guardian-admin-dashboard/src/services/authService.js b/guardian-admin-dashboard/src/services/authService.js
index 9ac70474b..2b8826e73 100644
--- a/guardian-admin-dashboard/src/services/authService.js
+++ b/guardian-admin-dashboard/src/services/authService.js
@@ -1,72 +1,30 @@
-//Needs to be integrated with Backend API once the CORS issue is solved.
-
-// import api from "./api";
-
-// export async function loginAdmin({ email, password }) {
-// const response = await api.post("/auth/login", {
-// email,
-// password,
-// });
-
-// return response.data;
-// }
-
-// export async function sendPin(email) {
-// const response = await api.post("/auth/send-pin", {
-// email,
-// });
-
-// return response.data;
-// }
-
-// export async function verifyPin({ email, otp }) {
-// const response = await api.post("/auth/verify-pin", {
-// email,
-// otp,
-// });
-
-// return response.data;
-// }
-
-// Mock login and otp fallback
+import api from "./api";
export async function loginAdmin({ email, password }) {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve({
- user: {
- id: "demo-admin-id",
- fullname: "Alice Smith",
- email,
- role: "admin",
- twoFactorRequired: true,
- },
- token: "demo-admin-token",
- });
- }, 700);
+ const response = await api.post("/auth/login", {
+ email,
+ password,
});
+
+ console.log("LOGIN RESPONSE:", response.data);
+ return response.data;
}
export async function sendPin(email) {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve({
- message: "OTP functionality is temporarily disabled for testing.",
- });
- }, 500);
+ const response = await api.post("/auth/send-pin", {
+ email,
});
+
+ console.log("SEND PIN RESPONSE:", response.data);
+ return response.data;
}
export async function verifyPin({ email, otp }) {
- return new Promise((resolve, reject) => {
- setTimeout(() => {
- if (otp === "123456") {
- resolve({
- message: "OTP verification bypassed for testing.",
- });
- } else {
- reject(new Error("Invalid OTP"));
- }
- }, 700);
+ const response = await api.post("/auth/verify-pin", {
+ email,
+ otp,
});
+
+ console.log("VERIFY PIN RESPONSE:", response.data);
+ return response.data;
}
\ No newline at end of file
diff --git a/guardian-admin-dashboard/src/services/orgService.js b/guardian-admin-dashboard/src/services/orgService.js
new file mode 100644
index 000000000..81c5ca8ed
--- /dev/null
+++ b/guardian-admin-dashboard/src/services/orgService.js
@@ -0,0 +1,26 @@
+export async function getMyOrganizations() {
+ return {
+ orgs: [
+ {
+ _id: "1",
+ name: "Guardian Health Org",
+ description: "Primary org for testing",
+ active: true,
+ createdBy: "admin001",
+ staff: ["u1", "u2", "u3"],
+ created_at: "2026-04-11T08:43:07.933Z",
+ updated_at: "2026-04-11T08:43:07.933Z"
+ },
+ {
+ _id: "2",
+ name: "Care Support Org",
+ description: "Secondary organisation",
+ active: false,
+ createdBy: "admin002",
+ staff: ["u4"],
+ created_at: "2026-03-02T10:20:00.000Z",
+ updated_at: "2026-03-15T11:10:00.000Z"
+ }
+ ]
+ };
+}
\ No newline at end of file
diff --git a/guardian-admin-dashboard/src/services/patientService.js b/guardian-admin-dashboard/src/services/patientService.js
new file mode 100644
index 000000000..317693bd9
--- /dev/null
+++ b/guardian-admin-dashboard/src/services/patientService.js
@@ -0,0 +1,38 @@
+import api from "./api";
+
+export const getAllPatients = async () => {
+ const response = await api.get("/admin/patients");
+ return response.data;
+};
+
+export const getPatientOverview = async (patientId, orgId) => {
+ const response = await api.get(`/admin/patients/${patientId}/overview`, {
+ params: { orgId },
+ });
+ return response.data;
+};
+
+export async function getPatients({ page = 1, limit = 10 } = {}) {
+ const params = { page, limit };
+ const response = await api.get('/admin/patients', { params });
+
+ console.log('GET patients response:', response.data);
+
+ return response.data;
+}
+
+export async function createPatient(patientData) {
+ const response = await api.post('/admin/patients', patientData);
+
+ console.log('POST create patient response:', response.data);
+
+ return response.data;
+}
+
+export async function deactivatePatient(id) {
+ const response = await api.delete(`/admin/patients/${id}`);
+
+ console.log('DELETE patient response:', response.data);
+
+ return response.data;
+}
diff --git a/guardian-admin-dashboard/src/services/staffService.js b/guardian-admin-dashboard/src/services/staffService.js
new file mode 100644
index 000000000..c0f8d7182
--- /dev/null
+++ b/guardian-admin-dashboard/src/services/staffService.js
@@ -0,0 +1,27 @@
+import api from './api';
+
+export async function getStaff({
+ page = 1,
+ limit = 10,
+ search = '',
+ orgId = '',
+ role = '',
+} = {}) {
+ const params = { page, limit };
+ if (search) params.search = search;
+ if (orgId) params.orgId = orgId;
+ if (role) params.role = role;
+
+ const response = await api.get('/admin/staff', { params });
+ return response.data;
+}
+
+export async function createStaff(userId) {
+ const response = await api.post('/admin/staff', { userId });
+ return response.data;
+}
+
+export async function deactivateStaff(id) {
+ const response = await api.put(`/admin/staff/${id}/deactivate`);
+ return response.data;
+}
diff --git a/guardian-admin-dashboard/src/utils/constants.js b/guardian-admin-dashboard/src/utils/constants.js
index 68bc3a8f1..dba88d487 100644
--- a/guardian-admin-dashboard/src/utils/constants.js
+++ b/guardian-admin-dashboard/src/utils/constants.js
@@ -11,9 +11,15 @@ export const ADMIN_NAV_ITEMS = [
path: "/dashboard/org-assignment",
},
{ id: "patients", label: "Patients", path: "/dashboard/patients" },
+ {
+ id: "patient-overview",
+ label: "Patient Overview",
+ path: "/dashboard/patient-overview",
+},
{ id: "nurse-roster", label: "Nurse Roster", path: "/dashboard/nurse-roster" },
{ id: "reports", label: "Reports", path: "/dashboard/reports" },
{ id: "settings", label: "Settings", path: "/dashboard/settings" },
+
];
export const DASHBOARD_STATS = [
{
@@ -48,4 +54,9 @@ export const STORAGE_KEYS = {
email: "guardian_admin_email",
pendingToken: "guardian_admin_pending_token",
pendingUser: "guardian_admin_pending_user",
-};
\ No newline at end of file
+};
+
+export const ROLE_OPTIONS = [
+ { value: 'doctor', label: 'Doctor' },
+ { value: 'nurse', label: 'Nurse' },
+];
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..b0578dffa
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "Guardian",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}