diff --git a/app/build.gradle b/app/build.gradle
index 18f94621d..8a663589d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -35,7 +35,11 @@ android {
}
buildTypes {
+ debug {
+ buildConfigField "boolean", "USE_MOCK_PATIENT_FLOW", "true"
+ }
release {
+ buildConfigField "boolean", "USE_MOCK_PATIENT_FLOW", "false"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
@@ -45,6 +49,7 @@ android {
targetCompatibility JavaVersion.VERSION_17
}
buildFeatures {
+ buildConfig true
viewBinding true
// dataBinding true
compose true
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 31d6a312a..54dc349d8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -152,6 +152,9 @@
+
diff --git a/app/src/main/java/deakin/gopher/guardian/PatientExerciseModules.kt b/app/src/main/java/deakin/gopher/guardian/PatientExerciseModules.kt
index 20e1a52eb..449000379 100644
--- a/app/src/main/java/deakin/gopher/guardian/PatientExerciseModules.kt
+++ b/app/src/main/java/deakin/gopher/guardian/PatientExerciseModules.kt
@@ -2,6 +2,7 @@ package deakin.gopher.guardian
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
+import androidx.activity.OnBackPressedCallback
/**
* Host activity for the patient exercise modules
@@ -11,6 +12,20 @@ class PatientExerciseModules : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_patient_exercise_container)
+ onBackPressedDispatcher.addCallback(
+ this,
+ object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (supportFragmentManager.backStackEntryCount > 0) {
+ supportFragmentManager.popBackStack()
+ } else {
+ isEnabled = false
+ onBackPressedDispatcher.onBackPressed()
+ }
+ }
+ },
+ )
+
if (savedInstanceState == null) {
val fragment = PatientExercisePortalFragment.newInstance()
supportFragmentManager.beginTransaction()
@@ -18,12 +33,4 @@ class PatientExerciseModules : AppCompatActivity() {
.commit()
}
}
-
- override fun onBackPressed() {
- if (supportFragmentManager.backStackEntryCount > 0) {
- supportFragmentManager.popBackStack()
- } else {
- super.onBackPressed()
- }
- }
}
diff --git a/app/src/main/java/deakin/gopher/guardian/model/MockPatientData.kt b/app/src/main/java/deakin/gopher/guardian/model/MockPatientData.kt
new file mode 100644
index 000000000..29f0dd61c
--- /dev/null
+++ b/app/src/main/java/deakin/gopher/guardian/model/MockPatientData.kt
@@ -0,0 +1,111 @@
+package deakin.gopher.guardian.model
+
+import deakin.gopher.guardian.model.register.User
+
+/** Local samples for DEBUG UI review (doctor list + overview + admin overview card). */
+object MockPatientData {
+ private val stubCaretaker =
+ User(
+ id = "mock-caretaker-1",
+ email = "j.morgan@example.com",
+ name = "Jamie Morgan",
+ roleName = "caretaker",
+ photoUrl = "",
+ organization = null,
+ )
+
+ private val stubNurses =
+ listOf(
+ User(
+ id = "mock-nurse-1",
+ email = "a.patel@example.com",
+ name = "Priya Patel",
+ roleName = "nurse",
+ photoUrl = "",
+ organization = null,
+ ),
+ User(
+ id = "mock-nurse-2",
+ email = "l.chen@example.com",
+ name = "Liam Chen",
+ roleName = "nurse",
+ photoUrl = "",
+ organization = null,
+ ),
+ )
+
+ val patients: List
+ get() =
+ listOf(
+ Patient(
+ id = "mock-patient-1",
+ fullname = "Eleanor Whitmore",
+ photoUrl = "",
+ dateOfBirth = "1948-06-15T00:00:00Z",
+ _age = 77,
+ gender = "female",
+ healthConditions =
+ listOf(
+ "type 2 diabetes",
+ "mild hypertension",
+ "early stage osteoarthritis",
+ ),
+ caretaker = stubCaretaker,
+ assignedNurses = stubNurses,
+ ),
+ Patient(
+ id = "mock-patient-2",
+ fullname = "Robert Singh",
+ photoUrl = "",
+ dateOfBirth = "1939-03-08T10:30:00Z",
+ _age = 87,
+ gender = "male",
+ healthConditions =
+ listOf(
+ "congestive heart failure",
+ "chronic kidney disease stage 3",
+ ),
+ caretaker = stubCaretaker,
+ assignedNurses = listOf(stubNurses.first()),
+ ),
+ Patient(
+ id = "mock-patient-3",
+ fullname = "Maria Santos",
+ photoUrl = "",
+ dateOfBirth = "1962-11-02T00:00:00Z",
+ _age = 62,
+ gender = "female",
+ healthConditions = emptyList(),
+ caretaker = stubCaretaker,
+ assignedNurses = emptyList(),
+ ),
+ )
+
+ fun patientById(id: String): Patient? = patients.find { it.id == id }
+
+ fun patientForOverviewOrDefault(id: String): Patient =
+ patientById(id) ?: patients.first()
+
+ /** Fills the “Clinical / admin overview” section during mock previews. */
+ fun adminOverviewForPatient(id: String): PatientAdminOverviewResponse {
+ val name = patientById(id)?.fullname ?: patients.first().fullname
+ return PatientAdminOverviewResponse(
+ summary =
+ "$name — routine follow-up cadence weekly. Stable on current medication plan.",
+ overview =
+ "Focus areas: hydration, mobilisation indoors, adherence to diabetic diet sheet.",
+ carePlanSummary = "Morning vitals weekly; pharmacist review fortnightly.",
+ notes = "Prefers reminders via SMS. Son visits Tuesdays.",
+ riskLevel = when (patientById(id)?.id) {
+ "mock-patient-2" -> "elevated"
+ "mock-patient-3" -> "low"
+ else -> "moderate"
+ },
+ alerts =
+ listOf(
+ "Renew script for ACE inhibitor due next month.",
+ "Updated emergency contact verified Jan 2026.",
+ ),
+ )
+ }
+}
diff --git a/app/src/main/java/deakin/gopher/guardian/model/PatientAdminOverviewResponse.kt b/app/src/main/java/deakin/gopher/guardian/model/PatientAdminOverviewResponse.kt
new file mode 100644
index 000000000..2ceb6fd3b
--- /dev/null
+++ b/app/src/main/java/deakin/gopher/guardian/model/PatientAdminOverviewResponse.kt
@@ -0,0 +1,15 @@
+package deakin.gopher.guardian.model
+
+import com.google.gson.annotations.SerializedName
+import java.io.Serializable
+
+/** Lenient decoding for GET /admin/patient-overview/{patientId}; unknown fields ignored by Gson. */
+data class PatientAdminOverviewResponse(
+ @SerializedName("patient") val patient: Patient? = null,
+ @SerializedName("summary") val summary: String? = null,
+ @SerializedName("notes") val notes: String? = null,
+ @SerializedName("overview") val overview: String? = null,
+ @SerializedName("carePlanSummary") val carePlanSummary: String? = null,
+ @SerializedName("riskLevel") val riskLevel: String? = null,
+ @SerializedName("alerts") val alerts: List? = null,
+) : Serializable
diff --git a/app/src/main/java/deakin/gopher/guardian/services/api/ApiService.kt b/app/src/main/java/deakin/gopher/guardian/services/api/ApiService.kt
index 0e9ea177e..3b2fef11c 100644
--- a/app/src/main/java/deakin/gopher/guardian/services/api/ApiService.kt
+++ b/app/src/main/java/deakin/gopher/guardian/services/api/ApiService.kt
@@ -5,6 +5,7 @@ import deakin.gopher.guardian.model.AddPatientResponse
import deakin.gopher.guardian.model.BaseModel
import deakin.gopher.guardian.model.Patient
import deakin.gopher.guardian.model.PatientActivity
+import deakin.gopher.guardian.model.PatientAdminOverviewResponse
import deakin.gopher.guardian.model.register.AuthResponse
import deakin.gopher.guardian.model.register.RegisterRequest
import okhttp3.MultipartBody
@@ -60,6 +61,24 @@ interface ApiService {
@Header("Authorization") token: String,
): Response>
+ @GET("patients/{patientId}")
+ suspend fun getPatientById(
+ @Header("Authorization") token: String,
+ @Path("patientId") patientId: String,
+ ): Response
+
+ @GET("admin/patient-overview/{patientId}")
+ suspend fun getPatientAdminOverview(
+ @Header("Authorization") token: String,
+ @Path("patientId") patientId: String,
+ ): Response
+
+ @GET("doctors/{doctorId}/patients")
+ suspend fun getDoctorPatients(
+ @Header("Authorization") token: String,
+ @Path("doctorId") doctorId: String,
+ ): Response>
+
@Multipart
@POST("patients/add")
suspend fun addPatient(
diff --git a/app/src/main/java/deakin/gopher/guardian/view/general/Homepage4doctor.kt b/app/src/main/java/deakin/gopher/guardian/view/general/Homepage4doctor.kt
index fb2461467..6e4e11dd2 100644
--- a/app/src/main/java/deakin/gopher/guardian/view/general/Homepage4doctor.kt
+++ b/app/src/main/java/deakin/gopher/guardian/view/general/Homepage4doctor.kt
@@ -1,20 +1,104 @@
package deakin.gopher.guardian.view.general
+import android.content.Intent
import android.os.Bundle
import android.widget.Button
-import androidx.appcompat.app.AppCompatActivity
+import android.widget.ProgressBar
+import android.widget.TextView
+import android.widget.Toast
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import deakin.gopher.guardian.adapter.PatientListAdapter
+import deakin.gopher.guardian.BuildConfig
import deakin.gopher.guardian.R
+import deakin.gopher.guardian.model.MockPatientData
+import deakin.gopher.guardian.model.login.SessionManager
import deakin.gopher.guardian.services.EmailPasswordAuthService
+import deakin.gopher.guardian.services.api.ApiClient
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class Homepage4doctor : BaseActivity() {
+ private lateinit var patientListAdapter: PatientListAdapter
+ private lateinit var progressBar: ProgressBar
+ private lateinit var emptyText: TextView
-class Homepage4doctor : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_homepage4doctor)
+ progressBar = findViewById(R.id.progressBarPatients)
+ emptyText = findViewById(R.id.tvEmptyPatients)
+
+ patientListAdapter =
+ PatientListAdapter(
+ emptyList(),
+ onPatientClick = { patient ->
+ val intent = Intent(this, PatientOverviewActivity::class.java)
+ intent.putExtra(PatientOverviewActivity.EXTRA_PATIENT_ID, patient.id)
+ startActivity(intent)
+ },
+ )
+
+ val recyclerView: RecyclerView = findViewById(R.id.recyclerViewDoctorPatients)
+ recyclerView.layoutManager = LinearLayoutManager(this)
+ recyclerView.adapter = patientListAdapter
+
val signOutButton: Button = findViewById(R.id.signOutButton_doctor)
signOutButton.setOnClickListener {
EmailPasswordAuthService.signOut(this)
finish()
}
}
+
+ override fun onResume() {
+ super.onResume()
+ fetchPatients()
+ }
+
+ private fun fetchPatients() {
+ if (BuildConfig.USE_MOCK_PATIENT_FLOW) {
+ progressBar.visibility = android.view.View.GONE
+ val mocks = MockPatientData.patients
+ patientListAdapter.updateData(mocks)
+ emptyText.visibility =
+ if (mocks.isEmpty()) android.view.View.VISIBLE else android.view.View.GONE
+ return
+ }
+
+ val token = "Bearer ${SessionManager.getToken()}"
+ val doctorId = SessionManager.getCurrentUser().id
+ CoroutineScope(Dispatchers.IO).launch {
+ withContext(Dispatchers.Main) {
+ progressBar.visibility = android.view.View.VISIBLE
+ emptyText.visibility = android.view.View.GONE
+ }
+
+ val response =
+ try {
+ ApiClient.apiService.getDoctorPatients(token, doctorId)
+ } catch (e: Exception) {
+ null
+ }
+
+ withContext(Dispatchers.Main) {
+ progressBar.visibility = android.view.View.GONE
+ if (response?.isSuccessful == true) {
+ val patients = response.body().orEmpty()
+ patientListAdapter.updateData(patients)
+ emptyText.visibility =
+ if (patients.isEmpty()) android.view.View.VISIBLE else android.view.View.GONE
+ } else {
+ emptyText.visibility = android.view.View.VISIBLE
+ Toast.makeText(
+ this@Homepage4doctor,
+ "Failed to load patients",
+ Toast.LENGTH_SHORT,
+ ).show()
+ }
+ }
+ }
+ }
}
diff --git a/app/src/main/java/deakin/gopher/guardian/view/general/PatientListActivity.kt b/app/src/main/java/deakin/gopher/guardian/view/general/PatientListActivity.kt
index c2bbfe079..beb7569d9 100644
--- a/app/src/main/java/deakin/gopher/guardian/view/general/PatientListActivity.kt
+++ b/app/src/main/java/deakin/gopher/guardian/view/general/PatientListActivity.kt
@@ -29,8 +29,8 @@ class PatientListActivity : BaseActivity() {
PatientListAdapter(
emptyList(),
onPatientClick = { patient ->
- val intent = Intent(this, PatientDetailsActivity::class.java)
- intent.putExtra("patient", patient)
+ val intent = Intent(this, PatientOverviewActivity::class.java)
+ intent.putExtra(PatientOverviewActivity.EXTRA_PATIENT_ID, patient.id)
startActivity(intent)
},
onAssignNurseClick = { patient ->
diff --git a/app/src/main/java/deakin/gopher/guardian/view/general/PatientOverviewActivity.kt b/app/src/main/java/deakin/gopher/guardian/view/general/PatientOverviewActivity.kt
new file mode 100644
index 000000000..ce221e72f
--- /dev/null
+++ b/app/src/main/java/deakin/gopher/guardian/view/general/PatientOverviewActivity.kt
@@ -0,0 +1,267 @@
+package deakin.gopher.guardian.view.general
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.lifecycle.lifecycleScope
+import com.bumptech.glide.Glide
+import com.google.gson.Gson
+import deakin.gopher.guardian.R
+import deakin.gopher.guardian.BuildConfig
+import deakin.gopher.guardian.databinding.ActivityPatientOverviewBinding
+import deakin.gopher.guardian.model.ApiErrorResponse
+import deakin.gopher.guardian.model.MockPatientData
+import deakin.gopher.guardian.model.Patient
+import deakin.gopher.guardian.model.PatientAdminOverviewResponse
+import deakin.gopher.guardian.model.login.Role
+import deakin.gopher.guardian.model.login.SessionManager
+import deakin.gopher.guardian.services.api.ApiClient
+import java.util.Locale
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import retrofit2.Response
+
+class PatientOverviewActivity : BaseActivity() {
+ private lateinit var binding: ActivityPatientOverviewBinding
+ private var patientIdArg: String? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityPatientOverviewBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ setSupportActionBar(binding.toolbar)
+ binding.toolbar.setNavigationOnClickListener {
+ onBackPressedDispatcher.onBackPressed()
+ }
+
+ if (SessionManager.getCurrentUser().role == Role.Nurse) {
+ binding.toolbar.setBackgroundColor(getColor(R.color.TG_blue))
+ }
+
+ patientIdArg = intent.getStringExtra(EXTRA_PATIENT_ID)?.trim()
+ if (patientIdArg.isNullOrEmpty()) {
+ Toast.makeText(this, R.string.patient_overview_missing_id, Toast.LENGTH_SHORT).show()
+ finish()
+ return
+ }
+
+ binding.btnRetry.setOnClickListener { loadOverview() }
+ loadOverview()
+ }
+
+ private fun loadOverview() {
+ val patientId = patientIdArg ?: return
+ setLoadingState(true)
+ binding.layoutError.visibility = View.GONE
+ binding.layoutEmpty.visibility = View.GONE
+ binding.svOverviewContent.visibility = View.GONE
+ resetAdminSection()
+
+ if (BuildConfig.USE_MOCK_PATIENT_FLOW) {
+ lifecycleScope.launch {
+ val patient = MockPatientData.patientForOverviewOrDefault(patientId)
+ setLoadingState(false)
+ binding.svOverviewContent.visibility = View.VISIBLE
+ bindPatientDetails(patient)
+ applyAdminOverview(MockPatientData.adminOverviewForPatient(patientId))
+ }
+ return
+ }
+
+ lifecycleScope.launch {
+ val token = "Bearer ${SessionManager.getToken()}"
+
+ val patientResponse =
+ withContext(Dispatchers.IO) {
+ try {
+ ApiClient.apiService.getPatientById(token, patientId)
+ } catch (_: Exception) {
+ null
+ }
+ }
+
+ if (patientResponse == null) {
+ setLoadingState(false)
+ showError(getString(R.string.patient_overview_error_generic))
+ return@launch
+ }
+
+ if (!patientResponse.isSuccessful) {
+ setLoadingState(false)
+ showError(resolveErrorMessage(patientResponse))
+ return@launch
+ }
+
+ val patient = patientResponse.body()
+ if (patient == null) {
+ setLoadingState(false)
+ showEmpty()
+ return@launch
+ }
+
+ if (patient.id != patientId) {
+ setLoadingState(false)
+ showError(getString(R.string.patient_overview_error_generic))
+ return@launch
+ }
+
+ val adminResponse =
+ withContext(Dispatchers.IO) {
+ try {
+ ApiClient.apiService.getPatientAdminOverview(token, patientId)
+ } catch (_: Exception) {
+ null
+ }
+ }
+
+ setLoadingState(false)
+
+ binding.svOverviewContent.visibility = View.VISIBLE
+ bindPatientDetails(patient)
+
+ if (adminResponse?.isSuccessful == true) {
+ adminResponse.body()?.let { applyAdminOverview(it) }
+ }
+ }
+ }
+
+ private fun bindPatientDetails(patient: Patient) {
+ binding.tvPatientName.text = patient.fullname
+ binding.tvOverviewAge.text = getString(R.string.patient_overview_age_line, patient.age)
+ binding.tvOverviewGender.text =
+ getString(R.string.patient_overview_gender_line, patient.gender.capitalizeWords())
+ binding.tvOverviewDob.text =
+ getString(
+ R.string.patient_overview_dob_line,
+ patient.dateOfBirth.substringBefore("T").ifEmpty { "—" },
+ )
+
+ binding.tvOverviewConditions.text =
+ if (patient.healthConditions.isEmpty()) {
+ getString(R.string.patient_overview_no_conditions)
+ } else {
+ patient.healthConditions.joinToString("\n") { line -> "• ${line.capitalizeWords()}" }
+ }
+
+ binding.tvCaretakerLine.text =
+ getString(
+ R.string.patient_overview_caretaker_line,
+ patient.caretaker.name,
+ patient.caretaker.email,
+ )
+
+ binding.tvAssignedNursesDetail.text =
+ if (patient.assignedNurses.isEmpty()) {
+ getString(R.string.patient_overview_no_nurses)
+ } else {
+ patient.assignedNurses.joinToString("\n") { nurse ->
+ "${nurse.name} (${nurse.email})"
+ }
+ }
+
+ Glide.with(this)
+ .load(patient.photoUrl)
+ .placeholder(R.drawable.profile)
+ .circleCrop()
+ .into(binding.imagePatient)
+ }
+
+ private fun resetAdminSection() {
+ binding.cardAdminOverview.visibility = View.GONE
+ listOf(
+ binding.tvOverviewRiskLevel,
+ binding.tvOverviewSummary,
+ binding.tvOverviewExtended,
+ binding.tvOverviewCarePlan,
+ binding.tvOverviewNotes,
+ binding.tvOverviewAlerts,
+ ).forEach { view ->
+ view.visibility = View.GONE
+ view.text = ""
+ }
+ }
+
+ private fun applyAdminOverview(body: PatientAdminOverviewResponse) {
+ var any = false
+
+ body.riskLevel?.takeIf { it.isNotBlank() }?.let {
+ binding.tvOverviewRiskLevel.visibility = View.VISIBLE
+ binding.tvOverviewRiskLevel.text = getString(R.string.patient_overview_risk_level, it)
+ any = true
+ }
+
+ body.summary?.takeIf { it.isNotBlank() }?.let {
+ binding.tvOverviewSummary.visibility = View.VISIBLE
+ binding.tvOverviewSummary.text = it
+ any = true
+ }
+
+ body.overview?.takeIf { it.isNotBlank() }?.let {
+ binding.tvOverviewExtended.visibility = View.VISIBLE
+ binding.tvOverviewExtended.text = it
+ any = true
+ }
+
+ body.carePlanSummary?.takeIf { it.isNotBlank() }?.let {
+ binding.tvOverviewCarePlan.visibility = View.VISIBLE
+ binding.tvOverviewCarePlan.text = it
+ any = true
+ }
+
+ body.notes?.takeIf { it.isNotBlank() }?.let {
+ binding.tvOverviewNotes.visibility = View.VISIBLE
+ binding.tvOverviewNotes.text = it
+ any = true
+ }
+
+ body.alerts?.takeIf { it.isNotEmpty() }?.let { list ->
+ binding.tvOverviewAlerts.visibility = View.VISIBLE
+ binding.tvOverviewAlerts.text = list.joinToString("\n") { alert -> "• $alert" }
+ any = true
+ }
+
+ if (any) {
+ binding.cardAdminOverview.visibility = View.VISIBLE
+ }
+ }
+
+ private fun setLoadingState(loading: Boolean) {
+ binding.progressLoading.visibility = if (loading) View.VISIBLE else View.GONE
+ }
+
+ private fun showError(message: String) {
+ binding.svOverviewContent.visibility = View.GONE
+ binding.layoutEmpty.visibility = View.GONE
+ binding.layoutError.visibility = View.VISIBLE
+ binding.tvErrorMessage.text = message
+ }
+
+ private fun showEmpty() {
+ binding.svOverviewContent.visibility = View.GONE
+ binding.layoutError.visibility = View.GONE
+ binding.layoutEmpty.visibility = View.VISIBLE
+ }
+
+ private fun resolveErrorMessage(response: Response<*>): String {
+ return try {
+ val raw = response.errorBody()?.string().orEmpty()
+ Gson().fromJson(raw, ApiErrorResponse::class.java)?.apiError
+ } catch (_: Exception) {
+ null
+ } ?: response.message().takeIf { it.isNotBlank() }
+ ?: getString(R.string.patient_overview_error_generic)
+ }
+
+ private fun String.capitalizeWords(): String =
+ split(" ").joinToString(" ") { word ->
+ word.replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
+ }
+ }
+
+ companion object {
+ const val EXTRA_PATIENT_ID = "patient_id"
+ }
+}
diff --git a/app/src/main/res/layout/activity_homepage4doctor.xml b/app/src/main/res/layout/activity_homepage4doctor.xml
index 6e7936f35..70dc13f60 100644
--- a/app/src/main/res/layout/activity_homepage4doctor.xml
+++ b/app/src/main/res/layout/activity_homepage4doctor.xml
@@ -1,8 +1,10 @@
+ android:layout_height="match_parent"
+ android:padding="16dp">
@@ -20,10 +20,54 @@
android:id="@+id/signOutButton_doctor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
android:text="Sign Out"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBaseline_toBaselineOf="@id/welcomeDoctorText"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_patient_overview.xml b/app/src/main/res/layout/activity_patient_overview.xml
new file mode 100644
index 000000000..356b98098
--- /dev/null
+++ b/app/src/main/res/layout/activity_patient_overview.xml
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9fe4ecab0..73d527668 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -208,6 +208,25 @@
Test: Medical Diagnostics
Test: Side Menu
Patients Overview
+ My patients
+ Patient overview
+ Demographics
+ Health
+ Care team
+ Clinical / admin overview
+ Assigned nurses
+ Patient photo
+ Age: %1$d
+ Gender: %1$s
+ Date of birth: %1$s
+ No health conditions listed
+ Caretaker: %1$s · %2$s
+ None assigned
+ Could not load this patient. Try again.
+ Retry
+ No patient record was returned.
+ Missing patient. Please go back and choose a patient again.
+ Risk level: %1$s
Patient Profile
First name
Marilyn