visual prototyping
This commit is contained in:
@ -39,6 +39,7 @@ import com.tomhempel.rbarometer.questionnaire.QuestionnaireValidation
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.json.JSONObject
|
||||
import com.tomhempel.rbarometer.utils.ScreenLoading
|
||||
import com.tomhempel.rbarometer.utils.ViewUtils
|
||||
@ -63,11 +64,15 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
private lateinit var sessionMetricClientsValue: TextView
|
||||
private lateinit var sessionMetricMeasuresValue: TextView
|
||||
private lateinit var sessionMetricSyncValue: TextView
|
||||
private lateinit var todaySessionsTitle: TextView
|
||||
private lateinit var todaySessionsSummary: TextView
|
||||
private lateinit var todaySessionsContainer: LinearLayout
|
||||
private lateinit var nextStepBannerCard: View
|
||||
private lateinit var nextStepEyebrow: TextView
|
||||
private lateinit var nextStepTitle: TextView
|
||||
private lateinit var nextStepBody: TextView
|
||||
private lateinit var nextStepActionButton: MaterialButton
|
||||
private lateinit var questionnaireListHost: View
|
||||
private lateinit var questionnaireScroll: View
|
||||
private lateinit var buttonContainer: LinearLayout
|
||||
private lateinit var bottomUploadDock: View
|
||||
@ -108,6 +113,65 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
UPLOAD,
|
||||
}
|
||||
|
||||
private data class TodaySession(
|
||||
val clientCode: String,
|
||||
val status: String,
|
||||
val cycle: Int,
|
||||
val session: Int,
|
||||
val totalSessions: Int,
|
||||
val plannedDate: String,
|
||||
val lastSession: String,
|
||||
val nextSuggestedDate: String,
|
||||
val measures: List<String>,
|
||||
)
|
||||
|
||||
private val dummyTodaySessions = listOf(
|
||||
TodaySession(
|
||||
clientCode = "A102",
|
||||
status = "Today",
|
||||
cycle = 3,
|
||||
session = 2,
|
||||
totalSessions = 6,
|
||||
plannedDate = "Today 14:00",
|
||||
lastSession = "Jun 17",
|
||||
nextSuggestedDate = "Jul 01",
|
||||
measures = listOf("WHO-5", "Stress thermometer", "Relationship check"),
|
||||
),
|
||||
TodaySession(
|
||||
clientCode = "B447",
|
||||
status = "Overdue",
|
||||
cycle = 1,
|
||||
session = 5,
|
||||
totalSessions = 6,
|
||||
plannedDate = "Jun 18",
|
||||
lastSession = "Jun 10",
|
||||
nextSuggestedDate = "Jun 26",
|
||||
measures = listOf("Mood check", "Goal tracking", "Counselor score review"),
|
||||
),
|
||||
TodaySession(
|
||||
clientCode = "C083",
|
||||
status = "Upcoming",
|
||||
cycle = 2,
|
||||
session = 1,
|
||||
totalSessions = 6,
|
||||
plannedDate = "Tomorrow",
|
||||
lastSession = "Jun 21",
|
||||
nextSuggestedDate = "Jul 08",
|
||||
measures = listOf("Cycle baseline", "WHO-5", "Stress thermometer"),
|
||||
),
|
||||
TodaySession(
|
||||
clientCode = "D219",
|
||||
status = "Inactive",
|
||||
cycle = 4,
|
||||
session = 6,
|
||||
totalSessions = 6,
|
||||
plannedDate = "Not planned",
|
||||
lastSession = "Jun 03",
|
||||
nextSuggestedDate = "On hold",
|
||||
measures = listOf("End-of-cycle review", "Participation status"),
|
||||
),
|
||||
)
|
||||
|
||||
private fun color(@ColorRes res: Int) = ContextCompat.getColor(activity, res)
|
||||
|
||||
private fun t(id: String) = LanguageManager.getText(languageID, id)
|
||||
@ -186,6 +250,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
|
||||
bindViews()
|
||||
loadQuestionnaireOrder()
|
||||
renderTodaySessions()
|
||||
createQuestionnaireButtons()
|
||||
setupCoachDisplay()
|
||||
setupLanguageSpinner()
|
||||
@ -219,11 +284,15 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
sessionMetricClientsValue = activity.findViewById(R.id.sessionMetricClientsValue)
|
||||
sessionMetricMeasuresValue = activity.findViewById(R.id.sessionMetricMeasuresValue)
|
||||
sessionMetricSyncValue = activity.findViewById(R.id.sessionMetricSyncValue)
|
||||
todaySessionsTitle = activity.findViewById(R.id.todaySessionsTitle)
|
||||
todaySessionsSummary = activity.findViewById(R.id.todaySessionsSummary)
|
||||
todaySessionsContainer = activity.findViewById(R.id.todaySessionsContainer)
|
||||
nextStepBannerCard = activity.findViewById(R.id.nextStepBannerCard)
|
||||
nextStepEyebrow = activity.findViewById(R.id.nextStepEyebrow)
|
||||
nextStepTitle = activity.findViewById(R.id.nextStepTitle)
|
||||
nextStepBody = activity.findViewById(R.id.nextStepBody)
|
||||
nextStepActionButton = activity.findViewById(R.id.nextStepActionButton)
|
||||
questionnaireListHost = activity.findViewById(R.id.questionnaireListHost)
|
||||
questionnaireScroll = activity.findViewById(R.id.questionnaireScroll)
|
||||
bottomUploadDock = activity.findViewById(R.id.bottomUploadDock)
|
||||
questionnaireListLoading = activity.findViewById(R.id.questionnaireListLoading)
|
||||
@ -242,6 +311,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
textView.text = hubFallback("Sitzungsinstrumente", "Session measures")
|
||||
applyHubLabels()
|
||||
updateSessionMetrics()
|
||||
renderTodaySessions()
|
||||
applyCoachDisplay()
|
||||
}
|
||||
|
||||
@ -276,6 +346,355 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderTodaySessions() {
|
||||
if (!::todaySessionsContainer.isInitialized) return
|
||||
todaySessionsTitle.text = hubFallback("Sitzungen", "Sessions")
|
||||
val dueCount = dummyTodaySessions.count { it.status == "Today" || it.status == "Overdue" }
|
||||
todaySessionsSummary.text = "$dueCount today"
|
||||
todaySessionsContainer.removeAllViews()
|
||||
addSessionGroup(
|
||||
title = hubFallback("Heute", "Today"),
|
||||
sessions = dummyTodaySessions.filter { it.status == "Today" || it.status == "Overdue" },
|
||||
)
|
||||
addSessionGroup(
|
||||
title = hubFallback("Demnächst", "Upcoming"),
|
||||
sessions = dummyTodaySessions.filter { it.status == "Upcoming" },
|
||||
)
|
||||
addSessionGroup(
|
||||
title = hubFallback("Inaktiv", "Inactive"),
|
||||
sessions = dummyTodaySessions.filter { it.status == "Inactive" },
|
||||
)
|
||||
}
|
||||
|
||||
private fun addSessionGroup(title: String, sessions: List<TodaySession>) {
|
||||
if (sessions.isEmpty()) return
|
||||
todaySessionsContainer.addView(TextView(activity).apply {
|
||||
text = title
|
||||
setPadding(dp(2), if (todaySessionsContainer.childCount == 0) 0 else dp(14), 0, dp(4))
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(R.color.brand_text_dark))
|
||||
})
|
||||
sessions.forEach { session ->
|
||||
todaySessionsContainer.addView(createTodaySessionCard(session))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTodaySessionCard(session: TodaySession): View {
|
||||
val card = MaterialCardView(activity).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
).also {
|
||||
it.setMargins(0, 0, 0, dp(10))
|
||||
}
|
||||
radius = dp(14).toFloat()
|
||||
strokeWidth = dp(if (session.status == "Today") 2 else 1)
|
||||
setStrokeColor(color(if (session.status == "Overdue") R.color.brand_chip_start_fill else R.color.brand_purple))
|
||||
setCardBackgroundColor(color(R.color.brand_header_surface))
|
||||
cardElevation = dp(2).toFloat()
|
||||
}
|
||||
val content = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(dp(16), dp(14), dp(16), dp(14))
|
||||
}
|
||||
val header = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
}
|
||||
val titleBlock = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||
}
|
||||
titleBlock.addView(TextView(activity).apply {
|
||||
text = session.clientCode
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(R.color.brand_text_dark))
|
||||
})
|
||||
titleBlock.addView(TextView(activity).apply {
|
||||
text = "Cycle ${session.cycle} - Session ${session.session} of ${session.totalSessions}"
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
setTextColor(color(R.color.brand_text_muted))
|
||||
})
|
||||
header.addView(titleBlock)
|
||||
header.addView(sessionStatusChip(session.status))
|
||||
content.addView(header)
|
||||
|
||||
content.addView(TextView(activity).apply {
|
||||
text = "Planned: ${session.plannedDate} Last: ${session.lastSession}"
|
||||
setPadding(0, dp(10), 0, dp(8))
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
setTextColor(color(R.color.brand_text_dark))
|
||||
})
|
||||
content.addView(TextView(activity).apply {
|
||||
text = hubFallback("Startet automatisch", "Starts automatically")
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(R.color.brand_text_muted))
|
||||
})
|
||||
session.measures.forEachIndexed { index, measure ->
|
||||
content.addView(measureRow(measure, checked = index == 0))
|
||||
}
|
||||
content.addView(scheduleNextDateBlock(session.nextSuggestedDate))
|
||||
|
||||
val actions = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
setPadding(0, dp(12), 0, 0)
|
||||
}
|
||||
actions.addView(todayAction("Manage", filled = false, weight = 1f) {
|
||||
showManageSessionSheet(session)
|
||||
})
|
||||
actions.addView(todayAction("Start session", filled = true, weight = 1.25f))
|
||||
content.addView(actions)
|
||||
card.addView(content)
|
||||
return card
|
||||
}
|
||||
|
||||
private fun sessionStatusChip(status: String): TextView =
|
||||
TextView(activity).apply {
|
||||
text = status
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(if (status == "Upcoming" || status == "Inactive") R.color.brand_chip_locked_text else R.color.brand_text_on_dark))
|
||||
setBackgroundResource(
|
||||
when (status) {
|
||||
"Overdue" -> R.drawable.bg_chip_amber
|
||||
"Upcoming", "Inactive" -> R.drawable.bg_chip_locked
|
||||
else -> R.drawable.bg_chip_green
|
||||
},
|
||||
)
|
||||
setPadding(dp(10), dp(6), dp(10), dp(6))
|
||||
}
|
||||
|
||||
private fun measureRow(label: String, checked: Boolean): View =
|
||||
LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
setPadding(0, dp(7), 0, 0)
|
||||
addView(TextView(activity).apply {
|
||||
text = if (checked) "[x]" else "[ ]"
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(if (checked) R.color.brand_purple else R.color.brand_text_muted))
|
||||
})
|
||||
addView(TextView(activity).apply {
|
||||
text = label
|
||||
setPadding(dp(8), 0, 0, 0)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
|
||||
setTextColor(color(R.color.brand_text_dark))
|
||||
})
|
||||
}
|
||||
|
||||
private fun scheduleNextDateBlock(suggestedDate: String): View =
|
||||
LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
background = ContextCompat.getDrawable(activity, R.drawable.bg_session_metric)
|
||||
setPadding(dp(12), dp(10), dp(12), dp(10))
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
)
|
||||
lp.setMargins(0, dp(12), 0, 0)
|
||||
layoutParams = lp
|
||||
addView(TextView(activity).apply {
|
||||
text = hubFallback("Nächsten Termin planen", "Schedule next date")
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
|
||||
setTextColor(color(R.color.brand_text_muted))
|
||||
})
|
||||
val row = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
setPadding(0, dp(4), 0, 0)
|
||||
}
|
||||
row.addView(TextView(activity).apply {
|
||||
text = suggestedDate
|
||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(R.color.brand_text_dark))
|
||||
})
|
||||
row.addView(todayAction("Edit date", filled = false, weight = 0f, fixedWidth = dp(96)))
|
||||
addView(row)
|
||||
}
|
||||
|
||||
private fun todayAction(
|
||||
label: String,
|
||||
filled: Boolean,
|
||||
weight: Float,
|
||||
fixedWidth: Int? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
): MaterialButton =
|
||||
MaterialButton(activity).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
fixedWidth ?: 0,
|
||||
dp(40),
|
||||
if (fixedWidth == null) weight else 0f,
|
||||
).also { it.setMargins(0, 0, dp(8), 0) }
|
||||
text = label
|
||||
isAllCaps = false
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
cornerRadius = dp(18)
|
||||
strokeWidth = dp(1)
|
||||
insetTop = 0
|
||||
insetBottom = 0
|
||||
setTextColor(color(if (filled) R.color.brand_text_on_dark else R.color.brand_purple))
|
||||
backgroundTintList = ColorStateList.valueOf(color(if (filled) R.color.brand_purple else R.color.brand_header_surface))
|
||||
strokeColor = ColorStateList.valueOf(color(R.color.brand_purple))
|
||||
setOnClickListener {
|
||||
if (onClick != null) onClick()
|
||||
else Toast.makeText(activity, "$label is prototype-only", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showManageSessionSheet(session: TodaySession) {
|
||||
val container = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setBackgroundColor(color(R.color.brand_surface))
|
||||
setPadding(dp(20), dp(18), dp(20), dp(18))
|
||||
}
|
||||
|
||||
val header = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
}
|
||||
val titleBlock = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||
}
|
||||
titleBlock.addView(TextView(activity).apply {
|
||||
text = "Manage ${session.clientCode}"
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(R.color.brand_text_dark))
|
||||
})
|
||||
titleBlock.addView(TextView(activity).apply {
|
||||
text = "Cycle ${session.cycle} - Session ${session.session} of ${session.totalSessions}"
|
||||
setPadding(0, dp(3), 0, 0)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
setTextColor(color(R.color.brand_text_muted))
|
||||
})
|
||||
val closeButton = MaterialButton(activity).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(dp(78), dp(40))
|
||||
text = "Close"
|
||||
isAllCaps = false
|
||||
cornerRadius = dp(18)
|
||||
strokeWidth = dp(1)
|
||||
insetTop = 0
|
||||
insetBottom = 0
|
||||
setTextColor(color(R.color.brand_purple))
|
||||
backgroundTintList = ColorStateList.valueOf(color(R.color.brand_header_surface))
|
||||
strokeColor = ColorStateList.valueOf(color(R.color.brand_header_stroke))
|
||||
}
|
||||
header.addView(titleBlock)
|
||||
header.addView(closeButton)
|
||||
|
||||
val body = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(0, dp(14), 0, 0)
|
||||
}
|
||||
body.addView(manageSectionTitle("Client status"))
|
||||
body.addView(LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
addView(manageStatusButton("Active", selected = session.status != "Inactive"))
|
||||
addView(manageStatusButton("Inactive", selected = session.status == "Inactive"))
|
||||
addView(manageStatusButton("Archived", selected = false))
|
||||
})
|
||||
body.addView(manageSectionTitle("Scheduling"))
|
||||
body.addView(manageAction("Edit next planned date", "Current suggestion: ${session.nextSuggestedDate}"))
|
||||
body.addView(manageAction("Reschedule current session", "Move this session to another date"))
|
||||
body.addView(manageAction("Skip this occurrence", "Keep the cycle but omit this planned visit"))
|
||||
body.addView(manageSectionTitle("Session"))
|
||||
body.addView(manageAction("View session protocol", "${session.measures.size} measures assigned"))
|
||||
body.addView(manageAction("Add counselor note", "Attach a local note to this client"))
|
||||
body.addView(manageAction("Sync client data", "Review pending local data for this client"))
|
||||
body.addView(manageSectionTitle("Lifecycle"))
|
||||
body.addView(manageAction("Mark no longer participating", "Move client to inactive"))
|
||||
body.addView(manageAction("Archive client", "Hide from active scheduling"))
|
||||
body.addView(manageAction("Delete client", "Prototype-only destructive option", destructive = true))
|
||||
|
||||
val scroll = ScrollView(activity).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
dp(430),
|
||||
)
|
||||
isFillViewport = false
|
||||
addView(body)
|
||||
}
|
||||
|
||||
container.addView(header)
|
||||
container.addView(scroll)
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setView(container)
|
||||
.create()
|
||||
closeButton.setOnClickListener { dialog.dismiss() }
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun manageSectionTitle(title: String): View =
|
||||
TextView(activity).apply {
|
||||
text = title
|
||||
setPadding(dp(2), dp(14), 0, dp(7))
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
setTextColor(color(R.color.brand_text_muted))
|
||||
}
|
||||
|
||||
private fun manageStatusButton(label: String, selected: Boolean): MaterialButton =
|
||||
MaterialButton(activity).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(0, dp(40), 1f).also {
|
||||
it.setMargins(0, 0, dp(8), 0)
|
||||
}
|
||||
text = label
|
||||
isAllCaps = false
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
cornerRadius = dp(18)
|
||||
strokeWidth = dp(1)
|
||||
insetTop = 0
|
||||
insetBottom = 0
|
||||
setTextColor(color(if (selected) R.color.brand_text_on_dark else R.color.brand_text_dark))
|
||||
backgroundTintList = ColorStateList.valueOf(
|
||||
color(if (selected) R.color.brand_purple else R.color.brand_header_surface),
|
||||
)
|
||||
strokeColor = ColorStateList.valueOf(color(R.color.brand_purple))
|
||||
setOnClickListener {
|
||||
Toast.makeText(activity, "Set status to $label is prototype-only", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun manageAction(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
destructive: Boolean = false,
|
||||
): View =
|
||||
MaterialButton(activity).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
).also { it.setMargins(0, 0, 0, dp(8)) }
|
||||
text = "$title\n$subtitle"
|
||||
isAllCaps = false
|
||||
gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
||||
textAlignment = View.TEXT_ALIGNMENT_VIEW_START
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
|
||||
cornerRadius = dp(16)
|
||||
strokeWidth = dp(1)
|
||||
insetTop = 0
|
||||
insetBottom = 0
|
||||
setPadding(dp(14), dp(11), dp(14), dp(11))
|
||||
setTextColor(color(if (destructive) R.color.brand_destructive_text else R.color.brand_text_dark))
|
||||
backgroundTintList = ColorStateList.valueOf(
|
||||
color(if (destructive) R.color.brand_destructive_fill else R.color.brand_header_surface),
|
||||
)
|
||||
strokeColor = ColorStateList.valueOf(
|
||||
color(if (destructive) R.color.brand_destructive_fill else R.color.brand_header_stroke),
|
||||
)
|
||||
setOnClickListener {
|
||||
Toast.makeText(activity, "$title is prototype-only", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadQuestionnaireOrder() {
|
||||
val serverItems = QuestionnaireCache.getQuestionnaireList(activity)
|
||||
Log.d(TAG, "loadQuestionnaireOrder: serverItems from cache = ${serverItems.map { it.id }}")
|
||||
@ -765,11 +1184,11 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
val hasClients = assignedClientsForUi().isNotEmpty()
|
||||
if (hasClients) {
|
||||
noClientsAssignedText.visibility = View.GONE
|
||||
textView.visibility = View.VISIBLE
|
||||
nextStepBannerCard.visibility = View.VISIBLE
|
||||
questionnaireScroll.visibility = View.VISIBLE
|
||||
textView.visibility = View.GONE
|
||||
nextStepBannerCard.visibility = View.GONE
|
||||
questionnaireListHost.visibility = View.GONE
|
||||
questionnaireScroll.visibility = View.GONE
|
||||
bottomUploadDock.visibility = View.VISIBLE
|
||||
updateNextStepBanner()
|
||||
} else {
|
||||
clearLoadedClientState()
|
||||
noClientsAssignedText.text = t("no_clients_assigned")
|
||||
@ -777,6 +1196,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
textView.visibility = View.GONE
|
||||
updateSessionMetrics()
|
||||
nextStepBannerCard.visibility = View.GONE
|
||||
questionnaireListHost.visibility = View.GONE
|
||||
questionnaireScroll.visibility = View.GONE
|
||||
bottomUploadDock.visibility = View.GONE
|
||||
}
|
||||
@ -784,53 +1204,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
|
||||
private fun updateNextStepBanner() {
|
||||
if (!::nextStepBannerCard.isInitialized) return
|
||||
val total = dynamicButtons.size
|
||||
if (total == 0) {
|
||||
nextStepBannerCard.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
if (assignedClientsForUi().isEmpty()) return
|
||||
|
||||
nextStepBannerCard.visibility = View.VISIBLE
|
||||
val completed = dynamicButtons.count { isCompleted(it) }
|
||||
nextStepEyebrow.text = hubFallback("Nächster Sitzungsschritt", "Next session step")
|
||||
nextStepTitle.text = hubFallback("Sitzungsstatus wird geprüft", "Checking session status")
|
||||
nextStepBody.text = hubFallback("Lokale Daten, Reviews und Synchronisation werden ausgewertet.", "Reviewing local measures, scoring work, and sync status.")
|
||||
nextStepActionButton.visibility = View.GONE
|
||||
nextStepActionButton.setOnClickListener(null)
|
||||
|
||||
nextStepJob?.cancel()
|
||||
val clientCode = selectedClientCode
|
||||
val token = TokenStore.getToken(activity)
|
||||
nextStepJob = CoroutineScope(Dispatchers.Main).launch {
|
||||
val summary = withContext(Dispatchers.IO) { UnuploadedWorkRepository.summarize() }
|
||||
val needsReview = withContext(Dispatchers.IO) {
|
||||
if (clientCode.isBlank() || token.isNullOrBlank() || !TokenStore.isSessionValid(activity)) {
|
||||
false
|
||||
} else {
|
||||
ReviewScoresHandler.hasPendingReviewsForClient(activity, token, clientCode)
|
||||
}
|
||||
}
|
||||
val action = when {
|
||||
summary.pendingQuestionnaires > 0 || summary.pendingScoringReviews > 0 -> NextStepAction.UPLOAD
|
||||
needsReview -> NextStepAction.REVIEW
|
||||
completed < total && nextAvailableQuestionnaireButton() != null -> NextStepAction.QUESTIONNAIRE
|
||||
else -> NextStepAction.NONE
|
||||
}
|
||||
val titleKey = when {
|
||||
action == NextStepAction.UPLOAD -> "next_step_upload_title"
|
||||
action == NextStepAction.REVIEW -> "next_step_review_title"
|
||||
completed < total -> "next_step_questionnaire_title"
|
||||
else -> "next_step_completed_title"
|
||||
}
|
||||
val bodyKey = when {
|
||||
action == NextStepAction.UPLOAD -> "next_step_upload_body"
|
||||
action == NextStepAction.REVIEW -> "next_step_review_body"
|
||||
completed < total -> "next_step_questionnaire_body"
|
||||
else -> "next_step_completed_body"
|
||||
}
|
||||
applyNextStepBanner(titleKey, bodyKey, action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyNextStepBanner(titleKey: String, bodyKey: String, action: NextStepAction) {
|
||||
@ -1120,6 +1496,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
}
|
||||
}
|
||||
reviewScoresButton.text = t("review_scores")
|
||||
reviewScoresButton.visibility = View.GONE
|
||||
uploadButton.text = t("upload")
|
||||
applyHubLabels()
|
||||
applyCoachDisplay()
|
||||
@ -1230,6 +1607,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
|
||||
private fun setupReviewScoresButton() {
|
||||
reviewScoresButton.text = t("review_scores")
|
||||
reviewScoresButton.visibility = View.GONE
|
||||
reviewScoresButton.setOnClickListener { openReviewScores() }
|
||||
}
|
||||
|
||||
@ -1258,7 +1636,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
||||
}
|
||||
|
||||
private fun setupUploadButton() {
|
||||
reviewScoresButton.text = t("review_scores")
|
||||
reviewScoresButton.visibility = View.GONE
|
||||
uploadButton.text = t("upload")
|
||||
uploadButton.setOnClickListener {
|
||||
TokenStore.ensureLegacyLoginTimestamp(activity)
|
||||
|
||||
@ -176,6 +176,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
@ -348,13 +349,73 @@
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/nextStepBannerCard" />
|
||||
app:layout_constraintTop_toBottomOf="@id/todaySessionsSection" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/todaySessionsSection"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomUploadDock"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sessionMetricsRow">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/todaySessionsTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Today's sessions"
|
||||
android:textColor="@color/brand_text_dark"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/todaySessionsSummary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_session_metric"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:textColor="@color/brand_text_muted"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="1"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingBottom="8dp"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/todaySessionsContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/nextStepBannerCard"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:visibility="gone"
|
||||
app:cardBackgroundColor="@color/brand_emphasis_bg"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="3dp"
|
||||
@ -362,7 +423,7 @@
|
||||
app:strokeWidth="1dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sessionMetricsRow">
|
||||
app:layout_constraintTop_toBottomOf="@id/todaySessionsSection">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@ -46,12 +46,11 @@
|
||||
android:background="@drawable/bg_auth_logo">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_person_24"
|
||||
app:tint="@color/brand_purple" />
|
||||
android:src="@drawable/ic_launcher_foreground" />
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
@ -353,13 +354,73 @@
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/nextStepBannerCard" />
|
||||
app:layout_constraintTop_toBottomOf="@id/todaySessionsSection" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/todaySessionsSection"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomUploadDock"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sessionMetricsRow">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/todaySessionsTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Today's sessions"
|
||||
android:textColor="@color/brand_text_dark"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/todaySessionsSummary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_session_metric"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:textColor="@color/brand_text_muted"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="1"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingBottom="8dp"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/todaySessionsContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/nextStepBannerCard"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:visibility="gone"
|
||||
app:cardBackgroundColor="@color/brand_emphasis_bg"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="3dp"
|
||||
@ -367,7 +428,7 @@
|
||||
app:strokeWidth="1dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sessionMetricsRow">
|
||||
app:layout_constraintTop_toBottomOf="@id/todaySessionsSection">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
Reference in New Issue
Block a user