überarbeiten der datenbank anzeige

This commit is contained in:
oxidiert
2025-09-23 15:17:34 +02:00
parent 8dc9be20a4
commit 91f6f77b73
3 changed files with 58 additions and 65 deletions

View File

@ -19,7 +19,6 @@ class DatabaseButtonHandler(
private val uiScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private val tag = "DatabaseButtonHandler"
// Services (wie bei dir)
private val headerRepo = HeaderOrderRepository(activity)
private val exporter = ExcelExportService(activity, headerRepo)
@ -34,16 +33,15 @@ class DatabaseButtonHandler(
private fun openDatabaseScreen() {
activity.setContentView(R.layout.database_screen)
val lang = safeLang() // ✨
val lang = safeLang()
val titleTv: TextView = requireView(R.id.title, "title") // ✨
val titleTv: TextView = requireView(R.id.title, "title")
val table: TableLayout = requireView(R.id.tableClients, "tableClients")
val progress: ProgressBar = requireView(R.id.progressBar, "progressBar")
val emptyView: TextView = requireView(R.id.emptyView, "emptyView")
val backButton: Button = requireView(R.id.backButton, "backButton")
val btnDownloadHeader: Button = requireView(R.id.btnDownloadHeader, "btnDownloadHeader")
// ✨ statische Texte lokalisieren
titleTv.text = t(lang, "database_clients_title") ?: "Datenbank Clients"
emptyView.text = t(lang, "no_clients_available") ?: "Keine Clients vorhanden."
backButton.text = t(lang, "previous") ?: "Zurück"
@ -56,7 +54,6 @@ class DatabaseButtonHandler(
emptyView.visibility = View.GONE
table.removeAllViews()
// ✨ Spaltenkopf lokalisieren
addHeaderRow(table, listOf("#", t(lang, "client_code") ?: "Client-Code"))
uiScope.launch {
@ -91,7 +88,7 @@ class DatabaseButtonHandler(
val savedUri = exporter.exportHeadersForAllClients()
progress.visibility = View.GONE
val lang = safeLang() // ✨
val lang = safeLang()
if (savedUri != null) {
Toast.makeText(
activity,
@ -115,7 +112,7 @@ class DatabaseButtonHandler(
private fun openClientOverviewScreen(clientCode: String) {
activity.setContentView(R.layout.client_overview_screen)
val lang = safeLang() // ✨
val lang = safeLang()
val title: TextView = requireView(R.id.titleClientOverview, "titleClientOverview")
val tableQ: TableLayout = requireView(R.id.tableQuestionnaires, "tableQuestionnaires")
@ -126,7 +123,6 @@ class DatabaseButtonHandler(
val headerLabel: TextView = requireView(R.id.headerLabel, "headerLabel")
val tableOrdered: TableLayout = requireView(R.id.tableOrdered, "tableOrdered")
// ✨
title.text = "${t(lang, "client") ?: "Client"}: $clientCode ${t(lang, "questionnaires") ?: "Fragebögen"}"
headerLabel.text = t(lang, "headers") ?: "header"
backButton.text = t(lang, "previous") ?: "Zurück"
@ -137,7 +133,6 @@ class DatabaseButtonHandler(
tableQ.removeAllViews()
tableOrdered.removeAllViews()
// ✨ Tabellenköpfe lokalisieren
addHeaderRow(
tableQ,
listOf("#", t(lang, "questionnaire_id") ?: "Fragebogen-ID", t(lang, "status") ?: "Status")
@ -149,46 +144,53 @@ class DatabaseButtonHandler(
uiScope.launch {
val result = withContext(Dispatchers.IO) {
val allQuestionnaires = MyApp.database.questionnaireDao().getAll()
val allQuestionnairesDb = MyApp.database.questionnaireDao().getAll()
val completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
Triple(allQuestionnaires, completedForClient, allAnswersForClient)
Triple(allQuestionnairesDb, completedForClient, allAnswersForClient)
}
// IDs aus der JSON-Reihenfolge lesen (alle, die es geben soll)
val idsFromAssets: List<String> = loadQuestionnaireIdsFromAssets()
progress.visibility = View.GONE
val allQuestionnaires: List<Questionnaire> = result.first
val dbQuestionnaires: List<Questionnaire> = result.first
val completedForClient = result.second
val allAnswersForClient = result.third
if (allQuestionnaires.isEmpty()) {
// Vereinigung: alles was in JSON steht + alles was in der DB existiert
val allIds: List<String> =
(idsFromAssets + dbQuestionnaires.map { it.id }).distinct()
if (allIds.isEmpty()) {
emptyView.text = t(lang, "no_questionnaires") ?: "Keine Fragebögen vorhanden."
emptyView.visibility = View.VISIBLE
}
val statusMap = completedForClient.associate { it.questionnaireId to it.isDone }
val questionnaireIdSet = allQuestionnaires.map { it.id }.toSet()
val questionnaireIdSet = allIds.toSet() // für die zweite Tabelle
val answerMap = allAnswersForClient.associate { it.questionId to it.answerValue }
// Tabelle 1 (Status)
allQuestionnaires
.sortedWith(compareBy({ extractQuestionnaireNumber(it.id) ?: Int.MAX_VALUE }, { it.id }))
.forEachIndexed { idx, q ->
val isDone = statusMap[q.id] ?: false
// Tabelle 1 (Status) JETZT mit allen IDs
allIds
.sortedWith(compareBy({ extractQuestionnaireNumber(it) ?: Int.MAX_VALUE }, { it }))
.forEachIndexed { idx, qid ->
val isDone = statusMap[qid] ?: false
val statusText = if (isDone) "" else ""
val statusTextColor = if (isDone) 0xFF4CAF50.toInt() else 0xFFF44336.toInt()
if (isDone) {
addClickableRow(
table = tableQ,
cells = listOf((idx + 1).toString(), q.id, statusText),
onClick = { openQuestionnaireDetailScreen(clientCode, q.id) },
cells = listOf((idx + 1).toString(), qid, statusText),
onClick = { openQuestionnaireDetailScreen(clientCode, qid) },
colorOverrides = mapOf(2 to statusTextColor)
)
} else {
addDisabledRow(
table = tableQ,
cells = listOf((idx + 1).toString(), q.id, statusText),
cells = listOf((idx + 1).toString(), qid, statusText),
colorOverrides = mapOf(2 to statusTextColor)
)
}
@ -219,7 +221,7 @@ class DatabaseButtonHandler(
rowBgColor = if (raw == "None") lightRed else lightGreen
}
val display = localizeHeaderValue(id, raw, lang) // ✨
val display = localizeHeaderValue(id, raw, lang)
val cellBgOverrides =
if (cellBgForQuestionnaire != null)
@ -242,7 +244,7 @@ class DatabaseButtonHandler(
private fun openQuestionnaireDetailScreen(clientCode: String, questionnaireId: String) {
activity.setContentView(R.layout.questionnaire_detail_screen)
val lang = safeLang() // ✨
val lang = safeLang()
val title: TextView = requireView(R.id.titleQuestionnaireDetail, "titleQuestionnaireDetail")
val table: TableLayout = requireView(R.id.tableQA, "tableQA")
@ -250,7 +252,6 @@ class DatabaseButtonHandler(
val emptyView: TextView = requireView(R.id.emptyViewQA, "emptyViewQA")
val backButton: Button = requireView(R.id.backButtonQA, "backButtonQA")
// ✨ Titel + Back + Empty übersetzen
title.text = "${t(lang, "client") ?: "Client"}: $clientCode ${t(lang, "questionnaire") ?: "Fragebogen"}: $questionnaireId"
backButton.text = t(lang, "previous") ?: "Zurück"
emptyView.text = t(lang, "no_questions_available") ?: "Keine Fragen vorhanden."
@ -260,7 +261,6 @@ class DatabaseButtonHandler(
emptyView.visibility = View.GONE
table.removeAllViews()
// ✨ Kopf: # | Frage | Antwort
addHeaderRow(table, listOf("#", t(lang, "question") ?: "Frage", t(lang, "answer") ?: "Antwort"))
uiScope.launch {
@ -281,17 +281,17 @@ class DatabaseButtonHandler(
val answerMap = answersForClient.associate { it.questionId to it.answerValue }
questions.forEachIndexed { idx, q: Question ->
val baseId = q.questionId.substringAfterLast('-', q.questionId) // z.B. consent_signed, gender_male
val qText = localizeQuestionLabel(q.questionId, q.question, lang) // ✨
val baseId = q.questionId.substringAfterLast('-', q.questionId)
val qText = localizeQuestionLabel(q.questionId, q.question, lang)
val raw = answerMap[q.questionId]?.takeIf { it.isNotBlank() } ?: ""
val aText = localizeAnswerValue(baseId, raw, lang) // ✨
val aText = localizeAnswerValue(baseId, raw, lang)
addRow(table, listOf((idx + 1).toString(), qText, aText))
}
}
}
// ---------------------------
// Lokalisierungshilfen (Anzeige)
// Hilfen
// ---------------------------
private fun safeLang(): String = try { languageIDProvider() } catch (_: Exception) { "GERMAN" }
@ -307,38 +307,27 @@ class DatabaseButtonHandler(
return if (out.equals(key, true) || out.isBlank()) null else out
}
// ✨ Frage-Label für die UI
private fun localizeQuestionLabel(questionId: String, fallbackQuestionText: String?, lang: String): String {
val field = questionId.substringAfterLast('-', questionId)
t(lang, field)?.let { return it } // z.B. consent_instruction
t(lang, questionId)?.let { return it } // kompletter key
t(lang, field)?.let { return it }
t(lang, questionId)?.let { return it }
fallbackQuestionText?.takeIf { it.isNotBlank() }?.let { return it }
return field.replace('_', ' ').replaceFirstChar { it.titlecase() }
}
// ✨ Antwort-Wert für die UI
private fun localizeAnswerValue(fieldId: String, raw: String, lang: String): String {
// Zahlen/Datum/Strich beibehalten
if (raw == "") return raw
if (raw.matches(Regex("^\\d{1,4}([./-]\\d{1,2}([./-]\\d{1,4})?)?\$"))) return raw
// 1) direkter Key
t(lang, raw)?.let { return it }
// 2) normalisiert
val norm = raw.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_')
if (norm.isNotBlank()) t(lang, norm)?.let { return it }
// 3) kombiniere Feld + Wert (häufiges Muster)
if (norm.isNotBlank()) {
t(lang, "${fieldId}_$norm")?.let { return it }
t(lang, "${fieldId}-${norm}")?.let { return it }
}
// Klammern entfernen, falls aus LanguageManager so geliefert
return stripBrackets(raw)
}
// ---------------------------
// Anzeige-Helfer
// ---------------------------
private fun addHeaderRow(table: TableLayout, labels: List<String>) {
val row = TableRow(activity)
labels.forEach { label -> row.addView(makeHeaderCell(label)) }
@ -442,7 +431,6 @@ class DatabaseButtonHandler(
return m?.groupValues?.get(1)?.toIntOrNull()
}
// ✨ alte localizeHeaderValue erweitert: nimmt jetzt lang mit
private fun localizeHeaderValue(id: String, raw: String, lang: String): String {
if (id == "client_code") return raw
@ -457,4 +445,17 @@ class DatabaseButtonHandler(
for (k in candidates) t(lang, k)?.let { return it }
return stripBrackets(raw)
}
/** Lädt alle Fragebogen-IDs aus questionnaire_order.json (file ohne .json). */
private fun loadQuestionnaireIdsFromAssets(): List<String> = try {
val input = activity.assets.open("questionnaire_order.json")
val json = input.bufferedReader().use { it.readText() }
val arr = JSONArray(json)
(0 until arr.length()).mapNotNull { i ->
val obj = arr.optJSONObject(i)
obj?.optString("file")?.removeSuffix(".json")
}
} catch (_: Exception) {
emptyList()
}
}

View File

@ -649,8 +649,8 @@ object LanguageManager {
"please_client_code" to "Please enter client code",
"no_profile" to "This client is not yet part of the database",
"questionnaires_finished" to "All questionnaires have been completed for this client!",
"edit" to "Bearbeiten",
"save" to "Speichern",
"edit" to "Edit",
"save" to "Save",
"upload" to "Upload",
"download" to "Download",
"database" to "Database",

View File

@ -15,26 +15,19 @@
android:textSize="20sp"
android:paddingBottom="8dp" />
<!-- Tabelle 1: Fragebögen (✓/✗) -->
<!-- OBERTEIL: Alle Fragebögen vollständig (kein Weight, keine vertikale ScrollView) -->
<HorizontalScrollView
android:id="@+id/qsScroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="horizontal">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="vertical">
<TableLayout
android:id="@+id/tableQuestionnaires"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1" />
</ScrollView>
</HorizontalScrollView>
<TextView
@ -54,7 +47,7 @@
android:paddingTop="8dp"
android:paddingBottom="8dp" />
<!-- HEADER + Tabelle 2: geordnete IDs/Werte -->
<!-- HEADER + Tabelle 2: Scrollbar bekommt den restlichen Platz -->
<TextView
android:id="@+id/headerLabel"
android:layout_width="match_parent"
@ -89,7 +82,6 @@
<Button
android:id="@+id/backButtonClient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Zurück" />
android:layout_height="wrap_content" />
</LinearLayout>