From 91f6f77b73517a2bc2e176bfc9dc1fa81a288115 Mon Sep 17 00:00:00 2001 From: oxidiert Date: Tue, 23 Sep 2025 15:17:34 +0200 Subject: [PATCH] =?UTF-8?q?=C3=BCberarbeiten=20der=20datenbank=20anzeige?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dano/test1/DatabaseButtonHandler.kt | 93 ++++++++++--------- .../java/com/dano/test1/LanguageManager.kt | 4 +- .../res/layout/client_overview_screen.xml | 26 ++---- 3 files changed, 58 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt b/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt index 4f56508..a19e341 100644 --- a/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt @@ -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 completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode) - val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode) - Triple(allQuestionnaires, completedForClient, allAnswersForClient) + val allQuestionnairesDb = MyApp.database.questionnaireDao().getAll() + val completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode) + val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode) + Triple(allQuestionnairesDb, completedForClient, allAnswersForClient) } + // IDs aus der JSON-Reihenfolge lesen (alle, die es geben soll) + val idsFromAssets: List = loadQuestionnaireIdsFromAssets() + progress.visibility = View.GONE - val allQuestionnaires: List = result.first + val dbQuestionnaires: List = 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 = + (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) { 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 = 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() + } } diff --git a/app/src/main/java/com/dano/test1/LanguageManager.kt b/app/src/main/java/com/dano/test1/LanguageManager.kt index 122b25e..9a1b154 100644 --- a/app/src/main/java/com/dano/test1/LanguageManager.kt +++ b/app/src/main/java/com/dano/test1/LanguageManager.kt @@ -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", diff --git a/app/src/main/res/layout/client_overview_screen.xml b/app/src/main/res/layout/client_overview_screen.xml index 71dcfe3..53e63ad 100644 --- a/app/src/main/res/layout/client_overview_screen.xml +++ b/app/src/main/res/layout/client_overview_screen.xml @@ -15,26 +15,19 @@ android:textSize="20sp" android:paddingBottom="8dp" /> - + - - - - + android:layout_height="wrap_content" + android:stretchColumns="1" /> - + + android:layout_height="wrap_content" />